feat: Expose remote destination (udp proxy maybe domain of node)

This commit is contained in:
Skyxim 2022-05-04 16:57:08 +08:00
parent bdfa16ca6f
commit fb58595d44
9 changed files with 66 additions and 18 deletions

View file

@ -13,8 +13,6 @@ import (
"github.com/Dreamacro/clash/transport/gun" "github.com/Dreamacro/clash/transport/gun"
"github.com/Dreamacro/clash/transport/trojan" "github.com/Dreamacro/clash/transport/trojan"
"github.com/Dreamacro/clash/transport/vless" "github.com/Dreamacro/clash/transport/vless"
"golang.org/x/net/http2"
) )
type Trojan struct { type Trojan struct {
@ -25,7 +23,7 @@ type Trojan struct {
// for gun mux // for gun mux
gunTLSConfig *tls.Config gunTLSConfig *tls.Config
gunConfig *gun.Config gunConfig *gun.Config
transport *http2.Transport transport *gun.TransportWrap
} }
type TrojanOption struct { type TrojanOption struct {

View file

@ -18,8 +18,6 @@ import (
"github.com/Dreamacro/clash/transport/gun" "github.com/Dreamacro/clash/transport/gun"
"github.com/Dreamacro/clash/transport/vless" "github.com/Dreamacro/clash/transport/vless"
"github.com/Dreamacro/clash/transport/vmess" "github.com/Dreamacro/clash/transport/vmess"
"golang.org/x/net/http2"
) )
const ( const (
@ -35,7 +33,7 @@ type Vless struct {
// for gun mux // for gun mux
gunTLSConfig *tls.Config gunTLSConfig *tls.Config
gunConfig *gun.Config gunConfig *gun.Config
transport *http2.Transport transport *gun.TransportWrap
} }
type VlessOption struct { type VlessOption struct {

View file

@ -15,8 +15,6 @@ import (
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/gun" "github.com/Dreamacro/clash/transport/gun"
"github.com/Dreamacro/clash/transport/vmess" "github.com/Dreamacro/clash/transport/vmess"
"golang.org/x/net/http2"
) )
type Vmess struct { type Vmess struct {
@ -27,7 +25,7 @@ type Vmess struct {
// for gun mux // for gun mux
gunTLSConfig *tls.Config gunTLSConfig *tls.Config
gunConfig *gun.Config gunConfig *gun.Config
transport *http2.Transport transport *gun.TransportWrap
} }
type VmessOption struct { type VmessOption struct {

View file

@ -170,6 +170,11 @@ func (r *Relay) proxies(metadata *C.Metadata, touch bool) ([]C.Proxy, []C.Proxy)
return targetProxies, chainProxies return targetProxies, chainProxies
} }
func (r *Relay) Addr() string {
proxies, _ := r.proxies(nil, true)
return proxies[len(proxies)-1].Addr()
}
func NewRelay(option *GroupCommonOption, providers []provider.ProxyProvider) *Relay { func NewRelay(option *GroupCommonOption, providers []provider.ProxyProvider) *Relay {
return &Relay{ return &Relay{
GroupBase: NewGroupBase(GroupBaseOption{ GroupBase: NewGroupBase(GroupBaseOption{

View file

@ -86,8 +86,12 @@ type Metadata struct {
Uid *int32 `json:"uid"` Uid *int32 `json:"uid"`
Process string `json:"process"` Process string `json:"process"`
ProcessPath string `json:"processPath"` ProcessPath string `json:"processPath"`
RemoteDst string `json:"remoteDestination"`
} }
// avoid stack overflow
type jsonMetadata Metadata
func (m *Metadata) RemoteAddress() string { func (m *Metadata) RemoteAddress() string {
return net.JoinHostPort(m.String(), m.DstPort) return net.JoinHostPort(m.String(), m.DstPort)
} }

View file

@ -39,14 +39,13 @@ type DialFn = func(network, addr string) (net.Conn, error)
type Conn struct { type Conn struct {
response *http.Response response *http.Response
request *http.Request request *http.Request
transport *http2.Transport transport *TransportWrap
writer *io.PipeWriter writer *io.PipeWriter
once sync.Once once sync.Once
close *atomic.Bool close *atomic.Bool
err error err error
remain int remain int
br *bufio.Reader br *bufio.Reader
// deadlines // deadlines
deadline *time.Timer deadline *time.Timer
} }
@ -150,8 +149,8 @@ func (g *Conn) Close() error {
return g.writer.Close() return g.writer.Close()
} }
func (g *Conn) LocalAddr() net.Addr { return &net.TCPAddr{IP: net.IPv4zero, Port: 0} } func (g *Conn) LocalAddr() net.Addr { return g.transport.LocalAddr() }
func (g *Conn) RemoteAddr() net.Addr { return &net.TCPAddr{IP: net.IPv4zero, Port: 0} } func (g *Conn) RemoteAddr() net.Addr { return g.transport.RemoteAddr() }
func (g *Conn) SetReadDeadline(t time.Time) error { return g.SetDeadline(t) } func (g *Conn) SetReadDeadline(t time.Time) error { return g.SetDeadline(t) }
func (g *Conn) SetWriteDeadline(t time.Time) error { return g.SetDeadline(t) } func (g *Conn) SetWriteDeadline(t time.Time) error { return g.SetDeadline(t) }
@ -167,13 +166,15 @@ func (g *Conn) SetDeadline(t time.Time) error {
return nil return nil
} }
func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config) *http2.Transport { func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config) *TransportWrap {
wrap := TransportWrap{}
dialFunc := func(network, addr string, cfg *tls.Config) (net.Conn, error) { dialFunc := func(network, addr string, cfg *tls.Config) (net.Conn, error) {
pconn, err := dialFn(network, addr) pconn, err := dialFn(network, addr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
wrap.remoteAddr = pconn.RemoteAddr()
cn := tls.Client(pconn, cfg) cn := tls.Client(pconn, cfg)
// fix tls handshake not timeout // fix tls handshake not timeout
@ -191,16 +192,18 @@ func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config) *http2.Transport {
return cn, nil return cn, nil
} }
return &http2.Transport{ wrap.Transport = &http2.Transport{
DialTLS: dialFunc, DialTLS: dialFunc,
TLSClientConfig: tlsConfig, TLSClientConfig: tlsConfig,
AllowHTTP: false, AllowHTTP: false,
DisableCompression: true, DisableCompression: true,
PingTimeout: 0, PingTimeout: 0,
} }
return &wrap
} }
func StreamGunWithTransport(transport *http2.Transport, cfg *Config) (net.Conn, error) { func StreamGunWithTransport(transport *TransportWrap, cfg *Config) (net.Conn, error) {
serviceName := "GunService" serviceName := "GunService"
if cfg.ServiceName != "" { if cfg.ServiceName != "" {
serviceName = cfg.ServiceName serviceName = cfg.ServiceName

View file

@ -12,13 +12,15 @@ import (
"golang.org/x/net/http2" "golang.org/x/net/http2"
) )
func NewHTTP2XTLSClient(dialFn DialFn, tlsConfig *tls.Config) *http2.Transport { func NewHTTP2XTLSClient(dialFn DialFn, tlsConfig *tls.Config) *TransportWrap {
wrap := TransportWrap{}
dialFunc := func(network, addr string, cfg *tls.Config) (net.Conn, error) { dialFunc := func(network, addr string, cfg *tls.Config) (net.Conn, error) {
pconn, err := dialFn(network, addr) pconn, err := dialFn(network, addr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
wrap.remoteAddr = pconn.RemoteAddr()
xtlsConfig := &xtls.Config{ xtlsConfig := &xtls.Config{
InsecureSkipVerify: cfg.InsecureSkipVerify, InsecureSkipVerify: cfg.InsecureSkipVerify,
ServerName: cfg.ServerName, ServerName: cfg.ServerName,
@ -37,13 +39,15 @@ func NewHTTP2XTLSClient(dialFn DialFn, tlsConfig *tls.Config) *http2.Transport {
return cn, nil return cn, nil
} }
return &http2.Transport{ wrap.Transport = &http2.Transport{
DialTLS: dialFunc, DialTLS: dialFunc,
TLSClientConfig: tlsConfig, TLSClientConfig: tlsConfig,
AllowHTTP: false, AllowHTTP: false,
DisableCompression: true, DisableCompression: true,
PingTimeout: 0, PingTimeout: 0,
} }
return &wrap
} }
func StreamGunWithXTLSConn(conn net.Conn, tlsConfig *tls.Config, cfg *Config) (net.Conn, error) { func StreamGunWithXTLSConn(conn net.Conn, tlsConfig *tls.Config, cfg *Config) (net.Conn, error) {

View file

@ -0,0 +1,20 @@
package gun
import (
"golang.org/x/net/http2"
"net"
)
type TransportWrap struct {
*http2.Transport
remoteAddr net.Addr
localAddr net.Addr
}
func (tw *TransportWrap) RemoteAddr() net.Addr {
return tw.remoteAddr
}
func (tw *TransportWrap) LocalAddr() net.Addr {
return tw.localAddr
}

View file

@ -9,6 +9,7 @@ import (
"path/filepath" "path/filepath"
"runtime" "runtime"
"strconv" "strconv"
"strings"
"sync" "sync"
"time" "time"
@ -269,6 +270,18 @@ func handleUDPConn(packet *inbound.PacketAdapter) {
return return
} }
pCtx.InjectPacketConn(rawPc) pCtx.InjectPacketConn(rawPc)
actualProxy := proxy.Unwrap(nil)
if actualProxy != nil {
if dst, _, err := net.SplitHostPort(actualProxy.Addr()); err == nil {
metadata.RemoteDst = dst
} else {
if addrError, ok := err.(*net.AddrError); ok && strings.Contains(addrError.Err, "missing port") {
metadata.RemoteDst = actualProxy.Addr()
}
}
}
pc := statistic.NewUDPTracker(rawPc, statistic.DefaultManager, metadata, rule) pc := statistic.NewUDPTracker(rawPc, statistic.DefaultManager, metadata, rule)
switch true { switch true {
@ -332,6 +345,11 @@ func handleTCPConn(connCtx C.ConnContext) {
} }
return return
} }
if tcpAddr, ok := remoteConn.RemoteAddr().(*net.TCPAddr); ok {
metadata.RemoteDst = tcpAddr.IP.String()
}
remoteConn = statistic.NewTCPTracker(remoteConn, statistic.DefaultManager, metadata, rule) remoteConn = statistic.NewTCPTracker(remoteConn, statistic.DefaultManager, metadata, rule)
defer func(remoteConn C.Conn) { defer func(remoteConn C.Conn) {
_ = remoteConn.Close() _ = remoteConn.Close()