streaming

Media streaming and broadcast systems in Go
Log | Files | Refs | README | LICENSE

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 }