mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-22 08:31:30 +00:00
Add shadowtls (#49)
* Add shadowtls outbound * Add shadowtls inbound * Add shadowtls example * Add shadowtls documentation
This commit is contained in:
parent
5a9c2b1e80
commit
5813e0ce7a
|
@ -18,6 +18,7 @@ const (
|
|||
TypeHysteria = "hysteria"
|
||||
TypeTor = "tor"
|
||||
TypeSSH = "ssh"
|
||||
TypeShadowTLS = "shadowtls"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
31
docs/configuration/inbound/shadowtls.md
Normal file
31
docs/configuration/inbound/shadowtls.md
Normal file
|
@ -0,0 +1,31 @@
|
|||
### Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "shadowtls",
|
||||
"tag": "st-in",
|
||||
|
||||
... // Listen Fields
|
||||
|
||||
"handshake": {
|
||||
"server": "google.com",
|
||||
"server_port": 443,
|
||||
|
||||
... // Dial Fields
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Listen Fields
|
||||
|
||||
See [Listen Fields](/configuration/shared/listen) for details.
|
||||
|
||||
|
||||
### Fields
|
||||
|
||||
#### handshake
|
||||
|
||||
==Required==
|
||||
|
||||
Handshake server address and [dial options](/configuration/shared/dial).
|
||||
|
29
docs/configuration/inbound/shadowtls.zh.md
Normal file
29
docs/configuration/inbound/shadowtls.zh.md
Normal file
|
@ -0,0 +1,29 @@
|
|||
### 结构
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "shadowtls",
|
||||
"tag": "st-in",
|
||||
|
||||
... // 监听字段
|
||||
|
||||
"handshake": {
|
||||
"server": "google.com",
|
||||
"server_port": 443,
|
||||
|
||||
... // 拨号字段
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 监听字段
|
||||
|
||||
参阅 [监听字段](/zh/configuration/shared/listen/)。
|
||||
|
||||
### 字段
|
||||
|
||||
#### handshake
|
||||
|
||||
==必填==
|
||||
|
||||
握手服务器地址和 [拨号参数](/zh/configuration/shared/dial/)。
|
38
docs/configuration/outbound/shadowtls.md
Normal file
38
docs/configuration/outbound/shadowtls.md
Normal file
|
@ -0,0 +1,38 @@
|
|||
### Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "shadowtls",
|
||||
"tag": "st-out",
|
||||
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 1080,
|
||||
"tls": {},
|
||||
|
||||
... // Dial Fields
|
||||
}
|
||||
```
|
||||
|
||||
### Fields
|
||||
|
||||
#### server
|
||||
|
||||
==Required==
|
||||
|
||||
The server address.
|
||||
|
||||
#### server_port
|
||||
|
||||
==Required==
|
||||
|
||||
The server port.
|
||||
|
||||
#### tls
|
||||
|
||||
==Required==
|
||||
|
||||
TLS configuration, see [TLS](/configuration/shared/tls/#outbound).
|
||||
|
||||
### Dial Fields
|
||||
|
||||
See [Dial Fields](/configuration/shared/dial) for details.
|
38
docs/configuration/outbound/shadowtls.zh.md
Normal file
38
docs/configuration/outbound/shadowtls.zh.md
Normal file
|
@ -0,0 +1,38 @@
|
|||
### 结构
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "shadowtls",
|
||||
"tag": "st-out",
|
||||
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 1080,
|
||||
"tls": {},
|
||||
|
||||
... // 拨号字段
|
||||
}
|
||||
```
|
||||
|
||||
### 字段
|
||||
|
||||
#### server
|
||||
|
||||
==必填==
|
||||
|
||||
服务器地址。
|
||||
|
||||
#### server_port
|
||||
|
||||
==必填==
|
||||
|
||||
服务器端口。
|
||||
|
||||
#### tls
|
||||
|
||||
==必填==
|
||||
|
||||
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
|
||||
|
||||
### 拨号字段
|
||||
|
||||
参阅 [拨号字段](/zh/configuration/shared/dial/)。
|
55
docs/examples/shadowtls.md
Normal file
55
docs/examples/shadowtls.md
Normal file
|
@ -0,0 +1,55 @@
|
|||
#### Server
|
||||
|
||||
```json
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "shadowtls",
|
||||
"listen": "::",
|
||||
"listen_port": 4443,
|
||||
"handshake": {
|
||||
"server": "google.com",
|
||||
"server_port": 443
|
||||
},
|
||||
"detour": "shadowsocks-in"
|
||||
},
|
||||
{
|
||||
"type": "shadowsocks",
|
||||
"tag": "shadowsocks-in",
|
||||
"listen": "127.0.0.1",
|
||||
"method": "2022-blake3-aes-128-gcm",
|
||||
"password": "8JCsPssfgS8tiRwiMlhARg=="
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### Client
|
||||
|
||||
```json
|
||||
{
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "shadowsocks",
|
||||
"method": "2022-blake3-aes-128-gcm",
|
||||
"password": "8JCsPssfgS8tiRwiMlhARg==",
|
||||
"detour": "shadowtls-out",
|
||||
"multiplex": {
|
||||
"enabled": 1,
|
||||
"max_connections": 4,
|
||||
"min_streams": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "shadowtls",
|
||||
"tag": "shadowtls-out",
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 4443,
|
||||
"tls": {
|
||||
"enabled": true,
|
||||
"server_name": "google.com"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
|
@ -39,6 +39,8 @@ func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, o
|
|||
return NewNaive(ctx, router, logger, options.Tag, options.NaiveOptions)
|
||||
case C.TypeHysteria:
|
||||
return NewHysteria(ctx, router, logger, options.Tag, options.HysteriaOptions)
|
||||
case C.TypeShadowTLS:
|
||||
return NewShadowTLS(ctx, router, logger, options.Tag, options.ShadowTLSOptions)
|
||||
default:
|
||||
return nil, E.New("unknown inbound type: ", options.Type)
|
||||
}
|
||||
|
|
|
@ -87,15 +87,3 @@ func (h *Shadowsocks) NewPacket(ctx context.Context, conn N.PacketConn, buffer *
|
|||
func (h *Shadowsocks) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (h *Shadowsocks) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||
return h.router.RouteConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *Shadowsocks) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
h.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
|
||||
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
|
93
inbound/shadowtls.go
Normal file
93
inbound/shadowtls.go
Normal file
|
@ -0,0 +1,93 @@
|
|||
package inbound
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
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/task"
|
||||
)
|
||||
|
||||
type ShadowTLS struct {
|
||||
myInboundAdapter
|
||||
handshakeDialer N.Dialer
|
||||
handshakeAddr M.Socksaddr
|
||||
}
|
||||
|
||||
func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowTLSInboundOptions) (*ShadowTLS, error) {
|
||||
inbound := &ShadowTLS{
|
||||
myInboundAdapter: myInboundAdapter{
|
||||
protocol: C.TypeShadowTLS,
|
||||
network: []string{N.NetworkTCP},
|
||||
ctx: ctx,
|
||||
router: router,
|
||||
logger: logger,
|
||||
tag: tag,
|
||||
listenOptions: options.ListenOptions,
|
||||
},
|
||||
handshakeDialer: dialer.New(router, options.Handshake.DialerOptions),
|
||||
handshakeAddr: options.Handshake.ServerOptions.Build(),
|
||||
}
|
||||
inbound.connHandler = inbound
|
||||
return inbound, nil
|
||||
}
|
||||
|
||||
func (s *ShadowTLS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
handshakeConn, err := s.handshakeDialer.DialContext(ctx, N.NetworkTCP, s.handshakeAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var handshake task.Group
|
||||
handshake.Append("client handshake", func(ctx context.Context) error {
|
||||
return s.copyUntilHandshakeFinished(handshakeConn, conn)
|
||||
})
|
||||
handshake.Append("server handshake", func(ctx context.Context) error {
|
||||
return s.copyUntilHandshakeFinished(conn, handshakeConn)
|
||||
})
|
||||
handshake.FastFail()
|
||||
err = handshake.Run(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.newConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (s *ShadowTLS) copyUntilHandshakeFinished(dst io.Writer, src io.Reader) error {
|
||||
const handshake = 0x16
|
||||
const changeCipherSpec = 0x14
|
||||
var hasSeenChangeCipherSpec bool
|
||||
var tlsHdr [5]byte
|
||||
for {
|
||||
_, err := io.ReadFull(src, tlsHdr[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
length := binary.BigEndian.Uint16(tlsHdr[3:])
|
||||
_, err = io.Copy(dst, io.MultiReader(bytes.NewReader(tlsHdr[:]), io.LimitReader(src, int64(length))))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tlsHdr[0] != handshake {
|
||||
if tlsHdr[0] != changeCipherSpec {
|
||||
return E.New("unexpected tls frame type: ", tlsHdr[0])
|
||||
}
|
||||
if !hasSeenChangeCipherSpec {
|
||||
hasSeenChangeCipherSpec = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
if hasSeenChangeCipherSpec {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
|
@ -68,6 +68,7 @@ nav:
|
|||
- Trojan: configuration/inbound/trojan.md
|
||||
- Naive: configuration/inbound/naive.md
|
||||
- Hysteria: configuration/inbound/hysteria.md
|
||||
- ShadowTLS: configuration/inbound/shadowtls.md
|
||||
- Tun: configuration/inbound/tun.md
|
||||
- Redirect: configuration/inbound/redirect.md
|
||||
- TProxy: configuration/inbound/tproxy.md
|
||||
|
@ -82,6 +83,7 @@ nav:
|
|||
- Trojan: configuration/outbound/trojan.md
|
||||
- WireGuard: configuration/outbound/wireguard.md
|
||||
- Hysteria: configuration/outbound/hysteria.md
|
||||
- ShadowTLS: configuration/outbound/shadowtls.md
|
||||
- Tor: configuration/outbound/tor.md
|
||||
- SSH: configuration/outbound/ssh.md
|
||||
- DNS: configuration/outbound/dns.md
|
||||
|
@ -95,6 +97,7 @@ nav:
|
|||
- Shadowsocks Server: examples/ss-server.md
|
||||
- Shadowsocks Client: examples/ss-client.md
|
||||
- Shadowsocks Tun: examples/ss-tun.md
|
||||
- ShadowTLS: examples/shadowtls.md
|
||||
- DNS Hijack: examples/dns-hijack.md
|
||||
- Contributing:
|
||||
- contributing/index.md
|
||||
|
|
|
@ -21,6 +21,7 @@ type _Inbound struct {
|
|||
TrojanOptions TrojanInboundOptions `json:"-"`
|
||||
NaiveOptions NaiveInboundOptions `json:"-"`
|
||||
HysteriaOptions HysteriaInboundOptions `json:"-"`
|
||||
ShadowTLSOptions ShadowTLSInboundOptions `json:"-"`
|
||||
}
|
||||
|
||||
type Inbound _Inbound
|
||||
|
@ -52,6 +53,8 @@ func (h Inbound) MarshalJSON() ([]byte, error) {
|
|||
v = h.NaiveOptions
|
||||
case C.TypeHysteria:
|
||||
v = h.HysteriaOptions
|
||||
case C.TypeShadowTLS:
|
||||
v = h.ShadowTLSOptions
|
||||
default:
|
||||
return nil, E.New("unknown inbound type: ", h.Type)
|
||||
}
|
||||
|
@ -89,6 +92,8 @@ func (h *Inbound) UnmarshalJSON(bytes []byte) error {
|
|||
v = &h.NaiveOptions
|
||||
case C.TypeHysteria:
|
||||
v = &h.HysteriaOptions
|
||||
case C.TypeShadowTLS:
|
||||
v = &h.ShadowTLSOptions
|
||||
default:
|
||||
return E.New("unknown inbound type: ", h.Type)
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ type _Outbound struct {
|
|||
HysteriaOptions HysteriaOutboundOptions `json:"-"`
|
||||
TorOptions TorOutboundOptions `json:"-"`
|
||||
SSHOptions SSHOutboundOptions `json:"-"`
|
||||
ShadowTLSOptions ShadowTLSOutboundOptions `json:"-"`
|
||||
SelectorOptions SelectorOutboundOptions `json:"-"`
|
||||
}
|
||||
|
||||
|
@ -50,6 +51,8 @@ func (h Outbound) MarshalJSON() ([]byte, error) {
|
|||
v = h.TorOptions
|
||||
case C.TypeSSH:
|
||||
v = h.SSHOptions
|
||||
case C.TypeShadowTLS:
|
||||
v = h.ShadowTLSOptions
|
||||
case C.TypeSelector:
|
||||
v = h.SelectorOptions
|
||||
default:
|
||||
|
@ -87,6 +90,8 @@ func (h *Outbound) UnmarshalJSON(bytes []byte) error {
|
|||
v = &h.TorOptions
|
||||
case C.TypeSSH:
|
||||
v = &h.SSHOptions
|
||||
case C.TypeShadowTLS:
|
||||
v = &h.ShadowTLSOptions
|
||||
case C.TypeSelector:
|
||||
v = &h.SelectorOptions
|
||||
default:
|
||||
|
|
17
option/shadowtls.go
Normal file
17
option/shadowtls.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package option
|
||||
|
||||
type ShadowTLSInboundOptions struct {
|
||||
ListenOptions
|
||||
Handshake ShadowTLSHandshakeOptions `json:"handshake"`
|
||||
}
|
||||
|
||||
type ShadowTLSHandshakeOptions struct {
|
||||
ServerOptions
|
||||
DialerOptions
|
||||
}
|
||||
|
||||
type ShadowTLSOutboundOptions struct {
|
||||
OutboundDialerOptions
|
||||
ServerOptions
|
||||
TLS *OutboundTLSOptions `json:"tls,omitempty"`
|
||||
}
|
|
@ -39,6 +39,8 @@ func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, o
|
|||
return NewTor(ctx, router, logger, options.Tag, options.TorOptions)
|
||||
case C.TypeSSH:
|
||||
return NewSSH(ctx, router, logger, options.Tag, options.SSHOptions)
|
||||
case C.TypeShadowTLS:
|
||||
return NewShadowTLS(ctx, router, logger, options.Tag, options.ShadowTLSOptions)
|
||||
case C.TypeSelector:
|
||||
return NewSelector(router, logger, options.Tag, options.SelectorOptions)
|
||||
default:
|
||||
|
|
84
outbound/shadowtls.go
Normal file
84
outbound/shadowtls.go
Normal file
|
@ -0,0 +1,84 @@
|
|||
package outbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
var _ adapter.Outbound = (*ShadowTLS)(nil)
|
||||
|
||||
type ShadowTLS struct {
|
||||
myOutboundAdapter
|
||||
dialer N.Dialer
|
||||
serverAddr M.Socksaddr
|
||||
tlsConfig *tls.Config
|
||||
}
|
||||
|
||||
func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowTLSOutboundOptions) (*ShadowTLS, error) {
|
||||
outbound := &ShadowTLS{
|
||||
myOutboundAdapter: myOutboundAdapter{
|
||||
protocol: C.TypeShadowTLS,
|
||||
network: []string{N.NetworkTCP},
|
||||
router: router,
|
||||
logger: logger,
|
||||
tag: tag,
|
||||
},
|
||||
dialer: dialer.NewOutbound(router, options.OutboundDialerOptions),
|
||||
serverAddr: options.ServerOptions.Build(),
|
||||
}
|
||||
if options.TLS == nil || !options.TLS.Enabled {
|
||||
return nil, C.ErrTLSRequired
|
||||
}
|
||||
options.TLS.MinVersion = "1.2"
|
||||
options.TLS.MaxVersion = "1.2"
|
||||
var err error
|
||||
outbound.tlsConfig, err = dialer.TLSConfig(options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return outbound, nil
|
||||
}
|
||||
|
||||
func (s *ShadowTLS) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
switch N.NetworkName(network) {
|
||||
case N.NetworkTCP:
|
||||
default:
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
conn, err := s.dialer.DialContext(ctx, N.NetworkTCP, s.serverAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConn, err := dialer.TLSClient(ctx, conn, s.tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = tlsConn.HandshakeContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (s *ShadowTLS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
func (s *ShadowTLS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
return NewConnection(ctx, s, conn, metadata)
|
||||
}
|
||||
|
||||
func (s *ShadowTLS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
return os.ErrInvalid
|
||||
}
|
|
@ -519,8 +519,9 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
|
|||
if !common.Contains(injectable.Network(), N.NetworkTCP) {
|
||||
return E.New("inject: TCP unsupported")
|
||||
}
|
||||
metadata.InboundDetour = ""
|
||||
metadata.LastInbound = metadata.Inbound
|
||||
metadata.Inbound = metadata.InboundDetour
|
||||
metadata.InboundDetour = ""
|
||||
err := injectable.NewConnection(ctx, conn, metadata)
|
||||
if err != nil {
|
||||
return E.Cause(err, "inject ", detour.Tag())
|
||||
|
@ -599,8 +600,9 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
|
|||
if !common.Contains(injectable.Network(), N.NetworkUDP) {
|
||||
return E.New("inject: UDP unsupported")
|
||||
}
|
||||
metadata.InboundDetour = ""
|
||||
metadata.LastInbound = metadata.Inbound
|
||||
metadata.Inbound = metadata.InboundDetour
|
||||
metadata.InboundDetour = ""
|
||||
err := injectable.NewPacketConnection(ctx, conn, metadata)
|
||||
if err != nil {
|
||||
return E.Cause(err, "inject ", detour.Tag())
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/sagernet/sing-box"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
"github.com/sagernet/sing/common/debug"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/protocol/socks"
|
||||
|
@ -17,6 +18,11 @@ import (
|
|||
)
|
||||
|
||||
func startInstance(t *testing.T, options option.Options) {
|
||||
if debug.Enabled {
|
||||
options.Log = &option.LogOptions{
|
||||
Level: "trace",
|
||||
}
|
||||
}
|
||||
var instance *box.Box
|
||||
var err error
|
||||
for retry := 0; retry < 3; retry++ {
|
||||
|
|
|
@ -36,6 +36,7 @@ const (
|
|||
ImageBoringTun = "ghcr.io/ntkme/boringtun:edge"
|
||||
ImageHysteria = "tobyxdd/hysteria:latest"
|
||||
ImageNginx = "nginx:stable"
|
||||
ImageShadowTLS = "ghcr.io/ihciah/shadow-tls:latest"
|
||||
)
|
||||
|
||||
var allImages = []string{
|
||||
|
@ -46,7 +47,8 @@ var allImages = []string{
|
|||
ImageNaive,
|
||||
ImageBoringTun,
|
||||
ImageHysteria,
|
||||
// ImageNginx,
|
||||
ImageNginx,
|
||||
ImageShadowTLS,
|
||||
}
|
||||
|
||||
var localIP = netip.MustParseAddr("127.0.0.1")
|
||||
|
|
170
test/shadowtls_test.go
Normal file
170
test/shadowtls_test.go
Normal file
|
@ -0,0 +1,170 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
func TestShadowTLS(t *testing.T) {
|
||||
method := shadowaead_2022.List[0]
|
||||
password := mkBase64(t, 16)
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||
ListenPort: clientPort,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: C.TypeShadowTLS,
|
||||
Tag: "in",
|
||||
ShadowTLSOptions: option.ShadowTLSInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||
ListenPort: serverPort,
|
||||
Detour: "detour",
|
||||
},
|
||||
Handshake: option.ShadowTLSHandshakeOptions{
|
||||
ServerOptions: option.ServerOptions{
|
||||
Server: "google.com",
|
||||
ServerPort: 443,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: C.TypeShadowsocks,
|
||||
Tag: "detour",
|
||||
ShadowsocksOptions: option.ShadowsocksInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||
ListenPort: otherPort,
|
||||
},
|
||||
Method: method,
|
||||
Password: password,
|
||||
},
|
||||
},
|
||||
},
|
||||
Outbounds: []option.Outbound{
|
||||
{
|
||||
Type: C.TypeShadowsocks,
|
||||
ShadowsocksOptions: option.ShadowsocksOutboundOptions{
|
||||
Method: method,
|
||||
Password: password,
|
||||
OutboundDialerOptions: option.OutboundDialerOptions{
|
||||
DialerOptions: option.DialerOptions{
|
||||
Detour: "detour",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: C.TypeShadowTLS,
|
||||
Tag: "detour",
|
||||
ShadowTLSOptions: option.ShadowTLSOutboundOptions{
|
||||
ServerOptions: option.ServerOptions{
|
||||
Server: "127.0.0.1",
|
||||
ServerPort: serverPort,
|
||||
},
|
||||
TLS: &option.OutboundTLSOptions{
|
||||
Enabled: true,
|
||||
ServerName: "google.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: C.TypeDirect,
|
||||
Tag: "direct",
|
||||
},
|
||||
},
|
||||
Route: &option.RouteOptions{
|
||||
Rules: []option.Rule{{
|
||||
DefaultOptions: option.DefaultRule{
|
||||
Inbound: []string{"detour"},
|
||||
Outbound: "direct",
|
||||
},
|
||||
}},
|
||||
},
|
||||
})
|
||||
testTCP(t, clientPort, testPort)
|
||||
}
|
||||
|
||||
func TestShadowTLSOutbound(t *testing.T) {
|
||||
startDockerContainer(t, DockerOptions{
|
||||
Image: ImageShadowTLS,
|
||||
Ports: []uint16{serverPort, otherPort},
|
||||
EntryPoint: "shadow-tls",
|
||||
Cmd: []string{"--threads", "1", "server", "0.0.0.0:" + F.ToString(serverPort), "127.0.0.1:" + F.ToString(otherPort), "google.com:443"},
|
||||
})
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||
ListenPort: clientPort,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "detour",
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||
ListenPort: otherPort,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Outbounds: []option.Outbound{
|
||||
{
|
||||
Type: C.TypeSocks,
|
||||
SocksOptions: option.SocksOutboundOptions{
|
||||
OutboundDialerOptions: option.OutboundDialerOptions{
|
||||
DialerOptions: option.DialerOptions{
|
||||
Detour: "detour",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: C.TypeShadowTLS,
|
||||
Tag: "detour",
|
||||
ShadowTLSOptions: option.ShadowTLSOutboundOptions{
|
||||
ServerOptions: option.ServerOptions{
|
||||
Server: "127.0.0.1",
|
||||
ServerPort: serverPort,
|
||||
},
|
||||
TLS: &option.OutboundTLSOptions{
|
||||
Enabled: true,
|
||||
ServerName: "google.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: C.TypeDirect,
|
||||
Tag: "direct",
|
||||
},
|
||||
},
|
||||
Route: &option.RouteOptions{
|
||||
Rules: []option.Rule{{
|
||||
DefaultOptions: option.DefaultRule{
|
||||
Inbound: []string{"detour"},
|
||||
Outbound: "direct",
|
||||
},
|
||||
}},
|
||||
},
|
||||
})
|
||||
testTCP(t, clientPort, testPort)
|
||||
}
|
Loading…
Reference in a new issue