UDPConn.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. /*
  2. * Copyright (c) 2018, Psiphon Inc.
  3. * All rights reserved.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. package psiphon
  20. import (
  21. "context"
  22. "net"
  23. "strconv"
  24. "syscall"
  25. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
  26. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
  27. )
  28. // NewUDPConn resolves raddr and configures a new *net.UDPConn. The UDP socket
  29. // is created using options in DialConfig, including DeviceBinder. The
  30. // returned UDPAddr uses DialConfig options IPv6Synthesizer and
  31. // ResolvedIPCallback.
  32. //
  33. // The network input may be "udp", "udp4", or "udp6". When "udp4" or "udp6" is
  34. // specified, the raddr host IP address or resolved domain addresses must
  35. // include IP address of the corresponding type.
  36. //
  37. // If laddr is specified, the UDP socket is bound to that local address. Any
  38. // laddr host must be an IP address.
  39. //
  40. // If useDial is specified, the UDP socket is "connected" to the raddr remote
  41. // address; otherwise the UDP socket is "unconnected", and each write
  42. // (WriteTo) can specify an arbitrary remote address.
  43. //
  44. // The caller should wrap the returned conn with common.WriteTimeoutUDPConn,
  45. // as appropriate.
  46. //
  47. // The returned conn is not a common.Closer; the caller is expected to wrap
  48. // this conn with another higher-level conn that provides that interface.
  49. func NewUDPConn(
  50. ctx context.Context,
  51. network string,
  52. dial bool,
  53. laddr string,
  54. raddr string,
  55. config *DialConfig) (*net.UDPConn, *net.UDPAddr, error) {
  56. switch network {
  57. case "udp", "udp4", "udp6":
  58. default:
  59. return nil, nil, errors.TraceNew("invalid network")
  60. }
  61. if laddr != "" {
  62. localHost, _, err := net.SplitHostPort(laddr)
  63. if err != nil {
  64. return nil, nil, errors.Trace(err)
  65. }
  66. if net.ParseIP(localHost) == nil {
  67. return nil, nil, errors.TraceNew("invalid local address")
  68. }
  69. }
  70. host, strPort, err := net.SplitHostPort(raddr)
  71. if err != nil {
  72. return nil, nil, errors.Trace(err)
  73. }
  74. port, err := strconv.Atoi(strPort)
  75. if err != nil {
  76. return nil, nil, errors.Trace(err)
  77. }
  78. if port <= 0 || port >= 65536 {
  79. return nil, nil, errors.Tracef("invalid destination port: %d", port)
  80. }
  81. if config.ResolveIP == nil {
  82. // Fail even if we don't need a resolver for this dial: this is a code
  83. // misconfiguration.
  84. return nil, nil, errors.TraceNew("missing resolver")
  85. }
  86. ipAddrs, err := config.ResolveIP(ctx, host)
  87. if err != nil {
  88. return nil, nil, errors.Trace(err)
  89. }
  90. if len(ipAddrs) < 1 {
  91. return nil, nil, errors.TraceNew("no IP address")
  92. }
  93. var ipAddr net.IP
  94. if network == "udp" {
  95. // Pick any IP address, IPv4 or IPv6.
  96. ipAddr = ipAddrs[prng.Intn(len(ipAddrs))]
  97. network = "udp4"
  98. if ipAddr.To4() == nil {
  99. network = "udp6"
  100. }
  101. } else {
  102. // "udp4" or "udp6" was specified, so pick from either IPv4 or IPv6
  103. // candidates.
  104. prng.Shuffle(len(ipAddrs), func(i, j int) {
  105. ipAddrs[i], ipAddrs[j] = ipAddrs[j], ipAddrs[i]
  106. })
  107. for _, nextIPAddr := range ipAddrs {
  108. if (network == "udp6") == (nextIPAddr.To4() == nil) {
  109. ipAddr = nextIPAddr
  110. break
  111. }
  112. }
  113. if ipAddr == nil {
  114. return nil, nil, errors.TraceNew("no IP address for network")
  115. }
  116. }
  117. // When configured, attempt to synthesize IPv6 addresses from
  118. // an IPv4 addresses for compatibility on DNS64/NAT64 networks.
  119. // If synthesize fails, try the original addresses.
  120. if config.IPv6Synthesizer != nil {
  121. if ipAddr.To4() != nil {
  122. synthesizedIPAddress := config.IPv6Synthesizer.IPv6Synthesize(ipAddr.String())
  123. if synthesizedIPAddress != "" {
  124. synthesizedAddr := net.ParseIP(synthesizedIPAddress)
  125. if synthesizedAddr != nil {
  126. ipAddr = synthesizedAddr
  127. }
  128. }
  129. }
  130. }
  131. controlFunc := func(_, _ string, c syscall.RawConn) error {
  132. var controlErr error
  133. err := c.Control(func(fd uintptr) {
  134. socketFD := int(fd)
  135. setAdditionalSocketOptions(socketFD)
  136. if config.DeviceBinder != nil {
  137. _, err := config.DeviceBinder.BindToDevice(socketFD)
  138. if err != nil {
  139. controlErr = errors.Tracef("BindToDevice failed: %s", err)
  140. return
  141. }
  142. }
  143. })
  144. if controlErr != nil {
  145. return errors.Trace(controlErr)
  146. }
  147. return errors.Trace(err)
  148. }
  149. var udpConn *net.UDPConn
  150. if dial {
  151. // Create a "connected" UDP socket, which is associated with a fixed
  152. // remote address. Writes will always send to that address.
  153. //
  154. // This mode is required for the Conjure DTLS transport.
  155. //
  156. // Unlike non-dial mode, the UDP socket doesn't listen on all local
  157. // IPs, and LocalAddr will return an address with a specific IP address.
  158. var addr net.Addr
  159. if laddr != "" {
  160. // ResolveUDPAddr won't resolve a domain here -- there's a check
  161. // above that the host in laddr must be an IP address.
  162. addr, err = net.ResolveUDPAddr(network, laddr)
  163. if err != nil {
  164. return nil, nil, errors.Trace(err)
  165. }
  166. }
  167. dialer := &net.Dialer{
  168. Control: controlFunc,
  169. LocalAddr: addr,
  170. }
  171. conn, err := dialer.DialContext(
  172. ctx, network, net.JoinHostPort(ipAddr.String(), strPort))
  173. if err != nil {
  174. return nil, nil, errors.Trace(err)
  175. }
  176. var ok bool
  177. udpConn, ok = conn.(*net.UDPConn)
  178. if !ok {
  179. return nil, nil, errors.Tracef("unexpected conn type: %T", conn)
  180. }
  181. } else {
  182. // Create an "unconnected" UDP socket, which can be used with WriteTo,
  183. // which specifies a remote address per write.
  184. //
  185. // This mode is required by quic-go.
  186. //
  187. // As documented in net.ListenUDP: with an unspecified IP address, the
  188. // resulting conn "listens on all available IP addresses of the local
  189. // system except multicast IP addresses".
  190. //
  191. // Limitation: these UDP sockets are not necessarily closed when a device
  192. // changes active network (e.g., WiFi to mobile). It's possible that a
  193. // QUIC connection does not immediately close on a network change, and
  194. // instead outbound packets are sent from a different active interface.
  195. // As quic-go does not yet support connection migration, these packets
  196. // will be dropped by the server. This situation is mitigated by use of
  197. // DeviceBinder; by network change event detection, which initiates new
  198. // tunnel connections; and by timeouts/keep-alives.
  199. listen := &net.ListenConfig{
  200. Control: controlFunc,
  201. }
  202. conn, err := listen.ListenPacket(ctx, network, laddr)
  203. if err != nil {
  204. return nil, nil, errors.Trace(err)
  205. }
  206. var ok bool
  207. udpConn, ok = conn.(*net.UDPConn)
  208. if !ok {
  209. return nil, nil, errors.Tracef("unexpected conn type: %T", conn)
  210. }
  211. }
  212. if config.ResolvedIPCallback != nil {
  213. config.ResolvedIPCallback(ipAddr.String())
  214. }
  215. return udpConn, &net.UDPAddr{IP: ipAddr, Port: port}, nil
  216. }