Skip to content

Commit

Permalink
feat: wireguard add dialer-proxy config to support chain forwarding
Browse files Browse the repository at this point in the history
  • Loading branch information
wwqgtxx committed Apr 10, 2023
1 parent 1dbefc4 commit 87b9e3d
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 74 deletions.
27 changes: 23 additions & 4 deletions adapter/outbound/wireguard.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (

CN "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/proxydialer"
"github.com/Dreamacro/clash/component/resolver"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
Expand Down Expand Up @@ -48,6 +49,7 @@ type WireGuardOption struct {
MTU int `proxy:"mtu,omitempty"`
UDP bool `proxy:"udp,omitempty"`
PersistentKeepalive int `proxy:"persistent-keepalive,omitempty"`
DialerProxy string `proxy:"dialer-proxy,omitempty"`

Peers []WireGuardPeerOption `proxy:"peers,omitempty"`
}
Expand All @@ -64,17 +66,34 @@ type WireGuardPeerOption struct {
}

type wgSingDialer struct {
dialer dialer.Dialer
dialer dialer.Dialer
proxyName string
}

var _ N.Dialer = (*wgSingDialer)(nil)

func (d *wgSingDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
return d.dialer.DialContext(ctx, network, destination.String())
var cDialer C.Dialer = d.dialer
if len(d.proxyName) > 0 {
pd, err := proxydialer.NewByName(d.proxyName, d.dialer)
if err != nil {
return nil, err
}
cDialer = pd
}
return cDialer.DialContext(ctx, network, destination.String())
}

func (d *wgSingDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
return d.dialer.ListenPacket(ctx, "udp", "", destination.AddrPort())
var cDialer C.Dialer = d.dialer
if len(d.proxyName) > 0 {
pd, err := proxydialer.NewByName(d.proxyName, d.dialer)
if err != nil {
return nil, err
}
cDialer = pd
}
return cDialer.ListenPacket(ctx, "udp", "", destination.AddrPort())
}

type wgNetDialer struct {
Expand Down Expand Up @@ -130,7 +149,7 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),
},
dialer: &wgSingDialer{dialer: dialer.NewDialer()},
dialer: &wgSingDialer{dialer: dialer.NewDialer(), proxyName: option.DialerProxy},
}
runtime.SetFinalizer(outbound, closeWireGuard)

Expand Down
46 changes: 3 additions & 43 deletions adapter/outboundgroup/relay.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,9 @@ package outboundgroup
import (
"context"
"encoding/json"
"net"
"net/netip"
"strings"

"github.com/Dreamacro/clash/adapter/outbound"
N "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/proxydialer"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/constant/provider"
)
Expand All @@ -18,36 +14,6 @@ type Relay struct {
*GroupBase
}

type proxyDialer struct {
proxy C.Proxy
dialer C.Dialer
}

func (p proxyDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
currentMeta, err := addrToMetadata(address)
if err != nil {
return nil, err
}
if strings.Contains(network, "udp") { // should not support this operation
currentMeta.NetWork = C.UDP
pc, err := p.proxy.ListenPacketWithDialer(ctx, p.dialer, currentMeta)
if err != nil {
return nil, err
}
return N.NewBindPacketConn(pc, currentMeta.UDPAddr()), nil
}
return p.proxy.DialContextWithDialer(ctx, p.dialer, currentMeta)
}

func (p proxyDialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) {
currentMeta, err := addrToMetadata(rAddrPort.String())
if err != nil {
return nil, err
}
currentMeta.NetWork = C.UDP
return p.proxy.ListenPacketWithDialer(ctx, p.dialer, currentMeta)
}

