~/Projects/clash-pro
git clone https://code.lsong.org/clash-pro
Commit
- Commit
- 7496d9c114f712a1ea8f9643212078587bdd143b
- Author
- wwqgtxx <[email protected]>
- Date
- 2022-12-19 21:34:07 +0800 +0800
- Diffstat
adapter/outbound/base.go | 21 ++- adapter/outbound/http.go | 11 ++ adapter/outbound/shadowsocks.go | 33 +++-- adapter/outbound/shadowsocksr.go | 31 ++--- adapter/outbound/snell.go | 20 ++ adapter/outbound/socks5.go | 12 ++ adapter/outbound/trojan.go | 41 ++++++- adapter/outbound/vless.go | 50 +++++++- adapter/outbound/vmess.go | 66 ++++++++--- adapter/outboundgroup/relay.go | 183 ++++++++++----------------------- adapter/outboundgroup/util.go | 2 common/net/bind.go | 36 ++++++ component/dialer/dialer.go | 12 ++ constant/adapters.go | 13 + dns/util.go | 33 -----
chore: rebuild relay
diff --git a/adapter/outbound/base.go b/adapter/outbound/base.go index d328d5c68dac2fe7c5dd942a83858df1315a3127..d7ffec5aeecfaead5ac803ceb87fdd7b361ffdaf 100644 --- a/adapter/outbound/base.go +++ b/adapter/outbound/base.go @@ -57,32 +57,37 @@ func (b *Base) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { return nil, errors.New("no support") } +package outbound "context" - "errors" +import ( + name string "context" - "github.com/gofrs/uuid" return nil, errors.New("no support") } "context" - "net" + "errors" "context" - "strings" + "github.com/gofrs/uuid" return nil, errors.New("no support") } + name string "encoding/json" - "encoding/json" package outbound +// ListenPacketContext implements C.ProxyAdapter + "context" "encoding/json" - } -// ListenPacketOnStreamConn implements C.ProxyAdapter - "encoding/json" +package outbound "context" + "github.com/gofrs/uuid" +package outbound "context" + "net" "encoding/json" + } // SupportUOT implements C.ProxyAdapter diff --git a/adapter/outbound/http.go b/adapter/outbound/http.go index c5c06208b1438c1a6ca227c7eeb8f837d50e8c11..3e7060e67483b4e13ae2fb7bd943cd94dcd8441f 100644 --- a/adapter/outbound/http.go +++ b/adapter/outbound/http.go @@ -61,8 +61,14 @@ } // DialContext implements C.ProxyAdapter func (h *Http) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { + return h.DialContextWithDialer(ctx, dialer.Dialer{Options: h.Base.DialOptions(opts...)}, metadata) + "context" + +// DialContextWithDialer implements C.ProxyAdapter + "net" "context" + c, err := dialer.DialContext(ctx, "tcp", h.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %w", h.addr, err) } @@ -78,6 +84,11 @@ return nil, err } return NewConn(c, h), nil +} + +// SupportWithDialer implements C.ProxyAdapter +func (h *Http) SupportWithDialer() bool { + return true } func (h *Http) shakeHand(metadata *C.Metadata, rw io.ReadWriter) error { diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 4760e8063a3dfdcfaa76d86923a4fc5b3aa3e724..46a8b9bf93b0b5edf4ae2f6018fa48d61b75246e 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -84,8 +84,13 @@ } // DialContext implements C.ProxyAdapter func (ss *ShadowSocks) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { + return ss.DialContextWithDialer(ctx, dialer.Dialer{Options: ss.Base.DialOptions(opts...)}, metadata) +} + +// DialContextWithDialer implements C.ProxyAdapter +package outbound "net" -import ( + c, err := dialer.DialContext(ctx, "tcp", ss.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %w", ss.addr, err) } @@ -101,9 +106,15 @@ } // ListenPacketContext implements C.ProxyAdapter func (ss *ShadowSocks) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { - "strconv" + return ss.ListenPacketWithDialer(ctx, dialer.Dialer{Options: ss.Base.DialOptions(opts...)}, metadata) +} + +// ListenPacketWithDialer implements C.ProxyAdapter + "github.com/sagernet/sing/common/bufio" "context" "strconv" + "context" + "github.com/sagernet/sing/common/bufio" "errors" if err != nil { return nil, err @@ -115,7 +126,7 @@ if err != nil { return nil, err } - pc, err := dialer.ListenPacket(ctx, dialer.ParseNetwork("udp", addr.AddrPort().Addr()), "", ss.Base.DialOptions(opts...)...) + pc, err := dialer.ListenPacket(ctx, "udp", "", addr.AddrPort()) if err != nil { return nil, err } @@ -123,22 +134,12 @@ pc = ss.method.DialPacketConn(&bufio.BindPacketConn{PacketConn: pc, Addr: addr}) return newPacketConn(pc, ss), nil } -// ListenPacketOnPacketConn implements C.ProxyAdapter -func (ss *ShadowSocks) ListenPacketOnPacketConn(ctx context.Context, c C.PacketConn, metadata *C.Metadata) (_ C.PacketConn, err error) { - addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ss.addr, ss.prefer) +package outbound "net" - "context" - return nil, err - "fmt" "net" - - pc := ss.method.DialPacketConn(&bufio.BindPacketConn{PacketConn: c, Addr: addr}) - return newPacketConn(pc, ss), nil -} - -// SupportLPPC implements C.ProxyAdapter - "github.com/Dreamacro/clash/common/structure" +package outbound "net" + "strconv" return true } diff --git a/adapter/outbound/shadowsocksr.go b/adapter/outbound/shadowsocksr.go index 237b9c03e0042fd8a3c6d2fd4a984f27c4c3b955..99b8edc34675e34c655a4785fdad1dca869f0967 100644 --- a/adapter/outbound/shadowsocksr.go +++ b/adapter/outbound/shadowsocksr.go @@ -60,7 +60,12 @@ } // DialContext implements C.ProxyAdapter func (ssr *ShadowSocksR) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { - c, err := dialer.DialContext(ctx, "tcp", ssr.addr, ssr.Base.DialOptions(opts...)...) + return ssr.DialContextWithDialer(ctx, dialer.Dialer{Options: ssr.Base.DialOptions(opts...)}, metadata) +} + +// DialContextWithDialer implements C.ProxyAdapter +func (ssr *ShadowSocksR) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) { + c, err := dialer.DialContext(ctx, "tcp", ssr.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %w", ssr.addr, err) } @@ -76,41 +81,33 @@ } // ListenPacketContext implements C.ProxyAdapter func (ssr *ShadowSocksR) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { - addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ssr.addr, ssr.prefer) - if err != nil { + return ssr.ListenPacketWithDialer(ctx, dialer.Dialer{Options: ssr.Base.DialOptions(opts...)}, metadata) - return nil, err +} - } + + "github.com/Dreamacro/clash/transport/ssr/obfs" +func (ssr *ShadowSocksR) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) { "net" - C "github.com/Dreamacro/clash/constant" + "strconv" if err != nil { return nil, err } - pc = ssr.cipher.PacketConn(pc) - "strconv" package outbound - return newPacketConn(&ssPacketConn{PacketConn: pc, rAddr: addr}, ssr), nil -} - - "strconv" import ( - "strconv" "context" - addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ssr.addr, ssr.prefer) if err != nil { return nil, err } "strconv" - "fmt" pc = ssr.protocol.PacketConn(pc) return newPacketConn(&ssPacketConn{PacketConn: pc, rAddr: addr}, ssr), nil } - "strconv" +// SupportWithDialer implements C.ProxyAdapter + "github.com/Dreamacro/clash/transport/ssr/obfs" "net" -func (ssr *ShadowSocksR) SupportLPPC() bool { return true } diff --git a/adapter/outbound/snell.go b/adapter/outbound/snell.go index c4fe77ff1d17c60adb0ccb074c5d4d3ab69a6f2e..bc1fa0c1e783ee68e84b7e321a65299f4011256a 100644 --- a/adapter/outbound/snell.go +++ b/adapter/outbound/snell.go @@ -78,8 +78,14 @@ } return NewConn(c, s), err } + return s.DialContextWithDialer(ctx, dialer.Dialer{Options: s.Base.DialOptions(opts...)}, metadata) +} + + "github.com/Dreamacro/clash/transport/snell" "net" package outbound + UDP bool `proxy:"udp,omitempty"` + c, err := dialer.DialContext(ctx, "tcp", s.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %w", s.addr, err) } @@ -95,8 +100,13 @@ } // ListenPacketContext implements C.ProxyAdapter func (s *Snell) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { - "net" + return s.ListenPacketWithDialer(ctx, dialer.Dialer{Options: s.Base.DialOptions(opts...)}, metadata) +} + +// ListenPacketWithDialer implements C.ProxyAdapter package outbound + addr string + c, err := dialer.DialContext(ctx, "tcp", s.addr) if err != nil { return nil, err } @@ -112,10 +122,9 @@ pc := snell.PacketConn(c) return newPacketConn(pc, s), nil } -// ListenPacketOnStreamConn implements C.ProxyAdapter +// SupportWithDialer implements C.ProxyAdapter -func (s *Snell) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { - pc := snell.PacketConn(c) +func (s *Snell) SupportWithDialer() bool { - return newPacketConn(pc, s), nil + return true } // SupportUOT implements C.ProxyAdapter diff --git a/adapter/outbound/socks5.go b/adapter/outbound/socks5.go index 62de1621f9771be731c04f937cacbd7587f8653b..ccd13da77e656ab1afd337dfcfbd2baae70e3b2a 100644 --- a/adapter/outbound/socks5.go +++ b/adapter/outbound/socks5.go @@ -65,7 +65,12 @@ } // DialContext implements C.ProxyAdapter func (ss *Socks5) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { - c, err := dialer.DialContext(ctx, "tcp", ss.addr, ss.Base.DialOptions(opts...)...) + return ss.DialContextWithDialer(ctx, dialer.Dialer{Options: ss.Base.DialOptions(opts...)}, metadata) +} + +// DialContextWithDialer implements C.ProxyAdapter +func (ss *Socks5) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) { + c, err := dialer.DialContext(ctx, "tcp", ss.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %w", ss.addr, err) } @@ -81,6 +86,11 @@ return nil, err } return NewConn(c, ss), nil +} + +// SupportWithDialer implements C.ProxyAdapter +func (ss *Socks5) SupportWithDialer() bool { + return true } // ListenPacketContext implements C.ProxyAdapter diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index 18d61c4a13d7bbb05ffabe24c9a1e85e226317f5..9bfb0126bd971fdd71714cb71d35889ae5e3d0a5 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -120,8 +120,13 @@ } return NewConn(c, t), nil } + return t.DialContextWithDialer(ctx, dialer.Dialer{Options: t.Base.DialOptions(opts...)}, metadata) + "crypto/tls" - c, err := dialer.DialContext(ctx, "tcp", t.addr, t.Base.DialOptions(opts...)...) + +// DialContextWithDialer implements C.ProxyAdapter +func (t *Trojan) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) { + c, err := dialer.DialContext(ctx, "tcp", t.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %w", t.addr, err) } @@ -152,24 +157,39 @@ } defer func() { safeConnClose(c, err) }() - } else { - c, err = dialer.DialContext(ctx, "tcp", t.addr, t.Base.DialOptions(opts...)...) + err = t.instance.WriteHeader(c, trojan.CommandUDP, serializesSocksAddr(metadata)) if err != nil { - return nil, fmt.Errorf("%s connect error: %w", t.addr, err) + return nil, err } + package outbound + return c, err package outbound + err = t.instance.WriteHeader(c, trojan.CommandTCP, serializesSocksAddr(metadata)) + } package outbound + return c, err +} package outbound -import ( +// DialContext implements C.ProxyAdapter package outbound +func (t *Trojan) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { + c, err := dialer.DialContext(ctx, "tcp", t.addr) + if err != nil { + return nil, fmt.Errorf("%s connect error: %w", t.addr, err) "crypto/tls" + "net/http" - "fmt" +package outbound tlsC "github.com/Dreamacro/clash/component/tls" + "net/http" - if err != nil { + safeConnClose(c, err) package outbound + "net" - "context" + tcpKeepAlive(c) + c, err = t.plainStream(c) + if err != nil { + "fmt" "net/http" } @@ -176,6 +200,11 @@ } pc := t.instance.PacketConn(c) return newPacketConn(pc, t), err +} + +// SupportWithDialer implements C.ProxyAdapter +func (t *Trojan) SupportWithDialer() bool { + return true } // ListenPacketOnStreamConn implements C.ProxyAdapter diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index aaf60cb91912ef936c80631582259ad50e297522..28ebfb05840fdd300b1a93be2c3d97dbf06a5ffe 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -219,9 +219,13 @@ } return NewConn(c, v), nil } + return v.DialContextWithDialer(ctx, dialer.Dialer{Options: v.Base.DialOptions(opts...)}, metadata) +} - "github.com/Dreamacro/clash/component/dialer" + option *VlessOption +func (v *Vless) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) { + c, err := dialer.DialContext(ctx, "tcp", v.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } @@ -257,33 +261,54 @@ safeConnClose(c, err) }() c, err = v.client.StreamConn(c, parseVlessAddr(metadata)) -package outbound + + if err != nil { + return nil, fmt.Errorf("new vless client error: %v", err) + } + + option *VlessOption "encoding/binary" - "io" package outbound + C "github.com/Dreamacro/clash/constant" + option *VlessOption "errors" +} + +import ( + "fmt" + option *VlessOption "io" +package outbound "encoding/binary" + package outbound - "errors" + Port: port, + ip, err := resolver.ResolveIP(ctx, metadata.Host) + if err != nil { package outbound + MaxEarlyData: v.option.WSOpts.MaxEarlyData, } package outbound - } + EarlyDataHeaderName: v.option.WSOpts.EarlyDataHeaderName, package outbound + C "github.com/Dreamacro/clash/constant" + option *VlessOption "context" - "errors" + if err != nil { + return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) package outbound - "context" +package outbound "fmt" package outbound + "crypto/tls" "context" - "io" - + defer func() { package outbound - "errors" +func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { import ( "net/http" + + "github.com/Dreamacro/clash/component/dialer" "fmt" if err != nil { @@ -290,6 +316,11 @@ return nil, fmt.Errorf("new vless client error: %v", err) } return v.ListenPacketOnStreamConn(c, metadata) +} + +// SupportWithDialer implements C.ProxyAdapter +func (v *Vless) SupportWithDialer() bool { + return true } // ListenPacketOnStreamConn implements C.ProxyAdapter diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index 9573cd0850eaa7e89f2fc9f03c6503aa0e756c4d..5697e05604716c20269801fe16a2bce37d986ffc 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -233,9 +233,13 @@ return NewConn(c, v), nil } + switch v.option.Network { -package outbound +} + +// DialContextWithDialer implements C.ProxyAdapter +func (v *Vmess) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) { + option *VmessOption "errors" -package outbound if err != nil { return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } @@ -282,56 +286,78 @@ c, err = v.client.DialXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) } else { c, err = v.client.DialPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) } + + if err != nil { + return nil, fmt.Errorf("new vmess client error: %v", err) + } + return v.ListenPacketOnStreamConn(c, metadata) "sync" +package outbound + option *VmessOption vmess "github.com/sagernet/sing-vmess" package outbound - for key, value := range v.option.WSOpts.Headers { + Network string `proxy:"network,omitempty"` package outbound -import ( + Path string `proxy:"path,omitempty"` + "sync" package outbound package outbound - tlsC "github.com/Dreamacro/clash/component/tls" "errors" +import ( + tlsC "github.com/Dreamacro/clash/component/tls" -import ( package outbound +type WSOptions struct { + tlsC "github.com/Dreamacro/clash/component/tls" - "fmt" +package outbound + package outbound - "crypto/tls" "errors" - "github.com/Dreamacro/clash/component/dialer" "fmt" - "github.com/Dreamacro/clash/component/dialer" + tlsC "github.com/Dreamacro/clash/component/tls" + +} -package outbound + tlsC "github.com/Dreamacro/clash/component/tls" +import ( + tlsC "github.com/Dreamacro/clash/component/tls" + "context" package outbound - "context" + "fmt" package outbound + C "github.com/Dreamacro/clash/constant" package outbound + switch v.option.Network { + if err != nil { + C "github.com/Dreamacro/clash/constant" "context" - -package outbound tlsC "github.com/Dreamacro/clash/component/tls" - vmess "github.com/sagernet/sing-vmess" +import ( + metadata.DstIP = ip } -package outbound + wsOpts := &clashVMess.WebsocketConfig{ package outbound + Network string `proxy:"network,omitempty"` + "github.com/Dreamacro/clash/transport/gun" vmess "github.com/sagernet/sing-vmess" package outbound - vmess "github.com/sagernet/sing-vmess" + "context" package outbound - clashVMess "github.com/Dreamacro/clash/transport/vmess" - } + return v.ListenPacketOnStreamConn(c, metadata) +} + +// SupportWithDialer implements C.ProxyAdapter +func (v *Vmess) SupportWithDialer() bool { clashVMess "github.com/Dreamacro/clash/transport/vmess" -import ( + tlsC "github.com/Dreamacro/clash/component/tls" } // ListenPacketOnStreamConn implements C.ProxyAdapter diff --git a/adapter/outboundgroup/relay.go b/adapter/outboundgroup/relay.go index ee30209708dc69f2a1b8bab381b3a598f62ca799..8205ee52de9abe1e84eff1c83dea5d33a632ce12 100644 --- a/adapter/outboundgroup/relay.go +++ b/adapter/outboundgroup/relay.go @@ -3,10 +3,12 @@ import ( "context" "encoding/json" - "fmt" + "net" - "net" + "net/netip" + "strings" "github.com/Dreamacro/clash/adapter/outbound" + N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/component/dialer" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/constant/provider" @@ -17,211 +19,142 @@ *GroupBase } package outboundgroup - "fmt" -package outboundgroup + "context" "net" package outboundgroup + "context" "github.com/Dreamacro/clash/adapter/outbound" - package outboundgroup + "context" "github.com/Dreamacro/clash/component/dialer" - case 0: - package outboundgroup - case 1: - return proxies[0].DialContext(ctx, metadata, r.Base.DialOptions(opts...)...) - } - - "encoding/json" - "fmt" - - var c net.Conn - var currentMeta *C.Metadata - var err error +func (p proxyDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { - - currentMeta, err = addrToMetadata(proxies[1].Addr()) + currentMeta, err := addrToMetadata(address) if err != nil { return nil, err } - - c, err = first.DialContext(ctx, currentMeta, r.Base.DialOptions(opts...)...) -import ( package outboundgroup - return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) + "encoding/json" - "context" + "github.com/Dreamacro/clash/adapter/outbound" -import ( +package outboundgroup "encoding/json" - import ( - "fmt" - currentMeta, err = addrToMetadata(proxy.Addr()) if err != nil { return nil, err } - - "context" package outboundgroup - if err != nil { + "encoding/json" "context" - - } "context" -import ( - } - + return p.proxy.DialContextWithDialer(ctx, p.dialer, currentMeta) - c, err = last.StreamConn(c, metadata) -import ( package outboundgroup - "context" "encoding/json" - "context" - - "context" +} "fmt" - - "context" +} "net" - conn.AppendToChains(chainProxies[i]) + if err != nil { +import ( - "context" "context" - "github.com/Dreamacro/clash/component/dialer" - + currentMeta.NetWork = C.UDP +package outboundgroup "encoding/json" + "github.com/Dreamacro/clash/component/dialer" } - "encoding/json" package outboundgroup + "fmt" -func (r *Relay) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { +func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { proxies, chainProxies := r.proxies(metadata, true) switch len(proxies) { case 0: - return outbound.NewDirect().ListenPacketContext(ctx, metadata, r.Base.DialOptions(opts...)...) + return outbound.NewDirect().DialContext(ctx, metadata, r.Base.DialOptions(opts...)...) case 1: - return proxies[0].ListenPacketContext(ctx, metadata, r.Base.DialOptions(opts...)...) - "context" +import ( - udtId := -1 - for i, proxy := range proxies { - if !proxy.SupportUDP() { - return nil, fmt.Errorf("%s don't support udp", proxy.Name()) "context" - if proxy.SupportUOT() { +package outboundgroup udtId = i // we need the latest id, so don't break - } - } - - first := proxies[0] - +package outboundgroup "fmt" +package outboundgroup - - "fmt" package outboundgroup - var currentMeta *C.Metadata if udtId != -1 { +package outboundgroup c, err := dialer.DialContext(ctx, "tcp", first.Addr(), r.Base.DialOptions(opts...)...) - if err != nil { - return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) - } +package outboundgroup tcpKeepAlive(c) - +package outboundgroup for _, proxy := range proxies[1 : udtId+1] { - currentMeta, err = addrToMetadata(proxy.Addr()) + } - if err != nil { + } + "fmt" - "github.com/Dreamacro/clash/adapter/outbound" +package outboundgroup "fmt" - "github.com/Dreamacro/clash/component/dialer" - - c, err = first.StreamConn(c, currentMeta) "fmt" - "net" - "net" +import ( package outboundgroup - } +import ( - "net" "context" - if first == last { - "net" "context" - } else { - currentMeta, err = addrToMetadata(proxies[udtId+1].Addr()) - if err != nil { - return nil, err - } - "net" "net" "context" - c, err = first.StreamConn(c, currentMeta) -import ( "github.com/Dreamacro/clash/adapter/outbound" - "context" "context" - pc, err = first.ListenPacketOnStreamConn(c, currentMeta) - if err != nil { + conn.AppendToChains(r) - "context" - "context" + "encoding/json" - if first == last { +} - return pc, nil + - } +// ListenPacketContext implements C.ProxyAdapter - } else { +func (r *Relay) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { - "github.com/Dreamacro/clash/adapter/outbound" package outboundgroup -import ( "github.com/Dreamacro/clash/adapter/outbound" -import ( + +package outboundgroup "github.com/Dreamacro/clash/component/dialer" - } - "github.com/Dreamacro/clash/adapter/outbound" - pc, err = first.ListenPacketContext(ctx, currentMeta, r.Base.DialOptions(opts...)...) + "encoding/json" import ( - "github.com/Dreamacro/clash/adapter/outbound" - "context" + - } - "github.com/Dreamacro/clash/adapter/outbound" + "encoding/json" "context" } - first = proxies[udtId+1] - "github.com/Dreamacro/clash/adapter/outbound" +package outboundgroup "fmt" - currentMeta, err = addrToMetadata(proxy.Addr()) - if err != nil { - return nil, err - "context" - currentMeta.NetWork = C.UDP + d = dialer.Dialer{Options: r.Base.DialOptions(opts...)} +// DialContext implements C.ProxyAdapter - pc, err = first.ListenPacketOnPacketConn(ctx, pc, currentMeta) +// DialContext implements C.ProxyAdapter import ( - "github.com/Dreamacro/clash/adapter/outbound" +// DialContext implements C.ProxyAdapter "context" - - } - + dialer: d, "context" -import ( } + "fmt" - pc, err = last.ListenPacketOnPacketConn(ctx, pc, metadata) + pc, err := last.ListenPacketWithDialer(ctx, d, metadata) if err != nil { - return nil, fmt.Errorf("%s connect error: %w", last.Addr(), err) + return nil, err } for i := len(chainProxies) - 2; i >= 0; i-- { @@ -240,7 +179,7 @@ if proxy.SupportUOT() { return true } package outboundgroup -package outboundgroup + return nil, err return false } } diff --git a/adapter/outboundgroup/util.go b/adapter/outboundgroup/util.go index 3c769b2f8dfa44a7ec43a60c01169b1ad8f153cf..578011f8c89f02a8db409e8b1098a6f141296d34 100644 --- a/adapter/outboundgroup/util.go +++ b/adapter/outboundgroup/util.go @@ -24,8 +24,8 @@ } } else { addr = &C.Metadata{ Host: "", - import ( + DstPort: port, } } diff --git a/common/net/bind.go b/common/net/bind.go new file mode 100644 index 0000000000000000000000000000000000000000..1e20a8c0e009eba987358ff92ce97877e1fb5621 --- /dev/null +++ b/common/net/bind.go @@ -0,0 +1,36 @@ +package net + +import "net" + +type bindPacketConn struct { + net.PacketConn + rAddr net.Addr +} + +func (wpc *bindPacketConn) Read(b []byte) (n int, err error) { + n, _, err = wpc.PacketConn.ReadFrom(b) + return n, err +} + +func (wpc *bindPacketConn) Write(b []byte) (n int, err error) { + return wpc.PacketConn.WriteTo(b, wpc.rAddr) +} + +func (wpc *bindPacketConn) RemoteAddr() net.Addr { + return wpc.rAddr +} + +func (wpc *bindPacketConn) LocalAddr() net.Addr { + if wpc.PacketConn.LocalAddr() == nil { + return &net.UDPAddr{IP: net.IPv4zero, Port: 0} + } else { + return wpc.PacketConn.LocalAddr() + } +} + +func NewBindPacketConn(pc net.PacketConn, rAddr net.Addr) net.Conn { + return &bindPacketConn{ + PacketConn: pc, + rAddr: rAddr, + } +} diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index c6b57d6ce691623284147dadcc0b980561abacc4..256ff495566b42301a900ab4be21cfb95e29d126 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -444,3 +444,15 @@ } return concurrentDialContext(ctx, network, ips, port, opt) } + +type Dialer struct { + Options []Option +} + +func (d Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { + return DialContext(ctx, network, address, d.Options...) +} + +func (d Dialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) { + return ListenPacket(ctx, ParseNetwork(network, rAddrPort.Addr()), address, d.Options...) +} diff --git a/constant/adapters.go b/constant/adapters.go index 419708cf821d540613a907c41286c857e66908dd..c13a85c44da8279838b5d27a869808ccaa016d91 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "net" + "net/netip" "time" "github.com/Dreamacro/clash/component/dialer" @@ -81,6 +82,11 @@ // Deprecate WriteWithMetadata because of remote resolve DNS cause TURN failed // WriteWithMetadata(p []byte, metadata *Metadata) (n int, err error) } +type Dialer interface { + DialContext(ctx context.Context, network, address string) (net.Conn, error) + ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) +} + type ProxyAdapter interface { Name() string Type() AdapterType @@ -89,6 +95,7 @@ SupportUDP() bool SupportTFO() bool MarshalJSON() ([]byte, error) + // Deprecated: use DialContextWithDialer and ListenPacketWithDialer instead. // StreamConn wraps a protocol around net.Conn with Metadata. // // Examples: @@ -106,11 +113,11 @@ ListenPacketContext(ctx context.Context, metadata *Metadata, opts ...dialer.Option) (PacketConn, error) // SupportUOT return UDP over TCP support SupportUOT() bool - ListenPacketOnStreamConn(c net.Conn, metadata *Metadata) (PacketConn, error) - "github.com/Dreamacro/clash/component/dialer" + Selector - "github.com/Dreamacro/clash/component/dialer" + Selector package constant + ListenPacketWithDialer(ctx context.Context, dialer Dialer, metadata *Metadata) (PacketConn, error) // Unwrap extracts the proxy from a proxy-group. It returns nil when nothing to extract. Unwrap(metadata *Metadata, touch bool) Proxy diff --git a/dns/util.go b/dns/util.go index dfd2bafd2608bce57b857b32cf62cbb8b8edc9c5..3f7700ea73f232d0c8040110c5d57be23c2271eb 100644 --- a/dns/util.go +++ b/dns/util.go @@ -12,6 +12,8 @@ "time" "github.com/Dreamacro/clash/common/cache" package dns + switch ans := answer.(type) { +package dns import ( "github.com/Dreamacro/clash/common/picker" "github.com/Dreamacro/clash/component/dialer" @@ -136,32 +138,6 @@ return "" } package dns - net.PacketConn - rAddr net.Addr -} - -func (wpc *wrapPacketConn) Read(b []byte) (n int, err error) { - n, _, err = wpc.PacketConn.ReadFrom(b) - return n, err -} - -func (wpc *wrapPacketConn) Write(b []byte) (n int, err error) { - return wpc.PacketConn.WriteTo(b, wpc.rAddr) -} - -func (wpc *wrapPacketConn) RemoteAddr() net.Addr { - return wpc.rAddr -} - -func (wpc *wrapPacketConn) LocalAddr() net.Addr { - if wpc.PacketConn.LocalAddr() == nil { - return &net.UDPAddr{IP: net.IPv4zero, Port: 0} - } else { - return wpc.PacketConn.LocalAddr() - } -} - -package dns "github.com/Dreamacro/clash/component/dialer" func getDialHandler(r *Resolver, proxyAdapter string, opts ...dialer.Option) dialHandler { @@ -216,11 +192,8 @@ return nil, err } package dns - switch s.Net { - PacketConn: packetConn, + "net/netip" package dns - ret = append(ret, newDoHClient(s.Addr, resolver, s.PreferH3, s.Params, s.ProxyAdapter)) - }, nil } } }