feat: wireguard add dialer-proxy
config to support chain forwarding
This commit is contained in:
parent
1dbefc40c8
commit
87b9e3d977
5 changed files with 106 additions and 74 deletions
|
@ -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"
|
||||
|
@ -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"`
|
||||
}
|
||||
|
@ -65,16 +67,33 @@ type WireGuardPeerOption struct {
|
|||
|
||||
type wgSingDialer struct {
|
||||
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 {
|
||||
|
@ -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)
|
||||
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
78
component/proxydialer/proxydialer.go
Normal file
78
component/proxydialer/proxydialer.go
Normal file
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue