stun.go 8.8 KB

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