2024-03-31 09:31:50 +11:00
|
|
|
/*
|
|
|
|
Copyright 2024 Josh Deprez
|
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
2024-03-10 11:57:03 +11:00
|
|
|
// Package aurp implements types for encoding and decoding AppleTalk
|
|
|
|
// Update-Based Routing Protocol (AURP, RFC 1504) messages.
|
|
|
|
package aurp
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/binary"
|
|
|
|
"fmt"
|
2024-03-15 15:17:21 +11:00
|
|
|
"io"
|
2024-03-10 11:57:03 +11:00
|
|
|
)
|
|
|
|
|
2024-03-15 15:17:21 +11:00
|
|
|
// Header represents an AURP packet header. It includes the AURP-Tr header,
|
|
|
|
// which includes the domain header.
|
2024-03-10 11:57:03 +11:00
|
|
|
type Header struct {
|
2024-03-17 13:31:26 +11:00
|
|
|
TrHeader
|
2024-03-15 15:17:21 +11:00
|
|
|
|
2024-03-10 11:57:03 +11:00
|
|
|
CommandCode CmdCode
|
|
|
|
Flags RoutingFlag
|
|
|
|
}
|
|
|
|
|
2024-03-15 15:17:21 +11:00
|
|
|
// WriteTo writes the encoded form of the header to w.
|
|
|
|
func (h *Header) WriteTo(w io.Writer) (int64, error) {
|
|
|
|
a := acc(w)
|
2024-03-17 13:31:26 +11:00
|
|
|
a.writeTo(&h.TrHeader)
|
2024-03-15 15:17:21 +11:00
|
|
|
a.write16(uint16(h.CommandCode))
|
|
|
|
a.write16(uint16(h.Flags))
|
|
|
|
return a.ret()
|
|
|
|
}
|
|
|
|
|
2024-03-17 13:31:26 +11:00
|
|
|
func parseHeader(p []byte) (Header, []byte, error) {
|
2024-03-15 16:15:24 +11:00
|
|
|
if len(p) < 4 {
|
2024-03-17 13:31:26 +11:00
|
|
|
return Header{}, p, fmt.Errorf("insufficient input length %d for header", len(p))
|
2024-03-15 16:15:24 +11:00
|
|
|
}
|
2024-03-17 13:31:26 +11:00
|
|
|
return Header{
|
2024-03-15 16:15:24 +11:00
|
|
|
CommandCode: CmdCode(binary.BigEndian.Uint16(p[:2])),
|
|
|
|
Flags: RoutingFlag(binary.BigEndian.Uint16(p[2:4])),
|
|
|
|
}, p[4:], nil
|
|
|
|
}
|
|
|
|
|
2024-03-10 11:57:03 +11:00
|
|
|
// CmdCode is the command code used in AURP packets.
|
|
|
|
type CmdCode uint16
|
|
|
|
|
|
|
|
// Various command codes.
|
|
|
|
const (
|
|
|
|
CmdCodeRIReq CmdCode = 0x0001
|
|
|
|
CmdCodeRIRsp CmdCode = 0x0002
|
|
|
|
CmdCodeRIAck CmdCode = 0x0003
|
|
|
|
CmdCodeRIUpd CmdCode = 0x0004
|
|
|
|
CmdCodeRD CmdCode = 0x0005
|
|
|
|
CmdCodeZoneReq CmdCode = 0x0006 // has subcodes
|
|
|
|
CmdCodeZoneRsp CmdCode = 0x0007 // has subcodes
|
|
|
|
CmdCodeOpenReq CmdCode = 0x0008
|
|
|
|
CmdCodeOpenRsp CmdCode = 0x0009
|
|
|
|
CmdCodeTickle CmdCode = 0x000e
|
|
|
|
CmdCodeTickleAck CmdCode = 0x000f
|
|
|
|
)
|
|
|
|
|
|
|
|
// RoutingFlag is used in the flags field
|
|
|
|
type RoutingFlag uint16
|
|
|
|
|
|
|
|
const (
|
2024-03-22 14:20:31 +11:00
|
|
|
// Open-Req and RI-Req (SUI flags)
|
2024-03-10 11:57:03 +11:00
|
|
|
RoutingFlagSUINA RoutingFlag = 0x4000
|
|
|
|
RoutingFlagSUINDOrNRC RoutingFlag = 0x2000
|
|
|
|
RoutingFlagSUINDC RoutingFlag = 0x1000
|
|
|
|
RoutingFlagSUIZC RoutingFlag = 0x0800
|
|
|
|
|
2024-03-22 14:20:31 +11:00
|
|
|
// The combination of the above four flags (the SUI flags).
|
|
|
|
RoutingFlagAllSUI RoutingFlag = 0x7800
|
|
|
|
|
2024-03-10 11:57:03 +11:00
|
|
|
// RI-Rsp and GDZL-Rsp
|
|
|
|
RoutingFlagLast RoutingFlag = 0x8000
|
|
|
|
|
2024-03-22 14:20:31 +11:00
|
|
|
// Open-Rsp (environment flags)
|
2024-03-10 11:57:03 +11:00
|
|
|
RoutingFlagRemappingActive RoutingFlag = 0x4000
|
|
|
|
RoutingFlagHopCountReduction RoutingFlag = 0x2000
|
|
|
|
RoutingFlagReservedEnv RoutingFlag = 0x1800
|
|
|
|
|
|
|
|
// RI-Ack
|
|
|
|
RoutingFlagSendZoneInfo RoutingFlag = 0x4000
|
|
|
|
)
|
2024-03-15 15:17:21 +11:00
|
|
|
|
|
|
|
// Packet represents a full AURP packet, not including UDP or lower layers, but
|
|
|
|
// including the domain header and higher layers.
|
|
|
|
type Packet interface {
|
|
|
|
io.WriterTo
|
|
|
|
}
|
|
|
|
|
2024-04-05 10:43:29 +11:00
|
|
|
// Inc increments a uint16. It avoids 0 (65535 + 1 = 1).
|
|
|
|
func Inc(p *uint16) {
|
|
|
|
*p++
|
|
|
|
if *p == 0 {
|
|
|
|
*p++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-15 16:15:24 +11:00
|
|
|
// 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.)
|
2024-03-24 21:31:11 +11:00
|
|
|
func ParsePacket(p []byte) (DomainHeader, Packet, error) {
|
2024-03-22 16:14:55 +11:00
|
|
|
dh, p, err := ParseDomainHeader(p)
|
2024-03-15 16:15:24 +11:00
|
|
|
if err != nil {
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, nil, err
|
2024-03-15 16:15:24 +11:00
|
|
|
}
|
|
|
|
if dh.Version != 1 {
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, nil, fmt.Errorf("unsupported domain header version %d", dh.Version)
|
2024-03-15 16:15:24 +11:00
|
|
|
}
|
|
|
|
switch dh.PacketType {
|
|
|
|
case PacketTypeAppleTalk:
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, &AppleTalkPacket{
|
2024-03-15 16:15:24 +11:00
|
|
|
DomainHeader: dh,
|
|
|
|
Data: p,
|
|
|
|
}, nil
|
|
|
|
|
|
|
|
case PacketTypeRouting:
|
|
|
|
tr, p, err := parseTrHeader(p)
|
|
|
|
if err != nil {
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, nil, err
|
2024-03-15 16:15:24 +11:00
|
|
|
}
|
|
|
|
tr.DomainHeader = dh
|
|
|
|
h, p, err := parseHeader(p)
|
|
|
|
if err != nil {
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, nil, err
|
2024-03-15 16:15:24 +11:00
|
|
|
}
|
|
|
|
h.TrHeader = tr
|
|
|
|
|
|
|
|
switch h.CommandCode {
|
|
|
|
case CmdCodeOpenReq:
|
|
|
|
oreq, err := parseOpenReq(p)
|
|
|
|
if err != nil {
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, nil, err
|
2024-03-15 16:15:24 +11:00
|
|
|
}
|
|
|
|
oreq.Header = h
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, oreq, nil
|
2024-03-15 16:15:24 +11:00
|
|
|
|
|
|
|
case CmdCodeOpenRsp:
|
|
|
|
orsp, err := parseOpenRsp(p)
|
|
|
|
if err != nil {
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, nil, err
|
2024-03-15 16:15:24 +11:00
|
|
|
}
|
|
|
|
orsp.Header = h
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, orsp, nil
|
2024-03-15 16:38:02 +11:00
|
|
|
|
2024-03-15 16:49:53 +11:00
|
|
|
case CmdCodeRIReq:
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, &RIReqPacket{
|
2024-03-15 16:49:53 +11:00
|
|
|
Header: h,
|
|
|
|
}, nil
|
|
|
|
|
|
|
|
case CmdCodeRIRsp:
|
2024-03-17 12:56:56 +11:00
|
|
|
rir, err := parseRIRsp(p)
|
|
|
|
if err != nil {
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, nil, err
|
2024-03-17 12:56:56 +11:00
|
|
|
}
|
|
|
|
rir.Header = h
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, rir, nil
|
2024-03-15 16:49:53 +11:00
|
|
|
|
|
|
|
case CmdCodeRIAck:
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, &RIAckPacket{
|
2024-03-15 16:49:53 +11:00
|
|
|
Header: h,
|
|
|
|
}, nil
|
|
|
|
|
2024-03-16 22:38:20 +11:00
|
|
|
case CmdCodeRIUpd:
|
|
|
|
riu, err := parseRIUpd(p)
|
|
|
|
if err != nil {
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, nil, err
|
2024-03-16 22:38:20 +11:00
|
|
|
}
|
|
|
|
riu.Header = h
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, riu, nil
|
2024-03-16 22:38:20 +11:00
|
|
|
|
2024-03-17 13:35:50 +11:00
|
|
|
case CmdCodeRD:
|
|
|
|
rd, err := parseRD(p)
|
|
|
|
if err != nil {
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, nil, err
|
2024-03-17 13:35:50 +11:00
|
|
|
}
|
|
|
|
rd.Header = h
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, rd, nil
|
2024-03-17 13:35:50 +11:00
|
|
|
|
2024-03-17 18:19:36 +11:00
|
|
|
case CmdCodeZoneReq:
|
|
|
|
sc, p, err := parseSubcode(p)
|
|
|
|
if err != nil {
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, nil, err
|
2024-03-17 18:19:36 +11:00
|
|
|
}
|
|
|
|
switch sc {
|
2024-03-17 21:08:18 +11:00
|
|
|
case SubcodeZoneInfoReq:
|
2024-03-17 18:19:36 +11:00
|
|
|
zir, err := parseZIReqPacket(p)
|
|
|
|
if err != nil {
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, nil, err
|
2024-03-17 18:19:36 +11:00
|
|
|
}
|
|
|
|
zir.Header = h
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, zir, nil
|
2024-03-17 18:19:36 +11:00
|
|
|
|
2024-03-17 21:08:18 +11:00
|
|
|
case SubcodeGetDomainZoneList:
|
|
|
|
gdzl, err := parseGDZLReqPacket(p)
|
|
|
|
if err != nil {
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, nil, err
|
2024-03-17 21:08:18 +11:00
|
|
|
}
|
|
|
|
gdzl.Header = h
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, gdzl, nil
|
2024-03-17 18:19:36 +11:00
|
|
|
|
2024-03-17 21:08:18 +11:00
|
|
|
case SubcodeGetZonesNet:
|
|
|
|
gzn, err := parseGZNReqPacket(p)
|
|
|
|
if err != nil {
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, nil, err
|
2024-03-17 21:08:18 +11:00
|
|
|
}
|
|
|
|
gzn.Header = h
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, gzn, nil
|
2024-03-17 18:19:36 +11:00
|
|
|
|
|
|
|
default:
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, nil, fmt.Errorf("unknown subcode %d", sc)
|
2024-03-17 18:19:36 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
case CmdCodeZoneRsp:
|
|
|
|
sc, p, err := parseSubcode(p)
|
|
|
|
if err != nil {
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, nil, err
|
2024-03-17 18:19:36 +11:00
|
|
|
}
|
|
|
|
switch sc {
|
2024-03-17 21:08:18 +11:00
|
|
|
case SubcodeZoneInfoNonExt, SubcodeZoneInfoExt:
|
2024-03-17 18:19:36 +11:00
|
|
|
zir, err := parseZIRspPacket(p)
|
|
|
|
if err != nil {
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, nil, err
|
2024-03-17 18:19:36 +11:00
|
|
|
}
|
|
|
|
zir.Header = h
|
2024-03-17 21:08:18 +11:00
|
|
|
zir.Subcode = sc // 1 or 2, only known at this layer
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, zir, nil
|
2024-03-17 18:19:36 +11:00
|
|
|
|
2024-03-17 21:08:18 +11:00
|
|
|
case SubcodeGetDomainZoneList:
|
|
|
|
gdzl, err := parseGDZLRspPacket(p)
|
|
|
|
if err != nil {
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, nil, err
|
2024-03-17 21:08:18 +11:00
|
|
|
}
|
|
|
|
gdzl.Header = h
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, gdzl, nil
|
2024-03-17 18:19:36 +11:00
|
|
|
|
2024-03-17 21:08:18 +11:00
|
|
|
case SubcodeGetZonesNet:
|
|
|
|
gzn, err := parseGZNRspPacket(p)
|
|
|
|
if err != nil {
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, nil, err
|
2024-03-17 21:08:18 +11:00
|
|
|
}
|
|
|
|
gzn.Header = h
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, gzn, nil
|
2024-03-17 18:19:36 +11:00
|
|
|
|
|
|
|
default:
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, nil, fmt.Errorf("unknown subcode %d", sc)
|
2024-03-17 18:19:36 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
case CmdCodeTickle:
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, &TicklePacket{
|
2024-03-17 18:19:36 +11:00
|
|
|
Header: h,
|
|
|
|
}, nil
|
|
|
|
|
|
|
|
case CmdCodeTickleAck:
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, &TickleAckPacket{
|
2024-03-17 18:19:36 +11:00
|
|
|
Header: h,
|
|
|
|
}, nil
|
|
|
|
|
2024-03-15 16:38:02 +11:00
|
|
|
default:
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, nil, fmt.Errorf("unknown routing packet command code %d", h.CommandCode)
|
2024-03-15 16:15:24 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
2024-03-24 21:31:11 +11:00
|
|
|
return dh, nil, fmt.Errorf("unsupported domain header packet type %d", dh.PacketType)
|
2024-03-15 16:15:24 +11:00
|
|
|
}
|
2024-03-15 15:17:21 +11:00
|
|
|
}
|