summaryrefslogtreecommitdiff
path: root/omapip/trace.c
blob: 23e4e50606ede8183802332a97c02ce1f966ec3a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
/* trace.c

   Subroutines that support tracing of OMAPI wire transactions and
   provide a mechanism for programs using OMAPI to trace their own
   transactions... */

/*
 * Copyright (c) 2012,2014 by Internet Systems Consortium, Inc. ("ISC")
 * Copyright (c) 2009-2010 by Internet Systems Consortium, Inc. ("ISC")
 * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC")
 * Copyright (c) 2001-2003 by Internet Software Consortium
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 *   Internet Systems Consortium, Inc.
 *   950 Charter Street
 *   Redwood City, CA 94063
 *   <info@isc.org>
 *   https://www.isc.org/
 *
 */

#include "dhcpd.h"
#include <omapip/omapip_p.h>
#include <errno.h>

#if defined (TRACING)
void (*trace_set_time_hook) (TIME);
static int tracing_stopped;
static int traceoutfile;
static int traceindex;
static trace_type_t **trace_types;
static int trace_type_count;
static int trace_type_max;
static trace_type_t *new_trace_types;
static FILE *traceinfile;
static tracefile_header_t tracefile_header;
static int trace_playback_flag;
trace_type_t trace_time_marker;

#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
extern omapi_array_t *trace_listeners;
extern omapi_array_t *omapi_connections;

extern int errno;

void trace_free_all ()
{
	trace_type_t *tp;
	int i;
	tp = new_trace_types;
	while (tp) {
		new_trace_types = tp -> next;
		if (tp -> name) {
			dfree (tp -> name, MDL);
			tp -> name = (char *)0;
		}
		dfree (tp, MDL);
		tp = new_trace_types;
	}
	for (i = 0; i < trace_type_count; i++) {
		if (trace_types [i]) {
			if (trace_types [i] -> name)
				dfree (trace_types [i] -> name, MDL);
			dfree (trace_types [i], MDL);
		}
	}
	dfree (trace_types, MDL);
	trace_types = (trace_type_t **)0;
	trace_type_count = trace_type_max = 0;

	omapi_array_free (&trace_listeners, MDL);
	omapi_array_free (&omapi_connections, MDL);
}
#endif

static isc_result_t trace_type_record (trace_type_t *,
				       unsigned, const char *, int);

int trace_playback ()
{
	return trace_playback_flag;
}

int trace_record ()
{
	if (traceoutfile && !tracing_stopped)
		return 1;
	return 0;
}

isc_result_t trace_init (void (*set_time) (TIME),
			 const char *file, int line)
{
	trace_type_t *root_type;
	static int root_setup = 0;

	if (root_setup)
		return ISC_R_SUCCESS;

	trace_set_time_hook = set_time;

	root_type = trace_type_register ("trace-index-mapping",
					 (void *)0, trace_index_map_input,
					 trace_index_stop_tracing, file, line);
	if (!root_type)
		return ISC_R_UNEXPECTED;
	if (new_trace_types == root_type)
		new_trace_types = new_trace_types -> next;
	root_type -> index = 0;
	trace_type_stash (root_type);

	root_setup = 1;
	return ISC_R_SUCCESS;
}

isc_result_t trace_begin (const char *filename,
			  const char *file, int line)
{
	tracefile_header_t tfh;
	int status;
	trace_type_t *tptr, *next;
	isc_result_t result;

	if (traceoutfile) {
		log_error ("%s(%d): trace_begin called twice",
			   file, line);
		return DHCP_R_INVALIDARG;
	}

	traceoutfile = open (filename, O_CREAT | O_WRONLY | O_EXCL, 0600);
	if (traceoutfile < 0 && errno == EEXIST) {
		log_error ("WARNING: Overwriting trace file \"%s\"", filename);
		traceoutfile = open (filename, O_WRONLY | O_EXCL | O_TRUNC,
				     0600);
	}

	if (traceoutfile < 0) {
		log_error ("%s(%d): trace_begin: %s: %m",
			   file, line, filename);
		return ISC_R_UNEXPECTED;
	}
#if defined (HAVE_SETFD)
	if (fcntl (traceoutfile, F_SETFD, 1) < 0)
		log_error ("Can't set close-on-exec on %s: %m", filename);
#endif

	tfh.magic = htonl (TRACEFILE_MAGIC);
	tfh.version = htonl (TRACEFILE_VERSION);
	tfh.hlen = htonl (sizeof (tracefile_header_t));
	tfh.phlen = htonl (sizeof (tracepacket_t));
	
	status = write (traceoutfile, &tfh, sizeof tfh);
	if (status < 0) {
		log_error ("%s(%d): trace_begin write failed: %m", file, line);
		return ISC_R_UNEXPECTED;
	} else if (status != sizeof tfh) {
		log_error ("%s(%d): trace_begin: short write (%d:%ld)",
			   file, line, status, (long)(sizeof tfh));
		trace_stop ();
		return ISC_R_UNEXPECTED;
	}

	/* Stash all the types that have already been set up. */
	if (new_trace_types) {
		next = new_trace_types;
		new_trace_types = (trace_type_t *)0;
		for (tptr = next; tptr; tptr = next) {
			next = tptr -> next;
			if (tptr -> index != 0) {
				result = (trace_type_record
					  (tptr,
					   strlen (tptr -> name), file, line));
				if (result != ISC_R_SUCCESS)
					return status;
			}
		}
	}
	
	return ISC_R_SUCCESS;
}

