External discover

This commit is contained in:
Jakob Borg 2013-12-22 21:35:05 -05:00
parent 31ea72dbb3
commit f2d8b68278
5 changed files with 162 additions and 99 deletions

1
discover/cmd/discosrv/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
discosrv

View File

@ -0,0 +1,74 @@
package main
import (
"log"
"net"
"sync"
"github.com/calmh/syncthing/discover"
)
type Node struct {
IP []byte
Port uint16
}
var (
nodes = make(map[string]Node)
lock sync.Mutex
)
func main() {
addr, _ := net.ResolveUDPAddr("udp", ":22025")
conn, err := net.ListenUDP("udp", addr)
if err != nil {
panic(err)
}
var buf = make([]byte, 1024)
for {
n, addr, err := conn.ReadFromUDP(buf)
if err != nil {
panic(err)
}
pkt, err := discover.DecodePacket(buf[:n])
if err != nil {
log.Println("Warning:", err)
continue
}
switch pkt.Magic {
case 0x20121025:
// Announcement
//lock.Lock()
ip := addr.IP.To4()
if ip == nil {
ip = addr.IP.To16()
}
node := Node{ip, uint16(pkt.Port)}
log.Println("<-", pkt.ID, node)
nodes[pkt.ID] = node
//lock.Unlock()
case 0x19760309:
// Query
//lock.Lock()
node, ok := nodes[pkt.ID]
//lock.Unlock()
if ok {
pkt := discover.Packet{
Magic: 0x20121025,
ID: pkt.ID,
Port: node.Port,
IP: node.IP,
}
_, _, err = conn.WriteMsgUDP(discover.EncodePacket(pkt), nil, addr)
if err != nil {
log.Println("Warning:", err)
} else {
log.Println("->", pkt.ID, node)
}
}
}
}
}

View File

