Line data Source code
1 : #include "fd_pcapng_private.h"
2 : #include "../fd_util.h"
3 : #include <errno.h>
4 : #include <stdio.h>
5 :
6 : /* FIXME Option parsing spec violation
7 :
8 : https://www.ietf.org/archive/id/draft-ietf-opsawg-pcapng-00.html#name-options
9 :
10 : > Code that reads pcapng files MUST NOT assume an option list will
11 : have an opt_endofopt option at the end; it MUST also check for the
12 : end of the block, and SHOULD treat blocks where the option list has
13 : no opt_endofopt option as if the option list had an opt_endofopt
14 : option at the end.
15 :
16 : This parser currently does not handle missing opt_endofopt */
17 :
18 : FD_FN_CONST ulong
19 3 : fd_pcapng_iter_align( void ) {
20 3 : return alignof(fd_pcapng_iter_t);
21 3 : }
22 :
23 : FD_FN_CONST ulong
24 3 : fd_pcapng_iter_footprint( void ) {
25 3 : return sizeof(fd_pcapng_iter_t);
26 3 : }
27 :
28 : static char const *
29 : fd_pcapng_iter_strerror( int error,
30 0 : FILE * file ) {
31 0 : static FD_TL char err_cstr_buf[ 1024UL ];
32 0 : char * err_cstr = fd_cstr_init( err_cstr_buf );
33 0 : if( error==EPROTO ) {
34 0 : return fd_cstr_printf( err_cstr, sizeof(err_cstr_buf), NULL, "parse error at %#lx", (ulong)ftell(file) );
35 0 : } else if( error==-1 && !feof( file ) ) {
36 0 : return "end of section";
37 0 : } else {
38 0 : return fd_cstr_printf( err_cstr, sizeof(err_cstr_buf), NULL, "%i-%s", error, fd_io_strerror( error ) );
39 0 : }
40 0 : }
41 :
42 : static int
43 : fd_pcapng_read_block( FILE * stream,
44 : fd_pcapng_iter_t * iter,
45 21 : fd_pcapng_block_hdr_t * _hdr ) {
46 :
47 : /* Remember offset of block */
48 21 : long pos = ftell( stream );
49 21 : if( FD_UNLIKELY( pos<0L ) )
50 0 : return ferror( stream );
51 21 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)pos, 4U ) ) ) {
52 0 : FD_LOG_DEBUG(( "pcapng: misaligned stream at %#lx", (ulong)pos ));
53 0 : return EPROTO;
54 0 : }
55 :
56 : /* Read header */
57 21 : fd_pcapng_block_hdr_t hdr;
58 21 : if( FD_UNLIKELY( 1UL!=fread( &hdr, sizeof(fd_pcapng_block_hdr_t), 1, stream ) ) ) {
59 3 : if( FD_LIKELY( feof( stream ) ) ) return -1; /* eof */
60 0 : else return ferror( stream );
61 3 : }
62 :
63 : /* Coherence check length field */
64 18 : if( FD_UNLIKELY( (hdr.block_sz < 12U) /* header and footer are mandatory */
65 18 : | (hdr.block_sz >32768U) /* way too large */
66 18 : | (!fd_ulong_is_aligned( hdr.block_sz, 4U )) ) ) {
67 0 : FD_LOG_DEBUG(( "pcapng: block with invalid size %#x at %#lx", hdr.block_sz, (ulong)pos ));
68 0 : return EPROTO;
69 0 : }
70 :
71 18 : if( FD_UNLIKELY( hdr.block_sz>FD_PCAPNG_BLOCK_SZ ) ) {
72 0 : FD_LOG_DEBUG(( "pcapng: block too large for buffer (%#x)", hdr.block_sz ));
73 0 : return EPROTO;
74 0 : }
75 :
76 18 : memcpy( iter->block_buf, &hdr, sizeof(fd_pcapng_block_hdr_t) );
77 18 : ulong remaining = hdr.block_sz - sizeof(fd_pcapng_block_hdr_t);
78 :
79 : /* Read rest of block */
80 18 : if( FD_UNLIKELY( 1UL!=fread( iter->block_buf + sizeof(fd_pcapng_block_hdr_t), remaining, 1, stream ) ) )
81 0 : return ferror( stream );
82 :
83 18 : iter->block_buf_sz = hdr.block_sz;
84 18 : iter->block_buf_pos = sizeof(fd_pcapng_block_hdr_t);
85 :
86 : /* Verify footer */
87 18 : uint * footer = (uint *)( iter->block_buf + hdr.block_sz - sizeof(uint) );
88 18 : uint block_sz = *footer;
89 :
90 : /* Check that header and footer match */
91 18 : if( FD_UNLIKELY( hdr.block_sz != block_sz ) ) {
92 0 : FD_LOG_DEBUG(( "pcapng: block size in header and footer don't match at %#lx", (ulong)pos ));
93 0 : return EPROTO;
94 0 : }
95 :
96 18 : *_hdr = hdr;
97 :
98 18 : return 0; /* success */
99 18 : }
100 :
101 : static int
102 : fd_pcapng_read_option( fd_pcapng_iter_t * iter,
103 30 : fd_pcapng_option_t * opt ) {
104 :
105 30 : if( FD_UNLIKELY( iter->block_buf_pos + 4UL > iter->block_buf_sz ) ) {
106 0 : iter->error = EPROTO;
107 0 : FD_LOG_WARNING(( "expected option, found end of block" ));
108 0 : return EPROTO;
109 0 : }
110 :
111 30 : struct __attribute__((packed)) {
112 30 : ushort type;
113 30 : ushort sz;
114 30 : } opt_hdr;
115 30 : memcpy( &opt_hdr, iter->block_buf + iter->block_buf_pos, 4UL );
116 30 : iter->block_buf_pos += 4UL;
117 30 : if( FD_UNLIKELY( opt_hdr.sz > (iter->block_buf_sz - iter->block_buf_pos) ) ) {
118 0 : iter->error = EPROTO;
119 0 : FD_LOG_WARNING(( "option size out of bounds" ));
120 0 : return EPROTO;
121 0 : }
122 :
123 30 : uint read_sz = fd_uint_min( opt_hdr.sz, opt->sz );
124 30 : opt->type = opt_hdr.type;
125 30 : opt->sz = (ushort)read_sz;
126 :
127 30 : if( read_sz ) {
128 15 : if( FD_UNLIKELY( iter->block_buf_pos + read_sz > iter->block_buf_sz ) ) {
129 0 : iter->error = EPROTO;
130 0 : FD_LOG_WARNING(( "out of bounds option" ));
131 0 : return EPROTO;
132 0 : }
133 15 : memcpy( opt->value, iter->block_buf + iter->block_buf_pos, read_sz );
134 15 : }
135 :
136 30 : iter->block_buf_pos += fd_uint_align_up( opt_hdr.sz, 4U );
137 30 : if( FD_UNLIKELY( iter->block_buf_pos > iter->block_buf_sz ) )
138 0 : return EPROTO;
139 :
140 30 : return 0; /* success */
141 30 : }
142 :
143 : fd_pcapng_iter_t *
144 : fd_pcapng_iter_new( void * mem,
145 3 : void * _file ) {
146 :
147 3 : if( FD_UNLIKELY( !mem ) ) {
148 0 : FD_LOG_WARNING(( "NULL mem" ));
149 0 : return NULL;
150 0 : }
151 3 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, alignof(fd_pcapng_iter_t) ) ) ) {
152 0 : FD_LOG_WARNING(( "unaligned mem" ));
153 0 : return NULL;
154 0 : }
155 3 : if( FD_UNLIKELY( !_file ) ) {
156 0 : FD_LOG_WARNING(( "NULL file" ));
157 0 : return NULL;
158 0 : }
159 :
160 3 : FILE * file = (FILE *)_file;
161 :
162 3 : memset( mem, 0, sizeof(fd_pcapng_iter_t) );
163 3 : fd_pcapng_iter_t * iter = (fd_pcapng_iter_t *)mem;
164 3 : iter->stream = (FILE *)file;
165 3 : iter->empty = 1;
166 :
167 : /* File starts with a Section Header Block */
168 :
169 3 : fd_pcapng_block_hdr_t shb_hdr;
170 3 : int err = fd_pcapng_read_block( file, iter, &shb_hdr );
171 3 : if( FD_UNLIKELY( err ) ) {
172 0 : FD_LOG_WARNING(( "pcapng: SHB read failed (%s)", fd_pcapng_iter_strerror( err, file ) ));
173 0 : return NULL;
174 0 : }
175 3 : if( FD_UNLIKELY( shb_hdr.block_type!=FD_PCAPNG_BLOCK_TYPE_SHB
176 3 : || shb_hdr.block_sz < sizeof(fd_pcapng_shb_t) ) ) {
177 0 : FD_LOG_WARNING(( "pcapng: not a valid Section Header Block" ));
178 0 : return NULL;
179 0 : }
180 :
181 :
182 3 : fd_pcapng_shb_t shb = FD_LOAD( fd_pcapng_shb_t, iter->block_buf );
183 3 : if( FD_UNLIKELY( (shb.version_major!=1) | (shb.version_minor!=0) ) ) {
184 0 : FD_LOG_WARNING(( "pcapng: unsupported file format version %u.%u",
185 0 : shb.version_major, shb.version_minor ));
186 0 : return NULL;
187 0 : }
188 :
189 3 : return iter;
190 3 : }
191 :
192 : void *
193 0 : fd_pcapng_iter_delete( fd_pcapng_iter_t * iter ) {
194 0 : void * mem = (void *)iter;
195 0 : memset( mem, 0, sizeof(fd_pcapng_iter_t) );
196 0 : return mem;
197 0 : }
198 :
199 : static fd_pcapng_frame_t *
200 15 : fd_pcapng_iter_next1( fd_pcapng_iter_t * iter ) {
201 15 : fd_pcapng_frame_t * pkt = &iter->pkt;
202 :
203 : /* Clear fields */
204 15 : pkt->ts = 0L;
205 15 : pkt->type = 0U;
206 15 : pkt->data_sz = 0U;
207 15 : pkt->orig_sz = 0U;
208 15 : pkt->if_idx = 0U;
209 15 : pkt->idb = NULL;
210 :
211 15 : FILE * stream = iter->stream;
212 :
213 : /* Attempt a number of times to find a frame of known type.
214 : Abort if there are too many unknown frames. */
215 18 : for( uint attempt=0U; attempt<256U; attempt++ ) {
216 :
217 18 : fd_pcapng_block_hdr_t hdr;
218 18 : if( FD_UNLIKELY( 0!=(iter->error = fd_pcapng_read_block( stream, iter, &hdr )) ) ) {
219 3 : if( FD_UNLIKELY( iter->error != -1 ) )
220 0 : FD_LOG_WARNING(( "pcapng: read failed (%s)", fd_pcapng_iter_strerror( iter->error, stream ) ));
221 3 : return NULL;
222 3 : }
223 :
224 15 : switch( hdr.block_type ) {
225 0 : case FD_PCAPNG_BLOCK_TYPE_SHB: {
226 0 : iter->error = -1; /* eof */
227 : /* FIXME CONSIDER SILENTLY CONTINUING? */
228 0 : return NULL;
229 0 : }
230 3 : case FD_PCAPNG_BLOCK_TYPE_IDB: {
231 : /* Read IDB */
232 3 : if( FD_UNLIKELY( hdr.block_sz<sizeof(fd_pcapng_idb_t) ) ) {
233 0 : iter->error = EPROTO;
234 0 : FD_LOG_WARNING(( "pcapng: invalid IDB block size (%#x)", hdr.block_sz ));
235 0 : return NULL;
236 0 : }
237 3 : fd_pcapng_idb_t idb = FD_LOAD( fd_pcapng_idb_t, iter->block_buf );
238 3 : iter->block_buf_pos = sizeof(fd_pcapng_idb_t);
239 :
240 : /* Add interface to list */
241 3 : if( FD_UNLIKELY( iter->iface_cnt>=FD_PCAPNG_IFACE_CNT ) ) {
242 0 : iter->error = EPROTO;
243 0 : FD_LOG_WARNING(( "pcapng: too many interfaces (max %d)", FD_PCAPNG_IFACE_CNT ));
244 0 : return NULL;
245 0 : }
246 :
247 3 : fd_pcapng_idb_desc_t * iface = &iter->iface[ iter->iface_cnt++ ];
248 3 : memset( iface, 0, sizeof(fd_pcapng_idb_desc_t) );
249 3 : iface->link_type = idb.link_type;
250 :
251 : /* Read options */
252 18 : for( uint j=0; j<FD_PCAPNG_MAX_OPT_CNT; j++ ) {
253 18 : uchar opt_buf[ 128UL ] __attribute__((aligned(32UL)));
254 18 : fd_pcapng_option_t opt = { .sz=sizeof(opt_buf), .value=&opt_buf };
255 18 : if( FD_UNLIKELY( 0!=(iter->error = fd_pcapng_read_option( iter, &opt )) ) ) {
256 0 : FD_LOG_WARNING(( "pcapng: read failed (%s)", fd_pcapng_iter_strerror( iter->error, stream ) ));
257 0 : return NULL;
258 0 : }
259 18 : if( !opt.type ) break;
260 15 : switch( opt.type ) {
261 0 : case FD_PCAPNG_OPT_COMMENT:
262 0 : FD_LOG_HEXDUMP_DEBUG(( "IDB comment", opt_buf, opt.sz ));
263 0 : break;
264 3 : case FD_PCAPNG_IDB_OPT_NAME:
265 3 : fd_cstr_fini( fd_cstr_append_text( fd_cstr_init( iface->opts.name ), (char const *)opt_buf, fd_ulong_min( sizeof(iface->opts.name)-1, opt.sz ) ) );
266 3 : iface->opts.name[ sizeof(iface->opts.name)-1 ] = '\0';
267 3 : break;
268 3 : case FD_PCAPNG_IDB_OPT_HARDWARE:
269 3 : fd_cstr_fini( fd_cstr_append_text( fd_cstr_init( iface->opts.hardware ), (char const *)opt_buf, fd_ulong_min( sizeof(iface->opts.hardware)-1, opt.sz ) ) );
270 3 : iface->opts.hardware[ sizeof(iface->opts.hardware)-1 ] = '\0';
271 3 : break;
272 3 : case FD_PCAPNG_IDB_OPT_IPV4_ADDR:
273 3 : if( FD_UNLIKELY( opt.sz!=4U ) )
274 0 : continue;
275 3 : memcpy( iface->opts.ip4_addr, opt_buf, 4UL );
276 3 : break;
277 3 : case FD_PCAPNG_IDB_OPT_MAC_ADDR:
278 3 : if( FD_UNLIKELY( opt.sz!=6U ) )
279 0 : continue;
280 3 : memcpy( iface->opts.mac_addr, opt_buf, 6UL );
281 3 : break;
282 3 : case FD_PCAPNG_IDB_OPT_TSRESOL:
283 3 : if( FD_UNLIKELY( opt.sz!=1U ) )
284 0 : continue;
285 3 : iface->opts.tsresol = opt_buf[ 0 ];
286 3 : break;
287 0 : default:
288 0 : FD_LOG_DEBUG(( "Ignoring unknown IDB option type %#x", opt.type ));
289 0 : break;
290 15 : }
291 15 : }
292 :
293 3 : break;
294 3 : }
295 3 : case FD_PCAPNG_BLOCK_TYPE_SPB: {
296 : /* Read SPB */
297 0 : if( FD_UNLIKELY( hdr.block_sz<sizeof(fd_pcapng_spb_t) ) ) {
298 0 : iter->error = EPROTO;
299 0 : FD_LOG_WARNING(( "pcapng: invalid SPB block size (%#x)", hdr.block_sz ));
300 0 : return NULL;
301 0 : }
302 :
303 0 : uint hdr_sz = sizeof(fd_pcapng_spb_t);
304 0 : uint data_sz = hdr.block_sz - hdr_sz;
305 :
306 0 : fd_pcapng_spb_t spb = FD_LOAD( fd_pcapng_spb_t, iter->block_buf );
307 0 : iter->block_buf_pos = hdr_sz;
308 :
309 0 : if( FD_UNLIKELY( spb.orig_len > (iter->block_buf_sz - iter->block_buf_pos) ) ) {
310 0 : iter->error = EPROTO;
311 0 : FD_LOG_WARNING(( "pcapng: invalid SPB block size (%#x)", hdr.block_sz ));
312 0 : return NULL;
313 0 : }
314 0 : pkt->data = iter->block_buf + iter->block_buf_pos;
315 :
316 0 : pkt->type = FD_PCAPNG_FRAME_SIMPLE;
317 0 : pkt->data_sz = (ushort)data_sz;
318 0 : pkt->orig_sz = (ushort)spb.orig_len;
319 0 : return pkt;
320 0 : }
321 9 : case FD_PCAPNG_BLOCK_TYPE_EPB: {
322 : /* Read EPB */
323 9 : if( FD_UNLIKELY( hdr.block_sz<sizeof(fd_pcapng_epb_t) ) ) {
324 0 : iter->error = EPROTO;
325 0 : FD_LOG_WARNING(( "pcapng: invalid EPB block size (%#x)", hdr.block_sz ));
326 0 : return NULL;
327 0 : }
328 :
329 9 : fd_pcapng_epb_t epb = FD_LOAD( fd_pcapng_epb_t, iter->block_buf );
330 9 : iter->block_buf_pos = sizeof(fd_pcapng_epb_t);
331 :
332 9 : if( FD_UNLIKELY( epb.cap_len > (iter->block_buf_sz - iter->block_buf_pos) ) ) {
333 0 : iter->error = EPROTO;
334 0 : FD_LOG_WARNING(( "pcapng: invalid EPB block size (%#x)", hdr.block_sz ));
335 0 : return NULL;
336 0 : }
337 9 : pkt->data = iter->block_buf + iter->block_buf_pos;
338 9 : iter->block_buf_pos += fd_uint_align_up( epb.cap_len, 4U );
339 :
340 : /* Read options */
341 9 : for( uint j=0; j<FD_PCAPNG_MAX_OPT_CNT; j++ ) {
342 9 : uchar opt_buf[ 128UL ] __attribute__((aligned(32UL)));
343 9 : fd_pcapng_option_t opt = { .sz=sizeof(opt_buf), .value=&opt_buf };
344 9 : if( FD_UNLIKELY( 0!=(iter->error = fd_pcapng_read_option( iter, &opt )) ) ) {
345 0 : FD_LOG_WARNING(( "pcapng: read failed (%s)", fd_pcapng_iter_strerror( iter->error, stream ) ));
346 0 : return NULL;
347 0 : }
348 9 : if( !opt.type ) break;
349 0 : switch( opt.type ) {
350 0 : case FD_PCAPNG_OPT_COMMENT:
351 0 : FD_LOG_HEXDUMP_DEBUG(( "Packet comment", opt_buf, opt.sz ));
352 0 : break;
353 0 : default:
354 0 : FD_LOG_DEBUG(( "Ignoring unknown EPB option type %#x", opt.type ));
355 0 : break;
356 0 : }
357 0 : }
358 :
359 9 : if( FD_LIKELY( epb.if_idx < iter->iface_cnt ) ) {
360 9 : ulong raw = ( ((ulong)epb.ts_hi << 32UL) | (ulong)epb.ts_lo );
361 : /* FIXME support more timestamp resolutions */
362 9 : if( iter->iface[ epb.if_idx ].opts.tsresol == FD_PCAPNG_TSRESOL_NS ) {
363 9 : pkt->ts = (long)raw;
364 9 : }
365 9 : }
366 :
367 9 : pkt->type = FD_PCAPNG_FRAME_ENHANCED;
368 9 : pkt->data_sz = (ushort)epb.cap_len;
369 9 : pkt->orig_sz = (ushort)epb.orig_len;
370 9 : pkt->if_idx = epb.if_idx;
371 9 : pkt->idb = (epb.if_idx<iter->iface_cnt) ? &iter->iface[ epb.if_idx ] : NULL;
372 9 : return pkt;
373 9 : }
374 3 : case FD_PCAPNG_BLOCK_TYPE_DSB: {
375 : /* Read DSB */
376 3 : if( FD_UNLIKELY( hdr.block_sz<sizeof(fd_pcapng_dsb_t) ) ) {
377 0 : iter->error = EPROTO;
378 0 : FD_LOG_WARNING(( "pcapng: invalid DSB block size (%#x)", hdr.block_sz ));
379 0 : return NULL;
380 0 : }
381 :
382 3 : fd_pcapng_dsb_t dsb = FD_LOAD( fd_pcapng_dsb_t, iter->block_buf );
383 3 : iter->block_buf_pos = sizeof(fd_pcapng_dsb_t);
384 :
385 3 : if( FD_UNLIKELY( dsb.secret_sz > (iter->block_buf_sz - iter->block_buf_pos) ) ) {
386 0 : iter->error = EPROTO;
387 0 : FD_LOG_WARNING(( "pcapng: invalid DSB block size (%#x)", hdr.block_sz ));
388 0 : return NULL;
389 0 : }
390 3 : pkt->data = iter->block_buf + sizeof(fd_pcapng_dsb_t);
391 3 : iter->block_buf_pos += fd_uint_align_up( dsb.secret_sz, 4U );
392 :
393 : /* Read options */
394 3 : for( uint j=0; j<FD_PCAPNG_MAX_OPT_CNT; j++ ) {
395 3 : uchar opt_buf[ 128UL ] __attribute__((aligned(32UL)));
396 3 : fd_pcapng_option_t opt = { .sz=sizeof(opt_buf), .value=&opt_buf };
397 3 : if( FD_UNLIKELY( 0!=(iter->error = fd_pcapng_read_option( iter, &opt )) ) ) {
398 0 : FD_LOG_WARNING(( "pcapng: read failed (%s)", fd_pcapng_iter_strerror( iter->error, stream ) ));
399 0 : return NULL;
400 0 : }
401 3 : if( !opt.type ) break;
402 0 : switch( opt.type ) {
403 0 : case FD_PCAPNG_OPT_COMMENT:
404 0 : FD_LOG_HEXDUMP_DEBUG(( "Decryption secrets comment", opt_buf, opt.sz ));
405 0 : break;
406 0 : default:
407 0 : FD_LOG_DEBUG(( "Ignoring unknown DSB option type %#x", opt.type ));
408 0 : break;
409 0 : }
410 0 : }
411 :
412 3 : if( dsb.secret_type!=FD_PCAPNG_SECRET_TYPE_TLS ) {
413 0 : FD_LOG_DEBUG(( "Ignoring secret (type %#x)", dsb.secret_type ));
414 0 : break;
415 0 : }
416 :
417 3 : pkt->type = FD_PCAPNG_FRAME_TLSKEYS;
418 3 : pkt->data_sz = dsb.secret_sz;
419 3 : return pkt;
420 3 : }
421 0 : default:
422 0 : FD_LOG_DEBUG(( "pcapng: skipping unknown block (type=%#x)", hdr.block_type ));
423 0 : break;
424 15 : }
425 15 : }
426 :
427 : /* Found no blocks that are interesting to user */
428 0 : iter->error = EPROTO;
429 0 : FD_LOG_WARNING(( "pcapng: aborting, too many non-packet frames" ));
430 0 : return NULL;
431 15 : }
432 :
433 : fd_pcapng_frame_t *
434 15 : fd_pcapng_iter_next( fd_pcapng_iter_t * iter ) {
435 15 : fd_pcapng_frame_t * frame = fd_pcapng_iter_next1( iter );
436 15 : iter->empty = !frame;
437 15 : return frame;
438 15 : }
439 :
440 : fd_pcapng_frame_t *
441 0 : fd_pcapng_iter_ele( fd_pcapng_iter_t * iter ) {
442 0 : if( FD_UNLIKELY( iter->empty ) ) return NULL;
443 0 : return &iter->pkt;
444 0 : }
445 :
446 : FD_FN_PURE int
447 3 : fd_pcapng_iter_err( fd_pcapng_iter_t const * iter ) {
448 3 : return iter->error;
449 3 : }
|