isc_result_t trace_write_packet (trace_type_t *ttype, unsigned length,
				 const char *buf, const char *file, int line)
{
	trace_iov_t iov;

	iov.buf = buf;
	iov.len = length;
	return trace_write_packet_iov (ttype, 1, &iov, file, line);
}

isc_result_t trace_write_packet_iov (trace_type_t *ttype,
				     int count, trace_iov_t *iov,
				     const char *file, int line)
{
	tracepacket_t tmp;
	int status;
	int i;
	int length;

	/* Really shouldn't get called here, but it may be hard to turn off
	   tracing midstream if the trace file write fails or something. */
	if (tracing_stopped)
		return 0;

	if (!ttype) {
		log_error ("%s(%d): trace_write_packet with null trace type",
			   file ? file : "<unknown file>", line);
		return DHCP_R_INVALIDARG;
	}
	if (!traceoutfile) {
		log_error ("%s(%d): trace_write_packet with no tracefile.",
			   file ? file : "<unknown file>", line);
		return DHCP_R_INVALIDARG;
	}
	
	/* Compute the total length of the iov. */
	length = 0;
	for (i = 0; i < count; i++)
		length += iov [i].len;

	/* We have to swap out the data, because it may be read back on a
	   machine of different endianness. */
	memset(&tmp, 0, sizeof(tmp));
	tmp.type_index = htonl (ttype -> index);
	tmp.when = htonl (time ((time_t *)0)); /* XXX */
	tmp.length = htonl (length);

	status = write (traceoutfile, &tmp, sizeof tmp);
	if (status < 0) {
		log_error ("%s(%d): trace_write_packet write failed: %m",
			   file, line);
		return ISC_R_UNEXPECTED;
	} else if (status != sizeof tmp) {
		log_error ("%s(%d): trace_write_packet: short write (%d:%ld)",
			   file, line, status, (long)(sizeof tmp));
		trace_stop ();
	}

	for (i = 0; i < count; i++) {
		status = write (traceoutfile, iov [i].buf, iov [i].len);
		if (status < 0) {
			log_error ("%s(%d): %s write failed: %m",
				   file, line, "trace_write_packet");
			return ISC_R_UNEXPECTED;
		} else if (status != iov [i].len) {
			log_error ("%s(%d): %s: short write (%d:%d)",
				   file, line,
				   "trace_write_packet", status, length);
			trace_stop ();
		}
	}

	/* Write padding on the end of the packet to align the next
	   packet to an 8-byte boundary.   This is in case we decide to
	   use mmap in some clever way later on. */
	if (length % 8) {
	    static char zero [] = { 0, 0, 0, 0, 0, 0, 0 };
	    unsigned padl = 8 - (length % 8);
		
	    status = write (traceoutfile, zero, padl);
	    if (status < 0) {
		log_error ("%s(%d): trace_write_packet write failed: %m",
			   file, line);
		return ISC_R_UNEXPECTED;
	    } else if (status != padl) {
		log_error ("%s(%d): trace_write_packet: short write (%d:%d)",
			   file, line, status, padl);
		trace_stop ();
	    }
	}

	return ISC_R_SUCCESS;
}

