github.com/kayoticsully/syncthing@v0.8.9-0.20140724133906-c45a2fdc03f8/discover/discover.go (about) 1 // Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file). 2 // All rights reserved. Use of this source code is governed by an MIT-style 3 // license that can be found in the LICENSE file. 4 5 package discover 6 7 import ( 8 "bytes" 9 "encoding/hex" 10 "errors" 11 "fmt" 12 "io" 13 "net" 14 "sync" 15 "time" 16 17 "github.com/calmh/syncthing/beacon" 18 "github.com/calmh/syncthing/events" 19 "github.com/calmh/syncthing/protocol" 20 ) 21 22 type Discoverer struct { 23 myID protocol.NodeID 24 listenAddrs []string 25 localBcastIntv time.Duration 26 globalBcastIntv time.Duration 27 beacon *beacon.Beacon 28 registry map[protocol.NodeID][]string 29 registryLock sync.RWMutex 30 extServer string 31 extPort uint16 32 localBcastTick <-chan time.Time 33 forcedBcastTick chan time.Time 34 extAnnounceOK bool 35 extAnnounceOKmut sync.Mutex 36 } 37 38 var ( 39 ErrIncorrectMagic = errors.New("incorrect magic number") 40 ) 41 42 // We tolerate a certain amount of errors because we might be running on 43 // laptops that sleep and wake, have intermittent network connectivity, etc. 44 // When we hit this many errors in succession, we stop. 45 const maxErrors = 30 46 47 func NewDiscoverer(id protocol.NodeID, addresses []string, localPort int) (*Discoverer, error) { 48 b, err := beacon.New(localPort) 49 if err != nil { 50 return nil, err 51 } 52 disc := &Discoverer{ 53 myID: id, 54 listenAddrs: addresses, 55 localBcastIntv: 30 * time.Second, 56 globalBcastIntv: 1800 * time.Second, 57 beacon: b, 58 registry: make(map[protocol.NodeID][]string), 59 } 60 61 go disc.recvAnnouncements() 62 63 return disc, nil 64 } 65 66 func (d *Discoverer) StartLocal() { 67 d.localBcastTick = time.Tick(d.localBcastIntv) 68 d.forcedBcastTick = make(chan time.Time) 69 go d.sendLocalAnnouncements() 70 } 71 72 func (d *Discoverer) StartGlobal(server string, extPort uint16) { 73 d.extServer = server 74 d.extPort = extPort 75 go d.sendExternalAnnouncements() 76 } 77 78 func (d *Discoverer) ExtAnnounceOK() bool { 79 d.extAnnounceOKmut.Lock() 80 defer d.extAnnounceOKmut.Unlock() 81 return d.extAnnounceOK 82 } 83 84 func (d *Discoverer) Lookup(node protocol.NodeID) []string { 85 d.registryLock.Lock() 86 addr, ok := d.registry[node] 87 d.registryLock.Unlock() 88 89 if ok { 90 return addr 91 } else if len(d.extServer) != 0 { 92 // We might want to cache this, but not permanently so it needs some intelligence 93 return d.externalLookup(node) 94 } 95 return nil 96 } 97 98 func (d *Discoverer) Hint(node string, addrs []string) { 99 resAddrs := resolveAddrs(addrs) 100 var id protocol.NodeID 101 id.UnmarshalText([]byte(node)) 102 d.registerNode(nil, Node{ 103 Addresses: resAddrs, 104 ID: id[:], 105 }) 106 } 107 108 func (d *Discoverer) All() map[protocol.NodeID][]string { 109 d.registryLock.RLock() 110 nodes := make(map[protocol.NodeID][]string, len(d.registry)) 111 for node, addrs := range d.registry { 112 addrsCopy := make([]string, len(addrs)) 113 copy(addrsCopy, addrs) 114 nodes[node] = addrsCopy 115 } 116 d.registryLock.RUnlock() 117 return nodes 118 } 119 120 func (d *Discoverer) announcementPkt() []byte { 121 var addrs []Address 122 for _, astr := range d.listenAddrs { 123 addr, err := net.ResolveTCPAddr("tcp", astr) 124 if err != nil { 125 l.Warnln("%v: not announcing %s", err, astr) 126 continue 127 } else if debug { 128 l.Debugf("discover: announcing %s: %#v", astr, addr) 129 } 130 if len(addr.IP) == 0 || addr.IP.IsUnspecified() { 131 addrs = append(addrs, Address{Port: uint16(addr.Port)}) 132 } else if bs := addr.IP.To4(); bs != nil { 133 addrs = append(addrs, Address{IP: bs, Port: uint16(addr.Port)}) 134 } else if bs := addr.IP.To16(); bs != nil { 135 addrs = append(addrs, Address{IP: bs, Port: uint16(addr.Port)}) 136 } 137 } 138 var pkt = Announce{ 139 Magic: AnnouncementMagic, 140 This: Node{d.myID[:], addrs}, 141 } 142 return pkt.MarshalXDR() 143 } 144 145 func (d *Discoverer) sendLocalAnnouncements() { 146 var addrs = resolveAddrs(d.listenAddrs) 147 148 var pkt = Announce{ 149 Magic: AnnouncementMagic, 150 This: Node{d.myID[:], addrs}, 151 } 152 153 for { 154 pkt.Extra = nil 155 d.registryLock.RLock() 156 for node, addrs := range d.registry { 157 if len(pkt.Extra) == 16 { 158 break 159 } 160 161 anode := Node{node[:], resolveAddrs(addrs)} 162 pkt.Extra = append(pkt.Extra, anode) 163 } 164 d.registryLock.RUnlock() 165 166 d.beacon.Send(pkt.MarshalXDR()) 167 168 select { 169 case <-d.localBcastTick: 170 case <-d.forcedBcastTick: 171 } 172 } 173 } 174 175 func (d *Discoverer) sendExternalAnnouncements() { 176 // this should go in the Discoverer struct 177 errorRetryIntv := 60 * time.Second 178 179 remote, err := net.ResolveUDPAddr("udp", d.extServer) 180 for err != nil { 181 l.Warnf("Global discovery: %v; trying again in %v", err, errorRetryIntv) 182 time.Sleep(errorRetryIntv) 183 remote, err = net.ResolveUDPAddr("udp", d.extServer) 184 } 185 186 conn, err := net.ListenUDP("udp", nil) 187 for err != nil { 188 l.Warnf("Global discovery: %v; trying again in %v", err, errorRetryIntv) 189 time.Sleep(errorRetryIntv) 190 conn, err = net.ListenUDP("udp", nil) 191 } 192 193 var buf []byte 194 if d.extPort != 0 { 195 var pkt = Announce{ 196 Magic: AnnouncementMagic, 197 This: Node{d.myID[:], []Address{{Port: d.extPort}}}, 198 } 199 buf = pkt.MarshalXDR() 200 } else { 201 buf = d.announcementPkt() 202 } 203 204 for { 205 var ok bool 206 207 if debug { 208 l.Debugf("discover: send announcement -> %v\n%s", remote, hex.Dump(buf)) 209 } 210 211 _, err := conn.WriteTo(buf, remote) 212 if err != nil { 213 if debug { 214 l.Debugln("discover: warning:", err) 215 } 216 ok = false 217 } else { 218 // Verify that the announce server responds positively for our node ID 219 220 time.Sleep(1 * time.Second) 221 res := d.externalLookup(d.myID) 222 if debug { 223 l.Debugln("discover: external lookup check:", res) 224 } 225 ok = len(res) > 0 226 } 227 228 d.extAnnounceOKmut.Lock() 229 d.extAnnounceOK = ok 230 d.extAnnounceOKmut.Unlock() 231 232 if ok { 233 time.Sleep(d.globalBcastIntv) 234 } else { 235 time.Sleep(errorRetryIntv) 236 } 237 } 238 } 239 240 func (d *Discoverer) recvAnnouncements() { 241 for { 242 buf, addr := d.beacon.Recv() 243 244 if debug { 245 l.Debugf("discover: read announcement:\n%s", hex.Dump(buf)) 246 } 247 248 var pkt Announce 249 err := pkt.UnmarshalXDR(buf) 250 if err != nil && err != io.EOF { 251 continue 252 } 253 254 if debug { 255 l.Debugf("discover: parsed announcement: %#v", pkt) 256 } 257 258 var newNode bool 259 if bytes.Compare(pkt.This.ID, d.myID[:]) != 0 { 260 newNode = d.registerNode(addr, pkt.This) 261 for _, node := range pkt.Extra { 262 if bytes.Compare(node.ID, d.myID[:]) != 0 { 263 if d.registerNode(nil, node) { 264 newNode = true 265 } 266 } 267 } 268 } 269 270 if newNode { 271 select { 272 case d.forcedBcastTick <- time.Now(): 273 } 274 } 275 } 276 } 277 278 func (d *Discoverer) registerNode(addr net.Addr, node Node) bool { 279 var addrs []string 280 for _, a := range node.Addresses { 281 var nodeAddr string 282 if len(a.IP) > 0 { 283 nodeAddr = fmt.Sprintf("%s:%d", net.IP(a.IP), a.Port) 284 addrs = append(addrs, nodeAddr) 285 } else if addr != nil { 286 ua := addr.(*net.UDPAddr) 287 ua.Port = int(a.Port) 288 nodeAddr = ua.String() 289 addrs = append(addrs, nodeAddr) 290 } 291 } 292 if len(addrs) == 0 { 293 if debug { 294 l.Debugln("discover: no valid address for", node.ID) 295 } 296 } 297 if debug { 298 l.Debugf("discover: register: %s -> %#v", node.ID, addrs) 299 } 300 var id protocol.NodeID 301 copy(id[:], node.ID) 302 d.registryLock.Lock() 303 _, seen := d.registry[id] 304 d.registry[id] = addrs 305 d.registryLock.Unlock() 306 307 if !seen { 308 events.Default.Log(events.NodeDiscovered, map[string]interface{}{ 309 "node": id.String(), 310 "addrs": addrs, 311 }) 312 } 313 return !seen 314 } 315 316 func (d *Discoverer) externalLookup(node protocol.NodeID) []string { 317 extIP, err := net.ResolveUDPAddr("udp", d.extServer) 318 if err != nil { 319 if debug { 320 l.Debugf("discover: %v; no external lookup", err) 321 } 322 return nil 323 } 324 325 conn, err := net.DialUDP("udp", nil, extIP) 326 if err != nil { 327 if debug { 328 l.Debugf("discover: %v; no external lookup", err) 329 } 330 return nil 331 } 332 defer conn.Close() 333 334 err = conn.SetDeadline(time.Now().Add(5 * time.Second)) 335 if err != nil { 336 if debug { 337 l.Debugf("discover: %v; no external lookup", err) 338 } 339 return nil 340 } 341 342 buf := Query{QueryMagic, node[:]}.MarshalXDR() 343 _, err = conn.Write(buf) 344 if err != nil { 345 if debug { 346 l.Debugf("discover: %v; no external lookup", err) 347 } 348 return nil 349 } 350 351 buf = make([]byte, 2048) 352 n, err := conn.Read(buf) 353 if err != nil { 354 if err, ok := err.(net.Error); ok && err.Timeout() { 355 // Expected if the server doesn't know about requested node ID 356 return nil 357 } 358 if debug { 359 l.Debugf("discover: %v; no external lookup", err) 360 } 361 return nil 362 } 363 364 if debug { 365 l.Debugf("discover: read external:\n%s", hex.Dump(buf[:n])) 366 } 367 368 var pkt Announce 369 err = pkt.UnmarshalXDR(buf[:n]) 370 if err != nil && err != io.EOF { 371 if debug { 372 l.Debugln("discover:", err) 373 } 374 return nil 375 } 376 377 if debug { 378 l.Debugf("discover: parsed external: %#v", pkt) 379 } 380 381 var addrs []string 382 for _, a := range pkt.This.Addresses { 383 nodeAddr := fmt.Sprintf("%s:%d", net.IP(a.IP), a.Port) 384 addrs = append(addrs, nodeAddr) 385 } 386 return addrs 387 } 388 389 func addrToAddr(addr *net.TCPAddr) Address { 390 if len(addr.IP) == 0 || addr.IP.IsUnspecified() { 391 return Address{Port: uint16(addr.Port)} 392 } else if bs := addr.IP.To4(); bs != nil { 393 return Address{IP: bs, Port: uint16(addr.Port)} 394 } else if bs := addr.IP.To16(); bs != nil { 395 return Address{IP: bs, Port: uint16(addr.Port)} 396 } 397 return Address{} 398 } 399 400 func resolveAddrs(addrs []string) []Address { 401 var raddrs []Address 402 for _, addrStr := range addrs { 403 addrRes, err := net.ResolveTCPAddr("tcp", addrStr) 404 if err != nil { 405 continue 406 } 407 addr := addrToAddr(addrRes) 408 if len(addr.IP) > 0 { 409 raddrs = append(raddrs, addr) 410 } else { 411 raddrs = append(raddrs, Address{Port: addr.Port}) 412 } 413 } 414 return raddrs 415 }