Line data Source code
1 : #include "fd_pcapng_private.h"
2 : #include "../fd_util.h"
3 :
4 : /* Capture related ****************************************************/
5 :
6 : #include <errno.h>
7 : #if defined(__linux__) || defined(__FreeBSD__)
8 : #include <net/if.h>
9 : #endif
10 :
11 : void
12 0 : fd_pcapng_shb_defaults( fd_pcapng_shb_opts_t * opt ) {
13 0 : # if FD_HAS_X86
14 0 : opt->hardware = "x86_64";
15 0 : # endif
16 :
17 0 : # if defined(__linux__)
18 0 : opt->os = "Linux";
19 0 : # endif
20 :
21 0 : opt->userappl = "Firedancer";
22 0 : }
23 :
24 : int
25 : fd_pcapng_idb_defaults( fd_pcapng_idb_opts_t * opt,
26 0 : uint if_idx ) {
27 0 : # if defined(__linux__) || defined(__FreeBSD__)
28 0 : static FD_TL char _name[ IF_NAMESIZE ];
29 0 : char * name = if_indextoname( if_idx, _name );
30 0 : if( FD_UNLIKELY( !name ) ) return 0;
31 0 : FD_STATIC_ASSERT( 16>=IF_NAMESIZE, ifname_sz );
32 0 : memcpy( opt->name, _name, 16UL );
33 : # else
34 : (void)if_idx;
35 : # endif
36 :
37 0 : opt->tsresol = FD_PCAPNG_TSRESOL_NS;
38 :
39 : /* TODO get ip4_addr, mac_addr, hardware from rtnetlink */
40 :
41 0 : return 1;
42 0 : }
43 :
44 : #if FD_HAS_HOSTED
45 :
46 : #include <stdio.h>
47 :
48 : /* Parsers ************************************************************/
49 :
50 : /* FIXME Option parsing spec violation
51 :
52 : https://www.ietf.org/archive/id/draft-ietf-opsawg-pcapng-00.html#name-options
53 :
54 : > Code that reads pcapng files MUST NOT assume an option list will
55 : have an opt_endofopt option at the end; it MUST also check for the
56 : end of the block, and SHOULD treat blocks where the option list has
57 : no opt_endofopt option as if the option list had an opt_endofopt
58 : option at the end.
59 :
60 : This parser currently does not handle missing opt_endofopt */
61 :
62 : FD_FN_CONST ulong
63 3 : fd_pcapng_iter_align( void ) {
64 3 : return alignof(fd_pcapng_iter_t);
65 3 : }
66 :
67 : FD_FN_CONST ulong
68 3 : fd_pcapng_iter_footprint( void ) {
69 3 : return sizeof(fd_pcapng_iter_t);
70 3 : }
71 :
72 : static char const *
73 : fd_pcapng_iter_strerror( int error,
74 0 : FILE * file ) {
75 0 : static FD_TL char err_cstr_buf[ 1024UL ];
76 0 : char * err_cstr = fd_cstr_init( err_cstr_buf );
77 0 : if( error==EPROTO ) {
78 0 : return fd_cstr_printf( err_cstr, sizeof(err_cstr_buf), NULL, "parse error at %#lx", (ulong)ftell(file) );
79 0 : } else if( error==-1 && !feof( file ) ) {
80 0 : return "end of section";
81 0 : } else {
82 0 : return fd_cstr_printf( err_cstr, sizeof(err_cstr_buf), NULL, "%i-%s", error, fd_io_strerror( error ) );
83 0 : }
84 0 : }
85 :
86 : static int
87 : fd_pcapng_peek_block( FILE * stream,
88 : fd_pcapng_block_hdr_t * _hdr,
89 21 : long * end_ptr ) {
90 :
91 : /* Remember offset of block */
92 21 : long pos = ftell( stream );
93 21 : if( FD_UNLIKELY( pos<0L ) )
94 0 : return ferror( stream );
95 21 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)pos, 4U ) ) ) {
96 0 : FD_LOG_DEBUG(( "pcapng: misaligned stream at %#lx", (ulong)pos ));
97 0 : return EPROTO;
98 0 : }
99 :
100 : /* Read header */
101 21 : fd_pcapng_block_hdr_t hdr;
102 21 : if( FD_UNLIKELY( 1UL!=fread( &hdr, sizeof(fd_pcapng_block_hdr_t), 1, stream ) ) ) {
103 3 : if( FD_LIKELY( feof( stream ) ) ) return -1; /* eof */
104 0 : else return ferror( stream );
105 3 : }
106 :
107 : /* Coherence check length field */
108 18 : if( FD_UNLIKELY( (hdr.block_sz < 12U) /* header and footer are mandatory */
109 18 : | (hdr.block_sz >32768U) /* way too large */
110 18 : | (!fd_ulong_is_aligned( hdr.block_sz, 4U )) ) ) {
111 0 : FD_LOG_DEBUG(( "pcapng: block with invalid size %#x at %#lx", hdr.block_sz, (ulong)pos ));
112 0 : return EPROTO;
113 0 : }
114 :
115 : /* Seek to block footer */
116 18 : if( FD_UNLIKELY( 0!=fseek( stream, (long)(hdr.block_sz - 12U), SEEK_CUR ) ) )
117 0 : return errno;
118 :
119 : /* Read footer */
120 18 : uint block_sz;
121 18 : if( FD_UNLIKELY( 1UL!=fread( &block_sz, sizeof(uint), 1, stream ) ) )
122 0 : return ferror( stream );
123 :
124 : /* Restore cursor */
125 18 : if( FD_UNLIKELY( 0!=fseek( stream, pos, SEEK_SET ) ) )
126 0 : return errno;
127 :
128 : /* Check that header and footer match */
129 18 : if( FD_UNLIKELY( hdr.block_sz != block_sz ) ) {
130 0 : FD_LOG_DEBUG(( "pcapng: block size in header and footer don't match at %#lx", (ulong)pos ));
131 0 : return EPROTO;
132 0 : }
133 :
134 18 : *_hdr = hdr;
135 18 : if( end_ptr ) *end_ptr = pos + (long)fd_uint_align_up( hdr.block_sz, 4U );
136 :
137 18 : return 0; /* success */
138 18 : }
139 :
140 : static int
141 : fd_pcapng_read_option( FILE * stream,
142 15 : fd_pcapng_option_t * opt ) {
143 :
144 15 : struct __attribute__((packed)) {
145 15 : ushort type;
146 15 : ushort sz;
147 15 : } opt_hdr;
148 :
149 15 : if( FD_UNLIKELY( 1UL!=fread( &opt_hdr, 4UL, 1UL, stream ) ) )
150 0 : return ferror( stream );
151 :
152 15 : uint end_off = fd_uint_align_up( opt_hdr.sz, 4U );
153 15 : uint read_sz = fd_uint_min( end_off, opt->sz );
154 :
155 15 : if( read_sz ) {
156 3 : if( FD_UNLIKELY( 1UL!=fread( opt->value, read_sz, 1UL, stream ) ) )
157 0 : return ferror( stream );
158 3 : end_off -= read_sz;
159 3 : }
160 :
161 15 : if( FD_UNLIKELY( 0!=fseek( stream, end_off, SEEK_CUR ) ) )
162 0 : return errno;
163 :
164 15 : return 0; /* success */
165 15 : }
166 :
167 : fd_pcapng_iter_t *
168 : fd_pcapng_iter_new( void * mem,
169 3 : void * _file ) {
170 :
171 3 : if( FD_UNLIKELY( !mem ) ) {
172 0 : FD_LOG_WARNING(( "NULL mem" ));
173 0 : return NULL;
174 0 : }
175 3 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, alignof(fd_pcapng_iter_t) ) ) ) {
176 0 : FD_LOG_WARNING(( "unaligned mem" ));
177 0 : return NULL;
178 0 : }
179 3 : if( FD_UNLIKELY( !_file ) ) {
180 0 : FD_LOG_WARNING(( "NULL file" ));
181 0 : return NULL;
182 0 : }
183 :
184 3 : FILE * file = (FILE *)_file;
185 :
186 3 : memset( mem, 0, sizeof(fd_pcapng_iter_t) );
187 3 : fd_pcapng_iter_t * iter = (fd_pcapng_iter_t *)mem;
188 3 : iter->stream = (FILE *)file;
189 :
190 : /* File starts with a Section Header Block */
191 :
192 3 : fd_pcapng_block_hdr_t shb_hdr;
193 3 : int err = fd_pcapng_peek_block( file, &shb_hdr, NULL );
194 3 : if( FD_UNLIKELY( err ) ) {
195 0 : FD_LOG_WARNING(( "pcapng: SHB read failed (%s)", fd_pcapng_iter_strerror( err, file ) ));
196 0 : return NULL;
197 0 : }
198 3 : if( FD_UNLIKELY( shb_hdr.block_type!=FD_PCAPNG_BLOCK_TYPE_SHB
199 3 : || shb_hdr.block_sz < sizeof(fd_pcapng_shb_t) ) ) {
200 0 : FD_LOG_WARNING(( "pcapng: not a valid Section Header Block" ));
201 0 : return NULL;
202 0 : }
203 :
204 : /* Read Section Header Block */
205 :
206 3 : fd_pcapng_shb_t shb;
207 3 : if( FD_UNLIKELY( 1UL!=fread( &shb, sizeof(fd_pcapng_shb_t), 1UL, file )
208 3 : || 0 !=fseek( file, (long)shb_hdr.block_sz - (long)sizeof(fd_pcapng_shb_t), SEEK_CUR ) ) ) {
209 0 : FD_LOG_WARNING(( "pcapng: SHB read failed (%s)", fd_pcapng_iter_strerror( err, file ) ));
210 0 : return NULL;
211 0 : }
212 :
213 3 : if( FD_UNLIKELY( (shb.version_major!=1) | (shb.version_minor!=0) ) ) {
214 0 : FD_LOG_WARNING(( "pcapng: unsupported file format version %u.%u",
215 0 : shb.version_major, shb.version_minor ));
216 0 : return NULL;
217 0 : }
218 :
219 3 : return iter;
220 3 : }
221 :
222 : void *
223 0 : fd_pcapng_iter_delete( fd_pcapng_iter_t * iter ) {
224 0 : void * mem = (void *)iter;
225 0 : memset( mem, 0, sizeof(fd_pcapng_iter_t) );
226 0 : return mem;
227 0 : }
228 :
229 : fd_pcapng_frame_t *
230 15 : fd_pcapng_iter_next( fd_pcapng_iter_t * iter ) {
231 :
232 15 : static FD_TL fd_pcapng_frame_t pkt;
233 :
234 : /* Clear fields */
235 15 : pkt.ts = 0L;
236 15 : pkt.type = 0U;
237 15 : pkt.data_sz = 0U;
238 15 : pkt.orig_sz = 0U;
239 15 : pkt.if_idx = 0U;
240 :
241 15 : FILE * stream = iter->stream;
242 :
243 : /* Attempt a number of times to find a frame of known type.
244 : Abort if there are too many unknown frames. */
245 18 : for( uint attempt=0U; attempt<256U; attempt++ ) {
246 :
247 18 : fd_pcapng_block_hdr_t hdr;
248 18 : long end;
249 18 : if( FD_UNLIKELY( 0!=(iter->error = fd_pcapng_peek_block( stream, &hdr, &end )) ) ) {
250 3 : if( FD_UNLIKELY( iter->error != -1 ) )
251 0 : FD_LOG_WARNING(( "pcapng: read failed (%s)", fd_pcapng_iter_strerror( iter->error, stream ) ));
252 3 : return NULL;
253 3 : }
254 :
255 15 : switch( hdr.block_type ) {
256 0 : case FD_PCAPNG_BLOCK_TYPE_SHB: {
257 0 : iter->error = -1; /* eof */
258 0 : return NULL;
259 0 : }
260 3 : case FD_PCAPNG_BLOCK_TYPE_IDB: {
261 : /* Read IDB */
262 3 : if( FD_UNLIKELY( hdr.block_sz<sizeof(fd_pcapng_idb_t) ) ) {
263 0 : iter->error = EPROTO;
264 0 : FD_LOG_WARNING(( "pcapng: invalid IDB block size (%#x)", hdr.block_sz ));
265 0 : return NULL;
266 0 : }
267 3 : fd_pcapng_idb_t idb;
268 3 : if( FD_UNLIKELY( 1UL!=fread( &idb, sizeof(fd_pcapng_idb_t), 1UL, stream ) ) ) {
269 0 : iter->error = ferror( stream );
270 0 : FD_LOG_WARNING(( "pcapng: read failed (%s)", fd_pcapng_iter_strerror( iter->error, stream ) ));
271 0 : return NULL;
272 0 : }
273 :
274 : /* Add interface to list */
275 3 : if( FD_UNLIKELY( iter->iface_cnt>=FD_PCAPNG_IFACE_CNT ) ) {
276 0 : iter->error = EPROTO;
277 0 : FD_LOG_WARNING(( "pcapng: too many interfaces (max %d)", FD_PCAPNG_IFACE_CNT ));
278 0 : return NULL;
279 0 : }
280 :
281 3 : fd_pcapng_idb_desc_t * iface = &iter->iface[ iter->iface_cnt++ ];
282 3 : memset( iface, 0, sizeof(fd_pcapng_idb_desc_t) );
283 3 : iface->link_type = idb.link_type;
284 :
285 : /* Read options */
286 3 : for( uint j=0; j<FD_PCAPNG_MAX_OPT_CNT; j++ ) {
287 3 : uchar opt_buf[ 128UL ] __attribute__((aligned(32UL)));
288 3 : fd_pcapng_option_t opt = { .sz=sizeof(opt_buf), .value=&opt_buf };
289 3 : if( FD_UNLIKELY( 0!=(iter->error = fd_pcapng_read_option( stream, &opt )) ) ) {
290 0 : FD_LOG_WARNING(( "pcapng: read failed (%s)", fd_pcapng_iter_strerror( iter->error, stream ) ));
291 0 : return NULL;
292 0 : }
293 3 : if( !opt.type ) break;
294 0 : switch( opt.type ) {
295 0 : case FD_PCAPNG_OPT_COMMENT:
296 0 : FD_LOG_HEXDUMP_DEBUG(( "IDB comment", opt_buf, opt.sz ));
297 0 : break;
298 0 : case FD_PCAPNG_IDB_OPT_NAME:
299 0 : fd_cstr_fini( fd_cstr_append_cstr_safe( fd_cstr_init( iface->opts.name ), (char const *)opt_buf, sizeof(opt_buf) ) );
300 0 : break;
301 0 : case FD_PCAPNG_IDB_OPT_HARDWARE:
302 0 : fd_cstr_fini( fd_cstr_append_cstr_safe( fd_cstr_init( iface->opts.hardware ), (char const *)opt_buf, sizeof(opt_buf) ) );
303 0 : break;
304 0 : case FD_PCAPNG_IDB_OPT_IPV4_ADDR:
305 0 : if( FD_UNLIKELY( opt.sz!=4U ) )
306 0 : continue;
307 0 : memcpy( iface->opts.ip4_addr, opt_buf, 4UL );
308 0 : break;
309 0 : case FD_PCAPNG_IDB_OPT_MAC_ADDR:
310 0 : if( FD_UNLIKELY( opt.sz!=6U ) )
311 0 : continue;
312 0 : memcpy( iface->opts.mac_addr, opt_buf, 6UL );
313 0 : break;
314 0 : case FD_PCAPNG_IDB_OPT_TSRESOL:
315 0 : if( FD_UNLIKELY( opt.sz!=1U ) )
316 0 : continue;
317 0 : iface->opts.tsresol = opt_buf[ 0 ];
318 0 : break;
319 0 : default:
320 0 : FD_LOG_DEBUG(( "Ignoring unknown IDB option type %#x", opt.type ));
321 0 : break;
322 0 : }
323 0 : }
324 :
325 : /* Seek to end of block */
326 3 : if( FD_UNLIKELY( 0!=fseek( stream, end, SEEK_SET ) ) ) {
327 0 : iter->error = errno;
328 0 : FD_LOG_WARNING(( "pcapng: seek failed (%s)", fd_pcapng_iter_strerror( iter->error, stream ) ));
329 0 : return NULL;
330 0 : }
331 :
332 : /* Next */
333 3 : break;
334 3 : }
335 3 : case FD_PCAPNG_BLOCK_TYPE_SPB: {
336 : /* Read SPB */
337 0 : if( FD_UNLIKELY( hdr.block_sz<sizeof(fd_pcapng_spb_t)
338 0 : || hdr.block_sz>FD_PCAPNG_FRAME_SZ ) ) {
339 0 : iter->error = EPROTO;
340 0 : FD_LOG_WARNING(( "pcapng: invalid SPB block size (%#x)", hdr.block_sz ));
341 0 : return NULL;
342 0 : }
343 :
344 0 : uint hdr_sz = sizeof(fd_pcapng_spb_t);
345 0 : uint data_sz = hdr.block_sz - hdr_sz;
346 :
347 0 : fd_pcapng_spb_t spb;
348 0 : if( FD_UNLIKELY( 1UL!=fread( &spb, hdr_sz, 1UL, stream ) ) ) {
349 0 : iter->error = ferror( stream );
350 0 : FD_LOG_WARNING(( "pcapng: read failed (%s)", fd_pcapng_iter_strerror( iter->error, stream ) ));
351 0 : return NULL;
352 0 : }
353 0 : if( FD_UNLIKELY( 1UL!=fread( &pkt.data, data_sz, 1UL, stream ) ) ) {
354 0 : iter->error = ferror( stream );
355 0 : FD_LOG_WARNING(( "pcapng: read failed (%s)", fd_pcapng_iter_strerror( iter->error, stream ) ));
356 0 : return NULL;
357 0 : }
358 :
359 : /* Seek to end of block */
360 0 : if( FD_UNLIKELY( 0!=fseek( stream, end, SEEK_SET ) ) ) {
361 0 : iter->error = errno;
362 0 : FD_LOG_WARNING(( "pcapng: seek failed (%s)", fd_pcapng_iter_strerror( iter->error, stream ) ));
363 0 : return NULL;
364 0 : }
365 :
366 0 : pkt.type = FD_PCAPNG_FRAME_SIMPLE;
367 0 : pkt.data_sz = (ushort)data_sz;
368 0 : pkt.orig_sz = (ushort)spb.orig_len;
369 0 : return &pkt;
370 0 : }
371 9 : case FD_PCAPNG_BLOCK_TYPE_EPB: {
372 : /* Read EPB */
373 9 : if( FD_UNLIKELY( hdr.block_sz<sizeof(fd_pcapng_epb_t)
374 9 : || hdr.block_sz>FD_PCAPNG_FRAME_SZ ) ) {
375 0 : iter->error = EPROTO;
376 0 : FD_LOG_WARNING(( "pcapng: invalid EPB block size (%#x)", hdr.block_sz ));
377 0 : return NULL;
378 0 : }
379 :
380 9 : fd_pcapng_epb_t epb;
381 9 : if( FD_UNLIKELY( 1UL!=fread( &epb, sizeof(fd_pcapng_epb_t), 1UL, stream ) ) ) {
382 0 : iter->error = ferror( stream );
383 0 : FD_LOG_WARNING(( "pcapng: read failed (%s)", fd_pcapng_iter_strerror( iter->error, stream ) ));
384 0 : return NULL;
385 0 : }
386 9 : if( FD_UNLIKELY( epb.cap_len>FD_PCAPNG_FRAME_SZ ) ) {
387 0 : iter->error = EPROTO;
388 0 : FD_LOG_WARNING(( "pcapng: oversize EPB data (%#x)", epb.cap_len ));
389 0 : return NULL;
390 0 : }
391 9 : if( FD_UNLIKELY( 1UL!=fread( &pkt.data, epb.cap_len, 1UL, stream ) ) ) {
392 0 : iter->error = ferror( stream );
393 0 : FD_LOG_WARNING(( "pcapng: read failed (%s)", fd_pcapng_iter_strerror( iter->error, stream ) ));
394 0 : return NULL;
395 0 : }
396 :
397 : /* Read options */
398 9 : for( uint j=0; j<FD_PCAPNG_MAX_OPT_CNT; j++ ) {
399 9 : uchar opt_buf[ 128UL ] __attribute__((aligned(32UL)));
400 9 : fd_pcapng_option_t opt = { .sz=sizeof(opt_buf), .value=&opt_buf };
401 9 : if( FD_UNLIKELY( 0!=(iter->error = fd_pcapng_read_option( stream, &opt )) ) ) {
402 0 : FD_LOG_WARNING(( "pcapng: read failed (%s)", fd_pcapng_iter_strerror( iter->error, stream ) ));
403 0 : return NULL;
404 0 : }
405 9 : if( !opt.type ) break;
406 0 : switch( opt.type ) {
407 0 : case FD_PCAPNG_OPT_COMMENT:
408 0 : FD_LOG_HEXDUMP_DEBUG(( "Packet comment", opt_buf, opt.sz ));
409 0 : break;
410 0 : default:
411 0 : FD_LOG_DEBUG(( "Ignoring unknown EPB option type %#x", opt.type ));
412 0 : break;
413 0 : }
414 0 : }
415 :
416 9 : if( FD_LIKELY( epb.if_idx < iter->iface_cnt ) ) {
417 9 : ulong raw = ( ((ulong)epb.ts_hi << 32UL) | (ulong)epb.ts_lo );
418 : /* FIXME support more timestamp resolutions */
419 9 : if( iter->iface[ epb.if_idx ].opts.tsresol == FD_PCAPNG_TSRESOL_NS ) {
420 0 : pkt.ts = (long)raw;
421 0 : }
422 9 : }
423 :
424 : /* Seek to end of block */
425 9 : if( FD_UNLIKELY( 0!=fseek( stream, end, SEEK_SET ) ) ) {
426 0 : iter->error = errno;
427 0 : FD_LOG_WARNING(( "pcapng: seek failed (%s)", fd_pcapng_iter_strerror( iter->error, stream ) ));
428 0 : return NULL;
429 0 : }
430 :
431 9 : pkt.type = FD_PCAPNG_FRAME_ENHANCED;
432 9 : pkt.data_sz = (ushort)epb.cap_len;
433 9 : pkt.orig_sz = (ushort)epb.orig_len;
434 9 : pkt.if_idx = epb.if_idx;
435 9 : return &pkt;
436 9 : }
437 3 : case FD_PCAPNG_BLOCK_TYPE_DSB: {
438 : /* Read DSB */
439 3 : if( FD_UNLIKELY( hdr.block_sz<sizeof(fd_pcapng_dsb_t)
440 3 : || hdr.block_sz>FD_PCAPNG_FRAME_SZ ) ) {
441 0 : iter->error = EPROTO;
442 0 : FD_LOG_WARNING(( "pcapng: invalid DSB block size (%#x)", hdr.block_sz ));
443 0 : return NULL;
444 0 : }
445 :
446 3 : fd_pcapng_dsb_t dsb;
447 3 : if( FD_UNLIKELY( 1UL!=fread( &dsb, sizeof(fd_pcapng_dsb_t), 1UL, stream ) ) ) {
448 0 : iter->error = ferror( stream );
449 0 : FD_LOG_WARNING(( "pcapng: read failed (%s)", fd_pcapng_iter_strerror( iter->error, stream ) ));
450 0 : return NULL;
451 0 : }
452 3 : if( FD_UNLIKELY( dsb.secret_sz>FD_PCAPNG_FRAME_SZ ) ) {
453 0 : iter->error = EPROTO;
454 0 : FD_LOG_WARNING(( "pcapng: oversize DSB data (%#x)", dsb.secret_sz ));
455 0 : return NULL;
456 0 : }
457 3 : if( FD_UNLIKELY( 1UL!=fread( &pkt.data, dsb.secret_sz, 1UL, stream ) ) ) {
458 0 : iter->error = ferror( stream );
459 0 : FD_LOG_WARNING(( "pcapng: read failed (%s)", fd_pcapng_iter_strerror( iter->error, stream ) ));
460 0 : return NULL;
461 0 : }
462 :
463 : /* Read options */
464 3 : for( uint j=0; j<FD_PCAPNG_MAX_OPT_CNT; j++ ) {
465 3 : uchar opt_buf[ 128UL ] __attribute__((aligned(32UL)));
466 3 : fd_pcapng_option_t opt = { .sz=sizeof(opt_buf), .value=&opt_buf };
467 3 : if( FD_UNLIKELY( 0!=(iter->error = fd_pcapng_read_option( stream, &opt )) ) ) {
468 0 : FD_LOG_WARNING(( "pcapng: read failed (%s)", fd_pcapng_iter_strerror( iter->error, stream ) ));
469 0 : return NULL;
470 0 : }
471 3 : if( !opt.type ) break;
472 0 : switch( opt.type ) {
473 0 : case FD_PCAPNG_OPT_COMMENT:
474 0 : FD_LOG_HEXDUMP_DEBUG(( "Decryption secrets comment", opt_buf, opt.sz ));
475 0 : break;
476 0 : default:
477 0 : FD_LOG_DEBUG(( "Ignoring unknown DSB option type %#x", opt.type ));
478 0 : break;
479 0 : }
480 0 : }
481 :
482 3 : if( dsb.secret_type!=FD_PCAPNG_SECRET_TYPE_TLS ) {
483 0 : FD_LOG_DEBUG(( "Ignoring secret (type %#x)", dsb.secret_type ));
484 0 : break;
485 0 : }
486 :
487 : /* Seek to end of block */
488 3 : if( FD_UNLIKELY( 0!=fseek( stream, end, SEEK_SET ) ) ) {
489 0 : iter->error = errno;
490 0 : FD_LOG_WARNING(( "pcapng: seek failed (%s)", fd_pcapng_iter_strerror( iter->error, stream ) ));
491 0 : return NULL;
492 0 : }
493 :
494 3 : pkt.type = FD_PCAPNG_FRAME_TLSKEYS;
495 3 : pkt.data_sz = dsb.secret_sz;
496 3 : return &pkt;
497 3 : }
498 0 : default:
499 0 : FD_LOG_DEBUG(( "pcapng: skipping unknown block (type=%#x)", hdr.block_type ));
500 0 : if( FD_UNLIKELY( 0!=fseek( stream, hdr.block_sz, SEEK_CUR ) ) ) {
501 0 : iter->error = errno;
502 0 : FD_LOG_WARNING(( "pcapng: seek failed (%s)", fd_pcapng_iter_strerror( iter->error, stream ) ));
503 0 : return NULL;
504 0 : }
505 15 : }
506 :
507 : /* Read block that is not interesting to user, continue to next */
508 15 : }
509 :
510 : /* Found no blocks that are interesting to user */
511 0 : iter->error = EPROTO;
512 0 : FD_LOG_WARNING(( "pcapng: aborting, too many non-packet frames" ));
513 0 : return NULL;
514 15 : }
515 :
516 : FD_FN_PURE int
517 3 : fd_pcapng_iter_err( fd_pcapng_iter_t const * iter ) {
518 3 : return iter->error;
519 3 : }
520 :
521 : /* fwrite-style funcs *************************************************/
522 :
523 : /* What follows are a bunch of serialization / writer functions. They
524 : maintain the following properties:
525 :
526 : - file handle is 4 byte aligned
527 : - buf is the write buffer up to
528 : - cursor is the next free byte in buffer (or next byte after end of
529 : buf is space exhausted)
530 : - Invariant: cursor <= FD_PCAPNG_BLOCK_SZ
531 : - fwrite is called once per func and write size is 4 byte aligned
532 : and no larger than FD_PCAPNG_BLOCK_SZ */
533 :
534 : /* FD_PCAPNG_FWRITE_OPT writes an option in the context of an fwrite-
535 : style function. Assumes that given length is <=65532.
536 :
537 : Args:
538 : ushort t (option type)
539 : ushort l (option length)
540 : void * v (ptr to option data) */
541 :
542 : #define FD_PCAPNG_FWRITE_OPT(t,l,v) \
543 72 : do { \
544 72 : ulong _sz = (ushort)( l ); \
545 72 : ulong _sz_align = (ushort)fd_ulong_align_up( _sz, 4UL ); \
546 72 : if( FD_UNLIKELY( cursor+4UL+_sz_align > FD_PCAPNG_BLOCK_SZ ) ) { \
547 0 : FD_LOG_WARNING(( "oversz pcapng block" )); \
548 0 : return 0UL; \
549 0 : } \
550 72 : *(ushort *)( buf+cursor ) = ( (ushort)(t) ); cursor+=2UL; \
551 72 : *(ushort *)( buf+cursor ) = ( (ushort)_sz ); cursor+=2UL; \
552 72 : fd_memcpy ( buf+cursor, (v), _sz ); \
553 72 : fd_memset ( buf+cursor+_sz, 0, _sz_align-_sz ); \
554 72 : cursor+=_sz_align; \
555 72 : } while(0);
556 :
557 : /* FD_PCAPNG_FWRITE_BLOCK_TERM terminates a block buffer being
558 : serialized in the context of an fwrite-style function. */
559 :
560 : #define FD_PCAPNG_FWRITE_BLOCK_TERM() \
561 15 : do { \
562 15 : if( FD_UNLIKELY( cursor+4UL > FD_PCAPNG_BLOCK_SZ ) ) { \
563 0 : FD_LOG_WARNING(( "oversz pcapng block" )); \
564 0 : return 0UL; \
565 0 : } \
566 15 : block->block_sz = (uint)(cursor+4UL); \
567 15 : *(uint *)( buf+cursor ) = (uint)(cursor+4UL); \
568 15 : cursor+=4UL; \
569 15 : } while(0);
570 :
571 : ulong
572 : fd_pcapng_fwrite_shb( fd_pcapng_shb_opts_t const * opt,
573 9 : void * file ) {
574 :
575 9 : uchar buf[ FD_PCAPNG_BLOCK_SZ ];
576 :
577 9 : fd_pcapng_shb_t * block = (fd_pcapng_shb_t *)buf;
578 :
579 9 : ulong cursor = sizeof(fd_pcapng_shb_t);
580 9 : *block = (fd_pcapng_shb_t) {
581 9 : .block_type = FD_PCAPNG_BLOCK_TYPE_SHB,
582 : /* block_sz set later */
583 9 : .byte_order_magic = FD_PCAPNG_BYTE_ORDER_MAGIC,
584 9 : .version_major = (ushort)1,
585 9 : .version_minor = (ushort)0,
586 9 : .section_sz = ULONG_MAX
587 9 : };
588 :
589 9 : if( opt ) {
590 9 : if( opt->hardware ) FD_PCAPNG_FWRITE_OPT( FD_PCAPNG_SHB_OPT_HARDWARE, strlen( opt->hardware ), opt->hardware );
591 9 : if( opt->os ) FD_PCAPNG_FWRITE_OPT( FD_PCAPNG_SHB_OPT_OS, strlen( opt->os ), opt->os );
592 9 : if( opt->userappl ) FD_PCAPNG_FWRITE_OPT( FD_PCAPNG_SHB_OPT_USERAPPL, strlen( opt->userappl ), opt->userappl );
593 9 : }
594 9 : FD_PCAPNG_FWRITE_OPT( 0, 0, NULL );
595 :
596 9 : FD_PCAPNG_FWRITE_BLOCK_TERM();
597 :
598 9 : return fwrite( buf, cursor, 1UL, (FILE *)file );
599 9 : }
600 :
601 : ulong
602 : fd_pcapng_fwrite_idb( uint link_type,
603 : fd_pcapng_idb_opts_t const * opt,
604 6 : void * file ) {
605 :
606 6 : uchar buf[ FD_PCAPNG_BLOCK_SZ ];
607 :
608 6 : fd_pcapng_idb_t * block = (fd_pcapng_idb_t *)buf;
609 :
610 6 : ulong cursor = sizeof(fd_pcapng_idb_t);
611 6 : *block = (fd_pcapng_idb_t) {
612 6 : .block_type = FD_PCAPNG_BLOCK_TYPE_IDB,
613 : /* block_sz set later */
614 6 : .link_type = (ushort)link_type,
615 6 : .snap_len = 0U, /* FIXME should appropriately set snap_len
616 : But this is not trivial. Needs balancing
617 : between buffer space available for meta
618 : and payload. (meta is variable length) */
619 6 : };
620 :
621 6 : uchar tsresol = FD_PCAPNG_TSRESOL_NS;
622 6 : FD_PCAPNG_FWRITE_OPT( FD_PCAPNG_IDB_OPT_TSRESOL, 1UL, &tsresol );
623 :
624 6 : if( opt ) {
625 :
626 6 : if( opt->name[0] )
627 6 : FD_PCAPNG_FWRITE_OPT( FD_PCAPNG_IDB_OPT_NAME, fd_cstr_nlen( opt->name, 16UL ), opt->name );
628 6 : if( fd_uint_load_4( opt->ip4_addr ) )
629 6 : FD_PCAPNG_FWRITE_OPT( FD_PCAPNG_IDB_OPT_IPV4_ADDR, 4UL, opt->ip4_addr );
630 6 : if( fd_ulong_load_6( opt->mac_addr ) )
631 6 : FD_PCAPNG_FWRITE_OPT( FD_PCAPNG_IDB_OPT_MAC_ADDR, 6UL, opt->mac_addr );
632 :
633 6 : if( opt->hardware[0] )
634 6 : FD_PCAPNG_FWRITE_OPT( FD_PCAPNG_IDB_OPT_HARDWARE, fd_cstr_nlen( opt->hardware, 64UL ), opt->hardware );
635 :
636 6 : }
637 6 : FD_PCAPNG_FWRITE_OPT( 0, 0, NULL );
638 :
639 6 : FD_PCAPNG_FWRITE_BLOCK_TERM();
640 :
641 6 : return fwrite( buf, cursor, 1UL, (FILE *)file );
642 6 : }
643 :
644 : ulong
645 : fd_pcapng_fwrite_pkt( long ts,
646 : void const * payload,
647 : ulong payload_sz,
648 12 : void * _file ) {
649 :
650 12 : FILE * file = (FILE *)_file;
651 12 : FD_TEST( fd_ulong_is_aligned( (ulong)ftell( file ), 4UL ) );
652 :
653 12 : ulong cursor = sizeof(fd_pcapng_epb_t);
654 12 : fd_pcapng_epb_t block = {
655 12 : .block_type = FD_PCAPNG_BLOCK_TYPE_EPB,
656 : /* block_sz set later */
657 12 : .if_idx = 0U,
658 12 : .ts_hi = (uint)( (ulong)ts >> 32UL ),
659 12 : .ts_lo = (uint)( (ulong)ts ),
660 12 : .cap_len = (uint)payload_sz,
661 12 : .orig_len = (uint)payload_sz
662 12 : };
663 :
664 12 : ulong payload_sz_align = fd_ulong_align_up( payload_sz, 4UL );
665 12 : uchar pad[8UL]={0};
666 12 : ulong pad_sz = payload_sz_align-payload_sz;
667 12 : cursor+=payload_sz_align;
668 :
669 : /* Empty option list */
670 12 : cursor+=4UL;
671 :
672 : /* Trailer */
673 12 : block.block_sz = (uint)cursor+4U;
674 :
675 : /* write header */
676 12 : if( FD_UNLIKELY( 1UL!=fwrite( &block, sizeof(fd_pcapng_epb_t), 1UL, file ) ) )
677 0 : return 0UL;
678 : /* copy payload */
679 12 : if( FD_UNLIKELY( 1UL!=fwrite( payload, payload_sz, 1UL, file ) ) )
680 0 : return 0UL;
681 : /* align */
682 12 : if( pad_sz )
683 9 : if( FD_UNLIKELY( 1UL!=fwrite( pad, pad_sz, 1UL, file ) ) )
684 0 : return 0UL;
685 : /* empty options */
686 12 : if( FD_UNLIKELY( 1UL!=fwrite( pad, 4UL, 1UL, file ) ) )
687 0 : return 0UL;
688 : /* write length trailer */
689 12 : if( FD_UNLIKELY( 1UL!=fwrite( &block.block_sz, 4UL, 1UL, file ) ) )
690 0 : return 0UL;
691 :
692 12 : return 1UL;
693 12 : }
694 :
695 : ulong
696 : fd_pcapng_fwrite_tls_key_log( uchar const * log,
697 : uint log_sz,
698 6 : void * _file ) {
699 :
700 6 : FILE * file = (FILE *)_file;
701 6 : FD_TEST( fd_ulong_is_aligned( (ulong)ftell( file ), 4UL ) );
702 :
703 6 : ulong cursor = sizeof(fd_pcapng_dsb_t);
704 6 : fd_pcapng_dsb_t block = {
705 6 : .block_type = FD_PCAPNG_BLOCK_TYPE_DSB,
706 : /* block_sz set later */
707 6 : .secret_type = FD_PCAPNG_SECRET_TYPE_TLS,
708 6 : .secret_sz = log_sz
709 6 : };
710 :
711 6 : uint log_sz_align = fd_uint_align_up( log_sz, 4UL );
712 6 : uchar pad[8] = {0};
713 6 : ulong pad_sz = log_sz_align-log_sz;
714 6 : cursor+=log_sz_align;
715 :
716 : /* end of options block */
717 6 : cursor+=4UL;
718 :
719 : /* derive size ahead of time */
720 6 : block.block_sz = (uint)cursor + 4U;
721 :
722 : /* write header */
723 6 : if( FD_UNLIKELY( 1UL!=fwrite( &block, sizeof(fd_pcapng_dsb_t), 1UL, file ) ) )
724 0 : return 0UL;
725 : /* copy log */
726 6 : if( FD_UNLIKELY( 1UL!=fwrite( log, log_sz, 1UL, file ) ) )
727 0 : return 0UL;
728 : /* align */
729 6 : if( pad_sz )
730 6 : if( FD_UNLIKELY( 1UL!=fwrite( pad, pad_sz, 1UL, file ) ) )
731 0 : return 0UL;
732 : /* empty options */
733 6 : if( FD_UNLIKELY( 1UL!=fwrite( pad, 4UL, 1UL, file ) ) )
734 0 : return 0UL;
735 : /* write length trailer */
736 6 : if( FD_UNLIKELY( 1UL!=fwrite( &block.block_sz, sizeof(uint), 1, file ) ) )
737 0 : return 0UL;
738 :
739 6 : return 1UL;
740 6 : }
741 :
742 : #endif /* FD_HAS_HOSTED */
|