瀏覽代碼

Add DestIpAddress() in Dialer interface

Android client prepares an IP before proxy connection is established. It is useful when connecting to wireguard (or quic) outbound with domain address. E.g. engage.cloudflareclient.com:2408
yuhan6665 1 年之前
父節點
當前提交
d60281d0a5
共有 4 個文件被更改,包括 91 次插入3 次删除
  1. 4 0
      app/proxyman/outbound/handler.go
  2. 70 3
      proxy/wireguard/client.go
  3. 8 0
      transport/internet/dialer.go
  4. 9 0
      transport/internet/system_dialer.go

+ 4 - 0
app/proxyman/outbound/handler.go

@@ -229,6 +229,10 @@ func (h *Handler) Address() net.Address {
 	return h.senderSettings.Via.AsAddress()
 }
 
+func (h *Handler) DestIpAddress() net.IP {
+	return internet.DestIpAddress()
+}
+
 // Dial implements internet.Dialer.
 func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connection, error) {
 	if h.senderSettings != nil {

+ 70 - 3
proxy/wireguard/client.go

@@ -22,7 +22,9 @@ package wireguard
 
 import (
 	"context"
+	"fmt"
 	"net/netip"
+	"strings"
 	"sync"
 
 	"github.com/xtls/xray-core/common"
@@ -49,7 +51,6 @@ type Handler struct {
 	policyManager policy.Manager
 	dns           dns.Client
 	// cached configuration
-	ipc              string
 	endpoints        []netip.Addr
 	hasIPv4, hasIPv6 bool
 	wgLock           sync.Mutex
@@ -69,7 +70,6 @@ func New(ctx context.Context, conf *DeviceConfig) (*Handler, error) {
 		conf:          conf,
 		policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
 		dns:           d,
-		ipc:           createIPCRequest(conf),
 		endpoints:     endpoints,
 		hasIPv4:       hasIPv4,
 		hasIPv6:       hasIPv6,
@@ -247,9 +247,76 @@ func (h *Handler) makeVirtualTun(bind *netBindClient) (Tunnel, error) {
 	bind.dnsOption.IPv4Enable = h.hasIPv4
 	bind.dnsOption.IPv6Enable = h.hasIPv6
 
-	if err = t.BuildDevice(h.ipc, bind); err != nil {
+	if err = t.BuildDevice(h.createIPCRequest(bind, h.conf), bind); err != nil {
 		_ = t.Close()
 		return nil, err
 	}
 	return t, nil
 }
+
+
+// serialize the config into an IPC request
+func (h *Handler) createIPCRequest(bind *netBindClient, conf *DeviceConfig) string {
+	var request strings.Builder
+
+	request.WriteString(fmt.Sprintf("private_key=%s\n", conf.SecretKey))
+
+	if !conf.IsClient {
+		// placeholder, we'll handle actual port listening on Xray
+		request.WriteString("listen_port=1337\n")
+	}
+
+	for _, peer := range conf.Peers {
+		if peer.PublicKey != "" {
+			request.WriteString(fmt.Sprintf("public_key=%s\n", peer.PublicKey))
+		}
+
+		if peer.PreSharedKey != "" {
+			request.WriteString(fmt.Sprintf("preshared_key=%s\n", peer.PreSharedKey))
+		}
+
+		split := strings.Split(peer.Endpoint, ":")
+		addr := net.ParseAddress(split[0])
+		if addr.Family().IsDomain() {
+			dialerIp := bind.dialer.DestIpAddress()
+			if dialerIp != nil {
+				addr = net.ParseAddress(dialerIp.String())
+				newError("createIPCRequest use dialer dest ip: ", addr).WriteToLog()
+			} else {
+				ips, err := h.dns.LookupIP(addr.Domain(), dns.IPOption{
+					IPv4Enable: h.hasIPv4 && h.conf.preferIP4(),
+					IPv6Enable: h.hasIPv6 && h.conf.preferIP6(),
+				})
+				{ // Resolve fallback
+					if (len(ips) == 0 || err != nil) && h.conf.hasFallback() {
+						ips, err = h.dns.LookupIP(addr.Domain(), dns.IPOption{
+							IPv4Enable: h.hasIPv4 && h.conf.fallbackIP4(),
+							IPv6Enable: h.hasIPv6 && h.conf.fallbackIP6(),
+						})
+					}
+				}
+				if err != nil {
+					newError("createIPCRequest failed to lookup DNS").Base(err).WriteToLog()
+				} else if len(ips) == 0 {
+					newError("createIPCRequest empty lookup DNS").WriteToLog()
+				} else {
+					addr = net.IPAddress(ips[dice.Roll(len(ips))])
+				}
+			}
+		}
+
+		if peer.Endpoint != "" {
+			request.WriteString(fmt.Sprintf("endpoint=%s:%s\n", addr, split[1]))
+		}
+
+		for _, ip := range peer.AllowedIps {
+			request.WriteString(fmt.Sprintf("allowed_ip=%s\n", ip))
+		}
+
+		if peer.KeepAlive != 0 {
+			request.WriteString(fmt.Sprintf("persistent_keepalive_interval=%d\n", peer.KeepAlive))
+		}
+	}
+
+	return request.String()[:request.Len()]
+}

+ 8 - 0
transport/internet/dialer.go

@@ -22,6 +22,9 @@ type Dialer interface {
 
 	// Address returns the address used by this Dialer. Maybe nil if not known.
 	Address() net.Address
+
+	// DestIpAddress returns the ip of proxy server. It is useful in case of Android client, which prepare an IP before proxy connection is established
+	DestIpAddress() net.IP
 }
 
 // dialFunc is an interface to dial network connection to a specific destination.
@@ -68,6 +71,11 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *MemoryStrea
 	return nil, newError("unknown network ", dest.Network)
 }
 
+// DestIpAddress returns the ip of proxy server. It is useful in case of Android client, which prepare an IP before proxy connection is established
+func DestIpAddress() net.IP {
+	return effectiveSystemDialer.DestIpAddress()
+}
+
 var (
 	dnsClient dns.Client
 	obm       outbound.Manager

+ 9 - 0
transport/internet/system_dialer.go

@@ -16,6 +16,7 @@ var effectiveSystemDialer SystemDialer = &DefaultSystemDialer{}
 
 type SystemDialer interface {
 	Dial(ctx context.Context, source net.Address, destination net.Destination, sockopt *SocketConfig) (net.Conn, error)
+	DestIpAddress() net.IP
 }
 
 type DefaultSystemDialer struct {
@@ -108,6 +109,10 @@ func (d *DefaultSystemDialer) Dial(ctx context.Context, src net.Address, dest ne
 	return dialer.DialContext(ctx, dest.Network.SystemString(), dest.NetAddr())
 }
 
+func (d *DefaultSystemDialer) DestIpAddress() net.IP {
+	return nil
+}
+
 type PacketConnWrapper struct {
 	Conn net.PacketConn
 	Dest net.Addr
@@ -172,6 +177,10 @@ func (v *SimpleSystemDialer) Dial(ctx context.Context, src net.Address, dest net
 	return v.adapter.Dial(dest.Network.SystemString(), dest.NetAddr())
 }
 
+func (d *SimpleSystemDialer) DestIpAddress() net.IP {
+	return nil
+}
+
 // UseAlternativeSystemDialer replaces the current system dialer with a given one.
 // Caller must ensure there is no race condition.
 //