stun.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. // Copyright (C) 2019 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. package stun
  7. import (
  8. "context"
  9. "net"
  10. "time"
  11. "github.com/ccding/go-stun/stun"
  12. "github.com/syncthing/syncthing/lib/config"
  13. "github.com/syncthing/syncthing/lib/svcutil"
  14. )
  15. const stunRetryInterval = 5 * time.Minute
  16. type (
  17. Host = stun.Host
  18. NATType = stun.NATType
  19. )
  20. // NAT types.
  21. const (
  22. NATError = stun.NATError
  23. NATUnknown = stun.NATUnknown
  24. NATNone = stun.NATNone
  25. NATBlocked = stun.NATBlocked
  26. NATFull = stun.NATFull
  27. NATSymmetric = stun.NATSymmetric
  28. NATRestricted = stun.NATRestricted
  29. NATPortRestricted = stun.NATPortRestricted
  30. NATSymmetricUDPFirewall = stun.NATSymmetricUDPFirewall
  31. )
  32. type Subscriber interface {
  33. OnNATTypeChanged(natType NATType)
  34. OnExternalAddressChanged(address *Host, via string)
  35. }
  36. type Service struct {
  37. name string
  38. cfg config.Wrapper
  39. subscriber Subscriber
  40. client *stun.Client
  41. lastWriter LastWriter
  42. natType NATType
  43. addr *Host
  44. }
  45. type LastWriter interface {
  46. LastWrite() time.Time
  47. }
  48. func New(cfg config.Wrapper, subscriber Subscriber, conn net.PacketConn, lastWriter LastWriter) *Service {
  49. // Construct the client to use the stun conn
  50. client := stun.NewClientWithConnection(conn)
  51. client.SetSoftwareName("") // Explicitly unset this, seems to freak some servers out.
  52. // Return the service and the other conn to the client
  53. name := "Stun@"
  54. if local := conn.LocalAddr(); local != nil {
  55. name += local.Network() + "://" + local.String()
  56. } else {
  57. name += "unknown"
  58. }
  59. s := &Service{
  60. name: name,
  61. cfg: cfg,
  62. subscriber: subscriber,
  63. client: client,
  64. lastWriter: lastWriter,
  65. natType: NATUnknown,
  66. addr: nil,
  67. }
  68. return s
  69. }
  70. func (s *Service) Serve(ctx context.Context) error {
  71. defer func() {
  72. s.setNATType(NATUnknown)
  73. s.setExternalAddress(nil, "")
  74. }()
  75. timer := time.NewTimer(time.Millisecond)
  76. for {
  77. disabled:
  78. select {
  79. case <-ctx.Done():
  80. return ctx.Err()
  81. case <-timer.C:
  82. }
  83. if s.cfg.Options().IsStunDisabled() {
  84. timer.Reset(time.Second)
  85. continue
  86. }
  87. l.Debugf("Starting stun for %s", s)
  88. for _, addr := range s.cfg.Options().StunServers() {
  89. // This blocks until we hit an exit condition or there are issues with the STUN server.
  90. // This returns a boolean signifying if a different STUN server should be tried (oppose to the whole thing
  91. // shutting down and this winding itself down.
  92. s.runStunForServer(ctx, addr)
  93. // Have we been asked to stop?
  94. select {
  95. case <-ctx.Done():
  96. return ctx.Err()
  97. default:
  98. }
  99. // Are we disabled?
  100. if s.cfg.Options().IsStunDisabled() {
  101. l.Infoln("STUN disabled")
  102. s.setNATType(NATUnknown)
  103. s.setExternalAddress(nil, "")
  104. goto disabled
  105. }
  106. // Unpunchable NAT? Chillout for some time.
  107. if !s.isCurrentNATTypePunchable() {
  108. break
  109. }
  110. }
  111. // We failed to contact all provided stun servers or the nat is not punchable.
  112. // Chillout for a while.
  113. timer.Reset(stunRetryInterval)
  114. }
  115. }
  116. func (s *Service) runStunForServer(ctx context.Context, addr string) {
  117. l.Debugf("Running stun for %s via %s", s, addr)
  118. // Resolve the address, so that in case the server advertises two
  119. // IPs, we always hit the same one, as otherwise, the mapping might
  120. // expire as we hit the other address, and cause us to flip flop
  121. // between servers/external addresses, as a result flooding discovery
  122. // servers.
  123. udpAddr, err := net.ResolveUDPAddr("udp", addr)
  124. if err != nil {
  125. l.Debugf("%s stun addr resolution on %s: %s", s, addr, err)
  126. return
  127. }
  128. s.client.SetServerAddr(udpAddr.String())
  129. var natType stun.NATType
  130. var extAddr *stun.Host
  131. err = svcutil.CallWithContext(ctx, func() error {
  132. natType, extAddr, err = s.client.Discover()
  133. return err
  134. })
  135. if err != nil || extAddr == nil {
  136. l.Debugf("%s stun discovery on %s: %s", s, addr, err)
  137. return
  138. }
  139. // The stun server is most likely borked, try another one.
  140. if natType == NATError || natType == NATUnknown || natType == NATBlocked {
  141. l.Debugf("%s stun discovery on %s resolved to %s", s, addr, natType)
  142. return
  143. }
  144. s.setNATType(natType)
  145. l.Debugf("%s detected NAT type: %s via %s", s, natType, addr)
  146. // We can't punch through this one, so no point doing keepalives
  147. // and such, just let the caller check the nat type and work it out themselves.
  148. if !s.isCurrentNATTypePunchable() {
  149. l.Debugf("%s cannot punch %s, skipping", s, natType)
  150. return
  151. }
  152. s.setExternalAddress(extAddr, addr)
  153. s.stunKeepAlive(ctx, addr, extAddr)
  154. }
  155. func (s *Service) stunKeepAlive(ctx context.Context, addr string, extAddr *Host) {
  156. var err error
  157. nextSleep := time.Duration(s.cfg.Options().StunKeepaliveStartS) * time.Second
  158. l.Debugf("%s starting stun keepalive via %s, next sleep %s", s, addr, nextSleep)
  159. var ourLastWrite time.Time
  160. for {
  161. if areDifferent(s.addr, extAddr) {
  162. // If the port has changed (addresses are not equal but the hosts are equal),
  163. // we're probably spending too much time between keepalives, reduce the sleep.
  164. if s.addr != nil && extAddr != nil && s.addr.IP() == extAddr.IP() {
  165. nextSleep /= 2
  166. l.Debugf("%s stun port change (%s to %s), next sleep %s", s, s.addr.TransportAddr(), extAddr.TransportAddr(), nextSleep)
  167. }
  168. s.setExternalAddress(extAddr, addr)
  169. // The stun server is probably stuffed, we've gone beyond min timeout, yet the address keeps changing.
  170. minSleep := time.Duration(s.cfg.Options().StunKeepaliveMinS) * time.Second
  171. if nextSleep < minSleep {
  172. l.Debugf("%s keepalive aborting, sleep below min: %s < %s", s, nextSleep, minSleep)
  173. return
  174. }
  175. }
  176. // Adjust the keepalives to fire only nextSleep after last write.
  177. lastWrite := ourLastWrite
  178. if quicLastWrite := s.lastWriter.LastWrite(); quicLastWrite.After(lastWrite) {
  179. lastWrite = quicLastWrite
  180. }
  181. minSleep := time.Duration(s.cfg.Options().StunKeepaliveMinS) * time.Second
  182. if nextSleep < minSleep {
  183. nextSleep = minSleep
  184. }
  185. tryLater:
  186. sleepFor := nextSleep
  187. timeUntilNextKeepalive := time.Until(lastWrite.Add(sleepFor))
  188. if timeUntilNextKeepalive > 0 {
  189. sleepFor = timeUntilNextKeepalive
  190. }
  191. l.Debugf("%s stun sleeping for %s", s, sleepFor)
  192. select {
  193. case <-time.After(sleepFor):
  194. case <-ctx.Done():
  195. l.Debugf("%s stopping, aborting stun", s)
  196. return
  197. }
  198. if s.cfg.Options().IsStunDisabled() {
  199. // Disabled, give up
  200. l.Debugf("%s disabled, aborting stun ", s)
  201. return
  202. }
  203. // Check if any writes happened while we were sleeping, if they did, sleep again
  204. lastWrite = s.lastWriter.LastWrite()
  205. if gap := time.Since(lastWrite); gap < nextSleep {
  206. l.Debugf("%s stun last write gap less than next sleep: %s < %s. Will try later", s, gap, nextSleep)
  207. goto tryLater
  208. }
  209. l.Debugf("%s stun keepalive", s)
  210. extAddr, err = s.client.Keepalive()
  211. if err != nil {
  212. l.Debugf("%s stun keepalive on %s: %s (%v)", s, addr, err, extAddr)
  213. return
  214. }
  215. ourLastWrite = time.Now()
  216. }
  217. }
  218. func (s *Service) setNATType(natType NATType) {
  219. if natType != s.natType {
  220. l.Debugf("Notifying %s of NAT type change: %s", s.subscriber, natType)
  221. s.subscriber.OnNATTypeChanged(natType)
  222. }
  223. s.natType = natType
  224. }
  225. func (s *Service) setExternalAddress(addr *Host, via string) {
  226. if areDifferent(s.addr, addr) {
  227. l.Debugf("Notifying %s of address change: %s via %s", s.subscriber, addr, via)
  228. s.subscriber.OnExternalAddressChanged(addr, via)
  229. }
  230. s.addr = addr
  231. }
  232. func (s *Service) String() string {
  233. return s.name
  234. }
  235. func (s *Service) isCurrentNATTypePunchable() bool {
  236. return s.natType == NATNone || s.natType == NATPortRestricted || s.natType == NATRestricted || s.natType == NATFull || s.natType == NATSymmetricUDPFirewall
  237. }
  238. func areDifferent(first, second *Host) bool {
  239. if (first == nil) != (second == nil) {
  240. return true
  241. }
  242. if first != nil {
  243. return first.TransportAddr() != second.TransportAddr()
  244. }
  245. return false
  246. }