mirror of
https://github.com/danderson/netboot.git
synced 2025-08-07 07:07:17 +02:00
159 lines
3.9 KiB
Go
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
|
|
}
|