mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-25 18:11:28 +00:00
refactor: Modular inbounds
This commit is contained in:
parent
0b2c7ec35c
commit
47736b27ba
|
@ -5,6 +5,7 @@ import (
|
|||
"net/netip"
|
||||
|
||||
"github.com/sagernet/sing-box/common/process"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
)
|
||||
|
@ -25,6 +26,11 @@ type UDPInjectableInbound interface {
|
|||
PacketConnectionHandlerEx
|
||||
}
|
||||
|
||||
type InboundRegistry interface {
|
||||
option.InboundOptionsRegistry
|
||||
CreateInbound(ctx context.Context, router Router, logger log.ContextLogger, tag string, outboundType string, options any) (Inbound, error)
|
||||
}
|
||||
|
||||
type InboundContext struct {
|
||||
Inbound string
|
||||
InboundType string
|
||||
|
@ -44,6 +50,7 @@ type InboundContext struct {
|
|||
|
||||
// cache
|
||||
|
||||
// Deprecated: implement in rule action
|
||||
InboundDetour string
|
||||
LastInbound string
|
||||
OriginDestination M.Socksaddr
|
||||
|
|
21
adapter/inbound/adapter.go
Normal file
21
adapter/inbound/adapter.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package inbound
|
||||
|
||||
type Adapter struct {
|
||||
inboundType string
|
||||
inboundTag string
|
||||
}
|
||||
|
||||
func NewAdapter(inboundType string, inboundTag string) Adapter {
|
||||
return Adapter{
|
||||
inboundType: inboundType,
|
||||
inboundTag: inboundTag,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Adapter) Type() string {
|
||||
return a.inboundType
|
||||
}
|
||||
|
||||
func (a *Adapter) Tag() string {
|
||||
return a.inboundTag
|
||||
}
|
68
adapter/inbound/registry.go
Normal file
68
adapter/inbound/registry.go
Normal file
|
@ -0,0 +1,68 @@
|
|||
package inbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
type ConstructorFunc[T any] func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options T) (adapter.Inbound, error)
|
||||
|
||||
func Register[Options any](registry *Registry, outboundType string, constructor ConstructorFunc[Options]) {
|
||||
registry.register(outboundType, func() any {
|
||||
return new(Options)
|
||||
}, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options any) (adapter.Inbound, error) {
|
||||
return constructor(ctx, router, logger, tag, common.PtrValueOrDefault(options.(*Options)))
|
||||
})
|
||||
}
|
||||
|
||||
var _ adapter.InboundRegistry = (*Registry)(nil)
|
||||
|
||||
type (
|
||||
optionsConstructorFunc func() any
|
||||
constructorFunc func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options any) (adapter.Inbound, error)
|
||||
)
|
||||
|
||||
type Registry struct {
|
||||
access sync.Mutex
|
||||
optionsType map[string]optionsConstructorFunc
|
||||
constructors map[string]constructorFunc
|
||||
}
|
||||
|
||||
func NewRegistry() *Registry {
|
||||
return &Registry{
|
||||
optionsType: make(map[string]optionsConstructorFunc),
|
||||
constructors: make(map[string]constructorFunc),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Registry) CreateOptions(outboundType string) (any, bool) {
|
||||
r.access.Lock()
|
||||
defer r.access.Unlock()
|
||||
optionsConstructor, loaded := r.optionsType[outboundType]
|
||||
if !loaded {
|
||||
return nil, false
|
||||
}
|
||||
return optionsConstructor(), true
|
||||
}
|
||||
|
||||
func (r *Registry) CreateInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, outboundType string, options any) (adapter.Inbound, error) {
|
||||
r.access.Lock()
|
||||
defer r.access.Unlock()
|
||||
constructor, loaded := r.constructors[outboundType]
|
||||
if !loaded {
|
||||
return nil, E.New("outbound type not found: " + outboundType)
|
||||
}
|
||||
return constructor(ctx, router, logger, tag, options)
|
||||
}
|
||||
|
||||
func (r *Registry) register(outboundType string, optionsConstructor optionsConstructorFunc, constructor constructorFunc) {
|
||||
r.access.Lock()
|
||||
defer r.access.Unlock()
|
||||
r.optionsType[outboundType] = optionsConstructor
|
||||
r.constructors[outboundType] = constructor
|
||||
}
|
|
@ -29,13 +29,13 @@ type (
|
|||
|
||||
type Registry struct {
|
||||
access sync.Mutex
|
||||
optiosnType map[string]optionsConstructorFunc
|
||||
optionsType map[string]optionsConstructorFunc
|
||||
constructors map[string]constructorFunc
|
||||
}
|
||||
|
||||
func NewRegistry() *Registry {
|
||||
return &Registry{
|
||||
optiosnType: make(map[string]optionsConstructorFunc),
|
||||
optionsType: make(map[string]optionsConstructorFunc),
|
||||
constructors: make(map[string]constructorFunc),
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ func NewRegistry() *Registry {
|
|||
func (r *Registry) CreateOptions(outboundType string) (any, bool) {
|
||||
r.access.Lock()
|
||||
defer r.access.Unlock()
|
||||
optionsConstructor, loaded := r.optiosnType[outboundType]
|
||||
optionsConstructor, loaded := r.optionsType[outboundType]
|
||||
if !loaded {
|
||||
return nil, false
|
||||
}
|
||||
|
@ -63,6 +63,6 @@ func (r *Registry) CreateOutbound(ctx context.Context, router adapter.Router, lo
|
|||
func (r *Registry) register(outboundType string, optionsConstructor optionsConstructorFunc, constructor constructorFunc) {
|
||||
r.access.Lock()
|
||||
defer r.access.Unlock()
|
||||
r.optiosnType[outboundType] = optionsConstructor
|
||||
r.optionsType[outboundType] = optionsConstructor
|
||||
r.constructors[outboundType] = constructor
|
||||
}
|
||||
|
|
50
box.go
50
box.go
|
@ -14,7 +14,6 @@ import (
|
|||
"github.com/sagernet/sing-box/experimental"
|
||||
"github.com/sagernet/sing-box/experimental/cachefile"
|
||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||
"github.com/sagernet/sing-box/inbound"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/protocol/direct"
|
||||
|
@ -44,17 +43,20 @@ type Box struct {
|
|||
type Options struct {
|
||||
option.Options
|
||||
Context context.Context
|
||||
PlatformInterface platform.Interface
|
||||
PlatformLogWriter log.PlatformWriter
|
||||
}
|
||||
|
||||
func Context(ctx context.Context, registry adapter.OutboundRegistry) context.Context {
|
||||
if service.FromContext[option.OutboundOptionsRegistry](ctx) != nil &&
|
||||
service.FromContext[adapter.OutboundRegistry](ctx) != nil {
|
||||
return ctx
|
||||
func Context(ctx context.Context, inboundRegistry adapter.InboundRegistry, outboundRegistry adapter.OutboundRegistry) context.Context {
|
||||
if service.FromContext[option.InboundOptionsRegistry](ctx) == nil ||
|
||||
service.FromContext[adapter.InboundRegistry](ctx) == nil {
|
||||
ctx = service.ContextWith[option.InboundOptionsRegistry](ctx, inboundRegistry)
|
||||
ctx = service.ContextWith[adapter.InboundRegistry](ctx, inboundRegistry)
|
||||
}
|
||||
if service.FromContext[option.OutboundOptionsRegistry](ctx) == nil ||
|
||||
service.FromContext[adapter.OutboundRegistry](ctx) == nil {
|
||||
ctx = service.ContextWith[option.OutboundOptionsRegistry](ctx, outboundRegistry)
|
||||
ctx = service.ContextWith[adapter.OutboundRegistry](ctx, outboundRegistry)
|
||||
}
|
||||
ctx = service.ContextWith[option.OutboundOptionsRegistry](ctx, registry)
|
||||
ctx = service.ContextWith[adapter.OutboundRegistry](ctx, registry)
|
||||
return ctx
|
||||
}
|
||||
|
||||
|
@ -64,6 +66,10 @@ func New(options Options) (*Box, error) {
|
|||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
inboundRegistry := service.FromContext[adapter.InboundRegistry](ctx)
|
||||
if inboundRegistry == nil {
|
||||
return nil, E.New("missing inbound registry in context")
|
||||
}
|
||||
outboundRegistry := service.FromContext[adapter.OutboundRegistry](ctx)
|
||||
if outboundRegistry == nil {
|
||||
return nil, E.New("missing outbound registry in context")
|
||||
|
@ -84,8 +90,9 @@ func New(options Options) (*Box, error) {
|
|||
if experimentalOptions.V2RayAPI != nil && experimentalOptions.V2RayAPI.Listen != "" {
|
||||
needV2RayAPI = true
|
||||
}
|
||||
platformInterface := service.FromContext[platform.Interface](ctx)
|
||||
var defaultLogWriter io.Writer
|
||||
if options.PlatformInterface != nil {
|
||||
if platformInterface != nil {
|
||||
defaultLogWriter = io.Discard
|
||||
}
|
||||
logFactory, err := log.New(log.Options{
|
||||
|
@ -106,11 +113,20 @@ func New(options Options) (*Box, error) {
|
|||
common.PtrValueOrDefault(options.DNS),
|
||||
common.PtrValueOrDefault(options.NTP),
|
||||
options.Inbounds,
|
||||
options.PlatformInterface,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse route options")
|
||||
}
|
||||
//nolint:staticcheck
|
||||
if len(options.LegacyInbounds) > 0 {
|
||||
for _, legacyInbound := range options.LegacyInbounds {
|
||||
options.Inbounds = append(options.Inbounds, option.Inbound{
|
||||
Type: legacyInbound.Type,
|
||||
Tag: legacyInbound.Tag,
|
||||
Options: common.Must1(legacyInbound.RawOptions()),
|
||||
})
|
||||
}
|
||||
}
|
||||
inbounds := make([]adapter.Inbound, 0, len(options.Inbounds))
|
||||
//nolint:staticcheck
|
||||
if len(options.LegacyOutbounds) > 0 {
|
||||
|
@ -124,25 +140,25 @@ func New(options Options) (*Box, error) {
|
|||
}
|
||||
outbounds := make([]adapter.Outbound, 0, len(options.Outbounds))
|
||||
for i, inboundOptions := range options.Inbounds {
|
||||
var in adapter.Inbound
|
||||
var currentInbound adapter.Inbound
|
||||
var tag string
|
||||
if inboundOptions.Tag != "" {
|
||||
tag = inboundOptions.Tag
|
||||
} else {
|
||||
tag = F.ToString(i)
|
||||
}
|
||||
in, err = inbound.New(
|
||||
currentInbound, err = inboundRegistry.CreateInbound(
|
||||
ctx,
|
||||
router,
|
||||
logFactory.NewLogger(F.ToString("inbound/", inboundOptions.Type, "[", tag, "]")),
|
||||
tag,
|
||||
inboundOptions,
|
||||
options.PlatformInterface,
|
||||
inboundOptions.Type,
|
||||
inboundOptions.Options,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse inbound[", i, "]")
|
||||
}
|
||||
inbounds = append(inbounds, in)
|
||||
inbounds = append(inbounds, currentInbound)
|
||||
}
|
||||
for i, outboundOptions := range options.Outbounds {
|
||||
var currentOutbound adapter.Outbound
|
||||
|
@ -181,8 +197,8 @@ func New(options Options) (*Box, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if options.PlatformInterface != nil {
|
||||
err = options.PlatformInterface.Initialize(ctx, router)
|
||||
if platformInterface != nil {
|
||||
err = platformInterface.Initialize(ctx, router)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "initialize platform interface")
|
||||
}
|
||||
|
|
|
@ -69,5 +69,5 @@ func preRun(cmd *cobra.Command, args []string) {
|
|||
configPaths = append(configPaths, "config.json")
|
||||
}
|
||||
globalCtx = service.ContextWith(globalCtx, deprecated.NewEnvManager(log.StdLogger()))
|
||||
globalCtx = box.Context(globalCtx, include.OutboundRegistry())
|
||||
globalCtx = box.Context(globalCtx, include.InboundRegistry(), include.OutboundRegistry())
|
||||
}
|
||||
|
|
|
@ -68,15 +68,10 @@ func merge(outputPath string) error {
|
|||
}
|
||||
|
||||
func mergePathResources(options *option.Options) error {
|
||||
for index, inbound := range options.Inbounds {
|
||||
rawOptions, err := inbound.RawOptions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tlsOptions, containsTLSOptions := rawOptions.(option.InboundTLSOptionsWrapper); containsTLSOptions {
|
||||
for _, inbound := range options.Inbounds {
|
||||
if tlsOptions, containsTLSOptions := inbound.Options.(option.InboundTLSOptionsWrapper); containsTLSOptions {
|
||||
tlsOptions.ReplaceInboundTLSOptions(mergeTLSInboundOptions(tlsOptions.TakeInboundTLSOptions()))
|
||||
}
|
||||
options.Inbounds[index] = inbound
|
||||
}
|
||||
for _, outbound := range options.Outbounds {
|
||||
switch outbound.Type {
|
||||
|
|
136
common/listener/listener.go
Normal file
136
common/listener/listener.go
Normal file
|
@ -0,0 +1,136 @@
|
|||
package listener
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/settings"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
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"
|
||||
)
|
||||
|
||||
type Listener struct {
|
||||
ctx context.Context
|
||||
logger logger.ContextLogger
|
||||
network []string
|
||||
listenOptions option.ListenOptions
|
||||
connHandler adapter.ConnectionHandlerEx
|
||||
packetHandler adapter.PacketHandlerEx
|
||||
oobPacketHandler adapter.OOBPacketHandlerEx
|
||||
threadUnsafePacketWriter bool
|
||||
disablePacketOutput bool
|
||||
setSystemProxy bool
|
||||
systemProxySOCKS bool
|
||||
|
||||
tcpListener net.Listener
|
||||
systemProxy settings.SystemProxy
|
||||
udpConn *net.UDPConn
|
||||
udpAddr M.Socksaddr
|
||||
packetOutbound chan *N.PacketBuffer
|
||||
packetOutboundClosed chan struct{}
|
||||
shutdown atomic.Bool
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
Context context.Context
|
||||
Logger logger.ContextLogger
|
||||
Network []string
|
||||
Listen option.ListenOptions
|
||||
ConnectionHandler adapter.ConnectionHandlerEx
|
||||
PacketHandler adapter.PacketHandlerEx
|
||||
OOBPacketHandler adapter.OOBPacketHandlerEx
|
||||
ThreadUnsafePacketWriter bool
|
||||
DisablePacketOutput bool
|
||||
SetSystemProxy bool
|
||||
SystemProxySOCKS bool
|
||||
}
|
||||
|
||||
func New(
|
||||
options Options,
|
||||
) *Listener {
|
||||
return &Listener{
|
||||
ctx: options.Context,
|
||||
logger: options.Logger,
|
||||
network: options.Network,
|
||||
listenOptions: options.Listen,
|
||||
connHandler: options.ConnectionHandler,
|
||||
packetHandler: options.PacketHandler,
|
||||
oobPacketHandler: options.OOBPacketHandler,
|
||||
threadUnsafePacketWriter: options.ThreadUnsafePacketWriter,
|
||||
disablePacketOutput: options.DisablePacketOutput,
|
||||
setSystemProxy: options.SetSystemProxy,
|
||||
systemProxySOCKS: options.SystemProxySOCKS,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Listener) Start() error {
|
||||
if common.Contains(l.network, N.NetworkTCP) {
|
||||
_, err := l.ListenTCP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go l.loopTCPIn()
|
||||
}
|
||||
if common.Contains(l.network, N.NetworkUDP) {
|
||||
_, err := l.ListenUDP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.packetOutboundClosed = make(chan struct{})
|
||||
l.packetOutbound = make(chan *N.PacketBuffer, 64)
|
||||
go l.loopUDPIn()
|
||||
if !l.disablePacketOutput {
|
||||
go l.loopUDPOut()
|
||||
}
|
||||
}
|
||||
if l.setSystemProxy {
|
||||
listenPort := M.SocksaddrFromNet(l.tcpListener.Addr()).Port
|
||||
var listenAddrString string
|
||||
listenAddr := l.listenOptions.Listen.Build()
|
||||
if listenAddr.IsUnspecified() {
|
||||
listenAddrString = "127.0.0.1"
|
||||
} else {
|
||||
listenAddrString = listenAddr.String()
|
||||
}
|
||||
systemProxy, err := settings.NewSystemProxy(l.ctx, M.ParseSocksaddrHostPort(listenAddrString, listenPort), l.systemProxySOCKS)
|
||||
if err != nil {
|
||||
return E.Cause(err, "initialize system proxy")
|
||||
}
|
||||
err = systemProxy.Enable()
|
||||
if err != nil {
|
||||
return E.Cause(err, "set system proxy")
|
||||
}
|
||||
l.systemProxy = systemProxy
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Listener) Close() error {
|
||||
l.shutdown.Store(true)
|
||||
var err error
|
||||
if l.systemProxy != nil && l.systemProxy.IsEnabled() {
|
||||
err = l.systemProxy.Disable()
|
||||
}
|
||||
return E.Errors(err, common.Close(
|
||||
l.tcpListener,
|
||||
common.PtrOrNil(l.udpConn),
|
||||
))
|
||||
}
|
||||
|
||||
func (l *Listener) TCPListener() net.Listener {
|
||||
return l.tcpListener
|
||||
}
|
||||
|
||||
func (l *Listener) UDPConn() *net.UDPConn {
|
||||
return l.udpConn
|
||||
}
|
||||
|
||||
func (l *Listener) ListenOptions() option.ListenOptions {
|
||||
return l.listenOptions
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
//go:build go1.21
|
||||
|
||||
package inbound
|
||||
package listener
|
||||
|
||||
import "net"
|
||||
|
16
common/listener/listener_go123.go
Normal file
16
common/listener/listener_go123.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
//go:build go1.23
|
||||
|
||||
package listener
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
func setKeepAliveConfig(listener *net.ListenConfig, idle time.Duration, interval time.Duration) {
|
||||
listener.KeepAliveConfig = net.KeepAliveConfig{
|
||||
Enable: true,
|
||||
Idle: idle,
|
||||
Interval: interval,
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
//go:build !go1.21
|
||||
|
||||
package inbound
|
||||
package listener
|
||||
|
||||
import "net"
|
||||
|
15
common/listener/listener_nongo123.go
Normal file
15
common/listener/listener_nongo123.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
//go:build !go1.23
|
||||
|
||||
package listener
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing/common/control"
|
||||
)
|
||||
|
||||
func setKeepAliveConfig(listener *net.ListenConfig, idle time.Duration, interval time.Duration) {
|
||||
listener.KeepAlive = idle
|
||||
listener.Control = control.Append(listener.Control, control.SetKeepAlivePeriod(idle, interval))
|
||||
}
|
85
common/listener/listener_tcp.go
Normal file
85
common/listener/listener_tcp.go
Normal file
|
@ -0,0 +1,85 @@
|
|||
package listener
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"github.com/metacubex/tfo-go"
|
||||
)
|
||||
|
||||
func (l *Listener) ListenTCP() (net.Listener, error) {
|
||||
var err error
|
||||
bindAddr := M.SocksaddrFrom(l.listenOptions.Listen.Build(), l.listenOptions.ListenPort)
|
||||
var tcpListener net.Listener
|
||||
var listenConfig net.ListenConfig
|
||||
if l.listenOptions.TCPKeepAlive >= 0 {
|
||||
keepIdle := time.Duration(l.listenOptions.TCPKeepAlive)
|
||||
if keepIdle == 0 {
|
||||
keepIdle = C.TCPKeepAliveInitial
|
||||
}
|
||||
keepInterval := time.Duration(l.listenOptions.TCPKeepAliveInterval)
|
||||
if keepInterval == 0 {
|
||||
keepInterval = C.TCPKeepAliveInterval
|
||||
}
|
||||
setKeepAliveConfig(&listenConfig, keepIdle, keepInterval)
|
||||
}
|
||||
if l.listenOptions.TCPMultiPath {
|
||||
if !go121Available {
|
||||
return nil, E.New("MultiPath TCP requires go1.21, please recompile your binary.")
|
||||
}
|
||||
setMultiPathTCP(&listenConfig)
|
||||
}
|
||||
if l.listenOptions.TCPFastOpen {
|
||||
var tfoConfig tfo.ListenConfig
|
||||
tfoConfig.ListenConfig = listenConfig
|
||||
tcpListener, err = tfoConfig.Listen(l.ctx, M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.String())
|
||||
} else {
|
||||
tcpListener, err = listenConfig.Listen(l.ctx, M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.String())
|
||||
}
|
||||
if err == nil {
|
||||
l.logger.Info("tcp server started at ", tcpListener.Addr())
|
||||
}
|
||||
//nolint:staticcheck
|
||||
if l.listenOptions.ProxyProtocol || l.listenOptions.ProxyProtocolAcceptNoHeader {
|
||||
return nil, E.New("Proxy Protocol is deprecated and removed in sing-box 1.6.0")
|
||||
}
|
||||
l.tcpListener = tcpListener
|
||||
return tcpListener, err
|
||||
}
|
||||
|
||||
func (l *Listener) loopTCPIn() {
|
||||
tcpListener := l.tcpListener
|
||||
var metadata adapter.InboundContext
|
||||
for {
|
||||
conn, err := tcpListener.Accept()
|
||||
if err != nil {
|
||||
//nolint:staticcheck
|
||||
if netError, isNetError := err.(net.Error); isNetError && netError.Temporary() {
|
||||
l.logger.Error(err)
|
||||
continue
|
||||
}
|
||||
if l.shutdown.Load() && E.IsClosed(err) {
|
||||
return
|
||||
}
|
||||
l.tcpListener.Close()
|
||||
l.logger.Error("tcp listener closed: ", err)
|
||||
continue
|
||||
}
|
||||
//nolint:staticcheck
|
||||
metadata.InboundDetour = l.listenOptions.Detour
|
||||
//nolint:staticcheck
|
||||
metadata.InboundOptions = l.listenOptions.InboundOptions
|
||||
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr()).Unwrap()
|
||||
metadata.OriginDestination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
|
||||
ctx := log.ContextWithNewID(l.ctx)
|
||||
l.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||
go l.connHandler.NewConnectionEx(ctx, conn, metadata, nil)
|
||||
}
|
||||
}
|
154
common/listener/listener_udp.go
Normal file
154
common/listener/listener_udp.go
Normal file
|
@ -0,0 +1,154 @@
|
|||
package listener
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
func (l *Listener) ListenUDP() (net.PacketConn, error) {
|
||||
bindAddr := M.SocksaddrFrom(l.listenOptions.Listen.Build(), l.listenOptions.ListenPort)
|
||||
var lc net.ListenConfig
|
||||
var udpFragment bool
|
||||
if l.listenOptions.UDPFragment != nil {
|
||||
udpFragment = *l.listenOptions.UDPFragment
|
||||
} else {
|
||||
udpFragment = l.listenOptions.UDPFragmentDefault
|
||||
}
|
||||
if !udpFragment {
|
||||
lc.Control = control.Append(lc.Control, control.DisableUDPFragment())
|
||||
}
|
||||
udpConn, err := lc.ListenPacket(l.ctx, M.NetworkFromNetAddr(N.NetworkUDP, bindAddr.Addr), bindAddr.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l.udpConn = udpConn.(*net.UDPConn)
|
||||
l.udpAddr = bindAddr
|
||||
l.logger.Info("udp server started at ", udpConn.LocalAddr())
|
||||
return udpConn, err
|
||||
}
|
||||
|
||||
func (l *Listener) UDPAddr() M.Socksaddr {
|
||||
return l.udpAddr
|
||||
}
|
||||
|
||||
func (l *Listener) PacketWriter() N.PacketWriter {
|
||||
return (*packetWriter)(l)
|
||||
}
|
||||
|
||||
func (l *Listener) loopUDPIn() {
|
||||
defer close(l.packetOutboundClosed)
|
||||
var buffer *buf.Buffer
|
||||
if !l.threadUnsafePacketWriter {
|
||||
buffer = buf.NewPacket()
|
||||
defer buffer.Release()
|
||||
}
|
||||
buffer.IncRef()
|
||||
defer buffer.DecRef()
|
||||
if l.oobPacketHandler != nil {
|
||||
oob := make([]byte, 1024)
|
||||
for {
|
||||
if l.threadUnsafePacketWriter {
|
||||
buffer = buf.NewPacket()
|
||||
} else {
|
||||
buffer.Reset()
|
||||
}
|
||||
n, oobN, _, addr, err := l.udpConn.ReadMsgUDPAddrPort(buffer.FreeBytes(), oob)
|
||||
if err != nil {
|
||||
if l.threadUnsafePacketWriter {
|
||||
buffer.Release()
|
||||
}
|
||||
if l.shutdown.Load() && E.IsClosed(err) {
|
||||
return
|
||||
}
|
||||
l.udpConn.Close()
|
||||
l.logger.Error("udp listener closed: ", err)
|
||||
return
|
||||
}
|
||||
buffer.Truncate(n)
|
||||
l.oobPacketHandler.NewPacketEx(buffer, oob[:oobN], M.SocksaddrFromNetIP(addr).Unwrap())
|
||||
}
|
||||
} else {
|
||||
for {
|
||||
if l.threadUnsafePacketWriter {
|
||||
buffer = buf.NewPacket()
|
||||
} else {
|
||||
buffer.Reset()
|
||||
}
|
||||
n, addr, err := l.udpConn.ReadFromUDPAddrPort(buffer.FreeBytes())
|
||||
if err != nil {
|
||||
if l.threadUnsafePacketWriter {
|
||||
buffer.Release()
|
||||
}
|
||||
if l.shutdown.Load() && E.IsClosed(err) {
|
||||
return
|
||||
}
|
||||
l.udpConn.Close()
|
||||
l.logger.Error("udp listener closed: ", err)
|
||||
return
|
||||
}
|
||||
buffer.Truncate(n)
|
||||
l.packetHandler.NewPacketEx(buffer, M.SocksaddrFromNetIP(addr).Unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Listener) loopUDPOut() {
|
||||
for {
|
||||
select {
|
||||
case packet := <-l.packetOutbound:
|
||||
destination := packet.Destination.AddrPort()
|
||||
_, err := l.udpConn.WriteToUDPAddrPort(packet.Buffer.Bytes(), destination)
|
||||
packet.Buffer.Release()
|
||||
N.PutPacketBuffer(packet)
|
||||
if err != nil {
|
||||
if l.shutdown.Load() && E.IsClosed(err) {
|
||||
return
|
||||
}
|
||||
l.udpConn.Close()
|
||||
l.logger.Error("udp listener write back: ", destination, ": ", err)
|
||||
return
|
||||
}
|
||||
continue
|
||||
case <-l.packetOutboundClosed:
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case packet := <-l.packetOutbound:
|
||||
packet.Buffer.Release()
|
||||
N.PutPacketBuffer(packet)
|
||||
case <-time.After(time.Second):
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type packetWriter Listener
|
||||
|
||||
func (w *packetWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
packet := N.NewPacketBuffer()
|
||||
packet.Buffer = buffer
|
||||
packet.Destination = destination
|
||||
select {
|
||||
case w.packetOutbound <- packet:
|
||||
return nil
|
||||
default:
|
||||
buffer.Release()
|
||||
N.PutPacketBuffer(packet)
|
||||
if w.shutdown.Load() {
|
||||
return os.ErrClosed
|
||||
}
|
||||
w.logger.Trace("dropped packet to ", destination)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (w *packetWriter) WriteIsThreadUnsafe() {
|
||||
}
|
|
@ -9,6 +9,8 @@ import (
|
|||
"github.com/sagernet/sing-box"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/process"
|
||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||
"github.com/sagernet/sing-box/include"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
|
@ -16,10 +18,11 @@ import (
|
|||
"github.com/sagernet/sing/common/json"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
"github.com/sagernet/sing/service"
|
||||
)
|
||||
|
||||
func parseConfig(configContent string) (option.Options, error) {
|
||||
options, err := json.UnmarshalExtended[option.Options]([]byte(configContent))
|
||||
func parseConfig(ctx context.Context, configContent string) (option.Options, error) {
|
||||
options, err := json.UnmarshalExtendedContext[option.Options](ctx, []byte(configContent))
|
||||
if err != nil {
|
||||
return option.Options{}, E.Cause(err, "decode config")
|
||||
}
|
||||
|
@ -27,16 +30,16 @@ func parseConfig(configContent string) (option.Options, error) {
|
|||
}
|
||||
|
||||
func CheckConfig(configContent string) error {
|
||||
options, err := parseConfig(configContent)
|
||||
options, err := parseConfig(box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry()), configContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
ctx = service.ContextWith[platform.Interface](ctx, (*platformInterfaceStub)(nil))
|
||||
instance, err := box.New(box.Options{
|
||||
Context: ctx,
|
||||
Options: options,
|
||||
PlatformInterface: (*platformInterfaceStub)(nil),
|
||||
Context: ctx,
|
||||
Options: options,
|
||||
})
|
||||
if err == nil {
|
||||
instance.Close()
|
||||
|
@ -138,7 +141,7 @@ func (s *platformInterfaceStub) OpenURL(url string) {
|
|||
}
|
||||
|
||||
func FormatConfig(configContent string) (string, error) {
|
||||
options, err := parseConfig(configContent)
|
||||
options, err := parseConfig(box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry()), configContent)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
|
@ -42,22 +42,22 @@ type BoxService struct {
|
|||
}
|
||||
|
||||
func NewService(configContent string, platformInterface PlatformInterface) (*BoxService, error) {
|
||||
options, err := parseConfig(configContent)
|
||||
ctx := box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry())
|
||||
options, err := parseConfig(ctx, configContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
runtimeDebug.FreeOSMemory()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx = box.Context(ctx, include.OutboundRegistry())
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
ctx = filemanager.WithDefault(ctx, sWorkingPath, sTempPath, sUserID, sGroupID)
|
||||
urlTestHistoryStorage := urltest.NewHistoryStorage()
|
||||
ctx = service.ContextWithPtr(ctx, urlTestHistoryStorage)
|
||||
ctx = service.ContextWith[deprecated.Manager](ctx, new(deprecatedManager))
|
||||
platformWrapper := &platformInterfaceWrapper{iif: platformInterface, useProcFS: platformInterface.UseProcFS()}
|
||||
ctx = service.ContextWith[platform.Interface](ctx, platformWrapper)
|
||||
instance, err := box.New(box.Options{
|
||||
Context: ctx,
|
||||
Options: options,
|
||||
PlatformInterface: platformWrapper,
|
||||
PlatformLogWriter: platformWrapper,
|
||||
})
|
||||
if err != nil {
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
package inbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.Inbound, platformInterface platform.Interface) (adapter.Inbound, error) {
|
||||
if options.Type == "" {
|
||||
return nil, E.New("missing inbound type")
|
||||
}
|
||||
switch options.Type {
|
||||
case C.TypeTun:
|
||||
return NewTun(ctx, router, logger, tag, options.TunOptions, platformInterface)
|
||||
case C.TypeRedirect:
|
||||
return NewRedirect(ctx, router, logger, tag, options.RedirectOptions), nil
|
||||
case C.TypeTProxy:
|
||||
return NewTProxy(ctx, router, logger, tag, options.TProxyOptions), nil
|
||||
case C.TypeDirect:
|
||||
return NewDirect(ctx, router, logger, tag, options.DirectOptions), nil
|
||||
case C.TypeSOCKS:
|
||||
return NewSocks(ctx, router, logger, tag, options.SocksOptions), nil
|
||||
case C.TypeHTTP:
|
||||
return NewHTTP(ctx, router, logger, tag, options.HTTPOptions)
|
||||
case C.TypeMixed:
|
||||
return NewMixed(ctx, router, logger, tag, options.MixedOptions), nil
|
||||
case C.TypeShadowsocks:
|
||||
return NewShadowsocks(ctx, router, logger, tag, options.ShadowsocksOptions)
|
||||
case C.TypeVMess:
|
||||
return NewVMess(ctx, router, logger, tag, options.VMessOptions)
|
||||
case C.TypeTrojan:
|
||||
return NewTrojan(ctx, router, logger, tag, options.TrojanOptions)
|
||||
case C.TypeNaive:
|
||||
return NewNaive(ctx, router, logger, tag, options.NaiveOptions)
|
||||
case C.TypeHysteria:
|
||||
return NewHysteria(ctx, router, logger, tag, options.HysteriaOptions)
|
||||
case C.TypeShadowTLS:
|
||||
return NewShadowTLS(ctx, router, logger, tag, options.ShadowTLSOptions)
|
||||
case C.TypeVLESS:
|
||||
return NewVLESS(ctx, router, logger, tag, options.VLESSOptions)
|
||||
case C.TypeTUIC:
|
||||
return NewTUIC(ctx, router, logger, tag, options.TUICOptions)
|
||||
case C.TypeHysteria2:
|
||||
return NewHysteria2(ctx, router, logger, tag, options.Hysteria2Options)
|
||||
default:
|
||||
return nil, E.New("unknown inbound type: ", options.Type)
|
||||
}
|
||||
}
|
|
@ -1,209 +0,0 @@
|
|||
package inbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/settings"
|
||||
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/atomic"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
var _ adapter.Inbound = (*myInboundAdapter)(nil)
|
||||
|
||||
type myInboundAdapter struct {
|
||||
protocol string
|
||||
network []string
|
||||
ctx context.Context
|
||||
router adapter.ConnectionRouterEx
|
||||
logger log.ContextLogger
|
||||
tag string
|
||||
listenOptions option.ListenOptions
|
||||
connHandler adapter.ConnectionHandlerEx
|
||||
packetHandler adapter.PacketHandlerEx
|
||||
oobPacketHandler adapter.OOBPacketHandlerEx
|
||||
packetUpstream any
|
||||
|
||||
// http mixed
|
||||
|
||||
setSystemProxy bool
|
||||
systemProxy settings.SystemProxy
|
||||
|
||||
// internal
|
||||
|
||||
tcpListener net.Listener
|
||||
udpConn *net.UDPConn
|
||||
udpAddr M.Socksaddr
|
||||
packetOutboundClosed chan struct{}
|
||||
packetOutbound chan *myInboundPacket
|
||||
|
||||
inShutdown atomic.Bool
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) Type() string {
|
||||
return a.protocol
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) Tag() string {
|
||||
return a.tag
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) Start() error {
|
||||
var err error
|
||||
if common.Contains(a.network, N.NetworkTCP) {
|
||||
_, err = a.ListenTCP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go a.loopTCPIn()
|
||||
}
|
||||
if common.Contains(a.network, N.NetworkUDP) {
|
||||
_, err = a.ListenUDP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.packetOutboundClosed = make(chan struct{})
|
||||
a.packetOutbound = make(chan *myInboundPacket)
|
||||
if a.oobPacketHandler != nil {
|
||||
if _, threadUnsafeHandler := common.Cast[N.ThreadUnsafeWriter](a.packetUpstream); !threadUnsafeHandler {
|
||||
go a.loopUDPOOBIn()
|
||||
} else {
|
||||
go a.loopUDPOOBInThreadSafe()
|
||||
}
|
||||
} else {
|
||||
if _, threadUnsafeHandler := common.Cast[N.ThreadUnsafeWriter](a.packetUpstream); !threadUnsafeHandler {
|
||||
go a.loopUDPIn()
|
||||
} else {
|
||||
go a.loopUDPInThreadSafe()
|
||||
}
|
||||
go a.loopUDPOut()
|
||||
}
|
||||
}
|
||||
if a.setSystemProxy {
|
||||
listenPort := M.SocksaddrFromNet(a.tcpListener.Addr()).Port
|
||||
var listenAddrString string
|
||||
listenAddr := a.listenOptions.Listen.Build()
|
||||
if listenAddr.IsUnspecified() {
|
||||
listenAddrString = "127.0.0.1"
|
||||
} else {
|
||||
listenAddrString = listenAddr.String()
|
||||
}
|
||||
var systemProxy settings.SystemProxy
|
||||
systemProxy, err = settings.NewSystemProxy(a.ctx, M.ParseSocksaddrHostPort(listenAddrString, listenPort), a.protocol == C.TypeMixed)
|
||||
if err != nil {
|
||||
return E.Cause(err, "initialize system proxy")
|
||||
}
|
||||
err = systemProxy.Enable()
|
||||
if err != nil {
|
||||
return E.Cause(err, "set system proxy")
|
||||
}
|
||||
a.systemProxy = systemProxy
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) Close() error {
|
||||
a.inShutdown.Store(true)
|
||||
var err error
|
||||
if a.systemProxy != nil && a.systemProxy.IsEnabled() {
|
||||
err = a.systemProxy.Disable()
|
||||
}
|
||||
return E.Errors(err, common.Close(
|
||||
a.tcpListener,
|
||||
common.PtrOrNil(a.udpConn),
|
||||
))
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) upstreamHandler(metadata adapter.InboundContext) adapter.UpstreamHandlerAdapter {
|
||||
return adapter.NewUpstreamHandler(metadata, a.newConnection, a.streamPacketConnection, a)
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) upstreamContextHandler() adapter.UpstreamHandlerAdapter {
|
||||
return adapter.NewUpstreamContextHandler(a.newConnection, a.newPacketConnection, a)
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
a.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||
return a.router.RouteConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) streamPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
a.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||
return a.router.RoutePacketConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
a.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
|
||||
a.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||
return a.router.RoutePacketConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) upstreamHandlerEx(metadata adapter.InboundContext) adapter.UpstreamHandlerAdapterEx {
|
||||
return adapter.NewUpstreamHandlerEx(metadata, a.newConnectionEx, a.streamPacketConnectionEx)
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) upstreamContextHandlerEx() adapter.UpstreamHandlerAdapterEx {
|
||||
return adapter.NewUpstreamContextHandlerEx(a.newConnectionEx, a.newPacketConnectionEx)
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) newConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
a.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||
a.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) newPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
a.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
|
||||
a.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||
a.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) streamPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
a.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||
a.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) createMetadata(conn net.Conn, metadata adapter.InboundContext) adapter.InboundContext {
|
||||
metadata.Inbound = a.tag
|
||||
metadata.InboundType = a.protocol
|
||||
metadata.InboundDetour = a.listenOptions.Detour
|
||||
metadata.InboundOptions = a.listenOptions.InboundOptions
|
||||
if !metadata.Source.IsValid() {
|
||||
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr()).Unwrap()
|
||||
}
|
||||
if !metadata.Destination.IsValid() {
|
||||
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
|
||||
}
|
||||
if tcpConn, isTCP := common.Cast[*net.TCPConn](conn); isTCP {
|
||||
metadata.OriginDestination = M.SocksaddrFromNet(tcpConn.LocalAddr()).Unwrap()
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
|
||||
// Deprecated: don't use
|
||||
func (a *myInboundAdapter) newError(err error) {
|
||||
a.logger.Error(err)
|
||||
}
|
||||
|
||||
// Deprecated: don't use
|
||||
func (a *myInboundAdapter) NewError(ctx context.Context, err error) {
|
||||
NewError(a.logger, ctx, err)
|
||||
}
|
||||
|
||||
// Deprecated: don't use
|
||||
func NewError(logger log.ContextLogger, ctx context.Context, err error) {
|
||||
common.Close(err)
|
||||
if E.IsClosedOrCanceled(err) {
|
||||
logger.DebugContext(ctx, "connection closed: ", err)
|
||||
return
|
||||
}
|
||||
logger.ErrorContext(ctx, err)
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
package inbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
func (a *myInboundAdapter) ListenTCP() (net.Listener, error) {
|
||||
var err error
|
||||
bindAddr := M.SocksaddrFrom(a.listenOptions.Listen.Build(), a.listenOptions.ListenPort)
|
||||
var tcpListener net.Listener
|
||||
var listenConfig net.ListenConfig
|
||||
// TODO: Add an option to customize the keep alive period
|
||||
listenConfig.KeepAlive = C.TCPKeepAliveInitial
|
||||
listenConfig.Control = control.Append(listenConfig.Control, control.SetKeepAlivePeriod(C.TCPKeepAliveInitial, C.TCPKeepAliveInterval))
|
||||
if a.listenOptions.TCPMultiPath {
|
||||
if !go121Available {
|
||||
return nil, E.New("MultiPath TCP requires go1.21, please recompile your binary.")
|
||||
}
|
||||
setMultiPathTCP(&listenConfig)
|
||||
}
|
||||
if a.listenOptions.TCPFastOpen {
|
||||
if !go120Available {
|
||||
return nil, E.New("TCP Fast Open requires go1.20, please recompile your binary.")
|
||||
}
|
||||
tcpListener, err = listenTFO(listenConfig, a.ctx, M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.String())
|
||||
} else {
|
||||
tcpListener, err = listenConfig.Listen(a.ctx, M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.String())
|
||||
}
|
||||
if err == nil {
|
||||
a.logger.Info("tcp server started at ", tcpListener.Addr())
|
||||
}
|
||||
if a.listenOptions.ProxyProtocol || a.listenOptions.ProxyProtocolAcceptNoHeader {
|
||||
return nil, E.New("Proxy Protocol is deprecated and removed in sing-box 1.6.0")
|
||||
}
|
||||
a.tcpListener = tcpListener
|
||||
return tcpListener, err
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) loopTCPIn() {
|
||||
tcpListener := a.tcpListener
|
||||
for {
|
||||
conn, err := tcpListener.Accept()
|
||||
if err != nil {
|
||||
//goland:noinspection GoDeprecation
|
||||
//nolint:staticcheck
|
||||
if netError, isNetError := err.(net.Error); isNetError && netError.Temporary() {
|
||||
a.logger.Error(err)
|
||||
continue
|
||||
}
|
||||
if a.inShutdown.Load() && E.IsClosed(err) {
|
||||
return
|
||||
}
|
||||
a.tcpListener.Close()
|
||||
a.logger.Error("serve error: ", err)
|
||||
continue
|
||||
}
|
||||
go a.injectTCP(conn, adapter.InboundContext{})
|
||||
}
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) injectTCP(conn net.Conn, metadata adapter.InboundContext) {
|
||||
ctx := log.ContextWithNewID(a.ctx)
|
||||
metadata = a.createMetadata(conn, metadata)
|
||||
a.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||
a.connHandler.NewConnectionEx(ctx, conn, metadata, nil)
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) routeTCP(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
metadata := a.createMetadata(conn, adapter.InboundContext{
|
||||
Source: source,
|
||||
Destination: destination,
|
||||
})
|
||||
a.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||
a.connHandler.NewConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
//go:build go1.20
|
||||
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/metacubex/tfo-go"
|
||||
)
|
||||
|
||||
const go120Available = true
|
||||
|
||||
func listenTFO(listenConfig net.ListenConfig, ctx context.Context, network string, address string) (net.Listener, error) {
|
||||
var tfoConfig tfo.ListenConfig
|
||||
tfoConfig.ListenConfig = listenConfig
|
||||
return tfoConfig.Listen(ctx, network, address)
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
//go:build !go1.20
|
||||
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
const go120Available = false
|
||||
|
||||
func listenTFO(listenConfig net.ListenConfig, ctx context.Context, network string, address string) (net.Listener, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
|
@ -1,208 +0,0 @@
|
|||
package inbound
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
func (a *myInboundAdapter) ListenUDP() (net.PacketConn, error) {
|
||||
bindAddr := M.SocksaddrFrom(a.listenOptions.Listen.Build(), a.listenOptions.ListenPort)
|
||||
var lc net.ListenConfig
|
||||
var udpFragment bool
|
||||
if a.listenOptions.UDPFragment != nil {
|
||||
udpFragment = *a.listenOptions.UDPFragment
|
||||
} else {
|
||||
udpFragment = a.listenOptions.UDPFragmentDefault
|
||||
}
|
||||
if !udpFragment {
|
||||
lc.Control = control.Append(lc.Control, control.DisableUDPFragment())
|
||||
}
|
||||
udpConn, err := lc.ListenPacket(a.ctx, M.NetworkFromNetAddr(N.NetworkUDP, bindAddr.Addr), bindAddr.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a.udpConn = udpConn.(*net.UDPConn)
|
||||
a.udpAddr = bindAddr
|
||||
a.logger.Info("udp server started at ", udpConn.LocalAddr())
|
||||
return udpConn, err
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) loopUDPIn() {
|
||||
defer close(a.packetOutboundClosed)
|
||||
buffer := buf.NewPacket()
|
||||
defer buffer.Release()
|
||||
buffer.IncRef()
|
||||
defer buffer.DecRef()
|
||||
for {
|
||||
buffer.Reset()
|
||||
n, addr, err := a.udpConn.ReadFromUDPAddrPort(buffer.FreeBytes())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
buffer.Truncate(n)
|
||||
a.packetHandler.NewPacketEx(buffer, M.SocksaddrFromNetIP(addr).Unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) loopUDPOOBIn() {
|
||||
defer close(a.packetOutboundClosed)
|
||||
buffer := buf.NewPacket()
|
||||
defer buffer.Release()
|
||||
buffer.IncRef()
|
||||
defer buffer.DecRef()
|
||||
oob := make([]byte, 1024)
|
||||
for {
|
||||
buffer.Reset()
|
||||
n, oobN, _, addr, err := a.udpConn.ReadMsgUDPAddrPort(buffer.FreeBytes(), oob)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
buffer.Truncate(n)
|
||||
a.oobPacketHandler.NewPacketEx(buffer, oob[:oobN], M.SocksaddrFromNetIP(addr).Unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) loopUDPInThreadSafe() {
|
||||
defer close(a.packetOutboundClosed)
|
||||
for {
|
||||
buffer := buf.NewPacket()
|
||||
n, addr, err := a.udpConn.ReadFromUDPAddrPort(buffer.FreeBytes())
|
||||
if err != nil {
|
||||
buffer.Release()
|
||||
return
|
||||
}
|
||||
buffer.Truncate(n)
|
||||
a.packetHandler.NewPacketEx(buffer, M.SocksaddrFromNetIP(addr).Unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) loopUDPOOBInThreadSafe() {
|
||||
defer close(a.packetOutboundClosed)
|
||||
oob := make([]byte, 1024)
|
||||
for {
|
||||
buffer := buf.NewPacket()
|
||||
n, oobN, _, addr, err := a.udpConn.ReadMsgUDPAddrPort(buffer.FreeBytes(), oob)
|
||||
if err != nil {
|
||||
buffer.Release()
|
||||
return
|
||||
}
|
||||
buffer.Truncate(n)
|
||||
a.oobPacketHandler.NewPacketEx(buffer, oob[:oobN], M.SocksaddrFromNetIP(addr).Unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) loopUDPOut() {
|
||||
for {
|
||||
select {
|
||||
case packet := <-a.packetOutbound:
|
||||
err := a.writePacket(packet.buffer, packet.destination)
|
||||
if err != nil && !E.IsClosed(err) {
|
||||
a.logger.Error(E.New("write back udp: ", err))
|
||||
}
|
||||
continue
|
||||
case <-a.packetOutboundClosed:
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case packet := <-a.packetOutbound:
|
||||
packet.buffer.Release()
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) packetConn() N.PacketConn {
|
||||
return (*myInboundPacketAdapter)(a)
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) createPacketMetadata(conn N.PacketConn, metadata adapter.InboundContext) adapter.InboundContext {
|
||||
metadata.Inbound = a.tag
|
||||
metadata.InboundType = a.protocol
|
||||
metadata.InboundDetour = a.listenOptions.Detour
|
||||
metadata.InboundOptions = a.listenOptions.InboundOptions
|
||||
if !metadata.Destination.IsValid() {
|
||||
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
|
||||
}
|
||||
metadata.OriginDestination = a.udpAddr
|
||||
return metadata
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) createPacketMetadataEx(source M.Socksaddr, destination M.Socksaddr) adapter.InboundContext {
|
||||
var metadata adapter.InboundContext
|
||||
metadata.Inbound = a.tag
|
||||
metadata.InboundType = a.protocol
|
||||
metadata.InboundDetour = a.listenOptions.Detour
|
||||
metadata.InboundOptions = a.listenOptions.InboundOptions
|
||||
metadata.Source = source
|
||||
metadata.Destination = destination
|
||||
metadata.OriginDestination = a.udpAddr
|
||||
return metadata
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) writePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
defer buffer.Release()
|
||||
return common.Error(a.udpConn.WriteToUDPAddrPort(buffer.Bytes(), destination.AddrPort()))
|
||||
}
|
||||
|
||||
type myInboundPacketAdapter myInboundAdapter
|
||||
|
||||
func (s *myInboundPacketAdapter) ReadPacket(buffer *buf.Buffer) (M.Socksaddr, error) {
|
||||
n, addr, err := s.udpConn.ReadFromUDPAddrPort(buffer.FreeBytes())
|
||||
if err != nil {
|
||||
return M.Socksaddr{}, err
|
||||
}
|
||||
buffer.Truncate(n)
|
||||
return M.SocksaddrFromNetIP(addr), nil
|
||||
}
|
||||
|
||||
func (s *myInboundPacketAdapter) WriteIsThreadUnsafe() {
|
||||
}
|
||||
|
||||
type myInboundPacket struct {
|
||||
buffer *buf.Buffer
|
||||
destination M.Socksaddr
|
||||
}
|
||||
|
||||
func (s *myInboundPacketAdapter) Upstream() any {
|
||||
return s.udpConn
|
||||
}
|
||||
|
||||
func (s *myInboundPacketAdapter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
select {
|
||||
case s.packetOutbound <- &myInboundPacket{buffer, destination}:
|
||||
return nil
|
||||
case <-s.packetOutboundClosed:
|
||||
return os.ErrClosed
|
||||
}
|
||||
}
|
||||
|
||||
func (s *myInboundPacketAdapter) Close() error {
|
||||
return s.udpConn.Close()
|
||||
}
|
||||
|
||||
func (s *myInboundPacketAdapter) LocalAddr() net.Addr {
|
||||
return s.udpConn.LocalAddr()
|
||||
}
|
||||
|
||||
func (s *myInboundPacketAdapter) SetDeadline(t time.Time) error {
|
||||
return s.udpConn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (s *myInboundPacketAdapter) SetReadDeadline(t time.Time) error {
|
||||
return s.udpConn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (s *myInboundPacketAdapter) SetWriteDeadline(t time.Time) error {
|
||||
return s.udpConn.SetWriteDeadline(t)
|
||||
}
|
|
@ -1,111 +0,0 @@
|
|||
package inbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/udpnat2"
|
||||
)
|
||||
|
||||
var _ adapter.Inbound = (*Direct)(nil)
|
||||
|
||||
type Direct struct {
|
||||
myInboundAdapter
|
||||
udpNat *udpnat.Service
|
||||
overrideOption int
|
||||
overrideDestination M.Socksaddr
|
||||
}
|
||||
|
||||
func NewDirect(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.DirectInboundOptions) *Direct {
|
||||
options.UDPFragmentDefault = true
|
||||
inbound := &Direct{
|
||||
myInboundAdapter: myInboundAdapter{
|
||||
protocol: C.TypeDirect,
|
||||
network: options.Network.Build(),
|
||||
ctx: ctx,
|
||||
router: router,
|
||||
logger: logger,
|
||||
tag: tag,
|
||||
listenOptions: options.ListenOptions,
|
||||
},
|
||||
}
|
||||
if options.OverrideAddress != "" && options.OverridePort != 0 {
|
||||
inbound.overrideOption = 1
|
||||
inbound.overrideDestination = M.ParseSocksaddrHostPort(options.OverrideAddress, options.OverridePort)
|
||||
} else if options.OverrideAddress != "" {
|
||||
inbound.overrideOption = 2
|
||||
inbound.overrideDestination = M.ParseSocksaddrHostPort(options.OverrideAddress, options.OverridePort)
|
||||
} else if options.OverridePort != 0 {
|
||||
inbound.overrideOption = 3
|
||||
inbound.overrideDestination = M.Socksaddr{Port: options.OverridePort}
|
||||
}
|
||||
var udpTimeout time.Duration
|
||||
if options.UDPTimeout != 0 {
|
||||
udpTimeout = time.Duration(options.UDPTimeout)
|
||||
} else {
|
||||
udpTimeout = C.UDPTimeout
|
||||
}
|
||||
inbound.udpNat = udpnat.New(inbound, inbound.preparePacketConnection, udpTimeout)
|
||||
inbound.connHandler = inbound
|
||||
inbound.packetHandler = inbound
|
||||
return inbound
|
||||
}
|
||||
|
||||
func (d *Direct) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
switch d.overrideOption {
|
||||
case 1:
|
||||
metadata.Destination = d.overrideDestination
|
||||
case 2:
|
||||
destination := d.overrideDestination
|
||||
destination.Port = metadata.Destination.Port
|
||||
metadata.Destination = destination
|
||||
case 3:
|
||||
metadata.Destination.Port = d.overrideDestination.Port
|
||||
}
|
||||
d.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||
return d.router.RouteConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (d *Direct) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
|
||||
var destination M.Socksaddr
|
||||
switch d.overrideOption {
|
||||
case 1:
|
||||
destination = d.overrideDestination
|
||||
case 2:
|
||||
destination = d.overrideDestination
|
||||
destination.Port = source.Port
|
||||
case 3:
|
||||
destination = source
|
||||
destination.Port = d.overrideDestination.Port
|
||||
}
|
||||
d.udpNat.NewPacket([][]byte{buffer.Bytes()}, source, destination, nil)
|
||||
}
|
||||
|
||||
func (d *Direct) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
d.newConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
func (d *Direct) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
d.newPacketConnectionEx(ctx, conn, d.createPacketMetadataEx(source, destination), onClose)
|
||||
}
|
||||
|
||||
func (d *Direct) preparePacketConnection(source M.Socksaddr, destination M.Socksaddr, userData any) (bool, context.Context, N.PacketWriter, N.CloseHandlerFunc) {
|
||||
return true, d.ctx, &directPacketWriter{d.packetConn(), source}, nil
|
||||
}
|
||||
|
||||
type directPacketWriter struct {
|
||||
writer N.PacketWriter
|
||||
source M.Socksaddr
|
||||
}
|
||||
|
||||
func (w *directPacketWriter) WritePacket(buffer *buf.Buffer, addr M.Socksaddr) error {
|
||||
return w.writer.WritePacket(buffer, w.source)
|
||||
}
|
119
inbound/http.go
119
inbound/http.go
|
@ -1,119 +0,0 @@
|
|||
package inbound
|
||||
|
||||
import (
|
||||
std_bufio "bufio"
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"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"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/protocol/http"
|
||||
)
|
||||
|
||||
var (
|
||||
_ adapter.Inbound = (*HTTP)(nil)
|
||||
_ adapter.TCPInjectableInbound = (*HTTP)(nil)
|
||||
)
|
||||
|
||||
type HTTP struct {
|
||||
myInboundAdapter
|
||||
authenticator *auth.Authenticator
|
||||
tlsConfig tls.ServerConfig
|
||||
}
|
||||
|
||||
func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPMixedInboundOptions) (*HTTP, error) {
|
||||
inbound := &HTTP{
|
||||
myInboundAdapter: myInboundAdapter{
|
||||
protocol: C.TypeHTTP,
|
||||
network: []string{N.NetworkTCP},
|
||||
ctx: ctx,
|
||||
router: uot.NewRouter(router, logger),
|
||||
logger: logger,
|
||||
tag: tag,
|
||||
listenOptions: options.ListenOptions,
|
||||
setSystemProxy: options.SetSystemProxy,
|
||||
},
|
||||
authenticator: auth.NewAuthenticator(options.Users),
|
||||
}
|
||||
if options.TLS != nil {
|
||||
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inbound.tlsConfig = tlsConfig
|
||||
}
|
||||
inbound.connHandler = inbound
|
||||
return inbound, nil
|
||||
}
|
||||
|
||||
func (h *HTTP) Start() error {
|
||||
if h.tlsConfig != nil {
|
||||
err := h.tlsConfig.Start()
|
||||
if err != nil {
|
||||
return E.Cause(err, "create TLS config")
|
||||
}
|
||||
}
|
||||
return h.myInboundAdapter.Start()
|
||||
}
|
||||
|
||||
func (h *HTTP) Close() error {
|
||||
return common.Close(
|
||||
&h.myInboundAdapter,
|
||||
h.tlsConfig,
|
||||
)
|
||||
}
|
||||
|
||||
func (h *HTTP) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
err := h.newConnection(ctx, conn, metadata, onClose)
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil {
|
||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HTTP) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) error {
|
||||
var err error
|
||||
if h.tlsConfig != nil {
|
||||
conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return http.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, nil, h.upstreamUserHandlerEx(metadata), metadata.Source, onClose)
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) upstreamUserHandlerEx(metadata adapter.InboundContext) adapter.UpstreamHandlerAdapterEx {
|
||||
return adapter.NewUpstreamHandlerEx(metadata, a.newUserConnection, a.streamUserPacketConnection)
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) newUserConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
user, loaded := auth.UserFromContext[string](ctx)
|
||||
if !loaded {
|
||||
a.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||
a.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
||||
return
|
||||
}
|
||||
metadata.User = user
|
||||
a.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination)
|
||||
a.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) streamUserPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
user, loaded := auth.UserFromContext[string](ctx)
|
||||
if !loaded {
|
||||
a.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||
a.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
return
|
||||
}
|
||||
metadata.User = user
|
||||
a.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
||||
a.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
//go:build !with_quic
|
||||
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
)
|
||||
|
||||
func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HysteriaInboundOptions) (adapter.Inbound, error) {
|
||||
return nil, C.ErrQUICNotIncluded
|
||||
}
|
||||
|
||||
func NewHysteria2(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.Hysteria2InboundOptions) (adapter.Inbound, error) {
|
||||
return nil, C.ErrQUICNotIncluded
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
package inbound
|
||||
|
||||
import (
|
||||
std_bufio "bufio"
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"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/auth"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/protocol/http"
|
||||
"github.com/sagernet/sing/protocol/socks"
|
||||
"github.com/sagernet/sing/protocol/socks/socks4"
|
||||
"github.com/sagernet/sing/protocol/socks/socks5"
|
||||
)
|
||||
|
||||
var (
|
||||
_ adapter.Inbound = (*Mixed)(nil)
|
||||
_ adapter.TCPInjectableInbound = (*Mixed)(nil)
|
||||
)
|
||||
|
||||
type Mixed struct {
|
||||
myInboundAdapter
|
||||
authenticator *auth.Authenticator
|
||||
}
|
||||
|
||||
func NewMixed(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPMixedInboundOptions) *Mixed {
|
||||
inbound := &Mixed{
|
||||
myInboundAdapter{
|
||||
protocol: C.TypeMixed,
|
||||
network: []string{N.NetworkTCP},
|
||||
ctx: ctx,
|
||||
router: uot.NewRouter(router, logger),
|
||||
logger: logger,
|
||||
tag: tag,
|
||||
listenOptions: options.ListenOptions,
|
||||
setSystemProxy: options.SetSystemProxy,
|
||||
},
|
||||
auth.NewAuthenticator(options.Users),
|
||||
}
|
||||
inbound.connHandler = inbound
|
||||
return inbound
|
||||
}
|
||||
|
||||
func (h *Mixed) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
err := h.newConnection(ctx, conn, metadata, onClose)
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil {
|
||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Mixed) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) error {
|
||||
reader := std_bufio.NewReader(conn)
|
||||
headerBytes, err := reader.Peek(1)
|
||||
if err != nil {
|
||||
return E.Cause(err, "peek first byte")
|
||||
}
|
||||
switch headerBytes[0] {
|
||||
case socks4.Version, socks5.Version:
|
||||
return socks.HandleConnectionEx(ctx, conn, reader, h.authenticator, nil, h.upstreamUserHandlerEx(metadata), metadata.Source, metadata.Destination, onClose)
|
||||
default:
|
||||
return http.HandleConnectionEx(ctx, conn, reader, h.authenticator, nil, h.upstreamUserHandlerEx(metadata), metadata.Source, onClose)
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
//go:build with_quic
|
||||
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"github.com/sagernet/quic-go"
|
||||
"github.com/sagernet/quic-go/http3"
|
||||
"github.com/sagernet/sing-quic"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
func (n *Naive) configureHTTP3Listener() error {
|
||||
err := qtls.ConfigureHTTP3(n.tlsConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
udpConn, err := n.ListenUDP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
quicListener, err := qtls.ListenEarly(udpConn, n.tlsConfig, &quic.Config{
|
||||
MaxIncomingStreams: 1 << 60,
|
||||
Allow0RTT: true,
|
||||
})
|
||||
if err != nil {
|
||||
udpConn.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
h3Server := &http3.Server{
|
||||
Port: int(n.listenOptions.ListenPort),
|
||||
Handler: n,
|
||||
}
|
||||
|
||||
go func() {
|
||||
sErr := h3Server.ServeListener(quicListener)
|
||||
udpConn.Close()
|
||||
if sErr != nil && !E.IsClosedOrCanceled(sErr) {
|
||||
n.logger.Error("http3 server serve error: ", sErr)
|
||||
}
|
||||
}()
|
||||
|
||||
n.h3Server = h3Server
|
||||
return nil
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
//go:build !with_quic
|
||||
|
||||
package inbound
|
||||
|
||||
import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
)
|
||||
|
||||
func (n *Naive) configureHTTP3Listener() error {
|
||||
return C.ErrQUICNotIncluded
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package inbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/redir"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
type Redirect struct {
|
||||
myInboundAdapter
|
||||
}
|
||||
|
||||
func NewRedirect(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.RedirectInboundOptions) *Redirect {
|
||||
redirect := &Redirect{
|
||||
myInboundAdapter{
|
||||
protocol: C.TypeRedirect,
|
||||
network: []string{N.NetworkTCP},
|
||||
ctx: ctx,
|
||||
router: router,
|
||||
logger: logger,
|
||||
tag: tag,
|
||||
listenOptions: options.ListenOptions,
|
||||
},
|
||||
}
|
||||
redirect.connHandler = redirect
|
||||
return redirect
|
||||
}
|
||||
|
||||
func (r *Redirect) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
destination, err := redir.GetOriginalDestination(conn)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
r.logger.ErrorContext(ctx, "process connection from ", conn.RemoteAddr(), ": get redirect destination: ", err)
|
||||
return
|
||||
}
|
||||
metadata.Destination = M.SocksaddrFromNetIP(destination)
|
||||
r.newConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
package inbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/mux"
|
||||
"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-shadowsocks"
|
||||
"github.com/sagernet/sing-shadowsocks/shadowaead"
|
||||
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
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/ntp"
|
||||
)
|
||||
|
||||
func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksInboundOptions) (adapter.Inbound, error) {
|
||||
if len(options.Users) > 0 && len(options.Destinations) > 0 {
|
||||
return nil, E.New("users and destinations options must not be combined")
|
||||
}
|
||||
if len(options.Users) > 0 {
|
||||
return newShadowsocksMulti(ctx, router, logger, tag, options)
|
||||
} else if len(options.Destinations) > 0 {
|
||||
return newShadowsocksRelay(ctx, router, logger, tag, options)
|
||||
} else {
|
||||
return newShadowsocks(ctx, router, logger, tag, options)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
_ adapter.Inbound = (*Shadowsocks)(nil)
|
||||
_ adapter.TCPInjectableInbound = (*Shadowsocks)(nil)
|
||||
)
|
||||
|
||||
type Shadowsocks struct {
|
||||
myInboundAdapter
|
||||
service shadowsocks.Service
|
||||
}
|
||||
|
||||
func newShadowsocks(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksInboundOptions) (*Shadowsocks, error) {
|
||||
inbound := &Shadowsocks{
|
||||
myInboundAdapter: myInboundAdapter{
|
||||
protocol: C.TypeShadowsocks,
|
||||
network: options.Network.Build(),
|
||||
ctx: ctx,
|
||||
router: uot.NewRouter(router, logger),
|
||||
logger: logger,
|
||||
tag: tag,
|
||||
listenOptions: options.ListenOptions,
|
||||
},
|
||||
}
|
||||
|
||||
inbound.connHandler = inbound
|
||||
inbound.packetHandler = inbound
|
||||
var err error
|
||||
inbound.router, err = mux.NewRouterWithOptions(inbound.router, logger, common.PtrValueOrDefault(options.Multiplex))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var udpTimeout time.Duration
|
||||
if options.UDPTimeout != 0 {
|
||||
udpTimeout = time.Duration(options.UDPTimeout)
|
||||
} else {
|
||||
udpTimeout = C.UDPTimeout
|
||||
}
|
||||
switch {
|
||||
case options.Method == shadowsocks.MethodNone:
|
||||
inbound.service = shadowsocks.NewNoneService(int64(udpTimeout.Seconds()), adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, inbound))
|
||||
case common.Contains(shadowaead.List, options.Method):
|
||||
inbound.service, err = shadowaead.NewService(options.Method, nil, options.Password, int64(udpTimeout.Seconds()), adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, inbound))
|
||||
case common.Contains(shadowaead_2022.List, options.Method):
|
||||
inbound.service, err = shadowaead_2022.NewServiceWithPassword(options.Method, options.Password, int64(udpTimeout.Seconds()), adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, inbound), ntp.TimeFuncFromContext(ctx))
|
||||
default:
|
||||
err = E.New("unsupported method: ", options.Method)
|
||||
}
|
||||
inbound.packetUpstream = inbound.service
|
||||
return inbound, err
|
||||
}
|
||||
|
||||
func (h *Shadowsocks) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
err := h.service.NewConnection(ctx, conn, adapter.UpstreamMetadata(metadata))
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil {
|
||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Shadowsocks) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
|
||||
err := h.service.NewPacket(h.ctx, h.packetConn(), buffer, M.Metadata{Source: source})
|
||||
if err != nil {
|
||||
h.logger.Error(E.Cause(err, "process packet from ", source))
|
||||
}
|
||||
}
|
||||
|
||||
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, h.createMetadata(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, h.createPacketMetadata(conn, metadata))
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
package inbound
|
||||
|
||||
import (
|
||||
std_bufio "bufio"
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"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/auth"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/protocol/socks"
|
||||
)
|
||||
|
||||
var (
|
||||
_ adapter.Inbound = (*Socks)(nil)
|
||||
_ adapter.TCPInjectableInbound = (*Socks)(nil)
|
||||
)
|
||||
|
||||
type Socks struct {
|
||||
myInboundAdapter
|
||||
authenticator *auth.Authenticator
|
||||
}
|
||||
|
||||
func NewSocks(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SocksInboundOptions) *Socks {
|
||||
inbound := &Socks{
|
||||
myInboundAdapter{
|
||||
protocol: C.TypeSOCKS,
|
||||
network: []string{N.NetworkTCP},
|
||||
ctx: ctx,
|
||||
router: uot.NewRouter(router, logger),
|
||||
logger: logger,
|
||||
tag: tag,
|
||||
listenOptions: options.ListenOptions,
|
||||
},
|
||||
auth.NewAuthenticator(options.Users),
|
||||
}
|
||||
inbound.connHandler = inbound
|
||||
return inbound
|
||||
}
|
||||
|
||||
func (h *Socks) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
err := socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, nil, h.upstreamUserHandlerEx(metadata), metadata.Source, metadata.Destination, onClose)
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil {
|
||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
//go:build !with_quic
|
||||
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
)
|
||||
|
||||
func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TUICInboundOptions) (adapter.Inbound, error) {
|
||||
return nil, C.ErrQUICNotIncluded
|
||||
}
|
|
@ -3,14 +3,22 @@
|
|||
package include
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing-box/adapter/inbound"
|
||||
"github.com/sagernet/sing-box/adapter/outbound"
|
||||
"github.com/sagernet/sing-box/protocol/hysteria"
|
||||
"github.com/sagernet/sing-box/protocol/hysteria2"
|
||||
_ "github.com/sagernet/sing-box/protocol/naive/quic"
|
||||
"github.com/sagernet/sing-box/protocol/tuic"
|
||||
_ "github.com/sagernet/sing-box/transport/v2rayquic"
|
||||
_ "github.com/sagernet/sing-dns/quic"
|
||||
)
|
||||
|
||||
func registerQUICInbounds(registry *inbound.Registry) {
|
||||
hysteria.RegisterInbound(registry)
|
||||
tuic.RegisterInbound(registry)
|
||||
hysteria2.RegisterInbound(registry)
|
||||
}
|
||||
|
||||
func registerQUICOutbounds(registry *outbound.Registry) {
|
||||
hysteria.RegisterOutbound(registry)
|
||||
tuic.RegisterOutbound(registry)
|
||||
|
|
|
@ -4,13 +4,18 @@ package include
|
|||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/adapter/inbound"
|
||||
"github.com/sagernet/sing-box/adapter/outbound"
|
||||
"github.com/sagernet/sing-box/common/listener"
|
||||
"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-box/protocol/naive"
|
||||
"github.com/sagernet/sing-box/transport/v2ray"
|
||||
"github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
|
@ -32,6 +37,21 @@ func init() {
|
|||
)
|
||||
}
|
||||
|
||||
func registerQUICInbounds(registry *inbound.Registry) {
|
||||
inbound.Register[option.HysteriaInboundOptions](registry, C.TypeHysteria, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HysteriaInboundOptions) (adapter.Inbound, error) {
|
||||
return nil, C.ErrQUICNotIncluded
|
||||
})
|
||||
inbound.Register[option.TUICInboundOptions](registry, C.TypeTUIC, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TUICInboundOptions) (adapter.Inbound, error) {
|
||||
return nil, C.ErrQUICNotIncluded
|
||||
})
|
||||
inbound.Register[option.Hysteria2InboundOptions](registry, C.TypeHysteria2, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.Hysteria2InboundOptions) (adapter.Inbound, error) {
|
||||
return nil, C.ErrQUICNotIncluded
|
||||
})
|
||||
naive.ConfigureHTTP3ListenerFunc = func(listener *listener.Listener, handler http.Handler, tlsConfig tls.ServerConfig, logger logger.Logger) (io.Closer, error) {
|
||||
return nil, C.ErrQUICNotIncluded
|
||||
}
|
||||
}
|
||||
|
||||
func registerQUICOutbounds(registry *outbound.Registry) {
|
||||
outbound.Register[option.HysteriaOutboundOptions](registry, C.TypeHysteria, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HysteriaOutboundOptions) (adapter.Outbound, error) {
|
||||
return nil, C.ErrQUICNotIncluded
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/adapter/inbound"
|
||||
"github.com/sagernet/sing-box/adapter/outbound"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
|
@ -13,17 +14,46 @@ import (
|
|||
"github.com/sagernet/sing-box/protocol/dns"
|
||||
"github.com/sagernet/sing-box/protocol/group"
|
||||
"github.com/sagernet/sing-box/protocol/http"
|
||||
"github.com/sagernet/sing-box/protocol/mixed"
|
||||
"github.com/sagernet/sing-box/protocol/naive"
|
||||
"github.com/sagernet/sing-box/protocol/redirect"
|
||||
"github.com/sagernet/sing-box/protocol/shadowsocks"
|
||||
"github.com/sagernet/sing-box/protocol/shadowtls"
|
||||
"github.com/sagernet/sing-box/protocol/socks"
|
||||
"github.com/sagernet/sing-box/protocol/ssh"
|
||||
"github.com/sagernet/sing-box/protocol/tor"
|
||||
"github.com/sagernet/sing-box/protocol/trojan"
|
||||
"github.com/sagernet/sing-box/protocol/tun"
|
||||
"github.com/sagernet/sing-box/protocol/vless"
|
||||
"github.com/sagernet/sing-box/protocol/vmess"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
func InboundRegistry() *inbound.Registry {
|
||||
registry := inbound.NewRegistry()
|
||||
|
||||
tun.RegisterInbound(registry)
|
||||
redirect.RegisterRedirect(registry)
|
||||
redirect.RegisterTProxy(registry)
|
||||
direct.RegisterInbound(registry)
|
||||
|
||||
socks.RegisterInbound(registry)
|
||||
http.RegisterInbound(registry)
|
||||
mixed.RegisterInbound(registry)
|
||||
|
||||
shadowsocks.RegisterInbound(registry)
|
||||
vmess.RegisterInbound(registry)
|
||||
trojan.RegisterInbound(registry)
|
||||
naive.RegisterInbound(registry)
|
||||
shadowtls.RegisterInbound(registry)
|
||||
vless.RegisterInbound(registry)
|
||||
|
||||
registerQUICInbounds(registry)
|
||||
registerStubForRemovedInbounds(registry)
|
||||
|
||||
return registry
|
||||
}
|
||||
|
||||
func OutboundRegistry() *outbound.Registry {
|
||||
registry := outbound.NewRegistry()
|
||||
|
||||
|
@ -52,6 +82,12 @@ func OutboundRegistry() *outbound.Registry {
|
|||
return registry
|
||||
}
|
||||
|
||||
func registerStubForRemovedInbounds(registry *inbound.Registry) {
|
||||
inbound.Register[option.ShadowsocksInboundOptions](registry, C.TypeShadowsocksR, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksInboundOptions) (adapter.Inbound, error) {
|
||||
return nil, E.New("ShadowsocksR is deprecated and removed in sing-box 1.6.0")
|
||||
})
|
||||
}
|
||||
|
||||
func registerStubForRemovedOutbounds(registry *outbound.Registry) {
|
||||
outbound.Register[option.ShadowsocksROutboundOptions](registry, C.TypeShadowsocksR, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksROutboundOptions) (adapter.Outbound, error) {
|
||||
return nil, E.New("ShadowsocksR is deprecated and removed in sing-box 1.6.0")
|
|
@ -1,101 +1,49 @@
|
|||
package option
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
"github.com/sagernet/sing/common/json/badjson"
|
||||
"github.com/sagernet/sing/service"
|
||||
)
|
||||
|
||||
type InboundOptionsRegistry interface {
|
||||
CreateOptions(outboundType string) (any, bool)
|
||||
}
|
||||
|
||||
type _Inbound struct {
|
||||
Type string `json:"type"`
|
||||
Tag string `json:"tag,omitempty"`
|
||||
TunOptions TunInboundOptions `json:"-"`
|
||||
RedirectOptions RedirectInboundOptions `json:"-"`
|
||||
TProxyOptions TProxyInboundOptions `json:"-"`
|
||||
DirectOptions DirectInboundOptions `json:"-"`
|
||||
SocksOptions SocksInboundOptions `json:"-"`
|
||||
HTTPOptions HTTPMixedInboundOptions `json:"-"`
|
||||
MixedOptions HTTPMixedInboundOptions `json:"-"`
|
||||
ShadowsocksOptions ShadowsocksInboundOptions `json:"-"`
|
||||
VMessOptions VMessInboundOptions `json:"-"`
|
||||
TrojanOptions TrojanInboundOptions `json:"-"`
|
||||
NaiveOptions NaiveInboundOptions `json:"-"`
|
||||
HysteriaOptions HysteriaInboundOptions `json:"-"`
|
||||
ShadowTLSOptions ShadowTLSInboundOptions `json:"-"`
|
||||
VLESSOptions VLESSInboundOptions `json:"-"`
|
||||
TUICOptions TUICInboundOptions `json:"-"`
|
||||
Hysteria2Options Hysteria2InboundOptions `json:"-"`
|
||||
Type string `json:"type"`
|
||||
Tag string `json:"tag,omitempty"`
|
||||
Options any `json:"-"`
|
||||
}
|
||||
|
||||
type Inbound _Inbound
|
||||
|
||||
func (h *Inbound) RawOptions() (any, error) {
|
||||
var rawOptionsPtr any
|
||||
switch h.Type {
|
||||
case C.TypeTun:
|
||||
rawOptionsPtr = &h.TunOptions
|
||||
case C.TypeRedirect:
|
||||
rawOptionsPtr = &h.RedirectOptions
|
||||
case C.TypeTProxy:
|
||||
rawOptionsPtr = &h.TProxyOptions
|
||||
case C.TypeDirect:
|
||||
rawOptionsPtr = &h.DirectOptions
|
||||
case C.TypeSOCKS:
|
||||
rawOptionsPtr = &h.SocksOptions
|
||||
case C.TypeHTTP:
|
||||
rawOptionsPtr = &h.HTTPOptions
|
||||
case C.TypeMixed:
|
||||
rawOptionsPtr = &h.MixedOptions
|
||||
case C.TypeShadowsocks:
|
||||
rawOptionsPtr = &h.ShadowsocksOptions
|
||||
case C.TypeVMess:
|
||||
rawOptionsPtr = &h.VMessOptions
|
||||
case C.TypeTrojan:
|
||||
rawOptionsPtr = &h.TrojanOptions
|
||||
case C.TypeNaive:
|
||||
rawOptionsPtr = &h.NaiveOptions
|
||||
case C.TypeHysteria:
|
||||
rawOptionsPtr = &h.HysteriaOptions
|
||||
case C.TypeShadowTLS:
|
||||
rawOptionsPtr = &h.ShadowTLSOptions
|
||||
case C.TypeVLESS:
|
||||
rawOptionsPtr = &h.VLESSOptions
|
||||
case C.TypeTUIC:
|
||||
rawOptionsPtr = &h.TUICOptions
|
||||
case C.TypeHysteria2:
|
||||
rawOptionsPtr = &h.Hysteria2Options
|
||||
case "":
|
||||
return nil, E.New("missing inbound type")
|
||||
default:
|
||||
return nil, E.New("unknown inbound type: ", h.Type)
|
||||
}
|
||||
return rawOptionsPtr, nil
|
||||
func (h *Inbound) MarshalJSONContext(ctx context.Context) ([]byte, error) {
|
||||
return badjson.MarshallObjectsContext(ctx, (*_Inbound)(h), h.Options)
|
||||
}
|
||||
|
||||
func (h Inbound) MarshalJSON() ([]byte, error) {
|
||||
rawOptions, err := h.RawOptions()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return badjson.MarshallObjects((_Inbound)(h), rawOptions)
|
||||
}
|
||||
|
||||
func (h *Inbound) UnmarshalJSON(bytes []byte) error {
|
||||
err := json.Unmarshal(bytes, (*_Inbound)(h))
|
||||
func (h *Inbound) UnmarshalJSONContext(ctx context.Context, content []byte) error {
|
||||
err := json.Unmarshal(content, (*_Inbound)(h))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rawOptions, err := h.RawOptions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = badjson.UnmarshallExcluded(bytes, (*_Inbound)(h), rawOptions)
|
||||
registry := service.FromContext[InboundOptionsRegistry](ctx)
|
||||
if registry == nil {
|
||||
return E.New("missing inbound options registry in context")
|
||||
}
|
||||
options, loaded := registry.CreateOptions(h.Type)
|
||||
if !loaded {
|
||||
return E.New("unknown inbound type: ", h.Type)
|
||||
}
|
||||
err = badjson.UnmarshallExcludedContext(ctx, content, (*_Inbound)(h), options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.Options = options
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -106,19 +54,24 @@ type InboundOptions struct {
|
|||
SniffTimeout Duration `json:"sniff_timeout,omitempty"`
|
||||
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
|
||||
UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"`
|
||||
Detour string `json:"detour,omitempty"`
|
||||
}
|
||||
|
||||
type ListenOptions struct {
|
||||
Listen *ListenAddress `json:"listen,omitempty"`
|
||||
ListenPort uint16 `json:"listen_port,omitempty"`
|
||||
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||
TCPMultiPath bool `json:"tcp_multi_path,omitempty"`
|
||||
UDPFragment *bool `json:"udp_fragment,omitempty"`
|
||||
UDPFragmentDefault bool `json:"-"`
|
||||
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
|
||||
ProxyProtocol bool `json:"proxy_protocol,omitempty"`
|
||||
ProxyProtocolAcceptNoHeader bool `json:"proxy_protocol_accept_no_header,omitempty"`
|
||||
Detour string `json:"detour,omitempty"`
|
||||
Listen *ListenAddress `json:"listen,omitempty"`
|
||||
ListenPort uint16 `json:"listen_port,omitempty"`
|
||||
TCPKeepAlive Duration `json:"tcp_keep_alive,omitempty"`
|
||||
TCPKeepAliveInterval Duration `json:"tcp_keep_alive_interval,omitempty"`
|
||||
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||
TCPMultiPath bool `json:"tcp_multi_path,omitempty"`
|
||||
UDPFragment *bool `json:"udp_fragment,omitempty"`
|
||||
UDPFragmentDefault bool `json:"-"`
|
||||
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
|
||||
|
||||
// Deprecated: removed
|
||||
ProxyProtocol bool `json:"proxy_protocol,omitempty"`
|
||||
// Deprecated: removed
|
||||
ProxyProtocolAcceptNoHeader bool `json:"proxy_protocol_accept_no_header,omitempty"`
|
||||
InboundOptions
|
||||
}
|
||||
|
||||
|
|
98
option/inbound_legacy.go
Normal file
98
option/inbound_legacy.go
Normal file
|
@ -0,0 +1,98 @@
|
|||
package option
|
||||
|
||||
import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
"github.com/sagernet/sing/common/json/badjson"
|
||||
)
|
||||
|
||||
type _LegacyInbound struct {
|
||||
Type string `json:"type"`
|
||||
Tag string `json:"tag,omitempty"`
|
||||
TunOptions TunInboundOptions `json:"-"`
|
||||
RedirectOptions RedirectInboundOptions `json:"-"`
|
||||
TProxyOptions TProxyInboundOptions `json:"-"`
|
||||
DirectOptions DirectInboundOptions `json:"-"`
|
||||
SocksOptions SocksInboundOptions `json:"-"`
|
||||
HTTPOptions HTTPMixedInboundOptions `json:"-"`
|
||||
MixedOptions HTTPMixedInboundOptions `json:"-"`
|
||||
ShadowsocksOptions ShadowsocksInboundOptions `json:"-"`
|
||||
VMessOptions VMessInboundOptions `json:"-"`
|
||||
TrojanOptions TrojanInboundOptions `json:"-"`
|
||||
NaiveOptions NaiveInboundOptions `json:"-"`
|
||||
HysteriaOptions HysteriaInboundOptions `json:"-"`
|
||||
ShadowTLSOptions ShadowTLSInboundOptions `json:"-"`
|
||||
VLESSOptions VLESSInboundOptions `json:"-"`
|
||||
TUICOptions TUICInboundOptions `json:"-"`
|
||||
Hysteria2Options Hysteria2InboundOptions `json:"-"`
|
||||
}
|
||||
|
||||
type LegacyInbound _LegacyInbound
|
||||
|
||||
func (h *LegacyInbound) RawOptions() (any, error) {
|
||||
var rawOptionsPtr any
|
||||
switch h.Type {
|
||||
case C.TypeTun:
|
||||
rawOptionsPtr = &h.TunOptions
|
||||
case C.TypeRedirect:
|
||||
rawOptionsPtr = &h.RedirectOptions
|
||||
case C.TypeTProxy:
|
||||
rawOptionsPtr = &h.TProxyOptions
|
||||
case C.TypeDirect:
|
||||
rawOptionsPtr = &h.DirectOptions
|
||||
case C.TypeSOCKS:
|
||||
rawOptionsPtr = &h.SocksOptions
|
||||
case C.TypeHTTP:
|
||||
rawOptionsPtr = &h.HTTPOptions
|
||||
case C.TypeMixed:
|
||||
rawOptionsPtr = &h.MixedOptions
|
||||
case C.TypeShadowsocks:
|
||||
rawOptionsPtr = &h.ShadowsocksOptions
|
||||
case C.TypeVMess:
|
||||
rawOptionsPtr = &h.VMessOptions
|
||||
case C.TypeTrojan:
|
||||
rawOptionsPtr = &h.TrojanOptions
|
||||
case C.TypeNaive:
|
||||
rawOptionsPtr = &h.NaiveOptions
|
||||
case C.TypeHysteria:
|
||||
rawOptionsPtr = &h.HysteriaOptions
|
||||
case C.TypeShadowTLS:
|
||||
rawOptionsPtr = &h.ShadowTLSOptions
|
||||
case C.TypeVLESS:
|
||||
rawOptionsPtr = &h.VLESSOptions
|
||||
case C.TypeTUIC:
|
||||
rawOptionsPtr = &h.TUICOptions
|
||||
case C.TypeHysteria2:
|
||||
rawOptionsPtr = &h.Hysteria2Options
|
||||
case "":
|
||||
return nil, E.New("missing inbound type")
|
||||
default:
|
||||
return nil, E.New("unknown inbound type: ", h.Type)
|
||||
}
|
||||
return rawOptionsPtr, nil
|
||||
}
|
||||
|
||||
func (h LegacyInbound) MarshalJSON() ([]byte, error) {
|
||||
rawOptions, err := h.RawOptions()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return badjson.MarshallObjects((_LegacyInbound)(h), rawOptions)
|
||||
}
|
||||
|
||||
func (h *LegacyInbound) UnmarshalJSON(bytes []byte) error {
|
||||
err := json.Unmarshal(bytes, (*_LegacyInbound)(h))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rawOptions, err := h.RawOptions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = badjson.UnmarshallExcluded(bytes, (*_LegacyInbound)(h), rawOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -18,6 +18,8 @@ type _Options struct {
|
|||
Route *RouteOptions `json:"route,omitempty"`
|
||||
Experimental *ExperimentalOptions `json:"experimental,omitempty"`
|
||||
|
||||
// Deprecated: use Inbounds instead
|
||||
LegacyInbounds []LegacyInbound `json:"inbound,omitempty"`
|
||||
// Deprecated: use Outbounds instead
|
||||
LegacyOutbounds []LegacyOutbound `json:"_"`
|
||||
}
|
||||
|
|
139
protocol/direct/inbound.go
Normal file
139
protocol/direct/inbound.go
Normal file
|
@ -0,0 +1,139 @@
|
|||
package direct
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/adapter/inbound"
|
||||
"github.com/sagernet/sing-box/common/listener"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/udpnat2"
|
||||
)
|
||||
|
||||
func RegisterInbound(registry *inbound.Registry) {
|
||||
inbound.Register[option.DirectInboundOptions](registry, C.TypeDirect, NewInbound)
|
||||
}
|
||||
|
||||
type Inbound struct {
|
||||
inbound.Adapter
|
||||
ctx context.Context
|
||||
router adapter.ConnectionRouterEx
|
||||
logger log.ContextLogger
|
||||
listener *listener.Listener
|
||||
udpNat *udpnat.Service
|
||||
overrideOption int
|
||||
overrideDestination M.Socksaddr
|
||||
}
|
||||
|
||||
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.DirectInboundOptions) (adapter.Inbound, error) {
|
||||
options.UDPFragmentDefault = true
|
||||
inbound := &Inbound{
|
||||
Adapter: inbound.NewAdapter(C.TypeDirect, tag),
|
||||
ctx: ctx,
|
||||
router: router,
|
||||
logger: logger,
|
||||
}
|
||||
if options.OverrideAddress != "" && options.OverridePort != 0 {
|
||||
inbound.overrideOption = 1
|
||||
inbound.overrideDestination = M.ParseSocksaddrHostPort(options.OverrideAddress, options.OverridePort)
|
||||
} else if options.OverrideAddress != "" {
|
||||
inbound.overrideOption = 2
|
||||
inbound.overrideDestination = M.ParseSocksaddrHostPort(options.OverrideAddress, options.OverridePort)
|
||||
} else if options.OverridePort != 0 {
|
||||
inbound.overrideOption = 3
|
||||
inbound.overrideDestination = M.Socksaddr{Port: options.OverridePort}
|
||||
}
|
||||
var udpTimeout time.Duration
|
||||
if options.UDPTimeout != 0 {
|
||||
udpTimeout = time.Duration(options.UDPTimeout)
|
||||
} else {
|
||||
udpTimeout = C.UDPTimeout
|
||||
}
|
||||
inbound.udpNat = udpnat.New(inbound, inbound.preparePacketConnection, udpTimeout)
|
||||
inbound.listener = listener.New(listener.Options{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
Network: options.Network.Build(),
|
||||
Listen: options.ListenOptions,
|
||||
ConnectionHandler: inbound,
|
||||
PacketHandler: inbound,
|
||||
})
|
||||
return inbound, nil
|
||||
}
|
||||
|
||||
func (i *Inbound) Start() error {
|
||||
return i.listener.Start()
|
||||
}
|
||||
|
||||
func (i *Inbound) Close() error {
|
||||
return i.listener.Close()
|
||||
}
|
||||
|
||||
func (i *Inbound) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
switch i.overrideOption {
|
||||
case 1:
|
||||
metadata.Destination = i.overrideDestination
|
||||
case 2:
|
||||
destination := i.overrideDestination
|
||||
destination.Port = metadata.Destination.Port
|
||||
metadata.Destination = destination
|
||||
case 3:
|
||||
metadata.Destination.Port = i.overrideDestination.Port
|
||||
}
|
||||
i.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||
return i.router.RouteConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (i *Inbound) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
|
||||
var destination M.Socksaddr
|
||||
switch i.overrideOption {
|
||||
case 1:
|
||||
destination = i.overrideDestination
|
||||
case 2:
|
||||
destination = i.overrideDestination
|
||||
destination.Port = source.Port
|
||||
case 3:
|
||||
destination = source
|
||||
destination.Port = i.overrideDestination.Port
|
||||
}
|
||||
i.udpNat.NewPacket([][]byte{buffer.Bytes()}, source, destination, nil)
|
||||
}
|
||||
|
||||
func (i *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
i.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||
metadata.Inbound = i.Tag()
|
||||
metadata.InboundType = i.Type()
|
||||
i.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
func (i *Inbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
i.logger.InfoContext(ctx, "inbound packet connection from ", source)
|
||||
i.logger.InfoContext(ctx, "inbound packet connection to ", destination)
|
||||
var metadata adapter.InboundContext
|
||||
metadata.Inbound = i.Tag()
|
||||
metadata.InboundType = i.Type()
|
||||
metadata.Source = source
|
||||
metadata.Destination = destination
|
||||
metadata.OriginDestination = i.listener.UDPAddr()
|
||||
i.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
func (i *Inbound) preparePacketConnection(source M.Socksaddr, destination M.Socksaddr, userData any) (bool, context.Context, N.PacketWriter, N.CloseHandlerFunc) {
|
||||
return true, log.ContextWithNewID(i.ctx), &directPacketWriter{i.listener.PacketWriter(), source}, nil
|
||||
}
|
||||
|
||||
type directPacketWriter struct {
|
||||
writer N.PacketWriter
|
||||
source M.Socksaddr
|
||||
}
|
||||
|
||||
func (w *directPacketWriter) WritePacket(buffer *buf.Buffer, addr M.Socksaddr) error {
|
||||
return w.writer.WritePacket(buffer, w.source)
|
||||
}
|
|
@ -7,7 +7,7 @@ import (
|
|||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
dns "github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
|
|
122
protocol/http/inbound.go
Normal file
122
protocol/http/inbound.go
Normal file
|
@ -0,0 +1,122 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
std_bufio "bufio"
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"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"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/protocol/http"
|
||||
)
|
||||
|
||||
func RegisterInbound(registry *inbound.Registry) {
|
||||
inbound.Register[option.HTTPMixedInboundOptions](registry, C.TypeHTTP, NewInbound)
|
||||
}
|
||||
|
||||
var _ adapter.TCPInjectableInbound = (*Inbound)(nil)
|
||||
|
||||
type Inbound struct {
|
||||
inbound.Adapter
|
||||
router adapter.ConnectionRouterEx
|
||||
logger log.ContextLogger
|
||||
listener *listener.Listener
|
||||
authenticator *auth.Authenticator
|
||||
tlsConfig tls.ServerConfig
|
||||
}
|
||||
|
||||
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPMixedInboundOptions) (adapter.Inbound, error) {
|
||||
inbound := &Inbound{
|
||||
Adapter: inbound.NewAdapter(C.TypeHTTP, tag),
|
||||
router: uot.NewRouter(router, logger),
|
||||
logger: logger,
|
||||
authenticator: auth.NewAuthenticator(options.Users),
|
||||
}
|
||||
if options.TLS != nil {
|
||||
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inbound.tlsConfig = tlsConfig
|
||||
}
|
||||
inbound.listener = listener.New(listener.Options{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
Network: []string{N.NetworkTCP},
|
||||
Listen: options.ListenOptions,
|
||||
ConnectionHandler: inbound,
|
||||
SetSystemProxy: options.SetSystemProxy,
|
||||
SystemProxySOCKS: false,
|
||||
})
|
||||
return inbound, nil
|
||||
}
|
||||
|
||||
func (h *Inbound) Start() error {
|
||||
if h.tlsConfig != nil {
|
||||
err := h.tlsConfig.Start()
|
||||
if err != nil {
|
||||
return E.Cause(err, "create TLS config")
|
||||
}
|
||||
}
|
||||
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) {
|
||||
err := h.newConnection(ctx, conn, metadata, onClose)
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil {
|
||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) error {
|
||||
var err error
|
||||
if h.tlsConfig != nil {
|
||||
conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return http.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, nil, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
|
||||
}
|
||||
|
||||
func (h *Inbound) newUserConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
user, loaded := auth.UserFromContext[string](ctx)
|
||||
if !loaded {
|
||||
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||
h.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
||||
return
|
||||
}
|
||||
metadata.User = user
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination)
|
||||
h.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
func (h *Inbound) streamUserPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
user, loaded := auth.UserFromContext[string](ctx)
|
||||
if !loaded {
|
||||
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||
h.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
return
|
||||
}
|
||||
metadata.User = user
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
||||
h.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
|
@ -1,6 +1,4 @@
|
|||
//go:build with_quic
|
||||
|
||||
package inbound
|
||||
package hysteria
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -8,7 +6,9 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/adapter/inbound"
|
||||
"github.com/sagernet/sing-box/common/humanize"
|
||||
"github.com/sagernet/sing-box/common/listener"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
|
@ -20,16 +20,21 @@ import (
|
|||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
var _ adapter.Inbound = (*Hysteria)(nil)
|
||||
func RegisterInbound(registry *inbound.Registry) {
|
||||
inbound.Register[option.HysteriaInboundOptions](registry, C.TypeHysteria, NewInbound)
|
||||
}
|
||||
|
||||
type Hysteria struct {
|
||||
myInboundAdapter
|
||||
type Inbound struct {
|
||||
inbound.Adapter
|
||||
router adapter.Router
|
||||
logger log.ContextLogger
|
||||
listener *listener.Listener
|
||||
tlsConfig tls.ServerConfig
|
||||
service *hysteria.Service[int]
|
||||
userNameList []string
|
||||
}
|
||||
|
||||
func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HysteriaInboundOptions) (*Hysteria, error) {
|
||||
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HysteriaInboundOptions) (adapter.Inbound, error) {
|
||||
options.UDPFragmentDefault = true
|
||||
if options.TLS == nil || !options.TLS.Enabled {
|
||||
return nil, C.ErrTLSRequired
|
||||
|
@ -38,16 +43,15 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inbound := &Hysteria{
|
||||
myInboundAdapter: myInboundAdapter{
|
||||
protocol: C.TypeHysteria,
|
||||
network: []string{N.NetworkUDP},
|
||||
ctx: ctx,
|
||||
router: router,
|
||||
logger: logger,
|
||||
tag: tag,
|
||||
listenOptions: options.ListenOptions,
|
||||
},
|
||||
inbound := &Inbound{
|
||||
Adapter: inbound.NewAdapter(C.TypeHysteria, tag),
|
||||
router: router,
|
||||
logger: logger,
|
||||
listener: listener.New(listener.Options{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
Listen: options.ListenOptions,
|
||||
}),
|
||||
tlsConfig: tlsConfig,
|
||||
}
|
||||
var sendBps, receiveBps uint64
|
||||
|
@ -113,9 +117,12 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||
return inbound, nil
|
||||
}
|
||||
|
||||
func (h *Hysteria) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
metadata = h.createMetadata(conn, metadata)
|
||||
metadata.Inbound = h.Tag()
|
||||
metadata.InboundType = h.Type()
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||
userID, _ := auth.UserFromContext[int](ctx)
|
||||
if userName := h.userNameList[userID]; userName != "" {
|
||||
|
@ -127,9 +134,13 @@ func (h *Hysteria) newConnection(ctx context.Context, conn net.Conn, metadata ad
|
|||
return h.router.RouteConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *Hysteria) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
metadata = h.createPacketMetadata(conn, metadata)
|
||||
metadata.Inbound = h.Tag()
|
||||
metadata.InboundType = h.Type()
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
metadata.OriginDestination = h.listener.UDPAddr()
|
||||
h.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
|
||||
userID, _ := auth.UserFromContext[int](ctx)
|
||||
if userName := h.userNameList[userID]; userName != "" {
|
||||
|
@ -141,23 +152,23 @@ func (h *Hysteria) newPacketConnection(ctx context.Context, conn N.PacketConn, m
|
|||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *Hysteria) Start() error {
|
||||
func (h *Inbound) Start() error {
|
||||
if h.tlsConfig != nil {
|
||||
err := h.tlsConfig.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
packetConn, err := h.myInboundAdapter.ListenUDP()
|
||||
packetConn, err := h.listener.ListenUDP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return h.service.Start(packetConn)
|
||||
}
|
||||
|
||||
func (h *Hysteria) Close() error {
|
||||
func (h *Inbound) Close() error {
|
||||
return common.Close(
|
||||
&h.myInboundAdapter,
|
||||
&h.listener,
|
||||
h.tlsConfig,
|
||||
common.PtrOrNil(h.service),
|
||||
)
|
|
@ -1,6 +1,4 @@
|
|||
//go:build with_quic
|
||||
|
||||
package inbound
|
||||
package hysteria2
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -11,6 +9,8 @@ import (
|
|||
"time"
|
||||
|
||||
"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"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
|
@ -23,16 +23,21 @@ import (
|
|||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
var _ adapter.Inbound = (*Hysteria2)(nil)
|
||||
func RegisterInbound(registry *inbound.Registry) {
|
||||
inbound.Register[option.Hysteria2InboundOptions](registry, C.TypeHysteria2, NewInbound)
|
||||
}
|
||||
|
||||
type Hysteria2 struct {
|
||||
myInboundAdapter
|
||||
type Inbound struct {
|
||||
inbound.Adapter
|
||||
router adapter.Router
|
||||
logger log.ContextLogger
|
||||
listener *listener.Listener
|
||||
tlsConfig tls.ServerConfig
|
||||
service *hysteria2.Service[int]
|
||||
userNameList []string
|
||||
}
|
||||
|
||||
func NewHysteria2(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.Hysteria2InboundOptions) (*Hysteria2, error) {
|
||||
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.Hysteria2InboundOptions) (adapter.Inbound, error) {
|
||||
options.UDPFragmentDefault = true
|
||||
if options.TLS == nil || !options.TLS.Enabled {
|
||||
return nil, C.ErrTLSRequired
|
||||
|
@ -76,16 +81,15 @@ func NewHysteria2(ctx context.Context, router adapter.Router, logger log.Context
|
|||
return nil, E.New("unknown masquerade URL scheme: ", masqueradeURL.Scheme)
|
||||
}
|
||||
}
|
||||
inbound := &Hysteria2{
|
||||
myInboundAdapter: myInboundAdapter{
|
||||
protocol: C.TypeHysteria2,
|
||||
network: []string{N.NetworkUDP},
|
||||
ctx: ctx,
|
||||
router: router,
|
||||
logger: logger,
|
||||
tag: tag,
|
||||
listenOptions: options.ListenOptions,
|
||||
},
|
||||
inbound := &Inbound{
|
||||
Adapter: inbound.NewAdapter(C.TypeHysteria2, tag),
|
||||
router: router,
|
||||
logger: logger,
|
||||
listener: listener.New(listener.Options{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
Listen: options.ListenOptions,
|
||||
}),
|
||||
tlsConfig: tlsConfig,
|
||||
}
|
||||
var udpTimeout time.Duration
|
||||
|
@ -124,9 +128,12 @@ func NewHysteria2(ctx context.Context, router adapter.Router, logger log.Context
|
|||
return inbound, nil
|
||||
}
|
||||
|
||||
func (h *Hysteria2) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
metadata = h.createMetadata(conn, metadata)
|
||||
metadata.Inbound = h.Tag()
|
||||
metadata.InboundType = h.Type()
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||
userID, _ := auth.UserFromContext[int](ctx)
|
||||
if userName := h.userNameList[userID]; userName != "" {
|
||||
|
@ -138,9 +145,13 @@ func (h *Hysteria2) newConnection(ctx context.Context, conn net.Conn, metadata a
|
|||
return h.router.RouteConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *Hysteria2) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
metadata = h.createPacketMetadata(conn, metadata)
|
||||
metadata.Inbound = h.Tag()
|
||||
metadata.InboundType = h.Type()
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
metadata.OriginDestination = h.listener.UDPAddr()
|
||||
h.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
|
||||
userID, _ := auth.UserFromContext[int](ctx)
|
||||
if userName := h.userNameList[userID]; userName != "" {
|
||||
|
@ -152,23 +163,23 @@ func (h *Hysteria2) newPacketConnection(ctx context.Context, conn N.PacketConn,
|
|||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *Hysteria2) Start() error {
|
||||
func (h *Inbound) Start() error {
|
||||
if h.tlsConfig != nil {
|
||||
err := h.tlsConfig.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
packetConn, err := h.myInboundAdapter.ListenUDP()
|
||||
packetConn, err := h.listener.ListenUDP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return h.service.Start(packetConn)
|
||||
}
|
||||
|
||||
func (h *Hysteria2) Close() error {
|
||||
func (h *Inbound) Close() error {
|
||||
return common.Close(
|
||||
&h.myInboundAdapter,
|
||||
&h.listener,
|
||||
h.tlsConfig,
|
||||
common.PtrOrNil(h.service),
|
||||
)
|
109
protocol/mixed/inbound.go
Normal file
109
protocol/mixed/inbound.go
Normal file
|
@ -0,0 +1,109 @@
|
|||
package mixed
|
||||
|
||||
import (
|
||||
std_bufio "bufio"
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"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/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/auth"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/protocol/http"
|
||||
"github.com/sagernet/sing/protocol/socks"
|
||||
"github.com/sagernet/sing/protocol/socks/socks4"
|
||||
"github.com/sagernet/sing/protocol/socks/socks5"
|
||||
)
|
||||
|
||||
func RegisterInbound(registry *inbound.Registry) {
|
||||
inbound.Register[option.HTTPMixedInboundOptions](registry, C.TypeMixed, NewInbound)
|
||||
}
|
||||
|
||||
var _ adapter.TCPInjectableInbound = (*Inbound)(nil)
|
||||
|
||||
type Inbound struct {
|
||||
inbound.Adapter
|
||||
router adapter.ConnectionRouterEx
|
||||
logger log.ContextLogger
|
||||
listener *listener.Listener
|
||||
authenticator *auth.Authenticator
|
||||
}
|
||||
|
||||
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPMixedInboundOptions) (adapter.Inbound, error) {
|
||||
inbound := &Inbound{
|
||||
Adapter: inbound.NewAdapter(C.TypeMixed, tag),
|
||||
router: uot.NewRouter(router, logger),
|
||||
logger: logger,
|
||||
authenticator: auth.NewAuthenticator(options.Users),
|
||||
}
|
||||
inbound.listener = listener.New(listener.Options{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
Network: []string{N.NetworkTCP},
|
||||
Listen: options.ListenOptions,
|
||||
ConnectionHandler: inbound,
|
||||
SetSystemProxy: options.SetSystemProxy,
|
||||
SystemProxySOCKS: true,
|
||||
})
|
||||
return inbound, nil
|
||||
}
|
||||
|
||||
func (h *Inbound) Start() error {
|
||||
return h.listener.Start()
|
||||
}
|
||||
|
||||
func (h *Inbound) Close() error {
|
||||
return h.listener.Close()
|
||||
}
|
||||
|
||||
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
err := h.newConnection(ctx, conn, metadata, onClose)
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil {
|
||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) error {
|
||||
reader := std_bufio.NewReader(conn)
|
||||
headerBytes, err := reader.Peek(1)
|
||||
if err != nil {
|
||||
return E.Cause(err, "peek first byte")
|
||||
}
|
||||
switch headerBytes[0] {
|
||||
case socks4.Version, socks5.Version:
|
||||
return socks.HandleConnectionEx(ctx, conn, reader, h.authenticator, nil, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, metadata.Destination, onClose)
|
||||
default:
|
||||
return http.HandleConnectionEx(ctx, conn, reader, h.authenticator, nil, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Inbound) newUserConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
user, loaded := auth.UserFromContext[string](ctx)
|
||||
if !loaded {
|
||||
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||
h.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
||||
return
|
||||
}
|
||||
metadata.User = user
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination)
|
||||
h.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
func (h *Inbound) streamUserPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
user, loaded := auth.UserFromContext[string](ctx)
|
||||
if !loaded {
|
||||
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||
h.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
return
|
||||
}
|
||||
metadata.User = user
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
||||
h.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
247
protocol/naive/inbound.go
Normal file
247
protocol/naive/inbound.go
Normal file
|
@ -0,0 +1,247 @@
|
|||
package naive
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"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-box/transport/v2rayhttp"
|
||||
"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"
|
||||
sHttp "github.com/sagernet/sing/protocol/http"
|
||||
)
|
||||
|
||||
var ConfigureHTTP3ListenerFunc func(listener *listener.Listener, handler http.Handler, tlsConfig tls.ServerConfig, logger logger.Logger) (io.Closer, error)
|
||||
|
||||
func RegisterInbound(registry *inbound.Registry) {
|
||||
inbound.Register[option.NaiveInboundOptions](registry, C.TypeNaive, NewInbound)
|
||||
}
|
||||
|
||||
type Inbound struct {
|
||||
inbound.Adapter
|
||||
ctx context.Context
|
||||
router adapter.ConnectionRouterEx
|
||||
logger logger.ContextLogger
|
||||
listener *listener.Listener
|
||||
network []string
|
||||
networkIsDefault bool
|
||||
authenticator *auth.Authenticator
|
||||
tlsConfig tls.ServerConfig
|
||||
httpServer *http.Server
|
||||
h3Server io.Closer
|
||||
}
|
||||
|
||||
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.NaiveInboundOptions) (adapter.Inbound, error) {
|
||||
inbound := &Inbound{
|
||||
Adapter: inbound.NewAdapter(C.TypeNaive, tag),
|
||||
ctx: ctx,
|
||||
router: uot.NewRouter(router, logger),
|
||||
listener: listener.New(listener.Options{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
Listen: options.ListenOptions,
|
||||
}),
|
||||
networkIsDefault: options.Network == "",
|
||||
network: options.Network.Build(),
|
||||
authenticator: auth.NewAuthenticator(options.Users),
|
||||
}
|
||||
if common.Contains(inbound.network, N.NetworkUDP) {
|
||||
if options.TLS == nil || !options.TLS.Enabled {
|
||||
return nil, E.New("TLS is required for QUIC server")
|
||||
}
|
||||
}
|
||||
if len(options.Users) == 0 {
|
||||
return nil, E.New("missing users")
|
||||
}
|
||||
if options.TLS != nil {
|
||||
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inbound.tlsConfig = tlsConfig
|
||||
}
|
||||
return inbound, nil
|
||||
}
|
||||
|
||||
func (n *Inbound) Start() error {
|
||||
var tlsConfig *tls.STDConfig
|
||||
if n.tlsConfig != nil {
|
||||
err := n.tlsConfig.Start()
|
||||
if err != nil {
|
||||
return E.Cause(err, "create TLS config")
|
||||
}
|
||||
tlsConfig, err = n.tlsConfig.Config()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if common.Contains(n.network, N.NetworkTCP) {
|
||||
tcpListener, err := n.listener.ListenTCP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n.httpServer = &http.Server{
|
||||
Handler: n,
|
||||
TLSConfig: tlsConfig,
|
||||
BaseContext: func(listener net.Listener) context.Context {
|
||||
return n.ctx
|
||||
},
|
||||
}
|
||||
go func() {
|
||||
var sErr error
|
||||
if tlsConfig != nil {
|
||||
sErr = n.httpServer.ServeTLS(tcpListener, "", "")
|
||||
} else {
|
||||
sErr = n.httpServer.Serve(tcpListener)
|
||||
}
|
||||
if sErr != nil && !E.IsClosedOrCanceled(sErr) {
|
||||
n.logger.Error("http server serve error: ", sErr)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if common.Contains(n.network, N.NetworkUDP) {
|
||||
http3Server, err := ConfigureHTTP3ListenerFunc(n.listener, n, n.tlsConfig, n.logger)
|
||||
if len(n.network) > 1 {
|
||||
n.logger.Warn(E.Cause(err, "naive http3 disabled"))
|
||||
} else if err != nil {
|
||||
return err
|
||||
} else {
|
||||
n.h3Server = http3Server
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *Inbound) Close() error {
|
||||
return common.Close(
|
||||
&n.listener,
|
||||
common.PtrOrNil(n.httpServer),
|
||||
n.h3Server,
|
||||
n.tlsConfig,
|
||||
)
|
||||
}
|
||||
|
||||
func (n *Inbound) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
ctx := log.ContextWithNewID(request.Context())
|
||||
if request.Method != "CONNECT" {
|
||||
rejectHTTP(writer, http.StatusBadRequest)
|
||||
n.badRequest(ctx, request, E.New("not CONNECT request"))
|
||||
return
|
||||
} else if request.Header.Get("Padding") == "" {
|
||||
rejectHTTP(writer, http.StatusBadRequest)
|
||||
n.badRequest(ctx, request, E.New("missing naive padding"))
|
||||
return
|
||||
}
|
||||
userName, password, authOk := sHttp.ParseBasicAuth(request.Header.Get("Proxy-Authorization"))
|
||||
if authOk {
|
||||
authOk = n.authenticator.Verify(userName, password)
|
||||
}
|
||||
if !authOk {
|
||||
rejectHTTP(writer, http.StatusProxyAuthRequired)
|
||||
n.badRequest(ctx, request, E.New("authorization failed"))
|
||||
return
|
||||
}
|
||||
writer.Header().Set("Padding", generateNaivePaddingHeader())
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
writer.(http.Flusher).Flush()
|
||||
|
||||
hostPort := request.URL.Host
|
||||
if hostPort == "" {
|
||||
hostPort = request.Host
|
||||
}
|
||||
source := sHttp.SourceAddress(request)
|
||||
destination := M.ParseSocksaddr(hostPort)
|
||||
|
||||
if hijacker, isHijacker := writer.(http.Hijacker); isHijacker {
|
||||
conn, _, err := hijacker.Hijack()
|
||||
if err != nil {
|
||||
n.badRequest(ctx, request, E.New("hijack failed"))
|
||||
return
|
||||
}
|
||||
n.newConnection(ctx, false, &naiveH1Conn{Conn: conn}, userName, source, destination)
|
||||
} else {
|
||||
n.newConnection(ctx, true, &naiveH2Conn{reader: request.Body, writer: writer, flusher: writer.(http.Flusher)}, userName, source, destination)
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Inbound) newConnection(ctx context.Context, waitForClose bool, conn net.Conn, userName string, source M.Socksaddr, destination M.Socksaddr) {
|
||||
if userName != "" {
|
||||
n.logger.InfoContext(ctx, "[", userName, "] inbound connection from ", source)
|
||||
n.logger.InfoContext(ctx, "[", userName, "] inbound connection to ", destination)
|
||||
} else {
|
||||
n.logger.InfoContext(ctx, "inbound connection from ", source)
|
||||
n.logger.InfoContext(ctx, "inbound connection to ", destination)
|
||||
}
|
||||
var metadata adapter.InboundContext
|
||||
metadata.Inbound = n.Tag()
|
||||
metadata.InboundType = n.Type()
|
||||
metadata.InboundDetour = n.listener.ListenOptions().Detour
|
||||
metadata.InboundOptions = n.listener.ListenOptions().InboundOptions
|
||||
metadata.Source = source
|
||||
metadata.Destination = destination
|
||||
metadata.OriginDestination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
|
||||
metadata.User = userName
|
||||
if !waitForClose {
|
||||
n.router.RouteConnectionEx(ctx, conn, metadata, nil)
|
||||
} else {
|
||||
done := make(chan struct{})
|
||||
wrapper := v2rayhttp.NewHTTP2Wrapper(conn)
|
||||
n.router.RouteConnectionEx(ctx, conn, metadata, N.OnceClose(func(it error) {
|
||||
close(done)
|
||||
}))
|
||||
<-done
|
||||
wrapper.CloseWrapper()
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Inbound) badRequest(ctx context.Context, request *http.Request, err error) {
|
||||
n.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", request.RemoteAddr))
|
||||
}
|
||||
|
||||
func rejectHTTP(writer http.ResponseWriter, statusCode int) {
|
||||
hijacker, ok := writer.(http.Hijacker)
|
||||
if !ok {
|
||||
writer.WriteHeader(statusCode)
|
||||
return
|
||||
}
|
||||
conn, _, err := hijacker.Hijack()
|
||||
if err != nil {
|
||||
writer.WriteHeader(statusCode)
|
||||
return
|
||||
}
|
||||
if tcpConn, isTCP := common.Cast[*net.TCPConn](conn); isTCP {
|
||||
tcpConn.SetLinger(0)
|
||||
}
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
func generateNaivePaddingHeader() string {
|
||||
paddingLen := rand.Intn(32) + 30
|
||||
padding := make([]byte, paddingLen)
|
||||
bits := rand.Uint64()
|
||||
for i := 0; i < 16; i++ {
|
||||
// Codes that won't be Huffman coded.
|
||||
padding[i] = "!#$()+<>?@[]^`{}"[bits&15]
|
||||
bits >>= 4
|
||||
}
|
||||
for i := 16; i < paddingLen; i++ {
|
||||
padding[i] = '~'
|
||||
}
|
||||
return string(padding)
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package inbound
|
||||
package naive
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"math/rand"
|
||||
|
@ -11,228 +10,12 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"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-box/transport/v2rayhttp"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
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/rw"
|
||||
sHttp "github.com/sagernet/sing/protocol/http"
|
||||
)
|
||||
|
||||
var _ adapter.Inbound = (*Naive)(nil)
|
||||
|
||||
type Naive struct {
|
||||
myInboundAdapter
|
||||
authenticator *auth.Authenticator
|
||||
tlsConfig tls.ServerConfig
|
||||
httpServer *http.Server
|
||||
h3Server any
|
||||
}
|
||||
|
||||
func NewNaive(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.NaiveInboundOptions) (*Naive, error) {
|
||||
inbound := &Naive{
|
||||
myInboundAdapter: myInboundAdapter{
|
||||
protocol: C.TypeNaive,
|
||||
network: options.Network.Build(),
|
||||
ctx: ctx,
|
||||
router: uot.NewRouter(router, logger),
|
||||
logger: logger,
|
||||
tag: tag,
|
||||
listenOptions: options.ListenOptions,
|
||||
},
|
||||
authenticator: auth.NewAuthenticator(options.Users),
|
||||
}
|
||||
if common.Contains(inbound.network, N.NetworkUDP) {
|
||||
if options.TLS == nil || !options.TLS.Enabled {
|
||||
return nil, E.New("TLS is required for QUIC server")
|
||||
}
|
||||
}
|
||||
if len(options.Users) == 0 {
|
||||
return nil, E.New("missing users")
|
||||
}
|
||||
if options.TLS != nil {
|
||||
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inbound.tlsConfig = tlsConfig
|
||||
}
|
||||
return inbound, nil
|
||||
}
|
||||
|
||||
func (n *Naive) Start() error {
|
||||
var tlsConfig *tls.STDConfig
|
||||
if n.tlsConfig != nil {
|
||||
err := n.tlsConfig.Start()
|
||||
if err != nil {
|
||||
return E.Cause(err, "create TLS config")
|
||||
}
|
||||
tlsConfig, err = n.tlsConfig.Config()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if common.Contains(n.network, N.NetworkTCP) {
|
||||
tcpListener, err := n.ListenTCP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n.httpServer = &http.Server{
|
||||
Handler: n,
|
||||
TLSConfig: tlsConfig,
|
||||
BaseContext: func(listener net.Listener) context.Context {
|
||||
return n.ctx
|
||||
},
|
||||
}
|
||||
go func() {
|
||||
var sErr error
|
||||
if tlsConfig != nil {
|
||||
sErr = n.httpServer.ServeTLS(tcpListener, "", "")
|
||||
} else {
|
||||
sErr = n.httpServer.Serve(tcpListener)
|
||||
}
|
||||
if sErr != nil && !E.IsClosedOrCanceled(sErr) {
|
||||
n.logger.Error("http server serve error: ", sErr)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if common.Contains(n.network, N.NetworkUDP) {
|
||||
err := n.configureHTTP3Listener()
|
||||
if !C.WithQUIC && len(n.network) > 1 {
|
||||
n.logger.Warn(E.Cause(err, "naive http3 disabled"))
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *Naive) Close() error {
|
||||
return common.Close(
|
||||
&n.myInboundAdapter,
|
||||
common.PtrOrNil(n.httpServer),
|
||||
n.h3Server,
|
||||
n.tlsConfig,
|
||||
)
|
||||
}
|
||||
|
||||
func (n *Naive) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
ctx := log.ContextWithNewID(request.Context())
|
||||
if request.Method != "CONNECT" {
|
||||
rejectHTTP(writer, http.StatusBadRequest)
|
||||
n.badRequest(ctx, request, E.New("not CONNECT request"))
|
||||
return
|
||||
} else if request.Header.Get("Padding") == "" {
|
||||
rejectHTTP(writer, http.StatusBadRequest)
|
||||
n.badRequest(ctx, request, E.New("missing naive padding"))
|
||||
return
|
||||
}
|
||||
userName, password, authOk := sHttp.ParseBasicAuth(request.Header.Get("Proxy-Authorization"))
|
||||
if authOk {
|
||||
authOk = n.authenticator.Verify(userName, password)
|
||||
}
|
||||
if !authOk {
|
||||
rejectHTTP(writer, http.StatusProxyAuthRequired)
|
||||
n.badRequest(ctx, request, E.New("authorization failed"))
|
||||
return
|
||||
}
|
||||
writer.Header().Set("Padding", generateNaivePaddingHeader())
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
writer.(http.Flusher).Flush()
|
||||
|
||||
hostPort := request.URL.Host
|
||||
if hostPort == "" {
|
||||
hostPort = request.Host
|
||||
}
|
||||
source := sHttp.SourceAddress(request)
|
||||
destination := M.ParseSocksaddr(hostPort)
|
||||
|
||||
if hijacker, isHijacker := writer.(http.Hijacker); isHijacker {
|
||||
conn, _, err := hijacker.Hijack()
|
||||
if err != nil {
|
||||
n.badRequest(ctx, request, E.New("hijack failed"))
|
||||
return
|
||||
}
|
||||
n.newConnection(ctx, false, &naiveH1Conn{Conn: conn}, userName, source, destination)
|
||||
} else {
|
||||
n.newConnection(ctx, true, &naiveH2Conn{reader: request.Body, writer: writer, flusher: writer.(http.Flusher)}, userName, source, destination)
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Naive) newConnection(ctx context.Context, waitForClose bool, conn net.Conn, userName string, source M.Socksaddr, destination M.Socksaddr) {
|
||||
if userName != "" {
|
||||
n.logger.InfoContext(ctx, "[", userName, "] inbound connection from ", source)
|
||||
n.logger.InfoContext(ctx, "[", userName, "] inbound connection to ", destination)
|
||||
} else {
|
||||
n.logger.InfoContext(ctx, "inbound connection from ", source)
|
||||
n.logger.InfoContext(ctx, "inbound connection to ", destination)
|
||||
}
|
||||
metadata := n.createMetadata(conn, adapter.InboundContext{
|
||||
Source: source,
|
||||
Destination: destination,
|
||||
User: userName,
|
||||
})
|
||||
if !waitForClose {
|
||||
n.router.RouteConnectionEx(ctx, conn, metadata, nil)
|
||||
} else {
|
||||
done := make(chan struct{})
|
||||
wrapper := v2rayhttp.NewHTTP2Wrapper(conn)
|
||||
n.router.RouteConnectionEx(ctx, conn, metadata, N.OnceClose(func(it error) {
|
||||
close(done)
|
||||
}))
|
||||
<-done
|
||||
wrapper.CloseWrapper()
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Naive) badRequest(ctx context.Context, request *http.Request, err error) {
|
||||
n.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", request.RemoteAddr))
|
||||
}
|
||||
|
||||
func rejectHTTP(writer http.ResponseWriter, statusCode int) {
|
||||
hijacker, ok := writer.(http.Hijacker)
|
||||
if !ok {
|
||||
writer.WriteHeader(statusCode)
|
||||
return
|
||||
}
|
||||
conn, _, err := hijacker.Hijack()
|
||||
if err != nil {
|
||||
writer.WriteHeader(statusCode)
|
||||
return
|
||||
}
|
||||
if tcpConn, isTCP := common.Cast[*net.TCPConn](conn); isTCP {
|
||||
tcpConn.SetLinger(0)
|
||||
}
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
func generateNaivePaddingHeader() string {
|
||||
paddingLen := rand.Intn(32) + 30
|
||||
padding := make([]byte, paddingLen)
|
||||
bits := rand.Uint64()
|
||||
for i := 0; i < 16; i++ {
|
||||
// Codes that won't be Huffman coded.
|
||||
padding[i] = "!#$()+<>?@[]^`{}"[bits&15]
|
||||
bits >>= 4
|
||||
}
|
||||
for i := 16; i < paddingLen; i++ {
|
||||
padding[i] = '~'
|
||||
}
|
||||
return string(padding)
|
||||
}
|
||||
|
||||
const kFirstPaddings = 8
|
||||
|
||||
type naiveH1Conn struct {
|
52
protocol/naive/quic/inbound_init.go
Normal file
52
protocol/naive/quic/inbound_init.go
Normal file
|
@ -0,0 +1,52 @@
|
|||
package quic
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/sagernet/quic-go"
|
||||
"github.com/sagernet/quic-go/http3"
|
||||
"github.com/sagernet/sing-box/common/listener"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
"github.com/sagernet/sing-box/protocol/naive"
|
||||
"github.com/sagernet/sing-quic"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
)
|
||||
|
||||
func init() {
|
||||
naive.ConfigureHTTP3ListenerFunc = func(listener *listener.Listener, handler http.Handler, tlsConfig tls.ServerConfig, logger logger.Logger) (io.Closer, error) {
|
||||
err := qtls.ConfigureHTTP3(tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
udpConn, err := listener.ListenUDP()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
quicListener, err := qtls.ListenEarly(udpConn, tlsConfig, &quic.Config{
|
||||
MaxIncomingStreams: 1 << 60,
|
||||
Allow0RTT: true,
|
||||
})
|
||||
if err != nil {
|
||||
udpConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
h3Server := &http3.Server{
|
||||
Handler: handler,
|
||||
}
|
||||
|
||||
go func() {
|
||||
sErr := h3Server.ServeListener(quicListener)
|
||||
udpConn.Close()
|
||||
if sErr != nil && !E.IsClosedOrCanceled(sErr) {
|
||||
logger.Error("http3 server closed: ", sErr)
|
||||
}
|
||||
}()
|
||||
|
||||
return quicListener, nil
|
||||
}
|
||||
}
|
65
protocol/redirect/redirect.go
Normal file
65
protocol/redirect/redirect.go
Normal file
|
@ -0,0 +1,65 @@
|
|||
package redirect
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"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/redir"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
func RegisterRedirect(registry *inbound.Registry) {
|
||||
inbound.Register[option.RedirectInboundOptions](registry, C.TypeRedirect, NewRedirect)
|
||||
}
|
||||
|
||||
type Redirect struct {
|
||||
inbound.Adapter
|
||||
router adapter.Router
|
||||
logger log.ContextLogger
|
||||
listener *listener.Listener
|
||||
}
|
||||
|
||||
func NewRedirect(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.RedirectInboundOptions) (adapter.Inbound, error) {
|
||||
redirect := &Redirect{
|
||||
Adapter: inbound.NewAdapter(C.TypeRedirect, tag),
|
||||
router: router,
|
||||
logger: logger,
|
||||
}
|
||||
redirect.listener = listener.New(listener.Options{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
Network: []string{N.NetworkTCP},
|
||||
Listen: options.ListenOptions,
|
||||
ConnectionHandler: redirect,
|
||||
})
|
||||
return redirect, nil
|
||||
}
|
||||
|
||||
func (h *Redirect) Start() error {
|
||||
return h.listener.Start()
|
||||
}
|
||||
|
||||
func (h *Redirect) Close() error {
|
||||
return h.listener.Close()
|
||||
}
|
||||
|
||||
func (h *Redirect) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
destination, err := redir.GetOriginalDestination(conn)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
h.logger.ErrorContext(ctx, "process connection from ", conn.RemoteAddr(), ": get redirect destination: ", err)
|
||||
return
|
||||
}
|
||||
metadata.Inbound = h.Tag()
|
||||
metadata.InboundType = h.Type()
|
||||
metadata.Destination = M.SocksaddrFromNetIP(destination)
|
||||
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||
h.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package inbound
|
||||
package redirect
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -8,6 +8,8 @@ import (
|
|||
"time"
|
||||
|
||||
"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/redir"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
|
@ -21,22 +23,25 @@ import (
|
|||
"github.com/sagernet/sing/common/udpnat2"
|
||||
)
|
||||
|
||||
type TProxy struct {
|
||||
myInboundAdapter
|
||||
udpNat *udpnat.Service
|
||||
func RegisterTProxy(registry *inbound.Registry) {
|
||||
inbound.Register[option.TProxyInboundOptions](registry, C.TypeTProxy, NewTProxy)
|
||||
}
|
||||
|
||||
func NewTProxy(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TProxyInboundOptions) *TProxy {
|
||||
type TProxy struct {
|
||||
inbound.Adapter
|
||||
ctx context.Context
|
||||
router adapter.Router
|
||||
logger log.ContextLogger
|
||||
listener *listener.Listener
|
||||
udpNat *udpnat.Service
|
||||
}
|
||||
|
||||
func NewTProxy(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TProxyInboundOptions) (adapter.Inbound, error) {
|
||||
tproxy := &TProxy{
|
||||
myInboundAdapter: myInboundAdapter{
|
||||
protocol: C.TypeTProxy,
|
||||
network: options.Network.Build(),
|
||||
ctx: ctx,
|
||||
router: router,
|
||||
logger: logger,
|
||||
tag: tag,
|
||||
listenOptions: options.ListenOptions,
|
||||
},
|
||||
Adapter: inbound.NewAdapter(C.TypeTProxy, tag),
|
||||
ctx: ctx,
|
||||
router: router,
|
||||
logger: logger,
|
||||
}
|
||||
var udpTimeout time.Duration
|
||||
if options.UDPTimeout != 0 {
|
||||
|
@ -44,28 +49,34 @@ func NewTProxy(ctx context.Context, router adapter.Router, logger log.ContextLog
|
|||
} else {
|
||||
udpTimeout = C.UDPTimeout
|
||||
}
|
||||
tproxy.connHandler = tproxy
|
||||
tproxy.oobPacketHandler = tproxy
|
||||
tproxy.udpNat = udpnat.New(tproxy, tproxy.preparePacketConnection, udpTimeout)
|
||||
return tproxy
|
||||
tproxy.listener = listener.New(listener.Options{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
Network: options.Network.Build(),
|
||||
Listen: options.ListenOptions,
|
||||
ConnectionHandler: tproxy,
|
||||
OOBPacketHandler: tproxy,
|
||||
})
|
||||
return tproxy, nil
|
||||
}
|
||||
|
||||
func (t *TProxy) Start() error {
|
||||
err := t.myInboundAdapter.Start()
|
||||
err := t.listener.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if t.tcpListener != nil {
|
||||
err = control.Conn(common.MustCast[syscall.Conn](t.tcpListener), func(fd uintptr) error {
|
||||
return redir.TProxy(fd, M.SocksaddrFromNet(t.tcpListener.Addr()).Addr.Is6())
|
||||
if listener := t.listener.TCPListener(); listener != nil {
|
||||
err = control.Conn(common.MustCast[syscall.Conn](listener), func(fd uintptr) error {
|
||||
return redir.TProxy(fd, M.SocksaddrFromNet(listener.Addr()).Addr.Is6())
|
||||
})
|
||||
if err != nil {
|
||||
return E.Cause(err, "configure tproxy TCP listener")
|
||||
}
|
||||
}
|
||||
if t.udpConn != nil {
|
||||
err = control.Conn(t.udpConn, func(fd uintptr) error {
|
||||
return redir.TProxy(fd, M.SocksaddrFromNet(t.udpConn.LocalAddr()).Addr.Is6())
|
||||
if conn := t.listener.UDPConn(); conn != nil {
|
||||
err = control.Conn(conn, func(fd uintptr) error {
|
||||
return redir.TProxy(fd, M.SocksaddrFromNet(conn.LocalAddr()).Addr.Is6())
|
||||
})
|
||||
if err != nil {
|
||||
return E.Cause(err, "configure tproxy UDP listener")
|
||||
|
@ -74,13 +85,26 @@ func (t *TProxy) Start() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (t *TProxy) Close() error {
|
||||
return t.listener.Close()
|
||||
}
|
||||
|
||||
func (t *TProxy) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
|
||||
t.newConnectionEx(ctx, conn, metadata, onClose)
|
||||
t.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||
t.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
func (t *TProxy) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
t.newPacketConnectionEx(ctx, conn, t.createPacketMetadataEx(source, destination), onClose)
|
||||
t.logger.InfoContext(ctx, "inbound packet connection from ", source)
|
||||
t.logger.InfoContext(ctx, "inbound packet connection to ", destination)
|
||||
var metadata adapter.InboundContext
|
||||
metadata.Inbound = t.Tag()
|
||||
metadata.InboundType = t.Type()
|
||||
metadata.Source = source
|
||||
metadata.Destination = destination
|
||||
metadata.OriginDestination = t.listener.UDPAddr()
|
||||
t.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
func (t *TProxy) NewPacketEx(buffer *buf.Buffer, oob []byte, source M.Socksaddr) {
|
||||
|
@ -100,8 +124,9 @@ type tproxyPacketWriter struct {
|
|||
}
|
||||
|
||||
func (t *TProxy) preparePacketConnection(source M.Socksaddr, destination M.Socksaddr, userData any) (bool, context.Context, N.PacketWriter, N.CloseHandlerFunc) {
|
||||
writer := &tproxyPacketWriter{ctx: t.ctx, source: source.AddrPort(), destination: destination}
|
||||
return true, t.ctx, writer, func(it error) {
|
||||
ctx := log.ContextWithNewID(t.ctx)
|
||||
writer := &tproxyPacketWriter{ctx: ctx, source: source.AddrPort(), destination: destination}
|
||||
return true, ctx, writer, func(it error) {
|
||||
common.Close(common.PtrOrNil(writer.conn))
|
||||
}
|
||||
}
|
179
protocol/shadowsocks/inbound.go
Normal file
179
protocol/shadowsocks/inbound.go
Normal file
|
@ -0,0 +1,179 @@
|
|||
package shadowsocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"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/mux"
|
||||
"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-shadowsocks"
|
||||
"github.com/sagernet/sing-shadowsocks/shadowaead"
|
||||
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
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"
|
||||
"github.com/sagernet/sing/common/ntp"
|
||||
)
|
||||
|
||||
func RegisterInbound(registry *inbound.Registry) {
|
||||
inbound.Register[option.ShadowsocksInboundOptions](registry, C.TypeShadowsocks, NewInbound)
|
||||
}
|
||||
|
||||
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksInboundOptions) (adapter.Inbound, error) {
|
||||
if len(options.Users) > 0 && len(options.Destinations) > 0 {
|
||||
return nil, E.New("users and destinations options must not be combined")
|
||||
}
|
||||
if len(options.Users) > 0 {
|
||||
return newMultiInbound(ctx, router, logger, tag, options)
|
||||
} else if len(options.Destinations) > 0 {
|
||||
return newRelayInbound(ctx, router, logger, tag, options)
|
||||
} else {
|
||||
return newInbound(ctx, router, logger, tag, options)
|
||||
}
|
||||
}
|
||||
|
||||
var _ adapter.TCPInjectableInbound = (*Inbound)(nil)
|
||||
|
||||
type Inbound struct {
|
||||
inbound.Adapter
|
||||
ctx context.Context
|
||||
router adapter.ConnectionRouterEx
|
||||
logger logger.ContextLogger
|
||||
listener *listener.Listener
|
||||
service shadowsocks.Service
|
||||
}
|
||||
|
||||
func newInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksInboundOptions) (*Inbound, error) {
|
||||
inbound := &Inbound{
|
||||
Adapter: inbound.NewAdapter(C.TypeShadowsocks, tag),
|
||||
ctx: ctx,
|
||||
router: uot.NewRouter(router, logger),
|
||||
logger: logger,
|
||||
}
|
||||
var err error
|
||||
inbound.router, err = mux.NewRouterWithOptions(router, logger, common.PtrValueOrDefault(options.Multiplex))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var udpTimeout time.Duration
|
||||
if options.UDPTimeout != 0 {
|
||||
udpTimeout = time.Duration(options.UDPTimeout)
|
||||
} else {
|
||||
udpTimeout = C.UDPTimeout
|
||||
}
|
||||
switch {
|
||||
case options.Method == shadowsocks.MethodNone:
|
||||
inbound.service = shadowsocks.NewNoneService(int64(udpTimeout.Seconds()), adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, inbound))
|
||||
case common.Contains(shadowaead.List, options.Method):
|
||||
inbound.service, err = shadowaead.NewService(options.Method, nil, options.Password, int64(udpTimeout.Seconds()), adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, inbound))
|
||||
case common.Contains(shadowaead_2022.List, options.Method):
|
||||
inbound.service, err = shadowaead_2022.NewServiceWithPassword(options.Method, options.Password, int64(udpTimeout.Seconds()), adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, inbound), ntp.TimeFuncFromContext(ctx))
|
||||
default:
|
||||
err = E.New("unsupported method: ", options.Method)
|
||||
}
|
||||
inbound.listener = listener.New(listener.Options{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
Network: options.Network.Build(),
|
||||
Listen: options.ListenOptions,
|
||||
ConnectionHandler: inbound,
|
||||
PacketHandler: inbound,
|
||||
ThreadUnsafePacketWriter: true,
|
||||
})
|
||||
return inbound, err
|
||||
}
|
||||
|
||||
func (h *Inbound) Start() error {
|
||||
return h.listener.Start()
|
||||
}
|
||||
|
||||
func (h *Inbound) Close() error {
|
||||
return h.listener.Close()
|
||||
}
|
||||
|
||||
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
err := h.service.NewConnection(ctx, conn, adapter.UpstreamMetadata(metadata))
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil {
|
||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Inbound) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
|
||||
err := h.service.NewPacket(h.ctx, &stubPacketConn{h.listener.PacketWriter()}, buffer, M.Metadata{Source: source})
|
||||
if err != nil {
|
||||
h.logger.Error(E.Cause(err, "process packet from ", source))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||
metadata.Inbound = h.Tag()
|
||||
metadata.InboundType = h.Type()
|
||||
return h.router.RouteConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *Inbound) 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)
|
||||
metadata.Inbound = h.Tag()
|
||||
metadata.InboundType = h.Type()
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
var _ N.PacketConn = (*stubPacketConn)(nil)
|
||||
|
||||
type stubPacketConn struct {
|
||||
N.PacketWriter
|
||||
}
|
||||
|
||||
func (c *stubPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
|
||||
panic("stub!")
|
||||
}
|
||||
|
||||
func (c *stubPacketConn) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *stubPacketConn) LocalAddr() net.Addr {
|
||||
panic("stub!")
|
||||
}
|
||||
|
||||
func (c *stubPacketConn) SetDeadline(t time.Time) error {
|
||||
panic("stub!")
|
||||
}
|
||||
|
||||
func (c *stubPacketConn) SetReadDeadline(t time.Time) error {
|
||||
panic("stub!")
|
||||
}
|
||||
|
||||
func (c *stubPacketConn) SetWriteDeadline(t time.Time) error {
|
||||
panic("stub!")
|
||||
}
|
||||
|
||||
func (h *Inbound) NewError(ctx context.Context, err error) {
|
||||
NewError(h.logger, ctx, err)
|
||||
}
|
||||
|
||||
// Deprecated: remove
|
||||
func NewError(logger logger.ContextLogger, ctx context.Context, err error) {
|
||||
common.Close(err)
|
||||
if E.IsClosedOrCanceled(err) {
|
||||
logger.DebugContext(ctx, "connection closed: ", err)
|
||||
return
|
||||
}
|
||||
logger.ErrorContext(ctx, err)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package inbound
|
||||
package shadowsocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -7,6 +7,8 @@ import (
|
|||
"time"
|
||||
|
||||
"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/mux"
|
||||
"github.com/sagernet/sing-box/common/uot"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
|
@ -20,36 +22,31 @@ import (
|
|||
"github.com/sagernet/sing/common/buf"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/ntp"
|
||||
)
|
||||
|
||||
var (
|
||||
_ adapter.Inbound = (*ShadowsocksMulti)(nil)
|
||||
_ adapter.TCPInjectableInbound = (*ShadowsocksMulti)(nil)
|
||||
)
|
||||
var _ adapter.TCPInjectableInbound = (*MultiInbound)(nil)
|
||||
|
||||
type ShadowsocksMulti struct {
|
||||
myInboundAdapter
|
||||
service shadowsocks.MultiService[int]
|
||||
users []option.ShadowsocksUser
|
||||
type MultiInbound struct {
|
||||
inbound.Adapter
|
||||
ctx context.Context
|
||||
router adapter.ConnectionRouterEx
|
||||
logger logger.ContextLogger
|
||||
listener *listener.Listener
|
||||
service shadowsocks.MultiService[int]
|
||||
users []option.ShadowsocksUser
|
||||
}
|
||||
|
||||
func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksInboundOptions) (*ShadowsocksMulti, error) {
|
||||
inbound := &ShadowsocksMulti{
|
||||
myInboundAdapter: myInboundAdapter{
|
||||
protocol: C.TypeShadowsocks,
|
||||
network: options.Network.Build(),
|
||||
ctx: ctx,
|
||||
router: uot.NewRouter(router, logger),
|
||||
logger: logger,
|
||||
tag: tag,
|
||||
listenOptions: options.ListenOptions,
|
||||
},
|
||||
func newMultiInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksInboundOptions) (*MultiInbound, error) {
|
||||
inbound := &MultiInbound{
|
||||
Adapter: inbound.NewAdapter(C.TypeShadowsocks, tag),
|
||||
ctx: ctx,
|
||||
router: uot.NewRouter(router, logger),
|
||||
logger: logger,
|
||||
}
|
||||
inbound.connHandler = inbound
|
||||
inbound.packetHandler = inbound
|
||||
var err error
|
||||
inbound.router, err = mux.NewRouterWithOptions(inbound.router, logger, common.PtrValueOrDefault(options.Multiplex))
|
||||
if err != nil {
|
||||
|
@ -91,12 +88,28 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.
|
|||
return nil, err
|
||||
}
|
||||
inbound.service = service
|
||||
inbound.packetUpstream = service
|
||||
inbound.users = options.Users
|
||||
inbound.listener = listener.New(listener.Options{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
Network: options.Network.Build(),
|
||||
Listen: options.ListenOptions,
|
||||
ConnectionHandler: inbound,
|
||||
PacketHandler: inbound,
|
||||
ThreadUnsafePacketWriter: true,
|
||||
})
|
||||
return inbound, err
|
||||
}
|
||||
|
||||
func (h *ShadowsocksMulti) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
func (h *MultiInbound) Start() error {
|
||||
return h.listener.Start()
|
||||
}
|
||||
|
||||
func (h *MultiInbound) Close() error {
|
||||
return h.listener.Close()
|
||||
}
|
||||
|
||||
func (h *MultiInbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
err := h.service.NewConnection(ctx, conn, adapter.UpstreamMetadata(metadata))
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil {
|
||||
|
@ -104,14 +117,14 @@ func (h *ShadowsocksMulti) NewConnectionEx(ctx context.Context, conn net.Conn, m
|
|||
}
|
||||
}
|
||||
|
||||
func (h *ShadowsocksMulti) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
|
||||
err := h.service.NewPacket(h.ctx, h.packetConn(), buffer, M.Metadata{Source: source})
|
||||
func (h *MultiInbound) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
|
||||
err := h.service.NewPacket(h.ctx, &stubPacketConn{h.listener.PacketWriter()}, buffer, M.Metadata{Source: source})
|
||||
if err != nil {
|
||||
h.logger.Error(E.Cause(err, "process packet from ", source))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *ShadowsocksMulti) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
func (h *MultiInbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
userIndex, loaded := auth.UserFromContext[int](ctx)
|
||||
if !loaded {
|
||||
return os.ErrInvalid
|
||||
|
@ -123,10 +136,12 @@ func (h *ShadowsocksMulti) newConnection(ctx context.Context, conn net.Conn, met
|
|||
metadata.User = user
|
||||
}
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination)
|
||||
return h.router.RouteConnection(ctx, conn, h.createMetadata(conn, metadata))
|
||||
metadata.Inbound = h.Tag()
|
||||
metadata.InboundType = h.Type()
|
||||
return h.router.RouteConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *ShadowsocksMulti) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
func (h *MultiInbound) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
userIndex, loaded := auth.UserFromContext[int](ctx)
|
||||
if !loaded {
|
||||
return os.ErrInvalid
|
||||
|
@ -140,5 +155,13 @@ func (h *ShadowsocksMulti) newPacketConnection(ctx context.Context, conn N.Packe
|
|||
ctx = log.ContextWithNewID(ctx)
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection from ", metadata.Source)
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
||||
return h.router.RoutePacketConnection(ctx, conn, h.createPacketMetadata(conn, metadata))
|
||||
metadata.Inbound = h.Tag()
|
||||
metadata.InboundType = h.Type()
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *MultiInbound) NewError(ctx context.Context, err error) {
|
||||
NewError(h.logger, ctx, err)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package inbound
|
||||
package shadowsocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -7,6 +7,8 @@ import (
|
|||
"time"
|
||||
|
||||
"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/mux"
|
||||
"github.com/sagernet/sing-box/common/uot"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
|
@ -18,36 +20,31 @@ import (
|
|||
"github.com/sagernet/sing/common/buf"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
var (
|
||||
_ adapter.Inbound = (*ShadowsocksRelay)(nil)
|
||||
_ adapter.TCPInjectableInbound = (*ShadowsocksRelay)(nil)
|
||||
)
|
||||
var _ adapter.TCPInjectableInbound = (*RelayInbound)(nil)
|
||||
|
||||
type ShadowsocksRelay struct {
|
||||
myInboundAdapter
|
||||
type RelayInbound struct {
|
||||
inbound.Adapter
|
||||
ctx context.Context
|
||||
router adapter.ConnectionRouterEx
|
||||
logger logger.ContextLogger
|
||||
listener *listener.Listener
|
||||
service *shadowaead_2022.RelayService[int]
|
||||
destinations []option.ShadowsocksDestination
|
||||
}
|
||||
|
||||
func newShadowsocksRelay(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksInboundOptions) (*ShadowsocksRelay, error) {
|
||||
inbound := &ShadowsocksRelay{
|
||||
myInboundAdapter: myInboundAdapter{
|
||||
protocol: C.TypeShadowsocks,
|
||||
network: options.Network.Build(),
|
||||
ctx: ctx,
|
||||
router: uot.NewRouter(router, logger),
|
||||
logger: logger,
|
||||
tag: tag,
|
||||
listenOptions: options.ListenOptions,
|
||||
},
|
||||
func newRelayInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksInboundOptions) (*RelayInbound, error) {
|
||||
inbound := &RelayInbound{
|
||||
Adapter: inbound.NewAdapter(C.TypeShadowsocks, tag),
|
||||
ctx: ctx,
|
||||
router: uot.NewRouter(router, logger),
|
||||
logger: logger,
|
||||
destinations: options.Destinations,
|
||||
}
|
||||
inbound.connHandler = inbound
|
||||
inbound.packetHandler = inbound
|
||||
var err error
|
||||
inbound.router, err = mux.NewRouterWithOptions(inbound.router, logger, common.PtrValueOrDefault(options.Multiplex))
|
||||
if err != nil {
|
||||
|
@ -77,11 +74,27 @@ func newShadowsocksRelay(ctx context.Context, router adapter.Router, logger log.
|
|||
return nil, err
|
||||
}
|
||||
inbound.service = service
|
||||
inbound.packetUpstream = service
|
||||
inbound.listener = listener.New(listener.Options{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
Network: options.Network.Build(),
|
||||
Listen: options.ListenOptions,
|
||||
ConnectionHandler: inbound,
|
||||
PacketHandler: inbound,
|
||||
ThreadUnsafePacketWriter: true,
|
||||
})
|
||||
return inbound, err
|
||||
}
|
||||
|
||||
func (h *ShadowsocksRelay) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
func (h *RelayInbound) Start() error {
|
||||
return h.listener.Start()
|
||||
}
|
||||
|
||||
func (h *RelayInbound) Close() error {
|
||||
return h.listener.Close()
|
||||
}
|
||||
|
||||
func (h *RelayInbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
err := h.service.NewConnection(ctx, conn, adapter.UpstreamMetadata(metadata))
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil {
|
||||
|
@ -89,14 +102,14 @@ func (h *ShadowsocksRelay) NewConnectionEx(ctx context.Context, conn net.Conn, m
|
|||
}
|
||||
}
|
||||
|
||||
func (h *ShadowsocksRelay) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
|
||||
err := h.service.NewPacket(h.ctx, h.packetConn(), buffer, M.Metadata{Source: source})
|
||||
func (h *RelayInbound) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
|
||||
err := h.service.NewPacket(h.ctx, &stubPacketConn{h.listener.PacketWriter()}, buffer, M.Metadata{Source: source})
|
||||
if err != nil {
|
||||
h.logger.Error(E.Cause(err, "process packet from ", source))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *ShadowsocksRelay) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
func (h *RelayInbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
destinationIndex, loaded := auth.UserFromContext[int](ctx)
|
||||
if !loaded {
|
||||
return os.ErrInvalid
|
||||
|
@ -108,10 +121,12 @@ func (h *ShadowsocksRelay) newConnection(ctx context.Context, conn net.Conn, met
|
|||
metadata.User = destination
|
||||
}
|
||||
h.logger.InfoContext(ctx, "[", destination, "] inbound connection to ", metadata.Destination)
|
||||
return h.router.RouteConnection(ctx, conn, h.createMetadata(conn, metadata))
|
||||
metadata.Inbound = h.Tag()
|
||||
metadata.InboundType = h.Type()
|
||||
return h.router.RouteConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *ShadowsocksRelay) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
func (h *RelayInbound) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
destinationIndex, loaded := auth.UserFromContext[int](ctx)
|
||||
if !loaded {
|
||||
return os.ErrInvalid
|
||||
|
@ -125,5 +140,13 @@ func (h *ShadowsocksRelay) newPacketConnection(ctx context.Context, conn N.Packe
|
|||
ctx = log.ContextWithNewID(ctx)
|
||||
h.logger.InfoContext(ctx, "[", destination, "] inbound packet connection from ", metadata.Source)
|
||||
h.logger.InfoContext(ctx, "[", destination, "] inbound packet connection to ", metadata.Destination)
|
||||
return h.router.RoutePacketConnection(ctx, conn, h.createPacketMetadata(conn, metadata))
|
||||
metadata.Inbound = h.Tag()
|
||||
metadata.InboundType = h.Type()
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *RelayInbound) NewError(ctx context.Context, err error) {
|
||||
NewError(h.logger, ctx, err)
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
package inbound
|
||||
package shadowtls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/adapter/inbound"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
"github.com/sagernet/sing-box/common/listener"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
|
@ -13,25 +15,27 @@ import (
|
|||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
type ShadowTLS struct {
|
||||
myInboundAdapter
|
||||
service *shadowtls.Service
|
||||
func RegisterInbound(registry *inbound.Registry) {
|
||||
inbound.Register[option.ShadowTLSInboundOptions](registry, C.TypeShadowTLS, NewInbound)
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
type Inbound struct {
|
||||
inbound.Adapter
|
||||
router adapter.Router
|
||||
logger logger.ContextLogger
|
||||
listener *listener.Listener
|
||||
service *shadowtls.Service
|
||||
}
|
||||
|
||||
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowTLSInboundOptions) (adapter.Inbound, error) {
|
||||
inbound := &Inbound{
|
||||
Adapter: inbound.NewAdapter(C.TypeShadowTLS, tag),
|
||||
router: router,
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
if options.Version == 0 {
|
||||
|
@ -68,22 +72,36 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context
|
|||
},
|
||||
HandshakeForServerName: handshakeForServerName,
|
||||
StrictMode: options.StrictMode,
|
||||
Handler: adapter.NewUpstreamContextHandler(inbound.newConnection, nil, inbound),
|
||||
Handler: adapter.NewUpstreamContextHandler(inbound.newConnection, nil, nil),
|
||||
Logger: logger,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inbound.service = service
|
||||
inbound.connHandler = inbound
|
||||
inbound.listener = listener.New(listener.Options{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
Network: []string{N.NetworkTCP},
|
||||
Listen: options.ListenOptions,
|
||||
ConnectionHandler: inbound,
|
||||
})
|
||||
return inbound, nil
|
||||
}
|
||||
|
||||
func (h *ShadowTLS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
func (h *Inbound) Start() error {
|
||||
return h.listener.Start()
|
||||
}
|
||||
|
||||
func (h *Inbound) Close() error {
|
||||
return h.listener.Close()
|
||||
}
|
||||
|
||||
func (h *Inbound) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
|
||||
}
|
||||
|
||||
func (h *ShadowTLS) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
if userName, _ := auth.UserFromContext[string](ctx); userName != "" {
|
||||
metadata.User = userName
|
||||
h.logger.InfoContext(ctx, "[", userName, "] inbound connection to ", metadata.Destination)
|
||||
|
@ -93,7 +111,7 @@ func (h *ShadowTLS) newConnection(ctx context.Context, conn net.Conn, metadata a
|
|||
return h.router.RouteConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *ShadowTLS) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
err := h.NewConnection(ctx, conn, metadata)
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil {
|
90
protocol/socks/inbound.go
Normal file
90
protocol/socks/inbound.go
Normal file
|
@ -0,0 +1,90 @@
|
|||
package socks
|
||||
|
||||
import (
|
||||
std_bufio "bufio"
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"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/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/auth"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/protocol/socks"
|
||||
)
|
||||
|
||||
func RegisterInbound(registry *inbound.Registry) {
|
||||
inbound.Register[option.SocksInboundOptions](registry, C.TypeSOCKS, NewInbound)
|
||||
}
|
||||
|
||||
var _ adapter.TCPInjectableInbound = (*Inbound)(nil)
|
||||
|
||||
type Inbound struct {
|
||||
inbound.Adapter
|
||||
logger logger.ContextLogger
|
||||
router adapter.ConnectionRouterEx
|
||||
listener *listener.Listener
|
||||
authenticator *auth.Authenticator
|
||||
}
|
||||
|
||||
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SocksInboundOptions) (adapter.Inbound, error) {
|
||||
inbound := &Inbound{
|
||||
Adapter: inbound.NewAdapter(C.TypeSOCKS, tag),
|
||||
router: uot.NewRouter(router, logger),
|
||||
authenticator: auth.NewAuthenticator(options.Users),
|
||||
}
|
||||
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() error {
|
||||
return h.listener.Start()
|
||||
}
|
||||
|
||||
func (h *Inbound) Close() error {
|
||||
return h.listener.Close()
|
||||
}
|
||||
|
||||
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
err := socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, nil, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, metadata.Destination, onClose)
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil {
|
||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Inbound) newUserConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
user, loaded := auth.UserFromContext[string](ctx)
|
||||
if !loaded {
|
||||
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||
h.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
||||
return
|
||||
}
|
||||
metadata.User = user
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination)
|
||||
h.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
func (h *Inbound) streamUserPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
user, loaded := auth.UserFromContext[string](ctx)
|
||||
if !loaded {
|
||||
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||
h.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
return
|
||||
}
|
||||
metadata.User = user
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
||||
h.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package inbound
|
||||
package trojan
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -6,6 +6,8 @@ import (
|
|||
"os"
|
||||
|
||||
"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/mux"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
|
@ -21,13 +23,17 @@ import (
|
|||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
var (
|
||||
_ adapter.Inbound = (*Trojan)(nil)
|
||||
_ adapter.TCPInjectableInbound = (*Trojan)(nil)
|
||||
)
|
||||
func RegisterInbound(registry *inbound.Registry) {
|
||||
inbound.Register[option.TrojanInboundOptions](registry, C.TypeTrojan, NewInbound)
|
||||
}
|
||||
|
||||
type Trojan struct {
|
||||
myInboundAdapter
|
||||
var _ adapter.TCPInjectableInbound = (*Inbound)(nil)
|
||||
|
||||
type Inbound struct {
|
||||
inbound.Adapter
|
||||
router adapter.ConnectionRouterEx
|
||||
logger log.ContextLogger
|
||||
listener *listener.Listener
|
||||
service *trojan.Service[int]
|
||||
users []option.TrojanUser
|
||||
tlsConfig tls.ServerConfig
|
||||
|
@ -36,18 +42,12 @@ type Trojan struct {
|
|||
transport adapter.V2RayServerTransport
|
||||
}
|
||||
|
||||
func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TrojanInboundOptions) (*Trojan, error) {
|
||||
inbound := &Trojan{
|
||||
myInboundAdapter: myInboundAdapter{
|
||||
protocol: C.TypeTrojan,
|
||||
network: []string{N.NetworkTCP},
|
||||
ctx: ctx,
|
||||
router: router,
|
||||
logger: logger,
|
||||
tag: tag,
|
||||
listenOptions: options.ListenOptions,
|
||||
},
|
||||
users: options.Users,
|
||||
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TrojanInboundOptions) (adapter.Inbound, error) {
|
||||
inbound := &Inbound{
|
||||
Adapter: inbound.NewAdapter(C.TypeTrojan, tag),
|
||||
router: router,
|
||||
logger: logger,
|
||||
users: options.Users,
|
||||
}
|
||||
if options.TLS != nil {
|
||||
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
|
@ -80,7 +80,7 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
|
|||
}
|
||||
fallbackHandler = adapter.NewUpstreamContextHandler(inbound.fallbackConnection, nil, nil)
|
||||
}
|
||||
service := trojan.NewService[int](adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound), fallbackHandler)
|
||||
service := trojan.NewService[int](adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, nil), fallbackHandler, logger)
|
||||
err := service.UpdateUsers(common.MapIndexed(options.Users, func(index int, it option.TrojanUser) int {
|
||||
return index
|
||||
}), common.Map(options.Users, func(it option.TrojanUser) string {
|
||||
|
@ -90,7 +90,7 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
|
|||
return nil, err
|
||||
}
|
||||
if options.Transport != nil {
|
||||
inbound.transport, err = v2ray.NewServerTransport(ctx, logger, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, (*trojanTransportHandler)(inbound))
|
||||
inbound.transport, err = v2ray.NewServerTransport(ctx, logger, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, (*inboundTransportHandler)(inbound))
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create server transport: ", options.Transport.Type)
|
||||
}
|
||||
|
@ -100,11 +100,17 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
|
|||
return nil, err
|
||||
}
|
||||
inbound.service = service
|
||||
inbound.connHandler = inbound
|
||||
inbound.listener = listener.New(listener.Options{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
Network: []string{N.NetworkTCP},
|
||||
Listen: options.ListenOptions,
|
||||
ConnectionHandler: inbound,
|
||||
})
|
||||
return inbound, nil
|
||||
}
|
||||
|
||||
func (h *Trojan) Start() error {
|
||||
func (h *Inbound) Start() error {
|
||||
if h.tlsConfig != nil {
|
||||
err := h.tlsConfig.Start()
|
||||
if err != nil {
|
||||
|
@ -112,10 +118,10 @@ func (h *Trojan) Start() error {
|
|||
}
|
||||
}
|
||||
if h.transport == nil {
|
||||
return h.myInboundAdapter.Start()
|
||||
return h.listener.Start()
|
||||
}
|
||||
if common.Contains(h.transport.Network(), N.NetworkTCP) {
|
||||
tcpListener, err := h.myInboundAdapter.ListenTCP()
|
||||
tcpListener, err := h.listener.ListenTCP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -127,7 +133,7 @@ func (h *Trojan) Start() error {
|
|||
}()
|
||||
}
|
||||
if common.Contains(h.transport.Network(), N.NetworkUDP) {
|
||||
udpConn, err := h.myInboundAdapter.ListenUDP()
|
||||
udpConn, err := h.listener.ListenUDP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -141,15 +147,15 @@ func (h *Trojan) Start() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (h *Trojan) Close() error {
|
||||
func (h *Inbound) Close() error {
|
||||
return common.Close(
|
||||
&h.myInboundAdapter,
|
||||
&h.listener,
|
||||
h.tlsConfig,
|
||||
h.transport,
|
||||
)
|
||||
}
|
||||
|
||||
func (h *Trojan) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
func (h *Inbound) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
var err error
|
||||
if h.tlsConfig != nil && h.transport == nil {
|
||||
conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
||||
|
@ -160,7 +166,7 @@ func (h *Trojan) NewConnection(ctx context.Context, conn net.Conn, metadata adap
|
|||
return h.service.NewConnection(adapter.WithContext(ctx, &metadata), conn, adapter.UpstreamMetadata(metadata))
|
||||
}
|
||||
|
||||
func (h *Trojan) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
err := h.NewConnection(ctx, conn, metadata)
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil {
|
||||
|
@ -168,7 +174,7 @@ func (h *Trojan) NewConnectionEx(ctx context.Context, conn net.Conn, metadata ad
|
|||
}
|
||||
}
|
||||
|
||||
func (h *Trojan) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
userIndex, loaded := auth.UserFromContext[int](ctx)
|
||||
if !loaded {
|
||||
return os.ErrInvalid
|
||||
|
@ -183,7 +189,7 @@ func (h *Trojan) newConnection(ctx context.Context, conn net.Conn, metadata adap
|
|||
return h.router.RouteConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *Trojan) fallbackConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
func (h *Inbound) fallbackConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
var fallbackAddr M.Socksaddr
|
||||
if len(h.fallbackAddrTLSNextProto) > 0 {
|
||||
if tlsConn, loaded := common.Cast[tls.Conn](conn); loaded {
|
||||
|
@ -206,7 +212,7 @@ func (h *Trojan) fallbackConnection(ctx context.Context, conn net.Conn, metadata
|
|||
return h.router.RouteConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *Trojan) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
userIndex, loaded := auth.UserFromContext[int](ctx)
|
||||
if !loaded {
|
||||
return os.ErrInvalid
|
||||
|
@ -221,10 +227,18 @@ func (h *Trojan) newPacketConnection(ctx context.Context, conn N.PacketConn, met
|
|||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
var _ adapter.V2RayServerTransportHandler = (*trojanTransportHandler)(nil)
|
||||
var _ adapter.V2RayServerTransportHandler = (*inboundTransportHandler)(nil)
|
||||
|
||||
type trojanTransportHandler Trojan
|
||||
type inboundTransportHandler Inbound
|
||||
|
||||
func (t *trojanTransportHandler) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
(*Trojan)(t).routeTCP(ctx, conn, source, destination, onClose)
|
||||
func (h *inboundTransportHandler) 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()
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
metadata.Source = source
|
||||
metadata.Destination = destination
|
||||
h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||
(*Inbound)(h).NewConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
|
@ -1,6 +1,4 @@
|
|||
//go:build with_quic
|
||||
|
||||
package inbound
|
||||
package tuic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -8,6 +6,8 @@ import (
|
|||
"time"
|
||||
|
||||
"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"
|
||||
|
@ -22,16 +22,21 @@ import (
|
|||
"github.com/gofrs/uuid/v5"
|
||||
)
|
||||
|
||||
var _ adapter.Inbound = (*TUIC)(nil)
|
||||
func RegisterInbound(registry *inbound.Registry) {
|
||||
inbound.Register[option.TUICInboundOptions](registry, C.TypeTUIC, NewInbound)
|
||||
}
|
||||
|
||||
type TUIC struct {
|
||||
myInboundAdapter
|
||||
type Inbound struct {
|
||||
inbound.Adapter
|
||||
router adapter.ConnectionRouterEx
|
||||
logger log.ContextLogger
|
||||
listener *listener.Listener
|
||||
tlsConfig tls.ServerConfig
|
||||
server *tuic.Service[int]
|
||||
userNameList []string
|
||||
}
|
||||
|
||||
func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TUICInboundOptions) (*TUIC, error) {
|
||||
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TUICInboundOptions) (adapter.Inbound, error) {
|
||||
options.UDPFragmentDefault = true
|
||||
if options.TLS == nil || !options.TLS.Enabled {
|
||||
return nil, C.ErrTLSRequired
|
||||
|
@ -40,16 +45,14 @@ func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogge
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inbound := &TUIC{
|
||||
myInboundAdapter: myInboundAdapter{
|
||||
protocol: C.TypeTUIC,
|
||||
network: []string{N.NetworkUDP},
|
||||
ctx: ctx,
|
||||
router: uot.NewRouter(router, logger),
|
||||
logger: logger,
|
||||
tag: tag,
|
||||
listenOptions: options.ListenOptions,
|
||||
},
|
||||
inbound := &Inbound{
|
||||
Adapter: inbound.NewAdapter(C.TypeTUIC, tag),
|
||||
router: uot.NewRouter(router, logger),
|
||||
listener: listener.New(listener.Options{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
Listen: options.ListenOptions,
|
||||
}),
|
||||
tlsConfig: tlsConfig,
|
||||
}
|
||||
var udpTimeout time.Duration
|
||||
|
@ -95,9 +98,12 @@ func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogge
|
|||
return inbound, nil
|
||||
}
|
||||
|
||||
func (h *TUIC) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
metadata = h.createMetadata(conn, metadata)
|
||||
metadata.Inbound = h.Tag()
|
||||
metadata.InboundType = h.Type()
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||
userID, _ := auth.UserFromContext[int](ctx)
|
||||
if userName := h.userNameList[userID]; userName != "" {
|
||||
|
@ -109,9 +115,13 @@ func (h *TUIC) newConnection(ctx context.Context, conn net.Conn, metadata adapte
|
|||
return h.router.RouteConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *TUIC) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
metadata = h.createPacketMetadata(conn, metadata)
|
||||
metadata.Inbound = h.Tag()
|
||||
metadata.InboundType = h.Type()
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
metadata.OriginDestination = h.listener.UDPAddr()
|
||||
h.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
|
||||
userID, _ := auth.UserFromContext[int](ctx)
|
||||
if userName := h.userNameList[userID]; userName != "" {
|
||||
|
@ -123,23 +133,23 @@ func (h *TUIC) newPacketConnection(ctx context.Context, conn N.PacketConn, metad
|
|||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *TUIC) Start() error {
|
||||
func (h *Inbound) Start() error {
|
||||
if h.tlsConfig != nil {
|
||||
err := h.tlsConfig.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
packetConn, err := h.myInboundAdapter.ListenUDP()
|
||||
packetConn, err := h.listener.ListenUDP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return h.server.Start(packetConn)
|
||||
}
|
||||
|
||||
func (h *TUIC) Close() error {
|
||||
func (h *Inbound) Close() error {
|
||||
return common.Close(
|
||||
&h.myInboundAdapter,
|
||||
&h.listener,
|
||||
h.tlsConfig,
|
||||
common.PtrOrNil(h.server),
|
||||
)
|
|
@ -1,4 +1,4 @@
|
|||
package inbound
|
||||
package tun
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -11,6 +11,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/adapter/inbound"
|
||||
"github.com/sagernet/sing-box/common/taskmonitor"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/experimental/deprecated"
|
||||
|
@ -24,13 +25,16 @@ import (
|
|||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/ranges"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
"github.com/sagernet/sing/service"
|
||||
|
||||
"go4.org/netipx"
|
||||
)
|
||||
|
||||
var _ adapter.Inbound = (*TUN)(nil)
|
||||
func RegisterInbound(registry *inbound.Registry) {
|
||||
inbound.Register[option.TunInboundOptions](registry, C.TypeTun, NewInbound)
|
||||
}
|
||||
|
||||
type TUN struct {
|
||||
type Inbound struct {
|
||||
tag string
|
||||
ctx context.Context
|
||||
router adapter.Router
|
||||
|
@ -55,7 +59,7 @@ type TUN struct {
|
|||
routeExcludeAddressSet []*netipx.IPSet
|
||||
}
|
||||
|
||||
func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions, platformInterface platform.Interface) (*TUN, error) {
|
||||
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions) (adapter.Inbound, error) {
|
||||
address := options.Address
|
||||
var deprecatedAddressUsed bool
|
||||
//nolint:staticcheck
|
||||
|
@ -164,7 +168,7 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
|
|||
outputMark = tun.DefaultAutoRedirectOutputMark
|
||||
}
|
||||
|
||||
inbound := &TUN{
|
||||
inbound := &Inbound{
|
||||
tag: tag,
|
||||
ctx: ctx,
|
||||
router: router,
|
||||
|
@ -198,7 +202,7 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
|
|||
endpointIndependentNat: options.EndpointIndependentNat,
|
||||
udpTimeout: udpTimeout,
|
||||
stack: options.Stack,
|
||||
platformInterface: platformInterface,
|
||||
platformInterface: service.FromContext[platform.Interface](ctx),
|
||||
platformOptions: common.PtrValueOrDefault(options.Platform),
|
||||
}
|
||||
if options.AutoRedirect {
|
||||
|
@ -285,15 +289,15 @@ func parseRange(uidRanges []ranges.Range[uint32], rangeList []string) ([]ranges.
|
|||
return uidRanges, nil
|
||||
}
|
||||
|
||||
func (t *TUN) Type() string {
|
||||
func (t *Inbound) Type() string {
|
||||
return C.TypeTun
|
||||
}
|
||||
|
||||
func (t *TUN) Tag() string {
|
||||
func (t *Inbound) Tag() string {
|
||||
return t.tag
|
||||
}
|
||||
|
||||
func (t *TUN) Start() error {
|
||||
func (t *Inbound) Start() error {
|
||||
if C.IsAndroid && t.platformInterface == nil {
|
||||
t.tunOptions.BuildAndroidRules(t.router.PackageManager())
|
||||
}
|
||||
|
@ -350,7 +354,7 @@ func (t *TUN) Start() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (t *TUN) PostStart() error {
|
||||
func (t *Inbound) PostStart() error {
|
||||
monitor := taskmonitor.New(t.logger, C.StartTimeout)
|
||||
if t.autoRedirect != nil {
|
||||
t.routeAddressSet = common.FlatMap(t.routeRuleSet, adapter.RuleSet.ExtractIPSet)
|
||||
|
@ -389,7 +393,7 @@ func (t *TUN) PostStart() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (t *TUN) updateRouteAddressSet(it adapter.RuleSet) {
|
||||
func (t *Inbound) updateRouteAddressSet(it adapter.RuleSet) {
|
||||
t.routeAddressSet = common.FlatMap(t.routeRuleSet, adapter.RuleSet.ExtractIPSet)
|
||||
t.routeExcludeAddressSet = common.FlatMap(t.routeExcludeRuleSet, adapter.RuleSet.ExtractIPSet)
|
||||
t.autoRedirect.UpdateRouteAddressSet()
|
||||
|
@ -397,7 +401,7 @@ func (t *TUN) updateRouteAddressSet(it adapter.RuleSet) {
|
|||
t.routeExcludeAddressSet = nil
|
||||
}
|
||||
|
||||
func (t *TUN) Close() error {
|
||||
func (t *Inbound) Close() error {
|
||||
return common.Close(
|
||||
t.tunStack,
|
||||
t.tunIf,
|
||||
|
@ -405,7 +409,7 @@ func (t *TUN) Close() error {
|
|||
)
|
||||
}
|
||||
|
||||
func (t *TUN) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr) error {
|
||||
func (t *Inbound) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr) error {
|
||||
return t.router.PreMatch(adapter.InboundContext{
|
||||
Inbound: t.tag,
|
||||
InboundType: C.TypeTun,
|
||||
|
@ -416,7 +420,7 @@ func (t *TUN) PrepareConnection(network string, source M.Socksaddr, destination
|
|||
})
|
||||
}
|
||||
|
||||
func (t *TUN) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
func (t *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
var metadata adapter.InboundContext
|
||||
metadata.Inbound = t.tag
|
||||
|
@ -429,7 +433,7 @@ func (t *TUN) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socks
|
|||
t.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
func (t *TUN) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
func (t *Inbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
var metadata adapter.InboundContext
|
||||
metadata.Inbound = t.tag
|
||||
|
@ -442,7 +446,7 @@ func (t *TUN) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, sour
|
|||
t.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
type autoRedirectHandler TUN
|
||||
type autoRedirectHandler Inbound
|
||||
|
||||
func (t *autoRedirectHandler) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
ctx = log.ContextWithNewID(ctx)
|
|
@ -1,4 +1,4 @@
|
|||
package inbound
|
||||
package vless
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -6,6 +6,8 @@ import (
|
|||
"os"
|
||||
|
||||
"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/mux"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
"github.com/sagernet/sing-box/common/uot"
|
||||
|
@ -20,37 +22,36 @@ import (
|
|||
"github.com/sagernet/sing/common/auth"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
var (
|
||||
_ adapter.Inbound = (*VLESS)(nil)
|
||||
_ adapter.TCPInjectableInbound = (*VLESS)(nil)
|
||||
)
|
||||
func RegisterInbound(registry *inbound.Registry) {
|
||||
inbound.Register[option.VLESSInboundOptions](registry, C.TypeVLESS, NewInbound)
|
||||
}
|
||||
|
||||
type VLESS struct {
|
||||
myInboundAdapter
|
||||
var _ adapter.TCPInjectableInbound = (*Inbound)(nil)
|
||||
|
||||
type Inbound struct {
|
||||
inbound.Adapter
|
||||
ctx context.Context
|
||||
router adapter.ConnectionRouterEx
|
||||
logger logger.ContextLogger
|
||||
listener *listener.Listener
|
||||
users []option.VLESSUser
|
||||
service *vless.Service[int]
|
||||
tlsConfig tls.ServerConfig
|
||||
transport adapter.V2RayServerTransport
|
||||
}
|
||||
|
||||
func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VLESSInboundOptions) (*VLESS, error) {
|
||||
inbound := &VLESS{
|
||||
myInboundAdapter: myInboundAdapter{
|
||||
protocol: C.TypeVLESS,
|
||||
network: []string{N.NetworkTCP},
|
||||
ctx: ctx,
|
||||
router: uot.NewRouter(router, logger),
|
||||
logger: logger,
|
||||
tag: tag,
|
||||
listenOptions: options.ListenOptions,
|
||||
},
|
||||
ctx: ctx,
|
||||
users: options.Users,
|
||||
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VLESSInboundOptions) (adapter.Inbound, error) {
|
||||
inbound := &Inbound{
|
||||
Adapter: inbound.NewAdapter(C.TypeVLESS, tag),
|
||||
ctx: ctx,
|
||||
router: uot.NewRouter(router, logger),
|
||||
logger: logger,
|
||||
users: options.Users,
|
||||
}
|
||||
var err error
|
||||
inbound.router, err = mux.NewRouterWithOptions(inbound.router, logger, common.PtrValueOrDefault(options.Multiplex))
|
||||
|
@ -73,16 +74,22 @@ func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
|||
}
|
||||
}
|
||||
if options.Transport != nil {
|
||||
inbound.transport, err = v2ray.NewServerTransport(ctx, logger, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, (*vlessTransportHandler)(inbound))
|
||||
inbound.transport, err = v2ray.NewServerTransport(ctx, logger, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, (*inboundTransportHandler)(inbound))
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create server transport: ", options.Transport.Type)
|
||||
}
|
||||
}
|
||||
inbound.connHandler = inbound
|
||||
inbound.listener = listener.New(listener.Options{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
Network: []string{N.NetworkTCP},
|
||||
Listen: options.ListenOptions,
|
||||
ConnectionHandler: inbound,
|
||||
})
|
||||
return inbound, nil
|
||||
}
|
||||
|
||||
func (h *VLESS) Start() error {
|
||||
func (h *Inbound) Start() error {
|
||||
if h.tlsConfig != nil {
|
||||
err := h.tlsConfig.Start()
|
||||
if err != nil {
|
||||
|
@ -90,10 +97,10 @@ func (h *VLESS) Start() error {
|
|||
}
|
||||
}
|
||||
if h.transport == nil {
|
||||
return h.myInboundAdapter.Start()
|
||||
return h.listener.Start()
|
||||
}
|
||||
if common.Contains(h.transport.Network(), N.NetworkTCP) {
|
||||
tcpListener, err := h.myInboundAdapter.ListenTCP()
|
||||
tcpListener, err := h.listener.ListenTCP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -105,7 +112,7 @@ func (h *VLESS) Start() error {
|
|||
}()
|
||||
}
|
||||
if common.Contains(h.transport.Network(), N.NetworkUDP) {
|
||||
udpConn, err := h.myInboundAdapter.ListenUDP()
|
||||
udpConn, err := h.listener.ListenUDP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -119,16 +126,16 @@ func (h *VLESS) Start() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (h *VLESS) Close() error {
|
||||
func (h *Inbound) Close() error {
|
||||
return common.Close(
|
||||
h.service,
|
||||
&h.myInboundAdapter,
|
||||
&h.listener,
|
||||
h.tlsConfig,
|
||||
h.transport,
|
||||
)
|
||||
}
|
||||
|
||||
func (h *VLESS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
func (h *Inbound) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
var err error
|
||||
if h.tlsConfig != nil && h.transport == nil {
|
||||
conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
||||
|
@ -139,7 +146,7 @@ func (h *VLESS) NewConnection(ctx context.Context, conn net.Conn, metadata adapt
|
|||
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
|
||||
}
|
||||
|
||||
func (h *VLESS) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
err := h.NewConnection(ctx, conn, metadata)
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil {
|
||||
|
@ -147,7 +154,7 @@ func (h *VLESS) NewConnectionEx(ctx context.Context, conn net.Conn, metadata ada
|
|||
}
|
||||
}
|
||||
|
||||
func (h *VLESS) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
userIndex, loaded := auth.UserFromContext[int](ctx)
|
||||
if !loaded {
|
||||
return os.ErrInvalid
|
||||
|
@ -162,7 +169,7 @@ func (h *VLESS) newConnection(ctx context.Context, conn net.Conn, metadata adapt
|
|||
return h.router.RouteConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *VLESS) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
userIndex, loaded := auth.UserFromContext[int](ctx)
|
||||
if !loaded {
|
||||
return os.ErrInvalid
|
||||
|
@ -183,10 +190,32 @@ func (h *VLESS) newPacketConnection(ctx context.Context, conn N.PacketConn, meta
|
|||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
var _ adapter.V2RayServerTransportHandler = (*vlessTransportHandler)(nil)
|
||||
var _ adapter.V2RayServerTransportHandler = (*inboundTransportHandler)(nil)
|
||||
|
||||
type vlessTransportHandler VLESS
|
||||
type inboundTransportHandler Inbound
|
||||
|
||||
func (t *vlessTransportHandler) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
t.routeTCP(ctx, conn, source, destination, onClose)
|
||||
func (h *inboundTransportHandler) 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()
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
metadata.Source = source
|
||||
metadata.Destination = destination
|
||||
h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||
(*Inbound)(h).NewConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
func (h *Inbound) NewError(ctx context.Context, err error) {
|
||||
NewError(h.logger, ctx, err)
|
||||
}
|
||||
|
||||
// Deprecated: remove
|
||||
func NewError(logger logger.ContextLogger, ctx context.Context, err error) {
|
||||
common.Close(err)
|
||||
if E.IsClosedOrCanceled(err) {
|
||||
logger.DebugContext(ctx, "connection closed: ", err)
|
||||
return
|
||||
}
|
||||
logger.ErrorContext(ctx, err)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package inbound
|
||||
package vmess
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -6,6 +6,8 @@ import (
|
|||
"os"
|
||||
|
||||
"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/mux"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
"github.com/sagernet/sing-box/common/uot"
|
||||
|
@ -19,38 +21,37 @@ import (
|
|||
"github.com/sagernet/sing/common/auth"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/ntp"
|
||||
)
|
||||
|
||||
var (
|
||||
_ adapter.Inbound = (*VMess)(nil)
|
||||
_ adapter.TCPInjectableInbound = (*VMess)(nil)
|
||||
)
|
||||
func RegisterInbound(registry *inbound.Registry) {
|
||||
inbound.Register[option.VMessInboundOptions](registry, C.TypeVMess, NewInbound)
|
||||
}
|
||||
|
||||
type VMess struct {
|
||||
myInboundAdapter
|
||||
var _ adapter.TCPInjectableInbound = (*Inbound)(nil)
|
||||
|
||||
type Inbound struct {
|
||||
inbound.Adapter
|
||||
ctx context.Context
|
||||
router adapter.ConnectionRouterEx
|
||||
logger logger.ContextLogger
|
||||
listener *listener.Listener
|
||||
service *vmess.Service[int]
|
||||
users []option.VMessUser
|
||||
tlsConfig tls.ServerConfig
|
||||
transport adapter.V2RayServerTransport
|
||||
}
|
||||
|
||||
func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VMessInboundOptions) (*VMess, error) {
|
||||
inbound := &VMess{
|
||||
myInboundAdapter: myInboundAdapter{
|
||||
protocol: C.TypeVMess,
|
||||
network: []string{N.NetworkTCP},
|
||||
ctx: ctx,
|
||||
router: uot.NewRouter(router, logger),
|
||||
logger: logger,
|
||||
tag: tag,
|
||||
listenOptions: options.ListenOptions,
|
||||
},
|
||||
ctx: ctx,
|
||||
users: options.Users,
|
||||
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VMessInboundOptions) (adapter.Inbound, error) {
|
||||
inbound := &Inbound{
|
||||
Adapter: inbound.NewAdapter(C.TypeVMess, tag),
|
||||
ctx: ctx,
|
||||
router: uot.NewRouter(router, logger),
|
||||
logger: logger,
|
||||
users: options.Users,
|
||||
}
|
||||
var err error
|
||||
inbound.router, err = mux.NewRouterWithOptions(inbound.router, logger, common.PtrValueOrDefault(options.Multiplex))
|
||||
|
@ -83,16 +84,22 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
|||
}
|
||||
}
|
||||
if options.Transport != nil {
|
||||
inbound.transport, err = v2ray.NewServerTransport(ctx, logger, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, (*vmessTransportHandler)(inbound))
|
||||
inbound.transport, err = v2ray.NewServerTransport(ctx, logger, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, (*inboundTransportHandler)(inbound))
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create server transport: ", options.Transport.Type)
|
||||
}
|
||||
}
|
||||
inbound.connHandler = inbound
|
||||
inbound.listener = listener.New(listener.Options{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
Network: []string{N.NetworkTCP},
|
||||
Listen: options.ListenOptions,
|
||||
ConnectionHandler: inbound,
|
||||
})
|
||||
return inbound, nil
|
||||
}
|
||||
|
||||
func (h *VMess) Start() error {
|
||||
func (h *Inbound) Start() error {
|
||||
err := h.service.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -104,10 +111,10 @@ func (h *VMess) Start() error {
|
|||
}
|
||||
}
|
||||
if h.transport == nil {
|
||||
return h.myInboundAdapter.Start()
|
||||
return h.listener.Start()
|
||||
}
|
||||
if common.Contains(h.transport.Network(), N.NetworkTCP) {
|
||||
tcpListener, err := h.myInboundAdapter.ListenTCP()
|
||||
tcpListener, err := h.listener.ListenTCP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -119,7 +126,7 @@ func (h *VMess) Start() error {
|
|||
}()
|
||||
}
|
||||
if common.Contains(h.transport.Network(), N.NetworkUDP) {
|
||||
udpConn, err := h.myInboundAdapter.ListenUDP()
|
||||
udpConn, err := h.listener.ListenUDP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -133,16 +140,16 @@ func (h *VMess) Start() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (h *VMess) Close() error {
|
||||
func (h *Inbound) Close() error {
|
||||
return common.Close(
|
||||
h.service,
|
||||
&h.myInboundAdapter,
|
||||
&h.listener,
|
||||
h.tlsConfig,
|
||||
h.transport,
|
||||
)
|
||||
}
|
||||
|
||||
func (h *VMess) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
func (h *Inbound) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
var err error
|
||||
if h.tlsConfig != nil && h.transport == nil {
|
||||
conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
||||
|
@ -153,7 +160,7 @@ func (h *VMess) NewConnection(ctx context.Context, conn net.Conn, metadata adapt
|
|||
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
|
||||
}
|
||||
|
||||
func (h *VMess) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
err := h.NewConnection(ctx, conn, metadata)
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil {
|
||||
|
@ -161,7 +168,7 @@ func (h *VMess) NewConnectionEx(ctx context.Context, conn net.Conn, metadata ada
|
|||
}
|
||||
}
|
||||
|
||||
func (h *VMess) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
userIndex, loaded := auth.UserFromContext[int](ctx)
|
||||
if !loaded {
|
||||
return os.ErrInvalid
|
||||
|
@ -176,7 +183,7 @@ func (h *VMess) newConnection(ctx context.Context, conn net.Conn, metadata adapt
|
|||
return h.router.RouteConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *VMess) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
userIndex, loaded := auth.UserFromContext[int](ctx)
|
||||
if !loaded {
|
||||
return os.ErrInvalid
|
||||
|
@ -197,10 +204,32 @@ func (h *VMess) newPacketConnection(ctx context.Context, conn N.PacketConn, meta
|
|||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
var _ adapter.V2RayServerTransportHandler = (*vmessTransportHandler)(nil)
|
||||
var _ adapter.V2RayServerTransportHandler = (*inboundTransportHandler)(nil)
|
||||
|
||||
type vmessTransportHandler VMess
|
||||
type inboundTransportHandler Inbound
|
||||
|
||||
func (t *vmessTransportHandler) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
(*VMess)(t).routeTCP(ctx, conn, source, destination, onClose)
|
||||
func (h *inboundTransportHandler) 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()
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
metadata.Source = source
|
||||
metadata.Destination = destination
|
||||
h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||
(*Inbound)(h).NewConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
func (h *Inbound) NewError(ctx context.Context, err error) {
|
||||
NewError(h.logger, ctx, err)
|
||||
}
|
||||
|
||||
// Deprecated: remove
|
||||
func NewError(logger logger.ContextLogger, ctx context.Context, err error) {
|
||||
common.Close(err)
|
||||
if E.IsClosedOrCanceled(err) {
|
||||
logger.DebugContext(ctx, "connection closed: ", err)
|
||||
return
|
||||
}
|
||||
logger.ErrorContext(ctx, err)
|
||||
}
|
|
@ -98,7 +98,6 @@ func NewRouter(
|
|||
dnsOptions option.DNSOptions,
|
||||
ntpOptions option.NTPOptions,
|
||||
inbounds []option.Inbound,
|
||||
platformInterface platform.Interface,
|
||||
) (*Router, error) {
|
||||
router := &Router{
|
||||
ctx: ctx,
|
||||
|
@ -121,10 +120,13 @@ func NewRouter(
|
|||
defaultInterface: options.DefaultInterface,
|
||||
defaultMark: options.DefaultMark,
|
||||
pauseManager: service.FromContext[pause.Manager](ctx),
|
||||
platformInterface: platformInterface,
|
||||
platformInterface: service.FromContext[platform.Interface](ctx),
|
||||
needWIFIState: hasRule(options.Rules, isWIFIRule) || hasDNSRule(dnsOptions.Rules, isWIFIDNSRule),
|
||||
needPackageManager: common.Any(inbounds, func(inbound option.Inbound) bool {
|
||||
return len(inbound.TunOptions.IncludePackage) > 0 || len(inbound.TunOptions.ExcludePackage) > 0
|
||||
if tunOptions, isTUN := inbound.Options.(*option.TunInboundOptions); isTUN && tunOptions.AutoRoute {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}),
|
||||
}
|
||||
router.dnsClient = dns.NewClient(dns.ClientOptions{
|
||||
|
@ -313,9 +315,15 @@ func NewRouter(
|
|||
router.fakeIPStore = fakeip.NewStore(ctx, router.logger, inet4Range, inet6Range)
|
||||
}
|
||||
|
||||
usePlatformDefaultInterfaceMonitor := platformInterface != nil && platformInterface.UsePlatformDefaultInterfaceMonitor()
|
||||
usePlatformDefaultInterfaceMonitor := router.platformInterface != nil && router.platformInterface.UsePlatformDefaultInterfaceMonitor()
|
||||
needInterfaceMonitor := options.AutoDetectInterface || common.Any(inbounds, func(inbound option.Inbound) bool {
|
||||
return inbound.HTTPOptions.SetSystemProxy || inbound.MixedOptions.SetSystemProxy || inbound.TunOptions.AutoRoute
|
||||
if httpMixedOptions, isHTTPMixed := inbound.Options.(*option.HTTPMixedInboundOptions); isHTTPMixed && httpMixedOptions.SetSystemProxy {
|
||||
return true
|
||||
}
|
||||
if tunOptions, isTUN := inbound.Options.(*option.TunInboundOptions); isTUN && tunOptions.AutoRoute {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
if !usePlatformDefaultInterfaceMonitor {
|
||||
|
@ -331,7 +339,7 @@ func NewRouter(
|
|||
interfaceMonitor, err := tun.NewDefaultInterfaceMonitor(router.networkMonitor, router.logger, tun.DefaultInterfaceMonitorOptions{
|
||||
InterfaceFinder: router.interfaceFinder,
|
||||
OverrideAndroidVPN: options.OverrideAndroidVPN,
|
||||
UnderNetworkExtension: platformInterface != nil && platformInterface.UnderNetworkExtension(),
|
||||
UnderNetworkExtension: router.platformInterface != nil && router.platformInterface.UnderNetworkExtension(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, E.New("auto_detect_interface unsupported on current platform")
|
||||
|
@ -340,7 +348,7 @@ func NewRouter(
|
|||
router.interfaceMonitor = interfaceMonitor
|
||||
}
|
||||
} else {
|
||||
interfaceMonitor := platformInterface.CreateDefaultInterfaceMonitor(router.logger)
|
||||
interfaceMonitor := router.platformInterface.CreateDefaultInterfaceMonitor(router.logger)
|
||||
interfaceMonitor.RegisterCallback(router.notifyNetworkUpdate)
|
||||
router.interfaceMonitor = interfaceMonitor
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ func TestBrutalShadowsocks(t *testing.T) {
|
|||
method := shadowaead_2022.List[0]
|
||||
password := mkBase64(t, 16)
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
@ -100,7 +100,7 @@ func TestBrutalTrojan(t *testing.T) {
|
|||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
password := mkBase64(t, 16)
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
@ -197,7 +197,7 @@ func TestBrutalTrojan(t *testing.T) {
|
|||
func TestBrutalVMess(t *testing.T) {
|
||||
user, _ := uuid.NewV4()
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
@ -279,7 +279,7 @@ func TestBrutalVMess(t *testing.T) {
|
|||
func TestBrutalVLESS(t *testing.T) {
|
||||
user, _ := uuid.NewV4()
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
// Since this is a feature one-off added by outsiders, I won't address these anymore.
|
||||
func _TestProxyProtocol(t *testing.T) {
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
dns "github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing-dns"
|
||||
|
||||
"github.com/gofrs/uuid/v5"
|
||||
)
|
||||
|
@ -14,7 +14,7 @@ import (
|
|||
func TestTUICDomainUDP(t *testing.T) {
|
||||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
|
|
@ -16,7 +16,7 @@ func TestECH(t *testing.T) {
|
|||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
echConfig, echKey := common.Must2(tls.ECHKeygenDefault("not.example.org", false))
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
@ -109,7 +109,7 @@ func TestECHQUIC(t *testing.T) {
|
|||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
echConfig, echKey := common.Must2(tls.ECHKeygenDefault("not.example.org", false))
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
@ -199,7 +199,7 @@ func TestECHHysteria2(t *testing.T) {
|
|||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
echConfig, echKey := common.Must2(tls.ECHKeygenDefault("not.example.org", false))
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
|
||||
func TestHTTPSelf(t *testing.T) {
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
|
|
@ -28,7 +28,7 @@ func testHysteria2Self(t *testing.T, salamanderPassword string) {
|
|||
}
|
||||
}
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
@ -115,7 +115,7 @@ func testHysteria2Self(t *testing.T, salamanderPassword string) {
|
|||
func TestHysteria2Inbound(t *testing.T) {
|
||||
caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeHysteria2,
|
||||
Hysteria2Options: option.Hysteria2InboundOptions{
|
||||
|
@ -167,7 +167,7 @@ func TestHysteria2Outbound(t *testing.T) {
|
|||
},
|
||||
})
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
func TestHysteriaSelf(t *testing.T) {
|
||||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
@ -98,7 +98,7 @@ func TestHysteriaSelf(t *testing.T) {
|
|||
func TestHysteriaInbound(t *testing.T) {
|
||||
caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeHysteria,
|
||||
HysteriaOptions: option.HysteriaInboundOptions{
|
||||
|
@ -149,7 +149,7 @@ func TestHysteriaOutbound(t *testing.T) {
|
|||
},
|
||||
})
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
|
|
|
@ -13,7 +13,7 @@ func TestChainedInbound(t *testing.T) {
|
|||
method := shadowaead_2022.List[0]
|
||||
password := mkBase64(t, 16)
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
|
|
@ -37,7 +37,7 @@ func TestMuxCoolServer(t *testing.T) {
|
|||
})
|
||||
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeVMess,
|
||||
VMessOptions: option.VMessInboundOptions{
|
||||
|
@ -81,7 +81,7 @@ func TestMuxCoolClient(t *testing.T) {
|
|||
})
|
||||
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
|
@ -112,7 +112,7 @@ func TestMuxCoolClient(t *testing.T) {
|
|||
func TestMuxCoolSelf(t *testing.T) {
|
||||
user := newUUID()
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
|
|
@ -55,7 +55,7 @@ func testShadowsocksMux(t *testing.T, options option.OutboundMultiplexOptions) {
|
|||
method := shadowaead_2022.List[0]
|
||||
password := mkBase64(t, 16)
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
@ -125,7 +125,7 @@ func testShadowsocksMux(t *testing.T, options option.OutboundMultiplexOptions) {
|
|||
func testVMessMux(t *testing.T, options option.OutboundMultiplexOptions) {
|
||||
user, _ := uuid.NewV4()
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
func TestNaiveInboundWithNginx(t *testing.T) {
|
||||
caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeNaive,
|
||||
NaiveOptions: option.NaiveInboundOptions{
|
||||
|
@ -59,7 +59,7 @@ func TestNaiveInboundWithNginx(t *testing.T) {
|
|||
func TestNaiveInbound(t *testing.T) {
|
||||
caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeNaive,
|
||||
NaiveOptions: option.NaiveInboundOptions{
|
||||
|
@ -103,7 +103,7 @@ func TestNaiveInbound(t *testing.T) {
|
|||
func TestNaiveHTTP3Inbound(t *testing.T) {
|
||||
caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeNaive,
|
||||
NaiveOptions: option.NaiveInboundOptions{
|
||||
|
|
|
@ -24,7 +24,7 @@ func testShadowsocksLegacy(t *testing.T, method string) {
|
|||
},
|
||||
})
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
|
|
|
@ -99,7 +99,7 @@ func testShadowsocksInboundWithShadowsocksRust(t *testing.T, method string, pass
|
|||
Cmd: []string{"-s", F.ToString("127.0.0.1:", serverPort), "-b", F.ToString("0.0.0.0:", clientPort), "-m", method, "-k", password, "-U"},
|
||||
})
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeShadowsocks,
|
||||
ShadowsocksOptions: option.ShadowsocksInboundOptions{
|
||||
|
@ -124,7 +124,7 @@ func testShadowsocksOutboundWithShadowsocksRust(t *testing.T, method string, pas
|
|||
Cmd: []string{"-s", F.ToString("0.0.0.0:", serverPort), "-m", method, "-k", password, "-U"},
|
||||
})
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
|
@ -154,7 +154,7 @@ func testShadowsocksOutboundWithShadowsocksRust(t *testing.T, method string, pas
|
|||
|
||||
func testShadowsocksSelf(t *testing.T, method string, password string) {
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
@ -221,7 +221,7 @@ func TestShadowsocksUoT(t *testing.T) {
|
|||
method := shadowaead_2022.List[0]
|
||||
password := mkBase64(t, 16)
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
@ -289,7 +289,7 @@ func TestShadowsocksUoT(t *testing.T) {
|
|||
|
||||
func testShadowsocks2022EIH(t *testing.T, method string, password string) {
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
|
|
@ -37,7 +37,7 @@ func testShadowTLS(t *testing.T, version int, password string, utlsEanbled bool)
|
|||
method := shadowaead_2022.List[0]
|
||||
ssPassword := mkBase64(t, 16)
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
|
@ -142,7 +142,7 @@ func testShadowTLS(t *testing.T, version int, password string, utlsEanbled bool)
|
|||
|
||||
func TestShadowTLSFallback(t *testing.T) {
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeShadowTLS,
|
||||
ShadowTLSOptions: option.ShadowTLSInboundOptions{
|
||||
|
@ -189,7 +189,7 @@ func TestShadowTLSInbound(t *testing.T) {
|
|||
Cmd: []string{"--v3", "--threads", "1", "client", "--listen", "0.0.0.0:" + F.ToString(otherPort), "--server", "127.0.0.1:" + F.ToString(serverPort), "--sni", "google.com", "--password", password},
|
||||
})
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "in",
|
||||
|
@ -283,7 +283,7 @@ func TestShadowTLSOutbound(t *testing.T) {
|
|||
Env: []string{"RUST_LOG=trace"},
|
||||
})
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
|
|
|
@ -33,7 +33,7 @@ func testShadowsocksPlugin(t *testing.T, name string, opts string, args string)
|
|||
},
|
||||
})
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
|
|
|
@ -13,7 +13,7 @@ func TestTCPSlowOpen(t *testing.T) {
|
|||
method := shadowaead.List[0]
|
||||
password := mkBase64(t, 16)
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
func TestUTLS(t *testing.T) {
|
||||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
|
|
@ -20,7 +20,7 @@ func TestTrojanOutbound(t *testing.T) {
|
|||
},
|
||||
})
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
|
@ -57,7 +57,7 @@ func TestTrojanOutbound(t *testing.T) {
|
|||
func TestTrojanSelf(t *testing.T) {
|
||||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
@ -140,7 +140,7 @@ func TestTrojanSelf(t *testing.T) {
|
|||
|
||||
func TestTrojanPlainSelf(t *testing.T) {
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
|
|
@ -29,7 +29,7 @@ func testTUICSelf(t *testing.T, udpStream bool, zeroRTTHandshake bool) {
|
|||
udpRelayMode = "quic"
|
||||
}
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
@ -113,7 +113,7 @@ func testTUICSelf(t *testing.T, udpStream bool, zeroRTTHandshake bool) {
|
|||
func TestTUICInbound(t *testing.T) {
|
||||
caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeTUIC,
|
||||
TUICOptions: option.TUICInboundOptions{
|
||||
|
@ -160,7 +160,7 @@ func TestTUICOutbound(t *testing.T) {
|
|||
},
|
||||
})
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
|
||||
func TestV2RayAPI(t *testing.T) {
|
||||
i := startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "in",
|
||||
|
|
|
@ -27,7 +27,7 @@ func testV2RayGRPCInbound(t *testing.T, forceLite bool) {
|
|||
require.NoError(t, err)
|
||||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeVMess,
|
||||
VMessOptions: option.VMessInboundOptions{
|
||||
|
@ -126,7 +126,7 @@ func testV2RayGRPCOutbound(t *testing.T, forceLite bool) {
|
|||
},
|
||||
})
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
|
|
@ -44,7 +44,7 @@ func testVMessTransportSelf(t *testing.T, server *option.V2RayTransportOptions,
|
|||
require.NoError(t, err)
|
||||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
@ -133,7 +133,7 @@ func testTrojanTransportSelf(t *testing.T, server *option.V2RayTransportOptions,
|
|||
require.NoError(t, err)
|
||||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
@ -224,7 +224,7 @@ func TestVMessQUICSelf(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
@ -312,7 +312,7 @@ func testV2RayTransportNOTLSSelf(t *testing.T, transport *option.V2RayTransportO
|
|||
user, err := uuid.DefaultGenerator.NewV4()
|
||||
require.NoError(t, err)
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
|
|
@ -61,7 +61,7 @@ func testV2RayWebsocketInbound(t *testing.T, maxEarlyData uint32, earlyDataHeade
|
|||
require.NoError(t, err)
|
||||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeVMess,
|
||||
VMessOptions: option.VMessInboundOptions{
|
||||
|
@ -158,7 +158,7 @@ func testV2RayWebsocketOutbound(t *testing.T, maxEarlyData uint32, earlyDataHead
|
|||
},
|
||||
})
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
|
|
@ -181,7 +181,7 @@ func testVMessInboundWithV2Ray(t *testing.T, security string, alterId int, authe
|
|||
})
|
||||
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeVMess,
|
||||
VMessOptions: option.VMessInboundOptions{
|
||||
|
@ -229,7 +229,7 @@ func testVMessOutboundWithV2Ray(t *testing.T, security string, globalPadding boo
|
|||
})
|
||||
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
|
@ -263,7 +263,7 @@ func testVMessOutboundWithV2Ray(t *testing.T, security string, globalPadding boo
|
|||
func testVMessSelf(t *testing.T, security string, alterId int, globalPadding bool, authenticatedLength bool, packetAddr bool) {
|
||||
user := newUUID()
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
|
|
|
@ -21,7 +21,7 @@ func _TestWireGuard(t *testing.T) {
|
|||
})
|
||||
time.Sleep(5 * time.Second)
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
)
|
||||
|
||||
func TestOptionsWrapper(t *testing.T) {
|
||||
inbound := option.Inbound{
|
||||
inbound := option.LegacyInbound{
|
||||
Type: C.TypeHTTP,
|
||||
HTTPOptions: option.HTTPMixedInboundOptions{
|
||||
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
|
||||
|
|
|
@ -8,12 +8,13 @@ import (
|
|||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/common/task"
|
||||
"github.com/sagernet/smux"
|
||||
)
|
||||
|
||||
func HandleMuxConnection(ctx context.Context, conn net.Conn, metadata M.Metadata, handler Handler) error {
|
||||
func HandleMuxConnection(ctx context.Context, conn net.Conn, metadata M.Metadata, handler Handler, logger logger.ContextLogger) error {
|
||||
session, err := smux.Server(conn, smuxConfig())
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -26,7 +27,7 @@ func HandleMuxConnection(ctx context.Context, conn net.Conn, metadata M.Metadata
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go newMuxConnection(ctx, stream, metadata, handler)
|
||||
go newMuxConnection(ctx, stream, metadata, handler, logger)
|
||||
}
|
||||
})
|
||||
group.Cleanup(func() {
|
||||
|
@ -35,10 +36,10 @@ func HandleMuxConnection(ctx context.Context, conn net.Conn, metadata M.Metadata
|
|||
return group.Run(ctx)
|
||||
}
|
||||
|
||||
func newMuxConnection(ctx context.Context, conn net.Conn, metadata M.Metadata, handler Handler) {
|
||||
func newMuxConnection(ctx context.Context, conn net.Conn, metadata M.Metadata, handler Handler, logger logger.ContextLogger) {
|
||||
err := newMuxConnection0(ctx, conn, metadata, handler)
|
||||
if err != nil {
|
||||
handler.NewError(ctx, E.Cause(err, "process trojan-go multiplex connection"))
|
||||
logger.ErrorContext(ctx, E.Cause(err, "process trojan-go multiplex connection"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
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"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
|
@ -17,7 +18,6 @@ import (
|
|||
type Handler interface {
|
||||
N.TCPConnectionHandler
|
||||
N.UDPConnectionHandler
|
||||
E.Handler
|
||||
}
|
||||
|
||||
type Service[K comparable] struct {
|
||||
|
@ -25,14 +25,16 @@ type Service[K comparable] struct {
|
|||
keys map[[56]byte]K
|
||||
handler Handler
|
||||
fallbackHandler N.TCPConnectionHandler
|
||||
logger logger.ContextLogger
|
||||
}
|
||||
|
||||
func NewService[K comparable](handler Handler, fallbackHandler N.TCPConnectionHandler) *Service[K] {
|
||||
func NewService[K comparable](handler Handler, fallbackHandler N.TCPConnectionHandler, logger logger.ContextLogger) *Service[K] {
|
||||
return &Service[K]{
|
||||
users: make(map[K][56]byte),
|
||||
keys: make(map[[56]byte]K),
|
||||
handler: handler,
|
||||
fallbackHandler: fallbackHandler,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,7 +112,7 @@ func (s *Service[K]) NewConnection(ctx context.Context, conn net.Conn, metadata
|
|||
return s.handler.NewPacketConnection(ctx, &PacketConn{Conn: conn}, metadata)
|
||||
// case CommandMux:
|
||||
default:
|
||||
return HandleMuxConnection(ctx, conn, metadata, s.handler)
|
||||
return HandleMuxConnection(ctx, conn, metadata, s.handler, s.logger)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
dns "github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing-dns"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue