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  }