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. // Don't shuffle or otherwise mutate the slice returned by ResolveIP.
  105. permutedIndexes := prng.Perm(len(ipAddrs))
  106. for _, i := range permutedIndexes {
  107. if (network == "udp6") == (ipAddrs[i].To4() == nil) {
  108. ipAddr = ipAddrs[i]
  109. break
  110. }
  111. }
  112. if ipAddr == nil {
  113. return nil, nil, errors.TraceNew("no IP address for network")
  114. }
  115. }
  116. // When configured, attempt to synthesize IPv6 addresses from
  117. // an IPv4 addresses for compatibility on DNS64/NAT64 networks.
  118. // If synthesize fails, try the original addresses.
  119. if config.IPv6Synthesizer != nil {
  120. if ipAddr.To4() != nil {
  121. synthesizedIPAddress := config.IPv6Synthesizer.IPv6Synthesize(ipAddr.String())
  122. if synthesizedIPAddress != "" {
  123. synthesizedAddr := net.ParseIP(synthesizedIPAddress)
  124. if synthesizedAddr != nil {
  125. ipAddr = synthesizedAddr
  126. }
  127. }
  128. }
  129. }
  130. controlFunc := func(_, _ string, c syscall.RawConn) error {
  131. var controlErr error
  132. err := c.Control(func(fd uintptr) {
  133. socketFD := int(fd)
  134. setAdditionalSocketOptions(socketFD)
  135. if config.DeviceBinder != nil {
  136. _, err := config.DeviceBinder.BindToDevice(socketFD)
  137. if err != nil {
  138. controlErr = errors.Tracef("BindToDevice failed: %s", err)
  139. return
  140. }
  141. }
  142. })
  143. if controlErr != nil {
  144. return errors.Trace(controlErr)
  145. }
  146. return errors.Trace(err)
  147. }
  148. var udpConn *net.UDPConn
  149. if dial {
  150. // Create a "connected" UDP socket, which is associated with a fixed
  151. // remote address. Writes will always send to that address.
  152. //
  153. // This mode is required for the Conjure DTLS transport.
  154. //
  155. // Unlike non-dial mode, the UDP socket doesn't listen on all local
  156. // IPs, and LocalAddr will return an address with a specific IP address.
  157. var addr net.Addr
  158. if laddr != "" {
  159. // ResolveUDPAddr won't resolve a domain here -- there's a check
  160. // above that the host in laddr must be an IP address.
  161. addr, err = net.ResolveUDPAddr(network, laddr)
  162. if err != nil {
  163. return nil, nil, errors.Trace(err)
  164. }
  165. }
  166. dialer := &net.Dialer{
  167. Control: controlFunc,
  168. LocalAddr: addr,
  169. }
  170. conn, err := dialer.DialContext(
  171. ctx, network, net.JoinHostPort(ipAddr.String(), strPort))
  172. if err != nil {
  173. return nil, nil, errors.Trace(err)
  174. }
  175. var ok bool
  176. udpConn, ok = conn.(*net.UDPConn)
  177. if !ok {
  178. return nil, nil, errors.Tracef("unexpected conn type: %T", conn)
  179. }
  180. } else {
  181. // Create an "unconnected" UDP socket, which can be used with WriteTo,
  182. // which specifies a remote address per write.
  183. //
  184. // This mode is required by quic-go.
  185. //
  186. // As documented in net.ListenUDP: with an unspecified IP address, the
  187. // resulting conn "listens on all available IP addresses of the local
  188. // system except multicast IP addresses".
  189. //
  190. // Limitation: these UDP sockets are not necessarily closed when a device
  191. // changes active network (e.g., WiFi to mobile). It's possible that a
  192. // QUIC connection does not immediately close on a network change, and
  193. // instead outbound packets are sent from a different active interface.
  194. // As quic-go does not yet support connection migration, these packets
  195. // will be dropped by the server. This situation is mitigated by use of
  196. // DeviceBinder; by network change event detection, which initiates new
  197. // tunnel connections; and by timeouts/keep-alives.
  198. listen := &net.ListenConfig{
  199. Control: controlFunc,
  200. }
  201. conn, err := listen.ListenPacket(ctx, network, laddr)
  202. if err != nil {
  203. return nil, nil, errors.Trace(err)
  204. }
  205. var ok bool
  206. udpConn, ok = conn.(*net.UDPConn)
  207. if !ok {
  208. return nil, nil, errors.Tracef("unexpected conn type: %T", conn)
  209. }
  210. }
  211. if config.ResolvedIPCallback != nil {
  212. config.ResolvedIPCallback(ipAddr.String())
  213. }
  214. return udpConn, &net.UDPAddr{IP: ipAddr, Port: port}, nil
  215. }