github.com/metacubex/mihomo@v1.18.5/adapter/outbound/direct.go (about)

     1  package outbound
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"net/netip"
     7  
     8  	N "github.com/metacubex/mihomo/common/net"
     9  	"github.com/metacubex/mihomo/component/dialer"
    10  	"github.com/metacubex/mihomo/component/loopback"
    11  	"github.com/metacubex/mihomo/component/resolver"
    12  	C "github.com/metacubex/mihomo/constant"
    13  )
    14  
    15  type Direct struct {
    16  	*Base
    17  	loopBack *loopback.Detector
    18  }
    19  
    20  type DirectOption struct {
    21  	BasicOption
    22  	Name string `proxy:"name"`
    23  }
    24  
    25  // DialContext implements C.ProxyAdapter
    26  func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
    27  	if err := d.loopBack.CheckConn(metadata); err != nil {
    28  		return nil, err
    29  	}
    30  	opts = append(opts, dialer.WithResolver(resolver.DefaultResolver))
    31  	c, err := dialer.DialContext(ctx, "tcp", metadata.RemoteAddress(), d.Base.DialOptions(opts...)...)
    32  	if err != nil {
    33  		return nil, err
    34  	}
    35  	N.TCPKeepAlive(c)
    36  	return d.loopBack.NewConn(NewConn(c, d)), nil
    37  }
    38  
    39  // ListenPacketContext implements C.ProxyAdapter
    40  func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
    41  	if err := d.loopBack.CheckPacketConn(metadata); err != nil {
    42  		return nil, err
    43  	}
    44  	// net.UDPConn.WriteTo only working with *net.UDPAddr, so we need a net.UDPAddr
    45  	if !metadata.Resolved() {
    46  		ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, resolver.DefaultResolver)
    47  		if err != nil {
    48  			return nil, errors.New("can't resolve ip")
    49  		}
    50  		metadata.DstIP = ip
    51  	}
    52  	pc, err := dialer.NewDialer(d.Base.DialOptions(opts...)...).ListenPacket(ctx, "udp", "", netip.AddrPortFrom(metadata.DstIP, metadata.DstPort))
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  	return d.loopBack.NewPacketConn(newPacketConn(pc, d)), nil
    57  }
    58  
    59  func NewDirectWithOption(option DirectOption) *Direct {
    60  	return &Direct{
    61  		Base: &Base{
    62  			name:   option.Name,
    63  			tp:     C.Direct,
    64  			udp:    true,
    65  			tfo:    option.TFO,
    66  			mpTcp:  option.MPTCP,
    67  			iface:  option.Interface,
    68  			rmark:  option.RoutingMark,
    69  			prefer: C.NewDNSPrefer(option.IPVersion),
    70  		},
    71  		loopBack: loopback.NewDetector(),
    72  	}
    73  }
    74  
    75  func NewDirect() *Direct {
    76  	return &Direct{
    77  		Base: &Base{
    78  			name:   "DIRECT",
    79  			tp:     C.Direct,
    80  			udp:    true,
    81  			prefer: C.DualStack,
    82  		},
    83  		loopBack: loopback.NewDetector(),
    84  	}
    85  }
    86  
    87  func NewCompatible() *Direct {
    88  	return &Direct{
    89  		Base: &Base{
    90  			name:   "COMPATIBLE",
    91  			tp:     C.Compatible,
    92  			udp:    true,
    93  			prefer: C.DualStack,
    94  		},
    95  		loopBack: loopback.NewDetector(),
    96  	}
    97  }