Home | History | Annotate | Line # | Download | only in omapip
trace.c revision 1.1.1.1
      1 /*	$NetBSD: trace.c,v 1.1.1.1 2018/04/07 22:34:27 christos Exp $	*/
      2 
      3 /* trace.c
      4 
      5    Subroutines that support tracing of OMAPI wire transactions and
      6    provide a mechanism for programs using OMAPI to trace their own
      7    transactions... */
      8 
      9 /*
     10  * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
     11  * Copyright (c) 2001-2003 by Internet Software Consortium
     12  *
     13  * This Source Code Form is subject to the terms of the Mozilla Public
     14  * License, v. 2.0. If a copy of the MPL was not distributed with this
     15  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     16  *
     17  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
     18  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     19  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
     20  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     21  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     22  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
     23  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     24  *
     25  *   Internet Systems Consortium, Inc.
     26  *   950 Charter Street
     27  *   Redwood City, CA 94063
     28  *   <info (at) isc.org>
     29  *   https://www.isc.org/
     30  *
     31  */
     32 
     33 #include <sys/cdefs.h>
     34 __RCSID("$NetBSD: trace.c,v 1.1.1.1 2018/04/07 22:34:27 christos Exp $");
     35 
     36 #include "dhcpd.h"
     37 #include <omapip/omapip_p.h>
     38 #include <errno.h>
     39 
     40 #if defined (TRACING)
     41 void (*trace_set_time_hook) (TIME);
     42 static int tracing_stopped;
     43 static int traceoutfile;
     44 static int traceindex;
     45 static trace_type_t **trace_types;
     46 static int trace_type_count;
     47 static int trace_type_max;
     48 static trace_type_t *new_trace_types;
     49 static FILE *traceinfile;
     50 static tracefile_header_t tracefile_header;
     51 static int trace_playback_flag;
     52 trace_type_t trace_time_marker;
     53 
     54 #if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
     55 extern omapi_array_t *trace_listeners;
     56 extern omapi_array_t *omapi_connections;
     57 
     58 extern int errno;
     59 
     60 void trace_free_all ()
     61 {
     62 	trace_type_t *tp;
     63 	int i;
     64 	tp = new_trace_types;
     65 	while (tp) {
     66 		new_trace_types = tp -> next;
     67 		if (tp -> name) {
     68 			dfree (tp -> name, MDL);
     69 			tp -> name = (char *)0;
     70 		}
     71 		dfree (tp, MDL);
     72 		tp = new_trace_types;
     73 	}
     74 	for (i = 0; i < trace_type_count; i++) {
     75 		if (trace_types [i]) {
     76 			if (trace_types [i] -> name)
     77 				dfree (trace_types [i] -> name, MDL);
     78 			dfree (trace_types [i], MDL);
     79 		}
     80 	}
     81 	dfree (trace_types, MDL);
     82 	trace_types = (trace_type_t **)0;
     83 	trace_type_count = trace_type_max = 0;
     84 
     85 	omapi_array_free (&trace_listeners, MDL);
     86 	omapi_array_free (&omapi_connections, MDL);
     87 }
     88 #endif
     89 
     90 static isc_result_t trace_type_record (trace_type_t *,
     91 				       unsigned, const char *, int);
     92 
     93 int trace_playback ()
     94 {
     95 	return trace_playback_flag;
     96 }
     97 
     98 int trace_record ()
     99 {
    100 	if (traceoutfile && !tracing_stopped)
    101 		return 1;
    102 	return 0;
    103 }
    104 
    105 isc_result_t trace_init (void (*set_time) (TIME),
    106 			 const char *file, int line)
    107 {
    108 	trace_type_t *root_type;
    109 	static int root_setup = 0;
    110 
    111 	if (root_setup)
    112 		return ISC_R_SUCCESS;
    113 
    114 	trace_set_time_hook = set_time;
    115 
    116 	root_type = trace_type_register ("trace-index-mapping",
    117 					 (void *)0, trace_index_map_input,
    118 					 trace_index_stop_tracing, file, line);
    119 	if (!root_type)
    120 		return ISC_R_UNEXPECTED;
    121 	if (new_trace_types == root_type)
    122 		new_trace_types = new_trace_types -> next;
    123 	root_type -> index = 0;
    124 	trace_type_stash (root_type);
    125 
    126 	root_setup = 1;
    127 	return ISC_R_SUCCESS;
    128 }
    129 
    130 isc_result_t trace_begin (const char *filename,
    131 			  const char *file, int line)
    132 {
    133 	tracefile_header_t tfh;
    134 	int status;
    135 	trace_type_t *tptr, *next;
    136 	isc_result_t result;
    137 
    138 	if (traceoutfile) {
    139 		log_error ("%s(%d): trace_begin called twice",
    140 			   file, line);
    141 		return DHCP_R_INVALIDARG;
    142 	}
    143 
    144 	traceoutfile = open (filename, O_CREAT | O_WRONLY | O_EXCL, 0600);
    145 	if (traceoutfile < 0 && errno == EEXIST) {
    146 		log_error ("WARNING: Overwriting trace file \"%s\"", filename);
    147 		traceoutfile = open (filename, O_WRONLY | O_EXCL | O_TRUNC,
    148 				     0600);
    149 	}
    150 
    151 	if (traceoutfile < 0) {
    152 		log_error ("%s(%d): trace_begin: %s: %m",
    153 			   file, line, filename);
    154 		return ISC_R_UNEXPECTED;
    155 	}
    156 #if defined (HAVE_SETFD)
    157 	if (fcntl (traceoutfile, F_SETFD, 1) < 0)
    158 		log_error ("Can't set close-on-exec on %s: %m", filename);
    159 #endif
    160 
    161 	tfh.magic = htonl (TRACEFILE_MAGIC);
    162 	tfh.version = htonl (TRACEFILE_VERSION);
    163 	tfh.hlen = htonl (sizeof (tracefile_header_t));
    164 	tfh.phlen = htonl (sizeof (tracepacket_t));
    165 
    166 	status = write (traceoutfile, &tfh, sizeof tfh);
    167 	if (status < 0) {
    168 		log_error ("%s(%d): trace_begin write failed: %m", file, line);
    169 		return ISC_R_UNEXPECTED;
    170 	} else if (status != sizeof tfh) {
    171 		log_error ("%s(%d): trace_begin: short write (%d:%ld)",
    172 			   file, line, status, (long)(sizeof tfh));
    173 		trace_stop ();
    174 		return ISC_R_UNEXPECTED;
    175 	}
    176 
    177 	/* Stash all the types that have already been set up. */
    178 	if (new_trace_types) {
    179 		next = new_trace_types;
    180 		new_trace_types = (trace_type_t *)0;
    181 		for (tptr = next; tptr; tptr = next) {
    182 			next = tptr -> next;
    183 			if (tptr -> index != 0) {
    184 				result = (trace_type_record
    185 					  (tptr,
    186 					   strlen (tptr -> name), file, line));
    187 				if (result != ISC_R_SUCCESS)
    188 					return status;
    189 			}
    190 		}
    191 	}
    192 
    193 	return ISC_R_SUCCESS;
    194 }
    195 
    196 isc_result_t trace_write_packet (trace_type_t *ttype, unsigned length,
    197 				 const char *buf, const char *file, int line)
    198 {
    199 	trace_iov_t iov;
    200 
    201 	iov.buf = buf;
    202 	iov.len = length;
    203 	return trace_write_packet_iov (ttype, 1, &iov, file, line);
    204 }
    205 
    206 isc_result_t trace_write_packet_iov (trace_type_t *ttype,
    207 				     int count, trace_iov_t *iov,
    208 				     const char *file, int line)
    209 {
    210 	tracepacket_t tmp;
    211 	int status;
    212 	int i;
    213 	int length;
    214 
    215 	/* Really shouldn't get called here, but it may be hard to turn off
    216 	   tracing midstream if the trace file write fails or something. */
    217 	if (tracing_stopped)
    218 		return 0;
    219 
    220 	if (!ttype) {
    221 		log_error ("%s(%d): trace_write_packet with null trace type",
    222 			   file ? file : "<unknown file>", line);
    223 		return DHCP_R_INVALIDARG;
    224 	}
    225 	if (!traceoutfile) {
    226 		log_error ("%s(%d): trace_write_packet with no tracefile.",
    227 			   file ? file : "<unknown file>", line);
    228 		return DHCP_R_INVALIDARG;
    229 	}
    230 
    231 	/* Compute the total length of the iov. */
    232 	length = 0;
    233 	for (i = 0; i < count; i++)
    234 		length += iov [i].len;
    235 
    236 	/* We have to swap out the data, because it may be read back on a
    237 	   machine of different endianness. */
    238 	memset(&tmp, 0, sizeof(tmp));
    239 	tmp.type_index = htonl (ttype -> index);
    240 	tmp.when = htonl (time ((time_t *)0)); /* XXX */
    241 	tmp.length = htonl (length);
    242 
    243 	status = write (traceoutfile, &tmp, sizeof tmp);
    244 	if (status < 0) {
    245 		log_error ("%s(%d): trace_write_packet write failed: %m",
    246 			   file, line);
    247 		return ISC_R_UNEXPECTED;
    248 	} else if (status != sizeof tmp) {
    249 		log_error ("%s(%d): trace_write_packet: short write (%d:%ld)",
    250 			   file, line, status, (long)(sizeof tmp));
    251 		trace_stop ();
    252 	}
    253 
    254 	for (i = 0; i < count; i++) {
    255 		status = write (traceoutfile, iov [i].buf, iov [i].len);
    256 		if (status < 0) {
    257 			log_error ("%s(%d): %s write failed: %m",
    258 				   file, line, "trace_write_packet");
    259 			return ISC_R_UNEXPECTED;
    260 		} else if (status != iov [i].len) {
    261 			log_error ("%s(%d): %s: short write (%d:%d)",
    262 				   file, line,
    263 				   "trace_write_packet", status, length);
    264 			trace_stop ();
    265 		}
    266 	}
    267 
    268 	/* Write padding on the end of the packet to align the next
    269 	   packet to an 8-byte boundary.   This is in case we decide to
    270 	   use mmap in some clever way later on. */
    271 	if (length % 8) {
    272 	    static char zero [] = { 0, 0, 0, 0, 0, 0, 0 };
    273 	    unsigned padl = 8 - (length % 8);
    274 
    275 	    status = write (traceoutfile, zero, padl);
    276 	    if (status < 0) {
    277 		log_error ("%s(%d): trace_write_packet write failed: %m",
    278 			   file, line);
    279 		return ISC_R_UNEXPECTED;
    280 	    } else if (status != padl) {
    281 		log_error ("%s(%d): trace_write_packet: short write (%d:%d)",
    282 			   file, line, status, padl);
    283 		trace_stop ();
    284 	    }
    285 	}
    286 
    287 	return ISC_R_SUCCESS;
    288 }
    289 
    290 void trace_type_stash (trace_type_t *tptr)
    291 {
    292 	trace_type_t **vec;
    293 	int delta;
    294 	if (trace_type_max <= tptr -> index) {
    295 		delta = tptr -> index - trace_type_max + 10;
    296 		vec = dmalloc (((trace_type_max + delta) *
    297 				sizeof (trace_type_t *)), MDL);
    298 		if (!vec)
    299 			return;
    300 		memset (&vec [trace_type_max], 0,
    301 			(sizeof (trace_type_t *)) * delta);
    302 		trace_type_max += delta;
    303 		if (trace_types) {
    304 		    memcpy (vec, trace_types,
    305 			    trace_type_count * sizeof (trace_type_t *));
    306 		    dfree (trace_types, MDL);
    307 		}
    308 		trace_types = vec;
    309 	}
    310 	trace_types [tptr -> index] = tptr;
    311 	if (tptr -> index >= trace_type_count)
    312 		trace_type_count = tptr -> index + 1;
    313 }
    314 
    315 trace_type_t *trace_type_register (const char *name,
    316 				   void *baggage,
    317 				   void (*have_packet) (trace_type_t *,
    318 							unsigned, char *),
    319 				   void (*stop_tracing) (trace_type_t *),
    320 				   const char *file, int line)
    321 {
    322 	trace_type_t *ttmp;
    323 	unsigned slen = strlen (name);
    324 	isc_result_t status;
    325 
    326 	ttmp = dmalloc (sizeof *ttmp, file, line);
    327 	if (!ttmp)
    328 		return ttmp;
    329 	ttmp -> index = -1;
    330 	ttmp -> name = dmalloc (slen + 1, file, line);
    331 	if (!ttmp -> name) {
    332 		dfree (ttmp, file, line);
    333 		return (trace_type_t *)0;
    334 	}
    335 	strcpy (ttmp -> name, name);
    336 	ttmp -> have_packet = have_packet;
    337 	ttmp -> stop_tracing = stop_tracing;
    338 
    339 	if (traceoutfile) {
    340 		status = trace_type_record (ttmp, slen, file, line);
    341 		if (status != ISC_R_SUCCESS) {
    342 			dfree (ttmp -> name, file, line);
    343 			dfree (ttmp, file, line);
    344 			return (trace_type_t *)0;
    345 		}
    346 	} else {
    347 		ttmp -> next = new_trace_types;
    348 		new_trace_types = ttmp;
    349 	}
    350 
    351 	return ttmp;
    352 }
    353 
    354 static isc_result_t trace_type_record (trace_type_t *ttmp, unsigned slen,
    355 				       const char *file, int line)
    356 {
    357 	trace_index_mapping_t *tim;
    358 	isc_result_t status;
    359 
    360 	tim = dmalloc (slen + TRACE_INDEX_MAPPING_SIZE, file, line);
    361 	if (!tim)
    362 		return ISC_R_NOMEMORY;
    363 	ttmp -> index = ++traceindex;
    364 	trace_type_stash (ttmp);
    365 	tim -> index = htonl (ttmp -> index);
    366 	memcpy (tim -> name, ttmp -> name, slen);
    367 	status = trace_write_packet (trace_types [0],
    368 				     slen + TRACE_INDEX_MAPPING_SIZE,
    369 				     (char *)tim, file, line);
    370 	dfree (tim, file, line);
    371 	return status;
    372 }
    373 
    374 /* Stop all registered trace types from trying to trace. */
    375 
    376 void trace_stop (void)
    377 {
    378 	int i;
    379 
    380 	for (i = 0; i < trace_type_count; i++)
    381 		if (trace_types [i] -> stop_tracing)
    382 			(*(trace_types [i] -> stop_tracing))
    383 				(trace_types [i]);
    384 	tracing_stopped = 1;
    385 }
    386 
    387 void trace_index_map_input (trace_type_t *ttype, unsigned length, char *buf)
    388 {
    389 	trace_index_mapping_t *tmap;
    390 	unsigned len;
    391 	trace_type_t *tptr, **prev;
    392 
    393 	if (length < TRACE_INDEX_MAPPING_SIZE) {
    394 		log_error ("short trace index mapping");
    395 		return;
    396 	}
    397 	tmap = (trace_index_mapping_t *)buf;
    398 
    399 	prev = &new_trace_types;
    400 	for (tptr = new_trace_types; tptr; tptr = tptr -> next) {
    401 		len = strlen (tptr -> name);
    402 		if (len == length - TRACE_INDEX_MAPPING_SIZE &&
    403 		    !memcmp (tptr -> name, tmap -> name, len)) {
    404 			tptr -> index = ntohl (tmap -> index);
    405 			trace_type_stash (tptr);
    406 			*prev = tptr -> next;
    407 			return;
    408 		}
    409 		prev = &tptr -> next;
    410 	}
    411 
    412 	log_error ("No registered trace type for type name %.*s",
    413 		   (int)length - TRACE_INDEX_MAPPING_SIZE, tmap -> name);
    414 	return;
    415 }
    416 
    417 void trace_index_stop_tracing (trace_type_t *ttype) { }
    418 
    419 void trace_replay_init (void)
    420 {
    421 	trace_playback_flag = 1;
    422 }
    423 
    424 void trace_file_replay (const char *filename)
    425 {
    426 	tracepacket_t *tpkt = NULL;
    427 	int status;
    428 	char *buf = NULL;
    429 	unsigned buflen;
    430 	unsigned bufmax = 0;
    431 	trace_type_t *ttype = NULL;
    432 	isc_result_t result;
    433 	int len;
    434 
    435 	traceinfile = fopen (filename, "r");
    436 	if (!traceinfile) {
    437 		log_error("Can't open tracefile %s: %m", filename);
    438 		return;
    439 	}
    440 #if defined (HAVE_SETFD)
    441 	if (fcntl (fileno(traceinfile), F_SETFD, 1) < 0)
    442 		log_error("Can't set close-on-exec on %s: %m", filename);
    443 #endif
    444 	status = fread(&tracefile_header, 1,
    445 		       sizeof tracefile_header, traceinfile);
    446 	if (status < sizeof tracefile_header) {
    447 		if (ferror(traceinfile))
    448 			log_error("Error reading trace file header: %m");
    449 		else
    450 			log_error("Short read on trace file header: %d %ld.",
    451 				  status, (long)(sizeof tracefile_header));
    452 		goto out;
    453 	}
    454 	tracefile_header.magic = ntohl(tracefile_header.magic);
    455 	tracefile_header.version = ntohl(tracefile_header.version);
    456 	tracefile_header.hlen = ntohl(tracefile_header.hlen);
    457 	tracefile_header.phlen = ntohl(tracefile_header.phlen);
    458 
    459 	if (tracefile_header.magic != TRACEFILE_MAGIC) {
    460 		log_error("%s: not a dhcp trace file.", filename);
    461 		goto out;
    462 	}
    463 	if (tracefile_header.version > TRACEFILE_VERSION) {
    464 		log_error ("tracefile version %ld > current %ld.",
    465 			   (long int)tracefile_header.version,
    466 			   (long int)TRACEFILE_VERSION);
    467 		goto out;
    468 	}
    469 	if (tracefile_header.phlen < sizeof *tpkt) {
    470 		log_error("tracefile packet size too small - %ld < %ld",
    471 			  (long int)tracefile_header.phlen,
    472 			  (long int)sizeof *tpkt);
    473 		goto out;
    474 	}
    475 	len = (sizeof tracefile_header) - tracefile_header.hlen;
    476 	if (len < 0) {
    477 		log_error("tracefile header size too small - %ld < %ld",
    478 			  (long int)tracefile_header.hlen,
    479 			  (long int)sizeof tracefile_header);
    480 		goto out;
    481 	}
    482 	if (len > 0) {
    483 		status = fseek(traceinfile, (long)len, SEEK_CUR);
    484 		if (status < 0) {
    485 			log_error("can't seek past header: %m");
    486 			goto out;
    487 		}
    488 	}
    489 
    490 	tpkt = dmalloc((unsigned)tracefile_header.phlen, MDL);
    491 	if (tpkt == NULL) {
    492 		log_error ("can't allocate trace packet header.");
    493 		goto out;
    494 	}
    495 
    496 	while ((result = trace_get_next_packet(&ttype, tpkt, &buf, &buflen,
    497 					       &bufmax)) == ISC_R_SUCCESS) {
    498 	    (*ttype->have_packet)(ttype, tpkt->length, buf);
    499 	    ttype = NULL;
    500 	}
    501       out:
    502 	fclose(traceinfile);
    503 	if (buf != NULL)
    504 		dfree(buf, MDL);
    505 	if (tpkt != NULL)
    506 		dfree(tpkt, MDL);
    507 }
    508 
    509 /* Get the next packet from the file.   If ttp points to a nonzero pointer
    510    to a trace type structure, check the next packet to see if it's of the
    511    expected type, and back off if not. */
    512 
    513 isc_result_t trace_get_next_packet (trace_type_t **ttp,
    514 				    tracepacket_t *tpkt,
    515 				    char **buf, unsigned *buflen,
    516 				    unsigned *bufmax)
    517 {
    518 	trace_type_t *ttype;
    519 	unsigned paylen;
    520 	int status, curposok = 0;
    521 	fpos_t curpos;
    522 
    523 	while(1) {
    524 		curposok = 0;
    525 		status = fgetpos(traceinfile, &curpos);
    526 		if (status < 0) {
    527 			log_error("Can't save tracefile position: %m");
    528 		} else {
    529 			curposok = 1;
    530 		}
    531 
    532 		status = fread(tpkt, 1, (size_t)tracefile_header.phlen,
    533 			       traceinfile);
    534 		if (status < tracefile_header.phlen) {
    535 			if (ferror(traceinfile))
    536 				log_error("Error reading trace packet header: "
    537 					  "%m");
    538 			else if (status == 0)
    539 				return ISC_R_EOF;
    540 			else
    541 				log_error ("Short read on trace packet header:"
    542 					   " %ld %ld.",
    543 					   (long int)status,
    544 					   (long int)tracefile_header.phlen);
    545 			return DHCP_R_PROTOCOLERROR;
    546 		}
    547 
    548 		/* Swap the packet. */
    549 		tpkt->type_index = ntohl(tpkt -> type_index);
    550 		tpkt->length = ntohl(tpkt -> length);
    551 		tpkt->when = ntohl(tpkt -> when);
    552 
    553 		/* See if there's a handler for this packet type. */
    554 		if (tpkt->type_index < trace_type_count &&
    555 		    trace_types[tpkt->type_index])
    556 			ttype = trace_types[tpkt->type_index];
    557 		else {
    558 			log_error ("Trace packet with unknown index %ld",
    559 				   (long int)tpkt->type_index);
    560 			return DHCP_R_PROTOCOLERROR;
    561 		}
    562 
    563 		/*
    564 		 * Determine if we should try to expire any timer events.
    565 		 * We do so if:
    566 		 *   we aren't looking for a specific type of packet
    567 		 *   we have a hook to use to update the timer
    568 		 *   the timestamp on the packet doesn't match the current time
    569 		 * When we do so we rewind the file to the beginning of this
    570 		 * packet and then try for a new packet.  This allows
    571 		 * any code triggered by a timeout to get the current packet
    572 		 * while we get the next one.
    573 		 */
    574 
    575 		if ((ttp != NULL) && (*ttp == NULL) &&
    576 		    (tpkt->when != cur_tv.tv_sec) &&
    577 		    (trace_set_time_hook != NULL)) {
    578 			if (curposok == 0) {
    579 				log_error("no curpos for fsetpos in "
    580 					  "tracefile");
    581 				return DHCP_R_PROTOCOLERROR;
    582 			}
    583 
    584 			status = fsetpos(traceinfile, &curpos);
    585 			if (status < 0) {
    586 				log_error("fsetpos in tracefile failed: %m");
    587 				return DHCP_R_PROTOCOLERROR;
    588 			}
    589 
    590 			(*trace_set_time_hook) (tpkt->when);
    591 			continue;
    592 		}
    593 		break;
    594 	}
    595 
    596 	/* If we were supposed to get a particular kind of packet,
    597 	   check to see that we got the right kind. */
    598 	if (ttp && *ttp && ttype != *ttp) {
    599 		log_error ("Read packet type %s when expecting %s",
    600 			   ttype -> name, (*ttp) -> name);
    601 		status = fsetpos (traceinfile, &curpos);
    602 		if (status < 0) {
    603 			log_error ("fsetpos in tracefile failed: %m");
    604 			return DHCP_R_PROTOCOLERROR;
    605 		}
    606 		return ISC_R_UNEXPECTEDTOKEN;
    607 	}
    608 
    609 	paylen = tpkt -> length;
    610 	if (paylen % 8)
    611 		paylen += 8 - (tpkt -> length % 8);
    612 
    613 	/* allocate a buffer if we need one or current buffer is too small */
    614 	if ((*buf == NULL) || (paylen > (*bufmax))) {
    615 		if ((*buf))
    616 			dfree ((*buf), MDL);
    617 		(*bufmax) = ((paylen + 1023) & ~1023U);
    618 		(*buf) = dmalloc ((*bufmax), MDL);
    619 		if (!(*buf)) {
    620 			log_error ("Can't allocate input buffer sized %d",
    621 				   (*bufmax));
    622 			return ISC_R_NOMEMORY;
    623 		}
    624 	}
    625 
    626 	status = fread ((*buf), 1, paylen, traceinfile);
    627 	if (status < paylen) {
    628 		if (ferror (traceinfile))
    629 			log_error ("Error reading trace payload: %m");
    630 		else
    631 			log_error ("Short read on trace payload: %d %d.",
    632 				   status, paylen);
    633 		return DHCP_R_PROTOCOLERROR;
    634 	}
    635 
    636 	/* Store the actual length of the payload. */
    637 	*buflen = tpkt -> length;
    638 
    639 	if (ttp)
    640 		*ttp = ttype;
    641 	return ISC_R_SUCCESS;
    642 }
    643 
    644 isc_result_t trace_get_packet (trace_type_t **ttp,
    645 			       unsigned *buflen, char **buf)
    646 {
    647 	tracepacket_t *tpkt;
    648 	unsigned bufmax = 0;
    649 	isc_result_t status;
    650 
    651 	if (!buf || *buf)
    652 		return DHCP_R_INVALIDARG;
    653 
    654 	tpkt = dmalloc ((unsigned)tracefile_header.phlen, MDL);
    655 	if (!tpkt) {
    656 		log_error ("can't allocate trace packet header.");
    657 		return ISC_R_NOMEMORY;
    658 	}
    659 
    660 	status = trace_get_next_packet (ttp, tpkt, buf, buflen, &bufmax);
    661 
    662 	dfree (tpkt, MDL);
    663 	return status;
    664 }
    665 
    666 /* Get a packet from the trace input file that contains a file with the
    667    specified name.   We don't hunt for the packet - it should be the next
    668    packet in the tracefile.   If it's not, or something else bad happens,
    669    return an error code. */
    670 
    671 isc_result_t trace_get_file (trace_type_t *ttype,
    672 			     const char *filename, unsigned *len, char **buf)
    673 {
    674 	fpos_t curpos;
    675 	unsigned max = 0;
    676 	tracepacket_t *tpkt;
    677 	int status;
    678 	isc_result_t result;
    679 
    680 	/* Disallow some obvious bogosities. */
    681 	if (!buf || !len || *buf)
    682 		return DHCP_R_INVALIDARG;
    683 
    684 	/* Save file position in case of filename mismatch. */
    685 	status = fgetpos (traceinfile, &curpos);
    686 	if (status < 0)
    687 		log_error ("Can't save tracefile position: %m");
    688 
    689 	tpkt = dmalloc ((unsigned)tracefile_header.phlen, MDL);
    690 	if (!tpkt) {
    691 		log_error ("can't allocate trace packet header.");
    692 		return ISC_R_NOMEMORY;
    693 	}
    694 
    695 	result = trace_get_next_packet (&ttype, tpkt, buf, len, &max);
    696 	/* done with tpkt, free it */
    697 	dfree (tpkt, MDL);
    698 	if (result != ISC_R_SUCCESS) {
    699 		if (*buf) {
    700 			dfree (*buf, MDL);
    701 			*buf = NULL;
    702 		}
    703 		return result;
    704 	}
    705 
    706 	/* Make sure the filename is right. */
    707 	if (strcmp (filename, *buf)) {
    708 		log_error ("Read file %s when expecting %s", *buf, filename);
    709 		dfree (*buf, MDL);
    710 		*buf = NULL;
    711 
    712 		status = fsetpos (traceinfile, &curpos);
    713 		if (status < 0) {
    714 			log_error ("fsetpos in tracefile failed: %m");
    715 			return DHCP_R_PROTOCOLERROR;
    716 		}
    717 		return ISC_R_UNEXPECTEDTOKEN;
    718 	}
    719 
    720 	return ISC_R_SUCCESS;
    721 }
    722 #endif /* TRACING */
    723