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 }