local.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. // Copyright (C) 2014 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. //go:generate go run ../../script/protofmt.go local.proto
  7. //go:generate protoc -I ../../vendor/ -I ../../vendor/github.com/gogo/protobuf/protobuf -I . --gogofast_out=. local.proto
  8. package discover
  9. import (
  10. "encoding/binary"
  11. "encoding/hex"
  12. "io"
  13. "net"
  14. "net/url"
  15. "strconv"
  16. "time"
  17. "github.com/syncthing/syncthing/lib/beacon"
  18. "github.com/syncthing/syncthing/lib/events"
  19. "github.com/syncthing/syncthing/lib/protocol"
  20. "github.com/syncthing/syncthing/lib/rand"
  21. "github.com/thejerf/suture"
  22. )
  23. type localClient struct {
  24. *suture.Supervisor
  25. myID protocol.DeviceID
  26. addrList AddressLister
  27. name string
  28. beacon beacon.Interface
  29. localBcastStart time.Time
  30. localBcastTick <-chan time.Time
  31. forcedBcastTick chan time.Time
  32. *cache
  33. }
  34. const (
  35. BroadcastInterval = 30 * time.Second
  36. CacheLifeTime = 3 * BroadcastInterval
  37. Magic = uint32(0x2EA7D90B) // same as in BEP
  38. v13Magic = uint32(0x7D79BC40) // previous version
  39. )
  40. func NewLocal(id protocol.DeviceID, addr string, addrList AddressLister) (FinderService, error) {
  41. c := &localClient{
  42. Supervisor: suture.NewSimple("local"),
  43. myID: id,
  44. addrList: addrList,
  45. localBcastTick: time.NewTicker(BroadcastInterval).C,
  46. forcedBcastTick: make(chan time.Time),
  47. localBcastStart: time.Now(),
  48. cache: newCache(),
  49. }
  50. host, port, err := net.SplitHostPort(addr)
  51. if err != nil {
  52. return nil, err
  53. }
  54. if len(host) == 0 {
  55. // A broadcast client
  56. c.name = "IPv4 local"
  57. bcPort, err := strconv.Atoi(port)
  58. if err != nil {
  59. return nil, err
  60. }
  61. c.startLocalIPv4Broadcasts(bcPort)
  62. } else {
  63. // A multicast client
  64. c.name = "IPv6 local"
  65. c.startLocalIPv6Multicasts(addr)
  66. }
  67. go c.sendLocalAnnouncements()
  68. return c, nil
  69. }
  70. func (c *localClient) startLocalIPv4Broadcasts(localPort int) {
  71. c.beacon = beacon.NewBroadcast(localPort)
  72. c.Add(c.beacon)
  73. go c.recvAnnouncements(c.beacon)
  74. }
  75. func (c *localClient) startLocalIPv6Multicasts(localMCAddr string) {
  76. c.beacon = beacon.NewMulticast(localMCAddr)
  77. c.Add(c.beacon)
  78. go c.recvAnnouncements(c.beacon)
  79. }
  80. // Lookup returns a list of addresses the device is available at.
  81. func (c *localClient) Lookup(device protocol.DeviceID) (addresses []string, err error) {
  82. if cache, ok := c.Get(device); ok {
  83. if time.Since(cache.when) < CacheLifeTime {
  84. addresses = cache.Addresses
  85. }
  86. }
  87. return
  88. }
  89. func (c *localClient) String() string {
  90. return c.name
  91. }
  92. func (c *localClient) Error() error {
  93. return c.beacon.Error()
  94. }
  95. // announcementPkt appends the local discovery packet to send to msg. Returns
  96. // true if the packet should be sent, false if there is nothing useful to
  97. // send.
  98. func (c *localClient) announcementPkt(instanceID int64, msg []byte) ([]byte, bool) {
  99. addrs := c.addrList.AllAddresses()
  100. if len(addrs) == 0 {
  101. // Nothing to announce
  102. return msg, false
  103. }
  104. if cap(msg) >= 4 {
  105. msg = msg[:4]
  106. } else {
  107. msg = make([]byte, 4)
  108. }
  109. binary.BigEndian.PutUint32(msg, Magic)
  110. pkt := Announce{
  111. ID: c.myID,
  112. Addresses: addrs,
  113. InstanceID: instanceID,
  114. }
  115. bs, _ := pkt.Marshal()
  116. msg = append(msg, bs...)
  117. return msg, true
  118. }
  119. func (c *localClient) sendLocalAnnouncements() {
  120. var msg []byte
  121. var ok bool
  122. instanceID := rand.Int63()
  123. for {
  124. if msg, ok = c.announcementPkt(instanceID, msg[:0]); ok {
  125. c.beacon.Send(msg)
  126. }
  127. select {
  128. case <-c.localBcastTick:
  129. case <-c.forcedBcastTick:
  130. }
  131. }
  132. }
  133. func (c *localClient) recvAnnouncements(b beacon.Interface) {
  134. warnedAbout := make(map[string]bool)
  135. for {
  136. buf, addr := b.Recv()
  137. if len(buf) < 4 {
  138. l.Debugf("discover: short packet from %s")
  139. continue
  140. }
  141. magic := binary.BigEndian.Uint32(buf)
  142. switch magic {
  143. case Magic:
  144. // All good
  145. case v13Magic:
  146. // Old version
  147. if !warnedAbout[addr.String()] {
  148. l.Warnf("Incompatible (v0.13) local discovery packet from %v - upgrade that device to connect", addr)
  149. warnedAbout[addr.String()] = true
  150. }
  151. continue
  152. default:
  153. l.Debugf("discover: Incorrect magic %x from %s", magic, addr)
  154. continue
  155. }
  156. var pkt Announce
  157. err := pkt.Unmarshal(buf[4:])
  158. if err != nil && err != io.EOF {
  159. l.Debugf("discover: Failed to unmarshal local announcement from %s:\n%s", addr, hex.Dump(buf))
  160. continue
  161. }
  162. l.Debugf("discover: Received local announcement from %s for %s", addr, pkt.ID)
  163. var newDevice bool
  164. if pkt.ID != c.myID {
  165. newDevice = c.registerDevice(addr, pkt)
  166. }
  167. if newDevice {
  168. // Force a transmit to announce ourselves, if we are ready to do
  169. // so right away.
  170. select {
  171. case c.forcedBcastTick <- time.Now():
  172. default:
  173. }
  174. }
  175. }
  176. }
  177. func (c *localClient) registerDevice(src net.Addr, device Announce) bool {
  178. // Remember whether we already had a valid cache entry for this device.
  179. // If the instance ID has changed the remote device has restarted since
  180. // we last heard from it, so we should treat it as a new device.
  181. ce, existsAlready := c.Get(device.ID)
  182. isNewDevice := !existsAlready || time.Since(ce.when) > CacheLifeTime || ce.instanceID != device.InstanceID
  183. // Any empty or unspecified addresses should be set to the source address
  184. // of the announcement. We also skip any addresses we can't parse.
  185. l.Debugln("discover: Registering addresses for", device.ID)
  186. var validAddresses []string
  187. for _, addr := range device.Addresses {
  188. u, err := url.Parse(addr)
  189. if err != nil {
  190. continue
  191. }
  192. tcpAddr, err := net.ResolveTCPAddr("tcp", u.Host)
  193. if err != nil {
  194. continue
  195. }
  196. if len(tcpAddr.IP) == 0 || tcpAddr.IP.IsUnspecified() {
  197. srcAddr, err := net.ResolveTCPAddr("tcp", src.String())
  198. if err != nil {
  199. continue
  200. }
  201. // Do not use IPv6 source address if requested scheme is tcp4
  202. if u.Scheme == "tcp4" && srcAddr.IP.To4() == nil {
  203. continue
  204. }
  205. // Do not use IPv4 source address if requested scheme is tcp6
  206. if u.Scheme == "tcp6" && srcAddr.IP.To4() != nil {
  207. continue
  208. }
  209. host, _, err := net.SplitHostPort(src.String())
  210. if err != nil {
  211. continue
  212. }
  213. u.Host = net.JoinHostPort(host, strconv.Itoa(tcpAddr.Port))
  214. l.Debugf("discover: Reconstructed URL is %#v", u)
  215. validAddresses = append(validAddresses, u.String())
  216. l.Debugf("discover: Replaced address %v in %s to get %s", tcpAddr.IP, addr, u.String())
  217. } else {
  218. validAddresses = append(validAddresses, addr)
  219. l.Debugf("discover: Accepted address %s verbatim", addr)
  220. }
  221. }
  222. c.Set(device.ID, CacheEntry{
  223. Addresses: validAddresses,
  224. when: time.Now(),
  225. found: true,
  226. instanceID: device.InstanceID,
  227. })
  228. if isNewDevice {
  229. events.Default.Log(events.DeviceDiscovered, map[string]interface{}{
  230. "device": device.ID.String(),
  231. "addrs": validAddresses,
  232. })
  233. }
  234. return isNewDevice
  235. }