public.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  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. "golang.org/x/net/ipv4"
  14. "golang.org/x/net/ipv6"
  15. "golang.org/x/net/proxy"
  16. )
  17. var errUnexpectedInterfaceType = errors.New("unexpected interface type")
  18. // SetTCPOptions sets our default TCP options on a TCP connection, possibly
  19. // digging through dialerConn to extract the *net.TCPConn
  20. func SetTCPOptions(conn net.Conn) error {
  21. switch conn := conn.(type) {
  22. case dialerConn:
  23. return SetTCPOptions(conn.Conn)
  24. case *net.TCPConn:
  25. var err error
  26. if err = conn.SetLinger(0); err != nil {
  27. return err
  28. }
  29. if err = conn.SetNoDelay(false); err != nil {
  30. return err
  31. }
  32. if err = conn.SetKeepAlivePeriod(60 * time.Second); err != nil {
  33. return err
  34. }
  35. if err = conn.SetKeepAlive(true); err != nil {
  36. return err
  37. }
  38. return nil
  39. default:
  40. return fmt.Errorf("unknown connection type %T", conn)
  41. }
  42. }
  43. func SetTrafficClass(conn net.Conn, class int) error {
  44. switch conn := conn.(type) {
  45. case dialerConn:
  46. return SetTrafficClass(conn.Conn, class)
  47. case *net.TCPConn:
  48. e1 := ipv4.NewConn(conn).SetTOS(class)
  49. e2 := ipv6.NewConn(conn).SetTrafficClass(class)
  50. if e1 != nil {
  51. return e1
  52. }
  53. return e2
  54. default:
  55. return fmt.Errorf("unknown connection type %T", conn)
  56. }
  57. }
  58. func dialContextWithFallback(ctx context.Context, fallback proxy.ContextDialer, network, addr string) (net.Conn, error) {
  59. dialer, ok := proxy.FromEnvironment().(proxy.ContextDialer)
  60. if !ok {
  61. return nil, errUnexpectedInterfaceType
  62. }
  63. if dialer == proxy.Direct {
  64. conn, err := fallback.DialContext(ctx, network, addr)
  65. l.Debugf("Dialing direct result %s %s: %v %v", network, addr, conn, err)
  66. return conn, err
  67. }
  68. if noFallback {
  69. conn, err := dialer.DialContext(ctx, network, addr)
  70. l.Debugf("Dialing no fallback result %s %s: %v %v", network, addr, conn, err)
  71. if err != nil {
  72. return nil, err
  73. }
  74. return dialerConn{conn, newDialerAddr(network, addr)}, nil
  75. }
  76. ctx, cancel := context.WithCancel(ctx)
  77. defer cancel()
  78. var proxyConn, fallbackConn net.Conn
  79. var proxyErr, fallbackErr error
  80. proxyDone := make(chan struct{})
  81. fallbackDone := make(chan struct{})
  82. go func() {
  83. proxyConn, proxyErr = dialer.DialContext(ctx, network, addr)
  84. l.Debugf("Dialing proxy result %s %s: %v %v", network, addr, proxyConn, proxyErr)
  85. if proxyErr == nil {
  86. proxyConn = dialerConn{proxyConn, newDialerAddr(network, addr)}
  87. }
  88. close(proxyDone)
  89. }()
  90. go func() {
  91. fallbackConn, fallbackErr = fallback.DialContext(ctx, network, addr)
  92. l.Debugf("Dialing fallback result %s %s: %v %v", network, addr, fallbackConn, fallbackErr)
  93. close(fallbackDone)
  94. }()
  95. <-proxyDone
  96. if proxyErr == nil {
  97. go func() {
  98. <-fallbackDone
  99. if fallbackErr == nil {
  100. _ = fallbackConn.Close()
  101. }
  102. }()
  103. return proxyConn, nil
  104. }
  105. <-fallbackDone
  106. return fallbackConn, fallbackErr
  107. }
  108. // DialContext dials via context and/or directly, depending on how it is configured.
  109. // If dialing via proxy and allowing fallback, dialing for both happens simultaneously
  110. // and the proxy connection is returned if successful.
  111. func DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
  112. return dialContextWithFallback(ctx, proxy.Direct, network, addr)
  113. }