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  }