public.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  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 *net.TCPConn:
  23. var err error
  24. if err = conn.SetLinger(0); err != nil {
  25. return err
  26. }
  27. if err = conn.SetNoDelay(false); err != nil {
  28. return err
  29. }
  30. if err = conn.SetKeepAlivePeriod(60 * time.Second); err != nil {
  31. return err
  32. }
  33. if err = conn.SetKeepAlive(true); err != nil {
  34. return err
  35. }
  36. return nil
  37. default:
  38. return fmt.Errorf("unknown connection type %T", conn)
  39. }
  40. }
  41. func SetTrafficClass(conn net.Conn, class int) error {
  42. switch conn := conn.(type) {
  43. case *net.TCPConn:
  44. e1 := ipv4.NewConn(conn).SetTOS(class)
  45. e2 := ipv6.NewConn(conn).SetTrafficClass(class)
  46. if e1 != nil {
  47. return e1
  48. }
  49. return e2
  50. default:
  51. return fmt.Errorf("unknown connection type %T", conn)
  52. }
  53. }
  54. func dialContextWithFallback(ctx context.Context, fallback proxy.ContextDialer, network, addr string) (net.Conn, error) {
  55. dialer, ok := proxy.FromEnvironment().(proxy.ContextDialer)
  56. if !ok {
  57. return nil, errUnexpectedInterfaceType
  58. }
  59. if dialer == proxy.Direct {
  60. return fallback.DialContext(ctx, network, addr)
  61. }
  62. if noFallback {
  63. return dialer.DialContext(ctx, network, addr)
  64. }
  65. ctx, cancel := context.WithCancel(ctx)
  66. defer cancel()
  67. var proxyConn, fallbackConn net.Conn
  68. var proxyErr, fallbackErr error
  69. proxyDone := make(chan struct{})
  70. fallbackDone := make(chan struct{})
  71. go func() {
  72. proxyConn, proxyErr = dialer.DialContext(ctx, network, addr)
  73. close(proxyDone)
  74. }()
  75. go func() {
  76. fallbackConn, fallbackErr = fallback.DialContext(ctx, network, addr)
  77. close(fallbackDone)
  78. }()
  79. <-proxyDone
  80. if proxyErr == nil {
  81. go func() {
  82. <-fallbackDone
  83. if fallbackErr == nil {
  84. fallbackConn.Close()
  85. }
  86. }()
  87. return proxyConn, nil
  88. }
  89. <-fallbackDone
  90. return fallbackConn, fallbackErr
  91. }
  92. // DialContext dials via context and/or directly, depending on how it is configured.
  93. // If dialing via proxy and allowing fallback, dialing for both happens simultaneously
  94. // and the proxy connection is returned if successful.
  95. func DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
  96. return dialContextWithFallback(ctx, proxy.Direct, network, addr)
  97. }