mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-04-01 19:07:35 +03:00
Add AnyTLS protocol
This commit is contained in:
parent
7799b89e8d
commit
c2a81243bf
15 changed files with 489 additions and 0 deletions
|
@ -19,6 +19,7 @@ const (
|
|||
TypeTor = "tor"
|
||||
TypeSSH = "ssh"
|
||||
TypeShadowTLS = "shadowtls"
|
||||
TypeAnyTLS = "anytls"
|
||||
TypeShadowsocksR = "shadowsocksr"
|
||||
TypeVLESS = "vless"
|
||||
TypeTUIC = "tuic"
|
||||
|
|
39
docs/configuration/inbound/anytls.md
Normal file
39
docs/configuration/inbound/anytls.md
Normal file
|
@ -0,0 +1,39 @@
|
|||
### Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "anytls",
|
||||
"tag": "anytls-in",
|
||||
|
||||
... // Listen Fields
|
||||
|
||||
"users": [
|
||||
{
|
||||
"name": "sekai",
|
||||
"password": "8JCsPssfgS8tiRwiMlhARg=="
|
||||
}
|
||||
],
|
||||
"padding_scheme": [],
|
||||
"tls": {}
|
||||
}
|
||||
```
|
||||
|
||||
### Listen Fields
|
||||
|
||||
See [Listen Fields](/configuration/shared/listen/) for details.
|
||||
|
||||
### Fields
|
||||
|
||||
#### users
|
||||
|
||||
==Required==
|
||||
|
||||
AnyTLS users.
|
||||
|
||||
#### padding_scheme
|
||||
|
||||
AnyTLS padding scheme line array.
|
||||
|
||||
#### tls
|
||||
|
||||
TLS configuration, see [TLS](/configuration/shared/tls/#inbound).
|
39
docs/configuration/inbound/anytls.zh.md
Normal file
39
docs/configuration/inbound/anytls.zh.md
Normal file
|
@ -0,0 +1,39 @@
|
|||
### 结构
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "anytls",
|
||||
"tag": "anytls-in",
|
||||
|
||||
... // 监听字段
|
||||
|
||||
"users": [
|
||||
{
|
||||
"name": "sekai",
|
||||
"password": "8JCsPssfgS8tiRwiMlhARg=="
|
||||
}
|
||||
],
|
||||
"padding_scheme": [],
|
||||
"tls": {}
|
||||
}
|
||||
```
|
||||
|
||||
### 监听字段
|
||||
|
||||
参阅 [监听字段](/zh/configuration/shared/listen/)。
|
||||
|
||||
### 字段
|
||||
|
||||
#### users
|
||||
|
||||
==必填==
|
||||
|
||||
AnyTLS 用户。
|
||||
|
||||
#### padding_scheme
|
||||
|
||||
AnyTLS 填充方案行数组。
|
||||
|
||||
#### tls
|
||||
|
||||
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。
|
|
@ -30,6 +30,7 @@
|
|||
| `tuic` | [TUIC](./tuic/) | :material-close: |
|
||||
| `hysteria2` | [Hysteria2](./hysteria2/) | :material-close: |
|
||||
| `vless` | [VLESS](./vless/) | TCP |
|
||||
| `anytls` | [AnyTLS](./anytls/) | TCP |
|
||||
| `tun` | [Tun](./tun/) | :material-close: |
|
||||
| `redirect` | [Redirect](./redirect/) | :material-close: |
|
||||
| `tproxy` | [TProxy](./tproxy/) | :material-close: |
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
| `tuic` | [TUIC](./tuic/) | :material-close: |
|
||||
| `hysteria2` | [Hysteria2](./hysteria2/) | :material-close: |
|
||||
| `vless` | [VLESS](./vless/) | TCP |
|
||||
| `anytls` | [AnyTLS](./anytls/) | TCP |
|
||||
| `tun` | [Tun](./tun/) | :material-close: |
|
||||
| `redirect` | [Redirect](./redirect/) | :material-close: |
|
||||
| `tproxy` | [TProxy](./tproxy/) | :material-close: |
|
||||
|
|
55
docs/configuration/outbound/anytls.md
Normal file
55
docs/configuration/outbound/anytls.md
Normal file
|
@ -0,0 +1,55 @@
|
|||
### Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "anytls",
|
||||
"tag": "anytls-out",
|
||||
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 1080,
|
||||
"password": "8JCsPssfgS8tiRwiMlhARg==",
|
||||
"idle_session_check_interval": "30s",
|
||||
"idle_session_timeout": "30s",
|
||||
"tls": {},
|
||||
|
||||
... // Dial Fields
|
||||
}
|
||||
```
|
||||
|
||||
### Fields
|
||||
|
||||
#### server
|
||||
|
||||
==Required==
|
||||
|
||||
The server address.
|
||||
|
||||
#### server_port
|
||||
|
||||
==Required==
|
||||
|
||||
The server port.
|
||||
|
||||
#### password
|
||||
|
||||
==Required==
|
||||
|
||||
The AnyTLS password.
|
||||
|
||||
#### idle_session_check_interval
|
||||
|
||||
Interval checking for idle sessions. Default: 30s.
|
||||
|
||||
#### idle_session_timeout
|
||||
|
||||
In the check, close sessions that have been idle for longer than this. Default: 30s.
|
||||
|
||||
#### tls
|
||||
|
||||
==Required==
|
||||
|
||||
TLS configuration, see [TLS](/configuration/shared/tls/#outbound).
|
||||
|
||||
### Dial Fields
|
||||
|
||||
See [Dial Fields](/configuration/shared/dial/) for details.
|
55
docs/configuration/outbound/anytls.zh.md
Normal file
55
docs/configuration/outbound/anytls.zh.md
Normal file
|
@ -0,0 +1,55 @@
|
|||
### 结构
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "anytls",
|
||||
"tag": "anytls-out",
|
||||
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 1080,
|
||||
"password": "8JCsPssfgS8tiRwiMlhARg==",
|
||||
"idle_session_check_interval": "30s",
|
||||
"idle_session_timeout": "30s",
|
||||
"tls": {},
|
||||
|
||||
... // 拨号字段
|
||||
}
|
||||
```
|
||||
|
||||
### 字段
|
||||
|
||||
#### server
|
||||
|
||||
==必填==
|
||||
|
||||
服务器地址。
|
||||
|
||||
#### server_port
|
||||
|
||||
==必填==
|
||||
|
||||
服务器端口。
|
||||
|
||||
#### password
|
||||
|
||||
==必填==
|
||||
|
||||
AnyTLS 密码。
|
||||
|
||||
#### idle_session_check_interval
|
||||
|
||||
检查空闲会话的时间间隔。默认值:30秒。
|
||||
|
||||
#### idle_session_timeout
|
||||
|
||||
在检查中,关闭闲置时间超过此值的会话。默认值:30秒。
|
||||
|
||||
#### tls
|
||||
|
||||
==必填==
|
||||
|
||||
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
|
||||
|
||||
### 拨号字段
|
||||
|
||||
参阅 [拨号字段](/zh/configuration/shared/dial/)。
|
|
@ -30,6 +30,7 @@
|
|||
| `shadowtls` | [ShadowTLS](./shadowtls/) |
|
||||
| `tuic` | [TUIC](./tuic/) |
|
||||
| `hysteria2` | [Hysteria2](./hysteria2/) |
|
||||
| `anytls` | [AnyTLS](./anytls/) |
|
||||
| `tor` | [Tor](./tor/) |
|
||||
| `ssh` | [SSH](./ssh/) |
|
||||
| `dns` | [DNS](./dns/) |
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
| `shadowtls` | [ShadowTLS](./shadowtls/) |
|
||||
| `tuic` | [TUIC](./tuic/) |
|
||||
| `hysteria2` | [Hysteria2](./hysteria2/) |
|
||||
| `anytls` | [AnyTLS](./anytls/) |
|
||||
| `tor` | [Tor](./tor/) |
|
||||
| `ssh` | [SSH](./ssh/) |
|
||||
| `dns` | [DNS](./dns/) |
|
||||
|
|
1
go.mod
1
go.mod
|
@ -3,6 +3,7 @@ module github.com/sagernet/sing-box
|
|||
go 1.23.1
|
||||
|
||||
require (
|
||||
github.com/anytls/sing-anytls v0.0.2
|
||||
github.com/caddyserver/certmagic v0.21.7
|
||||
github.com/cloudflare/circl v1.6.0
|
||||
github.com/cretz/bine v0.2.0
|
||||
|
|
4
go.sum
4
go.sum
|
@ -8,6 +8,10 @@ github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7V
|
|||
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||
github.com/anytls/sing-anytls v0.0.1 h1:Hex6GFUcgATWMWL2E9YgH/7oPgwdokiIF09UQi5BEC0=
|
||||
github.com/anytls/sing-anytls v0.0.1/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8=
|
||||
github.com/anytls/sing-anytls v0.0.2 h1:25azSh0o/LMcIkhS4ZutgRTIGwh8O3wuOhsThVM9K9o=
|
||||
github.com/anytls/sing-anytls v0.0.2/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8=
|
||||
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
|
||||
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/caddyserver/certmagic v0.21.7 h1:66KJioPFJwttL43KYSWk7ErSmE6LfaJgCQuhm8Sg6fg=
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/sagernet/sing-box/dns/transport/local"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/protocol/anytls"
|
||||
"github.com/sagernet/sing-box/protocol/block"
|
||||
"github.com/sagernet/sing-box/protocol/direct"
|
||||
protocolDNS "github.com/sagernet/sing-box/protocol/dns"
|
||||
|
@ -53,6 +54,7 @@ func InboundRegistry() *inbound.Registry {
|
|||
naive.RegisterInbound(registry)
|
||||
shadowtls.RegisterInbound(registry)
|
||||
vless.RegisterInbound(registry)
|
||||
anytls.RegisterInbound(registry)
|
||||
|
||||
registerQUICInbounds(registry)
|
||||
registerStubForRemovedInbounds(registry)
|
||||
|
@ -80,6 +82,7 @@ func OutboundRegistry() *outbound.Registry {
|
|||
ssh.RegisterOutbound(registry)
|
||||
shadowtls.RegisterOutbound(registry)
|
||||
vless.RegisterOutbound(registry)
|
||||
anytls.RegisterOutbound(registry)
|
||||
|
||||
registerQUICOutbounds(registry)
|
||||
registerWireGuardOutbound(registry)
|
||||
|
|
24
option/anytls.go
Normal file
24
option/anytls.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
package option
|
||||
|
||||
import "github.com/sagernet/sing/common/json/badoption"
|
||||
|
||||
type AnyTLSInboundOptions struct {
|
||||
ListenOptions
|
||||
InboundTLSOptionsContainer
|
||||
Users []AnyTLSUser `json:"users,omitempty"`
|
||||
PaddingScheme badoption.Listable[string] `json:"padding_scheme,omitempty"`
|
||||
}
|
||||
|
||||
type AnyTLSUser struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
}
|
||||
|
||||
type AnyTLSOutboundOptions struct {
|
||||
DialerOptions
|
||||
ServerOptions
|
||||
OutboundTLSOptionsContainer
|
||||
Password string `json:"password,omitempty"`
|
||||
IdleSessionCheckInterval badoption.Duration `json:"idle_session_check_interval,omitempty"`
|
||||
IdleSessionTimeout badoption.Duration `json:"idle_session_timeout,omitempty"`
|
||||
}
|
135
protocol/anytls/inbound.go
Normal file
135
protocol/anytls/inbound.go
Normal file
|
@ -0,0 +1,135 @@
|
|||
package anytls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/adapter/inbound"
|
||||
"github.com/sagernet/sing-box/common/listener"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
"github.com/sagernet/sing-box/common/uot"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
anytls "github.com/anytls/sing-anytls"
|
||||
"github.com/anytls/sing-anytls/padding"
|
||||
)
|
||||
|
||||
func RegisterInbound(registry *inbound.Registry) {
|
||||
inbound.Register[option.AnyTLSInboundOptions](registry, C.TypeAnyTLS, NewInbound)
|
||||
}
|
||||
|
||||
type Inbound struct {
|
||||
inbound.Adapter
|
||||
tlsConfig tls.ServerConfig
|
||||
router adapter.ConnectionRouterEx
|
||||
logger logger.ContextLogger
|
||||
listener *listener.Listener
|
||||
service *anytls.Service
|
||||
}
|
||||
|
||||
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.AnyTLSInboundOptions) (adapter.Inbound, error) {
|
||||
inbound := &Inbound{
|
||||
Adapter: inbound.NewAdapter(C.TypeAnyTLS, tag),
|
||||
router: uot.NewRouter(router, logger),
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
if options.TLS != nil && options.TLS.Enabled {
|
||||
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inbound.tlsConfig = tlsConfig
|
||||
}
|
||||
|
||||
paddingScheme := padding.DefaultPaddingScheme
|
||||
if len(options.PaddingScheme) > 0 {
|
||||
paddingScheme = []byte(strings.Join(options.PaddingScheme, "\n"))
|
||||
}
|
||||
|
||||
service, err := anytls.NewService(anytls.ServiceConfig{
|
||||
Users: common.Map(options.Users, func(it option.AnyTLSUser) anytls.User {
|
||||
return (anytls.User)(it)
|
||||
}),
|
||||
PaddingScheme: paddingScheme,
|
||||
Handler: (*inboundHandler)(inbound),
|
||||
Logger: logger,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inbound.service = service
|
||||
inbound.listener = listener.New(listener.Options{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
Network: []string{N.NetworkTCP},
|
||||
Listen: options.ListenOptions,
|
||||
ConnectionHandler: inbound,
|
||||
})
|
||||
return inbound, nil
|
||||
}
|
||||
|
||||
func (h *Inbound) Start(stage adapter.StartStage) error {
|
||||
if stage != adapter.StartStateStart {
|
||||
return nil
|
||||
}
|
||||
if h.tlsConfig != nil {
|
||||
err := h.tlsConfig.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return h.listener.Start()
|
||||
}
|
||||
|
||||
func (h *Inbound) Close() error {
|
||||
return common.Close(h.listener, h.tlsConfig)
|
||||
}
|
||||
|
||||
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
if h.tlsConfig != nil {
|
||||
tlsConn, err := tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
||||
if err != nil {
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source, ": TLS handshake"))
|
||||
return
|
||||
}
|
||||
conn = tlsConn
|
||||
}
|
||||
err := h.service.NewConnection(adapter.WithContext(ctx, &metadata), conn, metadata.Source, onClose)
|
||||
if err != nil {
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
||||
}
|
||||
}
|
||||
|
||||
type inboundHandler Inbound
|
||||
|
||||
func (h *inboundHandler) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
var metadata adapter.InboundContext
|
||||
metadata.Inbound = h.Tag()
|
||||
metadata.InboundType = h.Type()
|
||||
//nolint:staticcheck
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
//nolint:staticcheck
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
metadata.Source = source
|
||||
metadata.Destination = destination
|
||||
if userName, _ := auth.UserFromContext[string](ctx); userName != "" {
|
||||
metadata.User = userName
|
||||
h.logger.InfoContext(ctx, "[", userName, "] inbound connection to ", metadata.Destination)
|
||||
} else {
|
||||
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||
}
|
||||
h.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
129
protocol/anytls/outbound.go
Normal file
129
protocol/anytls/outbound.go
Normal file
|
@ -0,0 +1,129 @@
|
|||
package anytls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/adapter/outbound"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
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"
|
||||
"github.com/sagernet/sing/common/uot"
|
||||
|
||||
anytls "github.com/anytls/sing-anytls"
|
||||
)
|
||||
|
||||
func RegisterOutbound(registry *outbound.Registry) {
|
||||
outbound.Register[option.AnyTLSOutboundOptions](registry, C.TypeAnyTLS, NewOutbound)
|
||||
}
|
||||
|
||||
type Outbound struct {
|
||||
outbound.Adapter
|
||||
dialer N.Dialer
|
||||
server M.Socksaddr
|
||||
tlsConfig tls.Config
|
||||
client *anytls.Client
|
||||
uotClient *uot.Client
|
||||
logger log.ContextLogger
|
||||
}
|
||||
|
||||
func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.AnyTLSOutboundOptions) (adapter.Outbound, error) {
|
||||
outbound := &Outbound{
|
||||
Adapter: outbound.NewAdapterWithDialerOptions(C.TypeAnyTLS, tag, []string{N.NetworkTCP, N.NetworkUDP}, options.DialerOptions),
|
||||
server: options.ServerOptions.Build(),
|
||||
logger: logger,
|
||||
}
|
||||
if options.TLS == nil || !options.TLS.Enabled {
|
||||
return nil, C.ErrTLSRequired
|
||||
}
|
||||
|
||||
tlsConfig, err := tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.tlsConfig = tlsConfig
|
||||
|
||||
outboundDialer, err := dialer.NewWithOptions(dialer.Options{
|
||||
Context: ctx,
|
||||
Options: options.DialerOptions,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.dialer = outboundDialer
|
||||
|
||||
client, err := anytls.NewClient(ctx, anytls.ClientConfig{
|
||||
Password: options.Password,
|
||||
IdleSessionCheckInterval: options.IdleSessionCheckInterval.Build(),
|
||||
IdleSessionTimeout: options.IdleSessionTimeout.Build(),
|
||||
DialOut: outbound.dialOut,
|
||||
Logger: logger,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.client = client
|
||||
|
||||
outbound.uotClient = &uot.Client{
|
||||
Dialer: (anytlsDialer)(client.CreateProxy),
|
||||
Version: uot.Version,
|
||||
}
|
||||
return outbound, nil
|
||||
}
|
||||
|
||||
type anytlsDialer func(ctx context.Context, destination M.Socksaddr) (net.Conn, error)
|
||||
|
||||
func (d anytlsDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
return d(ctx, destination)
|
||||
}
|
||||
|
||||
func (d anytlsDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
func (h *Outbound) dialOut(ctx context.Context) (net.Conn, error) {
|
||||
conn, err := h.dialer.DialContext(ctx, N.NetworkTCP, h.server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConn, err := tls.ClientHandshake(ctx, conn, h.tlsConfig)
|
||||
if err != nil {
|
||||
common.Close(tlsConn, conn)
|
||||
return nil, err
|
||||
}
|
||||
return tlsConn, nil
|
||||
}
|
||||
|
||||
func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
ctx, metadata := adapter.ExtendContext(ctx)
|
||||
metadata.Outbound = h.Tag()
|
||||
metadata.Destination = destination
|
||||
switch N.NetworkName(network) {
|
||||
case N.NetworkTCP:
|
||||
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
||||
return h.client.CreateProxy(ctx, destination)
|
||||
case N.NetworkUDP:
|
||||
h.logger.InfoContext(ctx, "outbound UoT packet connection to ", destination)
|
||||
return h.uotClient.DialContext(ctx, network, destination)
|
||||
}
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
ctx, metadata := adapter.ExtendContext(ctx)
|
||||
metadata.Outbound = h.Tag()
|
||||
metadata.Destination = destination
|
||||
h.logger.InfoContext(ctx, "outbound UoT packet connection to ", destination)
|
||||
return h.uotClient.ListenPacket(ctx, destination)
|
||||
}
|
||||
|
||||
func (h *Outbound) Close() error {
|
||||
return common.Close(h.client)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue