github.com/metacubex/mihomo@v1.18.5/listener/tproxy/udp.go (about)

     1  package tproxy
     2  
     3  import (
     4  	"net"
     5  	"net/netip"
     6  
     7  	"github.com/metacubex/mihomo/adapter/inbound"
     8  	"github.com/metacubex/mihomo/common/pool"
     9  	C "github.com/metacubex/mihomo/constant"
    10  	"github.com/metacubex/mihomo/transport/socks5"
    11  )
    12  
    13  type UDPListener struct {
    14  	packetConn net.PacketConn
    15  	addr       string
    16  	closed     bool
    17  }
    18  
    19  // RawAddress implements C.Listener
    20  func (l *UDPListener) RawAddress() string {
    21  	return l.addr
    22  }
    23  
    24  // Address implements C.Listener
    25  func (l *UDPListener) Address() string {
    26  	return l.packetConn.LocalAddr().String()
    27  }
    28  
    29  // Close implements C.Listener
    30  func (l *UDPListener) Close() error {
    31  	l.closed = true
    32  	return l.packetConn.Close()
    33  }
    34  
    35  func NewUDP(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*UDPListener, error) {
    36  	if len(additions) == 0 {
    37  		additions = []inbound.Addition{
    38  			inbound.WithInName("DEFAULT-TPROXY"),
    39  			inbound.WithSpecialRules(""),
    40  		}
    41  	}
    42  	l, err := net.ListenPacket("udp", addr)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  
    47  	rl := &UDPListener{
    48  		packetConn: l,
    49  		addr:       addr,
    50  	}
    51  
    52  	c := l.(*net.UDPConn)
    53  
    54  	rc, err := c.SyscallConn()
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  
    59  	err = setsockopt(rc, addr)
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  
    64  	go func() {
    65  		oob := make([]byte, 1024)
    66  		for {
    67  			buf := pool.Get(pool.UDPBufferSize)
    68  			n, oobn, _, lAddr, err := c.ReadMsgUDPAddrPort(buf, oob)
    69  			if err != nil {
    70  				pool.Put(buf)
    71  				if rl.closed {
    72  					break
    73  				}
    74  				continue
    75  			}
    76  
    77  			rAddr, err := getOrigDst(oob[:oobn])
    78  			if err != nil {
    79  				continue
    80  			}
    81  
    82  			dscp, _ := getDSCP(oob[:oobn])
    83  			additions = append(additions, inbound.WithDSCP(dscp))
    84  
    85  			if rAddr.Addr().Is4() {
    86  				// try to unmap 4in6 address
    87  				lAddr = netip.AddrPortFrom(lAddr.Addr().Unmap(), lAddr.Port())
    88  			}
    89  			handlePacketConn(l, tunnel, buf[:n], lAddr, rAddr, additions...)
    90  		}
    91  	}()
    92  
    93  	return rl, nil
    94  }
    95  
    96  func handlePacketConn(pc net.PacketConn, tunnel C.Tunnel, buf []byte, lAddr, rAddr netip.AddrPort, additions ...inbound.Addition) {
    97  	target := socks5.AddrFromStdAddrPort(rAddr)
    98  	pkt := &packet{
    99  		pc:     pc,
   100  		lAddr:  lAddr,
   101  		buf:    buf,
   102  		tunnel: tunnel,
   103  	}
   104  	tunnel.HandleUDPPacket(inbound.NewPacket(target, pkt, C.TPROXY, additions...))
   105  }