pcap.go (3459B)
1 // Package pcap implements decoding and encoding of libcap's savefile 2 // format described in [pcap-savefile(5)]. 3 // 4 // [pcap-savefile(5)]: https://www.tcpdump.org/manpages/pcap-savefile.5.txt 5 package pcap 6 7 import ( 8 "encoding/binary" 9 "fmt" 10 "io" 11 "time" 12 ) 13 14 const ( 15 magicLittleEndian uint32 = 0xa1b2c3d4 16 magicBigEndian uint32 = 0xd4c3b2a1 17 ) 18 19 var version = [2]uint16{2, 4} 20 21 type GlobalHeader struct { 22 _ int32 // leftover from old pcap versions 23 _ int32 // leftover from old pcap versions 24 SnapLen uint32 25 Network uint32 26 } 27 28 type Header struct { 29 Time time.Time 30 OrigLen uint32 31 } 32 33 type header struct { 34 Seconds uint32 35 SubSeconds uint32 // micro or nanoseconds 36 InclLen uint32 37 OrigLen uint32 38 } 39 40 type Packet struct { 41 Header Header 42 Data []byte 43 } 44 45 type File struct { 46 Header GlobalHeader 47 Packets []Packet 48 } 49 50 // Decode returns a decoded savefile packet capture from rd. 51 func Decode(rd io.Reader) (*File, error) { 52 var magic uint32 53 if err := binary.Read(rd, binary.NativeEndian, &magic); err != nil { 54 return nil, fmt.Errorf("read magic number: %w", err) 55 } 56 if magic != magicLittleEndian && magic != magicBigEndian { 57 return nil, fmt.Errorf("unknown magic number %#x", magic) 58 } 59 60 var v [2]uint16 61 if err := binary.Read(rd, binary.LittleEndian, &v); err != nil { 62 return nil, fmt.Errorf("read pcap version: %w", err) 63 } 64 if v != version { 65 return nil, fmt.Errorf("unsupported version %d.%d", v[0], v[1]) 66 } 67 68 var gheader GlobalHeader 69 if err := binary.Read(rd, binary.LittleEndian, &gheader); err != nil { 70 return nil, fmt.Errorf("read pcap version: %w", err) 71 } 72 73 var packets []Packet 74 for i := 1; ; i++ { 75 var h header 76 err := binary.Read(rd, binary.LittleEndian, &h) 77 if err == io.EOF { 78 break 79 } else if err != nil { 80 return nil, fmt.Errorf("packet %d: read header: %w", i, err) 81 } 82 var p Packet 83 p.Header = Header{ 84 Time: time.Unix(int64(h.Seconds), int64(h.SubSeconds)*1000), 85 OrigLen: h.OrigLen, 86 } 87 88 p.Data = make([]byte, h.InclLen) 89 if _, err = io.ReadFull(rd, p.Data); err != nil { 90 return nil, fmt.Errorf("packet %d: read data: %w", i, err) 91 } 92 93 packets = append(packets, p) 94 } 95 96 return &File{gheader, packets}, nil 97 } 98 99 // Encode writes a savefile-encoded representation of file to w. 100 func Encode(w io.Writer, file *File) (n int64, err error) { 101 b := make([]byte, 4+4) // magic + version 102 binary.NativeEndian.PutUint32(b, magicLittleEndian) 103 binary.NativeEndian.PutUint16(b[4:6], version[0]) 104 binary.NativeEndian.PutUint16(b[6:8], version[1]) 105 nn, err := w.Write(b) 106 n += int64(nn) 107 if err != nil { 108 return n, fmt.Errorf("magic header: %w", err) 109 } 110 if err := binary.Write(w, binary.LittleEndian, &file.Header); err != nil { 111 return n, fmt.Errorf("global header: %w", err) 112 } 113 n += (4*4) // 4 32-bit ints in GlobalHeader 114 115 for i, p := range file.Packets { 116 sec, nsec := timestamp(p.Header.Time) 117 h := header{ 118 Seconds: sec, 119 SubSeconds: nsec / 1000, 120 InclLen: uint32(len(p.Data)), 121 OrigLen: p.Header.OrigLen, 122 } 123 if err := binary.Write(w, binary.LittleEndian, h); err != nil { 124 return n, fmt.Errorf("packet %d: header: %v", i, err) 125 } 126 n += 4*4 // 4 uint32s in header 127 nn, err := w.Write(p.Data) 128 n += int64(nn) 129 if err != nil { 130 return n, fmt.Errorf("packet %d: data: %v", i, err) 131 } 132 } 133 return n, nil 134 } 135 136 func timestamp(t time.Time) (seconds, nanoSeconds uint32) { 137 seconds = uint32(t.Unix()) 138 nanoSeconds = uint32(t.UnixNano() - t.Unix()*1e9) 139 return 140 }