mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-22 00:21:30 +00:00
Add back UoT support
This commit is contained in:
parent
cf80073f27
commit
517a89fa9c
|
@ -51,7 +51,7 @@ func (d *ResolveDialer) DialContext(ctx context.Context, network string, destina
|
|||
}
|
||||
|
||||
func (d *ResolveDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
if !destination.IsFqdn() {
|
||||
if !destination.IsFqdn() || destination.Fqdn == "" {
|
||||
return d.dialer.ListenPacket(ctx, destination)
|
||||
}
|
||||
ctx, metadata := adapter.AppendContext(ctx)
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
#### 2022/08/09
|
||||
#### 2022/08/11
|
||||
|
||||
* Add UoT option for [Shadowsocks](/configuration/outbound/shadowsocks) outbound, UoT support for all inbounds.
|
||||
|
||||
#### 2022/08/10
|
||||
|
||||
* Add full-featured [Naive](/configuration/inbound/naive) inbound
|
||||
* Fix default dns server option [#9] by iKirby
|
||||
|
||||
#### 2022/08/08
|
||||
#### 2022/08/09
|
||||
|
||||
No changelog before.
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
"method": "2022-blake3-aes-128-gcm",
|
||||
"password": "8JCsPssfgS8tiRwiMlhARg==",
|
||||
"network": "udp",
|
||||
"udp_over_tcp": false,
|
||||
"multiplex": {},
|
||||
|
||||
"detour": "upstream-out",
|
||||
|
@ -85,6 +86,12 @@ One of `tcp` `udp`.
|
|||
|
||||
Both is enabled by default.
|
||||
|
||||
#### udp_over_tcp
|
||||
|
||||
Enable UDP over TCP protocol.
|
||||
|
||||
Conflict with `multiplex`.
|
||||
|
||||
#### multiplex
|
||||
|
||||
Multiplex configuration, see [Multiplex structure](/configuration/shared/multiplex).
|
||||
|
|
|
@ -27,5 +27,6 @@ type ShadowsocksOutboundOptions struct {
|
|||
Method string `json:"method"`
|
||||
Password string `json:"password"`
|
||||
Network NetworkList `json:"network,omitempty"`
|
||||
UoT bool `json:"udp_over_tcp,omitempty"`
|
||||
MultiplexOptions *MultiplexOptions `json:"multiplex,omitempty"`
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/uot"
|
||||
)
|
||||
|
||||
var _ adapter.Outbound = (*Shadowsocks)(nil)
|
||||
|
@ -26,6 +27,7 @@ type Shadowsocks struct {
|
|||
dialer N.Dialer
|
||||
method shadowsocks.Method
|
||||
serverAddr M.Socksaddr
|
||||
uot bool
|
||||
multiplexDialer N.Dialer
|
||||
}
|
||||
|
||||
|
@ -45,10 +47,13 @@ func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
|
|||
dialer: dialer.NewOutbound(router, options.OutboundDialerOptions),
|
||||
method: method,
|
||||
serverAddr: options.ServerOptions.Build(),
|
||||
uot: options.UoT,
|
||||
}
|
||||
outbound.multiplexDialer, err = mux.NewClientWithOptions(ctx, (*shadowsocksDialer)(outbound), common.PtrValueOrDefault(options.MultiplexOptions))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if !options.UoT {
|
||||
outbound.multiplexDialer, err = mux.NewClientWithOptions(ctx, (*shadowsocksDialer)(outbound), common.PtrValueOrDefault(options.MultiplexOptions))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return outbound, nil
|
||||
}
|
||||
|
@ -59,6 +64,17 @@ func (h *Shadowsocks) DialContext(ctx context.Context, network string, destinati
|
|||
case N.NetworkTCP:
|
||||
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
||||
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,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return uot.NewClientConn(tcpConn), nil
|
||||
}
|
||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||
}
|
||||
return (*shadowsocksDialer)(h).DialContext(ctx, network, destination)
|
||||
|
@ -75,6 +91,17 @@ func (h *Shadowsocks) DialContext(ctx context.Context, network string, destinati
|
|||
|
||||
func (h *Shadowsocks) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
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,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return uot.NewClientConn(tcpConn), nil
|
||||
}
|
||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||
return (*shadowsocksDialer)(h).ListenPacket(ctx, destination)
|
||||
} else {
|
||||
|
|
|
@ -35,6 +35,7 @@ import (
|
|||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
"github.com/sagernet/sing/common/uot"
|
||||
)
|
||||
|
||||
var warnDefaultInterfaceOnUnsupportedPlatform = warning.New(
|
||||
|
@ -492,9 +493,15 @@ func (r *Router) DefaultOutbound(network string) adapter.Outbound {
|
|||
}
|
||||
|
||||
func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
if metadata.Destination.Fqdn == mux.Destination.Fqdn {
|
||||
switch metadata.Destination.Fqdn {
|
||||
case mux.Destination.Fqdn:
|
||||
r.logger.InfoContext(ctx, "inbound multiplex connection")
|
||||
return mux.NewConnection(ctx, r, r, r.logger, conn, metadata)
|
||||
case uot.UOTMagicAddress:
|
||||
r.logger.InfoContext(ctx, "inbound UoT connection")
|
||||
metadata.Network = N.NetworkUDP
|
||||
metadata.Destination = M.Socksaddr{}
|
||||
return r.RoutePacketConnection(ctx, uot.NewClientConn(conn), metadata)
|
||||
}
|
||||
if metadata.SniffEnabled {
|
||||
buffer := buf.NewPacket()
|
||||
|
@ -543,13 +550,12 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
|
|||
if metadata.SniffEnabled {
|
||||
buffer := buf.NewPacket()
|
||||
buffer.FullReset()
|
||||
_, err := conn.ReadPacket(buffer)
|
||||
destination, err := conn.ReadPacket(buffer)
|
||||
if err != nil {
|
||||
buffer.Release()
|
||||
return err
|
||||
}
|
||||
sniffMetadata, err := sniff.PeekPacket(ctx, buffer.Bytes(), sniff.DomainNameQuery, sniff.QUICClientHello, sniff.STUNMessage)
|
||||
originDestination := metadata.Destination
|
||||
if err == nil {
|
||||
metadata.Protocol = sniffMetadata.Protocol
|
||||
metadata.Domain = sniffMetadata.Domain
|
||||
|
@ -562,9 +568,9 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
|
|||
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol)
|
||||
}
|
||||
}
|
||||
conn = bufio.NewCachedPacketConn(conn, buffer, originDestination)
|
||||
conn = bufio.NewCachedPacketConn(conn, buffer, destination)
|
||||
}
|
||||
if metadata.Destination.IsFqdn() && metadata.DomainStrategy != dns.DomainStrategyAsIS {
|
||||
if metadata.Destination.IsFqdn() && metadata.Destination.Fqdn != uot.UOTMagicAddress && metadata.DomainStrategy != dns.DomainStrategyAsIS {
|
||||
addresses, err := r.Lookup(adapter.WithContext(ctx, &metadata), metadata.Destination.Fqdn, metadata.DomainStrategy)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -193,6 +194,68 @@ func testShadowsocksSelf(t *testing.T, method string, password string) {
|
|||
testSuit(t, clientPort, testPort)
|
||||
}
|
||||
|
||||
func TestShadowsocksUoT(t *testing.T) {
|
||||
method := shadowaead_2022.List[0]
|
||||
password := mkBase64(t, 16)
|
||||
startInstance(t, option.Options{
|
||||
Log: &option.LogOptions{
|
||||
Level: "trace",
|
||||
},
|
||||
Inbounds: []option.Inbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||
ListenPort: clientPort,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: C.TypeShadowsocks,
|
||||
ShadowsocksOptions: option.ShadowsocksInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||
ListenPort: serverPort,
|
||||
},
|
||||
Method: method,
|
||||
Password: password,
|
||||
},
|
||||
},
|
||||
},
|
||||
Outbounds: []option.Outbound{
|
||||
{
|
||||
Type: C.TypeDirect,
|
||||
},
|
||||
{
|
||||
Type: C.TypeShadowsocks,
|
||||
Tag: "ss-out",
|
||||
ShadowsocksOptions: option.ShadowsocksOutboundOptions{
|
||||
ServerOptions: option.ServerOptions{
|
||||
Server: "127.0.0.1",
|
||||
ServerPort: serverPort,
|
||||
},
|
||||
Method: method,
|
||||
Password: password,
|
||||
UoT: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Route: &option.RouteOptions{
|
||||
Rules: []option.Rule{
|
||||
{
|
||||
DefaultOptions: option.DefaultRule{
|
||||
Inbound: []string{"mixed-in"},
|
||||
Outbound: "ss-out",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
testSuit(t, clientPort, testPort)
|
||||
}
|
||||
|
||||
func mkBase64(t *testing.T, length int) string {
|
||||
psk := make([]byte, length)
|
||||
_, err := rand.Read(psk)
|
||||
|
|
Loading…
Reference in a new issue