void trace_type_stash (trace_type_t *tptr)
{
	trace_type_t **vec;
	int delta;
	if (trace_type_max <= tptr -> index) {
		delta = tptr -> index - trace_type_max + 10;
		vec = dmalloc (((trace_type_max + delta) *
				sizeof (trace_type_t *)), MDL);
		if (!vec)
			return;
		memset (&vec [trace_type_max], 0,
			(sizeof (trace_type_t *)) * delta);
		trace_type_max += delta;
		if (trace_types) {
		    memcpy (vec, trace_types,
			    trace_type_count * sizeof (trace_type_t *));
		    dfree (trace_types, MDL);
		}
		trace_types = vec;
	}
	trace_types [tptr -> index] = tptr;
	if (tptr -> index >= trace_type_count)
		trace_type_count = tptr -> index + 1;
}

trace_type_t *trace_type_register (const char *name,
				   void *baggage,
				   void (*have_packet) (trace_type_t *,
							unsigned, char *),
				   void (*stop_tracing) (trace_type_t *),
				   const char *file, int line)
{
	trace_type_t *ttmp;
	unsigned slen = strlen (name);
	isc_result_t status;

	ttmp = dmalloc (sizeof *ttmp, file, line);
	if (!ttmp)
		return ttmp;
	ttmp -> index = -1;
	ttmp -> name = dmalloc (slen + 1, file, line);
	if (!ttmp -> name) {
		dfree (ttmp, file, line);
		return (trace_type_t *)0;
	}
	strcpy (ttmp -> name, name);
	ttmp -> have_packet = have_packet;
	ttmp -> stop_tracing = stop_tracing;
	
	if (traceoutfile) {
		status = trace_type_record (ttmp, slen, file, line);
		if (status != ISC_R_SUCCESS) {
			dfree (ttmp -> name, file, line);
			dfree (ttmp, file, line);
			return (trace_type_t *)0;
		}
	} else {
		ttmp -> next = new_trace_types;
		new_trace_types = ttmp;
	}

	return ttmp;
}
						   
static isc_result_t trace_type_record (trace_type_t *ttmp, unsigned slen,
				       const char *file, int line)
{
	trace_index_mapping_t *tim;
	isc_result_t status;

	tim = dmalloc (slen + TRACE_INDEX_MAPPING_SIZE, file, line);
	if (!tim)
		return ISC_R_NOMEMORY;
	ttmp -> index = ++traceindex;
	trace_type_stash (ttmp);
	tim -> index = htonl (ttmp -> index);
	memcpy (tim -> name, ttmp -> name, slen);
	status = trace_write_packet (trace_types [0],
				     slen + TRACE_INDEX_MAPPING_SIZE,
				     (char *)tim, file, line);
	dfree (tim, file, line);
	return status;
}

/* Stop all registered trace types from trying to trace. */

void trace_stop (void)
{
	int i;

	for (i = 0; i < trace_type_count; i++)
		if (trace_types [i] -> stop_tracing)
			(*(trace_types [i] -> stop_tracing))
				(trace_types [i]);
	tracing_stopped = 1;
}

void trace_index_map_input (trace_type_t *ttype, unsigned length, char *buf)
{
	trace_index_mapping_t *tmap;
	unsigned len;
	trace_type_t *tptr, **prev;

	if (length < TRACE_INDEX_MAPPING_SIZE) {
		log_error ("short trace index mapping");
		return;
	}
	tmap = (trace_index_mapping_t *)buf;

	prev = &new_trace_types;
	for (tptr = new_trace_types; tptr; tptr = tptr -> next) {
		len = strlen (tptr -> name);
		if (len == length - TRACE_INDEX_MAPPING_SIZE &&
		    !memcmp (tptr -> name, tmap -> name, len)) {
			tptr -> index = ntohl (tmap -> index);
			trace_type_stash (tptr);
			*prev = tptr -> next;
			return;
		}
		prev = &tptr -> next;
	}
	
	log_error ("No registered trace type for type name %.*s",
		   (int)length - TRACE_INDEX_MAPPING_SIZE, tmap -> name);
	return;
}

void trace_index_stop_tracing (trace_type_t *ttype) { }

void trace_replay_init (void)
{
	trace_playback_flag = 1;
}

