public.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  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. ctx, cancel := context.WithCancel(ctx)
  78. defer cancel()
  79. var proxyConn, fallbackConn net.Conn
  80. var proxyErr, fallbackErr error
  81. proxyDone := make(chan struct{})
  82. fallbackDone := make(chan struct{})
  83. go func() {
  84. proxyConn, proxyErr = dialer.DialContext(ctx, network, addr)
  85. l.Debugf("Dialing proxy result %s %s: %v %v", network, addr, proxyConn, proxyErr)
  86. if proxyErr == nil {
  87. proxyConn = dialerConn{proxyConn, newDialerAddr(network, addr)}
  88. }
  89. close(proxyDone)
  90. }()
  91. go func() {
  92. fallbackConn, fallbackErr = fallback.DialContext(ctx, network, addr)
  93. l.Debugf("Dialing fallback result %s %s: %v %v", network, addr, fallbackConn, fallbackErr)
  94. close(fallbackDone)
  95. }()
  96. <-proxyDone
  97. if proxyErr == nil {
  98. go func() {
  99. <-fallbackDone
  100. if fallbackErr == nil {
  101. _ = fallbackConn.Close()
  102. }
  103. }()
  104. return proxyConn, nil
  105. }
  106. <-fallbackDone
  107. return fallbackConn, fallbackErr
  108. }
  109. // DialContext dials via context and/or directly, depending on how it is configured.
  110. // If dialing via proxy and allowing fallback, dialing for both happens simultaneously
  111. // and the proxy connection is returned if successful.
  112. func DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
  113. return dialContextWithFallback(ctx, proxy.Direct, network, addr)
  114. }
  115. // DialContextReusePort tries dialing via proxy if a proxy is configured, and falls back to
  116. // a direct connection reusing the port from the connections registry, if no proxy is defined, or connecting via proxy
  117. // fails. If the context has a timeout, the timeout might be applied twice.
  118. func DialContextReusePort(ctx context.Context, network, addr string) (net.Conn, error) {
  119. dialer := &net.Dialer{
  120. Control: ReusePortControl,
  121. }
  122. localAddrInterface := registry.Get(network, tcpAddrLess)
  123. if localAddrInterface != nil {
  124. if addr, ok := localAddrInterface.(*net.TCPAddr); !ok {
  125. return nil, errUnexpectedInterfaceType
  126. } else {
  127. dialer.LocalAddr = addr
  128. }
  129. }
  130. return dialContextWithFallback(ctx, dialer, network, addr)
  131. }