diff options
Diffstat (limited to 'vendor/github.com/google/gopacket/pcapgo')
-rw-r--r-- | vendor/github.com/google/gopacket/pcapgo/read.go | 155 | ||||
-rw-r--r-- | vendor/github.com/google/gopacket/pcapgo/read_test.go | 203 | ||||
-rw-r--r-- | vendor/github.com/google/gopacket/pcapgo/write.go | 103 | ||||
-rw-r--r-- | vendor/github.com/google/gopacket/pcapgo/write_test.go | 71 |
4 files changed, 532 insertions, 0 deletions
diff --git a/vendor/github.com/google/gopacket/pcapgo/read.go b/vendor/github.com/google/gopacket/pcapgo/read.go new file mode 100644 index 0000000..5acd06f --- /dev/null +++ b/vendor/github.com/google/gopacket/pcapgo/read.go @@ -0,0 +1,155 @@ +// Copyright 2014 Damjan Cvetko. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package pcapgo + +import ( + "encoding/binary" + "errors" + "fmt" + "io" + "time" + + "bufio" + "compress/gzip" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" +) + +// Reader wraps an underlying io.Reader to read packet data in PCAP +// format. See http://wiki.wireshark.org/Development/LibpcapFileFormat +// for information on the file format. +// +// We currenty read v2.4 file format with nanosecond and microsecdond +// timestamp resolution in little-endian and big-endian encoding. +// +// If the PCAP data is gzip compressed it is transparently uncompressed +// by wrapping the given io.Reader with a gzip.Reader. +type Reader struct { + r io.Reader + byteOrder binary.ByteOrder + nanoSecsFactor uint32 + versionMajor uint16 + versionMinor uint16 + // timezone + // sigfigs + snaplen uint32 + linkType layers.LinkType + // reusable buffer + buf [16]byte +} + +const magicNanoseconds = 0xA1B23C4D +const magicMicrosecondsBigendian = 0xD4C3B2A1 +const magicNanosecondsBigendian = 0x4D3CB2A1 + +const magicGzip1 = 0x1f +const magicGzip2 = 0x8b + +// NewReader returns a new reader object, for reading packet data from +// the given reader. The reader must be open and header data is +// read from it at this point. +// If the file format is not supported an error is returned +// +// // Create new reader: +// f, _ := os.Open("/tmp/file.pcap") +// defer f.Close() +// r, err := NewReader(f) +// data, ci, err := r.ReadPacketData() +func NewReader(r io.Reader) (*Reader, error) { + ret := Reader{r: r} + if err := ret.readHeader(); err != nil { + return nil, err + } + return &ret, nil +} + +func (r *Reader) readHeader() error { + br := bufio.NewReader(r.r) + gzipMagic, err := br.Peek(2) + if err != nil { + return err + } + + if gzipMagic[0] == magicGzip1 && gzipMagic[1] == magicGzip2 { + if r.r, err = gzip.NewReader(br); err != nil { + return err + } + } else { + r.r = br + } + + buf := make([]byte, 24) + if n, err := io.ReadFull(r.r, buf); err != nil { + return err + } else if n < 24 { + return errors.New("Not enough data for read") + } + if magic := binary.LittleEndian.Uint32(buf[0:4]); magic == magicNanoseconds { + r.byteOrder = binary.LittleEndian + r.nanoSecsFactor = 1 + } else if magic == magicNanosecondsBigendian { + r.byteOrder = binary.BigEndian + r.nanoSecsFactor = 1 + } else if magic == magicMicroseconds { + r.byteOrder = binary.LittleEndian + r.nanoSecsFactor = 1000 + } else if magic == magicMicrosecondsBigendian { + r.byteOrder = binary.BigEndian + r.nanoSecsFactor = 1000 + } else { + return fmt.Errorf("Unknown magic %x", magic) + } + if r.versionMajor = r.byteOrder.Uint16(buf[4:6]); r.versionMajor != versionMajor { + return fmt.Errorf("Unknown major version %d", r.versionMajor) + } + if r.versionMinor = r.byteOrder.Uint16(buf[6:8]); r.versionMinor != versionMinor { + return fmt.Errorf("Unknown minor version %d", r.versionMinor) + } + // ignore timezone 8:12 and sigfigs 12:16 + r.snaplen = r.byteOrder.Uint32(buf[16:20]) + r.linkType = layers.LinkType(r.byteOrder.Uint32(buf[20:24])) + return nil +} + +// ReadPacketData reads next packet from file. +func (r *Reader) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) { + if ci, err = r.readPacketHeader(); err != nil { + return + } + if ci.CaptureLength > int(r.snaplen) { + err = fmt.Errorf("capture length exceeds snap length: %d > %d", 16+ci.CaptureLength, r.snaplen) + return + } + data = make([]byte, ci.CaptureLength) + _, err = io.ReadFull(r.r, data) + return data, ci, err +} + +func (r *Reader) readPacketHeader() (ci gopacket.CaptureInfo, err error) { + if _, err = io.ReadFull(r.r, r.buf[:]); err != nil { + return + } + ci.Timestamp = time.Unix(int64(r.byteOrder.Uint32(r.buf[0:4])), int64(r.byteOrder.Uint32(r.buf[4:8])*r.nanoSecsFactor)).UTC() + ci.CaptureLength = int(r.byteOrder.Uint32(r.buf[8:12])) + ci.Length = int(r.byteOrder.Uint32(r.buf[12:16])) + return +} + +// LinkType returns network, as a layers.LinkType. +func (r *Reader) LinkType() layers.LinkType { + return r.linkType +} + +// Snaplen returns the snapshot length of the capture file. +func (r *Reader) Snaplen() uint32 { + return r.snaplen +} + +// Reader formater +func (r *Reader) String() string { + return fmt.Sprintf("PcapFile maj: %x min: %x snaplen: %d linktype: %s", r.versionMajor, r.versionMinor, r.snaplen, r.linkType) +} diff --git a/vendor/github.com/google/gopacket/pcapgo/read_test.go b/vendor/github.com/google/gopacket/pcapgo/read_test.go new file mode 100644 index 0000000..87bf7ab --- /dev/null +++ b/vendor/github.com/google/gopacket/pcapgo/read_test.go @@ -0,0 +1,203 @@ +// Copyright 2014 Damjan Cvetko. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. +package pcapgo + +import ( + "bytes" + "testing" + "time" +) + +// test header read +func TestCreatePcapReader(t *testing.T) { + test := []byte{ + 0xd4, 0xc3, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + } + buf := bytes.NewBuffer(test) + _, err := NewReader(buf) + if err != nil { + t.Error(err) + t.FailNow() + } +} + +// test big endian file read +func TestCreatePcapReaderBigEndian(t *testing.T) { + test := []byte{ + 0xa1, 0xb2, 0xc3, 0xd4, 0x00, 0x02, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + } + buf := bytes.NewBuffer(test) + _, err := NewReader(buf) + if err != nil { + t.Error(err) + t.FailNow() + } +} + +// test opening invalid data +func TestCreatePcapReaderFail(t *testing.T) { + test := []byte{ + 0xd0, 0xc3, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + } + buf := bytes.NewBuffer(test) + _, err := NewReader(buf) + if err == nil { + t.Error("Should fail but did not") + t.FailNow() + } +} + +func TestPacket(t *testing.T) { + test := []byte{ + 0xd4, 0xc3, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00, // magic, maj, min + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // tz, sigfigs + 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // snaplen, linkType + 0x5A, 0xCC, 0x1A, 0x54, 0x01, 0x00, 0x00, 0x00, // sec, usec + 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, // cap len, full len + 0x01, 0x02, 0x03, 0x04, // data + } + + buf := bytes.NewBuffer(test) + r, err := NewReader(buf) + + data, ci, err := r.ReadPacketData() + if err != nil { + t.Error(err) + t.FailNow() + } + if !ci.Timestamp.Equal(time.Date(2014, 9, 18, 12, 13, 14, 1000, time.UTC)) { + t.Error("Invalid time read") + t.FailNow() + } + if ci.CaptureLength != 4 || ci.Length != 8 { + t.Error("Invalid CapLen or Len") + } + want := []byte{1, 2, 3, 4} + if !bytes.Equal(data, want) { + t.Errorf("buf mismatch:\nwant: %+v\ngot: %+v", want, data) + } +} + +func TestPacketNano(t *testing.T) { + test := []byte{ + 0x4d, 0x3c, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00, // magic, maj, min + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // tz, sigfigs + 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // snaplen, linkType + 0x5A, 0xCC, 0x1A, 0x54, 0x01, 0x00, 0x00, 0x00, // sec, usec + 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, // cap len, full len + 0x01, 0x02, 0x03, 0x04, // data + } + + buf := bytes.NewBuffer(test) + r, err := NewReader(buf) + + data, ci, err := r.ReadPacketData() + if err != nil { + t.Error(err) + t.FailNow() + } + if !ci.Timestamp.Equal(time.Date(2014, 9, 18, 12, 13, 14, 1, time.UTC)) { + t.Error("Invalid time read") + t.FailNow() + } + if ci.CaptureLength != 4 || ci.Length != 8 { + t.Error("Invalid CapLen or Len") + } + want := []byte{1, 2, 3, 4} + if !bytes.Equal(data, want) { + t.Errorf("buf mismatch:\nwant: %+v\ngot: %+v", want, data) + } +} + +func TestGzipPacket(t *testing.T) { + test := []byte{ + 0x1f, 0x8b, 0x08, 0x08, 0x92, 0x4d, 0x81, 0x57, + 0x00, 0x03, 0x74, 0x65, 0x73, 0x74, 0x00, 0xbb, + 0x72, 0x78, 0xd3, 0x42, 0x26, 0x06, 0x16, 0x06, + 0x18, 0xf8, 0xff, 0x9f, 0x81, 0x81, 0x11, 0x48, + 0x47, 0x9d, 0x91, 0x0a, 0x01, 0xd1, 0x20, 0x19, + 0x0e, 0x20, 0x66, 0x64, 0x62, 0x66, 0x01, 0x00, + 0xe4, 0x76, 0x9b, 0x75, 0x2c, 0x00, 0x00, 0x00, + } + buf := bytes.NewBuffer(test) + r, err := NewReader(buf) + if err != nil { + t.Error("Unexpected error returned:", err) + t.FailNow() + } + + data, ci, err := r.ReadPacketData() + if err != nil { + t.Error(err) + t.FailNow() + } + if !ci.Timestamp.Equal(time.Date(2014, 9, 18, 12, 13, 14, 1000, time.UTC)) { + t.Error("Invalid time read") + t.FailNow() + } + if ci.CaptureLength != 4 || ci.Length != 8 { + t.Error("Invalid CapLen or Len") + } + want := []byte{1, 2, 3, 4} + if !bytes.Equal(data, want) { + t.Errorf("buf mismatch:\nwant: %+v\ngot: %+v", want, data) + } +} + +func TestTruncatedGzipPacket(t *testing.T) { + test := []byte{ + 0x1f, 0x8b, 0x08, + } + buf := bytes.NewBuffer(test) + _, err := NewReader(buf) + if err == nil { + t.Error("Should fail but did not") + t.FailNow() + } +} + +func TestPacketBufferReuse(t *testing.T) { + test := []byte{ + 0xd4, 0xc3, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00, // magic, maj, min + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // tz, sigfigs + 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // snaplen, linkType + 0x5A, 0xCC, 0x1A, 0x54, 0x01, 0x00, 0x00, 0x00, // sec, usec + 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, // cap len, full len + 0x01, 0x02, 0x03, 0x04, // data + 0x5A, 0xCC, 0x1A, 0x54, 0x01, 0x00, 0x00, 0x00, // sec, usec + 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, // cap len, full len + 0x01, 0x02, 0x03, 0x04, // data + } + + buf := bytes.NewBuffer(test) + r, err := NewReader(buf) + + data1, _, err := r.ReadPacketData() + if err != nil { + t.Error(err) + t.FailNow() + } + if want := []byte{1, 2, 3, 4}; !bytes.Equal(data1, want) { + t.Errorf("buf mismatch:\nwant: %+v\ngot: %+v", want, data1) + } + data2, _, err := r.ReadPacketData() + if err != nil { + t.Error(err) + t.FailNow() + } + for i := range data1 { + data1[i] = 0xff // modify data1 after getting data2, make sure we don't overlap buffers. + } + if want := []byte{1, 2, 3, 4}; !bytes.Equal(data2, want) { + t.Errorf("buf mismatch:\nwant: %+v\ngot: %+v", want, data2) + } +} diff --git a/vendor/github.com/google/gopacket/pcapgo/write.go b/vendor/github.com/google/gopacket/pcapgo/write.go new file mode 100644 index 0000000..bfc312f --- /dev/null +++ b/vendor/github.com/google/gopacket/pcapgo/write.go @@ -0,0 +1,103 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// Package pcapgo provides some native PCAP support, not requiring +// C libpcap to be installed. +package pcapgo + +import ( + "encoding/binary" + "fmt" + "io" + "time" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" +) + +// Writer wraps an underlying io.Writer to write packet data in PCAP +// format. See http://wiki.wireshark.org/Development/LibpcapFileFormat +// for information on the file format. +// +// For those that care, we currently write v2.4 files with nanosecond +// timestamp resolution and little-endian encoding. +type Writer struct { + w io.Writer +} + +const magicMicroseconds = 0xA1B2C3D4 +const versionMajor = 2 +const versionMinor = 4 + +// NewWriter returns a new writer object, for writing packet data out +// to the given writer. If this is a new empty writer (as opposed to +// an append), you must call WriteFileHeader before WritePacket. +// +// // Write a new file: +// f, _ := os.Create("/tmp/file.pcap") +// w := pcapgo.NewWriter(f) +// w.WriteFileHeader(65536, layers.LinkTypeEthernet) // new file, must do this. +// w.WritePacket(gopacket.CaptureInfo{...}, data1) +// f.Close() +// // Append to existing file (must have same snaplen and linktype) +// f2, _ := os.OpenFile("/tmp/file.pcap", os.O_APPEND, 0700) +// w2 := pcapgo.NewWriter(f2) +// // no need for file header, it's already written. +// w2.WritePacket(gopacket.CaptureInfo{...}, data2) +// f2.Close() +func NewWriter(w io.Writer) *Writer { + return &Writer{w: w} +} + +// WriteFileHeader writes a file header out to the writer. +// This must be called exactly once per output. +func (w *Writer) WriteFileHeader(snaplen uint32, linktype layers.LinkType) error { + var buf [24]byte + binary.LittleEndian.PutUint32(buf[0:4], magicMicroseconds) + binary.LittleEndian.PutUint16(buf[4:6], versionMajor) + binary.LittleEndian.PutUint16(buf[6:8], versionMinor) + // bytes 8:12 stay 0 (timezone = UTC) + // bytes 12:16 stay 0 (sigfigs is always set to zero, according to + // http://wiki.wireshark.org/Development/LibpcapFileFormat + binary.LittleEndian.PutUint32(buf[16:20], snaplen) + binary.LittleEndian.PutUint32(buf[20:24], uint32(linktype)) + _, err := w.w.Write(buf[:]) + return err +} + +const nanosPerMicro = 1000 + +func (w *Writer) writePacketHeader(ci gopacket.CaptureInfo) error { + var buf [16]byte + + t := ci.Timestamp + if t.IsZero() { + t = time.Now() + } + secs := t.Unix() + usecs := t.Nanosecond() / nanosPerMicro + binary.LittleEndian.PutUint32(buf[0:4], uint32(secs)) + binary.LittleEndian.PutUint32(buf[4:8], uint32(usecs)) + binary.LittleEndian.PutUint32(buf[8:12], uint32(ci.CaptureLength)) + binary.LittleEndian.PutUint32(buf[12:16], uint32(ci.Length)) + _, err := w.w.Write(buf[:]) + return err +} + +// WritePacket writes the given packet data out to the file. +func (w *Writer) WritePacket(ci gopacket.CaptureInfo, data []byte) error { + if ci.CaptureLength != len(data) { + return fmt.Errorf("capture length %d does not match data length %d", ci.CaptureLength, len(data)) + } + if ci.CaptureLength > ci.Length { + return fmt.Errorf("invalid capture info %+v: capture length > length", ci) + } + if err := w.writePacketHeader(ci); err != nil { + return fmt.Errorf("error writing packet header: %v", err) + } + _, err := w.w.Write(data) + return err +} diff --git a/vendor/github.com/google/gopacket/pcapgo/write_test.go b/vendor/github.com/google/gopacket/pcapgo/write_test.go new file mode 100644 index 0000000..5b87d6a --- /dev/null +++ b/vendor/github.com/google/gopacket/pcapgo/write_test.go @@ -0,0 +1,71 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package pcapgo + +import ( + "bytes" + "github.com/google/gopacket" + "testing" + "time" +) + +func TestWriteHeader(t *testing.T) { + var buf bytes.Buffer + w := NewWriter(&buf) + w.WriteFileHeader(0x1234, 0x56) + want := []byte{ + 0xd4, 0xc3, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x34, 0x12, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, + } + if got := buf.Bytes(); !bytes.Equal(got, want) { + t.Errorf("buf mismatch:\nwant: %+v\ngot: %+v", want, got) + } +} + +func TestWritePacket(t *testing.T) { + ci := gopacket.CaptureInfo{ + Timestamp: time.Unix(0x01020304, 0xAA*1000), + Length: 0xABCD, + CaptureLength: 10, + } + data := []byte{9, 8, 7, 6, 5, 4, 3, 2, 1, 0} + var buf bytes.Buffer + w := NewWriter(&buf) + w.WritePacket(ci, data) + want := []byte{ + 0x04, 0x03, 0x02, 0x01, 0xAA, 0x00, 0x00, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0xCD, 0xAB, 0x00, 0x00, + 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + } + if got := buf.Bytes(); !bytes.Equal(got, want) { + t.Errorf("buf mismatch:\nwant: %+v\ngot: %+v", want, got) + } +} + +func TestCaptureInfoErrors(t *testing.T) { + data := []byte{1, 2, 3, 4} + ts := time.Unix(0, 0) + for _, test := range []gopacket.CaptureInfo{ + gopacket.CaptureInfo{ + Timestamp: ts, + Length: 5, + CaptureLength: 5, + }, + gopacket.CaptureInfo{ + Timestamp: ts, + Length: 3, + CaptureLength: 4, + }, + } { + var buf bytes.Buffer + w := NewWriter(&buf) + if err := w.WritePacket(test, data); err == nil { + t.Errorf("CaptureInfo %+v should have error", test) + } + } +} |