瀏覽代碼

Update UoT protocol

世界 2 年之前
父節點
當前提交
78e02b52ca
共有 7 個文件被更改,包括 105 次插入43 次删除
  1. 2 2
      go.mod
  2. 2 2
      go.sum
  3. 1 0
      option/shadowsocks.go
  4. 6 5
      option/simple.go
  5. 33 10
      outbound/shadowsocks.go
  6. 43 20
      outbound/socks.go
  7. 18 4
      route/router.go

+ 2 - 2
go.mod

@@ -24,7 +24,7 @@ require (
 	github.com/sagernet/gomobile v0.0.0-20221130124640-349ebaa752ca
 	github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32
 	github.com/sagernet/reality v0.0.0-20230312150606-35ea9af0e0b8
-	github.com/sagernet/sing v0.1.9-0.20230313033500-448948d26d1a
+	github.com/sagernet/sing v0.1.9-0.20230315063014-2731df16725b
 	github.com/sagernet/sing-dns v0.1.4
 	github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9
 	github.com/sagernet/sing-shadowtls v0.1.0
@@ -51,7 +51,7 @@ require (
 	gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c
 )
 
-//replace github.com/sagernet/sing-tun => ../sing-tun
+//replace github.com/sagernet/sing => ../sing
 
 require (
 	github.com/ajg/form v1.5.1 // indirect

+ 2 - 2
go.sum

@@ -109,8 +109,8 @@ github.com/sagernet/reality v0.0.0-20230312150606-35ea9af0e0b8 h1:4M3+0/kqvJuTsi
 github.com/sagernet/reality v0.0.0-20230312150606-35ea9af0e0b8/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
 github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
 github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
-github.com/sagernet/sing v0.1.9-0.20230313033500-448948d26d1a h1:JgPPxKLiqA95Z0oTp9FyYUfij8xsjS2rBWtpQ41zFTo=
-github.com/sagernet/sing v0.1.9-0.20230313033500-448948d26d1a/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw=
+github.com/sagernet/sing v0.1.9-0.20230315063014-2731df16725b h1:1iKGftQ59+shDSx2RaLaxXJcMK/B+IU9WqUPwyBW+E0=
+github.com/sagernet/sing v0.1.9-0.20230315063014-2731df16725b/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw=
 github.com/sagernet/sing-dns v0.1.4 h1:7VxgeoSCiiazDSaXXQVcvrTBxFpOePPq/4XdgnUDN+0=
 github.com/sagernet/sing-dns v0.1.4/go.mod h1:1+6pCa48B1AI78lD+/i/dLgpw4MwfnsSpZo0Ds8wzzk=
 github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9 h1:qS39eA4C7x+zhEkySbASrtmb6ebdy5v0y2M6mgkmSO0=

+ 1 - 0
option/shadowsocks.go

@@ -29,5 +29,6 @@ type ShadowsocksOutboundOptions struct {
 	PluginOptions    string            `json:"plugin_opts,omitempty"`
 	Network          NetworkList       `json:"network,omitempty"`
 	UoT              bool              `json:"udp_over_tcp,omitempty"`
+	UoTVersion       int               `json:"udp_over_tcp_version,omitempty"`
 	MultiplexOptions *MultiplexOptions `json:"multiplex,omitempty"`
 }

+ 6 - 5
option/simple.go

@@ -17,11 +17,12 @@ type HTTPMixedInboundOptions struct {
 type SocksOutboundOptions struct {
 	DialerOptions
 	ServerOptions
-	Version  string      `json:"version,omitempty"`
-	Username string      `json:"username,omitempty"`
-	Password string      `json:"password,omitempty"`
-	Network  NetworkList `json:"network,omitempty"`
-	UoT      bool        `json:"udp_over_tcp,omitempty"`
+	Version    string      `json:"version,omitempty"`
+	Username   string      `json:"username,omitempty"`
+	Password   string      `json:"password,omitempty"`
+	Network    NetworkList `json:"network,omitempty"`
+	UoT        bool        `json:"udp_over_tcp,omitempty"`
+	UoTVersion int         `json:"udp_over_tcp_version,omitempty"`
 }
 
 type HTTPOutboundOptions struct {

+ 33 - 10
outbound/shadowsocks.go

@@ -30,6 +30,7 @@ type Shadowsocks struct {
 	serverAddr      M.Socksaddr
 	plugin          sip003.Plugin
 	uot             bool
+	uotVersion      int
 	multiplexDialer N.Dialer
 }
 
@@ -63,6 +64,14 @@ func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
 			return nil, err
 		}
 	}
+	switch options.UoTVersion {
+	case uot.LegacyVersion:
+		outbound.uotVersion = uot.LegacyVersion
+	case 0, uot.Version:
+		outbound.uotVersion = uot.Version
+	default:
+		return nil, E.New("unknown udp over tcp protocol version ", options.UoTVersion)
+	}
 	return outbound, nil
 }
 
@@ -77,14 +86,21 @@ func (h *Shadowsocks) DialContext(ctx context.Context, network string, destinati
 		case N.NetworkUDP:
 			if h.uot {
 				h.logger.InfoContext(ctx, "outbound UoT packet connection to ", destination)
-				tcpConn, err := (*shadowsocksDialer)(h).DialContext(ctx, N.NetworkTCP, M.Socksaddr{
-					Fqdn: uot.UOTMagicAddress,
-					Port: destination.Port,
-				})
+				var uotDestination M.Socksaddr
+				if h.uotVersion == uot.Version {
+					uotDestination.Fqdn = uot.MagicAddress
+				} else {
+					uotDestination.Fqdn = uot.LegacyMagicAddress
+				}
+				tcpConn, err := (*shadowsocksDialer)(h).DialContext(ctx, N.NetworkTCP, uotDestination)
 				if err != nil {
 					return nil, err
 				}
-				return uot.NewClientConn(tcpConn), nil
+				if h.uotVersion == uot.Version {
+					return uot.NewLazyConn(tcpConn, uot.Request{IsConnect: true, Destination: destination}), nil
+				} else {
+					return uot.NewConn(tcpConn, false, destination), nil
+				}
 			}
 			h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
 		}
@@ -107,14 +123,21 @@ func (h *Shadowsocks) ListenPacket(ctx context.Context, destination M.Socksaddr)
 	if h.multiplexDialer == nil {
 		if h.uot {
 			h.logger.InfoContext(ctx, "outbound UoT packet connection to ", destination)
-			tcpConn, err := (*shadowsocksDialer)(h).DialContext(ctx, N.NetworkTCP, M.Socksaddr{
-				Fqdn: uot.UOTMagicAddress,
-				Port: destination.Port,
-			})
+			var uotDestination M.Socksaddr
+			if h.uotVersion == uot.Version {
+				uotDestination.Fqdn = uot.MagicAddress
+			} else {
+				uotDestination.Fqdn = uot.LegacyMagicAddress
+			}
+			tcpConn, err := (*shadowsocksDialer)(h).DialContext(ctx, N.NetworkTCP, uotDestination)
 			if err != nil {
 				return nil, err
 			}
-			return uot.NewClientConn(tcpConn), nil
+			if h.uotVersion == uot.Version {
+				return uot.NewLazyConn(tcpConn, uot.Request{Destination: destination}), nil
+			} else {
+				return uot.NewConn(tcpConn, false, destination), nil
+			}
 		}
 		h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
 		return (*shadowsocksDialer)(h).ListenPacket(ctx, destination)

+ 43 - 20
outbound/socks.go

@@ -20,13 +20,13 @@ var _ adapter.Outbound = (*Socks)(nil)
 
 type Socks struct {
 	myOutboundAdapter
-	client  *socks.Client
-	resolve bool
-	uot     bool
+	client     *socks.Client
+	resolve    bool
+	uot        bool
+	uotVersion int
 }
 
 func NewSocks(router adapter.Router, logger log.ContextLogger, tag string, options option.SocksOutboundOptions) (*Socks, error) {
-	detour := dialer.New(router, options.DialerOptions)
 	var version socks.Version
 	var err error
 	if options.Version != "" {
@@ -37,18 +37,27 @@ func NewSocks(router adapter.Router, logger log.ContextLogger, tag string, optio
 	if err != nil {
 		return nil, err
 	}
-	return &Socks{
-		myOutboundAdapter{
+	outbound := &Socks{
+		myOutboundAdapter: myOutboundAdapter{
 			protocol: C.TypeSocks,
 			network:  options.Network.Build(),
 			router:   router,
 			logger:   logger,
 			tag:      tag,
 		},
-		socks.NewClient(detour, options.ServerOptions.Build(), version, options.Username, options.Password),
-		version == socks.Version4,
-		options.UoT,
-	}, nil
+		client:  socks.NewClient(dialer.New(router, options.DialerOptions), options.ServerOptions.Build(), version, options.Username, options.Password),
+		resolve: version == socks.Version4,
+		uot:     options.UoT,
+	}
+	switch options.UoTVersion {
+	case uot.LegacyVersion:
+		outbound.uotVersion = uot.LegacyVersion
+	case 0, uot.Version:
+		outbound.uotVersion = uot.Version
+	default:
+		return nil, E.New("unknown udp over tcp protocol version ", options.UoTVersion)
+	}
+	return outbound, nil
 }
 
 func (h *Socks) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
@@ -61,14 +70,21 @@ func (h *Socks) DialContext(ctx context.Context, network string, destination M.S
 	case N.NetworkUDP:
 		if h.uot {
 			h.logger.InfoContext(ctx, "outbound UoT packet connection to ", destination)
-			tcpConn, err := h.client.DialContext(ctx, N.NetworkTCP, M.Socksaddr{
-				Fqdn: uot.UOTMagicAddress,
-				Port: destination.Port,
-			})
+			var uotDestination M.Socksaddr
+			if h.uotVersion == uot.Version {
+				uotDestination.Fqdn = uot.MagicAddress
+			} else {
+				uotDestination.Fqdn = uot.LegacyMagicAddress
+			}
+			tcpConn, err := h.client.DialContext(ctx, N.NetworkTCP, uotDestination)
 			if err != nil {
 				return nil, err
 			}
-			return uot.NewClientConn(tcpConn), nil
+			if h.uotVersion == uot.Version {
+				return uot.NewLazyConn(tcpConn, uot.Request{IsConnect: true, Destination: destination}), nil
+			} else {
+				return uot.NewConn(tcpConn, false, destination), nil
+			}
 		}
 		h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
 	default:
@@ -90,14 +106,21 @@ func (h *Socks) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.
 	metadata.Destination = destination
 	if h.uot {
 		h.logger.InfoContext(ctx, "outbound UoT packet connection to ", destination)
-		tcpConn, err := h.client.DialContext(ctx, N.NetworkTCP, M.Socksaddr{
-			Fqdn: uot.UOTMagicAddress,
-			Port: destination.Port,
-		})
+		var uotDestination M.Socksaddr
+		if h.uotVersion == uot.Version {
+			uotDestination.Fqdn = uot.MagicAddress
+		} else {
+			uotDestination.Fqdn = uot.LegacyMagicAddress
+		}
+		tcpConn, err := h.client.DialContext(ctx, N.NetworkTCP, uotDestination)
 		if err != nil {
 			return nil, err
 		}
-		return uot.NewClientConn(tcpConn), nil
+		if h.uotVersion == uot.Version {
+			return uot.NewLazyConn(tcpConn, uot.Request{Destination: destination}), nil
+		} else {
+			return uot.NewConn(tcpConn, false, destination), nil
+		}
 	}
 	h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
 	return h.client.ListenPacket(ctx, destination)

+ 18 - 4
route/router.go

@@ -577,10 +577,24 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
 	case vmess.MuxDestination.Fqdn:
 		r.logger.InfoContext(ctx, "inbound legacy multiplex connection")
 		return vmess.HandleMuxConnection(ctx, conn, adapter.NewUpstreamHandler(metadata, r.RouteConnection, r.RoutePacketConnection, r))
-	case uot.UOTMagicAddress:
-		r.logger.InfoContext(ctx, "inbound UoT connection")
+	case uot.MagicAddress:
+		request, err := uot.ReadRequest(conn)
+		if err != nil {
+			return E.Cause(err, "read UoT request")
+		}
+		if request.IsConnect {
+			r.logger.InfoContext(ctx, "inbound UoT connect connection to ", request.Destination)
+		} else {
+			r.logger.InfoContext(ctx, "inbound UoT connection to ", request.Destination)
+		}
+		metadata.Domain = metadata.Destination.Fqdn
+		metadata.Destination = request.Destination
+		return r.RoutePacketConnection(ctx, uot.NewConn(conn, request.IsConnect, metadata.Destination), metadata)
+	case uot.LegacyMagicAddress:
+		r.logger.InfoContext(ctx, "inbound legacy UoT connection")
+		metadata.Domain = metadata.Destination.Fqdn
 		metadata.Destination = M.Socksaddr{Addr: netip.IPv4Unspecified()}
-		return r.RoutePacketConnection(ctx, uot.NewClientConn(conn), metadata)
+		return r.RoutePacketConnection(ctx, uot.NewConn(conn, false, metadata.Destination), metadata)
 	}
 	if metadata.InboundOptions.SniffEnabled {
 		buffer := buf.NewPacket()
@@ -685,7 +699,7 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
 		}
 		conn = bufio.NewCachedPacketConn(conn, buffer, destination)
 	}
-	if metadata.Destination.IsFqdn() && metadata.Destination.Fqdn != uot.UOTMagicAddress && dns.DomainStrategy(metadata.InboundOptions.DomainStrategy) != dns.DomainStrategyAsIS {
+	if metadata.Destination.IsFqdn() && dns.DomainStrategy(metadata.InboundOptions.DomainStrategy) != dns.DomainStrategyAsIS {
 		addresses, err := r.Lookup(adapter.WithContext(ctx, &metadata), metadata.Destination.Fqdn, dns.DomainStrategy(metadata.InboundOptions.DomainStrategy))
 		if err != nil {
 			return err