UDPConn.go 7.1 KB

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