TCPConn_bind.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. // +build !windows
  2. /*
  3. * Copyright (c) 2015, Psiphon Inc.
  4. * All rights reserved.
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. *
  19. */
  20. package psiphon
  21. import (
  22. "errors"
  23. "fmt"
  24. "math/rand"
  25. "net"
  26. "os"
  27. "strconv"
  28. "syscall"
  29. "github.com/Psiphon-Inc/goarista/monotime"
  30. "github.com/Psiphon-Inc/goselect"
  31. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
  32. )
  33. // tcpDial is the platform-specific part of interruptibleTCPDial
  34. //
  35. // To implement socket device binding, the lower-level syscall APIs are used.
  36. // The sequence of syscalls in this implementation are taken from:
  37. // https://github.com/golang/go/issues/6966
  38. // (originally: https://code.google.com/p/go/issues/detail?id=6966)
  39. func tcpDial(addr string, config *DialConfig) (net.Conn, error) {
  40. // Get the remote IP and port, resolving a domain name if necessary
  41. host, strPort, err := net.SplitHostPort(addr)
  42. if err != nil {
  43. return nil, common.ContextError(err)
  44. }
  45. port, err := strconv.Atoi(strPort)
  46. if err != nil {
  47. return nil, common.ContextError(err)
  48. }
  49. ipAddrs, err := LookupIP(host, config)
  50. if err != nil {
  51. return nil, common.ContextError(err)
  52. }
  53. if len(ipAddrs) < 1 {
  54. return nil, common.ContextError(errors.New("no IP address"))
  55. }
  56. // Iterate over a pseudorandom permutation of the destination
  57. // IPs and attempt connections.
  58. //
  59. // Only continue retrying as long as the initial ConnectTimeout
  60. // has not expired. Unlike net.Dial, we do not fractionalize the
  61. // timeout, as the ConnectTimeout is generally intended to apply
  62. // to a single attempt. So these serial retries are most useful
  63. // in cases of immediate failure, such as "no route to host"
  64. // errors when a host resolves to both IPv4 and IPv6 but IPv6
  65. // addresses are unreachable.
  66. // Retries at higher levels cover other cases: e.g.,
  67. // Controller.remoteServerListFetcher will retry its entire
  68. // operation and tcpDial will try a new permutation; or similarly,
  69. // Controller.establishCandidateGenerator will retry a candidate
  70. // tunnel server dials.
  71. permutedIndexes := rand.Perm(len(ipAddrs))
  72. lastErr := errors.New("unknown error")
  73. var deadline monotime.Time
  74. if config.ConnectTimeout != 0 {
  75. deadline = monotime.Now().Add(config.ConnectTimeout)
  76. }
  77. for iteration, index := range permutedIndexes {
  78. if iteration > 0 && deadline != 0 && monotime.Now().After(deadline) {
  79. // lastErr should be set by the previous iteration
  80. break
  81. }
  82. // Get address type (IPv4 or IPv6)
  83. var ipv4 [4]byte
  84. var ipv6 [16]byte
  85. var domain int
  86. var sockAddr syscall.Sockaddr
  87. ipAddr := ipAddrs[index]
  88. if ipAddr != nil && ipAddr.To4() != nil {
  89. copy(ipv4[:], ipAddr.To4())
  90. domain = syscall.AF_INET
  91. } else if ipAddr != nil && ipAddr.To16() != nil {
  92. copy(ipv6[:], ipAddr.To16())
  93. domain = syscall.AF_INET6
  94. } else {
  95. lastErr = common.ContextError(fmt.Errorf("Got invalid IP address: %s", ipAddr.String()))
  96. continue
  97. }
  98. if domain == syscall.AF_INET {
  99. sockAddr = &syscall.SockaddrInet4{Addr: ipv4, Port: port}
  100. } else if domain == syscall.AF_INET6 {
  101. sockAddr = &syscall.SockaddrInet6{Addr: ipv6, Port: port}
  102. }
  103. // Create a socket and bind to device, when configured to do so
  104. socketFd, err := syscall.Socket(domain, syscall.SOCK_STREAM, 0)
  105. if err != nil {
  106. lastErr = common.ContextError(err)
  107. continue
  108. }
  109. if config.DeviceBinder != nil {
  110. // WARNING: this potentially violates the direction to not call into
  111. // external components after the Controller may have been stopped.
  112. // TODO: rework DeviceBinder as an internal 'service' which can trap
  113. // external calls when they should not be made?
  114. err = config.DeviceBinder.BindToDevice(socketFd)
  115. if err != nil {
  116. syscall.Close(socketFd)
  117. lastErr = common.ContextError(fmt.Errorf("BindToDevice failed: %s", err))
  118. continue
  119. }
  120. }
  121. // Connect socket to the server's IP address
  122. err = syscall.SetNonblock(socketFd, true)
  123. if err != nil {
  124. syscall.Close(socketFd)
  125. lastErr = common.ContextError(err)
  126. continue
  127. }
  128. err = syscall.Connect(socketFd, sockAddr)
  129. if err != nil {
  130. if errno, ok := err.(syscall.Errno); !ok || errno != syscall.EINPROGRESS {
  131. syscall.Close(socketFd)
  132. lastErr = common.ContextError(err)
  133. continue
  134. }
  135. }
  136. fdset := &goselect.FDSet{}
  137. fdset.Set(uintptr(socketFd))
  138. timeout := config.ConnectTimeout
  139. if config.ConnectTimeout == 0 {
  140. timeout = -1
  141. }
  142. err = goselect.Select(socketFd+1, nil, fdset, nil, timeout)
  143. if err != nil {
  144. lastErr = common.ContextError(err)
  145. continue
  146. }
  147. if !fdset.IsSet(uintptr(socketFd)) {
  148. lastErr = common.ContextError(errors.New("file descriptor not set"))
  149. continue
  150. }
  151. err = syscall.SetNonblock(socketFd, false)
  152. if err != nil {
  153. syscall.Close(socketFd)
  154. lastErr = common.ContextError(err)
  155. continue
  156. }
  157. // Convert the socket fd to a net.Conn
  158. file := os.NewFile(uintptr(socketFd), "")
  159. netConn, err := net.FileConn(file) // net.FileConn() dups socketFd
  160. file.Close() // file.Close() closes socketFd
  161. if err != nil {
  162. lastErr = common.ContextError(err)
  163. continue
  164. }
  165. return netConn, nil
  166. }
  167. return nil, lastErr
  168. }