mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-13 20:28:32 +10:00
Compare commits
28 Commits
dev-next-w
...
v1.10.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3cc0e87cfb | ||
|
|
effea5a2b3 | ||
|
|
7f168c5ec6 | ||
|
|
0e9129ee3f | ||
|
|
1086d5e665 | ||
|
|
d9102ba599 | ||
|
|
17019f1729 | ||
|
|
6be07ed51f | ||
|
|
af58e3bec0 | ||
|
|
e58b549d0f | ||
|
|
1d81996ceb | ||
|
|
97c47e72c4 | ||
|
|
122be275b0 | ||
|
|
0bb1132034 | ||
|
|
de14337b4b | ||
|
|
1e07633914 | ||
|
|
e3e203844e | ||
|
|
84a102a6ef | ||
|
|
f1c76c4dde | ||
|
|
8df0aa5719 | ||
|
|
21faadb992 | ||
|
|
88099a304a | ||
|
|
f504fb0d46 | ||
|
|
1d517b6ca5 | ||
|
|
b702d0b67a | ||
|
|
a001e30d8b | ||
|
|
cdb93f0bb2 | ||
|
|
718cffea9a |
2
.github/workflows/debug.yml
vendored
2
.github/workflows/debug.yml
vendored
@@ -204,7 +204,7 @@ jobs:
|
|||||||
GOARM: ${{ matrix.goarm }}
|
GOARM: ${{ matrix.goarm }}
|
||||||
GOMIPS: ${{ matrix.gomips }}
|
GOMIPS: ${{ matrix.gomips }}
|
||||||
CGO_ENABLED: 0
|
CGO_ENABLED: 0
|
||||||
TAGS: with_clash_api,with_quic
|
TAGS: with_gvisor,with_dhcp,with_wireguard,with_clash_api,with_quic,with_utls,with_ech
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||||
|
|||||||
16
Makefile
16
Makefile
@@ -96,16 +96,14 @@ upload_android:
|
|||||||
release_android: lib_android update_android_version build_android upload_android
|
release_android: lib_android update_android_version build_android upload_android
|
||||||
|
|
||||||
publish_android:
|
publish_android:
|
||||||
cd ../sing-box-for-android && ./gradlew :app:publishPlayReleaseBundle
|
cd ../sing-box-for-android && ./gradlew :app:publishPlayReleaseBundle && ./gradlew --stop
|
||||||
|
|
||||||
publish_android_appcenter:
|
|
||||||
cd ../sing-box-for-android && ./gradlew :app:appCenterAssembleAndUploadPlayRelease
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: find why and remove `-destination 'generic/platform=iOS'`
|
# TODO: find why and remove `-destination 'generic/platform=iOS'`
|
||||||
|
# TODO: remove xcode clean when fix control widget fixed
|
||||||
build_ios:
|
build_ios:
|
||||||
cd ../sing-box-for-apple && \
|
cd ../sing-box-for-apple && \
|
||||||
rm -rf build/SFI.xcarchive && \
|
rm -rf build/SFI.xcarchive && \
|
||||||
|
xcodebuild clean -scheme SFI && \
|
||||||
xcodebuild archive -scheme SFI -configuration Release -destination 'generic/platform=iOS' -archivePath build/SFI.xcarchive -allowProvisioningUpdates
|
xcodebuild archive -scheme SFI -configuration Release -destination 'generic/platform=iOS' -archivePath build/SFI.xcarchive -allowProvisioningUpdates
|
||||||
|
|
||||||
upload_ios_app_store:
|
upload_ios_app_store:
|
||||||
@@ -147,9 +145,13 @@ build_macos_dmg:
|
|||||||
--hide-extension "SFM.app" \
|
--hide-extension "SFM.app" \
|
||||||
--app-drop-link 0 0 \
|
--app-drop-link 0 0 \
|
||||||
--skip-jenkins \
|
--skip-jenkins \
|
||||||
--notarize "notarytool-password" \
|
|
||||||
"../sing-box/dist/SFM/SFM.dmg" "build/SFM.System/SFM.app"
|
"../sing-box/dist/SFM/SFM.dmg" "build/SFM.System/SFM.app"
|
||||||
|
|
||||||
|
notarize_macos_dmg:
|
||||||
|
xcrun notarytool submit "dist/SFM/SFM.dmg" --wait \
|
||||||
|
--keychain-profile "notarytool-password" \
|
||||||
|
--no-s3-acceleration
|
||||||
|
|
||||||
upload_macos_dmg:
|
upload_macos_dmg:
|
||||||
cd dist/SFM && \
|
cd dist/SFM && \
|
||||||
cp SFM.dmg "SFM-${VERSION}-universal.dmg" && \
|
cp SFM.dmg "SFM-${VERSION}-universal.dmg" && \
|
||||||
@@ -164,7 +166,7 @@ upload_macos_dsyms:
|
|||||||
cp SFM.dSYMs.zip "SFM-${VERSION}-universal.dSYMs.zip" && \
|
cp SFM.dSYMs.zip "SFM-${VERSION}-universal.dSYMs.zip" && \
|
||||||
ghr --replace --draft --prerelease "v${VERSION}" "SFM-${VERSION}-universal.dSYMs.zip"
|
ghr --replace --draft --prerelease "v${VERSION}" "SFM-${VERSION}-universal.dSYMs.zip"
|
||||||
|
|
||||||
release_macos_standalone: build_macos_standalone build_macos_dmg upload_macos_dmg upload_macos_dsyms
|
release_macos_standalone: build_macos_standalone build_macos_dmg notarize_macos_dmg upload_macos_dmg upload_macos_dsyms
|
||||||
|
|
||||||
build_tvos:
|
build_tvos:
|
||||||
cd ../sing-box-for-apple && \
|
cd ../sing-box-for-apple && \
|
||||||
|
|||||||
104
adapter/conn_router.go
Normal file
104
adapter/conn_router.go
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
package adapter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/common/logger"
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConnectionRouter interface {
|
||||||
|
RouteConnection(ctx context.Context, conn net.Conn, metadata InboundContext) error
|
||||||
|
RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRouteHandler(
|
||||||
|
metadata InboundContext,
|
||||||
|
router ConnectionRouter,
|
||||||
|
logger logger.ContextLogger,
|
||||||
|
) UpstreamHandlerAdapter {
|
||||||
|
return &routeHandlerWrapper{
|
||||||
|
metadata: metadata,
|
||||||
|
router: router,
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRouteContextHandler(
|
||||||
|
router ConnectionRouter,
|
||||||
|
logger logger.ContextLogger,
|
||||||
|
) UpstreamHandlerAdapter {
|
||||||
|
return &routeContextHandlerWrapper{
|
||||||
|
router: router,
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ UpstreamHandlerAdapter = (*routeHandlerWrapper)(nil)
|
||||||
|
|
||||||
|
type routeHandlerWrapper struct {
|
||||||
|
metadata InboundContext
|
||||||
|
router ConnectionRouter
|
||||||
|
logger logger.ContextLogger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *routeHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||||
|
myMetadata := w.metadata
|
||||||
|
if metadata.Source.IsValid() {
|
||||||
|
myMetadata.Source = metadata.Source
|
||||||
|
}
|
||||||
|
if metadata.Destination.IsValid() {
|
||||||
|
myMetadata.Destination = metadata.Destination
|
||||||
|
}
|
||||||
|
return w.router.RouteConnection(ctx, conn, myMetadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *routeHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
||||||
|
myMetadata := w.metadata
|
||||||
|
if metadata.Source.IsValid() {
|
||||||
|
myMetadata.Source = metadata.Source
|
||||||
|
}
|
||||||
|
if metadata.Destination.IsValid() {
|
||||||
|
myMetadata.Destination = metadata.Destination
|
||||||
|
}
|
||||||
|
return w.router.RoutePacketConnection(ctx, conn, myMetadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *routeHandlerWrapper) NewError(ctx context.Context, err error) {
|
||||||
|
w.logger.ErrorContext(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ UpstreamHandlerAdapter = (*routeContextHandlerWrapper)(nil)
|
||||||
|
|
||||||
|
type routeContextHandlerWrapper struct {
|
||||||
|
router ConnectionRouter
|
||||||
|
logger logger.ContextLogger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *routeContextHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||||
|
myMetadata := ContextFrom(ctx)
|
||||||
|
if metadata.Source.IsValid() {
|
||||||
|
myMetadata.Source = metadata.Source
|
||||||
|
}
|
||||||
|
if metadata.Destination.IsValid() {
|
||||||
|
myMetadata.Destination = metadata.Destination
|
||||||
|
}
|
||||||
|
return w.router.RouteConnection(ctx, conn, *myMetadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *routeContextHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
||||||
|
myMetadata := ContextFrom(ctx)
|
||||||
|
if metadata.Source.IsValid() {
|
||||||
|
myMetadata.Source = metadata.Source
|
||||||
|
}
|
||||||
|
if metadata.Destination.IsValid() {
|
||||||
|
myMetadata.Destination = metadata.Destination
|
||||||
|
}
|
||||||
|
return w.router.RoutePacketConnection(ctx, conn, *myMetadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *routeContextHandlerWrapper) NewError(ctx context.Context, err error) {
|
||||||
|
w.logger.ErrorContext(ctx, err)
|
||||||
|
}
|
||||||
@@ -6,53 +6,27 @@ 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"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
type ConnectionHandler interface {
|
type ConnectionHandler interface {
|
||||||
NewConnection(ctx context.Context, conn net.Conn, metadata InboundContext) error
|
NewConnection(ctx context.Context, conn net.Conn, metadata InboundContext) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConnectionHandlerEx interface {
|
|
||||||
NewConnectionEx(ctx context.Context, conn net.Conn, metadata InboundContext, onClose N.CloseHandlerFunc)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: use PacketHandlerEx instead
|
|
||||||
type PacketHandler interface {
|
type PacketHandler interface {
|
||||||
NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata InboundContext) error
|
NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata InboundContext) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type PacketHandlerEx interface {
|
|
||||||
NewPacketEx(buffer *buf.Buffer, source M.Socksaddr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: use OOBPacketHandlerEx instead
|
|
||||||
type OOBPacketHandler interface {
|
type OOBPacketHandler interface {
|
||||||
NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, oob []byte, metadata InboundContext) error
|
NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, oob []byte, metadata InboundContext) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type OOBPacketHandlerEx interface {
|
|
||||||
NewPacketEx(buffer *buf.Buffer, oob []byte, source M.Socksaddr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
type PacketConnectionHandler interface {
|
type PacketConnectionHandler interface {
|
||||||
NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error
|
NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type PacketConnectionHandlerEx interface {
|
|
||||||
NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata InboundContext, onClose N.CloseHandlerFunc)
|
|
||||||
}
|
|
||||||
|
|
||||||
type UpstreamHandlerAdapter interface {
|
type UpstreamHandlerAdapter interface {
|
||||||
N.TCPConnectionHandler
|
N.TCPConnectionHandler
|
||||||
N.UDPConnectionHandler
|
N.UDPConnectionHandler
|
||||||
E.Handler
|
E.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpstreamHandlerAdapterEx interface {
|
|
||||||
N.TCPConnectionHandlerEx
|
|
||||||
N.UDPConnectionHandlerEx
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,12 +2,13 @@ package adapter
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net"
|
||||||
"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"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Inbound interface {
|
type Inbound interface {
|
||||||
@@ -16,19 +17,11 @@ type Inbound interface {
|
|||||||
Tag() string
|
Tag() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type TCPInjectableInbound interface {
|
type InjectableInbound interface {
|
||||||
Inbound
|
Inbound
|
||||||
ConnectionHandlerEx
|
Network() []string
|
||||||
}
|
NewConnection(ctx context.Context, conn net.Conn, metadata InboundContext) error
|
||||||
|
NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error
|
||||||
type UDPInjectableInbound interface {
|
|
||||||
Inbound
|
|
||||||
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 {
|
||||||
@@ -50,15 +43,10 @@ type InboundContext struct {
|
|||||||
|
|
||||||
// cache
|
// cache
|
||||||
|
|
||||||
// Deprecated: implement in rule action
|
InboundDetour string
|
||||||
InboundDetour string
|
LastInbound string
|
||||||
LastInbound string
|
OriginDestination M.Socksaddr
|
||||||
OriginDestination M.Socksaddr
|
InboundOptions option.InboundOptions
|
||||||
// Deprecated
|
|
||||||
InboundOptions option.InboundOptions
|
|
||||||
UDPDisableDomainUnmapping bool
|
|
||||||
DNSServer string
|
|
||||||
|
|
||||||
DestinationAddresses []netip.Addr
|
DestinationAddresses []netip.Addr
|
||||||
SourceGeoIPCode string
|
SourceGeoIPCode string
|
||||||
GeoIPCode string
|
GeoIPCode string
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -2,9 +2,8 @@ package adapter
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net"
|
||||||
|
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -16,9 +15,6 @@ type Outbound interface {
|
|||||||
Network() []string
|
Network() []string
|
||||||
Dependencies() []string
|
Dependencies() []string
|
||||||
N.Dialer
|
N.Dialer
|
||||||
}
|
NewConnection(ctx context.Context, conn net.Conn, metadata InboundContext) error
|
||||||
|
NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error
|
||||||
type OutboundRegistry interface {
|
|
||||||
option.OutboundOptionsRegistry
|
|
||||||
CreateOutbound(ctx context.Context, router Router, logger log.ContextLogger, tag string, outboundType string, options any) (Outbound, error)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -34,8 +34,6 @@ type Router interface {
|
|||||||
FakeIPStore() FakeIPStore
|
FakeIPStore() FakeIPStore
|
||||||
|
|
||||||
ConnectionRouter
|
ConnectionRouter
|
||||||
PreMatch(metadata InboundContext) error
|
|
||||||
ConnectionRouterEx
|
|
||||||
|
|
||||||
GeoIPReader() *geoip.Reader
|
GeoIPReader() *geoip.Reader
|
||||||
LoadGeosite(code string) (Rule, error)
|
LoadGeosite(code string) (Rule, error)
|
||||||
@@ -72,18 +70,6 @@ type Router interface {
|
|||||||
ResetNetwork() error
|
ResetNetwork() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: Use ConnectionRouterEx instead.
|
|
||||||
type ConnectionRouter interface {
|
|
||||||
RouteConnection(ctx context.Context, conn net.Conn, metadata InboundContext) error
|
|
||||||
RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type ConnectionRouterEx interface {
|
|
||||||
ConnectionRouter
|
|
||||||
RouteConnectionEx(ctx context.Context, conn net.Conn, metadata InboundContext, onClose N.CloseHandlerFunc)
|
|
||||||
RoutePacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata InboundContext, onClose N.CloseHandlerFunc)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ContextWithRouter(ctx context.Context, router Router) context.Context {
|
func ContextWithRouter(ctx context.Context, router Router) context.Context {
|
||||||
return service.ContextWith(ctx, router)
|
return service.ContextWith(ctx, router)
|
||||||
}
|
}
|
||||||
@@ -92,6 +78,28 @@ func RouterFromContext(ctx context.Context) Router {
|
|||||||
return service.FromContext[Router](ctx)
|
return service.FromContext[Router](ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HeadlessRule interface {
|
||||||
|
Match(metadata *InboundContext) bool
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Rule interface {
|
||||||
|
HeadlessRule
|
||||||
|
Service
|
||||||
|
Type() string
|
||||||
|
UpdateGeosite() error
|
||||||
|
Outbound() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type DNSRule interface {
|
||||||
|
Rule
|
||||||
|
DisableCache() bool
|
||||||
|
RewriteTTL() *uint32
|
||||||
|
ClientSubnet() *netip.Prefix
|
||||||
|
WithAddressLimit() bool
|
||||||
|
MatchAddressLimit(metadata *InboundContext) bool
|
||||||
|
}
|
||||||
|
|
||||||
type RuleSet interface {
|
type RuleSet interface {
|
||||||
Name() string
|
Name() string
|
||||||
StartContext(ctx context.Context, startContext *HTTPStartContext) error
|
StartContext(ctx context.Context, startContext *HTTPStartContext) error
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
package adapter
|
|
||||||
|
|
||||||
import (
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
|
||||||
)
|
|
||||||
|
|
||||||
type HeadlessRule interface {
|
|
||||||
Match(metadata *InboundContext) bool
|
|
||||||
String() string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Rule interface {
|
|
||||||
HeadlessRule
|
|
||||||
Service
|
|
||||||
Type() string
|
|
||||||
UpdateGeosite() error
|
|
||||||
Action() RuleAction
|
|
||||||
}
|
|
||||||
|
|
||||||
type DNSRule interface {
|
|
||||||
Rule
|
|
||||||
WithAddressLimit() bool
|
|
||||||
MatchAddressLimit(metadata *InboundContext) bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type RuleAction interface {
|
|
||||||
Type() string
|
|
||||||
String() string
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsFinalAction(action RuleAction) bool {
|
|
||||||
switch action.Type() {
|
|
||||||
case C.RuleActionTypeSniff, C.RuleActionTypeResolve:
|
|
||||||
return false
|
|
||||||
default:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,165 +4,112 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
ConnectionHandlerFuncEx = func(ctx context.Context, conn net.Conn, metadata InboundContext, onClose N.CloseHandlerFunc)
|
ConnectionHandlerFunc = func(ctx context.Context, conn net.Conn, metadata InboundContext) error
|
||||||
PacketConnectionHandlerFuncEx = func(ctx context.Context, conn N.PacketConn, metadata InboundContext, onClose N.CloseHandlerFunc)
|
PacketConnectionHandlerFunc = func(ctx context.Context, conn N.PacketConn, metadata InboundContext) error
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewUpstreamHandlerEx(
|
func NewUpstreamHandler(
|
||||||
metadata InboundContext,
|
metadata InboundContext,
|
||||||
connectionHandler ConnectionHandlerFuncEx,
|
connectionHandler ConnectionHandlerFunc,
|
||||||
packetHandler PacketConnectionHandlerFuncEx,
|
packetHandler PacketConnectionHandlerFunc,
|
||||||
) UpstreamHandlerAdapterEx {
|
errorHandler E.Handler,
|
||||||
return &myUpstreamHandlerWrapperEx{
|
) UpstreamHandlerAdapter {
|
||||||
|
return &myUpstreamHandlerWrapper{
|
||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
connectionHandler: connectionHandler,
|
connectionHandler: connectionHandler,
|
||||||
packetHandler: packetHandler,
|
packetHandler: packetHandler,
|
||||||
|
errorHandler: errorHandler,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ UpstreamHandlerAdapterEx = (*myUpstreamHandlerWrapperEx)(nil)
|
var _ UpstreamHandlerAdapter = (*myUpstreamHandlerWrapper)(nil)
|
||||||
|
|
||||||
type myUpstreamHandlerWrapperEx struct {
|
type myUpstreamHandlerWrapper struct {
|
||||||
metadata InboundContext
|
metadata InboundContext
|
||||||
connectionHandler ConnectionHandlerFuncEx
|
connectionHandler ConnectionHandlerFunc
|
||||||
packetHandler PacketConnectionHandlerFuncEx
|
packetHandler PacketConnectionHandlerFunc
|
||||||
|
errorHandler E.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *myUpstreamHandlerWrapperEx) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
func (w *myUpstreamHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||||
myMetadata := w.metadata
|
myMetadata := w.metadata
|
||||||
if source.IsValid() {
|
if metadata.Source.IsValid() {
|
||||||
myMetadata.Source = source
|
myMetadata.Source = metadata.Source
|
||||||
}
|
}
|
||||||
if destination.IsValid() {
|
if metadata.Destination.IsValid() {
|
||||||
myMetadata.Destination = destination
|
myMetadata.Destination = metadata.Destination
|
||||||
}
|
}
|
||||||
w.connectionHandler(ctx, conn, myMetadata, onClose)
|
return w.connectionHandler(ctx, conn, myMetadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *myUpstreamHandlerWrapperEx) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
func (w *myUpstreamHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
||||||
myMetadata := w.metadata
|
myMetadata := w.metadata
|
||||||
if source.IsValid() {
|
if metadata.Source.IsValid() {
|
||||||
myMetadata.Source = source
|
myMetadata.Source = metadata.Source
|
||||||
}
|
}
|
||||||
if destination.IsValid() {
|
if metadata.Destination.IsValid() {
|
||||||
myMetadata.Destination = destination
|
myMetadata.Destination = metadata.Destination
|
||||||
}
|
}
|
||||||
w.packetHandler(ctx, conn, myMetadata, onClose)
|
return w.packetHandler(ctx, conn, myMetadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ UpstreamHandlerAdapterEx = (*myUpstreamContextHandlerWrapperEx)(nil)
|
func (w *myUpstreamHandlerWrapper) NewError(ctx context.Context, err error) {
|
||||||
|
w.errorHandler.NewError(ctx, err)
|
||||||
type myUpstreamContextHandlerWrapperEx struct {
|
|
||||||
connectionHandler ConnectionHandlerFuncEx
|
|
||||||
packetHandler PacketConnectionHandlerFuncEx
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUpstreamContextHandlerEx(
|
func UpstreamMetadata(metadata InboundContext) M.Metadata {
|
||||||
connectionHandler ConnectionHandlerFuncEx,
|
return M.Metadata{
|
||||||
packetHandler PacketConnectionHandlerFuncEx,
|
Source: metadata.Source,
|
||||||
) UpstreamHandlerAdapterEx {
|
Destination: metadata.Destination,
|
||||||
return &myUpstreamContextHandlerWrapperEx{
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type myUpstreamContextHandlerWrapper struct {
|
||||||
|
connectionHandler ConnectionHandlerFunc
|
||||||
|
packetHandler PacketConnectionHandlerFunc
|
||||||
|
errorHandler E.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUpstreamContextHandler(
|
||||||
|
connectionHandler ConnectionHandlerFunc,
|
||||||
|
packetHandler PacketConnectionHandlerFunc,
|
||||||
|
errorHandler E.Handler,
|
||||||
|
) UpstreamHandlerAdapter {
|
||||||
|
return &myUpstreamContextHandlerWrapper{
|
||||||
connectionHandler: connectionHandler,
|
connectionHandler: connectionHandler,
|
||||||
packetHandler: packetHandler,
|
packetHandler: packetHandler,
|
||||||
|
errorHandler: errorHandler,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *myUpstreamContextHandlerWrapperEx) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
func (w *myUpstreamContextHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||||
myMetadata := ContextFrom(ctx)
|
myMetadata := ContextFrom(ctx)
|
||||||
if source.IsValid() {
|
if metadata.Source.IsValid() {
|
||||||
myMetadata.Source = source
|
myMetadata.Source = metadata.Source
|
||||||
}
|
}
|
||||||
if destination.IsValid() {
|
if metadata.Destination.IsValid() {
|
||||||
myMetadata.Destination = destination
|
myMetadata.Destination = metadata.Destination
|
||||||
}
|
}
|
||||||
w.connectionHandler(ctx, conn, *myMetadata, onClose)
|
return w.connectionHandler(ctx, conn, *myMetadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *myUpstreamContextHandlerWrapperEx) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
func (w *myUpstreamContextHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
||||||
myMetadata := ContextFrom(ctx)
|
myMetadata := ContextFrom(ctx)
|
||||||
if source.IsValid() {
|
if metadata.Source.IsValid() {
|
||||||
myMetadata.Source = source
|
myMetadata.Source = metadata.Source
|
||||||
}
|
}
|
||||||
if destination.IsValid() {
|
if metadata.Destination.IsValid() {
|
||||||
myMetadata.Destination = destination
|
myMetadata.Destination = metadata.Destination
|
||||||
}
|
}
|
||||||
w.packetHandler(ctx, conn, *myMetadata, onClose)
|
return w.packetHandler(ctx, conn, *myMetadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRouteHandlerEx(
|
func (w *myUpstreamContextHandlerWrapper) NewError(ctx context.Context, err error) {
|
||||||
metadata InboundContext,
|
w.errorHandler.NewError(ctx, err)
|
||||||
router ConnectionRouterEx,
|
|
||||||
) UpstreamHandlerAdapterEx {
|
|
||||||
return &routeHandlerWrapperEx{
|
|
||||||
metadata: metadata,
|
|
||||||
router: router,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ UpstreamHandlerAdapterEx = (*routeHandlerWrapperEx)(nil)
|
|
||||||
|
|
||||||
type routeHandlerWrapperEx struct {
|
|
||||||
metadata InboundContext
|
|
||||||
router ConnectionRouterEx
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *routeHandlerWrapperEx) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
|
||||||
if source.IsValid() {
|
|
||||||
r.metadata.Source = source
|
|
||||||
}
|
|
||||||
if destination.IsValid() {
|
|
||||||
r.metadata.Destination = destination
|
|
||||||
}
|
|
||||||
r.router.RouteConnectionEx(ctx, conn, r.metadata, onClose)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *routeHandlerWrapperEx) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
|
||||||
if source.IsValid() {
|
|
||||||
r.metadata.Source = source
|
|
||||||
}
|
|
||||||
if destination.IsValid() {
|
|
||||||
r.metadata.Destination = destination
|
|
||||||
}
|
|
||||||
r.router.RoutePacketConnectionEx(ctx, conn, r.metadata, onClose)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRouteContextHandlerEx(
|
|
||||||
router ConnectionRouterEx,
|
|
||||||
) UpstreamHandlerAdapterEx {
|
|
||||||
return &routeContextHandlerWrapperEx{
|
|
||||||
router: router,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ UpstreamHandlerAdapterEx = (*routeContextHandlerWrapperEx)(nil)
|
|
||||||
|
|
||||||
type routeContextHandlerWrapperEx struct {
|
|
||||||
router ConnectionRouterEx
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *routeContextHandlerWrapperEx) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
|
||||||
metadata := ContextFrom(ctx)
|
|
||||||
if source.IsValid() {
|
|
||||||
metadata.Source = source
|
|
||||||
}
|
|
||||||
if destination.IsValid() {
|
|
||||||
metadata.Destination = destination
|
|
||||||
}
|
|
||||||
r.router.RouteConnectionEx(ctx, conn, *metadata, onClose)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *routeContextHandlerWrapperEx) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
|
||||||
metadata := ContextFrom(ctx)
|
|
||||||
if source.IsValid() {
|
|
||||||
metadata.Source = source
|
|
||||||
}
|
|
||||||
if destination.IsValid() {
|
|
||||||
metadata.Destination = destination
|
|
||||||
}
|
|
||||||
r.router.RoutePacketConnectionEx(ctx, conn, *metadata, onClose)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,216 +0,0 @@
|
|||||||
package adapter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
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 (
|
|
||||||
// Deprecated
|
|
||||||
ConnectionHandlerFunc = func(ctx context.Context, conn net.Conn, metadata InboundContext) error
|
|
||||||
// Deprecated
|
|
||||||
PacketConnectionHandlerFunc = func(ctx context.Context, conn N.PacketConn, metadata InboundContext) error
|
|
||||||
)
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func NewUpstreamHandler(
|
|
||||||
metadata InboundContext,
|
|
||||||
connectionHandler ConnectionHandlerFunc,
|
|
||||||
packetHandler PacketConnectionHandlerFunc,
|
|
||||||
errorHandler E.Handler,
|
|
||||||
) UpstreamHandlerAdapter {
|
|
||||||
return &myUpstreamHandlerWrapper{
|
|
||||||
metadata: metadata,
|
|
||||||
connectionHandler: connectionHandler,
|
|
||||||
packetHandler: packetHandler,
|
|
||||||
errorHandler: errorHandler,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ UpstreamHandlerAdapter = (*myUpstreamHandlerWrapper)(nil)
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
type myUpstreamHandlerWrapper struct {
|
|
||||||
metadata InboundContext
|
|
||||||
connectionHandler ConnectionHandlerFunc
|
|
||||||
packetHandler PacketConnectionHandlerFunc
|
|
||||||
errorHandler E.Handler
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *myUpstreamHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
|
||||||
myMetadata := w.metadata
|
|
||||||
if metadata.Source.IsValid() {
|
|
||||||
myMetadata.Source = metadata.Source
|
|
||||||
}
|
|
||||||
if metadata.Destination.IsValid() {
|
|
||||||
myMetadata.Destination = metadata.Destination
|
|
||||||
}
|
|
||||||
return w.connectionHandler(ctx, conn, myMetadata)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *myUpstreamHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
|
||||||
myMetadata := w.metadata
|
|
||||||
if metadata.Source.IsValid() {
|
|
||||||
myMetadata.Source = metadata.Source
|
|
||||||
}
|
|
||||||
if metadata.Destination.IsValid() {
|
|
||||||
myMetadata.Destination = metadata.Destination
|
|
||||||
}
|
|
||||||
return w.packetHandler(ctx, conn, myMetadata)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *myUpstreamHandlerWrapper) NewError(ctx context.Context, err error) {
|
|
||||||
w.errorHandler.NewError(ctx, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func UpstreamMetadata(metadata InboundContext) M.Metadata {
|
|
||||||
return M.Metadata{
|
|
||||||
Source: metadata.Source,
|
|
||||||
Destination: metadata.Destination,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
type myUpstreamContextHandlerWrapper struct {
|
|
||||||
connectionHandler ConnectionHandlerFunc
|
|
||||||
packetHandler PacketConnectionHandlerFunc
|
|
||||||
errorHandler E.Handler
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func NewUpstreamContextHandler(
|
|
||||||
connectionHandler ConnectionHandlerFunc,
|
|
||||||
packetHandler PacketConnectionHandlerFunc,
|
|
||||||
errorHandler E.Handler,
|
|
||||||
) UpstreamHandlerAdapter {
|
|
||||||
return &myUpstreamContextHandlerWrapper{
|
|
||||||
connectionHandler: connectionHandler,
|
|
||||||
packetHandler: packetHandler,
|
|
||||||
errorHandler: errorHandler,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *myUpstreamContextHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
|
||||||
myMetadata := ContextFrom(ctx)
|
|
||||||
if metadata.Source.IsValid() {
|
|
||||||
myMetadata.Source = metadata.Source
|
|
||||||
}
|
|
||||||
if metadata.Destination.IsValid() {
|
|
||||||
myMetadata.Destination = metadata.Destination
|
|
||||||
}
|
|
||||||
return w.connectionHandler(ctx, conn, *myMetadata)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *myUpstreamContextHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
|
||||||
myMetadata := ContextFrom(ctx)
|
|
||||||
if metadata.Source.IsValid() {
|
|
||||||
myMetadata.Source = metadata.Source
|
|
||||||
}
|
|
||||||
if metadata.Destination.IsValid() {
|
|
||||||
myMetadata.Destination = metadata.Destination
|
|
||||||
}
|
|
||||||
return w.packetHandler(ctx, conn, *myMetadata)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *myUpstreamContextHandlerWrapper) NewError(ctx context.Context, err error) {
|
|
||||||
w.errorHandler.NewError(ctx, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use ConnectionRouterEx instead.
|
|
||||||
func NewRouteHandler(
|
|
||||||
metadata InboundContext,
|
|
||||||
router ConnectionRouter,
|
|
||||||
logger logger.ContextLogger,
|
|
||||||
) UpstreamHandlerAdapter {
|
|
||||||
return &routeHandlerWrapper{
|
|
||||||
metadata: metadata,
|
|
||||||
router: router,
|
|
||||||
logger: logger,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use ConnectionRouterEx instead.
|
|
||||||
func NewRouteContextHandler(
|
|
||||||
router ConnectionRouter,
|
|
||||||
logger logger.ContextLogger,
|
|
||||||
) UpstreamHandlerAdapter {
|
|
||||||
return &routeContextHandlerWrapper{
|
|
||||||
router: router,
|
|
||||||
logger: logger,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ UpstreamHandlerAdapter = (*routeHandlerWrapper)(nil)
|
|
||||||
|
|
||||||
// Deprecated: Use ConnectionRouterEx instead.
|
|
||||||
type routeHandlerWrapper struct {
|
|
||||||
metadata InboundContext
|
|
||||||
router ConnectionRouter
|
|
||||||
logger logger.ContextLogger
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *routeHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
|
||||||
myMetadata := w.metadata
|
|
||||||
if metadata.Source.IsValid() {
|
|
||||||
myMetadata.Source = metadata.Source
|
|
||||||
}
|
|
||||||
if metadata.Destination.IsValid() {
|
|
||||||
myMetadata.Destination = metadata.Destination
|
|
||||||
}
|
|
||||||
return w.router.RouteConnection(ctx, conn, myMetadata)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *routeHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
|
||||||
myMetadata := w.metadata
|
|
||||||
if metadata.Source.IsValid() {
|
|
||||||
myMetadata.Source = metadata.Source
|
|
||||||
}
|
|
||||||
if metadata.Destination.IsValid() {
|
|
||||||
myMetadata.Destination = metadata.Destination
|
|
||||||
}
|
|
||||||
return w.router.RoutePacketConnection(ctx, conn, myMetadata)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *routeHandlerWrapper) NewError(ctx context.Context, err error) {
|
|
||||||
w.logger.ErrorContext(ctx, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ UpstreamHandlerAdapter = (*routeContextHandlerWrapper)(nil)
|
|
||||||
|
|
||||||
// Deprecated: Use ConnectionRouterEx instead.
|
|
||||||
type routeContextHandlerWrapper struct {
|
|
||||||
router ConnectionRouter
|
|
||||||
logger logger.ContextLogger
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *routeContextHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
|
||||||
myMetadata := ContextFrom(ctx)
|
|
||||||
if metadata.Source.IsValid() {
|
|
||||||
myMetadata.Source = metadata.Source
|
|
||||||
}
|
|
||||||
if metadata.Destination.IsValid() {
|
|
||||||
myMetadata.Destination = metadata.Destination
|
|
||||||
}
|
|
||||||
return w.router.RouteConnection(ctx, conn, *myMetadata)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *routeContextHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
|
||||||
myMetadata := ContextFrom(ctx)
|
|
||||||
if metadata.Source.IsValid() {
|
|
||||||
myMetadata.Source = metadata.Source
|
|
||||||
}
|
|
||||||
if metadata.Destination.IsValid() {
|
|
||||||
myMetadata.Destination = metadata.Destination
|
|
||||||
}
|
|
||||||
return w.router.RoutePacketConnection(ctx, conn, *myMetadata)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *routeContextHandlerWrapper) NewError(ctx context.Context, err error) {
|
|
||||||
w.logger.ErrorContext(ctx, err)
|
|
||||||
}
|
|
||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -15,7 +16,8 @@ type V2RayServerTransport interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type V2RayServerTransportHandler interface {
|
type V2RayServerTransportHandler interface {
|
||||||
N.TCPConnectionHandlerEx
|
N.TCPConnectionHandler
|
||||||
|
E.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
type V2RayClientTransport interface {
|
type V2RayClientTransport interface {
|
||||||
|
|||||||
91
box.go
91
box.go
@@ -14,9 +14,10 @@ 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/protocol/direct"
|
"github.com/sagernet/sing-box/outbound"
|
||||||
"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"
|
||||||
@@ -43,37 +44,16 @@ 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)
|
||||||
@@ -90,9 +70,8 @@ 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 platformInterface != nil {
|
if options.PlatformInterface != nil {
|
||||||
defaultLogWriter = io.Discard
|
defaultLogWriter = io.Discard
|
||||||
}
|
}
|
||||||
logFactory, err := log.New(log.Options{
|
logFactory, err := log.New(log.Options{
|
||||||
@@ -113,92 +92,64 @@ 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")
|
||||||
}
|
}
|
||||||
//nolint:staticcheck
|
|
||||||
if len(options.LegacyInbounds) > 0 {
|
|
||||||
for _, legacyInbound := range options.LegacyInbounds {
|
|
||||||
options.Inbounds = append(options.Inbounds, option.Inbound{
|
|
||||||
Type: legacyInbound.Type,
|
|
||||||
Tag: legacyInbound.Tag,
|
|
||||||
Options: common.Must1(legacyInbound.RawOptions()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
inbounds := make([]adapter.Inbound, 0, len(options.Inbounds))
|
inbounds := make([]adapter.Inbound, 0, len(options.Inbounds))
|
||||||
//nolint:staticcheck
|
|
||||||
if len(options.LegacyOutbounds) > 0 {
|
|
||||||
for _, legacyOutbound := range options.LegacyOutbounds {
|
|
||||||
options.Outbounds = append(options.Outbounds, option.Outbound{
|
|
||||||
Type: legacyOutbound.Type,
|
|
||||||
Tag: legacyOutbound.Tag,
|
|
||||||
Options: common.Must1(legacyOutbound.RawOptions()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
outbounds := make([]adapter.Outbound, 0, len(options.Outbounds))
|
outbounds := make([]adapter.Outbound, 0, len(options.Outbounds))
|
||||||
for i, inboundOptions := range options.Inbounds {
|
for i, inboundOptions := range options.Inbounds {
|
||||||
var currentInbound adapter.Inbound
|
var in 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)
|
||||||
}
|
}
|
||||||
currentInbound, err = inboundRegistry.CreateInbound(
|
in, err = inbound.New(
|
||||||
ctx,
|
ctx,
|
||||||
router,
|
router,
|
||||||
logFactory.NewLogger(F.ToString("inbound/", inboundOptions.Type, "[", tag, "]")),
|
logFactory.NewLogger(F.ToString("inbound/", inboundOptions.Type, "[", tag, "]")),
|
||||||
tag,
|
tag,
|
||||||
inboundOptions.Type,
|
inboundOptions,
|
||||||
inboundOptions.Options,
|
options.PlatformInterface,
|
||||||
)
|
)
|
||||||
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, currentInbound)
|
inbounds = append(inbounds, in)
|
||||||
}
|
}
|
||||||
for i, outboundOptions := range options.Outbounds {
|
for i, outboundOptions := range options.Outbounds {
|
||||||
var currentOutbound adapter.Outbound
|
var out 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)
|
||||||
}
|
}
|
||||||
outboundCtx := ctx
|
out, err = outbound.New(
|
||||||
if tag != "" {
|
ctx,
|
||||||
// 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.Type,
|
outboundOptions)
|
||||||
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, currentOutbound)
|
outbounds = append(outbounds, out)
|
||||||
}
|
}
|
||||||
err = router.Initialize(inbounds, outbounds, func() adapter.Outbound {
|
err = router.Initialize(inbounds, outbounds, func() adapter.Outbound {
|
||||||
defaultOutbound, cErr := direct.NewOutbound(ctx, router, logFactory.NewLogger("outbound/direct"), "direct", option.DirectOutboundOptions{})
|
out, oErr := outbound.New(ctx, router, logFactory.NewLogger("outbound/direct"), "direct", option.Outbound{Type: "direct", Tag: "default"})
|
||||||
common.Must(cErr)
|
common.Must(oErr)
|
||||||
outbounds = append(outbounds, defaultOutbound)
|
outbounds = append(outbounds, out)
|
||||||
return defaultOutbound
|
return out
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if platformInterface != nil {
|
if options.PlatformInterface != nil {
|
||||||
err = platformInterface.Initialize(ctx, router)
|
err = options.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")
|
||||||
}
|
}
|
||||||
|
|||||||
Submodule clients/android updated: 45a1f5f0aa...cff12c57dd
Submodule clients/apple updated: c7d9b49de7...fa107e3b7c
@@ -58,7 +58,7 @@ func FindSDK() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func findNDK() bool {
|
func findNDK() bool {
|
||||||
const fixedVersion = "27.2.12479018"
|
const fixedVersion = "26.2.11394342"
|
||||||
const versionFile = "source.properties"
|
const versionFile = "source.properties"
|
||||||
if fixedPath := filepath.Join(androidSDKPath, "ndk", fixedVersion); rw.IsFile(filepath.Join(fixedPath, versionFile)) {
|
if fixedPath := filepath.Join(androidSDKPath, "ndk", fixedVersion); rw.IsFile(filepath.Join(fixedPath, versionFile)) {
|
||||||
androidNDKPath = fixedPath
|
androidNDKPath = fixedPath
|
||||||
|
|||||||
@@ -7,9 +7,8 @@ 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,6 +67,5 @@ func preRun(cmd *cobra.Command, args []string) {
|
|||||||
if len(configPaths) == 0 && len(configDirectories) == 0 {
|
if len(configPaths) == 0 && len(configDirectories) == 0 {
|
||||||
configPaths = append(configPaths, "config.json")
|
configPaths = append(configPaths, "config.json")
|
||||||
}
|
}
|
||||||
globalCtx = service.ContextWith(globalCtx, deprecated.NewEnvManager(log.StdLogger()))
|
globalCtx = service.ContextWith(globalCtx, deprecated.NewStderrManager(log.StdLogger()))
|
||||||
globalCtx = box.Context(globalCtx, include.InboundRegistry(), include.OutboundRegistry())
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func check() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(globalCtx)
|
||||||
instance, err := box.New(box.Options{
|
instance, err := box.New(box.Options{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
Options: options,
|
Options: options,
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
@@ -39,7 +38,7 @@ func format() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, optionsEntry := range optionsList {
|
for _, optionsEntry := range optionsList {
|
||||||
optionsEntry.options, err = badjson.Omitempty(context.TODO(), optionsEntry.options)
|
optionsEntry.options, err = badjson.Omitempty(optionsEntry.options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,19 +68,29 @@ func merge(outputPath string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func mergePathResources(options *option.Options) error {
|
func mergePathResources(options *option.Options) error {
|
||||||
for _, inbound := range options.Inbounds {
|
for index, inbound := range options.Inbounds {
|
||||||
if tlsOptions, containsTLSOptions := inbound.Options.(option.InboundTLSOptionsWrapper); containsTLSOptions {
|
rawOptions, err := inbound.RawOptions()
|
||||||
|
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 _, outbound := range options.Outbounds {
|
for index, 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:
|
||||||
mergeSSHOutboundOptions(outbound.Options.(*option.SSHOutboundOptions))
|
outbound.SSHOptions = mergeSSHOutboundOptions(outbound.SSHOptions)
|
||||||
}
|
}
|
||||||
if tlsOptions, containsTLSOptions := outbound.Options.(option.OutboundTLSOptionsWrapper); containsTLSOptions {
|
if tlsOptions, containsTLSOptions := rawOptions.(option.OutboundTLSOptionsWrapper); containsTLSOptions {
|
||||||
tlsOptions.ReplaceOutboundTLSOptions(mergeTLSOutboundOptions(tlsOptions.TakeOutboundTLSOptions()))
|
tlsOptions.ReplaceOutboundTLSOptions(mergeTLSOutboundOptions(tlsOptions.TakeOutboundTLSOptions()))
|
||||||
}
|
}
|
||||||
|
options.Outbounds[index] = outbound
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -128,12 +138,13 @@ func mergeTLSOutboundOptions(options *option.OutboundTLSOptions) *option.Outboun
|
|||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
func mergeSSHOutboundOptions(options *option.SSHOutboundOptions) {
|
func mergeSSHOutboundOptions(options option.SSHOutboundOptions) 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 {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/srs"
|
"github.com/sagernet/sing-box/common/srs"
|
||||||
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/json"
|
"github.com/sagernet/sing/common/json"
|
||||||
@@ -56,10 +55,6 @@ func compileRuleSet(sourcePath string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ruleSet, err := plainRuleSet.Upgrade()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var outputPath string
|
var outputPath string
|
||||||
if flagRuleSetCompileOutput == flagRuleSetCompileDefaultOutput {
|
if flagRuleSetCompileOutput == flagRuleSetCompileDefaultOutput {
|
||||||
if strings.HasSuffix(sourcePath, ".json") {
|
if strings.HasSuffix(sourcePath, ".json") {
|
||||||
@@ -74,7 +69,7 @@ func compileRuleSet(sourcePath string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = srs.Write(outputFile, ruleSet, plainRuleSet.Version == C.RuleSetVersion2)
|
err = srs.Write(outputFile, plainRuleSet.Options, plainRuleSet.Version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outputFile.Close()
|
outputFile.Close()
|
||||||
os.Remove(outputPath)
|
os.Remove(outputPath)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/sagernet/sing-box/cmd/sing-box/internal/convertor/adguard"
|
"github.com/sagernet/sing-box/cmd/sing-box/internal/convertor/adguard"
|
||||||
"github.com/sagernet/sing-box/common/srs"
|
"github.com/sagernet/sing-box/common/srs"
|
||||||
|
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"
|
||||||
@@ -77,7 +78,7 @@ func convertRuleSet(sourcePath string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer outputFile.Close()
|
defer outputFile.Close()
|
||||||
err = srs.Write(outputFile, option.PlainRuleSet{Rules: rules}, true)
|
err = srs.Write(outputFile, option.PlainRuleSet{Rules: rules}, C.RuleSetVersion2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outputFile.Close()
|
outputFile.Close()
|
||||||
os.Remove(outputPath)
|
os.Remove(outputPath)
|
||||||
|
|||||||
@@ -6,9 +6,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/srs"
|
"github.com/sagernet/sing-box/common/srs"
|
||||||
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/common/json"
|
"github.com/sagernet/sing/common/json"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@@ -48,14 +46,10 @@ func decompileRuleSet(sourcePath string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
plainRuleSet, err := srs.Read(reader, true)
|
ruleSet, err := srs.Read(reader, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ruleSet := option.PlainRuleSetCompat{
|
|
||||||
Version: C.RuleSetVersion1,
|
|
||||||
Options: plainRuleSet,
|
|
||||||
}
|
|
||||||
var outputPath string
|
var outputPath string
|
||||||
if flagRuleSetDecompileOutput == flagRuleSetDecompileDefaultOutput {
|
if flagRuleSetDecompileOutput == flagRuleSetDecompileDefaultOutput {
|
||||||
if strings.HasSuffix(sourcePath, ".srs") {
|
if strings.HasSuffix(sourcePath, ".srs") {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
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/route/rule"
|
"github.com/sagernet/sing-box/route"
|
||||||
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"
|
||||||
@@ -55,26 +55,25 @@ func ruleSetMatch(sourcePath string, domain string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "read rule-set")
|
return E.Cause(err, "read rule-set")
|
||||||
}
|
}
|
||||||
var plainRuleSet option.PlainRuleSet
|
var ruleSet option.PlainRuleSetCompat
|
||||||
switch flagRuleSetMatchFormat {
|
switch flagRuleSetMatchFormat {
|
||||||
case C.RuleSetFormatSource:
|
case C.RuleSetFormatSource:
|
||||||
var compat option.PlainRuleSetCompat
|
ruleSet, err = json.UnmarshalExtended[option.PlainRuleSetCompat](content)
|
||||||
compat, err = json.UnmarshalExtended[option.PlainRuleSetCompat](content)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
plainRuleSet, err = compat.Upgrade()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case C.RuleSetFormatBinary:
|
case C.RuleSetFormatBinary:
|
||||||
plainRuleSet, err = srs.Read(bytes.NewReader(content), false)
|
ruleSet, err = srs.Read(bytes.NewReader(content), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return E.New("unknown rule-set format: ", flagRuleSetMatchFormat)
|
return E.New("unknown rule-set format: ", flagRuleSetMatchFormat)
|
||||||
}
|
}
|
||||||
|
plainRuleSet, err := ruleSet.Upgrade()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
ipAddress := M.ParseAddr(domain)
|
ipAddress := M.ParseAddr(domain)
|
||||||
var metadata adapter.InboundContext
|
var metadata adapter.InboundContext
|
||||||
if ipAddress.IsValid() {
|
if ipAddress.IsValid() {
|
||||||
@@ -84,7 +83,7 @@ func ruleSetMatch(sourcePath string, domain string) error {
|
|||||||
}
|
}
|
||||||
for i, ruleOptions := range plainRuleSet.Rules {
|
for i, ruleOptions := range plainRuleSet.Rules {
|
||||||
var currentRule adapter.HeadlessRule
|
var currentRule adapter.HeadlessRule
|
||||||
currentRule, err = rule.NewHeadlessRule(nil, ruleOptions)
|
currentRule, err = route.NewHeadlessRule(nil, ruleOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "parse rule_set.rules.[", i, "]")
|
return E.Cause(err, "parse rule_set.rules.[", i, "]")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.UnmarshalExtendedContext[option.Options](globalCtx, configContent)
|
options, err := json.UnmarshalExtended[option.Options](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(globalCtx, options.options.RawMessage, mergedMessage, false)
|
mergedMessage, err = badjson.MergeJSON(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.UnmarshalJSONContext(globalCtx, mergedMessage)
|
err = mergedOptions.UnmarshalJSON(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")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box"
|
"github.com/sagernet/sing-box"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
@@ -26,9 +23,7 @@ func init() {
|
|||||||
func createPreStartedClient() (*box.Box, error) {
|
func createPreStartedClient() (*box.Box, error) {
|
||||||
options, err := readConfigAndMerge()
|
options, err := readConfigAndMerge()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !(errors.Is(err, os.ErrNotExist) && len(configDirectories) == 0 && len(configPaths) == 1) || configPaths[0] != "config.json" {
|
return nil, err
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
instance, err := box.New(box.Options{Options: options})
|
instance, err := box.New(box.Options{Options: options})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/route/rule"
|
"github.com/sagernet/sing-box/route"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@@ -26,7 +26,7 @@ example.arpa
|
|||||||
`))
|
`))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, rules, 1)
|
require.Len(t, rules, 1)
|
||||||
rule, err := rule.NewHeadlessRule(nil, rules[0])
|
rule, err := route.NewHeadlessRule(nil, rules[0])
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
matchDomain := []string{
|
matchDomain := []string{
|
||||||
"example.org",
|
"example.org",
|
||||||
@@ -85,7 +85,7 @@ func TestHosts(t *testing.T) {
|
|||||||
`))
|
`))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, rules, 1)
|
require.Len(t, rules, 1)
|
||||||
rule, err := rule.NewHeadlessRule(nil, rules[0])
|
rule, err := route.NewHeadlessRule(nil, rules[0])
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
matchDomain := []string{
|
matchDomain := []string{
|
||||||
"google.com",
|
"google.com",
|
||||||
@@ -115,7 +115,7 @@ www.example.org
|
|||||||
`))
|
`))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, rules, 1)
|
require.Len(t, rules, 1)
|
||||||
rule, err := rule.NewHeadlessRule(nil, rules[0])
|
rule, err := route.NewHeadlessRule(nil, rules[0])
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
matchDomain := []string{
|
matchDomain := []string{
|
||||||
"example.com",
|
"example.com",
|
||||||
|
|||||||
@@ -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,12 +2,8 @@ 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
|
|
||||||
|
|||||||
11
common/dialer/wireguard_control.go
Normal file
11
common/dialer/wireguard_control.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
//go:build with_wireguard
|
||||||
|
|
||||||
|
package dialer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sagernet/wireguard-go/conn"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ WireGuardListener = (conn.Listener)(nil)
|
||||||
|
|
||||||
|
var wgControlFns = conn.ControlFns
|
||||||
9
common/dialer/wiregurad_stub.go
Normal file
9
common/dialer/wiregurad_stub.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
//go:build !with_wireguard
|
||||||
|
|
||||||
|
package dialer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sagernet/sing/common/control"
|
||||||
|
)
|
||||||
|
|
||||||
|
var wgControlFns []control.Func
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
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,16 +0,0 @@
|
|||||||
//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,15 +0,0 @@
|
|||||||
//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))
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
package listener
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing/common/buf"
|
|
||||||
"github.com/sagernet/sing/common/control"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (l *Listener) ListenUDP() (net.PacketConn, error) {
|
|
||||||
bindAddr := M.SocksaddrFrom(l.listenOptions.Listen.Build(), l.listenOptions.ListenPort)
|
|
||||||
var lc net.ListenConfig
|
|
||||||
var udpFragment bool
|
|
||||||
if l.listenOptions.UDPFragment != nil {
|
|
||||||
udpFragment = *l.listenOptions.UDPFragment
|
|
||||||
} else {
|
|
||||||
udpFragment = l.listenOptions.UDPFragmentDefault
|
|
||||||
}
|
|
||||||
if !udpFragment {
|
|
||||||
lc.Control = control.Append(lc.Control, control.DisableUDPFragment())
|
|
||||||
}
|
|
||||||
udpConn, err := lc.ListenPacket(l.ctx, M.NetworkFromNetAddr(N.NetworkUDP, bindAddr.Addr), bindAddr.String())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
l.udpConn = udpConn.(*net.UDPConn)
|
|
||||||
l.udpAddr = bindAddr
|
|
||||||
l.logger.Info("udp server started at ", udpConn.LocalAddr())
|
|
||||||
return udpConn, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Listener) UDPAddr() M.Socksaddr {
|
|
||||||
return l.udpAddr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Listener) PacketWriter() N.PacketWriter {
|
|
||||||
return (*packetWriter)(l)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Listener) loopUDPIn() {
|
|
||||||
defer close(l.packetOutboundClosed)
|
|
||||||
var buffer *buf.Buffer
|
|
||||||
if !l.threadUnsafePacketWriter {
|
|
||||||
buffer = buf.NewPacket()
|
|
||||||
defer buffer.Release()
|
|
||||||
}
|
|
||||||
buffer.IncRef()
|
|
||||||
defer buffer.DecRef()
|
|
||||||
if l.oobPacketHandler != nil {
|
|
||||||
oob := make([]byte, 1024)
|
|
||||||
for {
|
|
||||||
if l.threadUnsafePacketWriter {
|
|
||||||
buffer = buf.NewPacket()
|
|
||||||
} else {
|
|
||||||
buffer.Reset()
|
|
||||||
}
|
|
||||||
n, oobN, _, addr, err := l.udpConn.ReadMsgUDPAddrPort(buffer.FreeBytes(), oob)
|
|
||||||
if err != nil {
|
|
||||||
if l.threadUnsafePacketWriter {
|
|
||||||
buffer.Release()
|
|
||||||
}
|
|
||||||
if l.shutdown.Load() && E.IsClosed(err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
l.udpConn.Close()
|
|
||||||
l.logger.Error("udp listener closed: ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
buffer.Truncate(n)
|
|
||||||
l.oobPacketHandler.NewPacketEx(buffer, oob[:oobN], M.SocksaddrFromNetIP(addr).Unwrap())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for {
|
|
||||||
if l.threadUnsafePacketWriter {
|
|
||||||
buffer = buf.NewPacket()
|
|
||||||
} else {
|
|
||||||
buffer.Reset()
|
|
||||||
}
|
|
||||||
n, addr, err := l.udpConn.ReadFromUDPAddrPort(buffer.FreeBytes())
|
|
||||||
if err != nil {
|
|
||||||
if l.threadUnsafePacketWriter {
|
|
||||||
buffer.Release()
|
|
||||||
}
|
|
||||||
if l.shutdown.Load() && E.IsClosed(err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
l.udpConn.Close()
|
|
||||||
l.logger.Error("udp listener closed: ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
buffer.Truncate(n)
|
|
||||||
l.packetHandler.NewPacketEx(buffer, M.SocksaddrFromNetIP(addr).Unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Listener) loopUDPOut() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case packet := <-l.packetOutbound:
|
|
||||||
destination := packet.Destination.AddrPort()
|
|
||||||
_, err := l.udpConn.WriteToUDPAddrPort(packet.Buffer.Bytes(), destination)
|
|
||||||
packet.Buffer.Release()
|
|
||||||
N.PutPacketBuffer(packet)
|
|
||||||
if err != nil {
|
|
||||||
if l.shutdown.Load() && E.IsClosed(err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
l.udpConn.Close()
|
|
||||||
l.logger.Error("udp listener write back: ", destination, ": ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
case <-l.packetOutboundClosed:
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case packet := <-l.packetOutbound:
|
|
||||||
packet.Buffer.Release()
|
|
||||||
N.PutPacketBuffer(packet)
|
|
||||||
case <-time.After(time.Second):
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type packetWriter Listener
|
|
||||||
|
|
||||||
func (w *packetWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
|
||||||
packet := N.NewPacketBuffer()
|
|
||||||
packet.Buffer = buffer
|
|
||||||
packet.Destination = destination
|
|
||||||
select {
|
|
||||||
case w.packetOutbound <- packet:
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
buffer.Release()
|
|
||||||
N.PutPacketBuffer(packet)
|
|
||||||
if w.shutdown.Load() {
|
|
||||||
return os.ErrClosed
|
|
||||||
}
|
|
||||||
w.logger.Trace("dropped packet to ", destination)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *packetWriter) WriteIsThreadUnsafe() {
|
|
||||||
}
|
|
||||||
@@ -15,11 +15,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Router struct {
|
type Router struct {
|
||||||
router adapter.ConnectionRouterEx
|
router adapter.ConnectionRouter
|
||||||
service *mux.Service
|
service *mux.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRouterWithOptions(router adapter.ConnectionRouterEx, logger logger.ContextLogger, options option.InboundMultiplexOptions) (adapter.ConnectionRouterEx, error) {
|
func NewRouterWithOptions(router adapter.ConnectionRouter, logger logger.ContextLogger, options option.InboundMultiplexOptions) (adapter.ConnectionRouter, error) {
|
||||||
if !options.Enabled {
|
if !options.Enabled {
|
||||||
return router, nil
|
return router, nil
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,6 @@ func NewRouterWithOptions(router adapter.ConnectionRouterEx, logger logger.Conte
|
|||||||
|
|
||||||
func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
if metadata.Destination == mux.Destination {
|
if metadata.Destination == mux.Destination {
|
||||||
// TODO: check if WithContext is necessary
|
|
||||||
return r.service.NewConnection(adapter.WithContext(ctx, &metadata), conn, adapter.UpstreamMetadata(metadata))
|
return r.service.NewConnection(adapter.WithContext(ctx, &metadata), conn, adapter.UpstreamMetadata(metadata))
|
||||||
} else {
|
} else {
|
||||||
return r.router.RouteConnection(ctx, conn, metadata)
|
return r.router.RouteConnection(ctx, conn, metadata)
|
||||||
@@ -64,15 +63,3 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
|
|||||||
func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
return r.router.RoutePacketConnection(ctx, conn, metadata)
|
return r.router.RoutePacketConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) RouteConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
|
||||||
if metadata.Destination == mux.Destination {
|
|
||||||
r.service.NewConnectionEx(adapter.WithContext(ctx, &metadata), conn, metadata.Source, metadata.Destination, onClose)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Router) RoutePacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
|
||||||
r.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
|
||||||
}
|
|
||||||
|
|||||||
32
common/mux/v2ray_legacy.go
Normal file
32
common/mux/v2ray_legacy.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package mux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
vmess "github.com/sagernet/sing-vmess"
|
||||||
|
"github.com/sagernet/sing/common/logger"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
type V2RayLegacyRouter struct {
|
||||||
|
router adapter.ConnectionRouter
|
||||||
|
logger logger.ContextLogger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewV2RayLegacyRouter(router adapter.ConnectionRouter, logger logger.ContextLogger) adapter.ConnectionRouter {
|
||||||
|
return &V2RayLegacyRouter{router, logger}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *V2RayLegacyRouter) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
|
if metadata.Destination.Fqdn == vmess.MuxDestination.Fqdn {
|
||||||
|
r.logger.InfoContext(ctx, "inbound legacy multiplex connection")
|
||||||
|
return vmess.HandleMuxConnection(ctx, conn, adapter.NewRouteHandler(metadata, r.router, r.logger))
|
||||||
|
}
|
||||||
|
return r.router.RouteConnection(ctx, conn, metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *V2RayLegacyRouter) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
|
return r.router.RoutePacketConnection(ctx, conn, metadata)
|
||||||
|
}
|
||||||
@@ -18,7 +18,7 @@ type (
|
|||||||
PacketSniffer = func(ctx context.Context, metadata *adapter.InboundContext, packet []byte) error
|
PacketSniffer = func(ctx context.Context, metadata *adapter.InboundContext, packet []byte) error
|
||||||
)
|
)
|
||||||
|
|
||||||
func Skip(metadata *adapter.InboundContext) bool {
|
func Skip(metadata adapter.InboundContext) bool {
|
||||||
// skip server first protocols
|
// skip server first protocols
|
||||||
switch metadata.Destination.Port {
|
switch metadata.Destination.Port {
|
||||||
case 25, 465, 587:
|
case 25, 465, 587:
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ const (
|
|||||||
ruleItemFinal uint8 = 0xFF
|
ruleItemFinal uint8 = 0xFF
|
||||||
)
|
)
|
||||||
|
|
||||||
func Read(reader io.Reader, recover bool) (ruleSet option.PlainRuleSet, err error) {
|
func Read(reader io.Reader, recover bool) (ruleSetCompat option.PlainRuleSetCompat, err error) {
|
||||||
var magicBytes [3]byte
|
var magicBytes [3]byte
|
||||||
_, err = io.ReadFull(reader, magicBytes[:])
|
_, err = io.ReadFull(reader, magicBytes[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -54,10 +54,10 @@ func Read(reader io.Reader, recover bool) (ruleSet option.PlainRuleSet, err erro
|
|||||||
var version uint8
|
var version uint8
|
||||||
err = binary.Read(reader, binary.BigEndian, &version)
|
err = binary.Read(reader, binary.BigEndian, &version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ruleSet, err
|
return ruleSetCompat, err
|
||||||
}
|
}
|
||||||
if version > C.RuleSetVersion2 {
|
if version > C.RuleSetVersionCurrent {
|
||||||
return ruleSet, E.New("unsupported version: ", version)
|
return ruleSetCompat, E.New("unsupported version: ", version)
|
||||||
}
|
}
|
||||||
compressReader, err := zlib.NewReader(reader)
|
compressReader, err := zlib.NewReader(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -68,9 +68,10 @@ func Read(reader io.Reader, recover bool) (ruleSet option.PlainRuleSet, err erro
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ruleSet.Rules = make([]option.HeadlessRule, length)
|
ruleSetCompat.Version = version
|
||||||
|
ruleSetCompat.Options.Rules = make([]option.HeadlessRule, length)
|
||||||
for i := uint64(0); i < length; i++ {
|
for i := uint64(0); i < length; i++ {
|
||||||
ruleSet.Rules[i], err = readRule(bReader, recover)
|
ruleSetCompat.Options.Rules[i], err = readRule(bReader, recover)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = E.Cause(err, "read rule[", i, "]")
|
err = E.Cause(err, "read rule[", i, "]")
|
||||||
return
|
return
|
||||||
@@ -79,18 +80,12 @@ func Read(reader io.Reader, recover bool) (ruleSet option.PlainRuleSet, err erro
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func Write(writer io.Writer, ruleSet option.PlainRuleSet, generateUnstable bool) error {
|
func Write(writer io.Writer, ruleSet option.PlainRuleSet, generateVersion uint8) error {
|
||||||
_, err := writer.Write(MagicBytes[:])
|
_, err := writer.Write(MagicBytes[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var version uint8
|
err = binary.Write(writer, binary.BigEndian, generateVersion)
|
||||||
if generateUnstable {
|
|
||||||
version = C.RuleSetVersion2
|
|
||||||
} else {
|
|
||||||
version = C.RuleSetVersion1
|
|
||||||
}
|
|
||||||
err = binary.Write(writer, binary.BigEndian, version)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -104,7 +99,7 @@ func Write(writer io.Writer, ruleSet option.PlainRuleSet, generateUnstable bool)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, rule := range ruleSet.Rules {
|
for _, rule := range ruleSet.Rules {
|
||||||
err = writeRule(bWriter, rule, generateUnstable)
|
err = writeRule(bWriter, rule, generateVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -135,12 +130,12 @@ func readRule(reader varbin.Reader, recover bool) (rule option.HeadlessRule, err
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeRule(writer varbin.Writer, rule option.HeadlessRule, generateUnstable bool) error {
|
func writeRule(writer varbin.Writer, rule option.HeadlessRule, generateVersion uint8) error {
|
||||||
switch rule.Type {
|
switch rule.Type {
|
||||||
case C.RuleTypeDefault:
|
case C.RuleTypeDefault:
|
||||||
return writeDefaultRule(writer, rule.DefaultOptions, generateUnstable)
|
return writeDefaultRule(writer, rule.DefaultOptions, generateVersion)
|
||||||
case C.RuleTypeLogical:
|
case C.RuleTypeLogical:
|
||||||
return writeLogicalRule(writer, rule.LogicalOptions, generateUnstable)
|
return writeLogicalRule(writer, rule.LogicalOptions, generateVersion)
|
||||||
default:
|
default:
|
||||||
panic("unknown rule type: " + rule.Type)
|
panic("unknown rule type: " + rule.Type)
|
||||||
}
|
}
|
||||||
@@ -240,7 +235,7 @@ func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHea
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, generateUnstable bool) error {
|
func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, generateVersion uint8) error {
|
||||||
err := binary.Write(writer, binary.BigEndian, uint8(0))
|
err := binary.Write(writer, binary.BigEndian, uint8(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -264,7 +259,7 @@ func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, gen
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = domain.NewMatcher(rule.Domain, rule.DomainSuffix, !generateUnstable).Write(writer)
|
err = domain.NewMatcher(rule.Domain, rule.DomainSuffix, generateVersion == C.RuleSetVersion1).Write(writer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -354,6 +349,9 @@ func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, gen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(rule.AdGuardDomain) > 0 {
|
if len(rule.AdGuardDomain) > 0 {
|
||||||
|
if generateVersion < C.RuleSetVersion2 {
|
||||||
|
return E.New("AdGuard rule items is only supported in version 2 or later")
|
||||||
|
}
|
||||||
err = binary.Write(writer, binary.BigEndian, ruleItemAdGuardDomain)
|
err = binary.Write(writer, binary.BigEndian, ruleItemAdGuardDomain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -457,7 +455,7 @@ func readLogicalRule(reader varbin.Reader, recovery bool) (logicalRule option.Lo
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeLogicalRule(writer varbin.Writer, logicalRule option.LogicalHeadlessRule, generateUnstable bool) error {
|
func writeLogicalRule(writer varbin.Writer, logicalRule option.LogicalHeadlessRule, generateVersion uint8) error {
|
||||||
err := binary.Write(writer, binary.BigEndian, uint8(1))
|
err := binary.Write(writer, binary.BigEndian, uint8(1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -478,7 +476,7 @@ func writeLogicalRule(writer varbin.Writer, logicalRule option.LogicalHeadlessRu
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, rule := range logicalRule.Rules {
|
for _, rule := range logicalRule.Rules {
|
||||||
err = writeRule(writer, rule, generateUnstable)
|
err = writeRule(writer, rule, generateVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,6 +97,10 @@ func (c *echServerConfig) startWatcher() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
err = watcher.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
c.watcher = watcher
|
c.watcher = watcher
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -232,7 +236,7 @@ func NewECHServer(ctx context.Context, logger log.Logger, options option.Inbound
|
|||||||
var echKey []byte
|
var echKey []byte
|
||||||
if len(options.ECH.Key) > 0 {
|
if len(options.ECH.Key) > 0 {
|
||||||
echKey = []byte(strings.Join(options.ECH.Key, "\n"))
|
echKey = []byte(strings.Join(options.ECH.Key, "\n"))
|
||||||
} else if options.KeyPath != "" {
|
} else if options.ECH.KeyPath != "" {
|
||||||
content, err := os.ReadFile(options.ECH.KeyPath)
|
content, err := os.ReadFile(options.ECH.KeyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "read ECH key")
|
return nil, E.Cause(err, "read ECH key")
|
||||||
|
|||||||
@@ -106,6 +106,10 @@ func (c *STDServerConfig) startWatcher() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
err = watcher.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
c.watcher = watcher
|
c.watcher = watcher
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,14 +13,14 @@ import (
|
|||||||
"github.com/sagernet/sing/common/uot"
|
"github.com/sagernet/sing/common/uot"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ adapter.ConnectionRouterEx = (*Router)(nil)
|
var _ adapter.ConnectionRouter = (*Router)(nil)
|
||||||
|
|
||||||
type Router struct {
|
type Router struct {
|
||||||
router adapter.ConnectionRouterEx
|
router adapter.ConnectionRouter
|
||||||
logger logger.ContextLogger
|
logger logger.ContextLogger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRouter(router adapter.ConnectionRouterEx, logger logger.ContextLogger) *Router {
|
func NewRouter(router adapter.ConnectionRouter, logger logger.ContextLogger) *Router {
|
||||||
return &Router{router, logger}
|
return &Router{router, logger}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,36 +51,3 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
|
|||||||
func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
return r.router.RoutePacketConnection(ctx, conn, metadata)
|
return r.router.RoutePacketConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) RouteConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
|
||||||
switch metadata.Destination.Fqdn {
|
|
||||||
case uot.MagicAddress:
|
|
||||||
request, err := uot.ReadRequest(conn)
|
|
||||||
if err != nil {
|
|
||||||
err = E.Cause(err, "UoT read request")
|
|
||||||
r.logger.ErrorContext(ctx, "process connection from ", metadata.Source, ": ", err)
|
|
||||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if request.IsConnect {
|
|
||||||
r.logger.InfoContext(ctx, "inbound UoT connect connection to ", request.Destination)
|
|
||||||
} else {
|
|
||||||
r.logger.InfoContext(ctx, "inbound UoT connection to ", request.Destination)
|
|
||||||
}
|
|
||||||
metadata.Domain = metadata.Destination.Fqdn
|
|
||||||
metadata.Destination = request.Destination
|
|
||||||
r.router.RoutePacketConnectionEx(ctx, uot.NewConn(conn, *request), metadata, onClose)
|
|
||||||
return
|
|
||||||
case uot.LegacyMagicAddress:
|
|
||||||
r.logger.InfoContext(ctx, "inbound legacy UoT connection")
|
|
||||||
metadata.Domain = metadata.Destination.Fqdn
|
|
||||||
metadata.Destination = M.Socksaddr{Addr: netip.IPv4Unspecified()}
|
|
||||||
r.RoutePacketConnectionEx(ctx, uot.NewConn(conn, uot.Request{}), metadata, onClose)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Router) RoutePacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
|
||||||
r.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -21,22 +21,5 @@ const (
|
|||||||
const (
|
const (
|
||||||
RuleSetVersion1 = 1 + iota
|
RuleSetVersion1 = 1 + iota
|
||||||
RuleSetVersion2
|
RuleSetVersion2
|
||||||
)
|
RuleSetVersionCurrent = RuleSetVersion2
|
||||||
|
|
||||||
const (
|
|
||||||
RuleActionTypeRoute = "route"
|
|
||||||
RuleActionTypeReturn = "return"
|
|
||||||
RuleActionTypeReject = "reject"
|
|
||||||
RuleActionTypeHijackDNS = "hijack-dns"
|
|
||||||
RuleActionTypeSniff = "sniff"
|
|
||||||
RuleActionTypeResolve = "resolve"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
RuleActionRejectMethodDefault = "default"
|
|
||||||
RuleActionRejectMethodReset = "reset"
|
|
||||||
RuleActionRejectMethodNetworkUnreachable = "network-unreachable"
|
|
||||||
RuleActionRejectMethodHostUnreachable = "host-unreachable"
|
|
||||||
RuleActionRejectMethodPortUnreachable = "port-unreachable"
|
|
||||||
RuleActionRejectMethodDrop = "drop"
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ func applyDebugListenOption(options option.DebugOptions) {
|
|||||||
|
|
||||||
encoder := json.NewEncoder(writer)
|
encoder := json.NewEncoder(writer)
|
||||||
encoder.SetIndent("", " ")
|
encoder.SetIndent("", " ")
|
||||||
encoder.Encode(memObject)
|
encoder.Encode(&memObject)
|
||||||
})
|
})
|
||||||
r.Route("/pprof", func(r chi.Router) {
|
r.Route("/pprof", func(r chi.Router) {
|
||||||
r.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
|
r.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
|||||||
@@ -2,18 +2,14 @@
|
|||||||
icon: material/alert-decagram
|
icon: material/alert-decagram
|
||||||
---
|
---
|
||||||
|
|
||||||
#### 1.11.0-alpha.5
|
### 1.10.3
|
||||||
|
|
||||||
* Fixes and improvements
|
* Fixes and improvements
|
||||||
|
|
||||||
#### 1.11.0-alpha.2
|
### 1.10.2
|
||||||
|
|
||||||
* Add warnings for usage of deprecated features
|
* Add deprecated warnings
|
||||||
* Fixes and improvements
|
* Fix proxying websocket connections in HTTP/mixed inbounds
|
||||||
|
|
||||||
#### 1.11.0-alpha.1
|
|
||||||
|
|
||||||
* Update quic-go to v0.48.0
|
|
||||||
* Fixes and improvements
|
* Fixes and improvements
|
||||||
|
|
||||||
### 1.10.1
|
### 1.10.1
|
||||||
@@ -47,7 +43,7 @@ Important changes since 1.9:
|
|||||||
The new auto-redirect feature allows TUN to automatically
|
The new auto-redirect feature allows TUN to automatically
|
||||||
configure connection redirection to improve proxy performance.
|
configure connection redirection to improve proxy performance.
|
||||||
|
|
||||||
When auto-redirect is enabled, new route address set options will allow you to
|
When auto-redirect is enabled, new route address set options will allow you to
|
||||||
automatically configure destination IP CIDR rules from a specified rule set to the firewall.
|
automatically configure destination IP CIDR rules from a specified rule set to the firewall.
|
||||||
|
|
||||||
Specified or unspecified destinations will bypass the sing-box routes to get better performance
|
Specified or unspecified destinations will bypass the sing-box routes to get better performance
|
||||||
|
|||||||
@@ -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/protocol/group"
|
"github.com/sagernet/sing-box/outbound"
|
||||||
"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)
|
||||||
outboundGroup, ok := proxy.(adapter.OutboundGroup)
|
group, 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 := outboundGroup.(adapter.URLTestGroup); isURLTestGroup {
|
if urlTestGroup, isURLTestGroup := group.(adapter.URLTestGroup); isURLTestGroup {
|
||||||
result, err = urlTestGroup.URLTest(ctx)
|
result, err = urlTestGroup.URLTest(ctx)
|
||||||
} else {
|
} else {
|
||||||
outbounds := common.FilterNotNil(common.Map(outboundGroup.All(), func(it string) adapter.Outbound {
|
outbounds := common.FilterNotNil(common.Map(group.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 := group.RealTag(detour)
|
realTag := outbound.RealTag(detour)
|
||||||
if checked[realTag] {
|
if checked[realTag] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,17 +18,19 @@ func configRouter(server *Server, logFactory log.Factory) http.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type configSchema struct {
|
type configSchema struct {
|
||||||
Port int `json:"port"`
|
Port int `json:"port"`
|
||||||
SocksPort int `json:"socks-port"`
|
SocksPort int `json:"socks-port"`
|
||||||
RedirPort int `json:"redir-port"`
|
RedirPort int `json:"redir-port"`
|
||||||
TProxyPort int `json:"tproxy-port"`
|
TProxyPort int `json:"tproxy-port"`
|
||||||
MixedPort int `json:"mixed-port"`
|
MixedPort int `json:"mixed-port"`
|
||||||
AllowLan bool `json:"allow-lan"`
|
AllowLan bool `json:"allow-lan"`
|
||||||
BindAddress string `json:"bind-address"`
|
BindAddress string `json:"bind-address"`
|
||||||
Mode string `json:"mode"`
|
Mode string `json:"mode"`
|
||||||
LogLevel string `json:"log-level"`
|
// sing-box added
|
||||||
IPv6 bool `json:"ipv6"`
|
ModeList []string `json:"mode-list"`
|
||||||
Tun map[string]any `json:"tun"`
|
LogLevel string `json:"log-level"`
|
||||||
|
IPv6 bool `json:"ipv6"`
|
||||||
|
Tun map[string]any `json:"tun"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func getConfigs(server *Server, logFactory log.Factory) func(w http.ResponseWriter, r *http.Request) {
|
func getConfigs(server *Server, logFactory log.Factory) func(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -41,6 +43,7 @@ func getConfigs(server *Server, logFactory log.Factory) func(w http.ResponseWrit
|
|||||||
}
|
}
|
||||||
render.JSON(w, r, &configSchema{
|
render.JSON(w, r, &configSchema{
|
||||||
Mode: server.mode,
|
Mode: server.mode,
|
||||||
|
ModeList: server.modeList,
|
||||||
BindAddress: "*",
|
BindAddress: "*",
|
||||||
LogLevel: log.FormatLevel(logLevel),
|
LogLevel: log.FormatLevel(logLevel),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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/protocol/group"
|
"github.com/sagernet/sing-box/outbound"
|
||||||
"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.(*group.Selector)
|
selector, ok := proxy.(*outbound.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 := group.RealTag(proxy)
|
realTag := outbound.RealTag(proxy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
server.urlTestHistory.DeleteURLTestHistory(realTag)
|
server.urlTestHistory.DeleteURLTestHistory(realTag)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -30,9 +30,10 @@ func getRules(router adapter.Router) func(w http.ResponseWriter, r *http.Request
|
|||||||
rules = append(rules, Rule{
|
rules = append(rules, Rule{
|
||||||
Type: rule.Type(),
|
Type: rule.Type(),
|
||||||
Payload: rule.String(),
|
Payload: rule.String(),
|
||||||
Proxy: rule.Action().String(),
|
Proxy: rule.Outbound(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
render.JSON(w, r, render.M{
|
render.JSON(w, r, render.M{
|
||||||
"rules": rules,
|
"rules": rules,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
R "github.com/sagernet/sing-box/route/rule"
|
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/atomic"
|
"github.com/sagernet/sing/common/atomic"
|
||||||
"github.com/sagernet/sing/common/bufio"
|
"github.com/sagernet/sing/common/bufio"
|
||||||
@@ -61,7 +60,7 @@ func (t TrackerMetadata) MarshalJSON() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
var rule string
|
var rule string
|
||||||
if t.Rule != nil {
|
if t.Rule != nil {
|
||||||
rule = F.ToString(t.Rule, " => ", t.Rule.Action())
|
rule = F.ToString(t.Rule, " => ", t.Rule.Outbound())
|
||||||
} else {
|
} else {
|
||||||
rule = "final"
|
rule = "final"
|
||||||
}
|
}
|
||||||
@@ -132,21 +131,19 @@ func NewTCPTracker(conn net.Conn, manager *Manager, metadata adapter.InboundCont
|
|||||||
outbound string
|
outbound string
|
||||||
outboundType string
|
outboundType string
|
||||||
)
|
)
|
||||||
var action adapter.RuleAction
|
if rule == nil {
|
||||||
if rule != nil {
|
if defaultOutbound, err := router.DefaultOutbound(N.NetworkTCP); err == nil {
|
||||||
action = rule.Action()
|
next = defaultOutbound.Tag()
|
||||||
}
|
}
|
||||||
if routeAction, isRouteAction := action.(*R.RuleActionRoute); isRouteAction {
|
} else {
|
||||||
next = routeAction.Outbound
|
next = rule.Outbound()
|
||||||
} else if defaultOutbound, err := router.DefaultOutbound(N.NetworkTCP); err == nil {
|
|
||||||
next = defaultOutbound.Tag()
|
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
|
chain = append(chain, next)
|
||||||
detour, loaded := router.Outbound(next)
|
detour, loaded := router.Outbound(next)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
chain = append(chain, next)
|
|
||||||
outbound = detour.Tag()
|
outbound = detour.Tag()
|
||||||
outboundType = detour.Type()
|
outboundType = detour.Type()
|
||||||
group, isGroup := detour.(adapter.OutboundGroup)
|
group, isGroup := detour.(adapter.OutboundGroup)
|
||||||
@@ -221,21 +218,19 @@ func NewUDPTracker(conn N.PacketConn, manager *Manager, metadata adapter.Inbound
|
|||||||
outbound string
|
outbound string
|
||||||
outboundType string
|
outboundType string
|
||||||
)
|
)
|
||||||
var action adapter.RuleAction
|
if rule == nil {
|
||||||
if rule != nil {
|
if defaultOutbound, err := router.DefaultOutbound(N.NetworkUDP); err == nil {
|
||||||
action = rule.Action()
|
next = defaultOutbound.Tag()
|
||||||
}
|
}
|
||||||
if routeAction, isRouteAction := action.(*R.RuleActionRoute); isRouteAction {
|
} else {
|
||||||
next = routeAction.Outbound
|
next = rule.Outbound()
|
||||||
} else if defaultOutbound, err := router.DefaultOutbound(N.NetworkUDP); err == nil {
|
|
||||||
next = defaultOutbound.Tag()
|
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
|
chain = append(chain, next)
|
||||||
detour, loaded := router.Outbound(next)
|
detour, loaded := router.Outbound(next)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
chain = append(chain, next)
|
|
||||||
outbound = detour.Tag()
|
outbound = detour.Tag()
|
||||||
outboundType = detour.Type()
|
outboundType = detour.Type()
|
||||||
group, isGroup := detour.(adapter.OutboundGroup)
|
group, isGroup := detour.(adapter.OutboundGroup)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package deprecated
|
package deprecated
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/sagernet/sing-box/common/badversion"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
F "github.com/sagernet/sing/common/format"
|
F "github.com/sagernet/sing/common/format"
|
||||||
|
|
||||||
@@ -23,8 +24,9 @@ func (n Note) Impending() bool {
|
|||||||
if !semver.IsValid("v" + C.Version) {
|
if !semver.IsValid("v" + C.Version) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
versionMinor := semver.Compare(semver.MajorMinor("v"+C.Version), "v"+n.ScheduledVersion)
|
versionCurrent := badversion.Parse(C.Version)
|
||||||
if versionMinor < 0 {
|
versionMinor := badversion.Parse(n.ScheduledVersion).Minor - versionCurrent.Minor
|
||||||
|
if versionCurrent.PreReleaseIdentifier == "" && versionMinor < 0 {
|
||||||
panic("invalid deprecated note: " + n.Name)
|
panic("invalid deprecated note: " + n.Name)
|
||||||
}
|
}
|
||||||
return versionMinor <= 1
|
return versionMinor <= 1
|
||||||
@@ -49,6 +51,7 @@ var OptionBadMatchSource = Note{
|
|||||||
Description: "legacy match source rule item",
|
Description: "legacy match source rule item",
|
||||||
DeprecatedVersion: "1.10.0",
|
DeprecatedVersion: "1.10.0",
|
||||||
ScheduledVersion: "1.11.0",
|
ScheduledVersion: "1.11.0",
|
||||||
|
EnvName: "BAD_MATCH_SOURCE",
|
||||||
MigrationLink: "https://sing-box.sagernet.org/deprecated/#match-source-rule-items-are-renamed",
|
MigrationLink: "https://sing-box.sagernet.org/deprecated/#match-source-rule-items-are-renamed",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,6 +78,7 @@ var OptionTUNAddressX = Note{
|
|||||||
Description: "legacy tun address fields",
|
Description: "legacy tun address fields",
|
||||||
DeprecatedVersion: "1.10.0",
|
DeprecatedVersion: "1.10.0",
|
||||||
ScheduledVersion: "1.12.0",
|
ScheduledVersion: "1.12.0",
|
||||||
|
EnvName: "TUN_ADDRESS_X",
|
||||||
MigrationLink: "https://sing-box.sagernet.org/migration/#tun-address-fields-are-merged",
|
MigrationLink: "https://sing-box.sagernet.org/migration/#tun-address-fields-are-merged",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,15 +7,23 @@ import (
|
|||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
type envManager struct {
|
type stderrManager struct {
|
||||||
logger logger.Logger
|
logger logger.Logger
|
||||||
|
reported map[string]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEnvManager(logger logger.Logger) Manager {
|
func NewStderrManager(logger logger.Logger) Manager {
|
||||||
return &envManager{logger: logger}
|
return &stderrManager{
|
||||||
|
logger: logger,
|
||||||
|
reported: make(map[string]bool),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *envManager) ReportDeprecated(feature Note) {
|
func (f *stderrManager) ReportDeprecated(feature Note) {
|
||||||
|
if f.reported[feature.Name] {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f.reported[feature.Name] = true
|
||||||
if !feature.Impending() {
|
if !feature.Impending() {
|
||||||
f.logger.Warn(feature.MessageWithLink())
|
f.logger.Warn(feature.MessageWithLink())
|
||||||
return
|
return
|
||||||
@@ -20,7 +20,6 @@ type CommandClient struct {
|
|||||||
type CommandClientOptions struct {
|
type CommandClientOptions struct {
|
||||||
Command int32
|
Command int32
|
||||||
StatusInterval int64
|
StatusInterval int64
|
||||||
IsMainClient bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type CommandClientHandler interface {
|
type CommandClientHandler interface {
|
||||||
@@ -29,7 +28,6 @@ type CommandClientHandler interface {
|
|||||||
ClearLogs()
|
ClearLogs()
|
||||||
WriteLogs(messageList StringIterator)
|
WriteLogs(messageList StringIterator)
|
||||||
WriteStatus(message *StatusMessage)
|
WriteStatus(message *StatusMessage)
|
||||||
OpenURL(url string)
|
|
||||||
WriteGroups(message OutboundGroupIterator)
|
WriteGroups(message OutboundGroupIterator)
|
||||||
InitializeClashMode(modeList StringIterator, currentMode string)
|
InitializeClashMode(modeList StringIterator, currentMode string)
|
||||||
UpdateClashMode(newMode string)
|
UpdateClashMode(newMode string)
|
||||||
@@ -93,13 +91,9 @@ func (c *CommandClient) Connect() error {
|
|||||||
c.handler.Connected()
|
c.handler.Connected()
|
||||||
go c.handleLogConn(conn)
|
go c.handleLogConn(conn)
|
||||||
case CommandStatus:
|
case CommandStatus:
|
||||||
err = binary.Write(conn, binary.BigEndian, c.options.IsMainClient)
|
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "write is main client")
|
|
||||||
}
|
|
||||||
err = binary.Write(conn, binary.BigEndian, c.options.StatusInterval)
|
err = binary.Write(conn, binary.BigEndian, c.options.StatusInterval)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "write header")
|
return E.Cause(err, "write interval")
|
||||||
}
|
}
|
||||||
c.handler.Connected()
|
c.handler.Connected()
|
||||||
go c.handleStatusConn(conn)
|
go c.handleStatusConn(conn)
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
package libbox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
"github.com/sagernet/sing/common/varbin"
|
|
||||||
)
|
|
||||||
|
|
||||||
type myEvent interface {
|
|
||||||
writeTo(writer varbin.Writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func readEvent(reader varbin.Reader) (myEvent, error) {
|
|
||||||
eventType, err := reader.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
switch eventType {
|
|
||||||
case eventTypeEmpty:
|
|
||||||
return nil, nil
|
|
||||||
case eventTypeOpenURL:
|
|
||||||
url, err := varbin.ReadValue[string](reader, binary.BigEndian)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &eventOpenURL{URL: url}, nil
|
|
||||||
default:
|
|
||||||
return nil, E.New("unknown event type: ", eventType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type eventOpenURL struct {
|
|
||||||
URL string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *eventOpenURL) writeTo(writer varbin.Writer) {
|
|
||||||
writer.WriteByte(eventTypeOpenURL)
|
|
||||||
varbin.Write(writer, binary.BigEndian, e.URL)
|
|
||||||
}
|
|
||||||
@@ -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/protocol/group"
|
"github.com/sagernet/sing-box/outbound"
|
||||||
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 outboundGroup OutboundGroup
|
var group OutboundGroup
|
||||||
outboundGroup.Tag = iGroup.Tag()
|
group.Tag = iGroup.Tag()
|
||||||
outboundGroup.Type = iGroup.Type()
|
group.Type = iGroup.Type()
|
||||||
_, outboundGroup.Selectable = iGroup.(*group.Selector)
|
_, group.Selectable = iGroup.(*outbound.Selector)
|
||||||
outboundGroup.Selected = iGroup.Now()
|
group.Selected = iGroup.Now()
|
||||||
if cacheFile != nil {
|
if cacheFile != nil {
|
||||||
if isExpand, loaded := cacheFile.LoadGroupExpand(outboundGroup.Tag); loaded {
|
if isExpand, loaded := cacheFile.LoadGroupExpand(group.Tag); loaded {
|
||||||
outboundGroup.IsExpand = isExpand
|
group.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)
|
||||||
}
|
}
|
||||||
outboundGroup.ItemList = append(outboundGroup.ItemList, &item)
|
group.ItemList = append(group.ItemList, &item)
|
||||||
}
|
}
|
||||||
if len(outboundGroup.ItemList) < 2 {
|
if len(group.ItemList) < 2 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
groups = append(groups, outboundGroup)
|
groups = append(groups, group)
|
||||||
}
|
}
|
||||||
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/protocol/group"
|
"github.com/sagernet/sing-box/outbound"
|
||||||
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.(*group.Selector)
|
selector, isSelector := outboundGroup.(*outbound.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))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ type CommandServer struct {
|
|||||||
urlTestUpdate chan struct{}
|
urlTestUpdate chan struct{}
|
||||||
modeUpdate chan struct{}
|
modeUpdate chan struct{}
|
||||||
logReset chan struct{}
|
logReset chan struct{}
|
||||||
events chan myEvent
|
|
||||||
|
|
||||||
closedConnections []Connection
|
closedConnections []Connection
|
||||||
}
|
}
|
||||||
@@ -53,7 +52,6 @@ func NewCommandServer(handler CommandServerHandler, maxLines int32) *CommandServ
|
|||||||
urlTestUpdate: make(chan struct{}, 1),
|
urlTestUpdate: make(chan struct{}, 1),
|
||||||
modeUpdate: make(chan struct{}, 1),
|
modeUpdate: make(chan struct{}, 1),
|
||||||
logReset: make(chan struct{}, 1),
|
logReset: make(chan struct{}, 1),
|
||||||
events: make(chan myEvent, 8),
|
|
||||||
}
|
}
|
||||||
server.observer = observable.NewObserver[string](server.subscriber, 64)
|
server.observer = observable.NewObserver[string](server.subscriber, 64)
|
||||||
return server
|
return server
|
||||||
@@ -63,12 +61,6 @@ func (s *CommandServer) SetService(newService *BoxService) {
|
|||||||
if newService != nil {
|
if newService != nil {
|
||||||
service.PtrFromContext[urltest.HistoryStorage](newService.ctx).SetHook(s.urlTestUpdate)
|
service.PtrFromContext[urltest.HistoryStorage](newService.ctx).SetHook(s.urlTestUpdate)
|
||||||
newService.instance.Router().ClashServer().(*clashapi.Server).SetModeUpdateHook(s.modeUpdate)
|
newService.instance.Router().ClashServer().(*clashapi.Server).SetModeUpdateHook(s.modeUpdate)
|
||||||
newService.platformInterface.openURLFunc = func(url string) {
|
|
||||||
select {
|
|
||||||
case s.events <- &eventOpenURL{URL: url}:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
s.service = newService
|
s.service = newService
|
||||||
s.notifyURLTestUpdate()
|
s.notifyURLTestUpdate()
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package libbox
|
package libbox
|
||||||
|
|
||||||
import (
|
import (
|
||||||
std_bufio "bufio"
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"net"
|
"net"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -10,15 +9,9 @@ import (
|
|||||||
"github.com/sagernet/sing-box/common/conntrack"
|
"github.com/sagernet/sing-box/common/conntrack"
|
||||||
"github.com/sagernet/sing-box/experimental/clashapi"
|
"github.com/sagernet/sing-box/experimental/clashapi"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
F "github.com/sagernet/sing/common/format"
|
|
||||||
"github.com/sagernet/sing/common/memory"
|
"github.com/sagernet/sing/common/memory"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
eventTypeEmpty byte = iota
|
|
||||||
eventTypeOpenURL
|
|
||||||
)
|
|
||||||
|
|
||||||
type StatusMessage struct {
|
type StatusMessage struct {
|
||||||
Memory int64
|
Memory int64
|
||||||
Goroutines int32
|
Goroutines int32
|
||||||
@@ -51,73 +44,31 @@ func (s *CommandServer) readStatus() StatusMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *CommandServer) handleStatusConn(conn net.Conn) error {
|
func (s *CommandServer) handleStatusConn(conn net.Conn) error {
|
||||||
var isMainClient bool
|
|
||||||
err := binary.Read(conn, binary.BigEndian, &isMainClient)
|
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "read is main client")
|
|
||||||
}
|
|
||||||
var interval int64
|
var interval int64
|
||||||
err = binary.Read(conn, binary.BigEndian, &interval)
|
err := binary.Read(conn, binary.BigEndian, &interval)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "read interval")
|
return E.Cause(err, "read interval")
|
||||||
}
|
}
|
||||||
ticker := time.NewTicker(time.Duration(interval))
|
ticker := time.NewTicker(time.Duration(interval))
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
ctx := connKeepAlive(conn)
|
ctx := connKeepAlive(conn)
|
||||||
writer := std_bufio.NewWriter(conn)
|
for {
|
||||||
if isMainClient {
|
err = binary.Write(conn, binary.BigEndian, s.readStatus())
|
||||||
for {
|
if err != nil {
|
||||||
writer.WriteByte(eventTypeEmpty)
|
return err
|
||||||
err = binary.Write(conn, binary.BigEndian, s.readStatus())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
writer.Flush()
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
case <-ticker.C:
|
|
||||||
case event := <-s.events:
|
|
||||||
event.writeTo(writer)
|
|
||||||
writer.Flush()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
select {
|
||||||
for {
|
case <-ctx.Done():
|
||||||
err = binary.Write(conn, binary.BigEndian, s.readStatus())
|
return ctx.Err()
|
||||||
if err != nil {
|
case <-ticker.C:
|
||||||
return err
|
|
||||||
}
|
|
||||||
writer.Flush()
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
case <-ticker.C:
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CommandClient) handleStatusConn(conn net.Conn) {
|
func (c *CommandClient) handleStatusConn(conn net.Conn) {
|
||||||
reader := std_bufio.NewReader(conn)
|
|
||||||
for {
|
for {
|
||||||
if c.options.IsMainClient {
|
|
||||||
rawEvent, err := readEvent(reader)
|
|
||||||
if err != nil {
|
|
||||||
c.handler.Disconnected(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch event := rawEvent.(type) {
|
|
||||||
case *eventOpenURL:
|
|
||||||
c.handler.OpenURL(event.URL)
|
|
||||||
continue
|
|
||||||
case nil:
|
|
||||||
default:
|
|
||||||
panic(F.ToString("unexpected event type: ", event))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var message StatusMessage
|
var message StatusMessage
|
||||||
err := binary.Read(reader, binary.BigEndian, &message)
|
err := binary.Read(conn, binary.BigEndian, &message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.handler.Disconnected(err.Error())
|
c.handler.Disconnected(err.Error())
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -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/protocol/group"
|
"github.com/sagernet/sing-box/outbound"
|
||||||
"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.(*group.URLTest)
|
urlTest, isURLTest := abstractOutboundGroup.(*outbound.URLTest)
|
||||||
if isURLTest {
|
if isURLTest {
|
||||||
go urlTest.CheckOutbounds()
|
go urlTest.CheckOutbounds()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ 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"
|
||||||
@@ -18,11 +17,10 @@ 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(ctx context.Context, configContent string) (option.Options, error) {
|
func parseConfig(configContent string) (option.Options, error) {
|
||||||
options, err := json.UnmarshalExtendedContext[option.Options](ctx, []byte(configContent))
|
options, err := json.UnmarshalExtended[option.Options]([]byte(configContent))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return option.Options{}, E.Cause(err, "decode config")
|
return option.Options{}, E.Cause(err, "decode config")
|
||||||
}
|
}
|
||||||
@@ -30,16 +28,16 @@ func parseConfig(ctx context.Context, configContent string) (option.Options, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
func CheckConfig(configContent string) error {
|
func CheckConfig(configContent string) error {
|
||||||
options, err := parseConfig(box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry()), configContent)
|
options, err := parseConfig(configContent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
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()
|
||||||
@@ -57,7 +55,7 @@ func (s *platformInterfaceStub) UsePlatformAutoDetectInterfaceControl() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *platformInterfaceStub) AutoDetectInterfaceControl() control.Func {
|
func (s *platformInterfaceStub) AutoDetectInterfaceControl(fd int) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,11 +135,12 @@ func (s *interfaceMonitorStub) RegisterCallback(callback tun.DefaultInterfaceUpd
|
|||||||
func (s *interfaceMonitorStub) UnregisterCallback(element *list.Element[tun.DefaultInterfaceUpdateCallback]) {
|
func (s *interfaceMonitorStub) UnregisterCallback(element *list.Element[tun.DefaultInterfaceUpdateCallback]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *platformInterfaceStub) OpenURL(url string) {
|
func (s *platformInterfaceStub) SendNotification(notification *platform.Notification) error {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func FormatConfig(configContent string) (string, error) {
|
func FormatConfig(configContent string) (string, error) {
|
||||||
options, err := parseConfig(box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry()), configContent)
|
options, err := parseConfig(configContent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,27 +4,28 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/experimental/deprecated"
|
"github.com/sagernet/sing-box/experimental/deprecated"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ deprecated.Manager = (*deprecatedManager)(nil)
|
var _ deprecated.Manager = (*deprecatedManager)(nil)
|
||||||
|
|
||||||
type deprecatedManager struct {
|
type deprecatedManager struct {
|
||||||
access sync.Mutex
|
access sync.Mutex
|
||||||
features []deprecated.Note
|
notes []deprecated.Note
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *deprecatedManager) ReportDeprecated(feature deprecated.Note) {
|
func (m *deprecatedManager) ReportDeprecated(feature deprecated.Note) {
|
||||||
m.access.Lock()
|
m.access.Lock()
|
||||||
defer m.access.Unlock()
|
defer m.access.Unlock()
|
||||||
m.features = append(m.features, feature)
|
m.notes = common.Uniq(append(m.notes, feature))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *deprecatedManager) Get() []deprecated.Note {
|
func (m *deprecatedManager) Get() []deprecated.Note {
|
||||||
m.access.Lock()
|
m.access.Lock()
|
||||||
defer m.access.Unlock()
|
defer m.access.Unlock()
|
||||||
features := m.features
|
notes := m.notes
|
||||||
m.features = nil
|
m.notes = nil
|
||||||
return features
|
return notes
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ = deprecated.Note(DeprecatedNote{})
|
var _ = deprecated.Note(DeprecatedNote{})
|
||||||
|
|||||||
30
experimental/libbox/link_flags_linux.go
Normal file
30
experimental/libbox/link_flags_linux.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package libbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// copied from net.linkFlags
|
||||||
|
func linkFlags(rawFlags uint32) net.Flags {
|
||||||
|
var f net.Flags
|
||||||
|
if rawFlags&syscall.IFF_UP != 0 {
|
||||||
|
f |= net.FlagUp
|
||||||
|
}
|
||||||
|
if rawFlags&syscall.IFF_RUNNING != 0 {
|
||||||
|
f |= net.FlagRunning
|
||||||
|
}
|
||||||
|
if rawFlags&syscall.IFF_BROADCAST != 0 {
|
||||||
|
f |= net.FlagBroadcast
|
||||||
|
}
|
||||||
|
if rawFlags&syscall.IFF_LOOPBACK != 0 {
|
||||||
|
f |= net.FlagLoopback
|
||||||
|
}
|
||||||
|
if rawFlags&syscall.IFF_POINTOPOINT != 0 {
|
||||||
|
f |= net.FlagPointToPoint
|
||||||
|
}
|
||||||
|
if rawFlags&syscall.IFF_MULTICAST != 0 {
|
||||||
|
f |= net.FlagMulticast
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
11
experimental/libbox/link_flags_stub.go
Normal file
11
experimental/libbox/link_flags_stub.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
//go:build !linux
|
||||||
|
|
||||||
|
package libbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func linkFlags(rawFlags uint32) net.Flags {
|
||||||
|
panic("stub!")
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ type PlatformInterface interface {
|
|||||||
IncludeAllNetworks() bool
|
IncludeAllNetworks() bool
|
||||||
ReadWIFIState() *WIFIState
|
ReadWIFIState() *WIFIState
|
||||||
ClearDNSCache()
|
ClearDNSCache()
|
||||||
|
SendNotification(notification *Notification) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type TunInterface interface {
|
type TunInterface interface {
|
||||||
@@ -38,6 +39,7 @@ type NetworkInterface struct {
|
|||||||
MTU int32
|
MTU int32
|
||||||
Name string
|
Name string
|
||||||
Addresses StringIterator
|
Addresses StringIterator
|
||||||
|
Flags int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type WIFIState struct {
|
type WIFIState struct {
|
||||||
@@ -54,6 +56,16 @@ type NetworkInterfaceIterator interface {
|
|||||||
HasNext() bool
|
HasNext() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Notification struct {
|
||||||
|
Identifier string
|
||||||
|
TypeName string
|
||||||
|
TypeID int32
|
||||||
|
Title string
|
||||||
|
Subtitle string
|
||||||
|
Body string
|
||||||
|
OpenURL string
|
||||||
|
}
|
||||||
|
|
||||||
type OnDemandRule interface {
|
type OnDemandRule interface {
|
||||||
Target() int32
|
Target() int32
|
||||||
DNSSearchDomainMatch() StringIterator
|
DNSSearchDomainMatch() StringIterator
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
type Interface interface {
|
type Interface interface {
|
||||||
Initialize(ctx context.Context, router adapter.Router) error
|
Initialize(ctx context.Context, router adapter.Router) error
|
||||||
UsePlatformAutoDetectInterfaceControl() bool
|
UsePlatformAutoDetectInterfaceControl() bool
|
||||||
AutoDetectInterfaceControl() control.Func
|
AutoDetectInterfaceControl(fd int) error
|
||||||
OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error)
|
OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error)
|
||||||
UsePlatformDefaultInterfaceMonitor() bool
|
UsePlatformDefaultInterfaceMonitor() bool
|
||||||
CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor
|
CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor
|
||||||
@@ -25,5 +25,15 @@ type Interface interface {
|
|||||||
ClearDNSCache()
|
ClearDNSCache()
|
||||||
ReadWIFIState() adapter.WIFIState
|
ReadWIFIState() adapter.WIFIState
|
||||||
process.Searcher
|
process.Searcher
|
||||||
OpenURL(url string)
|
SendNotification(notification *Notification) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Notification struct {
|
||||||
|
Identifier string
|
||||||
|
TypeName string
|
||||||
|
TypeID int32
|
||||||
|
Title string
|
||||||
|
Subtitle string
|
||||||
|
Body string
|
||||||
|
OpenURL string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ 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"
|
||||||
@@ -35,29 +34,28 @@ type BoxService struct {
|
|||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
instance *box.Box
|
instance *box.Box
|
||||||
platformInterface *platformInterfaceWrapper
|
|
||||||
pauseManager pause.Manager
|
pauseManager pause.Manager
|
||||||
urlTestHistoryStorage *urltest.HistoryStorage
|
urlTestHistoryStorage *urltest.HistoryStorage
|
||||||
|
|
||||||
servicePauseFields
|
servicePauseFields
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(configContent string, platformInterface PlatformInterface) (*BoxService, error) {
|
func NewService(configContent string, platformInterface PlatformInterface) (*BoxService, error) {
|
||||||
ctx := box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry())
|
options, err := parseConfig(configContent)
|
||||||
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(ctx)
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
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 {
|
||||||
@@ -69,7 +67,6 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
|
|||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
cancel: cancel,
|
cancel: cancel,
|
||||||
instance: instance,
|
instance: instance,
|
||||||
platformInterface: platformWrapper,
|
|
||||||
urlTestHistoryStorage: urlTestHistoryStorage,
|
urlTestHistoryStorage: urlTestHistoryStorage,
|
||||||
pauseManager: service.FromContext[pause.Manager](ctx),
|
pauseManager: service.FromContext[pause.Manager](ctx),
|
||||||
}, nil
|
}, nil
|
||||||
@@ -105,10 +102,9 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type platformInterfaceWrapper struct {
|
type platformInterfaceWrapper struct {
|
||||||
iif PlatformInterface
|
iif PlatformInterface
|
||||||
useProcFS bool
|
useProcFS bool
|
||||||
router adapter.Router
|
router adapter.Router
|
||||||
openURLFunc func(url string)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *platformInterfaceWrapper) Initialize(ctx context.Context, router adapter.Router) error {
|
func (w *platformInterfaceWrapper) Initialize(ctx context.Context, router adapter.Router) error {
|
||||||
@@ -120,12 +116,8 @@ func (w *platformInterfaceWrapper) UsePlatformAutoDetectInterfaceControl() bool
|
|||||||
return w.iif.UsePlatformAutoDetectInterfaceControl()
|
return w.iif.UsePlatformAutoDetectInterfaceControl()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *platformInterfaceWrapper) AutoDetectInterfaceControl() control.Func {
|
func (w *platformInterfaceWrapper) AutoDetectInterfaceControl(fd int) error {
|
||||||
return func(network, address string, conn syscall.RawConn) error {
|
return w.iif.AutoDetectInterfaceControl(int32(fd))
|
||||||
return control.Raw(conn, func(fd uintptr) error {
|
|
||||||
return w.iif.AutoDetectInterfaceControl(int32(fd))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *platformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) {
|
func (w *platformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) {
|
||||||
@@ -183,6 +175,7 @@ func (w *platformInterfaceWrapper) Interfaces() ([]control.Interface, error) {
|
|||||||
MTU: int(netInterface.MTU),
|
MTU: int(netInterface.MTU),
|
||||||
Name: netInterface.Name,
|
Name: netInterface.Name,
|
||||||
Addresses: common.Map(iteratorToArray[string](netInterface.Addresses), netip.MustParsePrefix),
|
Addresses: common.Map(iteratorToArray[string](netInterface.Addresses), netip.MustParsePrefix),
|
||||||
|
Flags: linkFlags(uint32(netInterface.Flags)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return interfaces, nil
|
return interfaces, nil
|
||||||
@@ -243,8 +236,6 @@ func (w *platformInterfaceWrapper) WriteMessage(level log.Level, message string)
|
|||||||
w.iif.WriteLog(message)
|
w.iif.WriteLog(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *platformInterfaceWrapper) OpenURL(url string) {
|
func (w *platformInterfaceWrapper) SendNotification(notification *platform.Notification) error {
|
||||||
if w.openURLFunc != nil {
|
return w.iif.SendNotification((*Notification)(notification))
|
||||||
w.openURLFunc(url)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -23,7 +24,6 @@ var (
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
debug.SetPanicOnFault(true)
|
debug.SetPanicOnFault(true)
|
||||||
debug.SetTraceback("all")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Setup(basePath string, workingPath string, tempPath string, isTVOS bool) {
|
func Setup(basePath string, workingPath string, tempPath string, isTVOS bool) {
|
||||||
|
|||||||
30
go.mod
30
go.mod
@@ -3,6 +3,7 @@ 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
|
||||||
@@ -16,23 +17,24 @@ 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
|
||||||
github.com/sagernet/cors v1.2.1
|
github.com/sagernet/cors v1.2.1
|
||||||
github.com/sagernet/fswatch v0.1.1
|
github.com/sagernet/fswatch v0.1.1
|
||||||
github.com/sagernet/gomobile v0.1.4
|
github.com/sagernet/gomobile v0.1.4
|
||||||
github.com/sagernet/gvisor v0.0.0-20241021032506-a4324256e4a3
|
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff
|
||||||
github.com/sagernet/quic-go v0.48.0-beta.1
|
github.com/sagernet/quic-go v0.48.2-beta.1
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||||
github.com/sagernet/sing v0.5.0-rc.4.0.20241101160402-8452992a6369
|
github.com/sagernet/sing v0.5.1
|
||||||
github.com/sagernet/sing-dns v0.3.0-rc.2.0.20241023053951-feb6d5403f2a
|
github.com/sagernet/sing-dns v0.3.0
|
||||||
github.com/sagernet/sing-mux v0.2.1-0.20241020175909-fe6153f7a9ec
|
github.com/sagernet/sing-mux v0.2.1
|
||||||
github.com/sagernet/sing-quic v0.3.0-rc.1
|
github.com/sagernet/sing-quic v0.3.1
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7
|
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
||||||
github.com/sagernet/sing-shadowtls v0.1.4
|
github.com/sagernet/sing-shadowtls v0.1.5
|
||||||
github.com/sagernet/sing-tun v0.4.0-rc.4.0.20241023054150-3b5b396d06f7
|
github.com/sagernet/sing-tun v0.4.2
|
||||||
github.com/sagernet/sing-vmess v0.1.12
|
github.com/sagernet/sing-vmess v0.1.12
|
||||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
|
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
|
||||||
github.com/sagernet/utls v1.6.7
|
github.com/sagernet/utls v1.6.7
|
||||||
@@ -42,17 +44,19 @@ require (
|
|||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
go.uber.org/zap v1.27.0
|
go.uber.org/zap v1.27.0
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||||
golang.org/x/crypto v0.28.0
|
golang.org/x/crypto v0.29.0
|
||||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
|
||||||
golang.org/x/mod v0.20.0
|
golang.org/x/mod v0.20.0
|
||||||
golang.org/x/net v0.30.0
|
golang.org/x/net v0.31.0
|
||||||
golang.org/x/sys v0.26.0
|
golang.org/x/sys v0.27.0
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
||||||
google.golang.org/grpc v1.63.2
|
google.golang.org/grpc v1.63.2
|
||||||
google.golang.org/protobuf v1.33.0
|
google.golang.org/protobuf v1.33.0
|
||||||
howett.net/plist v1.0.1
|
howett.net/plist v1.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//replace github.com/sagernet/sing => ../sing
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/ajg/form v1.5.1 // indirect
|
github.com/ajg/form v1.5.1 // indirect
|
||||||
github.com/andybalholm/brotli v1.0.6 // indirect
|
github.com/andybalholm/brotli v1.0.6 // indirect
|
||||||
@@ -86,8 +90,8 @@ require (
|
|||||||
github.com/vishvananda/netns v0.0.4 // indirect
|
github.com/vishvananda/netns v0.0.4 // indirect
|
||||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
golang.org/x/sync v0.8.0 // indirect
|
golang.org/x/sync v0.9.0 // indirect
|
||||||
golang.org/x/text v0.19.0 // indirect
|
golang.org/x/text v0.20.0 // indirect
|
||||||
golang.org/x/time v0.7.0 // indirect
|
golang.org/x/time v0.7.0 // indirect
|
||||||
golang.org/x/tools v0.24.0 // indirect
|
golang.org/x/tools v0.24.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||||
|
|||||||
62
go.sum
62
go.sum
@@ -1,3 +1,5 @@
|
|||||||
|
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=
|
||||||
@@ -7,6 +9,7 @@ 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=
|
||||||
@@ -78,6 +81,8 @@ 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=
|
||||||
@@ -99,33 +104,33 @@ github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQ
|
|||||||
github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o=
|
github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o=
|
||||||
github.com/sagernet/gomobile v0.1.4 h1:WzX9ka+iHdupMgy2Vdich+OAt7TM8C2cZbIbzNjBrJY=
|
github.com/sagernet/gomobile v0.1.4 h1:WzX9ka+iHdupMgy2Vdich+OAt7TM8C2cZbIbzNjBrJY=
|
||||||
github.com/sagernet/gomobile v0.1.4/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E=
|
github.com/sagernet/gomobile v0.1.4/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E=
|
||||||
github.com/sagernet/gvisor v0.0.0-20241021032506-a4324256e4a3 h1:RxEz7LhPNiF/gX/Hg+OXr5lqsM9iVAgmaK1L1vzlDRM=
|
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff h1:mlohw3360Wg1BNGook/UHnISXhUx4Gd/3tVLs5T0nSs=
|
||||||
github.com/sagernet/gvisor v0.0.0-20241021032506-a4324256e4a3/go.mod h1:ehZwnT2UpmOWAHFL48XdBhnd4Qu4hN2O3Ji0us3ZHMw=
|
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff/go.mod h1:ehZwnT2UpmOWAHFL48XdBhnd4Qu4hN2O3Ji0us3ZHMw=
|
||||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
|
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
|
||||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||||
github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
|
github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
|
||||||
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
|
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
|
||||||
github.com/sagernet/quic-go v0.48.0-beta.1 h1:86hQZrmuoARI3BpDRkQaP0iAVpywA4YsRrzJPYuPKWg=
|
github.com/sagernet/quic-go v0.48.2-beta.1 h1:W0plrLWa1XtOWDTdX3CJwxmQuxkya12nN5BRGZ87kEg=
|
||||||
github.com/sagernet/quic-go v0.48.0-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/+or9YMLaG5VeTk4k=
|
github.com/sagernet/quic-go v0.48.2-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/+or9YMLaG5VeTk4k=
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||||
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
||||||
github.com/sagernet/sing v0.5.0-rc.4.0.20241101160402-8452992a6369 h1:gfiUYWslwKM7OtvG37PV0iIDCWcacJSEUS2h29rpYac=
|
github.com/sagernet/sing v0.5.1 h1:mhL/MZVq0TjuvHcpYcFtmSD1BFOxZ/+8ofbNZcg1k1Y=
|
||||||
github.com/sagernet/sing v0.5.0-rc.4.0.20241101160402-8452992a6369/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing v0.5.1/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||||
github.com/sagernet/sing-dns v0.3.0-rc.2.0.20241023053951-feb6d5403f2a h1:jpAlbmZxc1LymZrmJacsvHI57Wito5xy8qASZJMWoOQ=
|
github.com/sagernet/sing-dns v0.3.0 h1:uHCIlbCwBxALJwXcEK1d75d7t3vzCSVEQsPfZR1cxQE=
|
||||||
github.com/sagernet/sing-dns v0.3.0-rc.2.0.20241023053951-feb6d5403f2a/go.mod h1:TqLIelI+FAbVEdiTRolhGLOwvhVjY7oT+wezlOJUQ7M=
|
github.com/sagernet/sing-dns v0.3.0/go.mod h1:TqLIelI+FAbVEdiTRolhGLOwvhVjY7oT+wezlOJUQ7M=
|
||||||
github.com/sagernet/sing-mux v0.2.1-0.20241020175909-fe6153f7a9ec h1:6Fd/VsEsw9qIjaGi1IBTZSb4b4v5JYtNcoiBtGsQC48=
|
github.com/sagernet/sing-mux v0.2.1 h1:N/3MHymfnFZRd29tE3TaXwPUVVgKvxhtOkiCMLp9HVo=
|
||||||
github.com/sagernet/sing-mux v0.2.1-0.20241020175909-fe6153f7a9ec/go.mod h1:RSwqqHwbtTOX3vs6ms8vMtBGH/0ZNyLm/uwt6TlmR84=
|
github.com/sagernet/sing-mux v0.2.1/go.mod h1:dm3BWL6NvES9pbib7llpylrq7Gq+LjlzG+0RacdxcyE=
|
||||||
github.com/sagernet/sing-quic v0.3.0-rc.1 h1:SlzL1yfEAKJyRduub8vzOVtbyTLAX7RZEEBZxO5utts=
|
github.com/sagernet/sing-quic v0.3.1 h1:kLg2n4JPnuzUPg7myJGbfGVJGeXiccXfV+PhXIlkSEc=
|
||||||
github.com/sagernet/sing-quic v0.3.0-rc.1/go.mod h1:uX+aUHA0fgIN6U3WViseDpSdTQriViZ7qz0Wbsf1mNQ=
|
github.com/sagernet/sing-quic v0.3.1/go.mod h1:g8b5Fj88KRM0H9lpKAxJj0EpkL/Yk06qXJAG7FuZd2I=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
|
github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
||||||
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
|
github.com/sagernet/sing-shadowtls v0.1.5 h1:uXxmq/HXh8DIiBGLzpMjCbWnzIAFs+lIxiTOjdgG5qo=
|
||||||
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
|
github.com/sagernet/sing-shadowtls v0.1.5/go.mod h1:tvrDPTGLrSM46Wnf7mSr+L8NHvgvF8M4YnJF790rZX4=
|
||||||
github.com/sagernet/sing-tun v0.4.0-rc.4.0.20241023054150-3b5b396d06f7 h1:wWfRBSP8v0Gc9yUeMgoKCiG+LIs/+bYLWWwVVYSbGFI=
|
github.com/sagernet/sing-tun v0.4.2 h1:GCP7TI/gwDH/iFIugYS3WcVhCcbDE6qwAbjYQ5W/m+E=
|
||||||
github.com/sagernet/sing-tun v0.4.0-rc.4.0.20241023054150-3b5b396d06f7/go.mod h1:2v1L3BQKzoOpGuKMwC6pcs/5/Xb5PBqzqL6Lq88IoS8=
|
github.com/sagernet/sing-tun v0.4.2/go.mod h1:1WQVMelJQjrtlzhzHwwPTSa7n41b3zSWP2DeJqWxruk=
|
||||||
github.com/sagernet/sing-vmess v0.1.12 h1:2gFD8JJb+eTFMoa8FIVMnknEi+vCSfaiTXTfEYAYAPg=
|
github.com/sagernet/sing-vmess v0.1.12 h1:2gFD8JJb+eTFMoa8FIVMnknEi+vCSfaiTXTfEYAYAPg=
|
||||||
github.com/sagernet/sing-vmess v0.1.12/go.mod h1:luTSsfyBGAc9VhtCqwjR+dt1QgqBhuYBCONB/POhF8I=
|
github.com/sagernet/sing-vmess v0.1.12/go.mod h1:luTSsfyBGAc9VhtCqwjR+dt1QgqBhuYBCONB/POhF8I=
|
||||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
||||||
@@ -141,6 +146,7 @@ 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=
|
||||||
@@ -162,19 +168,21 @@ 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.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||||
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
||||||
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
||||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||||
golang.org/x/sync v0.8.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=
|
||||||
@@ -182,14 +190,14 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
|
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
|||||||
54
inbound/builder.go
Normal file
54
inbound/builder.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
196
inbound/default.go
Normal file
196
inbound/default.go
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
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.ConnectionRouter
|
||||||
|
logger log.ContextLogger
|
||||||
|
tag string
|
||||||
|
listenOptions option.ListenOptions
|
||||||
|
connHandler adapter.ConnectionHandler
|
||||||
|
packetHandler adapter.PacketHandler
|
||||||
|
oobPacketHandler adapter.OOBPacketHandler
|
||||||
|
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) Network() []string {
|
||||||
|
return a.network
|
||||||
|
}
|
||||||
|
|
||||||
|
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) 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
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
return metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *myInboundAdapter) newError(err error) {
|
||||||
|
a.logger.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *myInboundAdapter) NewError(ctx context.Context, err error) {
|
||||||
|
NewError(a.logger, ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
88
inbound/default_tcp.go
Normal file
88
inbound/default_tcp.go
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
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)
|
||||||
|
hErr := a.connHandler.NewConnection(ctx, conn, metadata)
|
||||||
|
if hErr != nil {
|
||||||
|
conn.Close()
|
||||||
|
a.NewError(ctx, E.Cause(hErr, "process connection from ", metadata.Source))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *myInboundAdapter) routeTCP(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) {
|
||||||
|
a.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||||
|
hErr := a.newConnection(ctx, conn, metadata)
|
||||||
|
if hErr != nil {
|
||||||
|
conn.Close()
|
||||||
|
a.NewError(ctx, E.Cause(hErr, "process connection from ", metadata.Source))
|
||||||
|
}
|
||||||
|
}
|
||||||
18
inbound/default_tcp_go1.20.go
Normal file
18
inbound/default_tcp_go1.20.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
//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,6 +1,6 @@
|
|||||||
//go:build go1.21
|
//go:build go1.21
|
||||||
|
|
||||||
package listener
|
package inbound
|
||||||
|
|
||||||
import "net"
|
import "net"
|
||||||
|
|
||||||
15
inbound/default_tcp_nongo1.20.go
Normal file
15
inbound/default_tcp_nongo1.20.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
//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,6 +1,6 @@
|
|||||||
//go:build !go1.21
|
//go:build !go1.21
|
||||||
|
|
||||||
package listener
|
package inbound
|
||||||
|
|
||||||
import "net"
|
import "net"
|
||||||
|
|
||||||
229
inbound/default_udp.go
Normal file
229
inbound/default_udp.go
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
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()
|
||||||
|
packetService := (*myInboundPacketAdapter)(a)
|
||||||
|
for {
|
||||||
|
buffer.Reset()
|
||||||
|
n, addr, err := a.udpConn.ReadFromUDPAddrPort(buffer.FreeBytes())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
buffer.Truncate(n)
|
||||||
|
var metadata adapter.InboundContext
|
||||||
|
metadata.Inbound = a.tag
|
||||||
|
metadata.InboundType = a.protocol
|
||||||
|
metadata.InboundOptions = a.listenOptions.InboundOptions
|
||||||
|
metadata.Source = M.SocksaddrFromNetIP(addr).Unwrap()
|
||||||
|
metadata.OriginDestination = a.udpAddr
|
||||||
|
err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata)
|
||||||
|
if err != nil {
|
||||||
|
a.newError(E.Cause(err, "process packet from ", metadata.Source))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *myInboundAdapter) loopUDPOOBIn() {
|
||||||
|
defer close(a.packetOutboundClosed)
|
||||||
|
buffer := buf.NewPacket()
|
||||||
|
defer buffer.Release()
|
||||||
|
buffer.IncRef()
|
||||||
|
defer buffer.DecRef()
|
||||||
|
packetService := (*myInboundPacketAdapter)(a)
|
||||||
|
oob := make([]byte, 1024)
|
||||||
|
for {
|
||||||
|
buffer.Reset()
|
||||||
|
n, oobN, _, addr, err := a.udpConn.ReadMsgUDPAddrPort(buffer.FreeBytes(), oob)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
buffer.Truncate(n)
|
||||||
|
var metadata adapter.InboundContext
|
||||||
|
metadata.Inbound = a.tag
|
||||||
|
metadata.InboundType = a.protocol
|
||||||
|
metadata.InboundOptions = a.listenOptions.InboundOptions
|
||||||
|
metadata.Source = M.SocksaddrFromNetIP(addr).Unwrap()
|
||||||
|
metadata.OriginDestination = a.udpAddr
|
||||||
|
err = a.oobPacketHandler.NewPacket(a.ctx, packetService, buffer, oob[:oobN], metadata)
|
||||||
|
if err != nil {
|
||||||
|
a.newError(E.Cause(err, "process packet from ", metadata.Source))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *myInboundAdapter) loopUDPInThreadSafe() {
|
||||||
|
defer close(a.packetOutboundClosed)
|
||||||
|
packetService := (*myInboundPacketAdapter)(a)
|
||||||
|
for {
|
||||||
|
buffer := buf.NewPacket()
|
||||||
|
n, addr, err := a.udpConn.ReadFromUDPAddrPort(buffer.FreeBytes())
|
||||||
|
if err != nil {
|
||||||
|
buffer.Release()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
buffer.Truncate(n)
|
||||||
|
var metadata adapter.InboundContext
|
||||||
|
metadata.Inbound = a.tag
|
||||||
|
metadata.InboundType = a.protocol
|
||||||
|
metadata.InboundOptions = a.listenOptions.InboundOptions
|
||||||
|
metadata.Source = M.SocksaddrFromNetIP(addr).Unwrap()
|
||||||
|
metadata.OriginDestination = a.udpAddr
|
||||||
|
err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata)
|
||||||
|
if err != nil {
|
||||||
|
buffer.Release()
|
||||||
|
a.newError(E.Cause(err, "process packet from ", metadata.Source))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *myInboundAdapter) loopUDPOOBInThreadSafe() {
|
||||||
|
defer close(a.packetOutboundClosed)
|
||||||
|
packetService := (*myInboundPacketAdapter)(a)
|
||||||
|
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)
|
||||||
|
var metadata adapter.InboundContext
|
||||||
|
metadata.Inbound = a.tag
|
||||||
|
metadata.InboundType = a.protocol
|
||||||
|
metadata.InboundOptions = a.listenOptions.InboundOptions
|
||||||
|
metadata.Source = M.SocksaddrFromNetIP(addr).Unwrap()
|
||||||
|
metadata.OriginDestination = a.udpAddr
|
||||||
|
err = a.oobPacketHandler.NewPacket(a.ctx, packetService, buffer, oob[:oobN], metadata)
|
||||||
|
if err != nil {
|
||||||
|
buffer.Release()
|
||||||
|
a.newError(E.Cause(err, "process packet from ", metadata.Source))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *myInboundAdapter) loopUDPOut() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case packet := <-a.packetOutbound:
|
||||||
|
err := a.writePacket(packet.buffer, packet.destination)
|
||||||
|
if err != nil && !E.IsClosed(err) {
|
||||||
|
a.newError(E.New("write back udp: ", err))
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
case <-a.packetOutboundClosed:
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case packet := <-a.packetOutbound:
|
||||||
|
packet.buffer.Release()
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *myInboundAdapter) writePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||||
|
defer buffer.Release()
|
||||||
|
if destination.IsFqdn() {
|
||||||
|
udpAddr, err := net.ResolveUDPAddr(N.NetworkUDP, destination.String())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return common.Error(a.udpConn.WriteTo(buffer.Bytes(), udpAddr))
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
104
inbound/direct.go
Normal file
104
inbound/direct.go
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
package inbound
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
"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/udpnat"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ adapter.Inbound = (*Direct)(nil)
|
||||||
|
|
||||||
|
type Direct struct {
|
||||||
|
myInboundAdapter
|
||||||
|
udpNat *udpnat.Service[netip.AddrPort]
|
||||||
|
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[netip.AddrPort](int64(udpTimeout.Seconds()), adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound))
|
||||||
|
inbound.connHandler = inbound
|
||||||
|
inbound.packetHandler = inbound
|
||||||
|
inbound.packetUpstream = inbound.udpNat
|
||||||
|
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) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, 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.udpNat.NewContextPacket(ctx, metadata.Source.AddrPort(), buffer, adapter.UpstreamMetadata(metadata), func(natConn N.PacketConn) (context.Context, N.PacketWriter) {
|
||||||
|
return adapter.WithContext(log.ContextWithNewID(ctx), &metadata), &udpnat.DirectBackWriter{Source: conn, Nat: natConn}
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Direct) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
|
return d.router.RouteConnection(ctx, conn, metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Direct) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
|
ctx = log.ContextWithNewID(ctx)
|
||||||
|
d.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
|
||||||
|
return d.router.RoutePacketConnection(ctx, conn, metadata)
|
||||||
|
}
|
||||||
114
inbound/http.go
Normal file
114
inbound/http.go
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
package inbound
|
||||||
|
|
||||||
|
import (
|
||||||
|
std_bufio "bufio"
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"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.InjectableInbound = (*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) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
|
var err error
|
||||||
|
if h.tlsConfig != nil {
|
||||||
|
conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return http.HandleConnection(ctx, conn, std_bufio.NewReader(conn), h.authenticator, h.upstreamUserHandler(metadata), adapter.UpstreamMetadata(metadata))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HTTP) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *myInboundAdapter) upstreamUserHandler(metadata adapter.InboundContext) adapter.UpstreamHandlerAdapter {
|
||||||
|
return adapter.NewUpstreamHandler(metadata, a.newUserConnection, a.streamUserPacketConnection, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *myInboundAdapter) newUserConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
|
user, loaded := auth.UserFromContext[string](ctx)
|
||||||
|
if !loaded {
|
||||||
|
a.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||||
|
return a.router.RouteConnection(ctx, conn, metadata)
|
||||||
|
}
|
||||||
|
metadata.User = user
|
||||||
|
a.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination)
|
||||||
|
return a.router.RouteConnection(ctx, conn, metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *myInboundAdapter) streamUserPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
|
user, loaded := auth.UserFromContext[string](ctx)
|
||||||
|
if !loaded {
|
||||||
|
a.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||||
|
return a.router.RoutePacketConnection(ctx, conn, metadata)
|
||||||
|
}
|
||||||
|
metadata.User = user
|
||||||
|
a.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
||||||
|
return a.router.RoutePacketConnection(ctx, conn, metadata)
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
package hysteria
|
//go:build with_quic
|
||||||
|
|
||||||
|
package inbound
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -6,9 +8,7 @@ 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,21 +20,16 @@ import (
|
|||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterInbound(registry *inbound.Registry) {
|
var _ adapter.Inbound = (*Hysteria)(nil)
|
||||||
inbound.Register[option.HysteriaInboundOptions](registry, C.TypeHysteria, NewInbound)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Inbound struct {
|
type Hysteria struct {
|
||||||
inbound.Adapter
|
myInboundAdapter
|
||||||
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 NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HysteriaInboundOptions) (adapter.Inbound, error) {
|
func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HysteriaInboundOptions) (*Hysteria, 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
|
||||||
@@ -43,15 +38,16 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
inbound := &Inbound{
|
inbound := &Hysteria{
|
||||||
Adapter: inbound.NewAdapter(C.TypeHysteria, tag),
|
myInboundAdapter: myInboundAdapter{
|
||||||
router: router,
|
protocol: C.TypeHysteria,
|
||||||
logger: logger,
|
network: []string{N.NetworkUDP},
|
||||||
listener: listener.New(listener.Options{
|
ctx: ctx,
|
||||||
Context: ctx,
|
router: router,
|
||||||
Logger: logger,
|
logger: logger,
|
||||||
Listen: options.ListenOptions,
|
tag: tag,
|
||||||
}),
|
listenOptions: options.ListenOptions,
|
||||||
|
},
|
||||||
tlsConfig: tlsConfig,
|
tlsConfig: tlsConfig,
|
||||||
}
|
}
|
||||||
var sendBps, receiveBps uint64
|
var sendBps, receiveBps uint64
|
||||||
@@ -117,12 +113,9 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
return inbound, nil
|
return inbound, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (h *Hysteria) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
ctx = log.ContextWithNewID(ctx)
|
ctx = log.ContextWithNewID(ctx)
|
||||||
metadata.Inbound = h.Tag()
|
metadata = h.createMetadata(conn, metadata)
|
||||||
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 != "" {
|
||||||
@@ -134,13 +127,9 @@ func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata ada
|
|||||||
return h.router.RouteConnection(ctx, conn, metadata)
|
return h.router.RouteConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
func (h *Hysteria) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
ctx = log.ContextWithNewID(ctx)
|
ctx = log.ContextWithNewID(ctx)
|
||||||
metadata.Inbound = h.Tag()
|
metadata = h.createPacketMetadata(conn, metadata)
|
||||||
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 +141,23 @@ func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, me
|
|||||||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) Start() error {
|
func (h *Hysteria) 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.listener.ListenUDP()
|
packetConn, err := h.myInboundAdapter.ListenUDP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return h.service.Start(packetConn)
|
return h.service.Start(packetConn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) Close() error {
|
func (h *Hysteria) Close() error {
|
||||||
return common.Close(
|
return common.Close(
|
||||||
&h.listener,
|
&h.myInboundAdapter,
|
||||||
h.tlsConfig,
|
h.tlsConfig,
|
||||||
common.PtrOrNil(h.service),
|
common.PtrOrNil(h.service),
|
||||||
)
|
)
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
package hysteria2
|
//go:build with_quic
|
||||||
|
|
||||||
|
package inbound
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -9,8 +11,6 @@ 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,21 +23,16 @@ import (
|
|||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterInbound(registry *inbound.Registry) {
|
var _ adapter.Inbound = (*Hysteria2)(nil)
|
||||||
inbound.Register[option.Hysteria2InboundOptions](registry, C.TypeHysteria2, NewInbound)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Inbound struct {
|
type Hysteria2 struct {
|
||||||
inbound.Adapter
|
myInboundAdapter
|
||||||
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 NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.Hysteria2InboundOptions) (adapter.Inbound, error) {
|
func NewHysteria2(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.Hysteria2InboundOptions) (*Hysteria2, 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
|
||||||
@@ -81,15 +76,16 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
return nil, E.New("unknown masquerade URL scheme: ", masqueradeURL.Scheme)
|
return nil, E.New("unknown masquerade URL scheme: ", masqueradeURL.Scheme)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inbound := &Inbound{
|
inbound := &Hysteria2{
|
||||||
Adapter: inbound.NewAdapter(C.TypeHysteria2, tag),
|
myInboundAdapter: myInboundAdapter{
|
||||||
router: router,
|
protocol: C.TypeHysteria2,
|
||||||
logger: logger,
|
network: []string{N.NetworkUDP},
|
||||||
listener: listener.New(listener.Options{
|
ctx: ctx,
|
||||||
Context: ctx,
|
router: router,
|
||||||
Logger: logger,
|
logger: logger,
|
||||||
Listen: options.ListenOptions,
|
tag: tag,
|
||||||
}),
|
listenOptions: options.ListenOptions,
|
||||||
|
},
|
||||||
tlsConfig: tlsConfig,
|
tlsConfig: tlsConfig,
|
||||||
}
|
}
|
||||||
var udpTimeout time.Duration
|
var udpTimeout time.Duration
|
||||||
@@ -128,12 +124,9 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
return inbound, nil
|
return inbound, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (h *Hysteria2) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
ctx = log.ContextWithNewID(ctx)
|
ctx = log.ContextWithNewID(ctx)
|
||||||
metadata.Inbound = h.Tag()
|
metadata = h.createMetadata(conn, metadata)
|
||||||
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 != "" {
|
||||||
@@ -145,13 +138,9 @@ func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata ada
|
|||||||
return h.router.RouteConnection(ctx, conn, metadata)
|
return h.router.RouteConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
func (h *Hysteria2) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
ctx = log.ContextWithNewID(ctx)
|
ctx = log.ContextWithNewID(ctx)
|
||||||
metadata.Inbound = h.Tag()
|
metadata = h.createPacketMetadata(conn, metadata)
|
||||||
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 != "" {
|
||||||
@@ -163,23 +152,23 @@ func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, me
|
|||||||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) Start() error {
|
func (h *Hysteria2) 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.listener.ListenUDP()
|
packetConn, err := h.myInboundAdapter.ListenUDP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return h.service.Start(packetConn)
|
return h.service.Start(packetConn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) Close() error {
|
func (h *Hysteria2) Close() error {
|
||||||
return common.Close(
|
return common.Close(
|
||||||
&h.listener,
|
&h.myInboundAdapter,
|
||||||
h.tlsConfig,
|
h.tlsConfig,
|
||||||
common.PtrOrNil(h.service),
|
common.PtrOrNil(h.service),
|
||||||
)
|
)
|
||||||
20
inbound/hysteria_stub.go
Normal file
20
inbound/hysteria_stub.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
//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
|
||||||
|
}
|
||||||
66
inbound/mixed.go
Normal file
66
inbound/mixed.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package inbound
|
||||||
|
|
||||||
|
import (
|
||||||
|
std_bufio "bufio"
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
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.InjectableInbound = (*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) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
|
reader := std_bufio.NewReader(conn)
|
||||||
|
headerBytes, err := reader.Peek(1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch headerBytes[0] {
|
||||||
|
case socks4.Version, socks5.Version:
|
||||||
|
return socks.HandleConnection0(ctx, conn, reader, h.authenticator, h.upstreamUserHandler(metadata), adapter.UpstreamMetadata(metadata))
|
||||||
|
default:
|
||||||
|
return http.HandleConnection(ctx, conn, reader, h.authenticator, h.upstreamUserHandler(metadata), adapter.UpstreamMetadata(metadata))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Mixed) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package naive
|
package inbound
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io"
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
@@ -10,12 +11,220 @@ 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/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, &naiveH1Conn{Conn: conn}, userName, source, destination)
|
||||||
|
} else {
|
||||||
|
n.newConnection(ctx, &naiveH2Conn{reader: request.Body, writer: writer, flusher: writer.(http.Flusher)}, userName, source, destination)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Naive) newConnection(ctx context.Context, conn net.Conn, userName string, source, 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)
|
||||||
|
}
|
||||||
|
hErr := n.router.RouteConnection(ctx, conn, n.createMetadata(conn, adapter.InboundContext{
|
||||||
|
Source: source,
|
||||||
|
Destination: destination,
|
||||||
|
User: userName,
|
||||||
|
}))
|
||||||
|
if hErr != nil {
|
||||||
|
conn.Close()
|
||||||
|
n.NewError(ctx, E.Cause(hErr, "process connection from ", source))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Naive) badRequest(ctx context.Context, request *http.Request, err error) {
|
||||||
|
n.NewError(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 {
|
||||||
47
inbound/naive_quic.go
Normal file
47
inbound/naive_quic.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
//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
|
||||||
|
}
|
||||||
11
inbound/naive_quic_stub.go
Normal file
11
inbound/naive_quic_stub.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
//go:build !with_quic
|
||||||
|
|
||||||
|
package inbound
|
||||||
|
|
||||||
|
import (
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (n *Naive) configureHTTP3Listener() error {
|
||||||
|
return C.ErrQUICNotIncluded
|
||||||
|
}
|
||||||
44
inbound/redirect.go
Normal file
44
inbound/redirect.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
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"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
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) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
|
destination, err := redir.GetOriginalDestination(conn)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "get redirect destination")
|
||||||
|
}
|
||||||
|
metadata.Destination = M.SocksaddrFromNetIP(destination)
|
||||||
|
return r.newConnection(ctx, conn, metadata)
|
||||||
|
}
|
||||||
99
inbound/shadowsocks.go
Normal file
99
inbound/shadowsocks.go
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
package inbound
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"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"
|
||||||
|
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.InjectableInbound = (*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()), inbound.upstreamContextHandler())
|
||||||
|
case common.Contains(shadowaead.List, options.Method):
|
||||||
|
inbound.service, err = shadowaead.NewService(options.Method, nil, options.Password, int64(udpTimeout.Seconds()), inbound.upstreamContextHandler())
|
||||||
|
case common.Contains(shadowaead_2022.List, options.Method):
|
||||||
|
inbound.service, err = shadowaead_2022.NewServiceWithPassword(options.Method, options.Password, int64(udpTimeout.Seconds()), inbound.upstreamContextHandler(), ntp.TimeFuncFromContext(ctx))
|
||||||
|
default:
|
||||||
|
err = E.New("unsupported method: ", options.Method)
|
||||||
|
}
|
||||||
|
inbound.packetUpstream = inbound.service
|
||||||
|
return inbound, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Shadowsocks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
|
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Shadowsocks) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext) error {
|
||||||
|
return h.service.NewPacket(adapter.WithContext(ctx, &metadata), conn, buffer, adapter.UpstreamMetadata(metadata))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Shadowsocks) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shadowsocks
|
package inbound
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -7,8 +7,6 @@ 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"
|
||||||
@@ -22,31 +20,35 @@ 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"
|
|
||||||
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 _ adapter.TCPInjectableInbound = (*MultiInbound)(nil)
|
var (
|
||||||
|
_ adapter.Inbound = (*ShadowsocksMulti)(nil)
|
||||||
|
_ adapter.InjectableInbound = (*ShadowsocksMulti)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
type MultiInbound struct {
|
type ShadowsocksMulti struct {
|
||||||
inbound.Adapter
|
myInboundAdapter
|
||||||
ctx context.Context
|
service shadowsocks.MultiService[int]
|
||||||
router adapter.ConnectionRouterEx
|
users []option.ShadowsocksUser
|
||||||
logger logger.ContextLogger
|
|
||||||
listener *listener.Listener
|
|
||||||
service shadowsocks.MultiService[int]
|
|
||||||
users []option.ShadowsocksUser
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMultiInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksInboundOptions) (*MultiInbound, error) {
|
func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksInboundOptions) (*ShadowsocksMulti, error) {
|
||||||
inbound := &MultiInbound{
|
inbound := &ShadowsocksMulti{
|
||||||
Adapter: inbound.NewAdapter(C.TypeShadowsocks, tag),
|
myInboundAdapter: myInboundAdapter{
|
||||||
ctx: ctx,
|
protocol: C.TypeShadowsocks,
|
||||||
router: uot.NewRouter(router, logger),
|
network: options.Network.Build(),
|
||||||
logger: logger,
|
ctx: ctx,
|
||||||
|
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 {
|
||||||
@@ -64,15 +66,14 @@ func newMultiInbound(ctx context.Context, router adapter.Router, logger log.Cont
|
|||||||
options.Method,
|
options.Method,
|
||||||
options.Password,
|
options.Password,
|
||||||
int64(udpTimeout.Seconds()),
|
int64(udpTimeout.Seconds()),
|
||||||
adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, inbound),
|
adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound),
|
||||||
ntp.TimeFuncFromContext(ctx),
|
ntp.TimeFuncFromContext(ctx),
|
||||||
)
|
)
|
||||||
} else if common.Contains(shadowaead.List, options.Method) {
|
} else if common.Contains(shadowaead.List, options.Method) {
|
||||||
service, err = shadowaead.NewMultiService[int](
|
service, err = shadowaead.NewMultiService[int](
|
||||||
options.Method,
|
options.Method,
|
||||||
int64(udpTimeout.Seconds()),
|
int64(udpTimeout.Seconds()),
|
||||||
adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, inbound),
|
adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound))
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
return nil, E.New("unsupported method: " + options.Method)
|
return nil, E.New("unsupported method: " + options.Method)
|
||||||
}
|
}
|
||||||
@@ -88,43 +89,24 @@ func newMultiInbound(ctx context.Context, router adapter.Router, logger log.Cont
|
|||||||
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 *MultiInbound) Start() error {
|
func (h *ShadowsocksMulti) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
return h.listener.Start()
|
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *MultiInbound) Close() error {
|
func (h *ShadowsocksMulti) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext) error {
|
||||||
return h.listener.Close()
|
return h.service.NewPacket(adapter.WithContext(ctx, &metadata), conn, buffer, adapter.UpstreamMetadata(metadata))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *MultiInbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
func (h *ShadowsocksMulti) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
err := h.service.NewConnection(ctx, conn, adapter.UpstreamMetadata(metadata))
|
return os.ErrInvalid
|
||||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
|
||||||
if err != nil {
|
|
||||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *MultiInbound) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
|
func (h *ShadowsocksMulti) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
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 *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
|
||||||
@@ -136,12 +118,10 @@ func (h *MultiInbound) newConnection(ctx context.Context, conn net.Conn, metadat
|
|||||||
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)
|
||||||
metadata.Inbound = h.Tag()
|
|
||||||
metadata.InboundType = h.Type()
|
|
||||||
return h.router.RouteConnection(ctx, conn, metadata)
|
return h.router.RouteConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *MultiInbound) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
func (h *ShadowsocksMulti) 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
|
||||||
@@ -155,13 +135,5 @@ func (h *MultiInbound) newPacketConnection(ctx context.Context, conn N.PacketCon
|
|||||||
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)
|
||||||
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)
|
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *MultiInbound) NewError(ctx context.Context, err error) {
|
|
||||||
NewError(h.logger, ctx, err)
|
|
||||||
}
|
|
||||||
124
inbound/shadowsocks_relay.go
Normal file
124
inbound/shadowsocks_relay.go
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
package inbound
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"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/shadowaead_2022"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
"github.com/sagernet/sing/common/auth"
|
||||||
|
"github.com/sagernet/sing/common/buf"
|
||||||
|
F "github.com/sagernet/sing/common/format"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ adapter.Inbound = (*ShadowsocksRelay)(nil)
|
||||||
|
_ adapter.InjectableInbound = (*ShadowsocksRelay)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
type ShadowsocksRelay struct {
|
||||||
|
myInboundAdapter
|
||||||
|
service *shadowaead_2022.RelayService[int]
|
||||||
|
destinations []option.ShadowsocksDestination
|
||||||
|
}
|
||||||
|
|
||||||
|
func newShadowsocksRelay(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksInboundOptions) (*ShadowsocksRelay, error) {
|
||||||
|
inbound := &ShadowsocksRelay{
|
||||||
|
myInboundAdapter: myInboundAdapter{
|
||||||
|
protocol: C.TypeShadowsocks,
|
||||||
|
network: options.Network.Build(),
|
||||||
|
ctx: ctx,
|
||||||
|
router: uot.NewRouter(router, logger),
|
||||||
|
logger: logger,
|
||||||
|
tag: tag,
|
||||||
|
listenOptions: options.ListenOptions,
|
||||||
|
},
|
||||||
|
destinations: options.Destinations,
|
||||||
|
}
|
||||||
|
inbound.connHandler = inbound
|
||||||
|
inbound.packetHandler = inbound
|
||||||
|
var err error
|
||||||
|
inbound.router, err = mux.NewRouterWithOptions(inbound.router, logger, common.PtrValueOrDefault(options.Multiplex))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var udpTimeout time.Duration
|
||||||
|
if options.UDPTimeout != 0 {
|
||||||
|
udpTimeout = time.Duration(options.UDPTimeout)
|
||||||
|
} else {
|
||||||
|
udpTimeout = C.UDPTimeout
|
||||||
|
}
|
||||||
|
service, err := shadowaead_2022.NewRelayServiceWithPassword[int](
|
||||||
|
options.Method,
|
||||||
|
options.Password,
|
||||||
|
int64(udpTimeout.Seconds()),
|
||||||
|
adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = service.UpdateUsersWithPasswords(common.MapIndexed(options.Destinations, func(index int, user option.ShadowsocksDestination) int {
|
||||||
|
return index
|
||||||
|
}), common.Map(options.Destinations, func(user option.ShadowsocksDestination) string {
|
||||||
|
return user.Password
|
||||||
|
}), common.Map(options.Destinations, option.ShadowsocksDestination.Build))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
inbound.service = service
|
||||||
|
inbound.packetUpstream = service
|
||||||
|
return inbound, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ShadowsocksRelay) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
|
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ShadowsocksRelay) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext) error {
|
||||||
|
return h.service.NewPacket(adapter.WithContext(ctx, &metadata), conn, buffer, adapter.UpstreamMetadata(metadata))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ShadowsocksRelay) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ShadowsocksRelay) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
|
destinationIndex, loaded := auth.UserFromContext[int](ctx)
|
||||||
|
if !loaded {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
destination := h.destinations[destinationIndex].Name
|
||||||
|
if destination == "" {
|
||||||
|
destination = F.ToString(destinationIndex)
|
||||||
|
} else {
|
||||||
|
metadata.User = destination
|
||||||
|
}
|
||||||
|
h.logger.InfoContext(ctx, "[", destination, "] inbound connection to ", metadata.Destination)
|
||||||
|
return h.router.RouteConnection(ctx, conn, metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ShadowsocksRelay) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
|
destinationIndex, loaded := auth.UserFromContext[int](ctx)
|
||||||
|
if !loaded {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
destination := h.destinations[destinationIndex].Name
|
||||||
|
if destination == "" {
|
||||||
|
destination = F.ToString(destinationIndex)
|
||||||
|
} else {
|
||||||
|
metadata.User = destination
|
||||||
|
}
|
||||||
|
ctx = log.ContextWithNewID(ctx)
|
||||||
|
h.logger.InfoContext(ctx, "[", destination, "] inbound packet connection from ", metadata.Source)
|
||||||
|
h.logger.InfoContext(ctx, "[", destination, "] inbound packet connection to ", metadata.Destination)
|
||||||
|
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||||
|
}
|
||||||
@@ -1,41 +1,36 @@
|
|||||||
package shadowtls
|
package inbound
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"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/dialer"
|
"github.com/sagernet/sing-box/common/dialer"
|
||||||
"github.com/sagernet/sing-box/common/listener"
|
|
||||||
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-shadowtls"
|
"github.com/sagernet/sing-shadowtls"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/auth"
|
"github.com/sagernet/sing/common/auth"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
"github.com/sagernet/sing/common/logger"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterInbound(registry *inbound.Registry) {
|
type ShadowTLS struct {
|
||||||
inbound.Register[option.ShadowTLSInboundOptions](registry, C.TypeShadowTLS, NewInbound)
|
myInboundAdapter
|
||||||
|
service *shadowtls.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
type Inbound struct {
|
func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowTLSInboundOptions) (*ShadowTLS, error) {
|
||||||
inbound.Adapter
|
inbound := &ShadowTLS{
|
||||||
router adapter.Router
|
myInboundAdapter: myInboundAdapter{
|
||||||
logger logger.ContextLogger
|
protocol: C.TypeShadowTLS,
|
||||||
listener *listener.Listener
|
network: []string{N.NetworkTCP},
|
||||||
service *shadowtls.Service
|
ctx: ctx,
|
||||||
}
|
router: router,
|
||||||
|
logger: logger,
|
||||||
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowTLSInboundOptions) (adapter.Inbound, error) {
|
tag: tag,
|
||||||
inbound := &Inbound{
|
listenOptions: options.ListenOptions,
|
||||||
Adapter: inbound.NewAdapter(C.TypeShadowTLS, tag),
|
},
|
||||||
router: router,
|
|
||||||
logger: logger,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.Version == 0 {
|
if options.Version == 0 {
|
||||||
@@ -72,36 +67,22 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
},
|
},
|
||||||
HandshakeForServerName: handshakeForServerName,
|
HandshakeForServerName: handshakeForServerName,
|
||||||
StrictMode: options.StrictMode,
|
StrictMode: options.StrictMode,
|
||||||
Handler: adapter.NewUpstreamContextHandler(inbound.newConnection, nil, nil),
|
Handler: adapter.NewUpstreamContextHandler(inbound.newConnection, nil, inbound),
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
inbound.service = service
|
inbound.service = service
|
||||||
inbound.listener = listener.New(listener.Options{
|
inbound.connHandler = inbound
|
||||||
Context: ctx,
|
|
||||||
Logger: logger,
|
|
||||||
Network: []string{N.NetworkTCP},
|
|
||||||
Listen: options.ListenOptions,
|
|
||||||
ConnectionHandler: inbound,
|
|
||||||
})
|
|
||||||
return inbound, nil
|
return inbound, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) Start() error {
|
func (h *ShadowTLS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
return h.listener.Start()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Inbound) Close() error {
|
|
||||||
return h.listener.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Inbound) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
|
||||||
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
|
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (h *ShadowTLS) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
if userName, _ := auth.UserFromContext[string](ctx); userName != "" {
|
if userName, _ := auth.UserFromContext[string](ctx); userName != "" {
|
||||||
metadata.User = userName
|
metadata.User = userName
|
||||||
h.logger.InfoContext(ctx, "[", userName, "] inbound connection to ", metadata.Destination)
|
h.logger.InfoContext(ctx, "[", userName, "] inbound connection to ", metadata.Destination)
|
||||||
@@ -110,11 +91,3 @@ func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata ada
|
|||||||
}
|
}
|
||||||
return h.router.RouteConnection(ctx, conn, metadata)
|
return h.router.RouteConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
|
||||||
err := h.NewConnection(ctx, conn, metadata)
|
|
||||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
|
||||||
if err != nil {
|
|
||||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
51
inbound/socks.go
Normal file
51
inbound/socks.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package inbound
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
"github.com/sagernet/sing/protocol/socks"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ adapter.Inbound = (*Socks)(nil)
|
||||||
|
_ adapter.InjectableInbound = (*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) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
|
return socks.HandleConnection(ctx, conn, h.authenticator, h.upstreamUserHandler(metadata), adapter.UpstreamMetadata(metadata))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Socks) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
130
inbound/tproxy.go
Normal file
130
inbound/tproxy.go
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
package inbound
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
"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"
|
||||||
|
"github.com/sagernet/sing/common/udpnat"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TProxy struct {
|
||||||
|
myInboundAdapter
|
||||||
|
udpNat *udpnat.Service[netip.AddrPort]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTProxy(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TProxyInboundOptions) *TProxy {
|
||||||
|
tproxy := &TProxy{
|
||||||
|
myInboundAdapter: myInboundAdapter{
|
||||||
|
protocol: C.TypeTProxy,
|
||||||
|
network: options.Network.Build(),
|
||||||
|
ctx: ctx,
|
||||||
|
router: router,
|
||||||
|
logger: logger,
|
||||||
|
tag: tag,
|
||||||
|
listenOptions: options.ListenOptions,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var udpTimeout time.Duration
|
||||||
|
if options.UDPTimeout != 0 {
|
||||||
|
udpTimeout = time.Duration(options.UDPTimeout)
|
||||||
|
} else {
|
||||||
|
udpTimeout = C.UDPTimeout
|
||||||
|
}
|
||||||
|
tproxy.connHandler = tproxy
|
||||||
|
tproxy.oobPacketHandler = tproxy
|
||||||
|
tproxy.udpNat = udpnat.New[netip.AddrPort](int64(udpTimeout.Seconds()), tproxy.upstreamContextHandler())
|
||||||
|
tproxy.packetUpstream = tproxy.udpNat
|
||||||
|
return tproxy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TProxy) Start() error {
|
||||||
|
err := t.myInboundAdapter.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if t.tcpListener != nil {
|
||||||
|
err = control.Conn(common.MustCast[syscall.Conn](t.tcpListener), func(fd uintptr) error {
|
||||||
|
return redir.TProxy(fd, M.SocksaddrFromNet(t.tcpListener.Addr()).Addr.Is6())
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "configure tproxy TCP listener")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if t.udpConn != nil {
|
||||||
|
err = control.Conn(t.udpConn, func(fd uintptr) error {
|
||||||
|
return redir.TProxy(fd, M.SocksaddrFromNet(t.udpConn.LocalAddr()).Addr.Is6())
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "configure tproxy UDP listener")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TProxy) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
|
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
|
||||||
|
return t.newConnection(ctx, conn, metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TProxy) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, oob []byte, metadata adapter.InboundContext) error {
|
||||||
|
destination, err := redir.GetOriginalDestinationFromOOB(oob)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "get tproxy destination")
|
||||||
|
}
|
||||||
|
metadata.Destination = M.SocksaddrFromNetIP(destination).Unwrap()
|
||||||
|
t.udpNat.NewContextPacket(ctx, metadata.Source.AddrPort(), buffer, adapter.UpstreamMetadata(metadata), func(natConn N.PacketConn) (context.Context, N.PacketWriter) {
|
||||||
|
return adapter.WithContext(log.ContextWithNewID(ctx), &metadata), &tproxyPacketWriter{ctx: ctx, source: natConn, destination: metadata.Destination}
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type tproxyPacketWriter struct {
|
||||||
|
ctx context.Context
|
||||||
|
source N.PacketConn
|
||||||
|
destination M.Socksaddr
|
||||||
|
conn *net.UDPConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *tproxyPacketWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||||
|
defer buffer.Release()
|
||||||
|
conn := w.conn
|
||||||
|
if w.destination == destination && conn != nil {
|
||||||
|
_, err := conn.WriteToUDPAddrPort(buffer.Bytes(), M.AddrPortFromNet(w.source.LocalAddr()))
|
||||||
|
if err != nil {
|
||||||
|
w.conn = nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var listener net.ListenConfig
|
||||||
|
listener.Control = control.Append(listener.Control, control.ReuseAddr())
|
||||||
|
listener.Control = control.Append(listener.Control, redir.TProxyWriteBack())
|
||||||
|
packetConn, err := listener.ListenPacket(w.ctx, "udp", destination.String())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
udpConn := packetConn.(*net.UDPConn)
|
||||||
|
if w.destination == destination {
|
||||||
|
w.conn = udpConn
|
||||||
|
} else {
|
||||||
|
defer udpConn.Close()
|
||||||
|
}
|
||||||
|
return common.Error(udpConn.WriteToUDPAddrPort(buffer.Bytes(), M.AddrPortFromNet(w.source.LocalAddr())))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *tproxyPacketWriter) Close() error {
|
||||||
|
return common.Close(common.PtrOrNil(w.conn))
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package trojan
|
package inbound
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -6,8 +6,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"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/tls"
|
"github.com/sagernet/sing-box/common/tls"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
@@ -23,17 +21,13 @@ import (
|
|||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterInbound(registry *inbound.Registry) {
|
var (
|
||||||
inbound.Register[option.TrojanInboundOptions](registry, C.TypeTrojan, NewInbound)
|
_ adapter.Inbound = (*Trojan)(nil)
|
||||||
}
|
_ adapter.InjectableInbound = (*Trojan)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
var _ adapter.TCPInjectableInbound = (*Inbound)(nil)
|
type Trojan struct {
|
||||||
|
myInboundAdapter
|
||||||
type Inbound struct {
|
|
||||||
inbound.Adapter
|
|
||||||
router adapter.ConnectionRouterEx
|
|
||||||
logger log.ContextLogger
|
|
||||||
listener *listener.Listener
|
|
||||||
service *trojan.Service[int]
|
service *trojan.Service[int]
|
||||||
users []option.TrojanUser
|
users []option.TrojanUser
|
||||||
tlsConfig tls.ServerConfig
|
tlsConfig tls.ServerConfig
|
||||||
@@ -42,12 +36,18 @@ type Inbound struct {
|
|||||||
transport adapter.V2RayServerTransport
|
transport adapter.V2RayServerTransport
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TrojanInboundOptions) (adapter.Inbound, error) {
|
func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TrojanInboundOptions) (*Trojan, error) {
|
||||||
inbound := &Inbound{
|
inbound := &Trojan{
|
||||||
Adapter: inbound.NewAdapter(C.TypeTrojan, tag),
|
myInboundAdapter: myInboundAdapter{
|
||||||
router: router,
|
protocol: C.TypeTrojan,
|
||||||
logger: logger,
|
network: []string{N.NetworkTCP},
|
||||||
users: options.Users,
|
ctx: ctx,
|
||||||
|
router: router,
|
||||||
|
logger: logger,
|
||||||
|
tag: tag,
|
||||||
|
listenOptions: options.ListenOptions,
|
||||||
|
},
|
||||||
|
users: options.Users,
|
||||||
}
|
}
|
||||||
if options.TLS != nil {
|
if options.TLS != nil {
|
||||||
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||||
@@ -80,7 +80,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
}
|
}
|
||||||
fallbackHandler = adapter.NewUpstreamContextHandler(inbound.fallbackConnection, nil, nil)
|
fallbackHandler = adapter.NewUpstreamContextHandler(inbound.fallbackConnection, nil, nil)
|
||||||
}
|
}
|
||||||
service := trojan.NewService[int](adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, nil), fallbackHandler, logger)
|
service := trojan.NewService[int](adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound), fallbackHandler)
|
||||||
err := service.UpdateUsers(common.MapIndexed(options.Users, func(index int, it option.TrojanUser) int {
|
err := service.UpdateUsers(common.MapIndexed(options.Users, func(index int, it option.TrojanUser) int {
|
||||||
return index
|
return index
|
||||||
}), common.Map(options.Users, func(it option.TrojanUser) string {
|
}), common.Map(options.Users, func(it option.TrojanUser) string {
|
||||||
@@ -90,7 +90,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if options.Transport != nil {
|
if options.Transport != nil {
|
||||||
inbound.transport, err = v2ray.NewServerTransport(ctx, logger, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, (*inboundTransportHandler)(inbound))
|
inbound.transport, err = v2ray.NewServerTransport(ctx, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, (*trojanTransportHandler)(inbound))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "create server transport: ", options.Transport.Type)
|
return nil, E.Cause(err, "create server transport: ", options.Transport.Type)
|
||||||
}
|
}
|
||||||
@@ -100,17 +100,11 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
inbound.service = service
|
inbound.service = service
|
||||||
inbound.listener = listener.New(listener.Options{
|
inbound.connHandler = inbound
|
||||||
Context: ctx,
|
|
||||||
Logger: logger,
|
|
||||||
Network: []string{N.NetworkTCP},
|
|
||||||
Listen: options.ListenOptions,
|
|
||||||
ConnectionHandler: inbound,
|
|
||||||
})
|
|
||||||
return inbound, nil
|
return inbound, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) Start() error {
|
func (h *Trojan) Start() error {
|
||||||
if h.tlsConfig != nil {
|
if h.tlsConfig != nil {
|
||||||
err := h.tlsConfig.Start()
|
err := h.tlsConfig.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -118,10 +112,10 @@ func (h *Inbound) Start() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if h.transport == nil {
|
if h.transport == nil {
|
||||||
return h.listener.Start()
|
return h.myInboundAdapter.Start()
|
||||||
}
|
}
|
||||||
if common.Contains(h.transport.Network(), N.NetworkTCP) {
|
if common.Contains(h.transport.Network(), N.NetworkTCP) {
|
||||||
tcpListener, err := h.listener.ListenTCP()
|
tcpListener, err := h.myInboundAdapter.ListenTCP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -133,7 +127,7 @@ func (h *Inbound) Start() error {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
if common.Contains(h.transport.Network(), N.NetworkUDP) {
|
if common.Contains(h.transport.Network(), N.NetworkUDP) {
|
||||||
udpConn, err := h.listener.ListenUDP()
|
udpConn, err := h.myInboundAdapter.ListenUDP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -147,15 +141,20 @@ func (h *Inbound) Start() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) Close() error {
|
func (h *Trojan) Close() error {
|
||||||
return common.Close(
|
return common.Close(
|
||||||
&h.listener,
|
&h.myInboundAdapter,
|
||||||
h.tlsConfig,
|
h.tlsConfig,
|
||||||
h.transport,
|
h.transport,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (h *Trojan) newTransportConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
|
h.injectTCP(conn, metadata)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Trojan) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
var err error
|
var err error
|
||||||
if h.tlsConfig != nil && h.transport == nil {
|
if h.tlsConfig != nil && h.transport == nil {
|
||||||
conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
||||||
@@ -166,15 +165,11 @@ func (h *Inbound) NewConnection(ctx context.Context, conn net.Conn, metadata ada
|
|||||||
return h.service.NewConnection(adapter.WithContext(ctx, &metadata), conn, adapter.UpstreamMetadata(metadata))
|
return h.service.NewConnection(adapter.WithContext(ctx, &metadata), conn, adapter.UpstreamMetadata(metadata))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
func (h *Trojan) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
err := h.NewConnection(ctx, conn, metadata)
|
return os.ErrInvalid
|
||||||
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) error {
|
func (h *Trojan) 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
|
||||||
@@ -189,7 +184,7 @@ func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata ada
|
|||||||
return h.router.RouteConnection(ctx, conn, metadata)
|
return h.router.RouteConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) fallbackConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (h *Trojan) fallbackConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
var fallbackAddr M.Socksaddr
|
var fallbackAddr M.Socksaddr
|
||||||
if len(h.fallbackAddrTLSNextProto) > 0 {
|
if len(h.fallbackAddrTLSNextProto) > 0 {
|
||||||
if tlsConn, loaded := common.Cast[tls.Conn](conn); loaded {
|
if tlsConn, loaded := common.Cast[tls.Conn](conn); loaded {
|
||||||
@@ -212,7 +207,7 @@ func (h *Inbound) fallbackConnection(ctx context.Context, conn net.Conn, metadat
|
|||||||
return h.router.RouteConnection(ctx, conn, metadata)
|
return h.router.RouteConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
func (h *Trojan) 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
|
||||||
@@ -227,18 +222,13 @@ func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, me
|
|||||||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ adapter.V2RayServerTransportHandler = (*inboundTransportHandler)(nil)
|
var _ adapter.V2RayServerTransportHandler = (*trojanTransportHandler)(nil)
|
||||||
|
|
||||||
type inboundTransportHandler Inbound
|
type trojanTransportHandler Trojan
|
||||||
|
|
||||||
func (h *inboundTransportHandler) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
func (t *trojanTransportHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||||
var metadata adapter.InboundContext
|
return (*Trojan)(t).newTransportConnection(ctx, conn, adapter.InboundContext{
|
||||||
metadata.Inbound = h.Tag()
|
Source: metadata.Source,
|
||||||
metadata.InboundType = h.Type()
|
Destination: metadata.Destination,
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
})
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
|
||||||
metadata.Source = source
|
|
||||||
metadata.Destination = destination
|
|
||||||
h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
|
||||||
(*Inbound)(h).NewConnectionEx(ctx, conn, metadata, onClose)
|
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
package tuic
|
//go:build with_quic
|
||||||
|
|
||||||
|
package inbound
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -6,8 +8,6 @@ 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"
|
||||||
"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"
|
||||||
@@ -22,21 +22,16 @@ import (
|
|||||||
"github.com/gofrs/uuid/v5"
|
"github.com/gofrs/uuid/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterInbound(registry *inbound.Registry) {
|
var _ adapter.Inbound = (*TUIC)(nil)
|
||||||
inbound.Register[option.TUICInboundOptions](registry, C.TypeTUIC, NewInbound)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Inbound struct {
|
type TUIC struct {
|
||||||
inbound.Adapter
|
myInboundAdapter
|
||||||
router adapter.ConnectionRouterEx
|
|
||||||
logger log.ContextLogger
|
|
||||||
listener *listener.Listener
|
|
||||||
tlsConfig tls.ServerConfig
|
tlsConfig tls.ServerConfig
|
||||||
server *tuic.Service[int]
|
server *tuic.Service[int]
|
||||||
userNameList []string
|
userNameList []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TUICInboundOptions) (adapter.Inbound, error) {
|
func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TUICInboundOptions) (*TUIC, 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
|
||||||
@@ -45,14 +40,16 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
inbound := &Inbound{
|
inbound := &TUIC{
|
||||||
Adapter: inbound.NewAdapter(C.TypeTUIC, tag),
|
myInboundAdapter: myInboundAdapter{
|
||||||
router: uot.NewRouter(router, logger),
|
protocol: C.TypeTUIC,
|
||||||
listener: listener.New(listener.Options{
|
network: []string{N.NetworkUDP},
|
||||||
Context: ctx,
|
ctx: ctx,
|
||||||
Logger: logger,
|
router: uot.NewRouter(router, logger),
|
||||||
Listen: options.ListenOptions,
|
logger: logger,
|
||||||
}),
|
tag: tag,
|
||||||
|
listenOptions: options.ListenOptions,
|
||||||
|
},
|
||||||
tlsConfig: tlsConfig,
|
tlsConfig: tlsConfig,
|
||||||
}
|
}
|
||||||
var udpTimeout time.Duration
|
var udpTimeout time.Duration
|
||||||
@@ -98,12 +95,9 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
return inbound, nil
|
return inbound, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (h *TUIC) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
ctx = log.ContextWithNewID(ctx)
|
ctx = log.ContextWithNewID(ctx)
|
||||||
metadata.Inbound = h.Tag()
|
metadata = h.createMetadata(conn, metadata)
|
||||||
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 != "" {
|
||||||
@@ -115,13 +109,9 @@ func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata ada
|
|||||||
return h.router.RouteConnection(ctx, conn, metadata)
|
return h.router.RouteConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
func (h *TUIC) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
ctx = log.ContextWithNewID(ctx)
|
ctx = log.ContextWithNewID(ctx)
|
||||||
metadata.Inbound = h.Tag()
|
metadata = h.createPacketMetadata(conn, metadata)
|
||||||
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 != "" {
|
||||||
@@ -133,23 +123,23 @@ func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, me
|
|||||||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) Start() error {
|
func (h *TUIC) 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.listener.ListenUDP()
|
packetConn, err := h.myInboundAdapter.ListenUDP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return h.server.Start(packetConn)
|
return h.server.Start(packetConn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) Close() error {
|
func (h *TUIC) Close() error {
|
||||||
return common.Close(
|
return common.Close(
|
||||||
&h.listener,
|
&h.myInboundAdapter,
|
||||||
h.tlsConfig,
|
h.tlsConfig,
|
||||||
common.PtrOrNil(h.server),
|
common.PtrOrNil(h.server),
|
||||||
)
|
)
|
||||||
16
inbound/tuic_stub.go
Normal file
16
inbound/tuic_stub.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
//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
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user