More ZIP, another note I found on NBP
This commit is contained in:
parent
05215af5f7
commit
31b7c34535
3 changed files with 128 additions and 15 deletions
|
@ -16,21 +16,131 @@
|
||||||
|
|
||||||
package zip
|
package zip
|
||||||
|
|
||||||
import "github.com/sfiera/multitalk/pkg/ddp"
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/sfiera/multitalk/pkg/ddp"
|
||||||
|
)
|
||||||
|
|
||||||
type QueryPacket struct {
|
type QueryPacket struct {
|
||||||
Function Function // 1
|
// Function = 1
|
||||||
// NetworkCount uint8
|
// NetworkCount uint8
|
||||||
Networks []ddp.Network
|
Networks []ddp.Network
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReplyPacket struct {
|
func (p *QueryPacket) Marshal() ([]byte, error) {
|
||||||
Function Function // 2 or 8
|
if len(p.Networks) > 255 {
|
||||||
// NetworkCount uint8
|
return nil, fmt.Errorf("too many networks [%d > 255]", len(p.Networks))
|
||||||
Tuples []ZoneTuple
|
}
|
||||||
|
b := bytes.NewBuffer(nil)
|
||||||
|
b.WriteByte(FunctionQuery)
|
||||||
|
b.WriteByte(byte(len(p.Networks)))
|
||||||
|
for _, n := range p.Networks {
|
||||||
|
write16(b, n)
|
||||||
|
}
|
||||||
|
return b.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type ZoneTuple struct {
|
func UnmarshalQueryPacket(data []byte) (*QueryPacket, error) {
|
||||||
Network ddp.Network
|
if len(data) < 2 {
|
||||||
ZoneName string
|
return nil, fmt.Errorf("insufficient input length %d for ZIP Query packet", len(data))
|
||||||
|
}
|
||||||
|
if data[0] != FunctionQuery {
|
||||||
|
return nil, fmt.Errorf("not a ZIP Query packet (funciton = %d)", data[0])
|
||||||
|
}
|
||||||
|
p := &QueryPacket{
|
||||||
|
Networks: make([]ddp.Network, 0, data[1]),
|
||||||
|
}
|
||||||
|
for range data[1] {
|
||||||
|
data = data[2:]
|
||||||
|
if len(data) < 2 {
|
||||||
|
return nil, fmt.Errorf("insufficient remaining input length %d for network number", len(data))
|
||||||
|
}
|
||||||
|
p.Networks = append(p.Networks, ddp.Network(binary.BigEndian.Uint16(data[:2])))
|
||||||
|
}
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReplyPacket struct {
|
||||||
|
// Function = 2 or 8
|
||||||
|
Extended bool
|
||||||
|
// NetworkCount uint8
|
||||||
|
// "Replies contain the number of zones lists indicated in the Reply header"
|
||||||
|
// and
|
||||||
|
// "Extended Replies can contain only one zones list. ...
|
||||||
|
// (the network numbers in each pair will all be the same in an Extended
|
||||||
|
// Reply). The network count in the header indicates, not the number of zone
|
||||||
|
// names in the packet, but the number of zone names in the entire zones
|
||||||
|
// list for the requested network, which may span more than one packet."
|
||||||
|
// and
|
||||||
|
// "Note: Extended ZIP Replies may also be used for responding to ZIP
|
||||||
|
// queries with zones lists that all fit in one Reply packet. In this case,
|
||||||
|
// the network count will be equal to the number of zone names in the
|
||||||
|
// packet"
|
||||||
|
Networks map[ddp.Network][]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ReplyPacket) Marshal() ([]byte, error) {
|
||||||
|
if len(p.Networks) > 255 {
|
||||||
|
return nil, fmt.Errorf("too many networks [%d > 255]", len(p.Networks))
|
||||||
|
}
|
||||||
|
if len(p.Networks) > 1 && p.Extended {
|
||||||
|
return nil, fmt.Errorf("extended reply can only contain 1 network")
|
||||||
|
}
|
||||||
|
b := bytes.NewBuffer(nil)
|
||||||
|
if p.Extended {
|
||||||
|
b.WriteByte(FunctionExtendedReply)
|
||||||
|
} else {
|
||||||
|
b.WriteByte(FunctionReply)
|
||||||
|
b.WriteByte(byte(len(p.Networks)))
|
||||||
|
}
|
||||||
|
for n, zs := range p.Networks {
|
||||||
|
if p.Extended {
|
||||||
|
if len(zs) > 255 {
|
||||||
|
return nil, fmt.Errorf("too many zone names [%d > 255]", len(zs))
|
||||||
|
}
|
||||||
|
// TODO: handle spreading extended replies across multiple packets
|
||||||
|
b.WriteByte(byte(len(zs)))
|
||||||
|
}
|
||||||
|
for _, z := range zs {
|
||||||
|
if len(z) > 32 {
|
||||||
|
return nil, fmt.Errorf("len(%q) > 32", z)
|
||||||
|
}
|
||||||
|
write16(b, n)
|
||||||
|
b.WriteByte(byte(len(z)))
|
||||||
|
b.WriteString(z)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func UnmarshalReplyPacket(data []byte) (*ReplyPacket, error) {
|
||||||
|
if len(data) < 2 {
|
||||||
|
return nil, fmt.Errorf("insufficient input length %d for ZIP Reply packet", len(data))
|
||||||
|
}
|
||||||
|
if data[0] != FunctionReply && data[0] != FunctionExtendedReply {
|
||||||
|
return nil, fmt.Errorf("not a Reply or an Extended Reply (function = %d)", data[0])
|
||||||
|
}
|
||||||
|
p := &ReplyPacket{
|
||||||
|
Extended: data[0] == FunctionExtendedReply,
|
||||||
|
Networks: make(map[ddp.Network][]string),
|
||||||
|
}
|
||||||
|
// "network count" is kinda irrelevant for unmarshalling?
|
||||||
|
data = data[2:]
|
||||||
|
for len(data) > 0 {
|
||||||
|
if len(data) < 3 {
|
||||||
|
return nil, fmt.Errorf("insufficinet remaining input length %d for zone tuple", len(data))
|
||||||
|
}
|
||||||
|
network := ddp.Network(binary.BigEndian.Uint16(data[:2]))
|
||||||
|
slen := data[2]
|
||||||
|
data = data[3:]
|
||||||
|
if len(data) < int(slen) {
|
||||||
|
return nil, fmt.Errorf("insufficient remaining input length %d for length-%d prefixed string", len(data), slen)
|
||||||
|
}
|
||||||
|
p.Networks[network] = append(p.Networks[network], string(data[:slen]))
|
||||||
|
data = data[slen:]
|
||||||
|
}
|
||||||
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,13 +39,10 @@ func UnmarshalPacket(data []byte) (any, error) {
|
||||||
}
|
}
|
||||||
switch data[0] {
|
switch data[0] {
|
||||||
case FunctionQuery:
|
case FunctionQuery:
|
||||||
return nil, fmt.Errorf("ZIP Query unmarshaling unimplemented")
|
return UnmarshalQueryPacket(data)
|
||||||
|
|
||||||
case FunctionReply:
|
case FunctionReply, FunctionExtendedReply:
|
||||||
return nil, fmt.Errorf("ZIP Reply unmarshaling unimplemented")
|
return UnmarshalReplyPacket(data)
|
||||||
|
|
||||||
case FunctionExtendedReply:
|
|
||||||
return nil, fmt.Errorf("ZIP Extended Reply unmarshaling unimplemented")
|
|
||||||
|
|
||||||
case FunctionGetNetInfo:
|
case FunctionGetNetInfo:
|
||||||
return UnmarshalGetNetInfoPacket(data)
|
return UnmarshalGetNetInfoPacket(data)
|
||||||
|
|
6
main.go
6
main.go
|
@ -631,6 +631,11 @@ func handleNBPInAURP(pcapHandle *pcap.Handle, myHWAddr ethernet.Addr, ddpkt *ddp
|
||||||
return fmt.Errorf("couldn't marshal LkUp: %v", err)
|
return fmt.Errorf("couldn't marshal LkUp: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// "If the destination network is extended, however, the router must also
|
||||||
|
// change the destination network number to $0000, so that the packet is
|
||||||
|
// received by all nodes on the network (within the correct zone multicast
|
||||||
|
// address)."
|
||||||
|
ddpkt.DstNet = 0x0000
|
||||||
ddpkt.DstNode = 0xFF // Broadcast node address within the dest network
|
ddpkt.DstNode = 0xFF // Broadcast node address within the dest network
|
||||||
ddpkt.Data = nbpRaw
|
ddpkt.Data = nbpRaw
|
||||||
|
|
||||||
|
@ -638,6 +643,7 @@ func handleNBPInAURP(pcapHandle *pcap.Handle, myHWAddr ethernet.Addr, ddpkt *ddp
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// TODO: outFrame.Dst = zone-specific multicast address
|
||||||
outFrameRaw, err := ethertalk.Marshal(*outFrame)
|
outFrameRaw, err := ethertalk.Marshal(*outFrame)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
Loading…
Reference in a new issue