mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-25 18:11:28 +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) {
|
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)
|
return d.dialer.ListenPacket(ctx, destination)
|
||||||
}
|
}
|
||||||
ctx, metadata := adapter.AppendContext(ctx)
|
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
|
* Add full-featured [Naive](/configuration/inbound/naive) inbound
|
||||||
* Fix default dns server option [#9] by iKirby
|
* Fix default dns server option [#9] by iKirby
|
||||||
|
|
||||||
#### 2022/08/08
|
#### 2022/08/09
|
||||||
|
|
||||||
No changelog before.
|
No changelog before.
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
"method": "2022-blake3-aes-128-gcm",
|
"method": "2022-blake3-aes-128-gcm",
|
||||||
"password": "8JCsPssfgS8tiRwiMlhARg==",
|
"password": "8JCsPssfgS8tiRwiMlhARg==",
|
||||||
"network": "udp",
|
"network": "udp",
|
||||||
|
"udp_over_tcp": false,
|
||||||
"multiplex": {},
|
"multiplex": {},
|
||||||
|
|
||||||
"detour": "upstream-out",
|
"detour": "upstream-out",
|
||||||
|
@ -85,6 +86,12 @@ One of `tcp` `udp`.
|
||||||
|
|
||||||
Both is enabled by default.
|
Both is enabled by default.
|
||||||
|
|
||||||
|
#### udp_over_tcp
|
||||||
|
|
||||||
|
Enable UDP over TCP protocol.
|
||||||
|
|
||||||
|
Conflict with `multiplex`.
|
||||||
|
|
||||||
#### multiplex
|
#### multiplex
|
||||||
|
|
||||||
Multiplex configuration, see [Multiplex structure](/configuration/shared/multiplex).
|
Multiplex configuration, see [Multiplex structure](/configuration/shared/multiplex).
|
||||||
|
|
|
@ -27,5 +27,6 @@ type ShadowsocksOutboundOptions struct {
|
||||||
Method string `json:"method"`
|
Method string `json:"method"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Network NetworkList `json:"network,omitempty"`
|
Network NetworkList `json:"network,omitempty"`
|
||||||
|
UoT bool `json:"udp_over_tcp,omitempty"`
|
||||||
MultiplexOptions *MultiplexOptions `json:"multiplex,omitempty"`
|
MultiplexOptions *MultiplexOptions `json:"multiplex,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
"github.com/sagernet/sing/common/uot"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ adapter.Outbound = (*Shadowsocks)(nil)
|
var _ adapter.Outbound = (*Shadowsocks)(nil)
|
||||||
|
@ -26,6 +27,7 @@ type Shadowsocks struct {
|
||||||
dialer N.Dialer
|
dialer N.Dialer
|
||||||
method shadowsocks.Method
|
method shadowsocks.Method
|
||||||
serverAddr M.Socksaddr
|
serverAddr M.Socksaddr
|
||||||
|
uot bool
|
||||||
multiplexDialer N.Dialer
|
multiplexDialer N.Dialer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,11 +47,14 @@ func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
|
||||||
dialer: dialer.NewOutbound(router, options.OutboundDialerOptions),
|
dialer: dialer.NewOutbound(router, options.OutboundDialerOptions),
|
||||||
method: method,
|
method: method,
|
||||||
serverAddr: options.ServerOptions.Build(),
|
serverAddr: options.ServerOptions.Build(),
|
||||||
|
uot: options.UoT,
|
||||||
}
|
}
|
||||||
|
if !options.UoT {
|
||||||
outbound.multiplexDialer, err = mux.NewClientWithOptions(ctx, (*shadowsocksDialer)(outbound), common.PtrValueOrDefault(options.MultiplexOptions))
|
outbound.multiplexDialer, err = mux.NewClientWithOptions(ctx, (*shadowsocksDialer)(outbound), common.PtrValueOrDefault(options.MultiplexOptions))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return outbound, nil
|
return outbound, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +64,17 @@ func (h *Shadowsocks) DialContext(ctx context.Context, network string, destinati
|
||||||
case N.NetworkTCP:
|
case N.NetworkTCP:
|
||||||
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
||||||
case N.NetworkUDP:
|
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)
|
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||||
}
|
}
|
||||||
return (*shadowsocksDialer)(h).DialContext(ctx, network, 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) {
|
func (h *Shadowsocks) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
if h.multiplexDialer == nil {
|
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)
|
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||||
return (*shadowsocksDialer)(h).ListenPacket(ctx, destination)
|
return (*shadowsocksDialer)(h).ListenPacket(ctx, destination)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -35,6 +35,7 @@ import (
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/common/rw"
|
"github.com/sagernet/sing/common/rw"
|
||||||
|
"github.com/sagernet/sing/common/uot"
|
||||||
)
|
)
|
||||||
|
|
||||||
var warnDefaultInterfaceOnUnsupportedPlatform = warning.New(
|
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 {
|
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")
|
r.logger.InfoContext(ctx, "inbound multiplex connection")
|
||||||
return mux.NewConnection(ctx, r, r, r.logger, conn, metadata)
|
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 {
|
if metadata.SniffEnabled {
|
||||||
buffer := buf.NewPacket()
|
buffer := buf.NewPacket()
|
||||||
|
@ -543,13 +550,12 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
|
||||||
if metadata.SniffEnabled {
|
if metadata.SniffEnabled {
|
||||||
buffer := buf.NewPacket()
|
buffer := buf.NewPacket()
|
||||||
buffer.FullReset()
|
buffer.FullReset()
|
||||||
_, err := conn.ReadPacket(buffer)
|
destination, err := conn.ReadPacket(buffer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
buffer.Release()
|
buffer.Release()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
sniffMetadata, err := sniff.PeekPacket(ctx, buffer.Bytes(), sniff.DomainNameQuery, sniff.QUICClientHello, sniff.STUNMessage)
|
sniffMetadata, err := sniff.PeekPacket(ctx, buffer.Bytes(), sniff.DomainNameQuery, sniff.QUICClientHello, sniff.STUNMessage)
|
||||||
originDestination := metadata.Destination
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
metadata.Protocol = sniffMetadata.Protocol
|
metadata.Protocol = sniffMetadata.Protocol
|
||||||
metadata.Domain = sniffMetadata.Domain
|
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)
|
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)
|
addresses, err := r.Lookup(adapter.WithContext(ctx, &metadata), metadata.Destination.Fqdn, metadata.DomainStrategy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
||||||
F "github.com/sagernet/sing/common/format"
|
F "github.com/sagernet/sing/common/format"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -193,6 +194,68 @@ func testShadowsocksSelf(t *testing.T, method string, password string) {
|
||||||
testSuit(t, clientPort, testPort)
|
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 {
|
func mkBase64(t *testing.T, length int) string {
|
||||||
psk := make([]byte, length)
|
psk := make([]byte, length)
|
||||||
_, err := rand.Read(psk)
|
_, err := rand.Read(psk)
|
||||||
|
|
Loading…
Reference in a new issue