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
|
||||
|
||||
import "github.com/sfiera/multitalk/pkg/ddp"
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/sfiera/multitalk/pkg/ddp"
|
||||
)
|
||||
|
||||
type QueryPacket struct {
|
||||
Function Function // 1
|
||||
// Function = 1
|
||||
// NetworkCount uint8
|
||||
Networks []ddp.Network
|
||||
}
|
||||
|
||||
type ReplyPacket struct {
|
||||
Function Function // 2 or 8
|
||||
// NetworkCount uint8
|
||||
Tuples []ZoneTuple
|
||||
func (p *QueryPacket) Marshal() ([]byte, error) {
|
||||
if len(p.Networks) > 255 {
|
||||
return nil, fmt.Errorf("too many networks [%d > 255]", len(p.Networks))
|
||||
}
|
||||
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 {
|
||||
Network ddp.Network
|
||||
ZoneName string
|
||||
func UnmarshalQueryPacket(data []byte) (*QueryPacket, error) {
|
||||
if len(data) < 2 {
|
||||
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] {
|
||||
case FunctionQuery:
|
||||
return nil, fmt.Errorf("ZIP Query unmarshaling unimplemented")
|
||||
return UnmarshalQueryPacket(data)
|
||||
|
||||
case FunctionReply:
|
||||
return nil, fmt.Errorf("ZIP Reply unmarshaling unimplemented")
|
||||
|
||||
case FunctionExtendedReply:
|
||||
return nil, fmt.Errorf("ZIP Extended Reply unmarshaling unimplemented")
|
||||
case FunctionReply, FunctionExtendedReply:
|
||||
return UnmarshalReplyPacket(data)
|
||||
|
||||
case FunctionGetNetInfo:
|
||||
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)
|
||||
}
|
||||
|
||||
// "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.Data = nbpRaw
|
||||
|
||||
|
@ -638,6 +643,7 @@ func handleNBPInAURP(pcapHandle *pcap.Handle, myHWAddr ethernet.Addr, ddpkt *ddp
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: outFrame.Dst = zone-specific multicast address
|
||||
outFrameRaw, err := ethertalk.Marshal(*outFrame)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
Loading…
Reference in a new issue