1
0

global.go 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. // Copyright (C) 2015 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 http://mozilla.org/MPL/2.0/.
  6. package discover
  7. import (
  8. "bytes"
  9. "crypto/tls"
  10. "encoding/json"
  11. "errors"
  12. "io"
  13. "net/http"
  14. "net/url"
  15. "strconv"
  16. stdsync "sync"
  17. "time"
  18. "github.com/syncthing/syncthing/lib/events"
  19. "github.com/syncthing/syncthing/lib/protocol"
  20. )
  21. type globalClient struct {
  22. server string
  23. addrList AddressLister
  24. relayStat RelayStatusProvider
  25. announceClient httpClient
  26. queryClient httpClient
  27. noAnnounce bool
  28. stop chan struct{}
  29. errorHolder
  30. }
  31. type httpClient interface {
  32. Get(url string) (*http.Response, error)
  33. Post(url, ctype string, data io.Reader) (*http.Response, error)
  34. }
  35. const (
  36. defaultReannounceInterval = 30 * time.Minute
  37. announceErrorRetryInterval = 5 * time.Minute
  38. )
  39. type announcement struct {
  40. Direct []string `json:"direct"`
  41. Relays []Relay `json:"relays"`
  42. }
  43. type serverOptions struct {
  44. insecure bool // don't check certificate
  45. noAnnounce bool // don't announce
  46. id string // expected server device ID
  47. }
  48. func NewGlobal(server string, cert tls.Certificate, addrList AddressLister, relayStat RelayStatusProvider) (FinderService, error) {
  49. server, opts, err := parseOptions(server)
  50. if err != nil {
  51. return nil, err
  52. }
  53. var devID protocol.DeviceID
  54. if opts.id != "" {
  55. devID, err = protocol.DeviceIDFromString(opts.id)
  56. if err != nil {
  57. return nil, err
  58. }
  59. }
  60. // The http.Client used for announcements. It needs to have our
  61. // certificate to prove our identity, and may or may not verify the server
  62. // certificate depending on the insecure setting.
  63. var announceClient httpClient = &http.Client{
  64. Transport: &http.Transport{
  65. TLSClientConfig: &tls.Config{
  66. InsecureSkipVerify: opts.insecure,
  67. Certificates: []tls.Certificate{cert},
  68. },
  69. },
  70. }
  71. if opts.id != "" {
  72. announceClient = newIDCheckingHTTPClient(announceClient, devID)
  73. }
  74. // The http.Client used for queries. We don't need to present our
  75. // certificate here, so lets not include it. May be insecure if requested.
  76. var queryClient httpClient = &http.Client{
  77. Transport: &http.Transport{
  78. TLSClientConfig: &tls.Config{
  79. InsecureSkipVerify: opts.insecure,
  80. },
  81. },
  82. }
  83. if opts.id != "" {
  84. queryClient = newIDCheckingHTTPClient(queryClient, devID)
  85. }
  86. cl := &globalClient{
  87. server: server,
  88. addrList: addrList,
  89. relayStat: relayStat,
  90. announceClient: announceClient,
  91. queryClient: queryClient,
  92. noAnnounce: opts.noAnnounce,
  93. stop: make(chan struct{}),
  94. }
  95. cl.setError(errors.New("not announced"))
  96. return cl, nil
  97. }
  98. // Lookup returns the list of addresses where the given device is available;
  99. // direct, and via relays.
  100. func (c *globalClient) Lookup(device protocol.DeviceID) (direct []string, relays []Relay, err error) {
  101. qURL, err := url.Parse(c.server)
  102. if err != nil {
  103. return nil, nil, err
  104. }
  105. q := qURL.Query()
  106. q.Set("device", device.String())
  107. qURL.RawQuery = q.Encode()
  108. resp, err := c.queryClient.Get(qURL.String())
  109. if err != nil {
  110. l.Debugln("globalClient.Lookup", qURL, err)
  111. return nil, nil, err
  112. }
  113. if resp.StatusCode != 200 {
  114. resp.Body.Close()
  115. l.Debugln("globalClient.Lookup", qURL, resp.Status)
  116. return nil, nil, errors.New(resp.Status)
  117. }
  118. // TODO: Handle 429 and Retry-After?
  119. var ann announcement
  120. err = json.NewDecoder(resp.Body).Decode(&ann)
  121. resp.Body.Close()
  122. return ann.Direct, ann.Relays, err
  123. }
  124. func (c *globalClient) String() string {
  125. return "global@" + c.server
  126. }
  127. func (c *globalClient) Serve() {
  128. if c.noAnnounce {
  129. // We're configured to not do announcements, only lookups. To maintain
  130. // the same interface, we just pause here if Serve() is run.
  131. <-c.stop
  132. return
  133. }
  134. timer := time.NewTimer(0)
  135. defer timer.Stop()
  136. eventSub := events.Default.Subscribe(events.ExternalPortMappingChanged | events.RelayStateChanged)
  137. defer events.Default.Unsubscribe(eventSub)
  138. for {
  139. select {
  140. case <-eventSub.C():
  141. c.sendAnnouncement(timer)
  142. case <-timer.C:
  143. c.sendAnnouncement(timer)
  144. case <-c.stop:
  145. return
  146. }
  147. }
  148. }
  149. func (c *globalClient) sendAnnouncement(timer *time.Timer) {
  150. var ann announcement
  151. if c.addrList != nil {
  152. ann.Direct = c.addrList.ExternalAddresses()
  153. }
  154. if c.relayStat != nil {
  155. for _, relay := range c.relayStat.Relays() {
  156. latency, ok := c.relayStat.RelayStatus(relay)
  157. if ok {
  158. ann.Relays = append(ann.Relays, Relay{
  159. URL: relay,
  160. Latency: int32(latency / time.Millisecond),
  161. })
  162. }
  163. }
  164. }
  165. if len(ann.Direct)+len(ann.Relays) == 0 {
  166. c.setError(errors.New("nothing to announce"))
  167. l.Debugln("Nothing to announce")
  168. timer.Reset(announceErrorRetryInterval)
  169. return
  170. }
  171. // The marshal doesn't fail, I promise.
  172. postData, _ := json.Marshal(ann)
  173. l.Debugf("Announcement: %s", postData)
  174. resp, err := c.announceClient.Post(c.server, "application/json", bytes.NewReader(postData))
  175. if err != nil {
  176. l.Debugln("announce POST:", err)
  177. c.setError(err)
  178. timer.Reset(announceErrorRetryInterval)
  179. return
  180. }
  181. l.Debugln("announce POST:", resp.Status)
  182. resp.Body.Close()
  183. if resp.StatusCode < 200 || resp.StatusCode > 299 {
  184. l.Debugln("announce POST:", resp.Status)
  185. c.setError(errors.New(resp.Status))
  186. if h := resp.Header.Get("Retry-After"); h != "" {
  187. // The server has a recommendation on when we should
  188. // retry. Follow it.
  189. if secs, err := strconv.Atoi(h); err == nil && secs > 0 {
  190. l.Debugln("announce Retry-After:", secs, err)
  191. timer.Reset(time.Duration(secs) * time.Second)
  192. return
  193. }
  194. }
  195. timer.Reset(announceErrorRetryInterval)
  196. return
  197. }
  198. c.setError(nil)
  199. if h := resp.Header.Get("Reannounce-After"); h != "" {
  200. // The server has a recommendation on when we should
  201. // reannounce. Follow it.
  202. if secs, err := strconv.Atoi(h); err == nil && secs > 0 {
  203. l.Debugln("announce Reannounce-After:", secs, err)
  204. timer.Reset(time.Duration(secs) * time.Second)
  205. return
  206. }
  207. }
  208. timer.Reset(defaultReannounceInterval)
  209. }
  210. func (c *globalClient) Stop() {
  211. close(c.stop)
  212. }
  213. func (c *globalClient) Cache() map[protocol.DeviceID]CacheEntry {
  214. // The globalClient doesn't do caching
  215. return nil
  216. }
  217. // parseOptions parses and strips away any ?query=val options, setting the
  218. // corresponding field in the serverOptions struct. Unknown query options are
  219. // ignored and removed.
  220. func parseOptions(dsn string) (server string, opts serverOptions, err error) {
  221. p, err := url.Parse(dsn)
  222. if err != nil {
  223. return "", serverOptions{}, err
  224. }
  225. // Grab known options from the query string
  226. q := p.Query()
  227. opts.id = q.Get("id")
  228. opts.insecure = opts.id != "" || queryBool(q, "insecure")
  229. opts.noAnnounce = queryBool(q, "noannounce")
  230. // Check for disallowed combinations
  231. if p.Scheme == "http" {
  232. if !opts.insecure {
  233. return "", serverOptions{}, errors.New("http without insecure not supported")
  234. }
  235. if !opts.noAnnounce {
  236. return "", serverOptions{}, errors.New("http without noannounce not supported")
  237. }
  238. } else if p.Scheme != "https" {
  239. return "", serverOptions{}, errors.New("unsupported scheme " + p.Scheme)
  240. }
  241. // Remove the query string
  242. p.RawQuery = ""
  243. server = p.String()
  244. return
  245. }
  246. // queryBool returns the query parameter parsed as a boolean. An empty value
  247. // ("?foo") is considered true, as is any value string except false
  248. // ("?foo=false").
  249. func queryBool(q url.Values, key string) bool {
  250. if _, ok := q[key]; !ok {
  251. return false
  252. }
  253. return q.Get(key) != "false"
  254. }
  255. type idCheckingHTTPClient struct {
  256. httpClient
  257. id protocol.DeviceID
  258. }
  259. func newIDCheckingHTTPClient(client httpClient, id protocol.DeviceID) *idCheckingHTTPClient {
  260. return &idCheckingHTTPClient{
  261. httpClient: client,
  262. id: id,
  263. }
  264. }
  265. func (c *idCheckingHTTPClient) check(resp *http.Response) error {
  266. if resp.TLS == nil {
  267. return errors.New("security: not TLS")
  268. }
  269. if len(resp.TLS.PeerCertificates) == 0 {
  270. return errors.New("security: no certificates")
  271. }
  272. id := protocol.NewDeviceID(resp.TLS.PeerCertificates[0].Raw)
  273. if !id.Equals(c.id) {
  274. return errors.New("security: incorrect device id")
  275. }
  276. return nil
  277. }
  278. func (c *idCheckingHTTPClient) Get(url string) (*http.Response, error) {
  279. resp, err := c.httpClient.Get(url)
  280. if err != nil {
  281. return nil, err
  282. }
  283. if err := c.check(resp); err != nil {
  284. return nil, err
  285. }
  286. return resp, nil
  287. }
  288. func (c *idCheckingHTTPClient) Post(url, ctype string, data io.Reader) (*http.Response, error) {
  289. resp, err := c.httpClient.Post(url, ctype, data)
  290. if err != nil {
  291. return nil, err
  292. }
  293. if err := c.check(resp); err != nil {
  294. return nil, err
  295. }
  296. return resp, nil
  297. }
  298. type errorHolder struct {
  299. err error
  300. mut stdsync.Mutex // uses stdlib sync as I want this to be trivially embeddable, and there is no risk of blocking
  301. }
  302. func (e *errorHolder) setError(err error) {
  303. e.mut.Lock()
  304. e.err = err
  305. e.mut.Unlock()
  306. }
  307. func (e *errorHolder) Error() error {
  308. e.mut.Lock()
  309. err := e.err
  310. e.mut.Unlock()
  311. return err
  312. }