netboot/pcap/reader.go
2016-03-24 13:12:10 -07:00

159 lines
3.9 KiB
Go

// Copyright 2016 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package pcap implements reading and writing the "classic" libpcap format.
package pcap // import "go.universe.tf/netboot/pcap"
import (
"bufio"
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"time"
)
// LinkType describes the contents of each packet in a pcap.
type LinkType uint32
// Some of the more commonly used LinkTypes.
const (
LinkEthernet LinkType = 1
LinkRaw LinkType = 101
)
// Reader extracts packets from a pcap file.
type Reader struct {
LinkType LinkType
r io.Reader
order binary.ByteOrder
tmult int64
pkt *Packet
err error
}
// Packet is one raw packet and its metadata.
type Packet struct {
Timestamp time.Time
Length int
Bytes []byte
}
// NewReader returns a new Reader that decodes pcap data from r.
func NewReader(r io.Reader) (*Reader, error) {
ret := &Reader{
r: bufio.NewReader(r),
order: binary.LittleEndian,
}
header := struct {
Magic uint32
Major uint16
Minor uint16
// Timezone correction and time accuracy - both 0 in practice.
Ignored uint64
Snaplen uint32
Type uint32
}{}
bs := make([]byte, binary.Size(header))
if _, err := io.ReadFull(ret.r, bs); err != nil {
return nil, fmt.Errorf("reading pcap header: %s", err)
}
// Annoyingly, the header encodings are defined in terms of "same"
// or "opposite" endian, rather than in absolute terms, so reading
// the magic alone (as is intended) doesn't let us figure out what
// endianness to use. However, we can cheat and look at the
// major/minor version numbers instead. Try little-endian first,
// since that's more common these days.
if err := binary.Read(bytes.NewBuffer(bs), ret.order, &header); err != nil {
return nil, err
}
if header.Major == 0x200 && header.Minor == 0x400 {
// Byte order was wrong, read again
ret.order = binary.BigEndian
if err := binary.Read(bytes.NewBuffer(bs), ret.order, &header); err != nil {
return nil, err
}
}
switch header.Magic {
case 0xa1b2c3d4:
// Timestamps are (sec, usec)
ret.tmult = 1000
case 0xa1b23c4d:
// Timestamps are (sec, nsec)
ret.tmult = 1
default:
return nil, errors.New("bad magic")
}
if header.Major != 2 || header.Minor != 4 {
return nil, fmt.Errorf("Unknown pcap version %d.%d", header.Major, header.Minor)
}
ret.LinkType = LinkType(header.Type)
return ret, nil
}
// Packet returns the packet read by the last call to Next.
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 {
Sec uint32
SubSec uint32
Len uint32
OrigLen uint32
}{}
if err := binary.Read(r.r, r.order, &hdr); err != nil {
r.err = err
return false
}
bs := make([]byte, hdr.Len)
if _, err := io.ReadFull(r.r, bs); err != nil {
r.err = err
return false
}
r.pkt = &Packet{
Timestamp: time.Unix(int64(hdr.Sec), r.tmult*int64(hdr.SubSec)),
Length: int(hdr.OrigLen),
Bytes: bs,
}
return true
}