public.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  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 https://mozilla.org/MPL/2.0/.
  6. package dialer
  7. import (
  8. "context"
  9. "errors"
  10. "fmt"
  11. "net"
  12. "time"
  13. "github.com/syncthing/syncthing/lib/connections/registry"
  14. "golang.org/x/net/ipv4"
  15. "golang.org/x/net/ipv6"
  16. "golang.org/x/net/proxy"
  17. )
  18. var errUnexpectedInterfaceType = errors.New("unexpected interface type")
  19. // SetTCPOptions sets our default TCP options on a TCP connection, possibly
  20. // digging through dialerConn to extract the *net.TCPConn
  21. func SetTCPOptions(conn net.Conn) error {
  22. switch conn := conn.(type) {
  23. case dialerConn:
  24. return SetTCPOptions(conn.Conn)
  25. case *net.TCPConn:
  26. var err error
  27. if err = conn.SetLinger(0); err != nil {
  28. return err
  29. }
  30. if err = conn.SetNoDelay(false); err != nil {
  31. return err
  32. }
  33. if err = conn.SetKeepAlivePeriod(60 * time.Second); err != nil {
  34. return err
  35. }
  36. if err = conn.SetKeepAlive(true); err != nil {
  37. return err
  38. }
  39. return nil
  40. default:
  41. return fmt.Errorf("unknown connection type %T", conn)
  42. }
  43. }
  44. func SetTrafficClass(conn net.Conn, class int) error {
  45. switch conn := conn.(type) {
  46. case dialerConn:
  47. return SetTrafficClass(conn.Conn, class)
  48. case *net.TCPConn:
  49. e1 := ipv4.NewConn(conn).SetTOS(class)
  50. e2 := ipv6.NewConn(conn).SetTrafficClass(class)
  51. if e1 != nil {
  52. return e1
  53. }
  54. return e2
  55. default:
  56. return fmt.Errorf("unknown connection type %T", conn)
  57. }
  58. }
  59. func dialContextWithFallback(ctx context.Context, fallback proxy.ContextDialer, network, addr string) (net.Conn, error) {
  60. dialer, ok := proxy.FromEnvironment().(proxy.ContextDialer)
  61. if !ok {
  62. return nil, errUnexpectedInterfaceType
  63. }
  64. if dialer == proxy.Direct {
  65. conn, err := fallback.DialContext(ctx, network, addr)
  66. l.Debugf("Dialing direct result %s %s: %v %v", network, addr, conn, err)
  67. return conn, err
  68. }
  69. if noFallback {
  70. conn, err := dialer.DialContext(ctx, network, addr)
  71. l.Debugf("Dialing no fallback result %s %s: %v %v", network, addr, conn, err)
  72. if err != nil {
  73. return nil, err
  74. }
  75. return dialerConn{conn, newDialerAddr(network, addr)}, nil
  76. }
  77. proxyDialFudgeAddress := func(ctx context.Context, network, address string) (net.Conn, error) {
  78. conn, err := dialer.DialContext(ctx, network, addr)
  79. if err != nil {
  80. return nil, err
  81. }
  82. return dialerConn{conn, newDialerAddr(network, addr)}, err
  83. }
  84. return dialTwicePreferFirst(ctx, proxyDialFudgeAddress, fallback.DialContext, "proxy", "fallback", network, addr)
  85. }
  86. // DialContext dials via context and/or directly, depending on how it is configured.
  87. // If dialing via proxy and allowing fallback, dialing for both happens simultaneously
  88. // and the proxy connection is returned if successful.
  89. func DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
  90. return dialContextWithFallback(ctx, proxy.Direct, network, addr)
  91. }
  92. // DialContextReusePort tries dialing via proxy if a proxy is configured, and falls back to
  93. // a direct connection reusing the port from the connections registry, if no proxy is defined, or connecting via proxy
  94. // fails. It also in parallel dials without reusing the port, just in case reusing the port affects routing decisions badly.
  95. func DialContextReusePort(ctx context.Context, network, addr string) (net.Conn, error) {
  96. // If proxy is configured, there is no point trying to reuse listen addresses.
  97. if proxy.FromEnvironment() != proxy.Direct {
  98. return DialContext(ctx, network, addr)
  99. }
  100. localAddrInterface := registry.Get(network, tcpAddrLess)
  101. if localAddrInterface == nil {
  102. // Nothing listening, nothing to reuse.
  103. return DialContext(ctx, network, addr)
  104. }
  105. laddr, ok := localAddrInterface.(*net.TCPAddr)
  106. if !ok {
  107. return nil, errUnexpectedInterfaceType
  108. }
  109. // Dial twice, once reusing the listen address, another time not reusing it, just in case reusing the address
  110. // influences routing and we fail to reach our destination.
  111. dialer := net.Dialer{
  112. Control: ReusePortControl,
  113. LocalAddr: laddr,
  114. }
  115. return dialTwicePreferFirst(ctx, dialer.DialContext, (&net.Dialer{}).DialContext, "reuse", "non-reuse", network, addr)
  116. }
  117. type dialFunc func(ctx context.Context, network, address string) (net.Conn, error)
  118. func dialTwicePreferFirst(ctx context.Context, first, second dialFunc, firstName, secondName, network, address string) (net.Conn, error) {
  119. // Delay second dial by some time.
  120. sleep := time.Second
  121. if deadline, ok := ctx.Deadline(); ok {
  122. timeout := time.Until(deadline)
  123. if timeout > 0 {
  124. sleep = timeout / 3
  125. }
  126. }
  127. ctx, cancel := context.WithCancel(ctx)
  128. defer cancel()
  129. var firstConn, secondConn net.Conn
  130. var firstErr, secondErr error
  131. firstDone := make(chan struct{})
  132. secondDone := make(chan struct{})
  133. go func() {
  134. firstConn, firstErr = first(ctx, network, address)
  135. l.Debugf("Dialing %s result %s %s: %v %v", firstName, network, address, firstConn, firstErr)
  136. close(firstDone)
  137. }()
  138. go func() {
  139. select {
  140. case <-firstDone:
  141. if firstErr == nil {
  142. // First succeeded, no point doing anything in second
  143. secondErr = errors.New("didn't dial")
  144. close(secondDone)
  145. return
  146. }
  147. case <-ctx.Done():
  148. secondErr = ctx.Err()
  149. close(secondDone)
  150. return
  151. case <-time.After(sleep):
  152. }
  153. secondConn, secondErr = second(ctx, network, address)
  154. l.Debugf("Dialing %s result %s %s: %v %v", secondName, network, address, secondConn, secondErr)
  155. close(secondDone)
  156. }()
  157. <-firstDone
  158. if firstErr == nil {
  159. go func() {
  160. <-secondDone
  161. if secondConn != nil {
  162. _ = secondConn.Close()
  163. }
  164. }()
  165. return firstConn, firstErr
  166. }
  167. <-secondDone
  168. return secondConn, secondErr
  169. }