Rework XDR encoding
This commit is contained in:
115
discover/PROTOCOL.md
Normal file
115
discover/PROTOCOL.md
Normal file
@@ -0,0 +1,115 @@
|
||||
Node Discovery Protocol v2
|
||||
==========================
|
||||
|
||||
Mode of Operation
|
||||
-----------------
|
||||
|
||||
There are two distinct modes: "local discovery", performed on a LAN
|
||||
segment (broadcast domain) and "global discovery" performed over the
|
||||
Internet in general with the support of a well known server.
|
||||
|
||||
Local discovery does not use Query packets. Instead Announcement packets
|
||||
are sent periodically and each participating node keeps a table of the
|
||||
announcements it has seen. On multihomed hosts the announcement packets
|
||||
should be sent on each interface that syncthing will accept connections.
|
||||
|
||||
It is recommended that local discovery Announcement packets are sent on
|
||||
a 30 to 60 second interval, possibly with forced transmissions when a
|
||||
previously unknown node is discovered.
|
||||
|
||||
Global discovery is made possible by periodically updating a global server
|
||||
using Announcement packets indentical to those transmitted for local
|
||||
discovery. The node performing discovery will transmit a Query packet to
|
||||
the global server and expect an Announcement packet in response. In case
|
||||
the global server has no knowledge of the queried node ID, there will be
|
||||
no response. A timeout is to be used to determine lookup failure.
|
||||
|
||||
There is no message to unregister from the global server; instead
|
||||
registrations are forgotten after 60 minutes. It is recommended to
|
||||
send Announcement packets to the global server on a 30 minute interval.
|
||||
|
||||
Packet Formats
|
||||
--------------
|
||||
|
||||
The Announcement packet has the following structure:
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Magic Number (0x029E4C77) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Length of Node ID |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ Node ID (variable length) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Number of Addresses |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ Zero or more Address Structures \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
Address Structure:
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Length of IP |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ IP (variable length) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Port Number | 0x0000 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
This is the XDR encoding of:
|
||||
|
||||
struct Announcement {
|
||||
unsigned int MagicNumber;
|
||||
string NodeID<>;
|
||||
Address Addresses<>;
|
||||
}
|
||||
|
||||
struct Address {
|
||||
opaque IP<>;
|
||||
unsigned short PortNumber;
|
||||
}
|
||||
|
||||
NodeID is padded to a multiple of 32 bits and all fields are in sent in
|
||||
network (big endian) byte order. In the Address structure, the IP field
|
||||
can be of three differnt kinds;
|
||||
|
||||
- A zero length indicates that the IP address should be taken from the
|
||||
source address of the announcement packet, be it IPv4 or IPv6. The
|
||||
source address must be a valid unicast address.
|
||||
|
||||
- A four byte length indicates that the address is an IPv4 unicast
|
||||
address.
|
||||
|
||||
- A sixteen byte length indicates that the address is an IPv6 unicast
|
||||
address.
|
||||
|
||||
The Query packet has the following structure:
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Magic Number (0x23D63A9A) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Length of Node ID |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ Node ID (variable length) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
This is the XDR encoding of:
|
||||
|
||||
struct Announcement {
|
||||
unsigned int MagicNumber;
|
||||
string NodeID<>;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"flag"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -10,19 +14,38 @@ import (
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
IP []byte
|
||||
Port uint16
|
||||
Updated time.Time
|
||||
Addresses []Address
|
||||
Updated time.Time
|
||||
}
|
||||
|
||||
type Address struct {
|
||||
IP []byte
|
||||
Port uint16
|
||||
}
|
||||
|
||||
var (
|
||||
nodes = make(map[string]Node)
|
||||
lock sync.Mutex
|
||||
queries = 0
|
||||
nodes = make(map[string]Node)
|
||||
lock sync.Mutex
|
||||
queries = 0
|
||||
answered = 0
|
||||
)
|
||||
|
||||
func main() {
|
||||
addr, _ := net.ResolveUDPAddr("udp", ":22025")
|
||||
var debug bool
|
||||
var listen string
|
||||
var timestamp bool
|
||||
|
||||
flag.StringVar(&listen, "listen", ":22025", "Listen address")
|
||||
flag.BoolVar(&debug, "debug", false, "Enable debug output")
|
||||
flag.BoolVar(×tamp, "timestamp", true, "Timestamp the log output")
|
||||
flag.Parse()
|
||||
|
||||
log.SetOutput(os.Stdout)
|
||||
if !timestamp {
|
||||
log.SetFlags(0)
|
||||
}
|
||||
|
||||
addr, _ := net.ResolveUDPAddr("udp", listen)
|
||||
conn, err := net.ListenUDP("udp", addr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -41,8 +64,9 @@ func main() {
|
||||
deleted++
|
||||
}
|
||||
}
|
||||
log.Printf("Expired %d nodes; %d nodes in registry; %d queries", deleted, len(nodes), queries)
|
||||
log.Printf("Expired %d nodes; %d nodes in registry; %d queries (%d answered)", deleted, len(nodes), queries, answered)
|
||||
queries = 0
|
||||
answered = 0
|
||||
|
||||
lock.Unlock()
|
||||
}
|
||||
@@ -50,49 +74,163 @@ func main() {
|
||||
|
||||
var buf = make([]byte, 1024)
|
||||
for {
|
||||
buf = buf[:cap(buf)]
|
||||
n, addr, err := conn.ReadFromUDP(buf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
pkt, err := discover.DecodePacket(buf[:n])
|
||||
if err != nil {
|
||||
log.Println("Warning:", err)
|
||||
if n < 4 {
|
||||
log.Printf("Received short packet (%d bytes)", n)
|
||||
continue
|
||||
}
|
||||
|
||||
switch pkt.Magic {
|
||||
case 0x20121025:
|
||||
// Announcement
|
||||
lock.Lock()
|
||||
buf = buf[:n]
|
||||
magic := binary.BigEndian.Uint32(buf)
|
||||
|
||||
switch magic {
|
||||
case discover.AnnouncementMagicV1:
|
||||
var pkt discover.AnnounceV1
|
||||
err := pkt.UnmarshalXDR(buf)
|
||||
if err != nil {
|
||||
log.Println("AnnounceV1 Unmarshal:", err)
|
||||
log.Println(hex.Dump(buf))
|
||||
continue
|
||||
}
|
||||
if debug {
|
||||
log.Printf("<- %v %#v", addr, pkt)
|
||||
}
|
||||
|
||||
ip := addr.IP.To4()
|
||||
if ip == nil {
|
||||
ip = addr.IP.To16()
|
||||
}
|
||||
node := Node{
|
||||
IP: ip,
|
||||
Port: uint16(pkt.Port),
|
||||
Addresses: []Address{{
|
||||
IP: ip,
|
||||
Port: pkt.Port,
|
||||
}},
|
||||
Updated: time.Now(),
|
||||
}
|
||||
//log.Println("<-", pkt.ID, node)
|
||||
nodes[pkt.ID] = node
|
||||
lock.Unlock()
|
||||
case 0x19760309:
|
||||
// Query
|
||||
|
||||
lock.Lock()
|
||||
node, ok := nodes[pkt.ID]
|
||||
nodes[pkt.NodeID] = node
|
||||
lock.Unlock()
|
||||
|
||||
case discover.QueryMagicV1:
|
||||
var pkt discover.QueryV1
|
||||
err := pkt.UnmarshalXDR(buf)
|
||||
if err != nil {
|
||||
log.Println("QueryV1 Unmarshal:", err)
|
||||
log.Println(hex.Dump(buf))
|
||||
continue
|
||||
}
|
||||
if debug {
|
||||
log.Printf("<- %v %#v", addr, pkt)
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
node, ok := nodes[pkt.NodeID]
|
||||
queries++
|
||||
lock.Unlock()
|
||||
if ok {
|
||||
pkt := discover.Packet{
|
||||
Magic: 0x20121025,
|
||||
ID: pkt.ID,
|
||||
Port: node.Port,
|
||||
IP: node.IP,
|
||||
|
||||
if ok && len(node.Addresses) > 0 {
|
||||
pkt := discover.AnnounceV1{
|
||||
Magic: discover.AnnouncementMagicV1,
|
||||
NodeID: pkt.NodeID,
|
||||
Port: node.Addresses[0].Port,
|
||||
IP: node.Addresses[0].IP,
|
||||
}
|
||||
_, _, err = conn.WriteMsgUDP(discover.EncodePacket(pkt), nil, addr)
|
||||
if debug {
|
||||
log.Printf("-> %v %#v", addr, pkt)
|
||||
}
|
||||
|
||||
tb := pkt.MarshalXDR()
|
||||
_, _, err = conn.WriteMsgUDP(tb, nil, addr)
|
||||
if err != nil {
|
||||
log.Println("Warning:", err)
|
||||
log.Println("QueryV1 response write:", err)
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
answered++
|
||||
lock.Unlock()
|
||||
}
|
||||
|
||||
case discover.AnnouncementMagicV2:
|
||||
var pkt discover.AnnounceV2
|
||||
err := pkt.UnmarshalXDR(buf)
|
||||
if err != nil {
|
||||
log.Println("AnnounceV2 Unmarshal:", err)
|
||||
log.Println(hex.Dump(buf))
|
||||
continue
|
||||
}
|
||||
if debug {
|
||||
log.Printf("<- %v %#v", addr, pkt)
|
||||
}
|
||||
|
||||
ip := addr.IP.To4()
|
||||
if ip == nil {
|
||||
ip = addr.IP.To16()
|
||||
}
|
||||
|
||||
var addrs []Address
|
||||
for _, addr := range pkt.Addresses {
|
||||
tip := addr.IP
|
||||
if len(tip) == 0 {
|
||||
tip = ip
|
||||
}
|
||||
addrs = append(addrs, Address{
|
||||
IP: tip,
|
||||
Port: addr.Port,
|
||||
})
|
||||
}
|
||||
|
||||
node := Node{
|
||||
Addresses: addrs,
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
nodes[pkt.NodeID] = node
|
||||
lock.Unlock()
|
||||
|
||||
case discover.QueryMagicV2:
|
||||
var pkt discover.QueryV2
|
||||
err := pkt.UnmarshalXDR(buf)
|
||||
if err != nil {
|
||||
log.Println("QueryV2 Unmarshal:", err)
|
||||
log.Println(hex.Dump(buf))
|
||||
continue
|
||||
}
|
||||
if debug {
|
||||
log.Printf("<- %v %#v", addr, pkt)
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
node, ok := nodes[pkt.NodeID]
|
||||
queries++
|
||||
lock.Unlock()
|
||||
|
||||
if ok && len(node.Addresses) > 0 {
|
||||
pkt := discover.AnnounceV2{
|
||||
Magic: discover.AnnouncementMagicV2,
|
||||
NodeID: pkt.NodeID,
|
||||
}
|
||||
for _, addr := range node.Addresses {
|
||||
pkt.Addresses = append(pkt.Addresses, discover.Address{IP: addr.IP, Port: addr.Port})
|
||||
}
|
||||
if debug {
|
||||
log.Printf("-> %v %#v", addr, pkt)
|
||||
}
|
||||
|
||||
tb := pkt.MarshalXDR()
|
||||
_, _, err = conn.WriteMsgUDP(tb, nil, addr)
|
||||
if err != nil {
|
||||
log.Println("QueryV2 response write:", err)
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
answered++
|
||||
lock.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,89 +1,12 @@
|
||||
/*
|
||||
This is the local node discovery protocol. In principle we might be better
|
||||
served by something more standardized, such as mDNS / DNS-SD. In practice, this
|
||||
was much easier and quicker to get up and running.
|
||||
|
||||
The mode of operation is to periodically (currently once every 30 seconds)
|
||||
broadcast an Announcement packet to UDP port 21025. The packet has the
|
||||
following format:
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Magic Number (0x20121025) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Port Number | Reserved |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Length of NodeID |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ NodeID (variable length) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Length of IP |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ IP (variable length) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
This is the XDR encoding of:
|
||||
|
||||
struct Announcement {
|
||||
unsigned int Magic;
|
||||
unsigned short Port;
|
||||
string NodeID<>;
|
||||
}
|
||||
|
||||
(Hence NodeID is padded to a multiple of 32 bits)
|
||||
|
||||
The sending node's address is not encoded in local announcement -- the Length
|
||||
of IP field is set to zero and the address is taken to be the source address of
|
||||
the announcement. In announcement packets sent by a discovery server in
|
||||
response to a query, the IP is present and the length is either 4 (IPv4) or 16
|
||||
(IPv6).
|
||||
|
||||
Every time such a packet is received, a local table that maps NodeID to Address
|
||||
is updated. When the local node wants to connect to another node with the
|
||||
address specification 'dynamic', this table is consulted.
|
||||
|
||||
For external discovery, an identical packet is sent every 30 minutes to the
|
||||
external discovery server. The server keeps information for up to 60 minutes.
|
||||
To query the server, and UDP packet with the format below is sent.
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Magic Number (0x19760309) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Length of NodeID |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ NodeID (variable length) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
This is the XDR encoding of:
|
||||
|
||||
struct Announcement {
|
||||
unsigned int Magic;
|
||||
string NodeID<>;
|
||||
}
|
||||
|
||||
(Hence NodeID is padded to a multiple of 32 bits)
|
||||
|
||||
It is answered with an announcement packet for the queried node ID if the
|
||||
information is available. There is no answer for queries about unknown nodes. A
|
||||
reasonable timeout is recommended instead. (This, combined with server side
|
||||
rate limits for packets per source IP and queries per node ID, prevents the
|
||||
server from being used as an amplifier in a DDoS attack.)
|
||||
*/
|
||||
package discover
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -91,9 +14,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
AnnouncementPort = 21025
|
||||
AnnouncementMagic = 0x20121025
|
||||
QueryMagic = 0x19760309
|
||||
AnnouncementPort = 21025
|
||||
Debug = false
|
||||
)
|
||||
|
||||
type Discoverer struct {
|
||||
@@ -103,11 +25,15 @@ type Discoverer struct {
|
||||
ExtBroadcastIntv time.Duration
|
||||
|
||||
conn *net.UDPConn
|
||||
registry map[string]string
|
||||
registry map[string][]string
|
||||
registryLock sync.RWMutex
|
||||
extServer string
|
||||
}
|
||||
|
||||
var (
|
||||
ErrIncorrectMagic = errors.New("Incorrect magic number")
|
||||
)
|
||||
|
||||
// We tolerate a certain amount of errors because we might be running on
|
||||
// laptops that sleep and wake, have intermittent network connectivity, etc.
|
||||
// When we hit this many errors in succession, we stop.
|
||||
@@ -127,7 +53,7 @@ func NewDiscoverer(id string, port int, extServer string) (*Discoverer, error) {
|
||||
ExtBroadcastIntv: 1800 * time.Second,
|
||||
|
||||
conn: conn,
|
||||
registry: make(map[string]string),
|
||||
registry: make(map[string][]string),
|
||||
extServer: extServer,
|
||||
}
|
||||
|
||||
@@ -146,7 +72,8 @@ func NewDiscoverer(id string, port int, extServer string) (*Discoverer, error) {
|
||||
func (d *Discoverer) sendAnnouncements() {
|
||||
remote4 := &net.UDPAddr{IP: net.IP{255, 255, 255, 255}, Port: AnnouncementPort}
|
||||
|
||||
buf := EncodePacket(Packet{AnnouncementMagic, uint16(d.ListenPort), d.MyID, nil})
|
||||
pkt := AnnounceV2{AnnouncementMagicV2, d.MyID, []Address{{nil, 22000}}}
|
||||
buf := pkt.MarshalXDR()
|
||||
go d.writeAnnouncements(buf, remote4, d.BroadcastIntv)
|
||||
}
|
||||
|
||||
@@ -157,7 +84,8 @@ func (d *Discoverer) sendExtAnnouncements() {
|
||||
return
|
||||
}
|
||||
|
||||
buf := EncodePacket(Packet{AnnouncementMagic, uint16(22000), d.MyID, nil})
|
||||
pkt := AnnounceV2{AnnouncementMagicV2, d.MyID, []Address{{nil, 22000}}}
|
||||
buf := pkt.MarshalXDR()
|
||||
go d.writeAnnouncements(buf, extIP, d.ExtBroadcastIntv)
|
||||
}
|
||||
|
||||
@@ -189,90 +117,137 @@ func (d *Discoverer) recvAnnouncements() {
|
||||
continue
|
||||
}
|
||||
|
||||
pkt, err := DecodePacket(buf[:n])
|
||||
if err != nil || pkt.Magic != AnnouncementMagic {
|
||||
if Debug {
|
||||
log.Printf("read announcement:\n%s", hex.Dump(buf[:n]))
|
||||
}
|
||||
|
||||
var pkt AnnounceV2
|
||||
err = pkt.UnmarshalXDR(buf[:n])
|
||||
if err != nil {
|
||||
errCounter++
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
if Debug {
|
||||
log.Printf("read announcement: %#v", pkt)
|
||||
}
|
||||
|
||||
errCounter = 0
|
||||
|
||||
if pkt.ID != d.MyID {
|
||||
nodeAddr := fmt.Sprintf("%s:%d", addr.IP.String(), pkt.Port)
|
||||
d.registryLock.Lock()
|
||||
if d.registry[pkt.ID] != nodeAddr {
|
||||
d.registry[pkt.ID] = nodeAddr
|
||||
if pkt.NodeID != d.MyID {
|
||||
var addrs []string
|
||||
for _, a := range pkt.Addresses {
|
||||
var nodeAddr string
|
||||
if len(a.IP) > 0 {
|
||||
nodeAddr = fmt.Sprintf("%s:%d", ipStr(a.IP), a.Port)
|
||||
} else {
|
||||
nodeAddr = fmt.Sprintf("%s:%d", addr.IP.String(), a.Port)
|
||||
}
|
||||
addrs = append(addrs, nodeAddr)
|
||||
}
|
||||
if Debug {
|
||||
log.Printf("register: %#v", addrs)
|
||||
}
|
||||
d.registryLock.Lock()
|
||||
d.registry[pkt.NodeID] = addrs
|
||||
d.registryLock.Unlock()
|
||||
}
|
||||
}
|
||||
log.Println("discover/read: stopping due to too many errors:", err)
|
||||
}
|
||||
|
||||
func (d *Discoverer) externalLookup(node string) (string, bool) {
|
||||
func (d *Discoverer) externalLookup(node string) []string {
|
||||
extIP, err := net.ResolveUDPAddr("udp", d.extServer)
|
||||
if err != nil {
|
||||
log.Printf("discover/external: %v; no external lookup", err)
|
||||
return "", false
|
||||
return nil
|
||||
}
|
||||
|
||||
conn, err := net.DialUDP("udp", nil, extIP)
|
||||
if err != nil {
|
||||
log.Printf("discover/external: %v; no external lookup", err)
|
||||
return "", false
|
||||
return nil
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
err = conn.SetDeadline(time.Now().Add(5 * time.Second))
|
||||
if err != nil {
|
||||
log.Printf("discover/external: %v; no external lookup", err)
|
||||
return "", false
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err = conn.Write(EncodePacket(Packet{QueryMagic, 0, node, nil}))
|
||||
buf := QueryV2{QueryMagicV2, node}.MarshalXDR()
|
||||
_, err = conn.Write(buf)
|
||||
if err != nil {
|
||||
log.Printf("discover/external: %v; no external lookup", err)
|
||||
return "", false
|
||||
return nil
|
||||
}
|
||||
buffers.Put(buf)
|
||||
|
||||
var buf = buffers.Get(256)
|
||||
buf = buffers.Get(256)
|
||||
defer buffers.Put(buf)
|
||||
|
||||
n, err := conn.Read(buf)
|
||||
if err != nil {
|
||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||
// Expected if the server doesn't know about requested node ID
|
||||
return "", false
|
||||
return nil
|
||||
}
|
||||
log.Printf("discover/external/read: %v; no external lookup", err)
|
||||
return "", false
|
||||
return nil
|
||||
}
|
||||
|
||||
pkt, err := DecodePacket(buf[:n])
|
||||
if Debug {
|
||||
log.Printf("read external:\n%s", hex.Dump(buf[:n]))
|
||||
}
|
||||
|
||||
var pkt AnnounceV2
|
||||
err = pkt.UnmarshalXDR(buf[:n])
|
||||
if err != nil {
|
||||
log.Printf("discover/external/read: %v; no external lookup", err)
|
||||
return "", false
|
||||
log.Println("discover/external/decode:", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if pkt.Magic != AnnouncementMagic {
|
||||
log.Printf("discover/external/read: bad magic; no external lookup", err)
|
||||
return "", false
|
||||
if Debug {
|
||||
log.Printf("read external: %#v", pkt)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s:%d", ipStr(pkt.IP), pkt.Port), true
|
||||
var addrs []string
|
||||
for _, a := range pkt.Addresses {
|
||||
var nodeAddr string
|
||||
if len(a.IP) > 0 {
|
||||
nodeAddr = fmt.Sprintf("%s:%d", ipStr(a.IP), a.Port)
|
||||
}
|
||||
addrs = append(addrs, nodeAddr)
|
||||
}
|
||||
return addrs
|
||||
}
|
||||
|
||||
func (d *Discoverer) Lookup(node string) (string, bool) {
|
||||
func (d *Discoverer) Lookup(node string) []string {
|
||||
d.registryLock.Lock()
|
||||
addr, ok := d.registry[node]
|
||||
d.registryLock.Unlock()
|
||||
|
||||
if ok {
|
||||
return addr, true
|
||||
return addr
|
||||
} else if len(d.extServer) != 0 {
|
||||
// We might want to cache this, but not permanently so it needs some intelligence
|
||||
return d.externalLookup(node)
|
||||
}
|
||||
return "", false
|
||||
return nil
|
||||
}
|
||||
|
||||
func ipStr(ip []byte) string {
|
||||
var f = "%d"
|
||||
var s = "."
|
||||
if len(ip) > 4 {
|
||||
f = "%x"
|
||||
s = ":"
|
||||
}
|
||||
var ss = make([]string, len(ip))
|
||||
for i := range ip {
|
||||
ss[i] = fmt.Sprintf(f, ip[i])
|
||||
}
|
||||
return strings.Join(ss, s)
|
||||
}
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
package discover
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Packet struct {
|
||||
Magic uint32 // AnnouncementMagic or QueryMagic
|
||||
Port uint16 // unset if magic == QueryMagic
|
||||
ID string
|
||||
IP []byte // zero length in local announcements
|
||||
}
|
||||
|
||||
var (
|
||||
errBadMagic = errors.New("bad magic")
|
||||
errFormat = errors.New("incorrect packet format")
|
||||
)
|
||||
|
||||
func EncodePacket(pkt Packet) []byte {
|
||||
if l := len(pkt.IP); l != 0 && l != 4 && l != 16 {
|
||||
// bad ip format
|
||||
return nil
|
||||
}
|
||||
|
||||
var idbs = []byte(pkt.ID)
|
||||
var l = 4 + 4 + len(idbs) + pad(len(idbs))
|
||||
if pkt.Magic == AnnouncementMagic {
|
||||
l += 4 + 4 + len(pkt.IP)
|
||||
}
|
||||
|
||||
var buf = make([]byte, l)
|
||||
var offset = 0
|
||||
|
||||
binary.BigEndian.PutUint32(buf[offset:], pkt.Magic)
|
||||
offset += 4
|
||||
|
||||
if pkt.Magic == AnnouncementMagic {
|
||||
binary.BigEndian.PutUint16(buf[offset:], uint16(pkt.Port))
|
||||
offset += 4
|
||||
}
|
||||
|
||||
binary.BigEndian.PutUint32(buf[offset:], uint32(len(idbs)))
|
||||
offset += 4
|
||||
copy(buf[offset:], idbs)
|
||||
offset += len(idbs) + pad(len(idbs))
|
||||
|
||||
if pkt.Magic == AnnouncementMagic {
|
||||
binary.BigEndian.PutUint32(buf[offset:], uint32(len(pkt.IP)))
|
||||
offset += 4
|
||||
copy(buf[offset:], pkt.IP)
|
||||
offset += len(pkt.IP)
|
||||
}
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
func DecodePacket(buf []byte) (*Packet, error) {
|
||||
var p Packet
|
||||
var offset int
|
||||
|
||||
if len(buf) < 4 {
|
||||
// short packet
|
||||
return nil, errFormat
|
||||
}
|
||||
p.Magic = binary.BigEndian.Uint32(buf[offset:])
|
||||
offset += 4
|
||||
|
||||
if p.Magic != AnnouncementMagic && p.Magic != QueryMagic {
|
||||
return nil, errBadMagic
|
||||
}
|
||||
|
||||
if p.Magic == AnnouncementMagic {
|
||||
// Port Number
|
||||
|
||||
if len(buf) < offset+4 {
|
||||
// short packet
|
||||
return nil, errFormat
|
||||
}
|
||||
p.Port = binary.BigEndian.Uint16(buf[offset:])
|
||||
offset += 2
|
||||
reserved := binary.BigEndian.Uint16(buf[offset:])
|
||||
if reserved != 0 {
|
||||
return nil, errFormat
|
||||
}
|
||||
offset += 2
|
||||
}
|
||||
|
||||
// Node ID
|
||||
|
||||
if len(buf) < offset+4 {
|
||||
// short packet
|
||||
return nil, errFormat
|
||||
}
|
||||
l := binary.BigEndian.Uint32(buf[offset:])
|
||||
offset += 4
|
||||
|
||||
if len(buf) < offset+int(l)+pad(int(l)) {
|
||||
// short packet
|
||||
return nil, errFormat
|
||||
}
|
||||
idbs := buf[offset : offset+int(l)]
|
||||
p.ID = string(idbs)
|
||||
offset += int(l) + pad(int(l))
|
||||
|
||||
if p.Magic == AnnouncementMagic {
|
||||
// IP
|
||||
|
||||
if len(buf) < offset+4 {
|
||||
// short packet
|
||||
return nil, errFormat
|
||||
}
|
||||
l = binary.BigEndian.Uint32(buf[offset:])
|
||||
offset += 4
|
||||
|
||||
if l != 0 && l != 4 && l != 16 {
|
||||
// weird ip length
|
||||
return nil, errFormat
|
||||
}
|
||||
if len(buf) < offset+int(l) {
|
||||
// short packet
|
||||
return nil, errFormat
|
||||
}
|
||||
if l > 0 {
|
||||
p.IP = buf[offset : offset+int(l)]
|
||||
offset += int(l)
|
||||
}
|
||||
}
|
||||
|
||||
if len(buf[offset:]) > 0 {
|
||||
// extra data
|
||||
return nil, errFormat
|
||||
}
|
||||
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
func pad(l int) int {
|
||||
d := l % 4
|
||||
if d == 0 {
|
||||
return 0
|
||||
}
|
||||
return 4 - d
|
||||
}
|
||||
|
||||
func ipStr(ip []byte) string {
|
||||
switch len(ip) {
|
||||
case 4:
|
||||
return fmt.Sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3])
|
||||
case 16:
|
||||
return fmt.Sprintf("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
|
||||
ip[0], ip[1], ip[2], ip[3],
|
||||
ip[4], ip[5], ip[6], ip[7],
|
||||
ip[8], ip[9], ip[10], ip[11],
|
||||
ip[12], ip[13], ip[14], ip[15])
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
package discover
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var testdata = []struct {
|
||||
data []byte
|
||||
packet *Packet
|
||||
err error
|
||||
}{
|
||||
{
|
||||
[]byte{0x20, 0x12, 0x10, 0x25,
|
||||
0x12, 0x34, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x05,
|
||||
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00},
|
||||
&Packet{
|
||||
Magic: 0x20121025,
|
||||
Port: 0x1234,
|
||||
ID: "hello",
|
||||
},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
[]byte{0x20, 0x12, 0x10, 0x25,
|
||||
0x34, 0x56, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x08,
|
||||
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x21, 0x21, 0x21,
|
||||
0x00, 0x00, 0x00, 0x04,
|
||||
0x01, 0x02, 0x03, 0x04},
|
||||
&Packet{
|
||||
Magic: 0x20121025,
|
||||
Port: 0x3456,
|
||||
ID: "hello!!!",
|
||||
IP: []byte{1, 2, 3, 4},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
[]byte{0x19, 0x76, 0x03, 0x09,
|
||||
0x00, 0x00, 0x00, 0x06,
|
||||
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x21, 0x00, 0x00},
|
||||
&Packet{
|
||||
Magic: 0x19760309,
|
||||
ID: "hello!",
|
||||
},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
[]byte{0x20, 0x12, 0x10, 0x25,
|
||||
0x12, 0x34, 0x12, 0x34, // reserved bits not set to zero
|
||||
0x00, 0x00, 0x00, 0x06,
|
||||
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x21, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00},
|
||||
nil,
|
||||
errFormat,
|
||||
},
|
||||
{
|
||||
[]byte{0x20, 0x12, 0x10, 0x25,
|
||||
0x12, 0x34, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x06,
|
||||
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x21, // missing padding
|
||||
0x00, 0x00, 0x00, 0x00},
|
||||
nil,
|
||||
errFormat,
|
||||
},
|
||||
{
|
||||
[]byte{0x19, 0x77, 0x03, 0x09, // incorrect Magic
|
||||
0x00, 0x00, 0x00, 0x06,
|
||||
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x21, 0x00, 0x00},
|
||||
nil,
|
||||
errBadMagic,
|
||||
},
|
||||
{
|
||||
[]byte{0x19, 0x76, 0x03, 0x09,
|
||||
0x6c, 0x6c, 0x6c, 0x6c, // length exceeds packet size
|
||||
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x21, 0x00, 0x00},
|
||||
nil,
|
||||
errFormat,
|
||||
},
|
||||
{
|
||||
[]byte{0x19, 0x76, 0x03, 0x09,
|
||||
0x00, 0x00, 0x00, 0x06,
|
||||
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x21, 0x00, 0x00,
|
||||
0x23}, // extra data at the end
|
||||
nil,
|
||||
errFormat,
|
||||
},
|
||||
}
|
||||
|
||||
func TestDecodePacket(t *testing.T) {
|
||||
for i, test := range testdata {
|
||||
p, err := DecodePacket(test.data)
|
||||
if err != test.err {
|
||||
t.Errorf("%d: unexpected error %v", i, err)
|
||||
} else {
|
||||
if !reflect.DeepEqual(p, test.packet) {
|
||||
t.Errorf("%d: incorrect packet\n%v\n%v", i, test.packet, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodePacket(t *testing.T) {
|
||||
for i, test := range testdata {
|
||||
if test.err != nil {
|
||||
continue
|
||||
}
|
||||
buf := EncodePacket(*test.packet)
|
||||
if bytes.Compare(buf, test.data) != 0 {
|
||||
t.Errorf("%d: incorrect encoded packet\n% x\n% 0x", i, test.data, buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var ipstrTests = []struct {
|
||||
d []byte
|
||||
s string
|
||||
}{
|
||||
{[]byte{192, 168, 34}, ""},
|
||||
{[]byte{192, 168, 0, 34}, "192.168.0.34"},
|
||||
{[]byte{0x20, 0x01, 0x12, 0x34,
|
||||
0x34, 0x56, 0x56, 0x78,
|
||||
0x78, 0x00, 0x00, 0xdc,
|
||||
0x00, 0x00, 0x43, 0x54}, "2001:1234:3456:5678:7800:00dc:0000:4354"},
|
||||
}
|
||||
|
||||
func TestIPStr(t *testing.T) {
|
||||
for _, tc := range ipstrTests {
|
||||
s1 := ipStr(tc.d)
|
||||
if s1 != tc.s {
|
||||
t.Errorf("Incorrect ipstr %q != %q", tc.s, s1)
|
||||
}
|
||||
}
|
||||
}
|
||||
39
discover/packets.go
Normal file
39
discover/packets.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package discover
|
||||
|
||||
const (
|
||||
AnnouncementMagicV1 = 0x20121025
|
||||
QueryMagicV1 = 0x19760309
|
||||
)
|
||||
|
||||
type QueryV1 struct {
|
||||
Magic uint32
|
||||
NodeID string // max:64
|
||||
}
|
||||
|
||||
type AnnounceV1 struct {
|
||||
Magic uint32
|
||||
Port uint16
|
||||
NodeID string // max:64
|
||||
IP []byte // max:16
|
||||
}
|
||||
|
||||
const (
|
||||
AnnouncementMagicV2 = 0x029E4C77
|
||||
QueryMagicV2 = 0x23D63A9A
|
||||
)
|
||||
|
||||
type QueryV2 struct {
|
||||
Magic uint32
|
||||
NodeID string // max:64
|
||||
}
|
||||
|
||||
type AnnounceV2 struct {
|
||||
Magic uint32
|
||||
NodeID string // max:64
|
||||
Addresses []Address // max:16
|
||||
}
|
||||
|
||||
type Address struct {
|
||||
IP []byte // max:16
|
||||
Port uint16
|
||||
}
|
||||
220
discover/packets_xdr.go
Normal file
220
discover/packets_xdr.go
Normal file
@@ -0,0 +1,220 @@
|
||||
package discover
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"github.com/calmh/syncthing/xdr"
|
||||
)
|
||||
|
||||
func (o QueryV1) EncodeXDR(w io.Writer) (int, error) {
|
||||
var xw = xdr.NewWriter(w)
|
||||
return o.encodeXDR(xw)
|
||||
}
|
||||
|
||||
func (o QueryV1) MarshalXDR() []byte {
|
||||
var buf bytes.Buffer
|
||||
var xw = xdr.NewWriter(&buf)
|
||||
o.encodeXDR(xw)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (o QueryV1) encodeXDR(xw *xdr.Writer) (int, error) {
|
||||
xw.WriteUint32(o.Magic)
|
||||
if len(o.NodeID) > 64 {
|
||||
return xw.Tot(), xdr.ErrElementSizeExceeded
|
||||
}
|
||||
xw.WriteString(o.NodeID)
|
||||
return xw.Tot(), xw.Error()
|
||||
}
|
||||
|
||||
func (o *QueryV1) DecodeXDR(r io.Reader) error {
|
||||
xr := xdr.NewReader(r)
|
||||
return o.decodeXDR(xr)
|
||||
}
|
||||
|
||||
func (o *QueryV1) UnmarshalXDR(bs []byte) error {
|
||||
var buf = bytes.NewBuffer(bs)
|
||||
var xr = xdr.NewReader(buf)
|
||||
return o.decodeXDR(xr)
|
||||
}
|
||||
|
||||
func (o *QueryV1) decodeXDR(xr *xdr.Reader) error {
|
||||
o.Magic = xr.ReadUint32()
|
||||
o.NodeID = xr.ReadStringMax(64)
|
||||
return xr.Error()
|
||||
}
|
||||
|
||||
func (o AnnounceV1) EncodeXDR(w io.Writer) (int, error) {
|
||||
var xw = xdr.NewWriter(w)
|
||||
return o.encodeXDR(xw)
|
||||
}
|
||||
|
||||
func (o AnnounceV1) MarshalXDR() []byte {
|
||||
var buf bytes.Buffer
|
||||
var xw = xdr.NewWriter(&buf)
|
||||
o.encodeXDR(xw)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (o AnnounceV1) encodeXDR(xw *xdr.Writer) (int, error) {
|
||||
xw.WriteUint32(o.Magic)
|
||||
xw.WriteUint16(o.Port)
|
||||
if len(o.NodeID) > 64 {
|
||||
return xw.Tot(), xdr.ErrElementSizeExceeded
|
||||
}
|
||||
xw.WriteString(o.NodeID)
|
||||
if len(o.IP) > 16 {
|
||||
return xw.Tot(), xdr.ErrElementSizeExceeded
|
||||
}
|
||||
xw.WriteBytes(o.IP)
|
||||
return xw.Tot(), xw.Error()
|
||||
}
|
||||
|
||||
func (o *AnnounceV1) DecodeXDR(r io.Reader) error {
|
||||
xr := xdr.NewReader(r)
|
||||
return o.decodeXDR(xr)
|
||||
}
|
||||
|
||||
func (o *AnnounceV1) UnmarshalXDR(bs []byte) error {
|
||||
var buf = bytes.NewBuffer(bs)
|
||||
var xr = xdr.NewReader(buf)
|
||||
return o.decodeXDR(xr)
|
||||
}
|
||||
|
||||
func (o *AnnounceV1) decodeXDR(xr *xdr.Reader) error {
|
||||
o.Magic = xr.ReadUint32()
|
||||
o.Port = xr.ReadUint16()
|
||||
o.NodeID = xr.ReadStringMax(64)
|
||||
o.IP = xr.ReadBytesMax(16)
|
||||
return xr.Error()
|
||||
}
|
||||
|
||||
func (o QueryV2) EncodeXDR(w io.Writer) (int, error) {
|
||||
var xw = xdr.NewWriter(w)
|
||||
return o.encodeXDR(xw)
|
||||
}
|
||||
|
||||
func (o QueryV2) MarshalXDR() []byte {
|
||||
var buf bytes.Buffer
|
||||
var xw = xdr.NewWriter(&buf)
|
||||
o.encodeXDR(xw)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (o QueryV2) encodeXDR(xw *xdr.Writer) (int, error) {
|
||||
xw.WriteUint32(o.Magic)
|
||||
if len(o.NodeID) > 64 {
|
||||
return xw.Tot(), xdr.ErrElementSizeExceeded
|
||||
}
|
||||
xw.WriteString(o.NodeID)
|
||||
return xw.Tot(), xw.Error()
|
||||
}
|
||||
|
||||
func (o *QueryV2) DecodeXDR(r io.Reader) error {
|
||||
xr := xdr.NewReader(r)
|
||||
return o.decodeXDR(xr)
|
||||
}
|
||||
|
||||
func (o *QueryV2) UnmarshalXDR(bs []byte) error {
|
||||
var buf = bytes.NewBuffer(bs)
|
||||
var xr = xdr.NewReader(buf)
|
||||
return o.decodeXDR(xr)
|
||||
}
|
||||
|
||||
func (o *QueryV2) decodeXDR(xr *xdr.Reader) error {
|
||||
o.Magic = xr.ReadUint32()
|
||||
o.NodeID = xr.ReadStringMax(64)
|
||||
return xr.Error()
|
||||
}
|
||||
|
||||
func (o AnnounceV2) EncodeXDR(w io.Writer) (int, error) {
|
||||
var xw = xdr.NewWriter(w)
|
||||
return o.encodeXDR(xw)
|
||||
}
|
||||
|
||||
func (o AnnounceV2) MarshalXDR() []byte {
|
||||
var buf bytes.Buffer
|
||||
var xw = xdr.NewWriter(&buf)
|
||||
o.encodeXDR(xw)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (o AnnounceV2) encodeXDR(xw *xdr.Writer) (int, error) {
|
||||
xw.WriteUint32(o.Magic)
|
||||
if len(o.NodeID) > 64 {
|
||||
return xw.Tot(), xdr.ErrElementSizeExceeded
|
||||
}
|
||||
xw.WriteString(o.NodeID)
|
||||
if len(o.Addresses) > 16 {
|
||||
return xw.Tot(), xdr.ErrElementSizeExceeded
|
||||
}
|
||||
xw.WriteUint32(uint32(len(o.Addresses)))
|
||||
for i := range o.Addresses {
|
||||
o.Addresses[i].encodeXDR(xw)
|
||||
}
|
||||
return xw.Tot(), xw.Error()
|
||||
}
|
||||
|
||||
func (o *AnnounceV2) DecodeXDR(r io.Reader) error {
|
||||
xr := xdr.NewReader(r)
|
||||
return o.decodeXDR(xr)
|
||||
}
|
||||
|
||||
func (o *AnnounceV2) UnmarshalXDR(bs []byte) error {
|
||||
var buf = bytes.NewBuffer(bs)
|
||||
var xr = xdr.NewReader(buf)
|
||||
return o.decodeXDR(xr)
|
||||
}
|
||||
|
||||
func (o *AnnounceV2) decodeXDR(xr *xdr.Reader) error {
|
||||
o.Magic = xr.ReadUint32()
|
||||
o.NodeID = xr.ReadStringMax(64)
|
||||
_AddressesSize := int(xr.ReadUint32())
|
||||
if _AddressesSize > 16 {
|
||||
return xdr.ErrElementSizeExceeded
|
||||
}
|
||||
o.Addresses = make([]Address, _AddressesSize)
|
||||
for i := range o.Addresses {
|
||||
(&o.Addresses[i]).decodeXDR(xr)
|
||||
}
|
||||
return xr.Error()
|
||||
}
|
||||
|
||||
func (o Address) EncodeXDR(w io.Writer) (int, error) {
|
||||
var xw = xdr.NewWriter(w)
|
||||
return o.encodeXDR(xw)
|
||||
}
|
||||
|
||||
func (o Address) MarshalXDR() []byte {
|
||||
var buf bytes.Buffer
|
||||
var xw = xdr.NewWriter(&buf)
|
||||
o.encodeXDR(xw)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (o Address) encodeXDR(xw *xdr.Writer) (int, error) {
|
||||
if len(o.IP) > 16 {
|
||||
return xw.Tot(), xdr.ErrElementSizeExceeded
|
||||
}
|
||||
xw.WriteBytes(o.IP)
|
||||
xw.WriteUint16(o.Port)
|
||||
return xw.Tot(), xw.Error()
|
||||
}
|
||||
|
||||
func (o *Address) DecodeXDR(r io.Reader) error {
|
||||
xr := xdr.NewReader(r)
|
||||
return o.decodeXDR(xr)
|
||||
}
|
||||
|
||||
func (o *Address) UnmarshalXDR(bs []byte) error {
|
||||
var buf = bytes.NewBuffer(bs)
|
||||
var xr = xdr.NewReader(buf)
|
||||
return o.decodeXDR(xr)
|
||||
}
|
||||
|
||||
func (o *Address) decodeXDR(xr *xdr.Reader) error {
|
||||
o.IP = xr.ReadBytesMax(16)
|
||||
o.Port = xr.ReadUint16()
|
||||
return xr.Error()
|
||||
}
|
||||
Reference in New Issue
Block a user