void trace_file_replay (const char *filename)
{
	tracepacket_t *tpkt = NULL;
	int status;
	char *buf = NULL;
	unsigned buflen;
	unsigned bufmax = 0;
	trace_type_t *ttype = NULL;
	isc_result_t result;
	int len;

	traceinfile = fopen (filename, "r");
	if (!traceinfile) {
		log_error("Can't open tracefile %s: %m", filename);
		return;
	}
#if defined (HAVE_SETFD)
	if (fcntl (fileno(traceinfile), F_SETFD, 1) < 0)
		log_error("Can't set close-on-exec on %s: %m", filename);
#endif
	status = fread(&tracefile_header, 1,
		       sizeof tracefile_header, traceinfile);
	if (status < sizeof tracefile_header) {
		if (ferror(traceinfile))
			log_error("Error reading trace file header: %m");
		else
			log_error("Short read on trace file header: %d %ld.",
				  status, (long)(sizeof tracefile_header));
		goto out;
	}
	tracefile_header.magic = ntohl(tracefile_header.magic);
	tracefile_header.version = ntohl(tracefile_header.version);
	tracefile_header.hlen = ntohl(tracefile_header.hlen);
	tracefile_header.phlen = ntohl(tracefile_header.phlen);

	if (tracefile_header.magic != TRACEFILE_MAGIC) {
		log_error("%s: not a dhcp trace file.", filename);
		goto out;
	}
	if (tracefile_header.version > TRACEFILE_VERSION) {
		log_error ("tracefile version %ld > current %ld.",
			   (long int)tracefile_header.version,
			   (long int)TRACEFILE_VERSION);
		goto out;
	}
	if (tracefile_header.phlen < sizeof *tpkt) {
		log_error("tracefile packet size too small - %ld < %ld",
			  (long int)tracefile_header.phlen,
			  (long int)sizeof *tpkt);
		goto out;
	}
	len = (sizeof tracefile_header) - tracefile_header.hlen;
	if (len < 0) {
		log_error("tracefile header size too small - %ld < %ld",
			  (long int)tracefile_header.hlen,
			  (long int)sizeof tracefile_header);
		goto out;
	}
	if (len > 0) {
		status = fseek(traceinfile, (long)len, SEEK_CUR);
		if (status < 0) {
			log_error("can't seek past header: %m");
			goto out;
		}
	}

	tpkt = dmalloc((unsigned)tracefile_header.phlen, MDL);
	if (tpkt == NULL) {
		log_error ("can't allocate trace packet header.");
		goto out;
	}

	while ((result = trace_get_next_packet(&ttype, tpkt, &buf, &buflen,
					       &bufmax)) == ISC_R_SUCCESS) {
	    (*ttype->have_packet)(ttype, tpkt->length, buf);
	    ttype = NULL;
	}
      out:
	fclose(traceinfile);
	if (buf != NULL)
		dfree(buf, MDL);
	if (tpkt != NULL)
		dfree(tpkt, MDL);
}

/* Get the next packet from the file.   If ttp points to a nonzero pointer
   to a trace type structure, check the next packet to see if it's of the
   expected type, and back off if not. */

isc_result_t trace_get_next_packet (trace_type_t **ttp,
				    tracepacket_t *tpkt,
				    char **buf, unsigned *buflen,
				    unsigned *bufmax)
{
	trace_type_t *ttype;
	unsigned paylen;
	int status, curposok = 0;
	fpos_t curpos;

	while(1) {
		curposok = 0;
		status = fgetpos(traceinfile, &curpos);
		if (status < 0) {
			log_error("Can't save tracefile position: %m");
		} else {
			curposok = 1;
		}

		status = fread(tpkt, 1, (size_t)tracefile_header.phlen,
			       traceinfile);
		if (status < tracefile_header.phlen) {
			if (ferror(traceinfile))
				log_error("Error reading trace packet header: "
					  "%m");
			else if (status == 0)
				return ISC_R_EOF;
			else
				log_error ("Short read on trace packet header:"
					   " %ld %ld.",
					   (long int)status,
					   (long int)tracefile_header.phlen);
			return DHCP_R_PROTOCOLERROR;
		}

		/* Swap the packet. */
		tpkt->type_index = ntohl(tpkt -> type_index);
		tpkt->length = ntohl(tpkt -> length);
		tpkt->when = ntohl(tpkt -> when);
	
		/* See if there's a handler for this packet type. */
		if (tpkt->type_index < trace_type_count &&
		    trace_types[tpkt->type_index])
			ttype = trace_types[tpkt->type_index];
		else {
			log_error ("Trace packet with unknown index %ld",
				   (long int)tpkt->type_index);
			return DHCP_R_PROTOCOLERROR;
		}

		/*
		 * Determine if we should try to expire any timer events.
		 * We do so if:
		 *   we aren't looking for a specific type of packet
		 *   we have a hook to use to update the timer
		 *   the timestamp on the packet doesn't match the current time
		 * When we do so we rewind the file to the beginning of this
		 * packet and then try for a new packet.  This allows
		 * any code triggered by a timeout to get the current packet
		 * while we get the next one.
		 */

		if ((ttp != NULL) && (*ttp == NULL) &&
		    (tpkt->when != cur_tv.tv_sec) &&
		    (trace_set_time_hook != NULL)) {
			if (curposok == 0) {
				log_error("no curpos for fsetpos in "
					  "tracefile");
				return DHCP_R_PROTOCOLERROR;
			}
				
			status = fsetpos(traceinfile, &curpos);
			if (status < 0) {
				log_error("fsetpos in tracefile failed: %m");
				return DHCP_R_PROTOCOLERROR;
			}

			(*trace_set_time_hook) (tpkt->when);
			continue;
		}
		break;
	}

	/* If we were supposed to get a particular kind of packet,
	   check to see that we got the right kind. */
	if (ttp && *ttp && ttype != *ttp) {
		log_error ("Read packet type %s when expecting %s",
			   ttype -> name, (*ttp) -> name);
		status = fsetpos (traceinfile, &curpos);
		if (status < 0) {
			log_error ("fsetpos in tracefile failed: %m");
			return DHCP_R_PROTOCOLERROR;
		}
		return ISC_R_UNEXPECTEDTOKEN;
	}

	paylen = tpkt -> length;
	if (paylen % 8)
		paylen += 8 - (tpkt -> length % 8);
	if (paylen > (*bufmax)) {
		if ((*buf))
			dfree ((*buf), MDL);
		(*bufmax) = ((paylen + 1023) & ~1023U);
		(*buf) = dmalloc ((*bufmax), MDL);
		if (!(*buf)) {
			log_error ("Can't allocate input buffer sized %d",
				   (*bufmax));
			return ISC_R_NOMEMORY;
		}
	}
	
	status = fread ((*buf), 1, paylen, traceinfile);
	if (status < paylen) {
		if (ferror (traceinfile))
			log_error ("Error reading trace payload: %m");
		else
			log_error ("Short read on trace payload: %d %d.",
				   status, paylen);
		return DHCP_R_PROTOCOLERROR;
	}

	/* Store the actual length of the payload. */
	*buflen = tpkt -> length;

	if (ttp)
		*ttp = ttype;
	return ISC_R_SUCCESS;
}

