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 }