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