feat: Expose remote destination (udp proxy maybe domain of node)
This commit is contained in:
parent
d149ba688f
commit
319884469d
9 changed files with 66 additions and 18 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
20
transport/gun/transport.go
Normal file
20
transport/gun/transport.go
Normal 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
|
||||||
|
}
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in a new issue