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