Make pcap.Reader's API look like bufio.Scanner's.

The Next/Packet/Err structure makes read loops much
cleaner, and presents a more familiar interface for people
familiar with the stdlib.
This commit is contained in:
David Anderson 2016-02-27 21:21:10 -08:00
parent 6b9cba0085
commit 178a83f3e0
3 changed files with 42 additions and 28 deletions

View File

@ -27,6 +27,9 @@ type Reader struct {
r io.Reader r io.Reader
order binary.ByteOrder order binary.ByteOrder
tmult int64 tmult int64
pkt *Packet
err error
} }
// Packet is one raw packet and its metadata. // Packet is one raw packet and its metadata.
@ -94,8 +97,26 @@ func NewReader(r io.Reader) (*Reader, error) {
return ret, nil return ret, nil
} }
// Next returns the next packet in r. // Packet returns the packet read by the last call to Next.
func (r *Reader) Next() (*Packet, error) { func (r *Reader) Packet() *Packet {
return r.pkt
}
// Err returns the first non-EOF error encountered by the Reader.
func (r *Reader) Err() error {
if r.err == io.EOF {
return nil
}
return r.err
}
// Next advances the Reader to the next packet in the input, which
// will then be available through the Packet method. It returns false
// when the Reader stops, either by reaching the end of the input or
// an error. After Next returns false, the Err method will return any
// error that occured while reading, except that if it was io.EOF, Err
// will return nil.
func (r *Reader) Next() bool {
hdr := struct { hdr := struct {
Sec uint32 Sec uint32
SubSec uint32 SubSec uint32
@ -104,17 +125,20 @@ func (r *Reader) Next() (*Packet, error) {
}{} }{}
if err := binary.Read(r.r, r.order, &hdr); err != nil { if err := binary.Read(r.r, r.order, &hdr); err != nil {
return nil, err r.err = err
return false
} }
bs := make([]byte, hdr.Len) bs := make([]byte, hdr.Len)
if _, err := io.ReadFull(r.r, bs); err != nil { if _, err := io.ReadFull(r.r, bs); err != nil {
return nil, err r.err = err
return false
} }
return &Packet{ r.pkt = &Packet{
Timestamp: time.Unix(int64(hdr.Sec), r.tmult*int64(hdr.SubSec)), Timestamp: time.Unix(int64(hdr.Sec), r.tmult*int64(hdr.SubSec)),
Length: int(hdr.OrigLen), Length: int(hdr.OrigLen),
Bytes: bs, Bytes: bs,
}, nil }
return true
} }

View File

@ -2,7 +2,6 @@ package pcap
import ( import (
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"os" "os"
"testing" "testing"
@ -23,18 +22,15 @@ func TestFiles(t *testing.T) {
if r.LinkType != LinkEthernet { if r.LinkType != LinkEthernet {
t.Errorf("Expected link type %d, got %d", LinkEthernet, r.LinkType) t.Errorf("Expected link type %d, got %d", LinkEthernet, r.LinkType)
} }
pkts := []*Packet{} pkts := []*Packet{}
ReadLoop: for r.Next() {
for { pkts = append(pkts, r.Packet())
pkt, err := r.Next()
if err != nil {
if err == io.EOF {
break ReadLoop
} }
t.Fatalf("Unexpected error reading packets: %s", err) if r.Err() != nil {
} t.Fatalf("Reading packets from %s.pcap: %s", fname, r.Err())
pkts = append(pkts, pkt)
} }
res := pretty.Sprintf("%# v", pkts) res := pretty.Sprintf("%# v", pkts)
expectedFile := fmt.Sprintf("testdata/%s.parsed", fname) expectedFile := fmt.Sprintf("testdata/%s.parsed", fname)
expected, err := ioutil.ReadFile(expectedFile) expected, err := ioutil.ReadFile(expectedFile)

View File

@ -3,7 +3,6 @@ package pcap
import ( import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"io"
"reflect" "reflect"
"testing" "testing"
"time" "time"
@ -63,16 +62,11 @@ func TestReadback(t *testing.T) {
t.Fatalf("Wrote link type %d, read back %d", LinkEthernet, r.LinkType) t.Fatalf("Wrote link type %d, read back %d", LinkEthernet, r.LinkType)
} }
ReadLoop: for r.Next() {
for { readBack = append(readBack, r.Packet())
pkt, err := r.Next()
if err != nil {
if err == io.EOF {
break ReadLoop
} }
t.Fatalf("Unexpected error reading packets: %s", err) if r.Err() != nil {
} t.Fatalf("Reading packets back: %s", r.Err())
readBack = append(readBack, pkt)
} }
if !reflect.DeepEqual(pkts, readBack) { if !reflect.DeepEqual(pkts, readBack) {