@ -146,22 +146,19 @@ func NewDiscoverer(id string, port int, extPort int, extServer string) (*Discove
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})
buf := EncodePacket(Packet{AnnouncementMagic, uint16(d.ListenPort), d.MyID, nil})
go d.writeAnnouncements(buf, remote4, d.BroadcastIntv)
}
func (d *Discoverer) sendExtAnnouncements() {
extIPs, err := net.LookupIP(d.extServer)
extIP, err := net.ResolveUDPAddr("udp", d.extServer+":22025")
if err != nil {
log.Printf("discover/external: %v; no external announcements", err)
return
}
buf := encodePacket(packet{AnnouncementMagic, uint16(d.ExtListenPort), d.MyID, nil})
for _, extIP := range extIPs {
remote4 := &net.UDPAddr{IP: extIP, Port: AnnouncementPort}
go d.writeAnnouncements(buf, remote4, d.ExtBroadcastIntv)
}
buf := EncodePacket(Packet{AnnouncementMagic, uint16(d.ExtListenPort), d.MyID, nil})
go d.writeAnnouncements(buf, extIP, d.ExtBroadcastIntv)
}
func (d *Discoverer) writeAnnouncements(buf []byte, remote *net.UDPAddr, intv time.Duration) {
@ -170,6 +167,7 @@ func (d *Discoverer) writeAnnouncements(buf []byte, remote *net.UDPAddr, intv ti
for errCounter < maxErrors {
_, _, err = d.conn.WriteMsgUDP(buf, nil, remote)
if err != nil {
log.Println("discover/write: warning:", err)
errCounter++
} else {
errCounter = 0
@ -191,8 +189,8 @@ func (d *Discoverer) recvAnnouncements() {
continue
}
pkt, err := decodePacket(buf[:n])
if err != nil || pkt.magic != AnnouncementMagic {
pkt, err := DecodePacket(buf[:n])
if err != nil || pkt.Magic != AnnouncementMagic {
errCounter++
time.Sleep(time.Second)
continue
@ -200,11 +198,11 @@ func (d *Discoverer) recvAnnouncements() {
errCounter = 0
if pkt.id != d.MyID {
nodeAddr := fmt.Sprintf("%s:%d", addr.IP.String(), pkt.port)
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 d.registry[pkt.ID] != nodeAddr {
d.registry[pkt.ID] = nodeAddr
}
d.registryLock.Unlock()
}
@ -213,58 +211,48 @@ func (d *Discoverer) recvAnnouncements() {
}
func (d *Discoverer) externalLookup(node string) (string, bool) {
extIPs, err := net.LookupIP(d.extServer)
extIP, err := net.ResolveUDPAddr("udp", d.extServer+":22025")
if err != nil {
log.Printf("discover/external: %v; no external lookup", err)
return "", false
}
var res = make(chan string, len(extIPs))
var failed = 0
for _, extIP := range extIPs {
remote := &net.UDPAddr{IP: extIP, Port: AnnouncementPort}
conn, err := net.DialUDP("udp", nil, remote)
if err != nil {
log.Printf("discover/external: %v; no external lookup", err)
failed++
continue
}
_, err = conn.Write(encodePacket(packet{QueryMagic, 0, node, nil}))
if err != nil {
log.Printf("discover/external: %v; no external lookup", err)
failed++
continue
}
go func() {
var buf = make([]byte, 1024)
_, err = conn.Read(buf)
if err != nil {
log.Printf("discover/external/read: %v; no external lookup", err)
return
}
pkt, err := decodePacket(buf)
if err != nil {
log.Printf("discover/external/read: %v; no external lookup", err)
return
}
if pkt.magic != AnnouncementMagic {
log.Printf("discover/external/read: bad magic; no external lookup", err)
return
}
res <- fmt.Sprintf("%s:%d", ipStr(pkt.ip), pkt.port)
}()
}
if failed == len(extIPs) {
// no point in waiting
var res = make(chan string, 1)
conn, err := net.DialUDP("udp", nil, extIP)
if err != nil {
log.Printf("discover/external: %v; no external lookup", err)
return "", false
}
_, err = conn.Write(EncodePacket(Packet{QueryMagic, 0, node, nil}))
if err != nil {
log.Printf("discover/external: %v; no external lookup", err)
return "", false
}
log.Println("query", extIP)
go func() {
var buf = make([]byte, 1024)
n, err := conn.Read(buf)
if err != nil {
log.Printf("discover/external/read: %v; no external lookup", err)
return
}
pkt, err := DecodePacket(buf[:n])
if err != nil {
log.Printf("discover/external/read: %v; no external lookup", err)
return
}
if pkt.Magic != AnnouncementMagic {
log.Printf("discover/external/read: bad magic; no external lookup", err)
return
}
res <- fmt.Sprintf("%s:%d", ipStr(pkt.IP), pkt.Port)
}()
select {
case r := <-res:
return r, true

View File

@ -6,11 +6,11 @@ import (
"fmt"
)
type packet struct {
magic uint32 // AnnouncementMagic or QueryMagic
port uint16 // unset if magic == QueryMagic
id string
ip []byte // zero length in local announcements
type Packet struct {
Magic uint32 // AnnouncementMagic or QueryMagic
Port uint16 // unset if magic == QueryMagic
ID string
IP []byte // zero length in local announcements
}
var (
@ -18,26 +18,26 @@ var (
errFormat = errors.New("incorrect packet format")
)
func encodePacket(pkt packet) []byte {
if l := len(pkt.ip); l != 0 && l != 4 && l != 16 {
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 idbs = []byte(pkt.ID)
var l = 4 + 4 + len(idbs) + pad(len(idbs))
if pkt.magic == AnnouncementMagic {
l += 4 + 4 + len(pkt.ip)
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)
binary.BigEndian.PutUint32(buf[offset:], pkt.Magic)
offset += 4
if pkt.magic == AnnouncementMagic {
binary.BigEndian.PutUint16(buf[offset:], uint16(pkt.port))
if pkt.Magic == AnnouncementMagic {
binary.BigEndian.PutUint16(buf[offset:], uint16(pkt.Port))
offset += 4
}
@ -46,39 +46,39 @@ func encodePacket(pkt packet) []byte {
copy(buf[offset:], idbs)
offset += len(idbs) + pad(len(idbs))
if pkt.magic == AnnouncementMagic {
binary.BigEndian.PutUint32(buf[offset:], uint32(len(pkt.ip)))
if pkt.Magic == AnnouncementMagic {
binary.BigEndian.PutUint32(buf[offset:], uint32(len(pkt.IP)))
offset += 4
copy(buf[offset:], pkt.ip)
offset += len(pkt.ip)
copy(buf[offset:], pkt.IP)
offset += len(pkt.IP)
}
return buf
}
func decodePacket(buf []byte) (*packet, error) {
var p packet
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:])
p.Magic = binary.BigEndian.Uint32(buf[offset:])
offset += 4
if p.magic != AnnouncementMagic && p.magic != QueryMagic {
if p.Magic != AnnouncementMagic && p.Magic != QueryMagic {
return nil, errBadMagic
}
if p.magic == AnnouncementMagic {
if p.Magic == AnnouncementMagic {
// Port Number
if len(buf) < offset+4 {
// short packet
return nil, errFormat
}
p.port = binary.BigEndian.Uint16(buf[offset:])
p.Port = binary.BigEndian.Uint16(buf[offset:])
offset += 2
reserved := binary.BigEndian.Uint16(buf[offset:])
if reserved != 0 {
@ -101,10 +101,10 @@ func decodePacket(buf []byte) (*packet, error) {
return nil, errFormat
}
idbs := buf[offset : offset+int(l)]
p.id = string(idbs)
p.ID = string(idbs)
offset += int(l) + pad(int(l))
if p.magic == AnnouncementMagic {
if p.Magic == AnnouncementMagic {
// IP
if len(buf) < offset+4 {
@ -123,7 +123,7 @@ func decodePacket(buf []byte) (*packet, error) {
return nil, errFormat
}
if l > 0 {
p.ip = buf[offset : offset+int(l)]
p.IP = buf[offset : offset+int(l)]
offset += int(l)
}
}

View File

@ -8,7 +8,7 @@ import (
var testdata = []struct {
data []byte
packet *packet
packet *Packet
err error
}{
{
@ -17,10 +17,10 @@ var testdata = []struct {
0x00, 0x00, 0x00, 0x05,
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00},
&packet{
magic: 0x20121025,
port: 0x1234,
id: "hello",
&Packet{
Magic: 0x20121025,
Port: 0x1234,
ID: "hello",
},
nil,
},
@ -31,11 +31,11 @@ var testdata = []struct {
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},
&Packet{
Magic: 0x20121025,
Port: 0x3456,
ID: "hello!!!",
IP: []byte{1, 2, 3, 4},
},
nil,
},
@ -43,9 +43,9 @@ var testdata = []struct {
[]byte{0x19, 0x76, 0x03, 0x09,
0x00, 0x00, 0x00, 0x06,
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x21, 0x00, 0x00},
&packet{
magic: 0x19760309,
id: "hello!",
&Packet{
Magic: 0x19760309,
ID: "hello!",
},
nil,
},
@ -68,7 +68,7 @@ var testdata = []struct {
errFormat,
},
{
[]byte{0x19, 0x77, 0x03, 0x09, // incorrect magic
[]byte{0x19, 0x77, 0x03, 0x09, // incorrect Magic
0x00, 0x00, 0x00, 0x06,
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x21, 0x00, 0x00},
nil,
@ -93,7 +93,7 @@ var testdata = []struct {
func TestDecodePacket(t *testing.T) {
for i, test := range testdata {
p, err := decodePacket(test.data)
p, err := DecodePacket(test.data)
if err != test.err {
t.Errorf("%d: unexpected error %v", i, err)
} else {
@ -109,7 +109,7 @@ func TestEncodePacket(t *testing.T) {
if test.err != nil {
continue
}
buf := encodePacket(*test.packet)
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)
}