// DialContext implements C.ProxyAdapter
func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
proxies, chainProxies := r.proxies(metadata, true)
Expand All @@ -61,10 +27,7 @@ func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d
var d C.Dialer
d = dialer.NewDialer(r.Base.DialOptions(opts...)...)
for _, proxy := range proxies[:len(proxies)-1] {
d = proxyDialer{
proxy: proxy,
dialer: d,
}
d = proxydialer.New(proxy, d)
}
last := proxies[len(proxies)-1]
conn, err := last.DialContextWithDialer(ctx, d, metadata)
Expand Down Expand Up @@ -95,10 +58,7 @@ func (r *Relay) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
var d C.Dialer
d = dialer.NewDialer(r.Base.DialOptions(opts...)...)
for _, proxy := range proxies[:len(proxies)-1] {
d = proxyDialer{
proxy: proxy,
dialer: d,
}
d = proxydialer.New(proxy, d)
}
last := proxies[len(proxies)-1]
pc, err := last.ListenPacketWithDialer(ctx, d, metadata)
Expand Down
27 changes: 0 additions & 27 deletions adapter/outboundgroup/util.go
Original file line number Diff line number Diff line change
@@ -1,37 +1,10 @@
package outboundgroup

import (
"fmt"
"net"
"net/netip"
"time"

C "github.com/Dreamacro/clash/constant"
)

func addrToMetadata(rawAddress string) (addr *C.Metadata, err error) {
host, port, err := net.SplitHostPort(rawAddress)
if err != nil {
err = fmt.Errorf("addrToMetadata failed: %w", err)
return
}

if ip, err := netip.ParseAddr(host); err != nil {
addr = &C.Metadata{
Host: host,
DstPort: port,
}
} else {
addr = &C.Metadata{
Host: "",
DstIP: ip.Unmap(),
DstPort: port,
}
}

return
}

func tcpKeepAlive(c net.Conn) {
if tcp, ok := c.(*net.TCPConn); ok {
_ = tcp.SetKeepAlive(true)
Expand Down
78 changes: 78 additions & 0 deletions component/proxydialer/proxydialer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package proxydialer

import (
"context"
"fmt"
"net"
"net/netip"
"strings"

N "github.com/Dreamacro/clash/common/net"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/tunnel"
)

type proxyDialer struct {
proxy C.Proxy
dialer C.Dialer
}

func New(proxy C.Proxy, dialer C.Dialer) C.Dialer {
return proxyDialer{proxy: proxy, dialer: dialer}
}

func NewByName(proxyName string, dialer C.Dialer) (C.Dialer, error) {
proxies := tunnel.Proxies()
if proxy, ok := proxies[proxyName]; ok {
return New(proxy, dialer), nil
}
return nil, fmt.Errorf("proxyName[%s] not found", proxyName)
}

func (p proxyDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
currentMeta, err := addrToMetadata(address)
if err != nil {
return nil, err
}
if strings.Contains(network, "udp") { // using in wireguard outbound
currentMeta.NetWork = C.UDP
pc, err := p.proxy.ListenPacketWithDialer(ctx, p.dialer, currentMeta)
if err != nil {
return nil, err
}
return N.NewBindPacketConn(pc, currentMeta.UDPAddr()), nil
}
return p.proxy.DialContextWithDialer(ctx, p.dialer, currentMeta)
}

func (p proxyDialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) {
currentMeta, err := addrToMetadata(rAddrPort.String())
if err != nil {
return nil, err
}
currentMeta.NetWork = C.UDP
return p.proxy.ListenPacketWithDialer(ctx, p.dialer, currentMeta)
}

func addrToMetadata(rawAddress string) (addr *C.Metadata, err error) {
host, port, err := net.SplitHostPort(rawAddress)
if err != nil {
err = fmt.Errorf("addrToMetadata failed: %w", err)
return
}

if ip, err := netip.ParseAddr(host); err != nil {
addr = &C.Metadata{
Host: host,
DstPort: port,
}
} else {
addr = &C.Metadata{
Host: "",
DstIP: ip.Unmap(),
DstPort: port,
}
}

return
}
2 changes: 2 additions & 0 deletions docs/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,8 @@ proxies: # socks5
reserved: "U4An"
# 数组格式也是合法的
# reserved: [209,98,59]
# 一个出站代理的标识。当值不为空时,将使用指定的 proxy 发出连接
# dialer-proxy: "ss1"
# 如果peers不为空,该段落中的allowed_ips不可为空;前面段落的server,port,ip,ipv6,public-key,pre-shared-key均会被忽略,但private-key会被保留且只能在顶层指定
# peers:
# - server: 162.159.192.1
Expand Down

0 comments on commit 87b9e3d

Please sign in to comment.