github.com/metacubex/mihomo@v1.18.5/component/proxydialer/proxydialer.go (about)

     1  package proxydialer
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"net"
     8  	"net/netip"
     9  	"strings"
    10  
    11  	N "github.com/metacubex/mihomo/common/net"
    12  	"github.com/metacubex/mihomo/component/dialer"
    13  	"github.com/metacubex/mihomo/component/resolver"
    14  	C "github.com/metacubex/mihomo/constant"
    15  	"github.com/metacubex/mihomo/tunnel"
    16  	"github.com/metacubex/mihomo/tunnel/statistic"
    17  )
    18  
    19  type proxyDialer struct {
    20  	proxy     C.ProxyAdapter
    21  	dialer    C.Dialer
    22  	statistic bool
    23  }
    24  
    25  func New(proxy C.ProxyAdapter, dialer C.Dialer, statistic bool) C.Dialer {
    26  	return proxyDialer{proxy: proxy, dialer: dialer, statistic: statistic}
    27  }
    28  
    29  func NewByName(proxyName string, dialer C.Dialer) (C.Dialer, error) {
    30  	proxies := tunnel.Proxies()
    31  	if proxy, ok := proxies[proxyName]; ok {
    32  		return New(proxy, dialer, true), nil
    33  	}
    34  	return nil, fmt.Errorf("proxyName[%s] not found", proxyName)
    35  }
    36  
    37  func (p proxyDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
    38  	currentMeta := &C.Metadata{Type: C.INNER}
    39  	if err := currentMeta.SetRemoteAddress(address); err != nil {
    40  		return nil, err
    41  	}
    42  	if strings.Contains(network, "udp") { // using in wireguard outbound
    43  		if !currentMeta.Resolved() {
    44  			ip, err := resolver.ResolveIP(ctx, currentMeta.Host)
    45  			if err != nil {
    46  				return nil, errors.New("can't resolve ip")
    47  			}
    48  			currentMeta.DstIP = ip
    49  		}
    50  		pc, err := p.listenPacket(ctx, currentMeta)
    51  		if err != nil {
    52  			return nil, err
    53  		}
    54  		return N.NewBindPacketConn(pc, currentMeta.UDPAddr()), nil
    55  	}
    56  	var conn C.Conn
    57  	var err error
    58  	if d, ok := p.dialer.(dialer.Dialer); ok { // first using old function to let mux work
    59  		conn, err = p.proxy.DialContext(ctx, currentMeta, dialer.WithOption(d.Opt))
    60  	} else {
    61  		conn, err = p.proxy.DialContextWithDialer(ctx, p.dialer, currentMeta)
    62  	}
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  	if p.statistic {
    67  		conn = statistic.NewTCPTracker(conn, statistic.DefaultManager, currentMeta, nil, 0, 0, false)
    68  	}
    69  	return conn, err
    70  }
    71  
    72  func (p proxyDialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) {
    73  	currentMeta := &C.Metadata{Type: C.INNER, DstIP: rAddrPort.Addr(), DstPort: rAddrPort.Port()}
    74  	return p.listenPacket(ctx, currentMeta)
    75  }
    76  
    77  func (p proxyDialer) listenPacket(ctx context.Context, currentMeta *C.Metadata) (C.PacketConn, error) {
    78  	var pc C.PacketConn
    79  	var err error
    80  	currentMeta.NetWork = C.UDP
    81  	if d, ok := p.dialer.(dialer.Dialer); ok { // first using old function to let mux work
    82  		pc, err = p.proxy.ListenPacketContext(ctx, currentMeta, dialer.WithOption(d.Opt))
    83  	} else {
    84  		pc, err = p.proxy.ListenPacketWithDialer(ctx, p.dialer, currentMeta)
    85  	}
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  	if p.statistic {
    90  		pc = statistic.NewUDPTracker(pc, statistic.DefaultManager, currentMeta, nil, 0, 0, false)
    91  	}
    92  	return pc, nil
    93  }