WIP
This commit is contained in:
parent
30f8d709d2
commit
5bb3435dc9
3 changed files with 170 additions and 38 deletions
187
aurp/aurp.go
187
aurp/aurp.go
|
@ -39,14 +39,14 @@ func (dh *DomainHeader) WriteTo(w io.Writer) (int64, error) {
|
||||||
return a.ret()
|
return a.ret()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseDomainHeader parses a domain header, returning the DH and the remainder
|
// parseDomainHeader parses a domain header, returning the DH and the remainder
|
||||||
// of the input slice. It does not validate the version or packet type fields.
|
// of the input slice. It does not validate the version or packet type fields.
|
||||||
func ParseDomainHeader(b []byte) (*DomainHeader, []byte, error) {
|
func parseDomainHeader(b []byte) (*DomainHeader, []byte, error) {
|
||||||
ddi, b, err := ParseDomainIdentifier(b)
|
ddi, b, err := parseDomainIdentifier(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, b, err
|
return nil, b, err
|
||||||
}
|
}
|
||||||
sdi, b, err := ParseDomainIdentifier(b)
|
sdi, b, err := parseDomainIdentifier(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, b, err
|
return nil, b, err
|
||||||
}
|
}
|
||||||
|
@ -110,9 +110,9 @@ const (
|
||||||
AuthorityIP
|
AuthorityIP
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParseDomainIdentifier parses a DI from the front of b, and returns the DI and
|
// parseDomainIdentifier parses a DI from the front of b, and returns the DI and
|
||||||
// the remainder of the input slice.
|
// the remainder of the input slice or an error.
|
||||||
func ParseDomainIdentifier(b []byte) (DomainIdentifier, []byte, error) {
|
func parseDomainIdentifier(b []byte) (DomainIdentifier, []byte, error) {
|
||||||
if len(b) < 2 {
|
if len(b) < 2 {
|
||||||
return nil, b, fmt.Errorf("insufficient input length %d for domain identifier", len(b))
|
return nil, b, fmt.Errorf("insufficient input length %d for domain identifier", len(b))
|
||||||
}
|
}
|
||||||
|
@ -140,25 +140,36 @@ func ParseDomainIdentifier(b []byte) (DomainIdentifier, []byte, error) {
|
||||||
|
|
||||||
// TrHeader represent an AURP-Tr packet header. It includes the domain header.
|
// TrHeader represent an AURP-Tr packet header. It includes the domain header.
|
||||||
type TrHeader struct {
|
type TrHeader struct {
|
||||||
DomainHeader
|
*DomainHeader
|
||||||
|
|
||||||
ConnectionID uint16
|
ConnectionID uint16
|
||||||
Sequence uint16 // Note: 65535 is succeeded by 1, not 0
|
Sequence uint16 // Note: 65535 is succeeded by 1, not 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteTo writes the encoded form of the header to w.
|
// WriteTo writes the encoded form of the header to w, including the domain
|
||||||
|
// header.
|
||||||
func (h *TrHeader) WriteTo(w io.Writer) (int64, error) {
|
func (h *TrHeader) WriteTo(w io.Writer) (int64, error) {
|
||||||
a := acc(w)
|
a := acc(w)
|
||||||
a.writeTo(&h.DomainHeader)
|
a.writeTo(h.DomainHeader)
|
||||||
a.write16(h.ConnectionID)
|
a.write16(h.ConnectionID)
|
||||||
a.write16(h.Sequence)
|
a.write16(h.Sequence)
|
||||||
return a.ret()
|
return a.ret()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseTrHeader(p []byte) (*TrHeader, []byte, error) {
|
||||||
|
if len(p) < 4 {
|
||||||
|
return nil, p, fmt.Errorf("insufficient input length %d for tr header", len(p))
|
||||||
|
}
|
||||||
|
return &TrHeader{
|
||||||
|
ConnectionID: binary.BigEndian.Uint16(p[:2]),
|
||||||
|
Sequence: binary.BigEndian.Uint16(p[2:4]),
|
||||||
|
}, p[4:], nil
|
||||||
|
}
|
||||||
|
|
||||||
// Header represents an AURP packet header. It includes the AURP-Tr header,
|
// Header represents an AURP packet header. It includes the AURP-Tr header,
|
||||||
// which includes the domain header.
|
// which includes the domain header.
|
||||||
type Header struct {
|
type Header struct {
|
||||||
TrHeader
|
*TrHeader
|
||||||
|
|
||||||
CommandCode CmdCode
|
CommandCode CmdCode
|
||||||
Flags RoutingFlag
|
Flags RoutingFlag
|
||||||
|
@ -167,12 +178,22 @@ type Header struct {
|
||||||
// WriteTo writes the encoded form of the header to w.
|
// WriteTo writes the encoded form of the header to w.
|
||||||
func (h *Header) WriteTo(w io.Writer) (int64, error) {
|
func (h *Header) WriteTo(w io.Writer) (int64, error) {
|
||||||
a := acc(w)
|
a := acc(w)
|
||||||
a.writeTo(&h.TrHeader)
|
a.writeTo(h.TrHeader)
|
||||||
a.write16(uint16(h.CommandCode))
|
a.write16(uint16(h.CommandCode))
|
||||||
a.write16(uint16(h.Flags))
|
a.write16(uint16(h.Flags))
|
||||||
return a.ret()
|
return a.ret()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseHeader(p []byte) (*Header, []byte, error) {
|
||||||
|
if len(p) < 4 {
|
||||||
|
return nil, p, fmt.Errorf("insufficient input length %d for header", len(p))
|
||||||
|
}
|
||||||
|
return &Header{
|
||||||
|
CommandCode: CmdCode(binary.BigEndian.Uint16(p[:2])),
|
||||||
|
Flags: RoutingFlag(binary.BigEndian.Uint16(p[2:4])),
|
||||||
|
}, p[4:], nil
|
||||||
|
}
|
||||||
|
|
||||||
// CmdCode is the command code used in AURP packets.
|
// CmdCode is the command code used in AURP packets.
|
||||||
type CmdCode uint16
|
type CmdCode uint16
|
||||||
|
|
||||||
|
@ -233,6 +254,10 @@ type OptionTuple struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ot *OptionTuple) WriteTo(w io.Writer) (int64, error) {
|
func (ot *OptionTuple) WriteTo(w io.Writer) (int64, error) {
|
||||||
|
if len(ot.Data) > 254 {
|
||||||
|
return 0, fmt.Errorf("option tuple data too long [%d > 254]", len(ot.Data))
|
||||||
|
}
|
||||||
|
|
||||||
a := acc(w)
|
a := acc(w)
|
||||||
a.write8(uint8(len(ot.Data) + 1))
|
a.write8(uint8(len(ot.Data) + 1))
|
||||||
a.write8(uint8(ot.Type))
|
a.write8(uint8(ot.Type))
|
||||||
|
@ -240,6 +265,20 @@ func (ot *OptionTuple) WriteTo(w io.Writer) (int64, error) {
|
||||||
return a.ret()
|
return a.ret()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseOptionTuple(p []byte) (OptionTuple, []byte, error) {
|
||||||
|
if len(p) < 2 {
|
||||||
|
return OptionTuple{}, p, fmt.Errorf("insufficient input length %d for option tuple", len(p))
|
||||||
|
}
|
||||||
|
olen := int(p[0]) + 1
|
||||||
|
if len(p) < olen {
|
||||||
|
return OptionTuple{}, p, fmt.Errorf("insufficient input for option tuple data length %d", olen)
|
||||||
|
}
|
||||||
|
return OptionTuple{
|
||||||
|
Type: OptionType(p[1]),
|
||||||
|
Data: p[2:olen],
|
||||||
|
}, p[olen:], nil
|
||||||
|
}
|
||||||
|
|
||||||
// OptionType is used to distinguish different options.
|
// OptionType is used to distinguish different options.
|
||||||
type OptionType uint8
|
type OptionType uint8
|
||||||
|
|
||||||
|
@ -249,6 +288,39 @@ const (
|
||||||
// All other types reserved
|
// All other types reserved
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Options []OptionTuple
|
||||||
|
|
||||||
|
func (o Options) WriteTo(w io.Writer) (int64, error) {
|
||||||
|
if len(o) > 255 {
|
||||||
|
return 0, fmt.Errorf("too many options [%d > 255]", len(o))
|
||||||
|
}
|
||||||
|
|
||||||
|
a := acc(w)
|
||||||
|
a.write8(uint8(len(o)))
|
||||||
|
for _, ot := range o {
|
||||||
|
a.writeTo(&ot)
|
||||||
|
}
|
||||||
|
return a.ret()
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseOptions(p []byte) (Options, error) {
|
||||||
|
if len(p) < 1 {
|
||||||
|
return nil, fmt.Errorf("insufficint input length %d for options", len(p))
|
||||||
|
}
|
||||||
|
optc := p[0]
|
||||||
|
opts := make([]OptionTuple, optc)
|
||||||
|
for i := range optc {
|
||||||
|
ot, np, err := parseOptionTuple(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing option %d: %w", i, err)
|
||||||
|
}
|
||||||
|
opts[i] = ot
|
||||||
|
p = np
|
||||||
|
}
|
||||||
|
// TODO: warn about trailing data?
|
||||||
|
return opts, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Packet represents a full AURP packet, not including UDP or lower layers, but
|
// Packet represents a full AURP packet, not including UDP or lower layers, but
|
||||||
// including the domain header and higher layers.
|
// including the domain header and higher layers.
|
||||||
type Packet interface {
|
type Packet interface {
|
||||||
|
@ -257,42 +329,99 @@ type Packet interface {
|
||||||
|
|
||||||
// AppleTalkPacket is for encapsulated AppleTalk traffic.
|
// AppleTalkPacket is for encapsulated AppleTalk traffic.
|
||||||
type AppleTalkPacket struct {
|
type AppleTalkPacket struct {
|
||||||
DomainHeader // where PacketTypeAppleTalk
|
*DomainHeader // where PacketTypeAppleTalk
|
||||||
|
|
||||||
Data []byte
|
Data []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *AppleTalkPacket) WriteTo(w io.Writer) (int64, error) {
|
func (p *AppleTalkPacket) WriteTo(w io.Writer) (int64, error) {
|
||||||
a := acc(w)
|
a := acc(w)
|
||||||
a.writeTo(&p.DomainHeader)
|
a.writeTo(p.DomainHeader)
|
||||||
a.write(p.Data)
|
a.write(p.Data)
|
||||||
return a.ret()
|
return a.ret()
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenReq is used to open a one-way connection between AIRs.
|
// OpenReq is used to open a one-way connection between AIRs.
|
||||||
type OpenReqPacket struct {
|
type OpenReqPacket struct {
|
||||||
Header
|
*Header
|
||||||
|
|
||||||
Version uint16 // currently always 1
|
Version uint16 // currently always 1
|
||||||
//OptionCount uint8 = len(Options)
|
Options Options
|
||||||
Options []OptionTuple
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *OpenReqPacket) WriteTo(w io.Writer) (int64, error) {
|
func (p *OpenReqPacket) WriteTo(w io.Writer) (int64, error) {
|
||||||
if len(p.Options) > 255 {
|
|
||||||
return 0, fmt.Errorf("too many options [%d > 255]", len(p.Options))
|
|
||||||
}
|
|
||||||
|
|
||||||
a := acc(w)
|
a := acc(w)
|
||||||
a.writeTo(&p.Header)
|
a.writeTo(p.Header)
|
||||||
a.write16(p.Version)
|
a.write16(p.Version)
|
||||||
a.write8(uint8(len(p.Options)))
|
a.writeTo(p.Options)
|
||||||
for _, o := range p.Options {
|
|
||||||
a.writeTo(&o)
|
|
||||||
}
|
|
||||||
return a.ret()
|
return a.ret()
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParsePacket(p []byte) (Packet, error) {
|
func parseOpenReq(p []byte) (*OpenReqPacket, error) {
|
||||||
// TODO
|
if len(p) < 3 {
|
||||||
return nil, nil
|
return nil, fmt.Errorf("insufficient input length %d for Open-Req packet", len(p))
|
||||||
|
}
|
||||||
|
opts, err := parseOptions(p[2:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &OpenReqPacket{
|
||||||
|
Version: binary.BigEndian.Uint16(p[:2]),
|
||||||
|
Options: opts,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParsePacket parses the body of a UDP packet for a domain header, and then
|
||||||
|
// based on the packet type, an AURP-Tr header, an AURP routing header, and
|
||||||
|
// then a particular packet type.
|
||||||
|
//
|
||||||
|
// (This function contains the big switch statement.)
|
||||||
|
func ParsePacket(p []byte) (Packet, error) {
|
||||||
|
dh, p, err := parseDomainHeader(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if dh.Version != 1 {
|
||||||
|
return nil, fmt.Errorf("unsupported domain header version %d", dh.Version)
|
||||||
|
}
|
||||||
|
switch dh.PacketType {
|
||||||
|
case PacketTypeAppleTalk:
|
||||||
|
return &AppleTalkPacket{
|
||||||
|
DomainHeader: dh,
|
||||||
|
Data: p,
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
case PacketTypeRouting:
|
||||||
|
tr, p, err := parseTrHeader(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tr.DomainHeader = dh
|
||||||
|
h, p, err := parseHeader(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
h.TrHeader = tr
|
||||||
|
|
||||||
|
switch h.CommandCode {
|
||||||
|
case CmdCodeOpenReq:
|
||||||
|
oreq, err := parseOpenReq(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
oreq.Header = h
|
||||||
|
return oreq, nil
|
||||||
|
|
||||||
|
case CmdCodeOpenRsp:
|
||||||
|
orsp, err := parseOpenRsp(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
orsp.Header = h
|
||||||
|
return orsp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported domain header packet type %d", dh.PacketType)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ func (a *wtacc) write8(x uint8) {
|
||||||
if a.err != nil {
|
if a.err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
a.err = binary.Write(a.w, binary.BigEndian, x)
|
_, a.err = a.w.Write([]byte{x})
|
||||||
if a.err != nil {
|
if a.err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
17
main.go
17
main.go
|
@ -18,15 +18,18 @@ func main() {
|
||||||
// Incoming packet loop
|
// Incoming packet loop
|
||||||
pb := make([]byte, 65536)
|
pb := make([]byte, 65536)
|
||||||
for {
|
for {
|
||||||
plen, _, err := ln.ReadFromUDP(pb)
|
pktlen, _, readErr := ln.ReadFromUDP(pb)
|
||||||
if err != nil {
|
// "Callers should always process
|
||||||
log.Printf("Failed to read packet: %v", err)
|
// the n > 0 bytes returned before considering the error err."
|
||||||
continue
|
|
||||||
|
_, parseErr := aurp.ParsePacket(pb[:pktlen])
|
||||||
|
if parseErr != nil {
|
||||||
|
log.Printf("Failed to parse packet: %v", parseErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = aurp.ParsePacket(pb[:plen])
|
if readErr != nil {
|
||||||
if err != nil {
|
log.Printf("Failed to read packet: %v", readErr)
|
||||||
log.Printf("Failed to parse packet: %v", err)
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue