Packet formats complete

This commit is contained in:
Josh Deprez 2024-03-17 21:08:18 +11:00
parent 3d66d67713
commit 56d69309ea
Signed by: josh
SSH key fingerprint: SHA256:zZji7w1Ilh2RuUpbQcqkLPrqmRwpiCSycbF2EfKm6Kw
2 changed files with 235 additions and 29 deletions

View file

@ -4,7 +4,6 @@ package aurp
import (
"encoding/binary"
"errors"
"fmt"
"io"
)
@ -214,7 +213,7 @@ func ParsePacket(p []byte) (Packet, error) {
return nil, err
}
switch sc {
case CmdSubcodeZoneInfoReq:
case SubcodeZoneInfoReq:
zir, err := parseZIReqPacket(p)
if err != nil {
return nil, err
@ -222,11 +221,21 @@ func ParsePacket(p []byte) (Packet, error) {
zir.Header = h
return zir, nil
case CmdSubcodeGetDomainZoneList:
// TODO
case SubcodeGetDomainZoneList:
gdzl, err := parseGDZLReqPacket(p)
if err != nil {
return nil, err
}
gdzl.Header = h
return gdzl, nil
case CmdSubcodeGetZonesNet:
// TODO
case SubcodeGetZonesNet:
gzn, err := parseGZNReqPacket(p)
if err != nil {
return nil, err
}
gzn.Header = h
return gzn, nil
default:
return nil, fmt.Errorf("unknown subcode %d", sc)
@ -238,20 +247,30 @@ func ParsePacket(p []byte) (Packet, error) {
return nil, err
}
switch sc {
case CmdSubcodeZoneInfoNonExt, CmdSubcodeZoneInfoExt:
case SubcodeZoneInfoNonExt, SubcodeZoneInfoExt:
zir, err := parseZIRspPacket(p)
if err != nil {
return nil, err
}
zir.Header = h
zir.Subcode = sc
zir.Subcode = sc // 1 or 2, only known at this layer
return zir, nil
case CmdSubcodeGetDomainZoneList:
// TODO
case SubcodeGetDomainZoneList:
gdzl, err := parseGDZLRspPacket(p)
if err != nil {
return nil, err
}
gdzl.Header = h
return gdzl, nil
case CmdSubcodeGetZonesNet:
// TODO
case SubcodeGetZonesNet:
gzn, err := parseGZNRspPacket(p)
if err != nil {
return nil, err
}
gzn.Header = h
return gzn, nil
default:
return nil, fmt.Errorf("unknown subcode %d", sc)
@ -274,6 +293,4 @@ func ParsePacket(p []byte) (Packet, error) {
default:
return nil, fmt.Errorf("unsupported domain header packet type %d", dh.PacketType)
}
return nil, errors.New("unimplemented packet handling")
}

View file

@ -6,29 +6,28 @@ import (
"io"
)
// CmdSubcode is used to distinguish types of zone request/response.
type CmdSubcode uint16
// Subcode is used to distinguish types of zone request/response.
type Subcode uint16
// Various subcodes.
const (
CmdSubcodeZoneInfoReq CmdSubcode = 0x0001
CmdSubcodeZoneInfoNonExt CmdSubcode = 0x0001
CmdSubcodeZoneInfoExt CmdSubcode = 0x0002
CmdSubcodeGetZonesNet CmdSubcode = 0x0003
CmdSubcodeGetDomainZoneList CmdSubcode = 0x0004
SubcodeZoneInfoReq Subcode = 0x0001
SubcodeZoneInfoNonExt Subcode = 0x0001
SubcodeZoneInfoExt Subcode = 0x0002
SubcodeGetZonesNet Subcode = 0x0003
SubcodeGetDomainZoneList Subcode = 0x0004
)
func parseSubcode(p []byte) (CmdSubcode, []byte, error) {
func parseSubcode(p []byte) (Subcode, []byte, error) {
if len(p) < 2 {
return 0, p, fmt.Errorf("insufficient input length %d for subcode", len(p))
}
return CmdSubcode(binary.BigEndian.Uint16(p[:2])), p[2:], nil
return Subcode(binary.BigEndian.Uint16(p[:2])), p[2:], nil
}
type ZIReqPacket struct {
Header
Subcode CmdSubcode
Subcode
Networks []uint16
}
@ -36,7 +35,7 @@ func (p *ZIReqPacket) WriteTo(w io.Writer) (int64, error) {
p.Sequence = 0
p.CommandCode = CmdCodeZoneReq
p.Flags = 0
p.Subcode = CmdSubcodeZoneInfoReq
p.Subcode = SubcodeZoneInfoReq
a := acc(w)
a.writeTo(&p.Header)
@ -57,7 +56,7 @@ func parseZIReqPacket(p []byte) (*ZIReqPacket, error) {
ns[i] = binary.BigEndian.Uint16(p[i*2:][:2])
}
return &ZIReqPacket{
Subcode: CmdSubcodeZoneInfoReq,
Subcode: SubcodeZoneInfoReq,
Networks: ns,
}, nil
}
@ -75,7 +74,7 @@ func parseZIReqPacket(p []byte) (*ZIReqPacket, error) {
// "Duplicate zone names never exist in extended ZI-Rsp packets"
type ZIRspPacket struct {
Header
Subcode CmdSubcode
Subcode
Zones ZoneTuples
}
@ -103,6 +102,196 @@ func parseZIRspPacket(p []byte) (*ZIRspPacket, error) {
}, nil
}
type GDZLReqPacket struct {
Header
Subcode
StartIndex uint16
}
func (p *GDZLReqPacket) WriteTo(w io.Writer) (int64, error) {
p.Sequence = 0
p.CommandCode = CmdCodeZoneReq
p.Flags = 0
p.Subcode = SubcodeGetDomainZoneList
a := acc(w)
a.writeTo(&p.Header)
a.write16(uint16(p.Subcode))
a.write16(p.StartIndex)
return a.ret()
}
func parseGDZLReqPacket(p []byte) (*GDZLReqPacket, error) {
if len(p) < 2 {
return nil, fmt.Errorf("insufficient input length %d for GDZL-Req packet", len(p))
}
return &GDZLReqPacket{
Subcode: SubcodeGetDomainZoneList,
StartIndex: binary.BigEndian.Uint16(p[:2]),
}, nil
}
type GDZLRspPacket struct {
Header
Subcode
StartIndex int16
ZoneNames []string
}
func (p *GDZLRspPacket) WriteTo(w io.Writer) (int64, error) {
for _, zn := range p.ZoneNames {
if len(zn) > 127 {
return 0, fmt.Errorf("zone name %q too long", zn)
}
}
p.Sequence = 0
p.CommandCode = CmdCodeZoneRsp
// Flags is used to distinguish the final response packet, so leave alone
p.Subcode = SubcodeGetDomainZoneList
a := acc(w)
a.writeTo(&p.Header)
a.write16(uint16(p.Subcode))
a.write16(uint16(p.StartIndex))
if p.StartIndex == -1 {
return a.ret()
}
for _, zn := range p.ZoneNames {
// The spec is not clear what format these take, and Apple's example
// implementation always returns -1 (not supported), and I have no
// packet captures of this subcode.
// I'm guessing they're Pascal-style (length-prefixed) since that's used
// in the ZI-Rsp long tuples as well as throughout all the ancient Mac
// code.
a.write8(uint8(len(zn)))
a.write([]byte(zn))
}
return a.ret()
}
func parseGDZLRspPacket(p []byte) (*GDZLRspPacket, error) {
if len(p) < 2 {
return nil, fmt.Errorf("insufficient input length %d for GDZL-Rsp packet", len(p))
}
gdzl := &GDZLRspPacket{
Subcode: SubcodeGetDomainZoneList,
StartIndex: int16(binary.BigEndian.Uint16(p[:2])),
}
if gdzl.StartIndex == -1 {
return gdzl, nil
}
// See comment in GDZLRspPacket.WriteTo about the assumption here.
p = p[2:]
for len(p) > 0 {
strLen := p[0]
p = p[1:]
if len(p) < int(strLen) {
return nil, fmt.Errorf("insufficient remaining input length %d for zone name with length prefix %d", len(p), strLen)
}
gdzl.ZoneNames = append(gdzl.ZoneNames, string(p[:strLen]))
p = p[strLen:]
}
return gdzl, nil
}
type GZNReqPacket struct {
Header
Subcode
ZoneName string
}
func (p *GZNReqPacket) WriteTo(w io.Writer) (int64, error) {
if len(p.ZoneName) > 127 {
return 0, fmt.Errorf("zone name %q too long", p.ZoneName)
}
p.Sequence = 0
p.CommandCode = CmdCodeZoneReq
p.Flags = 0
p.Subcode = SubcodeGetZonesNet
a := acc(w)
a.writeTo(&p.Header)
a.write16(uint16(p.Subcode))
a.write8(uint8(len(p.ZoneName)))
a.write([]byte(p.ZoneName))
return a.ret()
}
func parseGZNReqPacket(p []byte) (*GZNReqPacket, error) {
if len(p) < 1 {
return nil, fmt.Errorf("insufficient input length %d for GZN-Req packet", len(p))
}
strLen := p[0]
p = p[1:]
if len(p) < int(strLen) {
return nil, fmt.Errorf("insufficient remaining input length %d for zone name with length prefix %d", len(p), strLen)
}
return &GZNReqPacket{
Subcode: SubcodeGetZonesNet,
ZoneName: string(p[:strLen]),
}, nil
}
type GZNRspPacket struct {
Header
Subcode
ZoneName string
NotSupported bool
Networks NetworkTuples
}
func (p *GZNRspPacket) WriteTo(w io.Writer) (int64, error) {
if len(p.ZoneName) > 127 {
return 0, fmt.Errorf("zone name %q too long", p.ZoneName)
}
a := acc(w)
a.writeTo(&p.Header)
a.write16(uint16(p.Subcode))
a.write8(uint8(len(p.ZoneName)))
if p.NotSupported {
a.write16(0xffff) // -1
return a.ret()
}
a.writeTo(p.Networks)
return a.ret()
}
func parseGZNRspPacket(p []byte) (*GZNRspPacket, error) {
if len(p) < 1 {
return nil, fmt.Errorf("insufficient input length %d for GZN-Rsp packet", len(p))
}
gzn := &GZNRspPacket{
Subcode: SubcodeGetZonesNet,
}
strLen := p[0]
p = p[1:]
if len(p) < int(strLen) {
return nil, fmt.Errorf("insufficient remaining input length %d for zone name with length prefix %d", len(p), strLen)
}
gzn.ZoneName = string(p[:strLen])
p = p[strLen:]
if len(p) < 2 {
return nil, fmt.Errorf("insufficient remaining input length %d for GZN-Rsp packet", len(p))
}
gzn.NotSupported = p[0] == 0xff && p[1] == 0xff
if gzn.NotSupported {
return gzn, nil
}
ns, err := parseNetworkTuples(p)
if err != nil {
return nil, err
}
gzn.Networks = ns
return gzn, nil
}
type ZoneTuples []ZoneTuple
type ZoneTuple struct {