isc_result_t trace_get_packet (trace_type_t **ttp,
			       unsigned *buflen, char **buf)
{
	tracepacket_t *tpkt;
	unsigned bufmax = 0;
	isc_result_t status;

	if (!buf || *buf)
		return DHCP_R_INVALIDARG;

	tpkt = dmalloc ((unsigned)tracefile_header.phlen, MDL);
	if (!tpkt) {
		log_error ("can't allocate trace packet header.");
		return ISC_R_NOMEMORY;
	}

	status = trace_get_next_packet (ttp, tpkt, buf, buflen, &bufmax);

	dfree (tpkt, MDL);
	return status;
}

/* Get a packet from the trace input file that contains a file with the
   specified name.   We don't hunt for the packet - it should be the next
   packet in the tracefile.   If it's not, or something else bad happens,
   return an error code. */

isc_result_t trace_get_file (trace_type_t *ttype,
			     const char *filename, unsigned *len, char **buf)
{
	fpos_t curpos;
	unsigned max = 0;
	tracepacket_t *tpkt;
	int status;
	isc_result_t result;

	/* Disallow some obvious bogosities. */
	if (!buf || !len || *buf)
		return DHCP_R_INVALIDARG;

	/* Save file position in case of filename mismatch. */
	status = fgetpos (traceinfile, &curpos);
	if (status < 0)
		log_error ("Can't save tracefile position: %m");

	tpkt = dmalloc ((unsigned)tracefile_header.phlen, MDL);
	if (!tpkt) {
		log_error ("can't allocate trace packet header.");
		return ISC_R_NOMEMORY;
	}

	result = trace_get_next_packet (&ttype, tpkt, buf, len, &max);
	/* done with tpkt, free it */
	dfree (tpkt, MDL);
	if (result != ISC_R_SUCCESS) {
		if (*buf) {
			dfree (*buf, MDL);
			*buf = NULL;
		}
		return result;
	}

	/* Make sure the filename is right. */
	if (strcmp (filename, *buf)) {
		log_error ("Read file %s when expecting %s", *buf, filename);
		dfree (*buf, MDL);
		*buf = NULL;

		status = fsetpos (traceinfile, &curpos);
		if (status < 0) {
			log_error ("fsetpos in tracefile failed: %m");
			return DHCP_R_PROTOCOLERROR;
		}
		return ISC_R_UNEXPECTEDTOKEN;
	}

	return ISC_R_SUCCESS;
}
#endif /* TRACING */