LCOV - code coverage report
Current view: top level - util/net - fd_pcapng.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 250 502 49.8 %
Date: 2024-11-13 11:58:15 Functions: 11 15 73.3 %

          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 */

Generated by: LCOV version 1.14