From f2d8b68278ddb58ef263864d473cea1a2e7668ec Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Sun, 22 Dec 2013 21:35:05 -0500 Subject: [PATCH] External discover --- discover/cmd/discosrv/.gitignore | 1 + discover/cmd/discosrv/main.go | 74 ++++++++++++++++++++++ discover/discover.go | 102 ++++++++++++++----------------- discover/encoding.go | 52 ++++++++-------- discover/encoding_test.go | 32 +++++----- 5 files changed, 162 insertions(+), 99 deletions(-) create mode 100644 discover/cmd/discosrv/.gitignore create mode 100644 discover/cmd/discosrv/main.go diff --git a/discover/cmd/discosrv/.gitignore b/discover/cmd/discosrv/.gitignore new file mode 100644 index 000000000..6d96f202c --- /dev/null +++ b/discover/cmd/discosrv/.gitignore @@ -0,0 +1 @@ +discosrv diff --git a/discover/cmd/discosrv/main.go b/discover/cmd/discosrv/main.go new file mode 100644 index 000000000..2941dd9ce --- /dev/null +++ b/discover/cmd/discosrv/main.go @@ -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) + } + } + } + + } +} diff --git a/discover/discover.go b/discover/discover.go index cf87baade..1aa067f15 100644 --- a/discover/discover.go +++ b/discover/discover.go @@ -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 diff --git a/discover/encoding.go b/discover/encoding.go index 5eb36833b..bdf7338b2 100644 --- a/discover/encoding.go +++ b/discover/encoding.go @@ -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) } } diff --git a/discover/encoding_test.go b/discover/encoding_test.go index bfad4b1e1..bd212e777 100644 --- a/discover/encoding_test.go +++ b/discover/encoding_test.go @@ -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) }