Browse Source

Added Transport Layer Chained Proxy Support

Shelikhoo 4 years ago
parent
commit
dd51d32250

+ 13 - 0
app/dispatcher/default.go

@@ -294,6 +294,19 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool) (Sni
 func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) {
 	var handler outbound.Handler
 
+	if forcedOutboundTag := session.GetForcedOutboundTagFromContext(ctx); forcedOutboundTag != "" {
+		session.SetForcedOutboundTagToContext(ctx, "")
+		if h := d.ohm.GetHandler(forcedOutboundTag); h != nil {
+			newError("taking platform initialized detour [", forcedOutboundTag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx))
+			handler = h
+		} else {
+			newError("non existing tag for platform initialized detour: ", forcedOutboundTag).AtError().WriteToLog(session.ExportIDToError(ctx))
+			common.Close(link.Writer)
+			common.Interrupt(link.Reader)
+			return
+		}
+	}
+
 	if d.router != nil {
 		if route, err := d.router.PickRoute(routing_session.AsRoutingContext(ctx)); err == nil {
 			tag := route.GetOutboundTag()

+ 7 - 1
app/proxyman/outbound/handler.go

@@ -159,7 +159,7 @@ func (h *Handler) Address() net.Address {
 // Dial implements internet.Dialer.
 func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Connection, error) {
 	if h.senderSettings != nil {
-		if h.senderSettings.ProxySettings.HasTag() {
+		if h.senderSettings.ProxySettings.HasTag() && !h.senderSettings.ProxySettings.TransportLayerProxy {
 			tag := h.senderSettings.ProxySettings.Tag
 			handler := h.outboundManager.GetHandler(tag)
 			if handler != nil {
@@ -196,6 +196,12 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Conn
 		}
 	}
 
+	if h.senderSettings.ProxySettings.HasTag() && h.senderSettings.ProxySettings.TransportLayerProxy {
+		tag := h.senderSettings.ProxySettings.Tag
+		newError("transport layer proxying to ", tag, " for dest ", dest).AtDebug().WriteToLog(session.ExportIDToError(ctx))
+		session.SetTransportLayerProxyTagToContext(ctx, tag)
+	}
+
 	conn, err := internet.Dial(ctx, dest, h.streamSettings)
 	return h.getStatCouterConnection(conn), err
 }

+ 16 - 0
common/session/context.go

@@ -84,3 +84,19 @@ func SockoptFromContext(ctx context.Context) *Sockopt {
 	}
 	return nil
 }
+
+func GetTransportLayerProxyTagFromContext(ctx context.Context) string {
+	return ContentFromContext(ctx).Attribute("transportLayerOutgoingTag")
+}
+
+func SetTransportLayerProxyTagToContext(ctx context.Context, tag string) {
+	ContentFromContext(ctx).SetAttribute("transportLayerOutgoingTag", tag)
+}
+
+func GetForcedOutboundTagFromContext(ctx context.Context) string {
+	return ContentFromContext(ctx).Attribute("forcedOutboundTag")
+}
+
+func SetForcedOutboundTagToContext(ctx context.Context, tag string) {
+	ContentFromContext(ctx).SetAttribute("forcedOutboundTag", tag)
+}

+ 4 - 2
infra/conf/transport_internet.go

@@ -485,7 +485,8 @@ func (c *StreamConfig) Build() (*internet.StreamConfig, error) {
 }
 
 type ProxyConfig struct {
-	Tag string `json:"tag"`
+	Tag                 string `json:"tag"`
+	TransportLayerProxy bool   `json:"transportLayer"`
 }
 
 // Build implements Buildable.
@@ -494,6 +495,7 @@ func (v *ProxyConfig) Build() (*internet.ProxyConfig, error) {
 		return nil, newError("Proxy tag is not set.")
 	}
 	return &internet.ProxyConfig{
-		Tag: v.Tag,
+		Tag:                 v.Tag,
+		TransportLayerProxy: v.TransportLayerProxy,
 	}, nil
 }

+ 34 - 0
transport/internet/dialer.go

@@ -2,6 +2,8 @@ package internet
 
 import (
 	"context"
+	core "github.com/v2fly/v2ray-core/v4"
+	"github.com/v2fly/v2ray-core/v4/features/routing"
 
 	"github.com/v2fly/v2ray-core/v4/common/net"
 	"github.com/v2fly/v2ray-core/v4/common/session"
@@ -68,5 +70,37 @@ func DialSystem(ctx context.Context, dest net.Destination, sockopt *SocketConfig
 	if outbound := session.OutboundFromContext(ctx); outbound != nil {
 		src = outbound.Gateway
 	}
+
+	if transportLayerOutgoingTag := session.GetTransportLayerProxyTagFromContext(ctx); transportLayerOutgoingTag != "" {
+		return DialTaggedOutbound(ctx, dest, transportLayerOutgoingTag)
+	}
+
 	return effectiveSystemDialer.Dial(ctx, src, dest, sockopt)
 }
+
+func DialTaggedOutbound(ctx context.Context, dest net.Destination, tag string) (net.Conn, error) {
+	var dispatcher routing.Dispatcher
+	if err := core.RequireFeatures(ctx, func(dispatcherInstance routing.Dispatcher) {
+		dispatcher = dispatcherInstance
+	}); err != nil {
+		return nil, newError("Required Feature dispatcher not resolved").Base(err)
+	}
+
+	content := new(session.Content)
+	content.SkipDNSResolve = true
+	session.SetForcedOutboundTagToContext(ctx, tag)
+
+	ctx = session.ContextWithContent(ctx, content)
+
+	r, err := dispatcher.Dispatch(ctx, dest)
+	if err != nil {
+		return nil, err
+	}
+	var readerOpt net.ConnectionOption
+	if dest.Network == net.Network_TCP {
+		readerOpt = net.ConnectionOutputMulti(r.Reader)
+	} else {
+		readerOpt = net.ConnectionOutputMultiUDP(r.Reader)
+	}
+	return net.NewConnection(net.ConnectionInputMulti(r.Writer), readerOpt), nil
+}