github.com/metacubex/mihomo@v1.18.5/adapter/outboundgroup/relay.go (about)

     1  package outboundgroup
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  
     7  	"github.com/metacubex/mihomo/adapter/outbound"
     8  	"github.com/metacubex/mihomo/component/dialer"
     9  	"github.com/metacubex/mihomo/component/proxydialer"
    10  	C "github.com/metacubex/mihomo/constant"
    11  	"github.com/metacubex/mihomo/constant/provider"
    12  )
    13  
    14  type Relay struct {
    15  	*GroupBase
    16  	Hidden bool
    17  	Icon   string
    18  }
    19  
    20  // DialContext implements C.ProxyAdapter
    21  func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
    22  	proxies, chainProxies := r.proxies(metadata, true)
    23  
    24  	switch len(proxies) {
    25  	case 0:
    26  		return outbound.NewDirect().DialContext(ctx, metadata, r.Base.DialOptions(opts...)...)
    27  	case 1:
    28  		return proxies[0].DialContext(ctx, metadata, r.Base.DialOptions(opts...)...)
    29  	}
    30  	var d C.Dialer
    31  	d = dialer.NewDialer(r.Base.DialOptions(opts...)...)
    32  	for _, proxy := range proxies[:len(proxies)-1] {
    33  		d = proxydialer.New(proxy, d, false)
    34  	}
    35  	last := proxies[len(proxies)-1]
    36  	conn, err := last.DialContextWithDialer(ctx, d, metadata)
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  
    41  	for i := len(chainProxies) - 2; i >= 0; i-- {
    42  		conn.AppendToChains(chainProxies[i])
    43  	}
    44  
    45  	conn.AppendToChains(r)
    46  
    47  	return conn, nil
    48  }
    49  
    50  // ListenPacketContext implements C.ProxyAdapter
    51  func (r *Relay) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) {
    52  	proxies, chainProxies := r.proxies(metadata, true)
    53  
    54  	switch len(proxies) {
    55  	case 0:
    56  		return outbound.NewDirect().ListenPacketContext(ctx, metadata, r.Base.DialOptions(opts...)...)
    57  	case 1:
    58  		return proxies[0].ListenPacketContext(ctx, metadata, r.Base.DialOptions(opts...)...)
    59  	}
    60  
    61  	var d C.Dialer
    62  	d = dialer.NewDialer(r.Base.DialOptions(opts...)...)
    63  	for _, proxy := range proxies[:len(proxies)-1] {
    64  		d = proxydialer.New(proxy, d, false)
    65  	}
    66  	last := proxies[len(proxies)-1]
    67  	pc, err := last.ListenPacketWithDialer(ctx, d, metadata)
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  
    72  	for i := len(chainProxies) - 2; i >= 0; i-- {
    73  		pc.AppendToChains(chainProxies[i])
    74  	}
    75  
    76  	pc.AppendToChains(r)
    77  
    78  	return pc, nil
    79  }
    80  
    81  // SupportUDP implements C.ProxyAdapter
    82  func (r *Relay) SupportUDP() bool {
    83  	proxies, _ := r.proxies(nil, false)
    84  	if len(proxies) == 0 { // C.Direct
    85  		return true
    86  	}
    87  	for i := len(proxies) - 1; i >= 0; i-- {
    88  		proxy := proxies[i]
    89  		if !proxy.SupportUDP() {
    90  			return false
    91  		}
    92  		if proxy.SupportUOT() {
    93  			return true
    94  		}
    95  		switch proxy.SupportWithDialer() {
    96  		case C.ALLNet:
    97  		case C.UDP:
    98  		default: // C.TCP and C.InvalidNet
    99  			return false
   100  		}
   101  	}
   102  	return true
   103  }
   104  
   105  // MarshalJSON implements C.ProxyAdapter
   106  func (r *Relay) MarshalJSON() ([]byte, error) {
   107  	all := []string{}
   108  	for _, proxy := range r.GetProxies(false) {
   109  		all = append(all, proxy.Name())
   110  	}
   111  	return json.Marshal(map[string]any{
   112  		"type":   r.Type().String(),
   113  		"all":    all,
   114  		"hidden": r.Hidden,
   115  		"icon":   r.Icon,
   116  	})
   117  }
   118  
   119  func (r *Relay) proxies(metadata *C.Metadata, touch bool) ([]C.Proxy, []C.Proxy) {
   120  	rawProxies := r.GetProxies(touch)
   121  
   122  	var proxies []C.Proxy
   123  	var chainProxies []C.Proxy
   124  	var targetProxies []C.Proxy
   125  
   126  	for n, proxy := range rawProxies {
   127  		proxies = append(proxies, proxy)
   128  		chainProxies = append(chainProxies, proxy)
   129  		subproxy := proxy.Unwrap(metadata, touch)
   130  		for subproxy != nil {
   131  			chainProxies = append(chainProxies, subproxy)
   132  			proxies[n] = subproxy
   133  			subproxy = subproxy.Unwrap(metadata, touch)
   134  		}
   135  	}
   136  
   137  	for _, proxy := range proxies {
   138  		if proxy.Type() != C.Direct && proxy.Type() != C.Compatible {
   139  			targetProxies = append(targetProxies, proxy)
   140  		}
   141  	}
   142  
   143  	return targetProxies, chainProxies
   144  }
   145  
   146  func (r *Relay) Addr() string {
   147  	proxies, _ := r.proxies(nil, false)
   148  	return proxies[len(proxies)-1].Addr()
   149  }
   150  
   151  func NewRelay(option *GroupCommonOption, providers []provider.ProxyProvider) *Relay {
   152  	return &Relay{
   153  		GroupBase: NewGroupBase(GroupBaseOption{
   154  			outbound.BaseOption{
   155  				Name:        option.Name,
   156  				Type:        C.Relay,
   157  				Interface:   option.Interface,
   158  				RoutingMark: option.RoutingMark,
   159  			},
   160  			"",
   161  			"",
   162  			"",
   163  			5000,
   164  			5,
   165  			providers,
   166  		}),
   167  		Hidden: option.Hidden,
   168  		Icon:   option.Icon,
   169  	}
   170  }