diff --git a/pcap/pcap.test b/pcap/pcap.test deleted file mode 100755 index f75335d..0000000 Binary files a/pcap/pcap.test and /dev/null differ diff --git a/pcap/pcap.go b/pcap/reader.go similarity index 98% rename from pcap/pcap.go rename to pcap/reader.go index 73460c1..7aa2f96 100644 --- a/pcap/pcap.go +++ b/pcap/reader.go @@ -73,7 +73,6 @@ func NewReader(r io.Reader) (*Reader, error) { return nil, err } } - fmt.Printf("%x\n", header.Magic) switch header.Magic { case 0xa1b2c3d4: // Timestamps are (sec, usec) diff --git a/pcap/pcap_test.go b/pcap/reader_test.go similarity index 100% rename from pcap/pcap_test.go rename to pcap/reader_test.go diff --git a/pcap/writer.go b/pcap/writer.go new file mode 100644 index 0000000..8f45453 --- /dev/null +++ b/pcap/writer.go @@ -0,0 +1,74 @@ +package pcap + +import ( + "encoding/binary" + "io" +) + +// Writer serializes Packets to an io.Writer. +type Writer struct { + Writer io.Writer + LinkType LinkType + SnapLen uint32 + ByteOrder binary.ByteOrder // defaults to binary.LittleEndian + + headerWritten bool +} + +func (w *Writer) order() binary.ByteOrder { + if w.ByteOrder != nil { + return w.ByteOrder + } + return binary.LittleEndian +} + +func (w *Writer) header() error { + hdr := struct { + Magic uint32 + Major uint16 + Minor uint16 + Ignored uint64 + Snaplen uint32 + Type uint32 + }{ + Magic: 0xa1b23c4d, + Major: 2, + Minor: 4, + Snaplen: w.SnapLen, + Type: uint32(w.LinkType), + } + + if err := binary.Write(w.Writer, w.order(), hdr); err != nil { + return err + } + w.headerWritten = true + return nil +} + +// Put serializes pkt to w.Writer. +func (w *Writer) Put(pkt *Packet) error { + if !w.headerWritten { + if err := w.header(); err != nil { + return err + } + } + hdr := struct { + Sec uint32 + NSec uint32 + Len uint32 + OrigLen uint32 + }{ + Sec: uint32(pkt.Timestamp.Unix()), + NSec: uint32(pkt.Timestamp.Nanosecond()), + Len: uint32(len(pkt.Bytes)), + OrigLen: uint32(pkt.Length), + } + + if err := binary.Write(w.Writer, w.order(), hdr); err != nil { + return err + } + if _, err := w.Writer.Write(pkt.Bytes); err != nil { + return err + } + return nil +} diff --git a/pcap/writer_test.go b/pcap/writer_test.go new file mode 100644 index 0000000..1d38b60 --- /dev/null +++ b/pcap/writer_test.go @@ -0,0 +1,86 @@ +package pcap + +import ( + "bytes" + "encoding/binary" + "io" + "reflect" + "testing" + "time" + + "github.com/kr/pretty" +) + +func TestReadback(t *testing.T) { + pkts := []*Packet{ + { + Timestamp: time.Now(), + Length: 42, + Bytes: []byte{1, 2, 3, 4}, + }, + { + Timestamp: time.Now(), + Length: 30, + Bytes: []byte{2, 3, 4, 5}, + }, + { + Timestamp: time.Now(), + Length: 20, + Bytes: []byte{3, 4, 5, 6}, + }, + { + Timestamp: time.Now(), + Length: 10, + Bytes: []byte{4, 5, 6, 7}, + }, + } + + serializations := map[string]bool{} + for _, order := range []binary.ByteOrder{nil, binary.LittleEndian, binary.BigEndian} { + var b bytes.Buffer + w := &Writer{ + Writer: &b, + LinkType: LinkEthernet, + SnapLen: 65535, + ByteOrder: order, + } + + for _, pkt := range pkts { + if err := w.Put(pkt); err != nil { + t.Fatalf(pretty.Sprintf("Writing packet %# v: %s", pkt, err)) + } + } + + // Record the binary form, to check how many different serializations we get. + serializations[b.String()] = true + + readBack := []*Packet{} + r, err := NewReader(&b) + if err != nil { + t.Fatalf("Initializing reader for writer read-back: %s", err) + } + if r.LinkType != LinkEthernet { + t.Fatalf("Wrote link type %d, read back %d", LinkEthernet, r.LinkType) + } + + ReadLoop: + for { + pkt, err := r.Next() + if err != nil { + if err == io.EOF { + break ReadLoop + } + t.Fatalf("Unexpected error reading packets: %s", err) + } + readBack = append(readBack, pkt) + } + + if !reflect.DeepEqual(pkts, readBack) { + t.Fatalf("Packets were mutated by write-then-read") + } + } + + if len(serializations) != 2 { + t.Fatalf("Expected 2 distinct serializations due to endianness, got %d", len(serializations)) + } +}