mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-23 09:01:30 +00:00
refactor: Modular inbounds/outbounds
This commit is contained in:
parent
aa2ac3e020
commit
153172799d
|
@ -5,6 +5,7 @@ import (
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/process"
|
"github.com/sagernet/sing-box/common/process"
|
||||||
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
)
|
)
|
||||||
|
@ -25,6 +26,11 @@ type UDPInjectableInbound interface {
|
||||||
PacketConnectionHandlerEx
|
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 {
|
type InboundContext struct {
|
||||||
Inbound string
|
Inbound string
|
||||||
InboundType string
|
InboundType string
|
||||||
|
@ -44,6 +50,7 @@ type InboundContext struct {
|
||||||
|
|
||||||
// cache
|
// cache
|
||||||
|
|
||||||
|
// Deprecated: implement in rule action
|
||||||
InboundDetour string
|
InboundDetour string
|
||||||
LastInbound string
|
LastInbound string
|
||||||
OriginDestination M.Socksaddr
|
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
|
||||||
|
}
|
|
@ -1,6 +1,10 @@
|
||||||
package adapter
|
package adapter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/log"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -13,3 +17,8 @@ type Outbound interface {
|
||||||
Dependencies() []string
|
Dependencies() []string
|
||||||
N.Dialer
|
N.Dialer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OutboundRegistry interface {
|
||||||
|
option.OutboundOptionsRegistry
|
||||||
|
CreateOutbound(ctx context.Context, router Router, logger log.ContextLogger, tag string, outboundType string, options any) (Outbound, error)
|
||||||
|
}
|
||||||
|
|
45
adapter/outbound/adapter.go
Normal file
45
adapter/outbound/adapter.go
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package outbound
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Adapter struct {
|
||||||
|
protocol string
|
||||||
|
network []string
|
||||||
|
tag string
|
||||||
|
dependencies []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdapter(protocol string, network []string, tag string, dependencies []string) Adapter {
|
||||||
|
return Adapter{
|
||||||
|
protocol: protocol,
|
||||||
|
network: network,
|
||||||
|
tag: tag,
|
||||||
|
dependencies: dependencies,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdapterWithDialerOptions(protocol string, network []string, tag string, dialOptions option.DialerOptions) Adapter {
|
||||||
|
var dependencies []string
|
||||||
|
if dialOptions.Detour != "" {
|
||||||
|
dependencies = []string{dialOptions.Detour}
|
||||||
|
}
|
||||||
|
return NewAdapter(protocol, network, tag, dependencies)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Adapter) Type() string {
|
||||||
|
return a.protocol
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Adapter) Tag() string {
|
||||||
|
return a.tag
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Adapter) Network() []string {
|
||||||
|
return a.network
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Adapter) Dependencies() []string {
|
||||||
|
return a.dependencies
|
||||||
|
}
|
|
@ -9,8 +9,6 @@ import (
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
|
||||||
"github.com/sagernet/sing-box/option"
|
|
||||||
"github.com/sagernet/sing-dns"
|
"github.com/sagernet/sing-dns"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
|
@ -21,42 +19,6 @@ import (
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
type myOutboundAdapter struct {
|
|
||||||
protocol string
|
|
||||||
network []string
|
|
||||||
router adapter.Router
|
|
||||||
logger log.ContextLogger
|
|
||||||
tag string
|
|
||||||
dependencies []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *myOutboundAdapter) Type() string {
|
|
||||||
return a.protocol
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *myOutboundAdapter) Tag() string {
|
|
||||||
return a.tag
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *myOutboundAdapter) Network() []string {
|
|
||||||
return a.network
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *myOutboundAdapter) Dependencies() []string {
|
|
||||||
return a.dependencies
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *myOutboundAdapter) NewError(ctx context.Context, err error) {
|
|
||||||
NewError(a.logger, ctx, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func withDialerDependency(options option.DialerOptions) []string {
|
|
||||||
if options.Detour != "" {
|
|
||||||
return []string{options.Detour}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata adapter.InboundContext) error {
|
func NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
ctx = adapter.WithContext(ctx, &metadata)
|
ctx = adapter.WithContext(ctx, &metadata)
|
||||||
var outConn net.Conn
|
var outConn net.Conn
|
||||||
|
@ -233,12 +195,3 @@ func CopyEarlyConn(ctx context.Context, conn net.Conn, serverConn net.Conn) erro
|
||||||
}
|
}
|
||||||
return bufio.CopyConn(ctx, conn, serverConn)
|
return bufio.CopyConn(ctx, conn, serverConn)
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
68
adapter/outbound/registry.go
Normal file
68
adapter/outbound/registry.go
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
package outbound
|
||||||
|
|
||||||
|
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.Outbound, 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.Outbound, error) {
|
||||||
|
return constructor(ctx, router, logger, tag, common.PtrValueOrDefault(options.(*Options)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ adapter.OutboundRegistry = (*Registry)(nil)
|
||||||
|
|
||||||
|
type (
|
||||||
|
optionsConstructorFunc func() any
|
||||||
|
constructorFunc func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options any) (adapter.Outbound, 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) CreateOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, outboundType string, options any) (adapter.Outbound, 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
|
||||||
|
}
|
73
box.go
73
box.go
|
@ -14,10 +14,9 @@ import (
|
||||||
"github.com/sagernet/sing-box/experimental"
|
"github.com/sagernet/sing-box/experimental"
|
||||||
"github.com/sagernet/sing-box/experimental/cachefile"
|
"github.com/sagernet/sing-box/experimental/cachefile"
|
||||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
"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/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-box/outbound"
|
"github.com/sagernet/sing-box/protocol/direct"
|
||||||
"github.com/sagernet/sing-box/route"
|
"github.com/sagernet/sing-box/route"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
@ -44,16 +43,37 @@ type Box struct {
|
||||||
type Options struct {
|
type Options struct {
|
||||||
option.Options
|
option.Options
|
||||||
Context context.Context
|
Context context.Context
|
||||||
PlatformInterface platform.Interface
|
|
||||||
PlatformLogWriter log.PlatformWriter
|
PlatformLogWriter log.PlatformWriter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
func New(options Options) (*Box, error) {
|
func New(options Options) (*Box, error) {
|
||||||
createdAt := time.Now()
|
createdAt := time.Now()
|
||||||
ctx := options.Context
|
ctx := options.Context
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
ctx = context.Background()
|
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")
|
||||||
|
}
|
||||||
ctx = service.ContextWithDefaultRegistry(ctx)
|
ctx = service.ContextWithDefaultRegistry(ctx)
|
||||||
ctx = pause.WithDefaultManager(ctx)
|
ctx = pause.WithDefaultManager(ctx)
|
||||||
experimentalOptions := common.PtrValueOrDefault(options.Experimental)
|
experimentalOptions := common.PtrValueOrDefault(options.Experimental)
|
||||||
|
@ -70,8 +90,9 @@ func New(options Options) (*Box, error) {
|
||||||
if experimentalOptions.V2RayAPI != nil && experimentalOptions.V2RayAPI.Listen != "" {
|
if experimentalOptions.V2RayAPI != nil && experimentalOptions.V2RayAPI.Listen != "" {
|
||||||
needV2RayAPI = true
|
needV2RayAPI = true
|
||||||
}
|
}
|
||||||
|
platformInterface := service.FromContext[platform.Interface](ctx)
|
||||||
var defaultLogWriter io.Writer
|
var defaultLogWriter io.Writer
|
||||||
if options.PlatformInterface != nil {
|
if platformInterface != nil {
|
||||||
defaultLogWriter = io.Discard
|
defaultLogWriter = io.Discard
|
||||||
}
|
}
|
||||||
logFactory, err := log.New(log.Options{
|
logFactory, err := log.New(log.Options{
|
||||||
|
@ -92,64 +113,70 @@ func New(options Options) (*Box, error) {
|
||||||
common.PtrValueOrDefault(options.DNS),
|
common.PtrValueOrDefault(options.DNS),
|
||||||
common.PtrValueOrDefault(options.NTP),
|
common.PtrValueOrDefault(options.NTP),
|
||||||
options.Inbounds,
|
options.Inbounds,
|
||||||
options.PlatformInterface,
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "parse route options")
|
return nil, E.Cause(err, "parse route options")
|
||||||
}
|
}
|
||||||
inbounds := make([]adapter.Inbound, 0, len(options.Inbounds))
|
|
||||||
outbounds := make([]adapter.Outbound, 0, len(options.Outbounds))
|
|
||||||
for i, inboundOptions := range options.Inbounds {
|
for i, inboundOptions := range options.Inbounds {
|
||||||
var in adapter.Inbound
|
var currentInbound adapter.Inbound
|
||||||
var tag string
|
var tag string
|
||||||
if inboundOptions.Tag != "" {
|
if inboundOptions.Tag != "" {
|
||||||
tag = inboundOptions.Tag
|
tag = inboundOptions.Tag
|
||||||
} else {
|
} else {
|
||||||
tag = F.ToString(i)
|
tag = F.ToString(i)
|
||||||
}
|
}
|
||||||
in, err = inbound.New(
|
currentInbound, err = inboundRegistry.CreateInbound(
|
||||||
ctx,
|
ctx,
|
||||||
router,
|
router,
|
||||||
logFactory.NewLogger(F.ToString("inbound/", inboundOptions.Type, "[", tag, "]")),
|
logFactory.NewLogger(F.ToString("inbound/", inboundOptions.Type, "[", tag, "]")),
|
||||||
tag,
|
tag,
|
||||||
inboundOptions,
|
inboundOptions.Type,
|
||||||
options.PlatformInterface,
|
inboundOptions.Options,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "parse inbound[", i, "]")
|
return nil, E.Cause(err, "parse inbound[", i, "]")
|
||||||
}
|
}
|
||||||
inbounds = append(inbounds, in)
|
inbounds = append(inbounds, currentInbound)
|
||||||
}
|
}
|
||||||
for i, outboundOptions := range options.Outbounds {
|
for i, outboundOptions := range options.Outbounds {
|
||||||
var out adapter.Outbound
|
var currentOutbound adapter.Outbound
|
||||||
var tag string
|
var tag string
|
||||||
if outboundOptions.Tag != "" {
|
if outboundOptions.Tag != "" {
|
||||||
tag = outboundOptions.Tag
|
tag = outboundOptions.Tag
|
||||||
} else {
|
} else {
|
||||||
tag = F.ToString(i)
|
tag = F.ToString(i)
|
||||||
}
|
}
|
||||||
out, err = outbound.New(
|
outboundCtx := ctx
|
||||||
ctx,
|
if tag != "" {
|
||||||
|
// TODO: remove this
|
||||||
|
outboundCtx = adapter.WithContext(outboundCtx, &adapter.InboundContext{
|
||||||
|
Outbound: tag,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
currentOutbound, err = outboundRegistry.CreateOutbound(
|
||||||
|
outboundCtx,
|
||||||
router,
|
router,
|
||||||
logFactory.NewLogger(F.ToString("outbound/", outboundOptions.Type, "[", tag, "]")),
|
logFactory.NewLogger(F.ToString("outbound/", outboundOptions.Type, "[", tag, "]")),
|
||||||
tag,
|
tag,
|
||||||
outboundOptions)
|
outboundOptions.Type,
|
||||||
|
outboundOptions.Options,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "parse outbound[", i, "]")
|
return nil, E.Cause(err, "parse outbound[", i, "]")
|
||||||
}
|
}
|
||||||
outbounds = append(outbounds, out)
|
outbounds = append(outbounds, currentOutbound)
|
||||||
}
|
}
|
||||||
err = router.Initialize(inbounds, outbounds, func() adapter.Outbound {
|
err = router.Initialize(inbounds, outbounds, func() adapter.Outbound {
|
||||||
out, oErr := outbound.New(ctx, router, logFactory.NewLogger("outbound/direct"), "direct", option.Outbound{Type: "direct", Tag: "default"})
|
defaultOutbound, cErr := direct.NewOutbound(ctx, router, logFactory.NewLogger("outbound/direct"), "direct", option.DirectOutboundOptions{})
|
||||||
common.Must(oErr)
|
common.Must(cErr)
|
||||||
outbounds = append(outbounds, out)
|
outbounds = append(outbounds, defaultOutbound)
|
||||||
return out
|
return defaultOutbound
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if options.PlatformInterface != nil {
|
if platformInterface != nil {
|
||||||
err = options.PlatformInterface.Initialize(ctx, router)
|
err = platformInterface.Initialize(ctx, router)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "initialize platform interface")
|
return nil, E.Cause(err, "initialize platform interface")
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,9 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box"
|
||||||
"github.com/sagernet/sing-box/experimental/deprecated"
|
"github.com/sagernet/sing-box/experimental/deprecated"
|
||||||
_ "github.com/sagernet/sing-box/include"
|
"github.com/sagernet/sing-box/include"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing/service"
|
"github.com/sagernet/sing/service"
|
||||||
"github.com/sagernet/sing/service/filemanager"
|
"github.com/sagernet/sing/service/filemanager"
|
||||||
|
@ -68,4 +69,5 @@ func preRun(cmd *cobra.Command, args []string) {
|
||||||
configPaths = append(configPaths, "config.json")
|
configPaths = append(configPaths, "config.json")
|
||||||
}
|
}
|
||||||
globalCtx = service.ContextWith(globalCtx, deprecated.NewStderrManager(log.StdLogger()))
|
globalCtx = service.ContextWith(globalCtx, deprecated.NewStderrManager(log.StdLogger()))
|
||||||
|
globalCtx = box.Context(globalCtx, include.InboundRegistry(), include.OutboundRegistry())
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
@ -38,7 +39,7 @@ func format() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, optionsEntry := range optionsList {
|
for _, optionsEntry := range optionsList {
|
||||||
optionsEntry.options, err = badjson.Omitempty(optionsEntry.options)
|
optionsEntry.options, err = badjson.Omitempty(context.TODO(), optionsEntry.options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,29 +68,19 @@ func merge(outputPath string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func mergePathResources(options *option.Options) error {
|
func mergePathResources(options *option.Options) error {
|
||||||
for index, inbound := range options.Inbounds {
|
for _, inbound := range options.Inbounds {
|
||||||
rawOptions, err := inbound.RawOptions()
|
if tlsOptions, containsTLSOptions := inbound.Options.(option.InboundTLSOptionsWrapper); containsTLSOptions {
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if tlsOptions, containsTLSOptions := rawOptions.(option.InboundTLSOptionsWrapper); containsTLSOptions {
|
|
||||||
tlsOptions.ReplaceInboundTLSOptions(mergeTLSInboundOptions(tlsOptions.TakeInboundTLSOptions()))
|
tlsOptions.ReplaceInboundTLSOptions(mergeTLSInboundOptions(tlsOptions.TakeInboundTLSOptions()))
|
||||||
}
|
}
|
||||||
options.Inbounds[index] = inbound
|
|
||||||
}
|
}
|
||||||
for index, outbound := range options.Outbounds {
|
for _, outbound := range options.Outbounds {
|
||||||
rawOptions, err := outbound.RawOptions()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
switch outbound.Type {
|
switch outbound.Type {
|
||||||
case C.TypeSSH:
|
case C.TypeSSH:
|
||||||
outbound.SSHOptions = mergeSSHOutboundOptions(outbound.SSHOptions)
|
mergeSSHOutboundOptions(outbound.Options.(*option.SSHOutboundOptions))
|
||||||
}
|
}
|
||||||
if tlsOptions, containsTLSOptions := rawOptions.(option.OutboundTLSOptionsWrapper); containsTLSOptions {
|
if tlsOptions, containsTLSOptions := outbound.Options.(option.OutboundTLSOptionsWrapper); containsTLSOptions {
|
||||||
tlsOptions.ReplaceOutboundTLSOptions(mergeTLSOutboundOptions(tlsOptions.TakeOutboundTLSOptions()))
|
tlsOptions.ReplaceOutboundTLSOptions(mergeTLSOutboundOptions(tlsOptions.TakeOutboundTLSOptions()))
|
||||||
}
|
}
|
||||||
options.Outbounds[index] = outbound
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -138,13 +128,12 @@ func mergeTLSOutboundOptions(options *option.OutboundTLSOptions) *option.Outboun
|
||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
func mergeSSHOutboundOptions(options option.SSHOutboundOptions) option.SSHOutboundOptions {
|
func mergeSSHOutboundOptions(options *option.SSHOutboundOptions) {
|
||||||
if options.PrivateKeyPath != "" {
|
if options.PrivateKeyPath != "" {
|
||||||
if content, err := os.ReadFile(os.ExpandEnv(options.PrivateKeyPath)); err == nil {
|
if content, err := os.ReadFile(os.ExpandEnv(options.PrivateKeyPath)); err == nil {
|
||||||
options.PrivateKey = trimStringArray(strings.Split(string(content), "\n"))
|
options.PrivateKey = trimStringArray(strings.Split(string(content), "\n"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return options
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func trimStringArray(array []string) []string {
|
func trimStringArray(array []string) []string {
|
||||||
|
|
|
@ -57,7 +57,7 @@ func readConfigAt(path string) (*OptionsEntry, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "read config at ", path)
|
return nil, E.Cause(err, "read config at ", path)
|
||||||
}
|
}
|
||||||
options, err := json.UnmarshalExtended[option.Options](configContent)
|
options, err := json.UnmarshalExtendedContext[option.Options](globalCtx, configContent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "decode config at ", path)
|
return nil, E.Cause(err, "decode config at ", path)
|
||||||
}
|
}
|
||||||
|
@ -109,13 +109,13 @@ func readConfigAndMerge() (option.Options, error) {
|
||||||
}
|
}
|
||||||
var mergedMessage json.RawMessage
|
var mergedMessage json.RawMessage
|
||||||
for _, options := range optionsList {
|
for _, options := range optionsList {
|
||||||
mergedMessage, err = badjson.MergeJSON(options.options.RawMessage, mergedMessage, false)
|
mergedMessage, err = badjson.MergeJSON(globalCtx, options.options.RawMessage, mergedMessage, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return option.Options{}, E.Cause(err, "merge config at ", options.path)
|
return option.Options{}, E.Cause(err, "merge config at ", options.path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var mergedOptions option.Options
|
var mergedOptions option.Options
|
||||||
err = mergedOptions.UnmarshalJSON(mergedMessage)
|
err = mergedOptions.UnmarshalJSONContext(globalCtx, mergedMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return option.Options{}, E.Cause(err, "unmarshal merged config")
|
return option.Options{}, E.Cause(err, "unmarshal merged config")
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,7 +125,7 @@ func NewDefault(router adapter.Router, options option.DialerOptions) (*DefaultDi
|
||||||
setMultiPathTCP(&dialer4)
|
setMultiPathTCP(&dialer4)
|
||||||
}
|
}
|
||||||
if options.IsWireGuardListener {
|
if options.IsWireGuardListener {
|
||||||
for _, controlFn := range wgControlFns {
|
for _, controlFn := range WgControlFns {
|
||||||
listener.Control = control.Append(listener.Control, controlFn)
|
listener.Control = control.Append(listener.Control, controlFn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,12 @@ package dialer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/common/control"
|
||||||
)
|
)
|
||||||
|
|
||||||
type WireGuardListener interface {
|
type WireGuardListener interface {
|
||||||
ListenPacketCompat(network, address string) (net.PacketConn, error)
|
ListenPacketCompat(network, address string) (net.PacketConn, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var WgControlFns []control.Func
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
//go:build with_wireguard
|
|
||||||
|
|
||||||
package dialer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/sagernet/wireguard-go/conn"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ WireGuardListener = (conn.Listener)(nil)
|
|
||||||
|
|
||||||
var wgControlFns = conn.ControlFns
|
|
|
@ -1,9 +0,0 @@
|
||||||
//go:build !with_wireguard
|
|
||||||
|
|
||||||
package dialer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/sagernet/sing/common/control"
|
|
||||||
)
|
|
||||||
|
|
||||||
var wgControlFns []control.Func
|
|
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
|
//go:build go1.21
|
||||||
|
|
||||||
package inbound
|
package listener
|
||||||
|
|
||||||
import "net"
|
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
|
//go:build !go1.21
|
||||||
|
|
||||||
package inbound
|
package listener
|
||||||
|
|
||||||
import "net"
|
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)
|
||||||
|
}
|
||||||
|
}
|
153
common/listener/listener_udp.go
Normal file
153
common/listener/listener_udp.go
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
package listener
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"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)
|
||||||
|
default:
|
||||||
|
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() {
|
||||||
|
}
|
|
@ -10,7 +10,7 @@ import (
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/urltest"
|
"github.com/sagernet/sing-box/common/urltest"
|
||||||
"github.com/sagernet/sing-box/outbound"
|
"github.com/sagernet/sing-box/protocol/group"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/batch"
|
"github.com/sagernet/sing/common/batch"
|
||||||
"github.com/sagernet/sing/common/json/badjson"
|
"github.com/sagernet/sing/common/json/badjson"
|
||||||
|
@ -59,7 +59,7 @@ func getGroup(server *Server) func(w http.ResponseWriter, r *http.Request) {
|
||||||
func getGroupDelay(server *Server) func(w http.ResponseWriter, r *http.Request) {
|
func getGroupDelay(server *Server) func(w http.ResponseWriter, r *http.Request) {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
proxy := r.Context().Value(CtxKeyProxy).(adapter.Outbound)
|
proxy := r.Context().Value(CtxKeyProxy).(adapter.Outbound)
|
||||||
group, ok := proxy.(adapter.OutboundGroup)
|
outboundGroup, ok := proxy.(adapter.OutboundGroup)
|
||||||
if !ok {
|
if !ok {
|
||||||
render.Status(r, http.StatusNotFound)
|
render.Status(r, http.StatusNotFound)
|
||||||
render.JSON(w, r, ErrNotFound)
|
render.JSON(w, r, ErrNotFound)
|
||||||
|
@ -82,10 +82,10 @@ func getGroupDelay(server *Server) func(w http.ResponseWriter, r *http.Request)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
var result map[string]uint16
|
var result map[string]uint16
|
||||||
if urlTestGroup, isURLTestGroup := group.(adapter.URLTestGroup); isURLTestGroup {
|
if urlTestGroup, isURLTestGroup := outboundGroup.(adapter.URLTestGroup); isURLTestGroup {
|
||||||
result, err = urlTestGroup.URLTest(ctx)
|
result, err = urlTestGroup.URLTest(ctx)
|
||||||
} else {
|
} else {
|
||||||
outbounds := common.FilterNotNil(common.Map(group.All(), func(it string) adapter.Outbound {
|
outbounds := common.FilterNotNil(common.Map(outboundGroup.All(), func(it string) adapter.Outbound {
|
||||||
itOutbound, _ := server.router.Outbound(it)
|
itOutbound, _ := server.router.Outbound(it)
|
||||||
return itOutbound
|
return itOutbound
|
||||||
}))
|
}))
|
||||||
|
@ -95,7 +95,7 @@ func getGroupDelay(server *Server) func(w http.ResponseWriter, r *http.Request)
|
||||||
var resultAccess sync.Mutex
|
var resultAccess sync.Mutex
|
||||||
for _, detour := range outbounds {
|
for _, detour := range outbounds {
|
||||||
tag := detour.Tag()
|
tag := detour.Tag()
|
||||||
realTag := outbound.RealTag(detour)
|
realTag := group.RealTag(detour)
|
||||||
if checked[realTag] {
|
if checked[realTag] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/urltest"
|
"github.com/sagernet/sing-box/common/urltest"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/outbound"
|
"github.com/sagernet/sing-box/protocol/group"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
F "github.com/sagernet/sing/common/format"
|
F "github.com/sagernet/sing/common/format"
|
||||||
"github.com/sagernet/sing/common/json/badjson"
|
"github.com/sagernet/sing/common/json/badjson"
|
||||||
|
@ -168,7 +168,7 @@ func updateProxy(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
proxy := r.Context().Value(CtxKeyProxy).(adapter.Outbound)
|
proxy := r.Context().Value(CtxKeyProxy).(adapter.Outbound)
|
||||||
selector, ok := proxy.(*outbound.Selector)
|
selector, ok := proxy.(*group.Selector)
|
||||||
if !ok {
|
if !ok {
|
||||||
render.Status(r, http.StatusBadRequest)
|
render.Status(r, http.StatusBadRequest)
|
||||||
render.JSON(w, r, newError("Must be a Selector"))
|
render.JSON(w, r, newError("Must be a Selector"))
|
||||||
|
@ -204,7 +204,7 @@ func getProxyDelay(server *Server) func(w http.ResponseWriter, r *http.Request)
|
||||||
|
|
||||||
delay, err := urltest.URLTest(ctx, url, proxy)
|
delay, err := urltest.URLTest(ctx, url, proxy)
|
||||||
defer func() {
|
defer func() {
|
||||||
realTag := outbound.RealTag(proxy)
|
realTag := group.RealTag(proxy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
server.urlTestHistory.DeleteURLTestHistory(realTag)
|
server.urlTestHistory.DeleteURLTestHistory(realTag)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/urltest"
|
"github.com/sagernet/sing-box/common/urltest"
|
||||||
"github.com/sagernet/sing-box/outbound"
|
"github.com/sagernet/sing-box/protocol/group"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/varbin"
|
"github.com/sagernet/sing/common/varbin"
|
||||||
"github.com/sagernet/sing/service"
|
"github.com/sagernet/sing/service"
|
||||||
|
@ -118,14 +118,14 @@ func writeGroups(writer io.Writer, boxService *BoxService) error {
|
||||||
}
|
}
|
||||||
var groups []OutboundGroup
|
var groups []OutboundGroup
|
||||||
for _, iGroup := range iGroups {
|
for _, iGroup := range iGroups {
|
||||||
var group OutboundGroup
|
var outboundGroup OutboundGroup
|
||||||
group.Tag = iGroup.Tag()
|
outboundGroup.Tag = iGroup.Tag()
|
||||||
group.Type = iGroup.Type()
|
outboundGroup.Type = iGroup.Type()
|
||||||
_, group.Selectable = iGroup.(*outbound.Selector)
|
_, outboundGroup.Selectable = iGroup.(*group.Selector)
|
||||||
group.Selected = iGroup.Now()
|
outboundGroup.Selected = iGroup.Now()
|
||||||
if cacheFile != nil {
|
if cacheFile != nil {
|
||||||
if isExpand, loaded := cacheFile.LoadGroupExpand(group.Tag); loaded {
|
if isExpand, loaded := cacheFile.LoadGroupExpand(outboundGroup.Tag); loaded {
|
||||||
group.IsExpand = isExpand
|
outboundGroup.IsExpand = isExpand
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,12 +142,12 @@ func writeGroups(writer io.Writer, boxService *BoxService) error {
|
||||||
item.URLTestTime = history.Time.Unix()
|
item.URLTestTime = history.Time.Unix()
|
||||||
item.URLTestDelay = int32(history.Delay)
|
item.URLTestDelay = int32(history.Delay)
|
||||||
}
|
}
|
||||||
group.ItemList = append(group.ItemList, &item)
|
outboundGroup.ItemList = append(outboundGroup.ItemList, &item)
|
||||||
}
|
}
|
||||||
if len(group.ItemList) < 2 {
|
if len(outboundGroup.ItemList) < 2 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
groups = append(groups, group)
|
groups = append(groups, outboundGroup)
|
||||||
}
|
}
|
||||||
return varbin.Write(writer, binary.BigEndian, groups)
|
return varbin.Write(writer, binary.BigEndian, groups)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/outbound"
|
"github.com/sagernet/sing-box/protocol/group"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/varbin"
|
"github.com/sagernet/sing/common/varbin"
|
||||||
)
|
)
|
||||||
|
@ -47,7 +47,7 @@ func (s *CommandServer) handleSelectOutbound(conn net.Conn) error {
|
||||||
if !isLoaded {
|
if !isLoaded {
|
||||||
return writeError(conn, E.New("selector not found: ", groupTag))
|
return writeError(conn, E.New("selector not found: ", groupTag))
|
||||||
}
|
}
|
||||||
selector, isSelector := outboundGroup.(*outbound.Selector)
|
selector, isSelector := outboundGroup.(*group.Selector)
|
||||||
if !isSelector {
|
if !isSelector {
|
||||||
return writeError(conn, E.New("outbound is not a selector: ", groupTag))
|
return writeError(conn, E.New("outbound is not a selector: ", groupTag))
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/urltest"
|
"github.com/sagernet/sing-box/common/urltest"
|
||||||
"github.com/sagernet/sing-box/outbound"
|
"github.com/sagernet/sing-box/protocol/group"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/batch"
|
"github.com/sagernet/sing/common/batch"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
@ -49,7 +49,7 @@ func (s *CommandServer) handleURLTest(conn net.Conn) error {
|
||||||
if !isOutboundGroup {
|
if !isOutboundGroup {
|
||||||
return writeError(conn, E.New("outbound is not a group: ", groupTag))
|
return writeError(conn, E.New("outbound is not a group: ", groupTag))
|
||||||
}
|
}
|
||||||
urlTest, isURLTest := abstractOutboundGroup.(*outbound.URLTest)
|
urlTest, isURLTest := abstractOutboundGroup.(*group.URLTest)
|
||||||
if isURLTest {
|
if isURLTest {
|
||||||
go urlTest.CheckOutbounds()
|
go urlTest.CheckOutbounds()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/process"
|
"github.com/sagernet/sing-box/common/process"
|
||||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||||
|
"github.com/sagernet/sing-box/include"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-tun"
|
"github.com/sagernet/sing-tun"
|
||||||
"github.com/sagernet/sing/common/control"
|
"github.com/sagernet/sing/common/control"
|
||||||
|
@ -17,10 +18,11 @@ import (
|
||||||
"github.com/sagernet/sing/common/json"
|
"github.com/sagernet/sing/common/json"
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
"github.com/sagernet/sing/common/x/list"
|
"github.com/sagernet/sing/common/x/list"
|
||||||
|
"github.com/sagernet/sing/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
func parseConfig(configContent string) (option.Options, error) {
|
func parseConfig(ctx context.Context, configContent string) (option.Options, error) {
|
||||||
options, err := json.UnmarshalExtended[option.Options]([]byte(configContent))
|
options, err := json.UnmarshalExtendedContext[option.Options](ctx, []byte(configContent))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return option.Options{}, E.Cause(err, "decode config")
|
return option.Options{}, E.Cause(err, "decode config")
|
||||||
}
|
}
|
||||||
|
@ -28,16 +30,17 @@ func parseConfig(configContent string) (option.Options, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckConfig(configContent string) error {
|
func CheckConfig(configContent string) error {
|
||||||
options, err := parseConfig(configContent)
|
ctx := box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry())
|
||||||
|
options, err := parseConfig(ctx, configContent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
ctx = service.ContextWith[platform.Interface](ctx, (*platformInterfaceStub)(nil))
|
||||||
instance, err := box.New(box.Options{
|
instance, err := box.New(box.Options{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
Options: options,
|
Options: options,
|
||||||
PlatformInterface: (*platformInterfaceStub)(nil),
|
|
||||||
})
|
})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
instance.Close()
|
instance.Close()
|
||||||
|
@ -140,7 +143,7 @@ func (s *platformInterfaceStub) SendNotification(notification *platform.Notifica
|
||||||
}
|
}
|
||||||
|
|
||||||
func FormatConfig(configContent string) (string, error) {
|
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 {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"github.com/sagernet/sing-box/experimental/deprecated"
|
"github.com/sagernet/sing-box/experimental/deprecated"
|
||||||
"github.com/sagernet/sing-box/experimental/libbox/internal/procfs"
|
"github.com/sagernet/sing-box/experimental/libbox/internal/procfs"
|
||||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||||
|
"github.com/sagernet/sing-box/include"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-tun"
|
"github.com/sagernet/sing-tun"
|
||||||
|
@ -41,21 +42,22 @@ type BoxService struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(configContent string, platformInterface PlatformInterface) (*BoxService, error) {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
runtimeDebug.FreeOSMemory()
|
runtimeDebug.FreeOSMemory()
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
ctx = filemanager.WithDefault(ctx, sWorkingPath, sTempPath, sUserID, sGroupID)
|
ctx = filemanager.WithDefault(ctx, sWorkingPath, sTempPath, sUserID, sGroupID)
|
||||||
urlTestHistoryStorage := urltest.NewHistoryStorage()
|
urlTestHistoryStorage := urltest.NewHistoryStorage()
|
||||||
ctx = service.ContextWithPtr(ctx, urlTestHistoryStorage)
|
ctx = service.ContextWithPtr(ctx, urlTestHistoryStorage)
|
||||||
ctx = service.ContextWith[deprecated.Manager](ctx, new(deprecatedManager))
|
ctx = service.ContextWith[deprecated.Manager](ctx, new(deprecatedManager))
|
||||||
platformWrapper := &platformInterfaceWrapper{iif: platformInterface, useProcFS: platformInterface.UseProcFS()}
|
platformWrapper := &platformInterfaceWrapper{iif: platformInterface, useProcFS: platformInterface.UseProcFS()}
|
||||||
|
ctx = service.ContextWith[platform.Interface](ctx, platformWrapper)
|
||||||
instance, err := box.New(box.Options{
|
instance, err := box.New(box.Options{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
Options: options,
|
Options: options,
|
||||||
PlatformInterface: platformWrapper,
|
|
||||||
PlatformLogWriter: platformWrapper,
|
PlatformLogWriter: platformWrapper,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/humanize"
|
"github.com/sagernet/sing-box/common/humanize"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
_ "github.com/sagernet/sing-box/include"
|
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -3,7 +3,6 @@ module github.com/sagernet/sing-box
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
berty.tech/go-libtor v1.0.385
|
|
||||||
github.com/caddyserver/certmagic v0.20.0
|
github.com/caddyserver/certmagic v0.20.0
|
||||||
github.com/cloudflare/circl v1.3.7
|
github.com/cloudflare/circl v1.3.7
|
||||||
github.com/cretz/bine v0.2.0
|
github.com/cretz/bine v0.2.0
|
||||||
|
@ -17,7 +16,6 @@ require (
|
||||||
github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa
|
github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa
|
||||||
github.com/mholt/acmez v1.2.0
|
github.com/mholt/acmez v1.2.0
|
||||||
github.com/miekg/dns v1.1.62
|
github.com/miekg/dns v1.1.62
|
||||||
github.com/ooni/go-libtor v1.1.8
|
|
||||||
github.com/oschwald/maxminddb-golang v1.12.0
|
github.com/oschwald/maxminddb-golang v1.12.0
|
||||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
|
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1
|
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1
|
||||||
|
|
8
go.sum
8
go.sum
|
@ -1,5 +1,3 @@
|
||||||
berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw=
|
|
||||||
berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw=
|
|
||||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||||
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
|
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
|
||||||
|
@ -9,7 +7,6 @@ github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NY
|
||||||
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||||
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/cretz/bine v0.1.0/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw=
|
|
||||||
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
|
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
|
||||||
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
|
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
@ -81,8 +78,6 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
|
||||||
github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss=
|
github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss=
|
||||||
github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0=
|
github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0=
|
||||||
github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU=
|
github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU=
|
||||||
github.com/ooni/go-libtor v1.1.8 h1:Wo3V3DVTxl5vZdxtQakqYP+DAHx7pPtAFSl1bnAa08w=
|
|
||||||
github.com/ooni/go-libtor v1.1.8/go.mod h1:q1YyLwRD9GeMyeerVvwc0vJ2YgwDLTp2bdVcrh/JXyI=
|
|
||||||
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
|
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
|
||||||
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
|
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
|
||||||
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
||||||
|
@ -146,7 +141,6 @@ github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3k
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
|
@ -168,7 +162,6 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||||
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
|
||||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||||
|
@ -182,7 +175,6 @@ golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||||
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
||||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
|
|
@ -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, false)
|
|
||||||
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,6 +3,24 @@
|
||||||
package include
|
package include
|
||||||
|
|
||||||
import (
|
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-box/transport/v2rayquic"
|
||||||
_ "github.com/sagernet/sing-dns/quic"
|
_ "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)
|
||||||
|
hysteria2.RegisterOutbound(registry)
|
||||||
|
}
|
||||||
|
|
|
@ -4,11 +4,18 @@ package include
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"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"
|
"github.com/sagernet/sing-box/common/tls"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing-box/protocol/naive"
|
||||||
"github.com/sagernet/sing-box/transport/v2ray"
|
"github.com/sagernet/sing-box/transport/v2ray"
|
||||||
"github.com/sagernet/sing-dns"
|
"github.com/sagernet/sing-dns"
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
|
@ -29,3 +36,30 @@ 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
|
||||||
|
})
|
||||||
|
outbound.Register[option.TUICOutboundOptions](registry, C.TypeTUIC, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TUICOutboundOptions) (adapter.Outbound, error) {
|
||||||
|
return nil, C.ErrQUICNotIncluded
|
||||||
|
})
|
||||||
|
outbound.Register[option.Hysteria2OutboundOptions](registry, C.TypeHysteria2, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.Hysteria2OutboundOptions) (adapter.Outbound, error) {
|
||||||
|
return nil, C.ErrQUICNotIncluded
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
95
include/registry.go
Normal file
95
include/registry.go
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
package include
|
||||||
|
|
||||||
|
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"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing-box/protocol/block"
|
||||||
|
"github.com/sagernet/sing-box/protocol/direct"
|
||||||
|
"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()
|
||||||
|
|
||||||
|
direct.RegisterOutbound(registry)
|
||||||
|
|
||||||
|
block.RegisterOutbound(registry)
|
||||||
|
dns.RegisterOutbound(registry)
|
||||||
|
|
||||||
|
group.RegisterSelector(registry)
|
||||||
|
group.RegisterURLTest(registry)
|
||||||
|
|
||||||
|
socks.RegisterOutbound(registry)
|
||||||
|
http.RegisterOutbound(registry)
|
||||||
|
shadowsocks.RegisterOutbound(registry)
|
||||||
|
vmess.RegisterOutbound(registry)
|
||||||
|
trojan.RegisterOutbound(registry)
|
||||||
|
tor.RegisterOutbound(registry)
|
||||||
|
ssh.RegisterOutbound(registry)
|
||||||
|
shadowtls.RegisterOutbound(registry)
|
||||||
|
vless.RegisterOutbound(registry)
|
||||||
|
|
||||||
|
registerQUICOutbounds(registry)
|
||||||
|
registerWireGuardOutbound(registry)
|
||||||
|
registerStubForRemovedOutbounds(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")
|
||||||
|
})
|
||||||
|
}
|
12
include/wireguard.go
Normal file
12
include/wireguard.go
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
//go:build with_wireguard
|
||||||
|
|
||||||
|
package include
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sagernet/sing-box/adapter/outbound"
|
||||||
|
"github.com/sagernet/sing-box/protocol/wireguard"
|
||||||
|
)
|
||||||
|
|
||||||
|
func registerWireGuardOutbound(registry *outbound.Registry) {
|
||||||
|
wireguard.RegisterOutbound(registry)
|
||||||
|
}
|
20
include/wireguard_stub.go
Normal file
20
include/wireguard_stub.go
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
//go:build !with_wireguard
|
||||||
|
|
||||||
|
package include
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/adapter/outbound"
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/log"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
)
|
||||||
|
|
||||||
|
func registerWireGuardOutbound(registry *outbound.Registry) {
|
||||||
|
outbound.Register[option.WireGuardOutboundOptions](registry, C.TypeWireGuard, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.WireGuardOutboundOptions) (adapter.Outbound, error) {
|
||||||
|
return nil, E.New(`WireGuard is not included in this build, rebuild with -tags with_wireguard`)
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,100 +1,49 @@
|
||||||
package option
|
package option
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/json"
|
"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 _Inbound struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Tag string `json:"tag,omitempty"`
|
Tag string `json:"tag,omitempty"`
|
||||||
TunOptions TunInboundOptions `json:"-"`
|
Options any `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 Inbound _Inbound
|
type Inbound _Inbound
|
||||||
|
|
||||||
func (h *Inbound) RawOptions() (any, error) {
|
func (h *Inbound) MarshalJSONContext(ctx context.Context) ([]byte, error) {
|
||||||
var rawOptionsPtr any
|
return badjson.MarshallObjectsContext(ctx, (*_Inbound)(h), h.Options)
|
||||||
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) MarshalJSON() ([]byte, error) {
|
func (h *Inbound) UnmarshalJSONContext(ctx context.Context, content []byte) error {
|
||||||
rawOptions, err := h.RawOptions()
|
err := json.Unmarshal(content, (*_Inbound)(h))
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return MarshallObjects((_Inbound)(h), rawOptions)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Inbound) UnmarshalJSON(bytes []byte) error {
|
|
||||||
err := json.Unmarshal(bytes, (*_Inbound)(h))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
rawOptions, err := h.RawOptions()
|
registry := service.FromContext[InboundOptionsRegistry](ctx)
|
||||||
if err != nil {
|
if registry == nil {
|
||||||
return err
|
return E.New("missing inbound options registry in context")
|
||||||
}
|
}
|
||||||
err = UnmarshallExcluded(bytes, (*_Inbound)(h), rawOptions)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
h.Options = options
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,19 +54,24 @@ type InboundOptions struct {
|
||||||
SniffTimeout Duration `json:"sniff_timeout,omitempty"`
|
SniffTimeout Duration `json:"sniff_timeout,omitempty"`
|
||||||
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
|
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
|
||||||
UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"`
|
UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"`
|
||||||
|
Detour string `json:"detour,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ListenOptions struct {
|
type ListenOptions struct {
|
||||||
Listen *ListenAddress `json:"listen,omitempty"`
|
Listen *ListenAddress `json:"listen,omitempty"`
|
||||||
ListenPort uint16 `json:"listen_port,omitempty"`
|
ListenPort uint16 `json:"listen_port,omitempty"`
|
||||||
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
TCPKeepAlive Duration `json:"tcp_keep_alive,omitempty"`
|
||||||
TCPMultiPath bool `json:"tcp_multi_path,omitempty"`
|
TCPKeepAliveInterval Duration `json:"tcp_keep_alive_interval,omitempty"`
|
||||||
UDPFragment *bool `json:"udp_fragment,omitempty"`
|
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||||
UDPFragmentDefault bool `json:"-"`
|
TCPMultiPath bool `json:"tcp_multi_path,omitempty"`
|
||||||
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
|
UDPFragment *bool `json:"udp_fragment,omitempty"`
|
||||||
ProxyProtocol bool `json:"proxy_protocol,omitempty"`
|
UDPFragmentDefault bool `json:"-"`
|
||||||
ProxyProtocolAcceptNoHeader bool `json:"proxy_protocol_accept_no_header,omitempty"`
|
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
|
||||||
Detour string `json:"detour,omitempty"`
|
|
||||||
|
// Deprecated: removed
|
||||||
|
ProxyProtocol bool `json:"proxy_protocol,omitempty"`
|
||||||
|
// Deprecated: removed
|
||||||
|
ProxyProtocolAcceptNoHeader bool `json:"proxy_protocol_accept_no_header,omitempty"`
|
||||||
InboundOptions
|
InboundOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,71 +0,0 @@
|
||||||
package option
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/sagernet/sing/common"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
"github.com/sagernet/sing/common/json"
|
|
||||||
"github.com/sagernet/sing/common/json/badjson"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ToMap(v any) (*badjson.JSONObject, error) {
|
|
||||||
inputContent, err := json.Marshal(v)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var content badjson.JSONObject
|
|
||||||
err = content.UnmarshalJSON(inputContent)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &content, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func MergeObjects(objects ...any) (*badjson.JSONObject, error) {
|
|
||||||
var content badjson.JSONObject
|
|
||||||
for _, object := range objects {
|
|
||||||
objectMap, err := ToMap(object)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
content.PutAll(objectMap)
|
|
||||||
}
|
|
||||||
return &content, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func MarshallObjects(objects ...any) ([]byte, error) {
|
|
||||||
objects = common.FilterNotNil(objects)
|
|
||||||
if len(objects) == 1 {
|
|
||||||
return json.Marshal(objects[0])
|
|
||||||
}
|
|
||||||
content, err := MergeObjects(objects...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return content.MarshalJSON()
|
|
||||||
}
|
|
||||||
|
|
||||||
func UnmarshallExcluded(inputContent []byte, parentObject any, object any) error {
|
|
||||||
parentContent, err := ToMap(parentObject)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var content badjson.JSONObject
|
|
||||||
err = content.UnmarshalJSON(inputContent)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, key := range parentContent.Keys() {
|
|
||||||
content.Remove(key)
|
|
||||||
}
|
|
||||||
if object == nil {
|
|
||||||
if content.IsEmpty() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return E.New("unexpected key: ", content.Keys()[0])
|
|
||||||
}
|
|
||||||
inputContent, err = content.MarshalJSON()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return json.UnmarshalDisallowUnknownFields(inputContent, object)
|
|
||||||
}
|
|
|
@ -2,6 +2,7 @@ package option
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
|
|
||||||
"github.com/sagernet/sing/common/json"
|
"github.com/sagernet/sing/common/json"
|
||||||
)
|
)
|
||||||
|
@ -20,8 +21,8 @@ type _Options struct {
|
||||||
|
|
||||||
type Options _Options
|
type Options _Options
|
||||||
|
|
||||||
func (o *Options) UnmarshalJSON(content []byte) error {
|
func (o *Options) UnmarshalJSONContext(ctx context.Context, content []byte) error {
|
||||||
decoder := json.NewDecoder(bytes.NewReader(content))
|
decoder := json.NewDecoderContext(ctx, bytes.NewReader(content))
|
||||||
decoder.DisallowUnknownFields()
|
decoder.DisallowUnknownFields()
|
||||||
err := decoder.Decode((*_Options)(o))
|
err := decoder.Decode((*_Options)(o))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -38,3 +39,5 @@ type LogOptions struct {
|
||||||
Timestamp bool `json:"timestamp,omitempty"`
|
Timestamp bool `json:"timestamp,omitempty"`
|
||||||
DisableColor bool `json:"-"`
|
DisableColor bool `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type StubOptions struct{}
|
|
@ -1,104 +1,49 @@
|
||||||
package option
|
package option
|
||||||
|
|
||||||
import (
|
import (
|
||||||
C "github.com/sagernet/sing-box/constant"
|
"context"
|
||||||
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/json"
|
"github.com/sagernet/sing/common/json"
|
||||||
|
"github.com/sagernet/sing/common/json/badjson"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
"github.com/sagernet/sing/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type OutboundOptionsRegistry interface {
|
||||||
|
CreateOptions(outboundType string) (any, bool)
|
||||||
|
}
|
||||||
|
|
||||||
type _Outbound struct {
|
type _Outbound struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Tag string `json:"tag,omitempty"`
|
Tag string `json:"tag,omitempty"`
|
||||||
DirectOptions DirectOutboundOptions `json:"-"`
|
Options any `json:"-"`
|
||||||
SocksOptions SocksOutboundOptions `json:"-"`
|
|
||||||
HTTPOptions HTTPOutboundOptions `json:"-"`
|
|
||||||
ShadowsocksOptions ShadowsocksOutboundOptions `json:"-"`
|
|
||||||
VMessOptions VMessOutboundOptions `json:"-"`
|
|
||||||
TrojanOptions TrojanOutboundOptions `json:"-"`
|
|
||||||
WireGuardOptions WireGuardOutboundOptions `json:"-"`
|
|
||||||
HysteriaOptions HysteriaOutboundOptions `json:"-"`
|
|
||||||
TorOptions TorOutboundOptions `json:"-"`
|
|
||||||
SSHOptions SSHOutboundOptions `json:"-"`
|
|
||||||
ShadowTLSOptions ShadowTLSOutboundOptions `json:"-"`
|
|
||||||
ShadowsocksROptions ShadowsocksROutboundOptions `json:"-"`
|
|
||||||
VLESSOptions VLESSOutboundOptions `json:"-"`
|
|
||||||
TUICOptions TUICOutboundOptions `json:"-"`
|
|
||||||
Hysteria2Options Hysteria2OutboundOptions `json:"-"`
|
|
||||||
SelectorOptions SelectorOutboundOptions `json:"-"`
|
|
||||||
URLTestOptions URLTestOutboundOptions `json:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Outbound _Outbound
|
type Outbound _Outbound
|
||||||
|
|
||||||
func (h *Outbound) RawOptions() (any, error) {
|
func (h *Outbound) MarshalJSONContext(ctx context.Context) ([]byte, error) {
|
||||||
var rawOptionsPtr any
|
return badjson.MarshallObjectsContext(ctx, (*_Outbound)(h), h.Options)
|
||||||
switch h.Type {
|
|
||||||
case C.TypeDirect:
|
|
||||||
rawOptionsPtr = &h.DirectOptions
|
|
||||||
case C.TypeBlock, C.TypeDNS:
|
|
||||||
rawOptionsPtr = nil
|
|
||||||
case C.TypeSOCKS:
|
|
||||||
rawOptionsPtr = &h.SocksOptions
|
|
||||||
case C.TypeHTTP:
|
|
||||||
rawOptionsPtr = &h.HTTPOptions
|
|
||||||
case C.TypeShadowsocks:
|
|
||||||
rawOptionsPtr = &h.ShadowsocksOptions
|
|
||||||
case C.TypeVMess:
|
|
||||||
rawOptionsPtr = &h.VMessOptions
|
|
||||||
case C.TypeTrojan:
|
|
||||||
rawOptionsPtr = &h.TrojanOptions
|
|
||||||
case C.TypeWireGuard:
|
|
||||||
rawOptionsPtr = &h.WireGuardOptions
|
|
||||||
case C.TypeHysteria:
|
|
||||||
rawOptionsPtr = &h.HysteriaOptions
|
|
||||||
case C.TypeTor:
|
|
||||||
rawOptionsPtr = &h.TorOptions
|
|
||||||
case C.TypeSSH:
|
|
||||||
rawOptionsPtr = &h.SSHOptions
|
|
||||||
case C.TypeShadowTLS:
|
|
||||||
rawOptionsPtr = &h.ShadowTLSOptions
|
|
||||||
case C.TypeShadowsocksR:
|
|
||||||
rawOptionsPtr = &h.ShadowsocksROptions
|
|
||||||
case C.TypeVLESS:
|
|
||||||
rawOptionsPtr = &h.VLESSOptions
|
|
||||||
case C.TypeTUIC:
|
|
||||||
rawOptionsPtr = &h.TUICOptions
|
|
||||||
case C.TypeHysteria2:
|
|
||||||
rawOptionsPtr = &h.Hysteria2Options
|
|
||||||
case C.TypeSelector:
|
|
||||||
rawOptionsPtr = &h.SelectorOptions
|
|
||||||
case C.TypeURLTest:
|
|
||||||
rawOptionsPtr = &h.URLTestOptions
|
|
||||||
case "":
|
|
||||||
return nil, E.New("missing outbound type")
|
|
||||||
default:
|
|
||||||
return nil, E.New("unknown outbound type: ", h.Type)
|
|
||||||
}
|
|
||||||
return rawOptionsPtr, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Outbound) MarshalJSON() ([]byte, error) {
|
func (h *Outbound) UnmarshalJSONContext(ctx context.Context, content []byte) error {
|
||||||
rawOptions, err := h.RawOptions()
|
err := json.Unmarshal(content, (*_Outbound)(h))
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return MarshallObjects((*_Outbound)(h), rawOptions)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Outbound) UnmarshalJSON(bytes []byte) error {
|
|
||||||
err := json.Unmarshal(bytes, (*_Outbound)(h))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
rawOptions, err := h.RawOptions()
|
registry := service.FromContext[OutboundOptionsRegistry](ctx)
|
||||||
if err != nil {
|
if registry == nil {
|
||||||
return err
|
return E.New("missing outbound options registry in context")
|
||||||
}
|
}
|
||||||
err = UnmarshallExcluded(bytes, (*_Outbound)(h), rawOptions)
|
options, loaded := registry.CreateOptions(h.Type)
|
||||||
|
if !loaded {
|
||||||
|
return E.New("unknown outbound type: ", h.Type)
|
||||||
|
}
|
||||||
|
err = badjson.UnmarshallExcludedContext(ctx, content, (*_Outbound)(h), options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
h.Options = options
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/json"
|
"github.com/sagernet/sing/common/json"
|
||||||
|
"github.com/sagernet/sing/common/json/badjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
type _Rule struct {
|
type _Rule struct {
|
||||||
|
@ -28,7 +29,7 @@ func (r Rule) MarshalJSON() ([]byte, error) {
|
||||||
default:
|
default:
|
||||||
return nil, E.New("unknown rule type: " + r.Type)
|
return nil, E.New("unknown rule type: " + r.Type)
|
||||||
}
|
}
|
||||||
return MarshallObjects((_Rule)(r), v)
|
return badjson.MarshallObjects((_Rule)(r), v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Rule) UnmarshalJSON(bytes []byte) error {
|
func (r *Rule) UnmarshalJSON(bytes []byte) error {
|
||||||
|
@ -46,7 +47,7 @@ func (r *Rule) UnmarshalJSON(bytes []byte) error {
|
||||||
default:
|
default:
|
||||||
return E.New("unknown rule type: " + r.Type)
|
return E.New("unknown rule type: " + r.Type)
|
||||||
}
|
}
|
||||||
err = UnmarshallExcluded(bytes, (*_Rule)(r), v)
|
err = badjson.UnmarshallExcluded(bytes, (*_Rule)(r), v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -109,7 +110,7 @@ type DefaultRule struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DefaultRule) MarshalJSON() ([]byte, error) {
|
func (r *DefaultRule) MarshalJSON() ([]byte, error) {
|
||||||
return MarshallObjects(r.RawDefaultRule, r.RuleAction)
|
return badjson.MarshallObjects(r.RawDefaultRule, r.RuleAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DefaultRule) UnmarshalJSON(data []byte) error {
|
func (r *DefaultRule) UnmarshalJSON(data []byte) error {
|
||||||
|
@ -117,7 +118,7 @@ func (r *DefaultRule) UnmarshalJSON(data []byte) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return UnmarshallExcluded(data, &r.RawDefaultRule, &r.RuleAction)
|
return badjson.UnmarshallExcluded(data, &r.RawDefaultRule, &r.RuleAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DefaultRule) IsValid() bool {
|
func (r *DefaultRule) IsValid() bool {
|
||||||
|
@ -139,7 +140,7 @@ type LogicalRule struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *LogicalRule) MarshalJSON() ([]byte, error) {
|
func (r *LogicalRule) MarshalJSON() ([]byte, error) {
|
||||||
return MarshallObjects(r._LogicalRule, r.RuleAction)
|
return badjson.MarshallObjects(r._LogicalRule, r.RuleAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *LogicalRule) UnmarshalJSON(data []byte) error {
|
func (r *LogicalRule) UnmarshalJSON(data []byte) error {
|
||||||
|
@ -147,7 +148,7 @@ func (r *LogicalRule) UnmarshalJSON(data []byte) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return UnmarshallExcluded(data, &r._LogicalRule, &r.RuleAction)
|
return badjson.UnmarshallExcluded(data, &r._LogicalRule, &r.RuleAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *LogicalRule) IsValid() bool {
|
func (r *LogicalRule) IsValid() bool {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/json"
|
"github.com/sagernet/sing/common/json"
|
||||||
|
"github.com/sagernet/sing/common/json/badjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
type _RuleAction struct {
|
type _RuleAction struct {
|
||||||
|
@ -36,9 +37,9 @@ func (r RuleAction) MarshalJSON() ([]byte, error) {
|
||||||
return nil, E.New("unknown rule action: " + r.Action)
|
return nil, E.New("unknown rule action: " + r.Action)
|
||||||
}
|
}
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return MarshallObjects((_RuleAction)(r))
|
return badjson.MarshallObjects((_RuleAction)(r))
|
||||||
}
|
}
|
||||||
return MarshallObjects((_RuleAction)(r), v)
|
return badjson.MarshallObjects((_RuleAction)(r), v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuleAction) UnmarshalJSON(data []byte) error {
|
func (r *RuleAction) UnmarshalJSON(data []byte) error {
|
||||||
|
@ -68,7 +69,7 @@ func (r *RuleAction) UnmarshalJSON(data []byte) error {
|
||||||
// check unknown fields
|
// check unknown fields
|
||||||
return json.UnmarshalDisallowUnknownFields(data, &_RuleAction{})
|
return json.UnmarshalDisallowUnknownFields(data, &_RuleAction{})
|
||||||
}
|
}
|
||||||
return UnmarshallExcluded(data, (*_RuleAction)(r), v)
|
return badjson.UnmarshallExcluded(data, (*_RuleAction)(r), v)
|
||||||
}
|
}
|
||||||
|
|
||||||
type _DNSRuleAction struct {
|
type _DNSRuleAction struct {
|
||||||
|
@ -95,9 +96,9 @@ func (r DNSRuleAction) MarshalJSON() ([]byte, error) {
|
||||||
return nil, E.New("unknown DNS rule action: " + r.Action)
|
return nil, E.New("unknown DNS rule action: " + r.Action)
|
||||||
}
|
}
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return MarshallObjects((_DNSRuleAction)(r))
|
return badjson.MarshallObjects((_DNSRuleAction)(r))
|
||||||
}
|
}
|
||||||
return MarshallObjects((_DNSRuleAction)(r), v)
|
return badjson.MarshallObjects((_DNSRuleAction)(r), v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DNSRuleAction) UnmarshalJSON(data []byte) error {
|
func (r *DNSRuleAction) UnmarshalJSON(data []byte) error {
|
||||||
|
@ -121,7 +122,7 @@ func (r *DNSRuleAction) UnmarshalJSON(data []byte) error {
|
||||||
// check unknown fields
|
// check unknown fields
|
||||||
return json.UnmarshalDisallowUnknownFields(data, &_DNSRuleAction{})
|
return json.UnmarshalDisallowUnknownFields(data, &_DNSRuleAction{})
|
||||||
}
|
}
|
||||||
return UnmarshallExcluded(data, (*_DNSRuleAction)(r), v)
|
return badjson.UnmarshallExcluded(data, (*_DNSRuleAction)(r), v)
|
||||||
}
|
}
|
||||||
|
|
||||||
type RouteActionOptions struct {
|
type RouteActionOptions struct {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/json"
|
"github.com/sagernet/sing/common/json"
|
||||||
|
"github.com/sagernet/sing/common/json/badjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
type _DNSRule struct {
|
type _DNSRule struct {
|
||||||
|
@ -28,7 +29,7 @@ func (r DNSRule) MarshalJSON() ([]byte, error) {
|
||||||
default:
|
default:
|
||||||
return nil, E.New("unknown rule type: " + r.Type)
|
return nil, E.New("unknown rule type: " + r.Type)
|
||||||
}
|
}
|
||||||
return MarshallObjects((_DNSRule)(r), v)
|
return badjson.MarshallObjects((_DNSRule)(r), v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DNSRule) UnmarshalJSON(bytes []byte) error {
|
func (r *DNSRule) UnmarshalJSON(bytes []byte) error {
|
||||||
|
@ -46,7 +47,7 @@ func (r *DNSRule) UnmarshalJSON(bytes []byte) error {
|
||||||
default:
|
default:
|
||||||
return E.New("unknown rule type: " + r.Type)
|
return E.New("unknown rule type: " + r.Type)
|
||||||
}
|
}
|
||||||
err = UnmarshallExcluded(bytes, (*_DNSRule)(r), v)
|
err = badjson.UnmarshallExcluded(bytes, (*_DNSRule)(r), v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -111,7 +112,7 @@ type DefaultDNSRule struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DefaultDNSRule) MarshalJSON() ([]byte, error) {
|
func (r *DefaultDNSRule) MarshalJSON() ([]byte, error) {
|
||||||
return MarshallObjects(r.RawDefaultDNSRule, r.DNSRuleAction)
|
return badjson.MarshallObjects(r.RawDefaultDNSRule, r.DNSRuleAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DefaultDNSRule) UnmarshalJSON(data []byte) error {
|
func (r *DefaultDNSRule) UnmarshalJSON(data []byte) error {
|
||||||
|
@ -119,7 +120,7 @@ func (r *DefaultDNSRule) UnmarshalJSON(data []byte) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return UnmarshallExcluded(data, &r.RawDefaultDNSRule, &r.DNSRuleAction)
|
return badjson.UnmarshallExcluded(data, &r.RawDefaultDNSRule, &r.DNSRuleAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DefaultDNSRule) IsValid() bool {
|
func (r *DefaultDNSRule) IsValid() bool {
|
||||||
|
@ -141,7 +142,7 @@ type LogicalDNSRule struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *LogicalDNSRule) MarshalJSON() ([]byte, error) {
|
func (r *LogicalDNSRule) MarshalJSON() ([]byte, error) {
|
||||||
return MarshallObjects(r._LogicalDNSRule, r.DNSRuleAction)
|
return badjson.MarshallObjects(r._LogicalDNSRule, r.DNSRuleAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *LogicalDNSRule) UnmarshalJSON(data []byte) error {
|
func (r *LogicalDNSRule) UnmarshalJSON(data []byte) error {
|
||||||
|
@ -149,7 +150,7 @@ func (r *LogicalDNSRule) UnmarshalJSON(data []byte) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return UnmarshallExcluded(data, &r._LogicalDNSRule, &r.DNSRuleAction)
|
return badjson.UnmarshallExcluded(data, &r._LogicalDNSRule, &r.DNSRuleAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *LogicalDNSRule) IsValid() bool {
|
func (r *LogicalDNSRule) IsValid() bool {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
F "github.com/sagernet/sing/common/format"
|
F "github.com/sagernet/sing/common/format"
|
||||||
"github.com/sagernet/sing/common/json"
|
"github.com/sagernet/sing/common/json"
|
||||||
|
"github.com/sagernet/sing/common/json/badjson"
|
||||||
|
|
||||||
"go4.org/netipx"
|
"go4.org/netipx"
|
||||||
)
|
)
|
||||||
|
@ -37,7 +38,7 @@ func (r RuleSet) MarshalJSON() ([]byte, error) {
|
||||||
default:
|
default:
|
||||||
return nil, E.New("unknown rule-set type: " + r.Type)
|
return nil, E.New("unknown rule-set type: " + r.Type)
|
||||||
}
|
}
|
||||||
return MarshallObjects((_RuleSet)(r), v)
|
return badjson.MarshallObjects((_RuleSet)(r), v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuleSet) UnmarshalJSON(bytes []byte) error {
|
func (r *RuleSet) UnmarshalJSON(bytes []byte) error {
|
||||||
|
@ -71,7 +72,7 @@ func (r *RuleSet) UnmarshalJSON(bytes []byte) error {
|
||||||
} else {
|
} else {
|
||||||
r.Format = ""
|
r.Format = ""
|
||||||
}
|
}
|
||||||
err = UnmarshallExcluded(bytes, (*_RuleSet)(r), v)
|
err = badjson.UnmarshallExcluded(bytes, (*_RuleSet)(r), v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -107,7 +108,7 @@ func (r HeadlessRule) MarshalJSON() ([]byte, error) {
|
||||||
default:
|
default:
|
||||||
return nil, E.New("unknown rule type: " + r.Type)
|
return nil, E.New("unknown rule type: " + r.Type)
|
||||||
}
|
}
|
||||||
return MarshallObjects((_HeadlessRule)(r), v)
|
return badjson.MarshallObjects((_HeadlessRule)(r), v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *HeadlessRule) UnmarshalJSON(bytes []byte) error {
|
func (r *HeadlessRule) UnmarshalJSON(bytes []byte) error {
|
||||||
|
@ -125,7 +126,7 @@ func (r *HeadlessRule) UnmarshalJSON(bytes []byte) error {
|
||||||
default:
|
default:
|
||||||
return E.New("unknown rule type: " + r.Type)
|
return E.New("unknown rule type: " + r.Type)
|
||||||
}
|
}
|
||||||
err = UnmarshallExcluded(bytes, (*_HeadlessRule)(r), v)
|
err = badjson.UnmarshallExcluded(bytes, (*_HeadlessRule)(r), v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -203,7 +204,7 @@ func (r PlainRuleSetCompat) MarshalJSON() ([]byte, error) {
|
||||||
default:
|
default:
|
||||||
return nil, E.New("unknown rule-set version: ", r.Version)
|
return nil, E.New("unknown rule-set version: ", r.Version)
|
||||||
}
|
}
|
||||||
return MarshallObjects((_PlainRuleSetCompat)(r), v)
|
return badjson.MarshallObjects((_PlainRuleSetCompat)(r), v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *PlainRuleSetCompat) UnmarshalJSON(bytes []byte) error {
|
func (r *PlainRuleSetCompat) UnmarshalJSON(bytes []byte) error {
|
||||||
|
@ -220,7 +221,7 @@ func (r *PlainRuleSetCompat) UnmarshalJSON(bytes []byte) error {
|
||||||
default:
|
default:
|
||||||
return E.New("unknown rule-set version: ", r.Version)
|
return E.New("unknown rule-set version: ", r.Version)
|
||||||
}
|
}
|
||||||
err = UnmarshallExcluded(bytes, (*_PlainRuleSetCompat)(r), v)
|
err = badjson.UnmarshallExcluded(bytes, (*_PlainRuleSetCompat)(r), v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ type HTTPMixedInboundOptions struct {
|
||||||
InboundTLSOptionsContainer
|
InboundTLSOptionsContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
type SocksOutboundOptions struct {
|
type SOCKSOutboundOptions struct {
|
||||||
DialerOptions
|
DialerOptions
|
||||||
ServerOptions
|
ServerOptions
|
||||||
Version string `json:"version,omitempty"`
|
Version string `json:"version,omitempty"`
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/json"
|
"github.com/sagernet/sing/common/json"
|
||||||
|
"github.com/sagernet/sing/common/json/badjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
type InboundACMEOptions struct {
|
type InboundACMEOptions struct {
|
||||||
|
@ -45,7 +46,7 @@ func (o ACMEDNS01ChallengeOptions) MarshalJSON() ([]byte, error) {
|
||||||
default:
|
default:
|
||||||
return nil, E.New("unknown provider type: " + o.Provider)
|
return nil, E.New("unknown provider type: " + o.Provider)
|
||||||
}
|
}
|
||||||
return MarshallObjects((_ACMEDNS01ChallengeOptions)(o), v)
|
return badjson.MarshallObjects((_ACMEDNS01ChallengeOptions)(o), v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *ACMEDNS01ChallengeOptions) UnmarshalJSON(bytes []byte) error {
|
func (o *ACMEDNS01ChallengeOptions) UnmarshalJSON(bytes []byte) error {
|
||||||
|
@ -62,7 +63,7 @@ func (o *ACMEDNS01ChallengeOptions) UnmarshalJSON(bytes []byte) error {
|
||||||
default:
|
default:
|
||||||
return E.New("unknown provider type: " + o.Provider)
|
return E.New("unknown provider type: " + o.Provider)
|
||||||
}
|
}
|
||||||
err = UnmarshallExcluded(bytes, (*_ACMEDNS01ChallengeOptions)(o), v)
|
err = badjson.UnmarshallExcluded(bytes, (*_ACMEDNS01ChallengeOptions)(o), v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/json"
|
"github.com/sagernet/sing/common/json"
|
||||||
|
"github.com/sagernet/sing/common/json/badjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
type _V2RayTransportOptions struct {
|
type _V2RayTransportOptions struct {
|
||||||
|
@ -35,7 +36,7 @@ func (o V2RayTransportOptions) MarshalJSON() ([]byte, error) {
|
||||||
default:
|
default:
|
||||||
return nil, E.New("unknown transport type: " + o.Type)
|
return nil, E.New("unknown transport type: " + o.Type)
|
||||||
}
|
}
|
||||||
return MarshallObjects((_V2RayTransportOptions)(o), v)
|
return badjson.MarshallObjects((_V2RayTransportOptions)(o), v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *V2RayTransportOptions) UnmarshalJSON(bytes []byte) error {
|
func (o *V2RayTransportOptions) UnmarshalJSON(bytes []byte) error {
|
||||||
|
@ -58,7 +59,7 @@ func (o *V2RayTransportOptions) UnmarshalJSON(bytes []byte) error {
|
||||||
default:
|
default:
|
||||||
return E.New("unknown transport type: " + o.Type)
|
return E.New("unknown transport type: " + o.Type)
|
||||||
}
|
}
|
||||||
err = UnmarshallExcluded(bytes, (*_V2RayTransportOptions)(o), v)
|
err = badjson.UnmarshallExcluded(bytes, (*_V2RayTransportOptions)(o), v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
package outbound
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
|
||||||
"github.com/sagernet/sing-box/log"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ adapter.Outbound = (*Block)(nil)
|
|
||||||
|
|
||||||
type Block struct {
|
|
||||||
myOutboundAdapter
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewBlock(logger log.ContextLogger, tag string) *Block {
|
|
||||||
return &Block{
|
|
||||||
myOutboundAdapter{
|
|
||||||
protocol: C.TypeBlock,
|
|
||||||
network: []string{N.NetworkTCP, N.NetworkUDP},
|
|
||||||
logger: logger,
|
|
||||||
tag: tag,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Block) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
|
||||||
h.logger.InfoContext(ctx, "blocked connection to ", destination)
|
|
||||||
return nil, io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Block) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
|
||||||
h.logger.InfoContext(ctx, "blocked packet connection to ", destination)
|
|
||||||
return nil, io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func (h *Block) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
|
||||||
conn.Close()
|
|
||||||
h.logger.InfoContext(ctx, "blocked connection to ", metadata.Destination)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func (h *Block) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
|
||||||
conn.Close()
|
|
||||||
h.logger.InfoContext(ctx, "blocked packet connection to ", metadata.Destination)
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
package outbound
|
|
||||||
|
|
||||||
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"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
)
|
|
||||||
|
|
||||||
func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.Outbound) (adapter.Outbound, error) {
|
|
||||||
if tag != "" {
|
|
||||||
ctx = adapter.WithContext(ctx, &adapter.InboundContext{
|
|
||||||
Outbound: tag,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if options.Type == "" {
|
|
||||||
return nil, E.New("missing outbound type")
|
|
||||||
}
|
|
||||||
ctx = ContextWithTag(ctx, tag)
|
|
||||||
switch options.Type {
|
|
||||||
case C.TypeDirect:
|
|
||||||
return NewDirect(router, logger, tag, options.DirectOptions)
|
|
||||||
case C.TypeBlock:
|
|
||||||
return NewBlock(logger, tag), nil
|
|
||||||
case C.TypeDNS:
|
|
||||||
return NewDNS(router, tag), nil
|
|
||||||
case C.TypeSOCKS:
|
|
||||||
return NewSocks(router, logger, tag, options.SocksOptions)
|
|
||||||
case C.TypeHTTP:
|
|
||||||
return NewHTTP(ctx, router, logger, tag, options.HTTPOptions)
|
|
||||||
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.TypeWireGuard:
|
|
||||||
return NewWireGuard(ctx, router, logger, tag, options.WireGuardOptions)
|
|
||||||
case C.TypeHysteria:
|
|
||||||
return NewHysteria(ctx, router, logger, tag, options.HysteriaOptions)
|
|
||||||
case C.TypeTor:
|
|
||||||
return NewTor(ctx, router, logger, tag, options.TorOptions)
|
|
||||||
case C.TypeSSH:
|
|
||||||
return NewSSH(ctx, router, logger, tag, options.SSHOptions)
|
|
||||||
case C.TypeShadowTLS:
|
|
||||||
return NewShadowTLS(ctx, router, logger, tag, options.ShadowTLSOptions)
|
|
||||||
case C.TypeShadowsocksR:
|
|
||||||
return NewShadowsocksR(ctx, router, logger, tag, options.ShadowsocksROptions)
|
|
||||||
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)
|
|
||||||
case C.TypeSelector:
|
|
||||||
return NewSelector(ctx, router, logger, tag, options.SelectorOptions)
|
|
||||||
case C.TypeURLTest:
|
|
||||||
return NewURLTest(ctx, router, logger, tag, options.URLTestOptions)
|
|
||||||
default:
|
|
||||||
return nil, E.New("unknown outbound type: ", options.Type)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
//go:build !with_quic
|
|
||||||
|
|
||||||
package outbound
|
|
||||||
|
|
||||||
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.HysteriaOutboundOptions) (adapter.Outbound, error) {
|
|
||||||
return nil, C.ErrQUICNotIncluded
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHysteria2(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.Hysteria2OutboundOptions) (adapter.Outbound, error) {
|
|
||||||
return nil, C.ErrQUICNotIncluded
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
package outbound
|
|
||||||
|
|
||||||
import "context"
|
|
||||||
|
|
||||||
type outboundTagKey struct{}
|
|
||||||
|
|
||||||
func ContextWithTag(ctx context.Context, outboundTag string) context.Context {
|
|
||||||
return context.WithValue(ctx, outboundTagKey{}, outboundTag)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TagFromContext(ctx context.Context) (string, bool) {
|
|
||||||
value, loaded := ctx.Value(outboundTagKey{}).(string)
|
|
||||||
return value, loaded
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
//go:build with_shadowsocksr
|
|
||||||
|
|
||||||
package outbound
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
"github.com/sagernet/sing-box/log"
|
|
||||||
"github.com/sagernet/sing-box/option"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ int = "ShadowsocksR is deprecated and removed in sing-box 1.6.0"
|
|
||||||
|
|
||||||
func NewShadowsocksR(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksROutboundOptions) (adapter.Outbound, error) {
|
|
||||||
return nil, os.ErrInvalid
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
//go:build !with_shadowsocksr
|
|
||||||
|
|
||||||
package outbound
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
"github.com/sagernet/sing-box/log"
|
|
||||||
"github.com/sagernet/sing-box/option"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewShadowsocksR(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,15 +0,0 @@
|
||||||
//go:build with_embedded_tor && !(android || ios)
|
|
||||||
|
|
||||||
package outbound
|
|
||||||
|
|
||||||
import (
|
|
||||||
"berty.tech/go-libtor"
|
|
||||||
"github.com/cretz/bine/tor"
|
|
||||||
)
|
|
||||||
|
|
||||||
func newConfig() tor.StartConf {
|
|
||||||
return tor.StartConf{
|
|
||||||
ProcessCreator: libtor.Creator,
|
|
||||||
UseEmbeddedControlConn: true,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
//go:build with_embedded_tor && (android || ios)
|
|
||||||
|
|
||||||
package outbound
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/cretz/bine/tor"
|
|
||||||
"github.com/ooni/go-libtor"
|
|
||||||
)
|
|
||||||
|
|
||||||
func newConfig() tor.StartConf {
|
|
||||||
return tor.StartConf{
|
|
||||||
ProcessCreator: libtor.Creator,
|
|
||||||
UseEmbeddedControlConn: true,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
//go:build !with_embedded_tor
|
|
||||||
|
|
||||||
package outbound
|
|
||||||
|
|
||||||
import "github.com/cretz/bine/tor"
|
|
||||||
|
|
||||||
func newConfig() tor.StartConf {
|
|
||||||
return tor.StartConf{}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
//go:build !with_quic
|
|
||||||
|
|
||||||
package outbound
|
|
||||||
|
|
||||||
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.TUICOutboundOptions) (adapter.Outbound, error) {
|
|
||||||
return nil, C.ErrQUICNotIncluded
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
//go:build !with_wireguard
|
|
||||||
|
|
||||||
package outbound
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
"github.com/sagernet/sing-box/log"
|
|
||||||
"github.com/sagernet/sing-box/option"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewWireGuard(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.WireGuardOutboundOptions) (adapter.Outbound, error) {
|
|
||||||
return nil, E.New(`WireGuard is not included in this build, rebuild with -tags with_wireguard`)
|
|
||||||
}
|
|
42
protocol/block/outbound.go
Normal file
42
protocol/block/outbound.go
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
package block
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/adapter/outbound"
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/log"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing/common/logger"
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterOutbound(registry *outbound.Registry) {
|
||||||
|
outbound.Register[option.StubOptions](registry, C.TypeBlock, New)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Outbound struct {
|
||||||
|
outbound.Adapter
|
||||||
|
logger logger.ContextLogger
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, _ option.StubOptions) (adapter.Outbound, error) {
|
||||||
|
return &Outbound{
|
||||||
|
Adapter: outbound.NewAdapter(C.TypeBlock, []string{N.NetworkTCP, N.NetworkUDP}, tag, nil),
|
||||||
|
logger: logger,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
|
h.logger.InfoContext(ctx, "blocked connection to ", destination)
|
||||||
|
return nil, syscall.EPERM
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
|
h.logger.InfoContext(ctx, "blocked packet connection to ", destination)
|
||||||
|
return nil, syscall.EPERM
|
||||||
|
}
|
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, false)
|
||||||
|
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)
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package outbound
|
package direct
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
|
@ -1,4 +1,4 @@
|
||||||
package outbound
|
package direct
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -7,6 +7,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/adapter/outbound"
|
||||||
"github.com/sagernet/sing-box/common/dialer"
|
"github.com/sagernet/sing-box/common/dialer"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
|
@ -14,17 +15,20 @@ import (
|
||||||
"github.com/sagernet/sing-dns"
|
"github.com/sagernet/sing-dns"
|
||||||
"github.com/sagernet/sing/common/bufio"
|
"github.com/sagernet/sing/common/bufio"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/common/logger"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
func RegisterOutbound(registry *outbound.Registry) {
|
||||||
_ adapter.Outbound = (*Direct)(nil)
|
outbound.Register[option.DirectOutboundOptions](registry, C.TypeDirect, NewOutbound)
|
||||||
_ N.ParallelDialer = (*Direct)(nil)
|
}
|
||||||
)
|
|
||||||
|
|
||||||
type Direct struct {
|
var _ N.ParallelDialer = (*Outbound)(nil)
|
||||||
myOutboundAdapter
|
|
||||||
|
type Outbound struct {
|
||||||
|
outbound.Adapter
|
||||||
|
logger logger.ContextLogger
|
||||||
dialer N.Dialer
|
dialer N.Dialer
|
||||||
domainStrategy dns.DomainStrategy
|
domainStrategy dns.DomainStrategy
|
||||||
fallbackDelay time.Duration
|
fallbackDelay time.Duration
|
||||||
|
@ -33,21 +37,15 @@ type Direct struct {
|
||||||
// loopBack *loopBackDetector
|
// loopBack *loopBackDetector
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, options option.DirectOutboundOptions) (*Direct, error) {
|
func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.DirectOutboundOptions) (adapter.Outbound, error) {
|
||||||
options.UDPFragmentDefault = true
|
options.UDPFragmentDefault = true
|
||||||
outboundDialer, err := dialer.New(router, options.DialerOptions)
|
outboundDialer, err := dialer.New(router, options.DialerOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
outbound := &Direct{
|
outbound := &Outbound{
|
||||||
myOutboundAdapter: myOutboundAdapter{
|
Adapter: outbound.NewAdapterWithDialerOptions(C.TypeDirect, []string{N.NetworkTCP, N.NetworkUDP}, tag, options.DialerOptions),
|
||||||
protocol: C.TypeDirect,
|
logger: logger,
|
||||||
network: []string{N.NetworkTCP, N.NetworkUDP},
|
|
||||||
router: router,
|
|
||||||
logger: logger,
|
|
||||||
tag: tag,
|
|
||||||
dependencies: withDialerDependency(options.DialerOptions),
|
|
||||||
},
|
|
||||||
domainStrategy: dns.DomainStrategy(options.DomainStrategy),
|
domainStrategy: dns.DomainStrategy(options.DomainStrategy),
|
||||||
fallbackDelay: time.Duration(options.FallbackDelay),
|
fallbackDelay: time.Duration(options.FallbackDelay),
|
||||||
dialer: outboundDialer,
|
dialer: outboundDialer,
|
||||||
|
@ -69,9 +67,9 @@ func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, opti
|
||||||
return outbound, nil
|
return outbound, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Direct) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
ctx, metadata := adapter.ExtendContext(ctx)
|
ctx, metadata := adapter.ExtendContext(ctx)
|
||||||
metadata.Outbound = h.tag
|
metadata.Outbound = h.Tag()
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
switch h.overrideOption {
|
switch h.overrideOption {
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -98,9 +96,9 @@ func (h *Direct) DialContext(ctx context.Context, network string, destination M.
|
||||||
return h.dialer.DialContext(ctx, network, destination)
|
return h.dialer.DialContext(ctx, network, destination)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Direct) DialParallel(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr) (net.Conn, error) {
|
func (h *Outbound) DialParallel(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr) (net.Conn, error) {
|
||||||
ctx, metadata := adapter.ExtendContext(ctx)
|
ctx, metadata := adapter.ExtendContext(ctx)
|
||||||
metadata.Outbound = h.tag
|
metadata.Outbound = h.Tag()
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
switch h.overrideOption {
|
switch h.overrideOption {
|
||||||
case 1, 2:
|
case 1, 2:
|
||||||
|
@ -125,9 +123,9 @@ func (h *Direct) DialParallel(ctx context.Context, network string, destination M
|
||||||
return N.DialParallel(ctx, h.dialer, network, destination, destinationAddresses, domainStrategy == dns.DomainStrategyPreferIPv6, h.fallbackDelay)
|
return N.DialParallel(ctx, h.dialer, network, destination, destinationAddresses, domainStrategy == dns.DomainStrategyPreferIPv6, h.fallbackDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Direct) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
ctx, metadata := adapter.ExtendContext(ctx)
|
ctx, metadata := adapter.ExtendContext(ctx)
|
||||||
metadata.Outbound = h.tag
|
metadata.Outbound = h.Tag()
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
originDestination := destination
|
originDestination := destination
|
||||||
switch h.overrideOption {
|
switch h.overrideOption {
|
||||||
|
@ -156,14 +154,14 @@ func (h *Direct) ListenPacket(ctx context.Context, destination M.Socksaddr) (net
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*func (h *Direct) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
/*func (h *Outbound) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
if h.loopBack.CheckConn(metadata.Source.AddrPort(), M.AddrPortFromNet(conn.LocalAddr())) {
|
if h.loopBack.CheckConn(metadata.Source.AddrPort(), M.AddrPortFromNet(conn.LocalAddr())) {
|
||||||
return E.New("reject loopback connection to ", metadata.Destination)
|
return E.New("reject loopback connection to ", metadata.Destination)
|
||||||
}
|
}
|
||||||
return NewConnection(ctx, h, conn, metadata)
|
return NewConnection(ctx, h, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Direct) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
func (h *Outbound) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
if h.loopBack.CheckPacketConn(metadata.Source.AddrPort(), M.AddrPortFromNet(conn.LocalAddr())) {
|
if h.loopBack.CheckPacketConn(metadata.Source.AddrPort(), M.AddrPortFromNet(conn.LocalAddr())) {
|
||||||
return E.New("reject loopback packet connection to ", metadata.Destination)
|
return E.New("reject loopback packet connection to ", metadata.Destination)
|
||||||
}
|
}
|
|
@ -1,11 +1,9 @@
|
||||||
package outbound
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
@ -21,44 +19,6 @@ import (
|
||||||
mDNS "github.com/miekg/dns"
|
mDNS "github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ adapter.Outbound = (*DNS)(nil)
|
|
||||||
|
|
||||||
type DNS struct {
|
|
||||||
myOutboundAdapter
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDNS(router adapter.Router, tag string) *DNS {
|
|
||||||
return &DNS{
|
|
||||||
myOutboundAdapter{
|
|
||||||
protocol: C.TypeDNS,
|
|
||||||
network: []string{N.NetworkTCP, N.NetworkUDP},
|
|
||||||
router: router,
|
|
||||||
tag: tag,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DNS) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
|
||||||
return nil, os.ErrInvalid
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DNS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
|
||||||
return nil, os.ErrInvalid
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func (d *DNS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
|
||||||
metadata.Destination = M.Socksaddr{}
|
|
||||||
defer conn.Close()
|
|
||||||
for {
|
|
||||||
conn.SetReadDeadline(time.Now().Add(C.DNSTimeout))
|
|
||||||
err := HandleStreamDNSRequest(ctx, d.router, conn, metadata)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func HandleStreamDNSRequest(ctx context.Context, router adapter.Router, conn net.Conn, metadata adapter.InboundContext) error {
|
func HandleStreamDNSRequest(ctx context.Context, router adapter.Router, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
var queryLength uint16
|
var queryLength uint16
|
||||||
err := binary.Read(conn, binary.BigEndian, &queryLength)
|
err := binary.Read(conn, binary.BigEndian, &queryLength)
|
||||||
|
@ -100,11 +60,6 @@ func HandleStreamDNSRequest(ctx context.Context, router adapter.Router, conn net
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
|
||||||
return NewDNSPacketConnection(ctx, d.router, conn, nil, metadata)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDNSPacketConnection(ctx context.Context, router adapter.Router, conn N.PacketConn, cachedPackets []*N.PacketBuffer, metadata adapter.InboundContext) error {
|
func NewDNSPacketConnection(ctx context.Context, router adapter.Router, conn N.PacketConn, cachedPackets []*N.PacketBuffer, metadata adapter.InboundContext) error {
|
||||||
metadata.Destination = M.Socksaddr{}
|
metadata.Destination = M.Socksaddr{}
|
||||||
var reader N.PacketReader = conn
|
var reader N.PacketReader = conn
|
61
protocol/dns/outbound.go
Normal file
61
protocol/dns/outbound.go
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/adapter/outbound"
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/log"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing/common/logger"
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterOutbound(registry *outbound.Registry) {
|
||||||
|
outbound.Register[option.StubOptions](registry, C.TypeDNS, NewOutbound)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Outbound struct {
|
||||||
|
outbound.Adapter
|
||||||
|
router adapter.Router
|
||||||
|
logger logger.ContextLogger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.StubOptions) (adapter.Outbound, error) {
|
||||||
|
return &Outbound{
|
||||||
|
Adapter: outbound.NewAdapter(C.TypeDNS, []string{N.NetworkTCP, N.NetworkUDP}, tag, nil),
|
||||||
|
router: router,
|
||||||
|
logger: logger,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
|
return nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
|
return nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated
|
||||||
|
func (d *Outbound) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
|
metadata.Destination = M.Socksaddr{}
|
||||||
|
defer conn.Close()
|
||||||
|
for {
|
||||||
|
conn.SetReadDeadline(time.Now().Add(C.DNSTimeout))
|
||||||
|
err := HandleStreamDNSRequest(ctx, d.router, conn, metadata)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated
|
||||||
|
func (d *Outbound) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
|
return NewDNSPacketConnection(ctx, d.router, conn, nil, metadata)
|
||||||
|
}
|
|
@ -1,28 +1,33 @@
|
||||||
package outbound
|
package group
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/adapter/outbound"
|
||||||
"github.com/sagernet/sing-box/common/interrupt"
|
"github.com/sagernet/sing-box/common/interrupt"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/common/logger"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/service"
|
"github.com/sagernet/sing/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
func RegisterSelector(registry *outbound.Registry) {
|
||||||
_ adapter.Outbound = (*Selector)(nil)
|
outbound.Register[option.SelectorOutboundOptions](registry, C.TypeSelector, NewSelector)
|
||||||
_ adapter.OutboundGroup = (*Selector)(nil)
|
}
|
||||||
)
|
|
||||||
|
var _ adapter.OutboundGroup = (*Selector)(nil)
|
||||||
|
|
||||||
type Selector struct {
|
type Selector struct {
|
||||||
myOutboundAdapter
|
outbound.Adapter
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
router adapter.Router
|
||||||
|
logger logger.ContextLogger
|
||||||
tags []string
|
tags []string
|
||||||
defaultTag string
|
defaultTag string
|
||||||
outbounds map[string]adapter.Outbound
|
outbounds map[string]adapter.Outbound
|
||||||
|
@ -31,16 +36,12 @@ type Selector struct {
|
||||||
interruptExternalConnections bool
|
interruptExternalConnections bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSelector(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SelectorOutboundOptions) (*Selector, error) {
|
func NewSelector(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SelectorOutboundOptions) (adapter.Outbound, error) {
|
||||||
outbound := &Selector{
|
outbound := &Selector{
|
||||||
myOutboundAdapter: myOutboundAdapter{
|
Adapter: outbound.NewAdapter(C.TypeSelector, nil, tag, options.Outbounds),
|
||||||
protocol: C.TypeSelector,
|
|
||||||
router: router,
|
|
||||||
logger: logger,
|
|
||||||
tag: tag,
|
|
||||||
dependencies: options.Outbounds,
|
|
||||||
},
|
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
|
router: router,
|
||||||
|
logger: logger,
|
||||||
tags: options.Outbounds,
|
tags: options.Outbounds,
|
||||||
defaultTag: options.Default,
|
defaultTag: options.Default,
|
||||||
outbounds: make(map[string]adapter.Outbound),
|
outbounds: make(map[string]adapter.Outbound),
|
||||||
|
@ -69,10 +70,10 @@ func (s *Selector) Start() error {
|
||||||
s.outbounds[tag] = detour
|
s.outbounds[tag] = detour
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.tag != "" {
|
if s.Tag() != "" {
|
||||||
cacheFile := service.FromContext[adapter.CacheFile](s.ctx)
|
cacheFile := service.FromContext[adapter.CacheFile](s.ctx)
|
||||||
if cacheFile != nil {
|
if cacheFile != nil {
|
||||||
selected := cacheFile.LoadSelected(s.tag)
|
selected := cacheFile.LoadSelected(s.Tag())
|
||||||
if selected != "" {
|
if selected != "" {
|
||||||
detour, loaded := s.outbounds[selected]
|
detour, loaded := s.outbounds[selected]
|
||||||
if loaded {
|
if loaded {
|
||||||
|
@ -117,10 +118,10 @@ func (s *Selector) SelectOutbound(tag string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
s.selected = detour
|
s.selected = detour
|
||||||
if s.tag != "" {
|
if s.Tag() != "" {
|
||||||
cacheFile := service.FromContext[adapter.CacheFile](s.ctx)
|
cacheFile := service.FromContext[adapter.CacheFile](s.ctx)
|
||||||
if cacheFile != nil {
|
if cacheFile != nil {
|
||||||
err := cacheFile.StoreSelected(s.tag, tag)
|
err := cacheFile.StoreSelected(s.Tag(), tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("store selected: ", err)
|
s.logger.Error("store selected: ", err)
|
||||||
}
|
}
|
||||||
|
@ -153,7 +154,7 @@ func (s *Selector) NewConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||||
if legacyHandler, ok := s.selected.(adapter.ConnectionHandler); ok {
|
if legacyHandler, ok := s.selected.(adapter.ConnectionHandler); ok {
|
||||||
return legacyHandler.NewConnection(ctx, conn, metadata)
|
return legacyHandler.NewConnection(ctx, conn, metadata)
|
||||||
} else {
|
} else {
|
||||||
return NewConnection(ctx, s.selected, conn, metadata)
|
return outbound.NewConnection(ctx, s.selected, conn, metadata)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +165,7 @@ func (s *Selector) NewPacketConnection(ctx context.Context, conn N.PacketConn, m
|
||||||
if legacyHandler, ok := s.selected.(adapter.PacketConnectionHandler); ok {
|
if legacyHandler, ok := s.selected.(adapter.PacketConnectionHandler); ok {
|
||||||
return legacyHandler.NewPacketConnection(ctx, conn, metadata)
|
return legacyHandler.NewPacketConnection(ctx, conn, metadata)
|
||||||
} else {
|
} else {
|
||||||
return NewPacketConnection(ctx, s.selected, conn, metadata)
|
return outbound.NewPacketConnection(ctx, s.selected, conn, metadata)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package outbound
|
package group
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -7,6 +7,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/adapter/outbound"
|
||||||
"github.com/sagernet/sing-box/common/interrupt"
|
"github.com/sagernet/sing-box/common/interrupt"
|
||||||
"github.com/sagernet/sing-box/common/urltest"
|
"github.com/sagernet/sing-box/common/urltest"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
@ -22,15 +23,20 @@ import (
|
||||||
"github.com/sagernet/sing/service/pause"
|
"github.com/sagernet/sing/service/pause"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func RegisterURLTest(registry *outbound.Registry) {
|
||||||
|
outbound.Register[option.URLTestOutboundOptions](registry, C.TypeURLTest, NewURLTest)
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_ adapter.Outbound = (*URLTest)(nil)
|
|
||||||
_ adapter.OutboundGroup = (*URLTest)(nil)
|
_ adapter.OutboundGroup = (*URLTest)(nil)
|
||||||
_ adapter.InterfaceUpdateListener = (*URLTest)(nil)
|
_ adapter.InterfaceUpdateListener = (*URLTest)(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
type URLTest struct {
|
type URLTest struct {
|
||||||
myOutboundAdapter
|
outbound.Adapter
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
router adapter.Router
|
||||||
|
logger log.ContextLogger
|
||||||
tags []string
|
tags []string
|
||||||
link string
|
link string
|
||||||
interval time.Duration
|
interval time.Duration
|
||||||
|
@ -40,17 +46,12 @@ type URLTest struct {
|
||||||
interruptExternalConnections bool
|
interruptExternalConnections bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewURLTest(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.URLTestOutboundOptions) (*URLTest, error) {
|
func NewURLTest(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.URLTestOutboundOptions) (adapter.Outbound, error) {
|
||||||
outbound := &URLTest{
|
outbound := &URLTest{
|
||||||
myOutboundAdapter: myOutboundAdapter{
|
Adapter: outbound.NewAdapter(C.TypeURLTest, []string{N.NetworkTCP, N.NetworkUDP}, tag, options.Outbounds),
|
||||||
protocol: C.TypeURLTest,
|
|
||||||
network: []string{N.NetworkTCP, N.NetworkUDP},
|
|
||||||
router: router,
|
|
||||||
logger: logger,
|
|
||||||
tag: tag,
|
|
||||||
dependencies: options.Outbounds,
|
|
||||||
},
|
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
|
router: router,
|
||||||
|
logger: logger,
|
||||||
tags: options.Outbounds,
|
tags: options.Outbounds,
|
||||||
link: options.URL,
|
link: options.URL,
|
||||||
interval: time.Duration(options.Interval),
|
interval: time.Duration(options.Interval),
|
||||||
|
@ -171,14 +172,14 @@ func (s *URLTest) ListenPacket(ctx context.Context, destination M.Socksaddr) (ne
|
||||||
// Deprecated
|
// Deprecated
|
||||||
func (s *URLTest) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (s *URLTest) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
||||||
return NewConnection(ctx, s, conn, metadata)
|
return outbound.NewConnection(ctx, s, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
// Deprecated
|
// Deprecated
|
||||||
func (s *URLTest) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
func (s *URLTest) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
||||||
return NewPacketConnection(ctx, s, conn, metadata)
|
return outbound.NewPacketConnection(ctx, s, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *URLTest) InterfaceUpdated() {
|
func (s *URLTest) InterfaceUpdated() {
|
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,4 +1,4 @@
|
||||||
package outbound
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -6,25 +6,30 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/adapter/outbound"
|
||||||
"github.com/sagernet/sing-box/common/dialer"
|
"github.com/sagernet/sing-box/common/dialer"
|
||||||
"github.com/sagernet/sing-box/common/tls"
|
"github.com/sagernet/sing-box/common/tls"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
|
"github.com/sagernet/sing/common/logger"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
sHTTP "github.com/sagernet/sing/protocol/http"
|
sHTTP "github.com/sagernet/sing/protocol/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ adapter.Outbound = (*HTTP)(nil)
|
func RegisterOutbound(registry *outbound.Registry) {
|
||||||
|
outbound.Register[option.HTTPOutboundOptions](registry, C.TypeHTTP, NewOutbound)
|
||||||
|
}
|
||||||
|
|
||||||
type HTTP struct {
|
type Outbound struct {
|
||||||
myOutboundAdapter
|
outbound.Adapter
|
||||||
|
logger logger.ContextLogger
|
||||||
client *sHTTP.Client
|
client *sHTTP.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPOutboundOptions) (*HTTP, error) {
|
func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPOutboundOptions) (adapter.Outbound, error) {
|
||||||
outboundDialer, err := dialer.New(router, options.DialerOptions)
|
outboundDialer, err := dialer.New(router, options.DialerOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -33,16 +38,10 @@ func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogge
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &HTTP{
|
return &Outbound{
|
||||||
myOutboundAdapter{
|
Adapter: outbound.NewAdapterWithDialerOptions(C.TypeHTTP, []string{N.NetworkTCP}, tag, options.DialerOptions),
|
||||||
protocol: C.TypeHTTP,
|
logger: logger,
|
||||||
network: []string{N.NetworkTCP},
|
client: sHTTP.NewClient(sHTTP.Options{
|
||||||
router: router,
|
|
||||||
logger: logger,
|
|
||||||
tag: tag,
|
|
||||||
dependencies: withDialerDependency(options.DialerOptions),
|
|
||||||
},
|
|
||||||
sHTTP.NewClient(sHTTP.Options{
|
|
||||||
Dialer: detour,
|
Dialer: detour,
|
||||||
Server: options.ServerOptions.Build(),
|
Server: options.ServerOptions.Build(),
|
||||||
Username: options.Username,
|
Username: options.Username,
|
||||||
|
@ -53,14 +52,14 @@ func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogge
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HTTP) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
ctx, metadata := adapter.ExtendContext(ctx)
|
ctx, metadata := adapter.ExtendContext(ctx)
|
||||||
metadata.Outbound = h.tag
|
metadata.Outbound = h.Tag()
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
||||||
return h.client.DialContext(ctx, network, destination)
|
return h.client.DialContext(ctx, network, destination)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HTTP) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
return nil, os.ErrInvalid
|
return nil, os.ErrInvalid
|
||||||
}
|
}
|
|
@ -1,6 +1,4 @@
|
||||||
//go:build with_quic
|
package hysteria
|
||||||
|
|
||||||
package inbound
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -8,7 +6,9 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"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/humanize"
|
||||||
|
"github.com/sagernet/sing-box/common/listener"
|
||||||
"github.com/sagernet/sing-box/common/tls"
|
"github.com/sagernet/sing-box/common/tls"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
|
@ -20,16 +20,21 @@ import (
|
||||||
N "github.com/sagernet/sing/common/network"
|
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 {
|
type Inbound struct {
|
||||||
myInboundAdapter
|
inbound.Adapter
|
||||||
|
router adapter.Router
|
||||||
|
logger log.ContextLogger
|
||||||
|
listener *listener.Listener
|
||||||
tlsConfig tls.ServerConfig
|
tlsConfig tls.ServerConfig
|
||||||
service *hysteria.Service[int]
|
service *hysteria.Service[int]
|
||||||
userNameList []string
|
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
|
options.UDPFragmentDefault = true
|
||||||
if options.TLS == nil || !options.TLS.Enabled {
|
if options.TLS == nil || !options.TLS.Enabled {
|
||||||
return nil, C.ErrTLSRequired
|
return nil, C.ErrTLSRequired
|
||||||
|
@ -38,16 +43,15 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
inbound := &Hysteria{
|
inbound := &Inbound{
|
||||||
myInboundAdapter: myInboundAdapter{
|
Adapter: inbound.NewAdapter(C.TypeHysteria, tag),
|
||||||
protocol: C.TypeHysteria,
|
router: router,
|
||||||
network: []string{N.NetworkUDP},
|
logger: logger,
|
||||||
ctx: ctx,
|
listener: listener.New(listener.Options{
|
||||||
router: router,
|
Context: ctx,
|
||||||
logger: logger,
|
Logger: logger,
|
||||||
tag: tag,
|
Listen: options.ListenOptions,
|
||||||
listenOptions: options.ListenOptions,
|
}),
|
||||||
},
|
|
||||||
tlsConfig: tlsConfig,
|
tlsConfig: tlsConfig,
|
||||||
}
|
}
|
||||||
var sendBps, receiveBps uint64
|
var sendBps, receiveBps uint64
|
||||||
|
@ -113,9 +117,12 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||||
return inbound, nil
|
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)
|
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)
|
h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||||
userID, _ := auth.UserFromContext[int](ctx)
|
userID, _ := auth.UserFromContext[int](ctx)
|
||||||
if userName := h.userNameList[userID]; userName != "" {
|
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)
|
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)
|
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)
|
h.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
|
||||||
userID, _ := auth.UserFromContext[int](ctx)
|
userID, _ := auth.UserFromContext[int](ctx)
|
||||||
if userName := h.userNameList[userID]; userName != "" {
|
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)
|
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hysteria) Start() error {
|
func (h *Inbound) Start() error {
|
||||||
if h.tlsConfig != nil {
|
if h.tlsConfig != nil {
|
||||||
err := h.tlsConfig.Start()
|
err := h.tlsConfig.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
packetConn, err := h.myInboundAdapter.ListenUDP()
|
packetConn, err := h.listener.ListenUDP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return h.service.Start(packetConn)
|
return h.service.Start(packetConn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hysteria) Close() error {
|
func (h *Inbound) Close() error {
|
||||||
return common.Close(
|
return common.Close(
|
||||||
&h.myInboundAdapter,
|
&h.listener,
|
||||||
h.tlsConfig,
|
h.tlsConfig,
|
||||||
common.PtrOrNil(h.service),
|
common.PtrOrNil(h.service),
|
||||||
)
|
)
|
|
@ -1,6 +1,4 @@
|
||||||
//go:build with_quic
|
package hysteria
|
||||||
|
|
||||||
package outbound
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -8,31 +6,39 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/adapter/outbound"
|
||||||
"github.com/sagernet/sing-box/common/dialer"
|
"github.com/sagernet/sing-box/common/dialer"
|
||||||
"github.com/sagernet/sing-box/common/humanize"
|
"github.com/sagernet/sing-box/common/humanize"
|
||||||
"github.com/sagernet/sing-box/common/tls"
|
"github.com/sagernet/sing-box/common/tls"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing-box/protocol/tuic"
|
||||||
"github.com/sagernet/sing-quic/hysteria"
|
"github.com/sagernet/sing-quic/hysteria"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/bufio"
|
"github.com/sagernet/sing/common/bufio"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/common/logger"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func RegisterOutbound(registry *outbound.Registry) {
|
||||||
|
outbound.Register[option.HysteriaOutboundOptions](registry, C.TypeHysteria, NewOutbound)
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_ adapter.Outbound = (*TUIC)(nil)
|
_ adapter.Outbound = (*tuic.Outbound)(nil)
|
||||||
_ adapter.InterfaceUpdateListener = (*TUIC)(nil)
|
_ adapter.InterfaceUpdateListener = (*tuic.Outbound)(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
type Hysteria struct {
|
type Outbound struct {
|
||||||
myOutboundAdapter
|
outbound.Adapter
|
||||||
|
logger logger.ContextLogger
|
||||||
client *hysteria.Client
|
client *hysteria.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HysteriaOutboundOptions) (*Hysteria, error) {
|
func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HysteriaOutboundOptions) (adapter.Outbound, error) {
|
||||||
options.UDPFragmentDefault = true
|
options.UDPFragmentDefault = true
|
||||||
if options.TLS == nil || !options.TLS.Enabled {
|
if options.TLS == nil || !options.TLS.Enabled {
|
||||||
return nil, C.ErrTLSRequired
|
return nil, C.ErrTLSRequired
|
||||||
|
@ -88,20 +94,14 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &Hysteria{
|
return &Outbound{
|
||||||
myOutboundAdapter: myOutboundAdapter{
|
Adapter: outbound.NewAdapterWithDialerOptions(C.TypeHysteria, networkList, tag, options.DialerOptions),
|
||||||
protocol: C.TypeHysteria,
|
logger: logger,
|
||||||
network: networkList,
|
client: client,
|
||||||
router: router,
|
|
||||||
logger: logger,
|
|
||||||
tag: tag,
|
|
||||||
dependencies: withDialerDependency(options.DialerOptions),
|
|
||||||
},
|
|
||||||
client: client,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hysteria) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
switch N.NetworkName(network) {
|
switch N.NetworkName(network) {
|
||||||
case N.NetworkTCP:
|
case N.NetworkTCP:
|
||||||
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
||||||
|
@ -117,15 +117,15 @@ func (h *Hysteria) DialContext(ctx context.Context, network string, destination
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hysteria) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||||
return h.client.ListenPacket(ctx, destination)
|
return h.client.ListenPacket(ctx, destination)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hysteria) InterfaceUpdated() {
|
func (h *Outbound) InterfaceUpdated() {
|
||||||
h.client.CloseWithError(E.New("network changed"))
|
h.client.CloseWithError(E.New("network changed"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hysteria) Close() error {
|
func (h *Outbound) Close() error {
|
||||||
return h.client.CloseWithError(os.ErrClosed)
|
return h.client.CloseWithError(os.ErrClosed)
|
||||||
}
|
}
|
|
@ -1,6 +1,4 @@
|
||||||
//go:build with_quic
|
package hysteria2
|
||||||
|
|
||||||
package inbound
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -11,6 +9,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"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/tls"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
|
@ -23,16 +23,21 @@ import (
|
||||||
N "github.com/sagernet/sing/common/network"
|
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 {
|
type Inbound struct {
|
||||||
myInboundAdapter
|
inbound.Adapter
|
||||||
|
router adapter.Router
|
||||||
|
logger log.ContextLogger
|
||||||
|
listener *listener.Listener
|
||||||
tlsConfig tls.ServerConfig
|
tlsConfig tls.ServerConfig
|
||||||
service *hysteria2.Service[int]
|
service *hysteria2.Service[int]
|
||||||
userNameList []string
|
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
|
options.UDPFragmentDefault = true
|
||||||
if options.TLS == nil || !options.TLS.Enabled {
|
if options.TLS == nil || !options.TLS.Enabled {
|
||||||
return nil, C.ErrTLSRequired
|
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)
|
return nil, E.New("unknown masquerade URL scheme: ", masqueradeURL.Scheme)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inbound := &Hysteria2{
|
inbound := &Inbound{
|
||||||
myInboundAdapter: myInboundAdapter{
|
Adapter: inbound.NewAdapter(C.TypeHysteria2, tag),
|
||||||
protocol: C.TypeHysteria2,
|
router: router,
|
||||||
network: []string{N.NetworkUDP},
|
logger: logger,
|
||||||
ctx: ctx,
|
listener: listener.New(listener.Options{
|
||||||
router: router,
|
Context: ctx,
|
||||||
logger: logger,
|
Logger: logger,
|
||||||
tag: tag,
|
Listen: options.ListenOptions,
|
||||||
listenOptions: options.ListenOptions,
|
}),
|
||||||
},
|
|
||||||
tlsConfig: tlsConfig,
|
tlsConfig: tlsConfig,
|
||||||
}
|
}
|
||||||
var udpTimeout time.Duration
|
var udpTimeout time.Duration
|
||||||
|
@ -124,9 +128,12 @@ func NewHysteria2(ctx context.Context, router adapter.Router, logger log.Context
|
||||||
return inbound, nil
|
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)
|
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)
|
h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||||
userID, _ := auth.UserFromContext[int](ctx)
|
userID, _ := auth.UserFromContext[int](ctx)
|
||||||
if userName := h.userNameList[userID]; userName != "" {
|
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)
|
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)
|
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)
|
h.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
|
||||||
userID, _ := auth.UserFromContext[int](ctx)
|
userID, _ := auth.UserFromContext[int](ctx)
|
||||||
if userName := h.userNameList[userID]; userName != "" {
|
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)
|
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hysteria2) Start() error {
|
func (h *Inbound) Start() error {
|
||||||
if h.tlsConfig != nil {
|
if h.tlsConfig != nil {
|
||||||
err := h.tlsConfig.Start()
|
err := h.tlsConfig.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
packetConn, err := h.myInboundAdapter.ListenUDP()
|
packetConn, err := h.listener.ListenUDP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return h.service.Start(packetConn)
|
return h.service.Start(packetConn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hysteria2) Close() error {
|
func (h *Inbound) Close() error {
|
||||||
return common.Close(
|
return common.Close(
|
||||||
&h.myInboundAdapter,
|
&h.listener,
|
||||||
h.tlsConfig,
|
h.tlsConfig,
|
||||||
common.PtrOrNil(h.service),
|
common.PtrOrNil(h.service),
|
||||||
)
|
)
|
|
@ -1,6 +1,4 @@
|
||||||
//go:build with_quic
|
package hysteria2
|
||||||
|
|
||||||
package outbound
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -8,31 +6,39 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/adapter/outbound"
|
||||||
"github.com/sagernet/sing-box/common/dialer"
|
"github.com/sagernet/sing-box/common/dialer"
|
||||||
"github.com/sagernet/sing-box/common/tls"
|
"github.com/sagernet/sing-box/common/tls"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing-box/protocol/tuic"
|
||||||
"github.com/sagernet/sing-quic/hysteria"
|
"github.com/sagernet/sing-quic/hysteria"
|
||||||
"github.com/sagernet/sing-quic/hysteria2"
|
"github.com/sagernet/sing-quic/hysteria2"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/bufio"
|
"github.com/sagernet/sing/common/bufio"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/common/logger"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func RegisterOutbound(registry *outbound.Registry) {
|
||||||
|
outbound.Register[option.Hysteria2OutboundOptions](registry, C.TypeHysteria2, NewOutbound)
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_ adapter.Outbound = (*TUIC)(nil)
|
_ adapter.Outbound = (*tuic.Outbound)(nil)
|
||||||
_ adapter.InterfaceUpdateListener = (*TUIC)(nil)
|
_ adapter.InterfaceUpdateListener = (*tuic.Outbound)(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
type Hysteria2 struct {
|
type Outbound struct {
|
||||||
myOutboundAdapter
|
outbound.Adapter
|
||||||
|
logger logger.ContextLogger
|
||||||
client *hysteria2.Client
|
client *hysteria2.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHysteria2(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.Hysteria2OutboundOptions) (*Hysteria2, error) {
|
func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.Hysteria2OutboundOptions) (adapter.Outbound, error) {
|
||||||
options.UDPFragmentDefault = true
|
options.UDPFragmentDefault = true
|
||||||
if options.TLS == nil || !options.TLS.Enabled {
|
if options.TLS == nil || !options.TLS.Enabled {
|
||||||
return nil, C.ErrTLSRequired
|
return nil, C.ErrTLSRequired
|
||||||
|
@ -74,20 +80,14 @@ func NewHysteria2(ctx context.Context, router adapter.Router, logger log.Context
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &Hysteria2{
|
return &Outbound{
|
||||||
myOutboundAdapter: myOutboundAdapter{
|
Adapter: outbound.NewAdapterWithDialerOptions(C.TypeHysteria2, networkList, tag, options.DialerOptions),
|
||||||
protocol: C.TypeHysteria2,
|
logger: logger,
|
||||||
network: networkList,
|
client: client,
|
||||||
router: router,
|
|
||||||
logger: logger,
|
|
||||||
tag: tag,
|
|
||||||
dependencies: withDialerDependency(options.DialerOptions),
|
|
||||||
},
|
|
||||||
client: client,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hysteria2) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
switch N.NetworkName(network) {
|
switch N.NetworkName(network) {
|
||||||
case N.NetworkTCP:
|
case N.NetworkTCP:
|
||||||
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
||||||
|
@ -103,15 +103,15 @@ func (h *Hysteria2) DialContext(ctx context.Context, network string, destination
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hysteria2) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||||
return h.client.ListenPacket(ctx)
|
return h.client.ListenPacket(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hysteria2) InterfaceUpdated() {
|
func (h *Outbound) InterfaceUpdated() {
|
||||||
h.client.CloseWithError(E.New("network changed"))
|
h.client.CloseWithError(E.New("network changed"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hysteria2) Close() error {
|
func (h *Outbound) Close() error {
|
||||||
return h.client.CloseWithError(os.ErrClosed)
|
return h.client.CloseWithError(os.ErrClosed)
|
||||||
}
|
}
|
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)
|
||||||
|
}
|
248
protocol/naive/inbound.go
Normal file
248
protocol/naive/inbound.go
Normal file
|
@ -0,0 +1,248 @@
|
||||||
|
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),
|
||||||
|
logger: 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 err == nil {
|
||||||
|
n.h3Server = http3Server
|
||||||
|
} else if len(n.network) > 1 {
|
||||||
|
n.logger.Warn(E.Cause(err, "naive http3 disabled"))
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io"
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
@ -11,228 +10,12 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"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"
|
||||||
"github.com/sagernet/sing/common/auth"
|
|
||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
"github.com/sagernet/sing/common/rw"
|
"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
|
const kFirstPaddings = 8
|
||||||
|
|
||||||
type naiveH1Conn struct {
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -8,6 +8,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"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"
|
"github.com/sagernet/sing-box/common/redir"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
|
@ -21,22 +23,25 @@ import (
|
||||||
"github.com/sagernet/sing/common/udpnat2"
|
"github.com/sagernet/sing/common/udpnat2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TProxy struct {
|
func RegisterTProxy(registry *inbound.Registry) {
|
||||||
myInboundAdapter
|
inbound.Register[option.TProxyInboundOptions](registry, C.TypeTProxy, NewTProxy)
|
||||||
udpNat *udpnat.Service
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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{
|
tproxy := &TProxy{
|
||||||
myInboundAdapter: myInboundAdapter{
|
Adapter: inbound.NewAdapter(C.TypeTProxy, tag),
|
||||||
protocol: C.TypeTProxy,
|
ctx: ctx,
|
||||||
network: options.Network.Build(),
|
router: router,
|
||||||
ctx: ctx,
|
logger: logger,
|
||||||
router: router,
|
|
||||||
logger: logger,
|
|
||||||
tag: tag,
|
|
||||||
listenOptions: options.ListenOptions,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
var udpTimeout time.Duration
|
var udpTimeout time.Duration
|
||||||
if options.UDPTimeout != 0 {
|
if options.UDPTimeout != 0 {
|
||||||
|
@ -44,28 +49,34 @@ func NewTProxy(ctx context.Context, router adapter.Router, logger log.ContextLog
|
||||||
} else {
|
} else {
|
||||||
udpTimeout = C.UDPTimeout
|
udpTimeout = C.UDPTimeout
|
||||||
}
|
}
|
||||||
tproxy.connHandler = tproxy
|
|
||||||
tproxy.oobPacketHandler = tproxy
|
|
||||||
tproxy.udpNat = udpnat.New(tproxy, tproxy.preparePacketConnection, udpTimeout, false)
|
tproxy.udpNat = udpnat.New(tproxy, tproxy.preparePacketConnection, udpTimeout, false)
|
||||||
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 {
|
func (t *TProxy) Start() error {
|
||||||
err := t.myInboundAdapter.Start()
|
err := t.listener.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if t.tcpListener != nil {
|
if listener := t.listener.TCPListener(); listener != nil {
|
||||||
err = control.Conn(common.MustCast[syscall.Conn](t.tcpListener), func(fd uintptr) error {
|
err = control.Conn(common.MustCast[syscall.Conn](listener), func(fd uintptr) error {
|
||||||
return redir.TProxy(fd, M.SocksaddrFromNet(t.tcpListener.Addr()).Addr.Is6())
|
return redir.TProxy(fd, M.SocksaddrFromNet(listener.Addr()).Addr.Is6())
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "configure tproxy TCP listener")
|
return E.Cause(err, "configure tproxy TCP listener")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if t.udpConn != nil {
|
if conn := t.listener.UDPConn(); conn != nil {
|
||||||
err = control.Conn(t.udpConn, func(fd uintptr) error {
|
err = control.Conn(conn, func(fd uintptr) error {
|
||||||
return redir.TProxy(fd, M.SocksaddrFromNet(t.udpConn.LocalAddr()).Addr.Is6())
|
return redir.TProxy(fd, M.SocksaddrFromNet(conn.LocalAddr()).Addr.Is6())
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "configure tproxy UDP listener")
|
return E.Cause(err, "configure tproxy UDP listener")
|
||||||
|
@ -74,13 +85,26 @@ func (t *TProxy) Start() error {
|
||||||
return nil
|
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) {
|
func (t *TProxy) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
|
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) {
|
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) {
|
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) {
|
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}
|
ctx := log.ContextWithNewID(t.ctx)
|
||||||
return true, t.ctx, writer, func(it error) {
|
writer := &tproxyPacketWriter{ctx: ctx, source: source.AddrPort(), destination: destination}
|
||||||
|
return true, ctx, writer, func(it error) {
|
||||||
common.Close(common.PtrOrNil(writer.conn))
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -7,6 +7,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"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/mux"
|
||||||
"github.com/sagernet/sing-box/common/uot"
|
"github.com/sagernet/sing-box/common/uot"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
@ -20,36 +22,31 @@ import (
|
||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
F "github.com/sagernet/sing/common/format"
|
F "github.com/sagernet/sing/common/format"
|
||||||
|
"github.com/sagernet/sing/common/logger"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/common/ntp"
|
"github.com/sagernet/sing/common/ntp"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var _ adapter.TCPInjectableInbound = (*MultiInbound)(nil)
|
||||||
_ adapter.Inbound = (*ShadowsocksMulti)(nil)
|
|
||||||
_ adapter.TCPInjectableInbound = (*ShadowsocksMulti)(nil)
|
|
||||||
)
|
|
||||||
|
|
||||||
type ShadowsocksMulti struct {
|
type MultiInbound struct {
|
||||||
myInboundAdapter
|
inbound.Adapter
|
||||||
service shadowsocks.MultiService[int]
|
ctx context.Context
|
||||||
users []option.ShadowsocksUser
|
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) {
|
func newMultiInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksInboundOptions) (*MultiInbound, error) {
|
||||||
inbound := &ShadowsocksMulti{
|
inbound := &MultiInbound{
|
||||||
myInboundAdapter: myInboundAdapter{
|
Adapter: inbound.NewAdapter(C.TypeShadowsocks, tag),
|
||||||
protocol: C.TypeShadowsocks,
|
ctx: ctx,
|
||||||
network: options.Network.Build(),
|
router: uot.NewRouter(router, logger),
|
||||||
ctx: ctx,
|
logger: logger,
|
||||||
router: uot.NewRouter(router, logger),
|
|
||||||
logger: logger,
|
|
||||||
tag: tag,
|
|
||||||
listenOptions: options.ListenOptions,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
inbound.connHandler = inbound
|
|
||||||
inbound.packetHandler = inbound
|
|
||||||
var err error
|
var err error
|
||||||
inbound.router, err = mux.NewRouterWithOptions(inbound.router, logger, common.PtrValueOrDefault(options.Multiplex))
|
inbound.router, err = mux.NewRouterWithOptions(inbound.router, logger, common.PtrValueOrDefault(options.Multiplex))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -91,12 +88,28 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
inbound.service = service
|
inbound.service = service
|
||||||
inbound.packetUpstream = service
|
|
||||||
inbound.users = options.Users
|
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
|
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))
|
err := h.service.NewConnection(ctx, conn, adapter.UpstreamMetadata(metadata))
|
||||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||||
if err != nil {
|
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) {
|
func (h *MultiInbound) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
|
||||||
err := h.service.NewPacket(h.ctx, h.packetConn(), buffer, M.Metadata{Source: source})
|
err := h.service.NewPacket(h.ctx, &stubPacketConn{h.listener.PacketWriter()}, buffer, M.Metadata{Source: source})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.logger.Error(E.Cause(err, "process packet from ", source))
|
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)
|
userIndex, loaded := auth.UserFromContext[int](ctx)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
return os.ErrInvalid
|
return os.ErrInvalid
|
||||||
|
@ -123,10 +136,12 @@ func (h *ShadowsocksMulti) newConnection(ctx context.Context, conn net.Conn, met
|
||||||
metadata.User = user
|
metadata.User = user
|
||||||
}
|
}
|
||||||
h.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination)
|
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)
|
userIndex, loaded := auth.UserFromContext[int](ctx)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
return os.ErrInvalid
|
return os.ErrInvalid
|
||||||
|
@ -140,5 +155,13 @@ func (h *ShadowsocksMulti) newPacketConnection(ctx context.Context, conn N.Packe
|
||||||
ctx = log.ContextWithNewID(ctx)
|
ctx = log.ContextWithNewID(ctx)
|
||||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection from ", metadata.Source)
|
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection from ", metadata.Source)
|
||||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -7,6 +7,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"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/mux"
|
||||||
"github.com/sagernet/sing-box/common/uot"
|
"github.com/sagernet/sing-box/common/uot"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
@ -18,36 +20,31 @@ import (
|
||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
F "github.com/sagernet/sing/common/format"
|
F "github.com/sagernet/sing/common/format"
|
||||||
|
"github.com/sagernet/sing/common/logger"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var _ adapter.TCPInjectableInbound = (*RelayInbound)(nil)
|
||||||
_ adapter.Inbound = (*ShadowsocksRelay)(nil)
|
|
||||||
_ adapter.TCPInjectableInbound = (*ShadowsocksRelay)(nil)
|
|
||||||
)
|
|
||||||
|
|
||||||
type ShadowsocksRelay struct {
|
type RelayInbound struct {
|
||||||
myInboundAdapter
|
inbound.Adapter
|
||||||
|
ctx context.Context
|
||||||
|
router adapter.ConnectionRouterEx
|
||||||
|
logger logger.ContextLogger
|
||||||
|
listener *listener.Listener
|
||||||
service *shadowaead_2022.RelayService[int]
|
service *shadowaead_2022.RelayService[int]
|
||||||
destinations []option.ShadowsocksDestination
|
destinations []option.ShadowsocksDestination
|
||||||
}
|
}
|
||||||
|
|
||||||
func newShadowsocksRelay(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksInboundOptions) (*ShadowsocksRelay, error) {
|
func newRelayInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksInboundOptions) (*RelayInbound, error) {
|
||||||
inbound := &ShadowsocksRelay{
|
inbound := &RelayInbound{
|
||||||
myInboundAdapter: myInboundAdapter{
|
Adapter: inbound.NewAdapter(C.TypeShadowsocks, tag),
|
||||||
protocol: C.TypeShadowsocks,
|
ctx: ctx,
|
||||||
network: options.Network.Build(),
|
router: uot.NewRouter(router, logger),
|
||||||
ctx: ctx,
|
logger: logger,
|
||||||
router: uot.NewRouter(router, logger),
|
|
||||||
logger: logger,
|
|
||||||
tag: tag,
|
|
||||||
listenOptions: options.ListenOptions,
|
|
||||||
},
|
|
||||||
destinations: options.Destinations,
|
destinations: options.Destinations,
|
||||||
}
|
}
|
||||||
inbound.connHandler = inbound
|
|
||||||
inbound.packetHandler = inbound
|
|
||||||
var err error
|
var err error
|
||||||
inbound.router, err = mux.NewRouterWithOptions(inbound.router, logger, common.PtrValueOrDefault(options.Multiplex))
|
inbound.router, err = mux.NewRouterWithOptions(inbound.router, logger, common.PtrValueOrDefault(options.Multiplex))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -77,11 +74,27 @@ func newShadowsocksRelay(ctx context.Context, router adapter.Router, logger log.
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
inbound.service = service
|
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
|
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))
|
err := h.service.NewConnection(ctx, conn, adapter.UpstreamMetadata(metadata))
|
||||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||||
if err != nil {
|
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) {
|
func (h *RelayInbound) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
|
||||||
err := h.service.NewPacket(h.ctx, h.packetConn(), buffer, M.Metadata{Source: source})
|
err := h.service.NewPacket(h.ctx, &stubPacketConn{h.listener.PacketWriter()}, buffer, M.Metadata{Source: source})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.logger.Error(E.Cause(err, "process packet from ", source))
|
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)
|
destinationIndex, loaded := auth.UserFromContext[int](ctx)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
return os.ErrInvalid
|
return os.ErrInvalid
|
||||||
|
@ -108,10 +121,12 @@ func (h *ShadowsocksRelay) newConnection(ctx context.Context, conn net.Conn, met
|
||||||
metadata.User = destination
|
metadata.User = destination
|
||||||
}
|
}
|
||||||
h.logger.InfoContext(ctx, "[", destination, "] inbound connection to ", metadata.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)
|
destinationIndex, loaded := auth.UserFromContext[int](ctx)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
return os.ErrInvalid
|
return os.ErrInvalid
|
||||||
|
@ -125,5 +140,13 @@ func (h *ShadowsocksRelay) newPacketConnection(ctx context.Context, conn N.Packe
|
||||||
ctx = log.ContextWithNewID(ctx)
|
ctx = log.ContextWithNewID(ctx)
|
||||||
h.logger.InfoContext(ctx, "[", destination, "] inbound packet connection from ", metadata.Source)
|
h.logger.InfoContext(ctx, "[", destination, "] inbound packet connection from ", metadata.Source)
|
||||||
h.logger.InfoContext(ctx, "[", destination, "] inbound packet connection to ", metadata.Destination)
|
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,10 +1,11 @@
|
||||||
package outbound
|
package shadowsocks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/adapter/outbound"
|
||||||
"github.com/sagernet/sing-box/common/dialer"
|
"github.com/sagernet/sing-box/common/dialer"
|
||||||
"github.com/sagernet/sing-box/common/mux"
|
"github.com/sagernet/sing-box/common/mux"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
@ -15,15 +16,19 @@ import (
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/bufio"
|
"github.com/sagernet/sing/common/bufio"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/common/logger"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/common/uot"
|
"github.com/sagernet/sing/common/uot"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ adapter.Outbound = (*Shadowsocks)(nil)
|
func RegisterOutbound(registry *outbound.Registry) {
|
||||||
|
outbound.Register[option.ShadowsocksOutboundOptions](registry, C.TypeShadowsocks, NewOutbound)
|
||||||
|
}
|
||||||
|
|
||||||
type Shadowsocks struct {
|
type Outbound struct {
|
||||||
myOutboundAdapter
|
outbound.Adapter
|
||||||
|
logger logger.ContextLogger
|
||||||
dialer N.Dialer
|
dialer N.Dialer
|
||||||
method shadowsocks.Method
|
method shadowsocks.Method
|
||||||
serverAddr M.Socksaddr
|
serverAddr M.Socksaddr
|
||||||
|
@ -32,7 +37,7 @@ type Shadowsocks struct {
|
||||||
multiplexDialer *mux.Client
|
multiplexDialer *mux.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksOutboundOptions) (*Shadowsocks, error) {
|
func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksOutboundOptions) (adapter.Outbound, error) {
|
||||||
method, err := shadowsocks.CreateMethod(ctx, options.Method, shadowsocks.MethodOptions{
|
method, err := shadowsocks.CreateMethod(ctx, options.Method, shadowsocks.MethodOptions{
|
||||||
Password: options.Password,
|
Password: options.Password,
|
||||||
})
|
})
|
||||||
|
@ -43,15 +48,9 @@ func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
outbound := &Shadowsocks{
|
outbound := &Outbound{
|
||||||
myOutboundAdapter: myOutboundAdapter{
|
Adapter: outbound.NewAdapterWithDialerOptions(C.TypeShadowsocks, options.Network.Build(), tag, options.DialerOptions),
|
||||||
protocol: C.TypeShadowsocks,
|
logger: logger,
|
||||||
network: options.Network.Build(),
|
|
||||||
router: router,
|
|
||||||
logger: logger,
|
|
||||||
tag: tag,
|
|
||||||
dependencies: withDialerDependency(options.DialerOptions),
|
|
||||||
},
|
|
||||||
dialer: outboundDialer,
|
dialer: outboundDialer,
|
||||||
method: method,
|
method: method,
|
||||||
serverAddr: options.ServerOptions.Build(),
|
serverAddr: options.ServerOptions.Build(),
|
||||||
|
@ -78,9 +77,9 @@ func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
|
||||||
return outbound, nil
|
return outbound, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Shadowsocks) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
ctx, metadata := adapter.ExtendContext(ctx)
|
ctx, metadata := adapter.ExtendContext(ctx)
|
||||||
metadata.Outbound = h.tag
|
metadata.Outbound = h.Tag()
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
if h.multiplexDialer == nil {
|
if h.multiplexDialer == nil {
|
||||||
switch N.NetworkName(network) {
|
switch N.NetworkName(network) {
|
||||||
|
@ -106,9 +105,9 @@ func (h *Shadowsocks) DialContext(ctx context.Context, network string, destinati
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Shadowsocks) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
ctx, metadata := adapter.ExtendContext(ctx)
|
ctx, metadata := adapter.ExtendContext(ctx)
|
||||||
metadata.Outbound = h.tag
|
metadata.Outbound = h.Tag()
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
if h.multiplexDialer == nil {
|
if h.multiplexDialer == nil {
|
||||||
if h.uotClient != nil {
|
if h.uotClient != nil {
|
||||||
|
@ -125,24 +124,24 @@ func (h *Shadowsocks) ListenPacket(ctx context.Context, destination M.Socksaddr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Shadowsocks) InterfaceUpdated() {
|
func (h *Outbound) InterfaceUpdated() {
|
||||||
if h.multiplexDialer != nil {
|
if h.multiplexDialer != nil {
|
||||||
h.multiplexDialer.Reset()
|
h.multiplexDialer.Reset()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Shadowsocks) Close() error {
|
func (h *Outbound) Close() error {
|
||||||
return common.Close(common.PtrOrNil(h.multiplexDialer))
|
return common.Close(common.PtrOrNil(h.multiplexDialer))
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ N.Dialer = (*shadowsocksDialer)(nil)
|
var _ N.Dialer = (*shadowsocksDialer)(nil)
|
||||||
|
|
||||||
type shadowsocksDialer Shadowsocks
|
type shadowsocksDialer Outbound
|
||||||
|
|
||||||
func (h *shadowsocksDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func (h *shadowsocksDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
ctx, metadata := adapter.ExtendContext(ctx)
|
ctx, metadata := adapter.ExtendContext(ctx)
|
||||||
metadata.Outbound = h.tag
|
metadata.Outbound = h.Tag()
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
switch N.NetworkName(network) {
|
switch N.NetworkName(network) {
|
||||||
case N.NetworkTCP:
|
case N.NetworkTCP:
|
||||||
|
@ -170,7 +169,7 @@ func (h *shadowsocksDialer) DialContext(ctx context.Context, network string, des
|
||||||
|
|
||||||
func (h *shadowsocksDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
func (h *shadowsocksDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
ctx, metadata := adapter.ExtendContext(ctx)
|
ctx, metadata := adapter.ExtendContext(ctx)
|
||||||
metadata.Outbound = h.tag
|
metadata.Outbound = h.Tag()
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
outConn, err := h.dialer.DialContext(ctx, N.NetworkUDP, h.serverAddr)
|
outConn, err := h.dialer.DialContext(ctx, N.NetworkUDP, h.serverAddr)
|
||||||
if err != nil {
|
if err != nil {
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue