github.com/metacubex/mihomo@v1.18.5/listener/tproxy/packet.go (about) 1 package tproxy 2 3 import ( 4 "errors" 5 "fmt" 6 "net" 7 "net/netip" 8 9 "github.com/metacubex/mihomo/adapter/inbound" 10 "github.com/metacubex/mihomo/common/pool" 11 C "github.com/metacubex/mihomo/constant" 12 "github.com/metacubex/mihomo/log" 13 ) 14 15 type packet struct { 16 pc net.PacketConn 17 lAddr netip.AddrPort 18 buf []byte 19 tunnel C.Tunnel 20 } 21 22 func (c *packet) Data() []byte { 23 return c.buf 24 } 25 26 // WriteBack opens a new socket binding `addr` to write UDP packet back 27 func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) { 28 tc, err := createOrGetLocalConn(addr, c.LocalAddr(), c.tunnel) 29 if err != nil { 30 n = 0 31 return 32 } 33 n, err = tc.Write(b) 34 return 35 } 36 37 // LocalAddr returns the source IP/Port of UDP Packet 38 func (c *packet) LocalAddr() net.Addr { 39 return &net.UDPAddr{IP: c.lAddr.Addr().AsSlice(), Port: int(c.lAddr.Port()), Zone: c.lAddr.Addr().Zone()} 40 } 41 42 func (c *packet) Drop() { 43 _ = pool.Put(c.buf) 44 c.buf = nil 45 } 46 47 func (c *packet) InAddr() net.Addr { 48 return c.pc.LocalAddr() 49 } 50 51 // this function listen at rAddr and write to lAddr 52 // for here, rAddr is the ip/port client want to access 53 // lAddr is the ip/port client opened 54 func createOrGetLocalConn(rAddr, lAddr net.Addr, tunnel C.Tunnel) (*net.UDPConn, error) { 55 remote := rAddr.String() 56 local := lAddr.String() 57 natTable := tunnel.NatTable() 58 localConn := natTable.GetForLocalConn(local, remote) 59 // localConn not exist 60 if localConn == nil { 61 cond, loaded := natTable.GetOrCreateLockForLocalConn(local, remote) 62 if loaded { 63 cond.L.Lock() 64 cond.Wait() 65 // we should get localConn here 66 localConn = natTable.GetForLocalConn(local, remote) 67 if localConn == nil { 68 return nil, fmt.Errorf("localConn is nil, nat entry not exist") 69 } 70 cond.L.Unlock() 71 } else { 72 if cond == nil { 73 return nil, fmt.Errorf("cond is nil, nat entry not exist") 74 } 75 defer func() { 76 natTable.DeleteLockForLocalConn(local, remote) 77 cond.Broadcast() 78 }() 79 conn, err := listenLocalConn(rAddr, lAddr, tunnel) 80 if err != nil { 81 log.Errorln("listenLocalConn failed with error: %s, packet loss (rAddr[%T]=%s lAddr[%T]=%s)", err.Error(), rAddr, remote, lAddr, local) 82 return nil, err 83 } 84 natTable.AddForLocalConn(local, remote, conn) 85 localConn = conn 86 } 87 } 88 return localConn, nil 89 } 90 91 // this function listen at rAddr 92 // and send what received to program itself, then send to real remote 93 func listenLocalConn(rAddr, lAddr net.Addr, tunnel C.Tunnel) (*net.UDPConn, error) { 94 additions := []inbound.Addition{ 95 inbound.WithInName("DEFAULT-TPROXY"), 96 inbound.WithSpecialRules(""), 97 } 98 lc, err := dialUDP("udp", rAddr.(*net.UDPAddr).AddrPort(), lAddr.(*net.UDPAddr).AddrPort()) 99 if err != nil { 100 return nil, err 101 } 102 go func() { 103 log.Debugln("TProxy listenLocalConn rAddr=%s lAddr=%s", rAddr.String(), lAddr.String()) 104 for { 105 buf := pool.Get(pool.UDPBufferSize) 106 br, err := lc.Read(buf) 107 if err != nil { 108 pool.Put(buf) 109 if errors.Is(err, net.ErrClosed) { 110 log.Debugln("TProxy local conn listener exit.. rAddr=%s lAddr=%s", rAddr.String(), lAddr.String()) 111 return 112 } 113 } 114 // since following localPackets are pass through this socket which listen rAddr 115 // I choose current listener as packet's packet conn 116 handlePacketConn(lc, tunnel, buf[:br], lAddr.(*net.UDPAddr).AddrPort(), rAddr.(*net.UDPAddr).AddrPort(), additions...) 117 } 118 }() 119 return lc, nil 120 }