TCPConn_bind.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. //go:build !windows
  2. // +build !windows
  3. /*
  4. * Copyright (c) 2015, Psiphon Inc.
  5. * All rights reserved.
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation, either version 3 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. *
  20. */
  21. package psiphon
  22. import (
  23. "context"
  24. "fmt"
  25. "math/rand"
  26. "net"
  27. "strconv"
  28. "syscall"
  29. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
  30. )
  31. // tcpDial is the platform-specific part of DialTCP
  32. func tcpDial(ctx context.Context, addr string, config *DialConfig) (net.Conn, error) {
  33. // Get the remote IP and port, resolving a domain name if necessary
  34. host, strPort, err := net.SplitHostPort(addr)
  35. if err != nil {
  36. return nil, errors.Trace(err)
  37. }
  38. port, err := strconv.Atoi(strPort)
  39. if err != nil {
  40. return nil, errors.Trace(err)
  41. }
  42. ipAddrs, err := LookupIP(ctx, host, config)
  43. if err != nil {
  44. return nil, errors.Trace(err)
  45. }
  46. if len(ipAddrs) < 1 {
  47. return nil, errors.TraceNew("no IP address")
  48. }
  49. // When configured, attempt to synthesize IPv6 addresses from
  50. // an IPv4 addresses for compatibility on DNS64/NAT64 networks.
  51. // If synthesize fails, try the original addresses.
  52. if config.IPv6Synthesizer != nil {
  53. for i, ipAddr := range ipAddrs {
  54. if ipAddr.To4() != nil {
  55. synthesizedIPAddress := config.IPv6Synthesizer.IPv6Synthesize(ipAddr.String())
  56. if synthesizedIPAddress != "" {
  57. synthesizedAddr := net.ParseIP(synthesizedIPAddress)
  58. if synthesizedAddr != nil {
  59. ipAddrs[i] = synthesizedAddr
  60. }
  61. }
  62. }
  63. }
  64. }
  65. // Iterate over a pseudorandom permutation of the destination
  66. // IPs and attempt connections.
  67. //
  68. // Only continue retrying as long as the dial context is not
  69. // done. Unlike net.Dial, we do not fractionalize the context
  70. // deadline, as the dial is generally intended to apply to a
  71. // single attempt. So these serial retries are most useful in
  72. // cases of immediate failure, such as "no route to host"
  73. // errors when a host resolves to both IPv4 and IPv6 but IPv6
  74. // addresses are unreachable.
  75. //
  76. // Retries at higher levels cover other cases: e.g.,
  77. // Controller.remoteServerListFetcher will retry its entire
  78. // operation and tcpDial will try a new permutation; or similarly,
  79. // Controller.establishCandidateGenerator will retry a candidate
  80. // tunnel server dials.
  81. permutedIndexes := rand.Perm(len(ipAddrs))
  82. lastErr := errors.TraceNew("unknown error")
  83. for _, index := range permutedIndexes {
  84. dialer := &net.Dialer{
  85. Control: func(_, _ string, c syscall.RawConn) error {
  86. var controlErr error
  87. err := c.Control(func(fd uintptr) {
  88. socketFD := int(fd)
  89. setAdditionalSocketOptions(socketFD)
  90. if config.BPFProgramInstructions != nil {
  91. err := setSocketBPF(config.BPFProgramInstructions, socketFD)
  92. if err != nil {
  93. controlErr = errors.Tracef("setSocketBPF failed: %s", err)
  94. return
  95. }
  96. }
  97. if config.DeviceBinder != nil {
  98. _, err := config.DeviceBinder.BindToDevice(socketFD)
  99. if err != nil {
  100. controlErr = errors.Tracef("BindToDevice failed: %s", err)
  101. return
  102. }
  103. }
  104. })
  105. if controlErr != nil {
  106. return errors.Trace(controlErr)
  107. }
  108. return errors.Trace(err)
  109. },
  110. }
  111. conn, err := dialer.DialContext(
  112. ctx, "tcp", fmt.Sprintf("%s:%d", ipAddrs[index].String(), port))
  113. if err != nil {
  114. lastErr = errors.Trace(err)
  115. continue
  116. }
  117. return &TCPConn{Conn: conn}, nil
  118. }
  119. return nil, lastErr
  120. }