1
0

egress.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  1. package amneziawgnet
  2. import (
  3. "context"
  4. "crypto/hmac"
  5. "encoding/binary"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "net"
  10. "net/netip"
  11. "sync"
  12. "time"
  13. "gvisor.dev/gvisor/pkg/tcpip"
  14. "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
  15. "github.com/mhsanaei/3x-ui/v3/internal/amneziawg"
  16. "github.com/mhsanaei/3x-ui/v3/internal/logger"
  17. )
  18. // EgressBasePort is the loopback port the panel's SOCKS5 egress server tries
  19. // first; generated amneziawg socks bridges dial whichever port it holds.
  20. const EgressBasePort = 64900
  21. // socks5EgressServer is a minimal loopback SOCKS5 server routing Xray's
  22. // bridged amneziawg outbounds into their embedded devices' netstacks.
  23. type socks5EgressServer struct {
  24. mu sync.Mutex
  25. stacks map[string]*Device // outbound tag -> its device
  26. dns map[string]string // outbound tag -> its DNS server
  27. tracked map[net.Conn]struct{}
  28. // dnsServer resolves domain targets through the outbound netstack.
  29. dnsServer string
  30. listener net.Listener // nil when stopped; acceptLoop takes it as an arg
  31. closing chan struct{} // per-listener lifetime signal, rearmed by Listen
  32. wg sync.WaitGroup
  33. }
  34. var (
  35. egressOnce sync.Once
  36. egressServer *socks5EgressServer
  37. )
  38. // GetEgressServer returns the process-wide SOCKS5 egress server singleton.
  39. func GetEgressServer() *socks5EgressServer {
  40. egressOnce.Do(func() {
  41. egressServer = &socks5EgressServer{
  42. stacks: map[string]*Device{},
  43. dns: map[string]string{},
  44. tracked: map[net.Conn]struct{}{},
  45. }
  46. })
  47. return egressServer
  48. }
  49. // currentDNSServer reads dnsServer under lock -- custom per-tag DNS preferred.
  50. func (s *socks5EgressServer) currentDNSServer(tag ...string) string {
  51. s.mu.Lock()
  52. defer s.mu.Unlock()
  53. if len(tag) > 0 && tag[0] != "" {
  54. if custom, ok := s.dns[tag[0]]; ok && custom != "" {
  55. return custom
  56. }
  57. }
  58. return s.dnsServer
  59. }
  60. // SetDNSServer overrides the domain-target resolver (tests).
  61. func (s *socks5EgressServer) SetDNSServer(addr string) {
  62. s.mu.Lock()
  63. defer s.mu.Unlock()
  64. s.dnsServer = addr
  65. }
  66. // SetStack registers or replaces the device backing an outbound tag.
  67. func (s *socks5EgressServer) SetStack(tag string, dev *Device, dnsServer ...string) {
  68. norm := ""
  69. if len(dnsServer) > 0 && dnsServer[0] != "" {
  70. norm = amneziawg.NormalizeDNSServer(dnsServer[0])
  71. }
  72. s.mu.Lock()
  73. prevDev := s.stacks[tag]
  74. prevDNS := s.dns[tag]
  75. s.stacks[tag] = dev
  76. if norm != "" {
  77. s.dns[tag] = norm
  78. } else {
  79. delete(s.dns, tag)
  80. }
  81. changed := prevDev != dev || prevDNS != norm
  82. s.mu.Unlock()
  83. if changed {
  84. flushTunnelDNSCacheForTag(tag)
  85. }
  86. }
  87. // DeleteStack drops an outbound tag's registration (outbound removed).
  88. func (s *socks5EgressServer) DeleteStack(tag string) {
  89. s.mu.Lock()
  90. delete(s.stacks, tag)
  91. delete(s.dns, tag)
  92. s.mu.Unlock()
  93. flushTunnelDNSCacheForTag(tag)
  94. }
  95. // Listen starts accepting on EgressBasePort, or on any free loopback port when
  96. // the OS refuses it. Idempotent; the caller's reconcile tick retries a failure.
  97. func (s *socks5EgressServer) Listen() error {
  98. s.mu.Lock()
  99. defer s.mu.Unlock()
  100. if s.listener != nil {
  101. return nil
  102. }
  103. lc := &net.ListenConfig{}
  104. ln, err := lc.Listen(context.Background(), "tcp", fmt.Sprintf("127.0.0.1:%d", EgressBasePort))
  105. if err != nil {
  106. var fallbackErr error
  107. if ln, fallbackErr = lc.Listen(context.Background(), "tcp", "127.0.0.1:0"); fallbackErr != nil {
  108. return fmt.Errorf("amneziawgnet: egress listen: %w", errors.Join(err, fallbackErr))
  109. }
  110. logger.Warningf("amneziawgnet: egress port %d unavailable, using a free port: %v", EgressBasePort, err)
  111. }
  112. s.listener = ln
  113. s.closing = make(chan struct{})
  114. logger.Infof("amneziawgnet: egress socks listening on %s", ln.Addr())
  115. s.wg.Add(1)
  116. go s.acceptLoop(ln, s.closing)
  117. return nil
  118. }
  119. // Port is the port generated socks bridges must dial: the bound one, or
  120. // EgressBasePort while nothing is bound, since Listen tries it first.
  121. func (s *socks5EgressServer) Port() int {
  122. if port, ok := s.boundPort(); ok {
  123. return port
  124. }
  125. return EgressBasePort
  126. }
  127. func (s *socks5EgressServer) boundPort() (int, bool) {
  128. s.mu.Lock()
  129. defer s.mu.Unlock()
  130. if s.listener == nil {
  131. return 0, false
  132. }
  133. addr, ok := s.listener.Addr().(*net.TCPAddr)
  134. if !ok {
  135. return 0, false
  136. }
  137. return addr.Port, true
  138. }
  139. // EgressPort is the process-wide egress server's Port.
  140. func EgressPort() int {
  141. return GetEgressServer().Port()
  142. }
  143. // Close stops the listener and in-flight handlers; signal first so an accept
  144. // error always observes closing.
  145. func (s *socks5EgressServer) Close() {
  146. s.mu.Lock()
  147. ln := s.listener
  148. s.listener = nil
  149. if ln == nil {
  150. s.mu.Unlock()
  151. return
  152. }
  153. close(s.closing)
  154. tracked := s.tracked
  155. s.tracked = map[net.Conn]struct{}{}
  156. s.mu.Unlock()
  157. ln.Close()
  158. for conn := range tracked {
  159. conn.Close()
  160. }
  161. s.wg.Wait()
  162. }
  163. func (s *socks5EgressServer) acceptLoop(ln net.Listener, closing chan struct{}) {
  164. defer s.wg.Done()
  165. for {
  166. conn, err := ln.Accept()
  167. if err != nil {
  168. select {
  169. case <-closing:
  170. return
  171. default:
  172. }
  173. logger.Warningf("amneziawgnet: egress accept: %v", err)
  174. continue
  175. }
  176. select {
  177. case <-closing:
  178. conn.Close()
  179. return
  180. default:
  181. }
  182. s.mu.Lock()
  183. if s.listener == nil {
  184. s.mu.Unlock()
  185. conn.Close()
  186. return
  187. }
  188. s.tracked[conn] = struct{}{}
  189. s.wg.Add(1)
  190. s.mu.Unlock()
  191. go func(c net.Conn) {
  192. defer s.wg.Done()
  193. defer func() {
  194. s.mu.Lock()
  195. delete(s.tracked, c)
  196. s.mu.Unlock()
  197. }()
  198. s.handleConn(c)
  199. }(conn)
  200. }
  201. }
  202. // stackFor resolves a tag to its live device at use time, so rebuilds take
  203. // effect for new connections without touching the listener.
  204. func (s *socks5EgressServer) stackFor(tag string) (*Device, bool) {
  205. s.mu.Lock()
  206. defer s.mu.Unlock()
  207. dev, ok := s.stacks[tag]
  208. return dev, ok
  209. }
  210. func (s *socks5EgressServer) handleConn(conn net.Conn) {
  211. defer conn.Close()
  212. // Bound the pre-auth handshake so a silent client never pins a handler
  213. // indefinitely across Close() and wg.Wait().
  214. _ = conn.SetDeadline(time.Now().Add(portForwardDialTimeout))
  215. method, err := socks5Greeting(conn)
  216. if err != nil || method == 0xFF {
  217. return
  218. }
  219. user := ""
  220. if method == 0x02 {
  221. // RFC 1929 sub-negotiation: VER(1) | ULEN(1) | UNAME | PLEN(1) |
  222. // PASSWD -- the leading 0x01 version byte must be consumed first.
  223. var ver [1]byte
  224. if _, err := io.ReadFull(conn, ver[:]); err != nil {
  225. return
  226. }
  227. var ulen [1]byte
  228. if _, err := io.ReadFull(conn, ulen[:]); err != nil {
  229. return
  230. }
  231. uname := make([]byte, ulen[0])
  232. if _, err := io.ReadFull(conn, uname); err != nil {
  233. return
  234. }
  235. user = string(uname)
  236. var plen [1]byte
  237. if _, err := io.ReadFull(conn, plen[:]); err != nil {
  238. return
  239. }
  240. pass := make([]byte, plen[0])
  241. if _, err := io.ReadFull(conn, pass); err != nil {
  242. return
  243. }
  244. if !hmac.Equal(pass, []byte(SocksPassword())) {
  245. _, _ = conn.Write([]byte{0x01, 0x01})
  246. return
  247. }
  248. if _, err := conn.Write([]byte{0x01, 0x00}); err != nil {
  249. return
  250. }
  251. }
  252. var req [4]byte
  253. if _, err := io.ReadFull(conn, req[:]); err != nil {
  254. return
  255. }
  256. // Handshake complete: clear deadline for the relay phase.
  257. _ = conn.SetDeadline(time.Time{})
  258. target, err := readSocksRequestTarget(conn, req[3])
  259. if err != nil {
  260. writeSocksReply(conn, 0x01, netip.AddrPort{})
  261. return
  262. }
  263. switch req[1] {
  264. case 0x01: // CONNECT
  265. dev, ok := s.stackFor(user)
  266. if !ok {
  267. writeSocksReply(conn, 0x05, netip.AddrPort{})
  268. return
  269. }
  270. dest, err := target.resolveTunnelVia(s.currentDNSServer(user), user, dev)
  271. if err != nil {
  272. logger.Warningf("amneziawgnet: egress %q: resolve %s: %v", user, target, err)
  273. writeSocksReply(conn, 0x04, netip.AddrPort{})
  274. return
  275. }
  276. s.relayTCP(dev, user, conn, dest)
  277. case 0x03: // UDP ASSOCIATE
  278. dev, ok := s.stackFor(user)
  279. if !ok {
  280. writeSocksReply(conn, 0x05, netip.AddrPort{})
  281. return
  282. }
  283. s.relayUDP(dev, user, udpControl{conn: conn}, target)
  284. default:
  285. writeSocksReply(conn, 0x07, netip.AddrPort{})
  286. }
  287. }
  288. // socks5Greeting requires RFC 1929 username/password auth (0x02).
  289. // Returns 0xFF when unauthenticated or unsupported.
  290. func socks5Greeting(conn net.Conn) (byte, error) {
  291. var hdr [2]byte
  292. if _, err := io.ReadFull(conn, hdr[:]); err != nil {
  293. return 0xFF, err
  294. }
  295. methods := make([]byte, hdr[1])
  296. if _, err := io.ReadFull(conn, methods); err != nil {
  297. return 0xFF, err
  298. }
  299. hasUserPass := false
  300. for _, m := range methods {
  301. if m == 0x02 {
  302. hasUserPass = true
  303. break
  304. }
  305. }
  306. if !hasUserPass {
  307. _, _ = conn.Write([]byte{0x05, 0xFF})
  308. return 0xFF, nil
  309. }
  310. if _, err := conn.Write([]byte{0x05, 0x02}); err != nil {
  311. return 0xFF, err
  312. }
  313. return 0x02, nil
  314. }
  315. // socksTarget is a parsed SOCKS5 request address: an IP, or the raw hostname
  316. // for ATYP 0x03 (resolved through the outbound's tunnel, never host-side).
  317. type socksTarget struct {
  318. host string
  319. ip netip.Addr
  320. port uint16
  321. }
  322. func readSocksRequestTarget(r io.Reader, atyp byte) (socksTarget, error) {
  323. var t socksTarget
  324. switch atyp {
  325. case 0x01:
  326. var b [4]byte
  327. if _, err := io.ReadFull(r, b[:]); err != nil {
  328. return t, err
  329. }
  330. t.ip = netip.AddrFrom4(b)
  331. case 0x04:
  332. var b [16]byte
  333. if _, err := io.ReadFull(r, b[:]); err != nil {
  334. return t, err
  335. }
  336. t.ip = netip.AddrFrom16(b)
  337. case 0x03:
  338. var l [1]byte
  339. if _, err := io.ReadFull(r, l[:]); err != nil {
  340. return t, err
  341. }
  342. name := make([]byte, l[0])
  343. if _, err := io.ReadFull(r, name); err != nil {
  344. return t, err
  345. }
  346. t.host = string(name)
  347. default:
  348. return t, fmt.Errorf("unsupported SOCKS5 request address type %d", atyp)
  349. }
  350. var portBytes [2]byte
  351. if _, err := io.ReadFull(r, portBytes[:]); err != nil {
  352. return t, err
  353. }
  354. t.port = binary.BigEndian.Uint16(portBytes[:])
  355. return t, nil
  356. }
  357. func (t socksTarget) String() string {
  358. if t.ip.IsValid() {
  359. return netip.AddrPortFrom(t.ip, t.port).String()
  360. }
  361. return fmt.Sprintf("%s:%d", t.host, t.port)
  362. }
  363. // Domain targets resolve via the tunnel; reply-side helper must not be used here.
  364. func (t socksTarget) resolveTunnelVia(dnsServer, tag string, dev *Device) (netip.AddrPort, error) {
  365. if t.ip.IsValid() {
  366. if !tunnelSupportsAddr(dev.LocalAddresses(), t.ip) {
  367. return netip.AddrPort{}, fmt.Errorf("tunnel has no route for %s (device addresses %v)", t.ip, dev.LocalAddresses())
  368. }
  369. return netip.AddrPortFrom(t.ip, t.port), nil
  370. }
  371. ctx, cancel := context.WithTimeout(context.Background(), tunnelResolveTimeout)
  372. defer cancel()
  373. addr, err := resolveTunnelVia(ctx, dev, tag, dnsServer, t.host)
  374. if err != nil {
  375. return netip.AddrPort{}, err
  376. }
  377. return netip.AddrPortFrom(addr, t.port), nil
  378. }
  379. // writeSocksReply emits a reply with an empty v4 bind address; Xray only
  380. // reads the code byte.
  381. func writeSocksReply(w io.Writer, code byte, _ netip.AddrPort) {
  382. out := []byte{0x05, code, 0x00, 0x01, 0, 0, 0, 0, 0, 0}
  383. _, _ = w.Write(out)
  384. }
  385. // relayTCP dials dest inside the tagged outbound's netstack and pipes both
  386. // directions until either side closes.
  387. func (s *socks5EgressServer) relayTCP(dev *Device, tag string, upstream net.Conn, dest netip.AddrPort) {
  388. fa := tcpip.FullAddress{
  389. NIC: 1,
  390. Addr: tcpip.AddrFromSlice(dest.Addr().AsSlice()),
  391. Port: dest.Port(),
  392. }
  393. // Bound dial with portForwardDialTimeout so unreachable peers do not
  394. // pin goroutines and netstack endpoints in s.tracked.
  395. dctx, dcancel := context.WithTimeout(context.Background(), portForwardDialTimeout)
  396. defer dcancel()
  397. tunnelConn, err := gonet.DialContextTCP(dctx, dev.Stack, fa, tunnelNetwork(dest.Addr()))
  398. if err != nil {
  399. logger.Warningf("amneziawgnet: egress %q: dial tunnel %s: %v", tag, dest, err)
  400. writeSocksReply(upstream, 0x01, netip.AddrPort{})
  401. return
  402. }
  403. defer tunnelConn.Close()
  404. if _, err := upstream.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0}); err != nil {
  405. return
  406. }
  407. done := make(chan struct{}, 2)
  408. go func() { _, _ = io.Copy(tunnelConn, upstream); done <- struct{}{} }()
  409. go func() { _, _ = io.Copy(upstream, tunnelConn); done <- struct{}{} }()
  410. <-done
  411. }
  412. // udpControl is the control half of one UDP ASSOCIATE: the TCP connection
  413. // whose lifetime bounds the association (RFC 1928).
  414. type udpControl struct{ conn net.Conn }
  415. // egressUDPSession is one UDP ASSOCIATE flow: host-facing socket plus a
  416. // connected tunnel endpoint whose source port makes replies answerable.
  417. type egressUDPSession struct {
  418. dst netip.AddrPort
  419. conn *gonet.UDPConn
  420. }
  421. // relayUDP answers the associate request and relays datagrams to
  422. // per-destination tunnel endpoints until the control connection closes.
  423. func (s *socks5EgressServer) relayUDP(dev *Device, tag string, ctl udpControl, _ socksTarget) {
  424. udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)})
  425. if err != nil {
  426. logger.Warningf("amneziawgnet: egress %q: udp bind: %v", tag, err)
  427. writeSocksReply(ctl.conn, 0x01, netip.AddrPort{})
  428. return
  429. }
  430. defer udpConn.Close()
  431. local := udpConn.LocalAddr().(*net.UDPAddr)
  432. ip4 := local.IP.To4()
  433. reply := []byte{
  434. 0x05, 0x00, 0x00, 0x01, ip4[0], ip4[1], ip4[2], ip4[3],
  435. byte(local.Port >> 8), byte(local.Port),
  436. }
  437. if _, err := ctl.conn.Write(reply); err != nil {
  438. return
  439. }
  440. sessions := &udpEgressSessions{m: map[netip.AddrPort]*egressUDPSession{}}
  441. // Reader: strip per-datagram SOCKS5 headers and forward into the tunnel;
  442. // only the associated client's address is accepted.
  443. go func() {
  444. var client netip.AddrPort
  445. buf := make([]byte, 65536)
  446. for {
  447. n, from, err := udpConn.ReadFrom(buf)
  448. if err != nil {
  449. return
  450. }
  451. if src, ok := udpAddrPort(from); ok {
  452. if client.IsValid() && src != client {
  453. continue // RFC 1928: only the associated client may send
  454. }
  455. client = src
  456. }
  457. data := buf[:n]
  458. if len(data) < 4 {
  459. continue
  460. }
  461. atyp := data[3]
  462. var dst netip.AddrPort
  463. var payloadOff int
  464. if atyp == 0x03 {
  465. name, port, hdrLen, perr := parseDatagramDomainHeader(data)
  466. if perr != nil {
  467. continue
  468. }
  469. // Resolve off reader loop so slow tunnel DNS lookups do not
  470. // stall other destinations on this association.
  471. go func(client netip.AddrPort, name string, port uint16, hdrLen int, datagram []byte) {
  472. dnsSrv := s.currentDNSServer(tag)
  473. rctx, rcancel := context.WithTimeout(context.Background(), tunnelResolveTimeout)
  474. daddr, rerr := resolveTunnelVia(rctx, dev, tag, dnsSrv, name)
  475. rcancel()
  476. if rerr != nil {
  477. logger.Warningf("amneziawgnet: egress %q: resolve udp %q (dns=%s): %v", tag, name, dnsSrv, rerr)
  478. return
  479. }
  480. s.deliverUDPDatagram(dev, tag, udpConn, client, sessions, netip.AddrPortFrom(daddr, port), datagram[hdrLen:])
  481. }(client, name, port, hdrLen, append([]byte(nil), data...))
  482. continue
  483. } else {
  484. hdrLen := 4 + addrLen(atyp) + 2
  485. if hdrLen <= 6 || len(data) < hdrLen {
  486. continue
  487. }
  488. d, derr := parseDatagramHeader(data[:hdrLen])
  489. if derr != nil {
  490. logger.Warningf("amneziawgnet: egress %q: udp header: %v", tag, derr)
  491. continue
  492. }
  493. dst = d
  494. payloadOff = hdrLen
  495. }
  496. s.deliverUDPDatagram(dev, tag, udpConn, client, sessions, dst, data[payloadOff:])
  497. }
  498. }()
  499. // Control-conn close tears down relaying -- relay.go UDPRelay contract.
  500. buf := make([]byte, 512)
  501. for {
  502. if _, err := ctl.conn.Read(buf); err != nil {
  503. sessions.closeAll()
  504. return
  505. }
  506. }
  507. }
  508. // udpEgressSessions guards the association's session map: the reader
  509. // goroutine inserts while the control-conn teardown iterates.
  510. type udpEgressSessions struct {
  511. mu sync.Mutex
  512. m map[netip.AddrPort]*egressUDPSession
  513. }
  514. func (s *udpEgressSessions) getOrDial(dev *Device, tag string, udpConn *net.UDPConn, client netip.AddrPort, dst netip.AddrPort) *egressUDPSession {
  515. s.mu.Lock()
  516. defer s.mu.Unlock()
  517. if sess, ok := s.m[dst]; ok {
  518. return sess
  519. }
  520. if !tunnelSupportsAddr(dev.LocalAddresses(), dst.Addr()) {
  521. logger.Warningf("amneziawgnet: egress %q: dial udp %s: tunnel has no route for address family (device addresses %v)", tag, dst, dev.LocalAddresses())
  522. return nil
  523. }
  524. raddr := tcpip.FullAddress{
  525. NIC: 1,
  526. Addr: tcpip.AddrFromSlice(dst.Addr().AsSlice()),
  527. Port: dst.Port(),
  528. }
  529. conn, err := gonet.DialUDP(dev.Stack, nil, &raddr, tunnelNetwork(dst.Addr()))
  530. if err != nil {
  531. logger.Warningf("amneziawgnet: egress %q: dial udp %s: %v", tag, dst, err)
  532. return nil
  533. }
  534. sess := &egressUDPSession{dst: dst, conn: conn}
  535. s.m[dst] = sess
  536. go pumpUDPEgress(udpConn, client, sess, s)
  537. return sess
  538. }
  539. func (s *udpEgressSessions) closeAll() {
  540. s.mu.Lock()
  541. defer s.mu.Unlock()
  542. for _, sess := range s.m {
  543. sess.conn.Close()
  544. }
  545. }
  546. // deliverUDPDatagram forwards one payload to dst through the tunnel endpoint.
  547. // Safe for concurrent use across resolver and direct-path goroutines.
  548. func (s *socks5EgressServer) deliverUDPDatagram(dev *Device, tag string, udpConn *net.UDPConn, client netip.AddrPort, sessions *udpEgressSessions, dst netip.AddrPort, payload []byte) {
  549. if !client.IsValid() {
  550. return // nothing to reply to yet
  551. }
  552. sess := sessions.getOrDial(dev, tag, udpConn, client, dst)
  553. if sess == nil {
  554. return
  555. }
  556. if _, werr := sess.conn.Write(payload); werr != nil {
  557. logger.Warningf("amneziawgnet: egress %q: send udp to %s: %v", tag, dst, werr)
  558. }
  559. }
  560. // pumpUDPEgress reads replies from one connected tunnel endpoint and writes
  561. // them back to the associated client as SOCKS5 UDP datagrams.
  562. func pumpUDPEgress(udpConn *net.UDPConn, client netip.AddrPort, sess *egressUDPSession, sessions *udpEgressSessions) {
  563. defer func() {
  564. sessions.mu.Lock()
  565. delete(sessions.m, sess.dst)
  566. sessions.mu.Unlock()
  567. sess.conn.Close()
  568. }()
  569. buf := make([]byte, 65536)
  570. for {
  571. // Reap idle egress sessions to avoid holding them indefinitely.
  572. _ = sess.conn.SetReadDeadline(time.Now().Add(portForwardUDPIdleTimeout))
  573. n, err := sess.conn.Read(buf)
  574. if err != nil {
  575. return
  576. }
  577. hdr := make([]byte, 0, 3+1+16+2+n)
  578. hdr = append(hdr, 0x00, 0x00, 0x00) // RSV RSV FRAG(=0)
  579. if sess.dst.Addr().Is4() {
  580. b := sess.dst.Addr().As4()
  581. hdr = append(hdr, 0x01)
  582. hdr = append(hdr, b[:]...)
  583. } else {
  584. b := sess.dst.Addr().As16()
  585. hdr = append(hdr, 0x04)
  586. hdr = append(hdr, b[:]...)
  587. }
  588. var portBytes [2]byte
  589. binary.BigEndian.PutUint16(portBytes[:], sess.dst.Port())
  590. hdr = append(hdr, portBytes[:]...)
  591. hdr = append(hdr, buf[:n]...)
  592. if _, err := udpConn.WriteTo(hdr, net.UDPAddrFromAddrPort(client)); err != nil {
  593. return
  594. }
  595. }
  596. }
  597. // parseDatagramDomainHeader decodes a domain SOCKS5 UDP header (RSV RSV FRAG
  598. // 0x03 LEN NAME PORT) into name, port, and header length.
  599. func parseDatagramDomainHeader(data []byte) (name string, port uint16, hdrLen int, err error) {
  600. if len(data) < 5 {
  601. return "", 0, 0, fmt.Errorf("short domain header")
  602. }
  603. l := int(data[4])
  604. hdrLen = 4 + 1 + l + 2
  605. if l == 0 || len(data) < hdrLen {
  606. return "", 0, 0, fmt.Errorf("short domain payload")
  607. }
  608. name = string(data[5 : 5+l])
  609. port = binary.BigEndian.Uint16(data[5+l : 7+l])
  610. return name, port, hdrLen, nil
  611. }
  612. // addrLen returns the wire length of a SOCKS5 address of the given ATYP.
  613. func addrLen(atyp byte) int {
  614. switch atyp {
  615. case 0x01:
  616. return 4
  617. case 0x04:
  618. return 16
  619. default:
  620. return -1
  621. }
  622. }
  623. // parseDatagramHeader decodes the destination from the front of a SOCKS5 UDP
  624. // datagram header block (RSV RSV FRAG ATYP ADDR PORT).
  625. func parseDatagramHeader(hdr []byte) (netip.AddrPort, error) {
  626. if len(hdr) < 4 {
  627. return netip.AddrPort{}, fmt.Errorf("short header")
  628. }
  629. atyp := hdr[3]
  630. body := hdr[4:]
  631. switch atyp {
  632. case 0x01:
  633. if len(body) < 6 {
  634. return netip.AddrPort{}, fmt.Errorf("short v4")
  635. }
  636. return netip.AddrPortFrom(netip.AddrFrom4([4]byte(body[:4])), binary.BigEndian.Uint16(body[4:6])), nil
  637. case 0x04:
  638. if len(body) < 18 {
  639. return netip.AddrPort{}, fmt.Errorf("short v6")
  640. }
  641. return netip.AddrPortFrom(netip.AddrFrom16([16]byte(body[:16])), binary.BigEndian.Uint16(body[16:18])), nil
  642. default:
  643. return netip.AddrPort{}, fmt.Errorf("unsupported atyp %d", atyp)
  644. }
  645. }