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

     1  package outbound
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"net"
     7  	"strings"
     8  	"syscall"
     9  
    10  	N "github.com/metacubex/mihomo/common/net"
    11  	"github.com/metacubex/mihomo/common/utils"
    12  	"github.com/metacubex/mihomo/component/dialer"
    13  	C "github.com/metacubex/mihomo/constant"
    14  )
    15  
    16  type Base struct {
    17  	name   string
    18  	addr   string
    19  	iface  string
    20  	tp     C.AdapterType
    21  	udp    bool
    22  	xudp   bool
    23  	tfo    bool
    24  	mpTcp  bool
    25  	rmark  int
    26  	id     string
    27  	prefer C.DNSPrefer
    28  }
    29  
    30  // Name implements C.ProxyAdapter
    31  func (b *Base) Name() string {
    32  	return b.name
    33  }
    34  
    35  // Id implements C.ProxyAdapter
    36  func (b *Base) Id() string {
    37  	if b.id == "" {
    38  		b.id = utils.NewUUIDV6().String()
    39  	}
    40  
    41  	return b.id
    42  }
    43  
    44  // Type implements C.ProxyAdapter
    45  func (b *Base) Type() C.AdapterType {
    46  	return b.tp
    47  }
    48  
    49  // StreamConnContext implements C.ProxyAdapter
    50  func (b *Base) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (net.Conn, error) {
    51  	return c, C.ErrNotSupport
    52  }
    53  
    54  func (b *Base) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
    55  	return nil, C.ErrNotSupport
    56  }
    57  
    58  // DialContextWithDialer implements C.ProxyAdapter
    59  func (b *Base) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) {
    60  	return nil, C.ErrNotSupport
    61  }
    62  
    63  // ListenPacketContext implements C.ProxyAdapter
    64  func (b *Base) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
    65  	return nil, C.ErrNotSupport
    66  }
    67  
    68  // ListenPacketWithDialer implements C.ProxyAdapter
    69  func (b *Base) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) {
    70  	return nil, C.ErrNotSupport
    71  }
    72  
    73  // SupportWithDialer implements C.ProxyAdapter
    74  func (b *Base) SupportWithDialer() C.NetWork {
    75  	return C.InvalidNet
    76  }
    77  
    78  // SupportUOT implements C.ProxyAdapter
    79  func (b *Base) SupportUOT() bool {
    80  	return false
    81  }
    82  
    83  // SupportUDP implements C.ProxyAdapter
    84  func (b *Base) SupportUDP() bool {
    85  	return b.udp
    86  }
    87  
    88  // SupportXUDP implements C.ProxyAdapter
    89  func (b *Base) SupportXUDP() bool {
    90  	return b.xudp
    91  }
    92  
    93  // SupportTFO implements C.ProxyAdapter
    94  func (b *Base) SupportTFO() bool {
    95  	return b.tfo
    96  }
    97  
    98  // IsL3Protocol implements C.ProxyAdapter
    99  func (b *Base) IsL3Protocol(metadata *C.Metadata) bool {
   100  	return false
   101  }
   102  
   103  // MarshalJSON implements C.ProxyAdapter
   104  func (b *Base) MarshalJSON() ([]byte, error) {
   105  	return json.Marshal(map[string]string{
   106  		"type": b.Type().String(),
   107  		"id":   b.Id(),
   108  	})
   109  }
   110  
   111  // Addr implements C.ProxyAdapter
   112  func (b *Base) Addr() string {
   113  	return b.addr
   114  }
   115  
   116  // Unwrap implements C.ProxyAdapter
   117  func (b *Base) Unwrap(metadata *C.Metadata, touch bool) C.Proxy {
   118  	return nil
   119  }
   120  
   121  // DialOptions return []dialer.Option from struct
   122  func (b *Base) DialOptions(opts ...dialer.Option) []dialer.Option {
   123  	if b.iface != "" {
   124  		opts = append(opts, dialer.WithInterface(b.iface))
   125  	}
   126  
   127  	if b.rmark != 0 {
   128  		opts = append(opts, dialer.WithRoutingMark(b.rmark))
   129  	}
   130  
   131  	switch b.prefer {
   132  	case C.IPv4Only:
   133  		opts = append(opts, dialer.WithOnlySingleStack(true))
   134  	case C.IPv6Only:
   135  		opts = append(opts, dialer.WithOnlySingleStack(false))
   136  	case C.IPv4Prefer:
   137  		opts = append(opts, dialer.WithPreferIPv4())
   138  	case C.IPv6Prefer:
   139  		opts = append(opts, dialer.WithPreferIPv6())
   140  	default:
   141  	}
   142  
   143  	if b.tfo {
   144  		opts = append(opts, dialer.WithTFO(true))
   145  	}
   146  
   147  	if b.mpTcp {
   148  		opts = append(opts, dialer.WithMPTCP(true))
   149  	}
   150  
   151  	return opts
   152  }
   153  
   154  type BasicOption struct {
   155  	TFO         bool   `proxy:"tfo,omitempty" group:"tfo,omitempty"`
   156  	MPTCP       bool   `proxy:"mptcp,omitempty" group:"mptcp,omitempty"`
   157  	Interface   string `proxy:"interface-name,omitempty" group:"interface-name,omitempty"`
   158  	RoutingMark int    `proxy:"routing-mark,omitempty" group:"routing-mark,omitempty"`
   159  	IPVersion   string `proxy:"ip-version,omitempty" group:"ip-version,omitempty"`
   160  	DialerProxy string `proxy:"dialer-proxy,omitempty"` // don't apply this option into groups, but can set a group name in a proxy
   161  }
   162  
   163  type BaseOption struct {
   164  	Name        string
   165  	Addr        string
   166  	Type        C.AdapterType
   167  	UDP         bool
   168  	XUDP        bool
   169  	TFO         bool
   170  	MPTCP       bool
   171  	Interface   string
   172  	RoutingMark int
   173  	Prefer      C.DNSPrefer
   174  }
   175  
   176  func NewBase(opt BaseOption) *Base {
   177  	return &Base{
   178  		name:   opt.Name,
   179  		addr:   opt.Addr,
   180  		tp:     opt.Type,
   181  		udp:    opt.UDP,
   182  		xudp:   opt.XUDP,
   183  		tfo:    opt.TFO,
   184  		mpTcp:  opt.MPTCP,
   185  		iface:  opt.Interface,
   186  		rmark:  opt.RoutingMark,
   187  		prefer: opt.Prefer,
   188  	}
   189  }
   190  
   191  type conn struct {
   192  	N.ExtendedConn
   193  	chain                   C.Chain
   194  	actualRemoteDestination string
   195  }
   196  
   197  func (c *conn) RemoteDestination() string {
   198  	return c.actualRemoteDestination
   199  }
   200  
   201  // Chains implements C.Connection
   202  func (c *conn) Chains() C.Chain {
   203  	return c.chain
   204  }
   205  
   206  // AppendToChains implements C.Connection
   207  func (c *conn) AppendToChains(a C.ProxyAdapter) {
   208  	c.chain = append(c.chain, a.Name())
   209  }
   210  
   211  func (c *conn) Upstream() any {
   212  	return c.ExtendedConn
   213  }
   214  
   215  func (c *conn) WriterReplaceable() bool {
   216  	return true
   217  }
   218  
   219  func (c *conn) ReaderReplaceable() bool {
   220  	return true
   221  }
   222  
   223  func NewConn(c net.Conn, a C.ProxyAdapter) C.Conn {
   224  	if _, ok := c.(syscall.Conn); !ok { // exclusion system conn like *net.TCPConn
   225  		c = N.NewDeadlineConn(c) // most conn from outbound can't handle readDeadline correctly
   226  	}
   227  	return &conn{N.NewExtendedConn(c), []string{a.Name()}, parseRemoteDestination(a.Addr())}
   228  }
   229  
   230  type packetConn struct {
   231  	N.EnhancePacketConn
   232  	chain                   C.Chain
   233  	adapterName             string
   234  	connID                  string
   235  	actualRemoteDestination string
   236  }
   237  
   238  func (c *packetConn) RemoteDestination() string {
   239  	return c.actualRemoteDestination
   240  }
   241  
   242  // Chains implements C.Connection
   243  func (c *packetConn) Chains() C.Chain {
   244  	return c.chain
   245  }
   246  
   247  // AppendToChains implements C.Connection
   248  func (c *packetConn) AppendToChains(a C.ProxyAdapter) {
   249  	c.chain = append(c.chain, a.Name())
   250  }
   251  
   252  func (c *packetConn) LocalAddr() net.Addr {
   253  	lAddr := c.EnhancePacketConn.LocalAddr()
   254  	return N.NewCustomAddr(c.adapterName, c.connID, lAddr) // make quic-go's connMultiplexer happy
   255  }
   256  
   257  func (c *packetConn) Upstream() any {
   258  	return c.EnhancePacketConn
   259  }
   260  
   261  func (c *packetConn) WriterReplaceable() bool {
   262  	return true
   263  }
   264  
   265  func (c *packetConn) ReaderReplaceable() bool {
   266  	return true
   267  }
   268  
   269  func newPacketConn(pc net.PacketConn, a C.ProxyAdapter) C.PacketConn {
   270  	epc := N.NewEnhancePacketConn(pc)
   271  	if _, ok := pc.(syscall.Conn); !ok { // exclusion system conn like *net.UDPConn
   272  		epc = N.NewDeadlineEnhancePacketConn(epc) // most conn from outbound can't handle readDeadline correctly
   273  	}
   274  	return &packetConn{epc, []string{a.Name()}, a.Name(), utils.NewUUIDV4().String(), parseRemoteDestination(a.Addr())}
   275  }
   276  
   277  func parseRemoteDestination(addr string) string {
   278  	if dst, _, err := net.SplitHostPort(addr); err == nil {
   279  		return dst
   280  	} else {
   281  		if addrError, ok := err.(*net.AddrError); ok && strings.Contains(addrError.Err, "missing port") {
   282  			return dst
   283  		} else {
   284  			return ""
   285  		}
   286  	}
   287  }