diff --git a/capture/pcap/pcap.go b/capture/pcap/pcap.go index 2e5305a..b0000fd 100644 --- a/capture/pcap/pcap.go +++ b/capture/pcap/pcap.go @@ -1,6 +1,8 @@ package pcap import ( + "bufio" + "compress/gzip" "errors" "fmt" "io" @@ -15,7 +17,8 @@ import ( // Source denotes a pcap file capture source type Source struct { - io.Reader + reader *bufio.Reader + gzipReader *gzip.Reader header Header buf []byte @@ -35,11 +38,17 @@ func NewSource(iface string, r io.Reader) (*Source, error) { if r == nil { return nil, errors.New("nil io.Reader provided") } + obj := Source{ - Reader: r, + reader: bufio.NewReader(r), buf: make([]byte, HeaderSize), } + // Check if the source is compressed + if err := obj.checkCompression(); err != nil { + return nil, err + } + // Parse the main header if err := obj.read(obj.buf); err != nil { return nil, err @@ -47,7 +56,7 @@ func NewSource(iface string, r io.Reader) (*Source, error) { // If required, swap endianess as defined here: // https://wiki.wireshark.org/Development/LibpcapFileFormat - obj.header = *(*Header)(unsafe.Pointer(&obj.buf[0])) + obj.header = *(*Header)(unsafe.Pointer(&obj.buf[0])) // #nosec G103 if obj.header.MagicNumber == MagicSwappedEndianess { obj.header = obj.header.SwapEndianess() obj.swapEndianess = true @@ -163,8 +172,8 @@ func (s *Source) Unblock() error { // Close stops / closes the capture source func (s *Source) Close() error { - if readCloser, ok := s.Reader.(io.ReadCloser); ok { - return readCloser.Close() + if s.gzipReader != nil { + return s.gzipReader.Close() } return nil } @@ -184,6 +193,30 @@ func (s *Source) PacketAddCallbackFn(fn func(payload []byte, totalLen uint32, pk //////////////////////////////////////////////////////////////////////// +func (s *Source) checkCompression() error { + + // Check if the first two bytes match a gzip file magic in either + // endianess + magicBytes, err := s.reader.Peek(2) + if err != nil { + return err + } + if (magicBytes[0] == 0x1f && magicBytes[1] == 0x8b) || + (magicBytes[0] == 0x8b && magicBytes[1] == 0x1f) { + + // Attempt to open a new gzip decompressor + s.gzipReader, err = gzip.NewReader(s.reader) + if err != nil { + return err + } + + // Wrap a new bufio.Reader around the gzip decompressor + s.reader = bufio.NewReader(s.gzipReader) + } + + return nil +} + func (s *Source) nextPacket() (pktHeader PacketHeader, err error) { pktHeader, err = s.nextPacketHeader() if err != nil { @@ -209,7 +242,7 @@ func (s *Source) nextPacketHeader() (PacketHeader, error) { if err := s.read(s.buf[:PacketHeaderSize]); err != nil { return PacketHeader{}, err } - return *(*PacketHeader)(unsafe.Pointer(&s.buf[0])), nil + return *(*PacketHeader)(unsafe.Pointer(&s.buf[0])), nil // #nosec G103 } func (s *Source) nextPacketData(snapLen int) error { @@ -222,7 +255,7 @@ func (s *Source) nextPacketData(snapLen int) error { } func (s *Source) read(buf []byte) error { - n, err := io.ReadAtLeast(s.Reader, buf, len(buf)) + n, err := io.ReadAtLeast(s.reader, buf, len(buf)) if err != nil { return err } diff --git a/capture/pcap/pcap_data_test.go b/capture/pcap/pcap_data_test.go deleted file mode 100644 index 4a30d7b..0000000 --- a/capture/pcap/pcap_data_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package pcap - -const ( - pcapTestInputNPackets = 6 - - // Little-Endian encoded test file - pcapTestInputLE = `1MOyoQIABAAAAAAAAAAAAIAAAAABAAAAUGsdZFnNCwCAAAAA1gAAAKDOyN35EQANuUFBnQgARRAA -yJAkQABABpWUCgAAAgoAAGYAFpZO1PdUjbI6PPKAGAIKFSIAAAEBCArnczKjTaduVQAAAHDfcY5p -P/X3csiPQsLtX7Xm7MZFOeQTMDGDPINvt8pf9DAOTlLpqdAV13H9I9Q0MAi27I7VgpvIzTjtUGsd -ZPPOCwBCAAAAQgAAAAANuUFBnaDOyN35EQgARUgANNmjQABABkxxCgAAZgoAAAKWTgAWsjo88tT3 -VSGAECUAt2gAAAEBCApNp26E53Myo1BrHWR11gsAQgAAAEIAAAAADblBQZ1gIcA6U50IAEUAADSi -mkAAQAYHagoAAJxjUCLUyZQBu+usPVlPzzIZgBAA3kndAAABAQgKR++equlDVSZQax1kddYLAEIA -AABCAAAAAA25QUGdYCHAOlOdCABFAAA0optAAEAGB2kKAACcY1Ai1MmUAbvrrD1ZT88yToAQAN5J -qAAAAQEICkfvnqrpQ1UmUGsdZFrXCwCAAAAAZwQAAAANuUFBnWAhwDpTnQgARQAEWaKcQABABgND -CgAAnGNQItTJlAG766w9WU/PMk6AGADeJnEAAAEBCApH756r6UNVJhcDAQQgCUTVGZxgcNdm1N/6 -oG7hQDTn0wHP6LetGdJeU3Eio2u5J07Pr4W6tBf62rWvyGuWfqmVq1wXDKbiUGsdZO9QDACAAAAA -lwEAAGAhwDpTnQANuUFBnQgARQABiYE/QADhBoZvY1Ai1AoAAJwBu8mUT88yTuusQX6AGAH2TIAA -AAEBCArpQ1VQR++eqxcDAQFQmHgX4TvaRftrJW81aPAL9M/KJQ3BpjJzgYTAU3aCqGZIHKg9Xfvs -5h8cxBpLErB5vklPfUYpZ6Yw` - - // Big-Endian encoded test file - pcapTestInputBE = `obLD1AACAAQAAAAAAAAAAAAAAIAAAAABZB1rUAALzVkAAACAAAAA1qDOyN35EQANuUFBnQgARRAA -yJAkQABABpWUCgAAAgoAAGYAFpZO1PdUjbI6PPKAGAIKFSIAAAEBCArnczKjTaduVQAAAHDfcY5p -P/X3csiPQsLtX7Xm7MZFOeQTMDGDPINvt8pf9DAOTlLpqdAV13H9I9Q0MAi27I7VgpvIzTjtZB1r -UAALzvMAAABCAAAAQgANuUFBnaDOyN35EQgARUgANNmjQABABkxxCgAAZgoAAAKWTgAWsjo88tT3 -VSGAECUAt2gAAAEBCApNp26E53Myo2Qda1AAC9Z1AAAAQgAAAEIADblBQZ1gIcA6U50IAEUAADSi -mkAAQAYHagoAAJxjUCLUyZQBu+usPVlPzzIZgBAA3kndAAABAQgKR++equlDVSZkHWtQAAvWdQAA -AEIAAABCAA25QUGdYCHAOlOdCABFAAA0optAAEAGB2kKAACcY1Ai1MmUAbvrrD1ZT88yToAQAN5J -qAAAAQEICkfvnqrpQ1UmZB1rUAAL11oAAACAAAAEZwANuUFBnWAhwDpTnQgARQAEWaKcQABABgND -CgAAnGNQItTJlAG766w9WU/PMk6AGADeJnEAAAEBCApH756r6UNVJhcDAQQgCUTVGZxgcNdm1N/6 -oG7hQDTn0wHP6LetGdJeU3Eio2u5J07Pr4W6tBf62rWvyGuWfqmVq1wXDKbiZB1rUAAMUO8AAACA -AAABl2AhwDpTnQANuUFBnQgARQABiYE/QADhBoZvY1Ai1AoAAJwBu8mUT88yTuusQX6AGAH2TIAA -AAEBCArpQ1VQR++eqxcDAQFQmHgX4TvaRftrJW81aPAL9M/KJQ3BpjJzgYTAU3aCqGZIHKg9Xfvs -5h8cxBpLErB5vklPfUYpZ6Yw` -) diff --git a/capture/pcap/pcap_test.go b/capture/pcap/pcap_test.go index dd81a9c..3f6aec3 100644 --- a/capture/pcap/pcap_test.go +++ b/capture/pcap/pcap_test.go @@ -2,10 +2,12 @@ package pcap import ( "bytes" - "encoding/base64" + "embed" "io" + "io/fs" "net" "os" + "path/filepath" "testing" "github.com/fako1024/slimcap/capture" @@ -14,7 +16,12 @@ import ( "github.com/stretchr/testify/require" ) -var testDataLE, testDataBE []byte +const ( + pcapTestInputNPackets = 6 + testDataPath = "testdata" +) + +var testData []fs.DirEntry func TestInvalidInput(t *testing.T) { @@ -53,12 +60,14 @@ func TestInvalidInput(t *testing.T) { func TestReader(t *testing.T) { - for _, testData := range [][]byte{ - testDataLE, - testDataBE, - } { + for _, dirent := range testData { + file, err := os.Open(filepath.Join(testDataPath, dirent.Name())) + require.Nil(t, err) + defer func() { + require.Nil(t, file.Close()) + }() - src, err := NewSource("pcap", bytes.NewBuffer(testData)) + src, err := NewSource("pcap", file) require.Nil(t, err) require.Equal(t, &link.Link{ @@ -86,7 +95,7 @@ func TestReader(t *testing.T) { require.Equal(t, capture.PacketUnknown, pktType) require.Nil(t, ipLayer) - src.NextPacketFn(func(payload []byte, totalLen uint32, pktType, ipLayerOffset byte) error { + err = src.NextPacketFn(func(payload []byte, totalLen uint32, pktType, ipLayerOffset byte) error { require.Nil(t, payload) require.Zero(t, totalLen) require.Equal(t, capture.PacketUnknown, pktType) @@ -107,11 +116,14 @@ func TestReader(t *testing.T) { func TestMockPipe(t *testing.T) { - for _, testData := range [][]byte{ - testDataLE, - testDataBE, - } { - src, err := NewSource("pcap", bytes.NewBuffer(testData)) + for _, dirent := range testData { + file, err := os.Open(filepath.Join(testDataPath, dirent.Name())) + require.Nil(t, err) + defer func() { + require.Nil(t, file.Close()) + }() + + src, err := NewSource("pcap", file) require.Nil(t, err) // Setup a mock source @@ -232,12 +244,14 @@ func TestCaptureMethods(t *testing.T) { } func testCaptureMethods(t *testing.T, fn func(t *testing.T, src *Source)) { + for _, dirent := range testData { + file, err := os.Open(filepath.Join(testDataPath, dirent.Name())) + require.Nil(t, err) + defer func() { + require.Nil(t, file.Close()) + }() - for _, testData := range [][]byte{ - testDataLE, - testDataBE, - } { - src, err := NewSource("pcap", bytes.NewBuffer(testData)) + src, err := NewSource("pcap", file) require.Nil(t, err) for i := 0; i < pcapTestInputNPackets; i++ { @@ -248,13 +262,13 @@ func testCaptureMethods(t *testing.T, fn func(t *testing.T, src *Source)) { } } +//go:embed testdata/* +var pcaps embed.FS + func TestMain(m *testing.M) { var err error - if testDataLE, err = base64.StdEncoding.DecodeString(pcapTestInputLE); err != nil { - panic(err) - } - if testDataBE, err = base64.StdEncoding.DecodeString(pcapTestInputBE); err != nil { + if testData, err = pcaps.ReadDir(testDataPath); err != nil { panic(err) } diff --git a/capture/pcap/testdata/be.pcap b/capture/pcap/testdata/be.pcap new file mode 100644 index 0000000..586882d Binary files /dev/null and b/capture/pcap/testdata/be.pcap differ diff --git a/capture/pcap/testdata/be.pcap.gz b/capture/pcap/testdata/be.pcap.gz new file mode 100644 index 0000000..2be88f5 Binary files /dev/null and b/capture/pcap/testdata/be.pcap.gz differ diff --git a/capture/pcap/testdata/le.pcap b/capture/pcap/testdata/le.pcap new file mode 100644 index 0000000..8b0e17d Binary files /dev/null and b/capture/pcap/testdata/le.pcap differ diff --git a/capture/pcap/testdata/le.pcap.gz b/capture/pcap/testdata/le.pcap.gz new file mode 100644 index 0000000..6cc91f1 Binary files /dev/null and b/capture/pcap/testdata/le.pcap.gz differ