Crazy sekai overturns the small pond

This commit is contained in:
世界 2024-10-21 23:38:34 +08:00
parent 1086d5e665
commit 56e209fa50
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
139 changed files with 2852 additions and 1554 deletions

View file

@ -1,104 +0,0 @@
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)
}

View file

@ -6,27 +6,53 @@ 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
}

View file

@ -2,13 +2,11 @@ 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/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 {
@ -17,11 +15,14 @@ type Inbound interface {
Tag() string Tag() string
} }
type InjectableInbound interface { type TCPInjectableInbound interface {
Inbound Inbound
Network() []string ConnectionHandlerEx
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 InboundContext struct { type InboundContext struct {
@ -43,16 +44,18 @@ type InboundContext struct {
// cache // cache
InboundDetour string InboundDetour string
LastInbound string LastInbound string
OriginDestination M.Socksaddr OriginDestination M.Socksaddr
InboundOptions option.InboundOptions // Deprecated
DestinationAddresses []netip.Addr InboundOptions option.InboundOptions
SourceGeoIPCode string UDPDisableDomainUnmapping bool
GeoIPCode string DestinationAddresses []netip.Addr
ProcessInfo *process.Info SourceGeoIPCode string
QueryType uint16 GeoIPCode string
FakeIP bool ProcessInfo *process.Info
QueryType uint16
FakeIP bool
// rule cache // rule cache

View file

@ -1,9 +1,6 @@
package adapter package adapter
import ( import (
"context"
"net"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
) )
@ -15,6 +12,4 @@ 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
} }

View file

@ -34,6 +34,7 @@ type Router interface {
FakeIPStore() FakeIPStore FakeIPStore() FakeIPStore
ConnectionRouter ConnectionRouter
ConnectionRouterEx
GeoIPReader() *geoip.Reader GeoIPReader() *geoip.Reader
LoadGeosite(code string) (Rule, error) LoadGeosite(code string) (Rule, error)
@ -70,6 +71,18 @@ 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)
} }
@ -78,28 +91,6 @@ 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

38
adapter/rule.go Normal file
View file

@ -0,0 +1,38 @@
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
}
}

View file

@ -4,112 +4,165 @@ 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 (
ConnectionHandlerFunc = func(ctx context.Context, conn net.Conn, metadata InboundContext) error ConnectionHandlerFuncEx = func(ctx context.Context, conn net.Conn, metadata InboundContext, onClose N.CloseHandlerFunc)
PacketConnectionHandlerFunc = func(ctx context.Context, conn N.PacketConn, metadata InboundContext) error PacketConnectionHandlerFuncEx = func(ctx context.Context, conn N.PacketConn, metadata InboundContext, onClose N.CloseHandlerFunc)
) )
func NewUpstreamHandler( func NewUpstreamHandlerEx(
metadata InboundContext, metadata InboundContext,
connectionHandler ConnectionHandlerFunc, connectionHandler ConnectionHandlerFuncEx,
packetHandler PacketConnectionHandlerFunc, packetHandler PacketConnectionHandlerFuncEx,
errorHandler E.Handler, ) UpstreamHandlerAdapterEx {
) UpstreamHandlerAdapter { return &myUpstreamHandlerWrapperEx{
return &myUpstreamHandlerWrapper{
metadata: metadata, metadata: metadata,
connectionHandler: connectionHandler, connectionHandler: connectionHandler,
packetHandler: packetHandler, packetHandler: packetHandler,
errorHandler: errorHandler,
} }
} }
var _ UpstreamHandlerAdapter = (*myUpstreamHandlerWrapper)(nil) var _ UpstreamHandlerAdapterEx = (*myUpstreamHandlerWrapperEx)(nil)
type myUpstreamHandlerWrapper struct { type myUpstreamHandlerWrapperEx struct {
metadata InboundContext metadata InboundContext
connectionHandler ConnectionHandlerFunc connectionHandler ConnectionHandlerFuncEx
packetHandler PacketConnectionHandlerFunc packetHandler PacketConnectionHandlerFuncEx
errorHandler E.Handler
} }
func (w *myUpstreamHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { func (w *myUpstreamHandlerWrapperEx) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
myMetadata := w.metadata myMetadata := w.metadata
if metadata.Source.IsValid() { if source.IsValid() {
myMetadata.Source = metadata.Source myMetadata.Source = source
} }
if metadata.Destination.IsValid() { if destination.IsValid() {
myMetadata.Destination = metadata.Destination myMetadata.Destination = destination
} }
return w.connectionHandler(ctx, conn, myMetadata) w.connectionHandler(ctx, conn, myMetadata, onClose)
} }
func (w *myUpstreamHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error { func (w *myUpstreamHandlerWrapperEx) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
myMetadata := w.metadata myMetadata := w.metadata
if metadata.Source.IsValid() { if source.IsValid() {
myMetadata.Source = metadata.Source myMetadata.Source = source
} }
if metadata.Destination.IsValid() { if destination.IsValid() {
myMetadata.Destination = metadata.Destination myMetadata.Destination = destination
} }
return w.packetHandler(ctx, conn, myMetadata) w.packetHandler(ctx, conn, myMetadata, onClose)
} }
func (w *myUpstreamHandlerWrapper) NewError(ctx context.Context, err error) { var _ UpstreamHandlerAdapterEx = (*myUpstreamContextHandlerWrapperEx)(nil)
w.errorHandler.NewError(ctx, err)
type myUpstreamContextHandlerWrapperEx struct {
connectionHandler ConnectionHandlerFuncEx
packetHandler PacketConnectionHandlerFuncEx
} }
func UpstreamMetadata(metadata InboundContext) M.Metadata { func NewUpstreamContextHandlerEx(
return M.Metadata{ connectionHandler ConnectionHandlerFuncEx,
Source: metadata.Source, packetHandler PacketConnectionHandlerFuncEx,
Destination: metadata.Destination, ) UpstreamHandlerAdapterEx {
} 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 *myUpstreamContextHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { func (w *myUpstreamContextHandlerWrapperEx) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
myMetadata := ContextFrom(ctx) myMetadata := ContextFrom(ctx)
if metadata.Source.IsValid() { if source.IsValid() {
myMetadata.Source = metadata.Source myMetadata.Source = source
} }
if metadata.Destination.IsValid() { if destination.IsValid() {
myMetadata.Destination = metadata.Destination myMetadata.Destination = destination
} }
return w.connectionHandler(ctx, conn, *myMetadata) w.connectionHandler(ctx, conn, *myMetadata, onClose)
} }
func (w *myUpstreamContextHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error { func (w *myUpstreamContextHandlerWrapperEx) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
myMetadata := ContextFrom(ctx) myMetadata := ContextFrom(ctx)
if metadata.Source.IsValid() { if source.IsValid() {
myMetadata.Source = metadata.Source myMetadata.Source = source
} }
if metadata.Destination.IsValid() { if destination.IsValid() {
myMetadata.Destination = metadata.Destination myMetadata.Destination = destination
} }
return w.packetHandler(ctx, conn, *myMetadata) w.packetHandler(ctx, conn, *myMetadata, onClose)
} }
func (w *myUpstreamContextHandlerWrapper) NewError(ctx context.Context, err error) { func NewRouteHandlerEx(
w.errorHandler.NewError(ctx, err) metadata InboundContext,
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)
} }

216
adapter/upstream_legacy.go Normal file
View file

@ -0,0 +1,216 @@
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)
}

View file

@ -4,7 +4,6 @@ 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"
) )
@ -16,8 +15,7 @@ type V2RayServerTransport interface {
} }
type V2RayServerTransportHandler interface { type V2RayServerTransportHandler interface {
N.TCPConnectionHandler N.TCPConnectionHandlerEx
E.Handler
} }
type V2RayClientTransport interface { type V2RayClientTransport interface {

View file

@ -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" "github.com/sagernet/sing-box/route/rule"
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"
@ -83,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 = route.NewHeadlessRule(nil, ruleOptions) currentRule, err = rule.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, "]")
} }

View file

@ -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" "github.com/sagernet/sing-box/route/rule"
"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 := route.NewHeadlessRule(nil, rules[0]) rule, err := rule.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 := route.NewHeadlessRule(nil, rules[0]) rule, err := rule.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 := route.NewHeadlessRule(nil, rules[0]) rule, err := rule.NewHeadlessRule(nil, rules[0])
require.NoError(t, err) require.NoError(t, err)
matchDomain := []string{ matchDomain := []string{
"example.com", "example.com",

View file

@ -15,11 +15,11 @@ import (
) )
type Router struct { type Router struct {
router adapter.ConnectionRouter router adapter.ConnectionRouterEx
service *mux.Service service *mux.Service
} }
func NewRouterWithOptions(router adapter.ConnectionRouter, logger logger.ContextLogger, options option.InboundMultiplexOptions) (adapter.ConnectionRouter, error) { func NewRouterWithOptions(router adapter.ConnectionRouterEx, logger logger.ContextLogger, options option.InboundMultiplexOptions) (adapter.ConnectionRouterEx, error) {
if !options.Enabled { if !options.Enabled {
return router, nil return router, nil
} }
@ -54,6 +54,7 @@ func NewRouterWithOptions(router adapter.ConnectionRouter, logger logger.Context
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)
@ -63,3 +64,15 @@ 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)
}

View file

@ -1,32 +0,0 @@
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)
}

View file

@ -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:

View file

@ -13,14 +13,14 @@ import (
"github.com/sagernet/sing/common/uot" "github.com/sagernet/sing/common/uot"
) )
var _ adapter.ConnectionRouter = (*Router)(nil) var _ adapter.ConnectionRouterEx = (*Router)(nil)
type Router struct { type Router struct {
router adapter.ConnectionRouter router adapter.ConnectionRouterEx
logger logger.ContextLogger logger logger.ContextLogger
} }
func NewRouter(router adapter.ConnectionRouter, logger logger.ContextLogger) *Router { func NewRouter(router adapter.ConnectionRouterEx, logger logger.ContextLogger) *Router {
return &Router{router, logger} return &Router{router, logger}
} }
@ -51,3 +51,36 @@ 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)
}

View file

@ -23,3 +23,18 @@ const (
RuleSetVersion2 RuleSetVersion2
RuleSetVersionCurrent = RuleSetVersion2 RuleSetVersionCurrent = RuleSetVersion2
) )
const (
RuleActionTypeRoute = "route"
RuleActionTypeReturn = "return"
RuleActionTypeReject = "reject"
RuleActionTypeHijackDNS = "hijack-dns"
RuleActionTypeSniff = "sniff"
RuleActionTypeResolve = "resolve"
)
const (
RuleActionRejectMethodDefault = "default"
RuleActionRejectMethodPortUnreachable = "port-unreachable"
RuleActionRejectMethodDrop = "drop"
)

View file

@ -30,10 +30,9 @@ 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.Outbound(), Proxy: rule.Action().String(),
}) })
} }
render.JSON(w, r, render.M{ render.JSON(w, r, render.M{
"rules": rules, "rules": rules,
}) })

View file

@ -5,6 +5,7 @@ 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"
@ -60,7 +61,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.Outbound()) rule = F.ToString(t.Rule, " => ", t.Rule.Action())
} else { } else {
rule = "final" rule = "final"
} }
@ -131,19 +132,21 @@ func NewTCPTracker(conn net.Conn, manager *Manager, metadata adapter.InboundCont
outbound string outbound string
outboundType string outboundType string
) )
if rule == nil { var action adapter.RuleAction
if defaultOutbound, err := router.DefaultOutbound(N.NetworkTCP); err == nil { if rule != nil {
next = defaultOutbound.Tag() action = rule.Action()
} }
} else { if routeAction, isRouteAction := action.(*R.RuleActionRoute); isRouteAction {
next = rule.Outbound() next = routeAction.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)
@ -218,19 +221,21 @@ func NewUDPTracker(conn N.PacketConn, manager *Manager, metadata adapter.Inbound
outbound string outbound string
outboundType string outboundType string
) )
if rule == nil { var action adapter.RuleAction
if defaultOutbound, err := router.DefaultOutbound(N.NetworkUDP); err == nil { if rule != nil {
next = defaultOutbound.Tag() action = rule.Action()
} }
} else { if routeAction, isRouteAction := action.(*R.RuleActionRoute); isRouteAction {
next = rule.Outbound() next = routeAction.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)

12
go.mod
View file

@ -27,14 +27,14 @@ require (
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff
github.com/sagernet/quic-go v0.48.1-beta.1 github.com/sagernet/quic-go v0.48.1-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.1 github.com/sagernet/sing v0.6.0-alpha.20
github.com/sagernet/sing-dns v0.3.0 github.com/sagernet/sing-dns v0.4.0-alpha.3
github.com/sagernet/sing-mux v0.2.1 github.com/sagernet/sing-mux v0.3.0-alpha.1
github.com/sagernet/sing-quic v0.3.1 github.com/sagernet/sing-quic v0.4.0-alpha.4
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.5 github.com/sagernet/sing-shadowtls v0.2.0-alpha.2
github.com/sagernet/sing-tun v0.4.2 github.com/sagernet/sing-tun v0.6.0-alpha.14
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

24
go.sum
View file

@ -115,22 +115,22 @@ github.com/sagernet/quic-go v0.48.1-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/
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.1 h1:mhL/MZVq0TjuvHcpYcFtmSD1BFOxZ/+8ofbNZcg1k1Y= github.com/sagernet/sing v0.6.0-alpha.20 h1:coxvnzeEGSLNNPntUW7l8WUEHPIwqKszZNbU019To9c=
github.com/sagernet/sing v0.5.1/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/sagernet/sing v0.6.0-alpha.20/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing-dns v0.3.0 h1:uHCIlbCwBxALJwXcEK1d75d7t3vzCSVEQsPfZR1cxQE= github.com/sagernet/sing-dns v0.4.0-alpha.3 h1:TcAQdz68Gs28VD9o9zDIW7IS8A9LZDruTPI9g9JbGHA=
github.com/sagernet/sing-dns v0.3.0/go.mod h1:TqLIelI+FAbVEdiTRolhGLOwvhVjY7oT+wezlOJUQ7M= github.com/sagernet/sing-dns v0.4.0-alpha.3/go.mod h1:9LHcYKg2bGQpbtXrfNbopz8ok/zBK9ljiI2kmFG9JKg=
github.com/sagernet/sing-mux v0.2.1 h1:N/3MHymfnFZRd29tE3TaXwPUVVgKvxhtOkiCMLp9HVo= github.com/sagernet/sing-mux v0.3.0-alpha.1 h1:IgNX5bJBpL41gGbp05pdDOvh/b5eUQ6cv9240+Ngipg=
github.com/sagernet/sing-mux v0.2.1/go.mod h1:dm3BWL6NvES9pbib7llpylrq7Gq+LjlzG+0RacdxcyE= github.com/sagernet/sing-mux v0.3.0-alpha.1/go.mod h1:FTcImmdfW38Lz7b+HQ+mxxOth1lz4ao8uEnz+MwIJQE=
github.com/sagernet/sing-quic v0.3.1 h1:kLg2n4JPnuzUPg7myJGbfGVJGeXiccXfV+PhXIlkSEc= github.com/sagernet/sing-quic v0.4.0-alpha.4 h1:P9xAx3nIfcqb9M8jfgs0uLm+VxCcaY++FCqaBfHY3dQ=
github.com/sagernet/sing-quic v0.3.1/go.mod h1:g8b5Fj88KRM0H9lpKAxJj0EpkL/Yk06qXJAG7FuZd2I= github.com/sagernet/sing-quic v0.4.0-alpha.4/go.mod h1:h5RkKTmUhudJKzK7c87FPXD5w1bJjVyxMN9+opZcctA=
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.5 h1:uXxmq/HXh8DIiBGLzpMjCbWnzIAFs+lIxiTOjdgG5qo= github.com/sagernet/sing-shadowtls v0.2.0-alpha.2 h1:RPrpgAdkP5td0vLfS5ldvYosFjSsZtRPxiyLV6jyKg0=
github.com/sagernet/sing-shadowtls v0.1.5/go.mod h1:tvrDPTGLrSM46Wnf7mSr+L8NHvgvF8M4YnJF790rZX4= github.com/sagernet/sing-shadowtls v0.2.0-alpha.2/go.mod h1:0j5XlzKxaWRIEjc1uiSKmVoWb0k+L9QgZVb876+thZA=
github.com/sagernet/sing-tun v0.4.2 h1:GCP7TI/gwDH/iFIugYS3WcVhCcbDE6qwAbjYQ5W/m+E= github.com/sagernet/sing-tun v0.6.0-alpha.14 h1:0nE66HdC6nBSOaUG0CEV5rwB5Te3Gts9buVOPvWrGT4=
github.com/sagernet/sing-tun v0.4.2/go.mod h1:1WQVMelJQjrtlzhzHwwPTSa7n41b3zSWP2DeJqWxruk= github.com/sagernet/sing-tun v0.6.0-alpha.14/go.mod h1:xvZlEl1EGBbQeshv4UXmG7hA3f0ngFjpdCIYk308vfg=
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=

View file

@ -22,13 +22,13 @@ type myInboundAdapter struct {
protocol string protocol string
network []string network []string
ctx context.Context ctx context.Context
router adapter.ConnectionRouter router adapter.ConnectionRouterEx
logger log.ContextLogger logger log.ContextLogger
tag string tag string
listenOptions option.ListenOptions listenOptions option.ListenOptions
connHandler adapter.ConnectionHandler connHandler adapter.ConnectionHandlerEx
packetHandler adapter.PacketHandler packetHandler adapter.PacketHandlerEx
oobPacketHandler adapter.OOBPacketHandler oobPacketHandler adapter.OOBPacketHandlerEx
packetUpstream any packetUpstream any
// http mixed // http mixed
@ -55,10 +55,6 @@ func (a *myInboundAdapter) Tag() string {
return a.tag return a.tag
} }
func (a *myInboundAdapter) Network() []string {
return a.network
}
func (a *myInboundAdapter) Start() error { func (a *myInboundAdapter) Start() error {
var err error var err error
if common.Contains(a.network, N.NetworkTCP) { if common.Contains(a.network, N.NetworkTCP) {
@ -150,6 +146,31 @@ func (a *myInboundAdapter) newPacketConnection(ctx context.Context, conn N.Packe
return a.router.RoutePacketConnection(ctx, conn, metadata) return a.router.RoutePacketConnection(ctx, conn, metadata)
} }
func (a *myInboundAdapter) upstreamHandlerEx(metadata adapter.InboundContext) adapter.UpstreamHandlerAdapterEx {
return adapter.NewUpstreamHandlerEx(metadata, a.newConnectionEx, a.streamPacketConnectionEx)
}
func (a *myInboundAdapter) upstreamContextHandlerEx() adapter.UpstreamHandlerAdapterEx {
return adapter.NewUpstreamContextHandlerEx(a.newConnectionEx, a.newPacketConnectionEx)
}
func (a *myInboundAdapter) newConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
a.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
a.router.RouteConnectionEx(ctx, conn, metadata, onClose)
}
func (a *myInboundAdapter) newPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
ctx = log.ContextWithNewID(ctx)
a.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
a.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
a.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
}
func (a *myInboundAdapter) streamPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
a.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
a.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
}
func (a *myInboundAdapter) createMetadata(conn net.Conn, metadata adapter.InboundContext) adapter.InboundContext { func (a *myInboundAdapter) createMetadata(conn net.Conn, metadata adapter.InboundContext) adapter.InboundContext {
metadata.Inbound = a.tag metadata.Inbound = a.tag
metadata.InboundType = a.protocol metadata.InboundType = a.protocol
@ -167,25 +188,17 @@ func (a *myInboundAdapter) createMetadata(conn net.Conn, metadata adapter.Inboun
return metadata return metadata
} }
func (a *myInboundAdapter) createPacketMetadata(conn N.PacketConn, metadata adapter.InboundContext) adapter.InboundContext { // Deprecated: don't use
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) { func (a *myInboundAdapter) newError(err error) {
a.logger.Error(err) a.logger.Error(err)
} }
// Deprecated: don't use
func (a *myInboundAdapter) NewError(ctx context.Context, err error) { func (a *myInboundAdapter) NewError(ctx context.Context, err error) {
NewError(a.logger, ctx, err) NewError(a.logger, ctx, err)
} }
// Deprecated: don't use
func NewError(logger log.ContextLogger, ctx context.Context, err error) { func NewError(logger log.ContextLogger, ctx context.Context, err error) {
common.Close(err) common.Close(err)
if E.IsClosedOrCanceled(err) { if E.IsClosedOrCanceled(err) {

View file

@ -71,18 +71,14 @@ func (a *myInboundAdapter) injectTCP(conn net.Conn, metadata adapter.InboundCont
ctx := log.ContextWithNewID(a.ctx) ctx := log.ContextWithNewID(a.ctx)
metadata = a.createMetadata(conn, metadata) metadata = a.createMetadata(conn, metadata)
a.logger.InfoContext(ctx, "inbound connection from ", metadata.Source) a.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
hErr := a.connHandler.NewConnection(ctx, conn, metadata) a.connHandler.NewConnectionEx(ctx, conn, metadata, nil)
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) { func (a *myInboundAdapter) routeTCP(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
metadata := a.createMetadata(conn, adapter.InboundContext{
Source: source,
Destination: destination,
})
a.logger.InfoContext(ctx, "inbound connection from ", metadata.Source) a.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
hErr := a.newConnection(ctx, conn, metadata) a.connHandler.NewConnectionEx(ctx, conn, metadata, onClose)
if hErr != nil {
conn.Close()
a.NewError(ctx, E.Cause(hErr, "process connection from ", metadata.Source))
}
} }

View file

@ -42,7 +42,6 @@ func (a *myInboundAdapter) loopUDPIn() {
defer buffer.Release() defer buffer.Release()
buffer.IncRef() buffer.IncRef()
defer buffer.DecRef() defer buffer.DecRef()
packetService := (*myInboundPacketAdapter)(a)
for { for {
buffer.Reset() buffer.Reset()
n, addr, err := a.udpConn.ReadFromUDPAddrPort(buffer.FreeBytes()) n, addr, err := a.udpConn.ReadFromUDPAddrPort(buffer.FreeBytes())
@ -50,16 +49,7 @@ func (a *myInboundAdapter) loopUDPIn() {
return return
} }
buffer.Truncate(n) buffer.Truncate(n)
var metadata adapter.InboundContext a.packetHandler.NewPacketEx(buffer, M.SocksaddrFromNetIP(addr).Unwrap())
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))
}
} }
} }
@ -69,7 +59,6 @@ func (a *myInboundAdapter) loopUDPOOBIn() {
defer buffer.Release() defer buffer.Release()
buffer.IncRef() buffer.IncRef()
defer buffer.DecRef() defer buffer.DecRef()
packetService := (*myInboundPacketAdapter)(a)
oob := make([]byte, 1024) oob := make([]byte, 1024)
for { for {
buffer.Reset() buffer.Reset()
@ -78,22 +67,12 @@ func (a *myInboundAdapter) loopUDPOOBIn() {
return return
} }
buffer.Truncate(n) buffer.Truncate(n)
var metadata adapter.InboundContext a.oobPacketHandler.NewPacketEx(buffer, oob[:oobN], M.SocksaddrFromNetIP(addr).Unwrap())
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() { func (a *myInboundAdapter) loopUDPInThreadSafe() {
defer close(a.packetOutboundClosed) defer close(a.packetOutboundClosed)
packetService := (*myInboundPacketAdapter)(a)
for { for {
buffer := buf.NewPacket() buffer := buf.NewPacket()
n, addr, err := a.udpConn.ReadFromUDPAddrPort(buffer.FreeBytes()) n, addr, err := a.udpConn.ReadFromUDPAddrPort(buffer.FreeBytes())
@ -102,23 +81,12 @@ func (a *myInboundAdapter) loopUDPInThreadSafe() {
return return
} }
buffer.Truncate(n) buffer.Truncate(n)
var metadata adapter.InboundContext a.packetHandler.NewPacketEx(buffer, M.SocksaddrFromNetIP(addr).Unwrap())
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() { func (a *myInboundAdapter) loopUDPOOBInThreadSafe() {
defer close(a.packetOutboundClosed) defer close(a.packetOutboundClosed)
packetService := (*myInboundPacketAdapter)(a)
oob := make([]byte, 1024) oob := make([]byte, 1024)
for { for {
buffer := buf.NewPacket() buffer := buf.NewPacket()
@ -128,17 +96,7 @@ func (a *myInboundAdapter) loopUDPOOBInThreadSafe() {
return return
} }
buffer.Truncate(n) buffer.Truncate(n)
var metadata adapter.InboundContext a.oobPacketHandler.NewPacketEx(buffer, oob[:oobN], M.SocksaddrFromNetIP(addr).Unwrap())
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))
}
} }
} }
@ -148,7 +106,7 @@ func (a *myInboundAdapter) loopUDPOut() {
case packet := <-a.packetOutbound: case packet := <-a.packetOutbound:
err := a.writePacket(packet.buffer, packet.destination) err := a.writePacket(packet.buffer, packet.destination)
if err != nil && !E.IsClosed(err) { if err != nil && !E.IsClosed(err) {
a.newError(E.New("write back udp: ", err)) a.logger.Error(E.New("write back udp: ", err))
} }
continue continue
case <-a.packetOutboundClosed: case <-a.packetOutboundClosed:
@ -164,15 +122,36 @@ func (a *myInboundAdapter) loopUDPOut() {
} }
} }
func (a *myInboundAdapter) packetConn() N.PacketConn {
return (*myInboundPacketAdapter)(a)
}
func (a *myInboundAdapter) createPacketMetadata(conn N.PacketConn, metadata adapter.InboundContext) adapter.InboundContext {
metadata.Inbound = a.tag
metadata.InboundType = a.protocol
metadata.InboundDetour = a.listenOptions.Detour
metadata.InboundOptions = a.listenOptions.InboundOptions
if !metadata.Destination.IsValid() {
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
}
metadata.OriginDestination = a.udpAddr
return metadata
}
func (a *myInboundAdapter) createPacketMetadataEx(source M.Socksaddr, destination M.Socksaddr) adapter.InboundContext {
var metadata adapter.InboundContext
metadata.Inbound = a.tag
metadata.InboundType = a.protocol
metadata.InboundDetour = a.listenOptions.Detour
metadata.InboundOptions = a.listenOptions.InboundOptions
metadata.Source = source
metadata.Destination = destination
metadata.OriginDestination = a.udpAddr
return metadata
}
func (a *myInboundAdapter) writePacket(buffer *buf.Buffer, destination M.Socksaddr) error { func (a *myInboundAdapter) writePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
defer buffer.Release() 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())) return common.Error(a.udpConn.WriteToUDPAddrPort(buffer.Bytes(), destination.AddrPort()))
} }

View file

@ -3,7 +3,6 @@ package inbound
import ( import (
"context" "context"
"net" "net"
"net/netip"
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@ -13,14 +12,14 @@ import (
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/udpnat" "github.com/sagernet/sing/common/udpnat2"
) )
var _ adapter.Inbound = (*Direct)(nil) var _ adapter.Inbound = (*Direct)(nil)
type Direct struct { type Direct struct {
myInboundAdapter myInboundAdapter
udpNat *udpnat.Service[netip.AddrPort] udpNat *udpnat.Service
overrideOption int overrideOption int
overrideDestination M.Socksaddr overrideDestination M.Socksaddr
} }
@ -54,10 +53,9 @@ func NewDirect(ctx context.Context, router adapter.Router, logger log.ContextLog
} else { } else {
udpTimeout = C.UDPTimeout udpTimeout = C.UDPTimeout
} }
inbound.udpNat = udpnat.New[netip.AddrPort](int64(udpTimeout.Seconds()), adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound)) inbound.udpNat = udpnat.New(inbound, inbound.preparePacketConnection, udpTimeout, false)
inbound.connHandler = inbound inbound.connHandler = inbound
inbound.packetHandler = inbound inbound.packetHandler = inbound
inbound.packetUpstream = inbound.udpNat
return inbound return inbound
} }
@ -76,29 +74,38 @@ func (d *Direct) NewConnection(ctx context.Context, conn net.Conn, metadata adap
return d.router.RouteConnection(ctx, conn, metadata) return d.router.RouteConnection(ctx, conn, metadata)
} }
func (d *Direct) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext) error { func (d *Direct) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
var destination M.Socksaddr
switch d.overrideOption { switch d.overrideOption {
case 1: case 1:
metadata.Destination = d.overrideDestination destination = d.overrideDestination
case 2: case 2:
destination := d.overrideDestination destination = d.overrideDestination
destination.Port = metadata.Destination.Port destination.Port = source.Port
metadata.Destination = destination
case 3: case 3:
metadata.Destination.Port = d.overrideDestination.Port destination = source
destination.Port = d.overrideDestination.Port
} }
d.udpNat.NewContextPacket(ctx, metadata.Source.AddrPort(), buffer, adapter.UpstreamMetadata(metadata), func(natConn N.PacketConn) (context.Context, N.PacketWriter) { d.udpNat.NewPacket([][]byte{buffer.Bytes()}, source, destination, nil)
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 { func (d *Direct) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
return d.router.RouteConnection(ctx, conn, metadata) d.newConnectionEx(ctx, conn, metadata, onClose)
} }
func (d *Direct) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { func (d *Direct) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
ctx = log.ContextWithNewID(ctx) d.newPacketConnectionEx(ctx, conn, d.createPacketMetadataEx(source, destination), onClose)
d.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source) }
return d.router.RoutePacketConnection(ctx, conn, metadata)
func (d *Direct) preparePacketConnection(source M.Socksaddr, destination M.Socksaddr, userData any) (bool, context.Context, N.PacketWriter, N.CloseHandlerFunc) {
return true, d.ctx, &directPacketWriter{d.packetConn(), source}, nil
}
type directPacketWriter struct {
writer N.PacketWriter
source M.Socksaddr
}
func (w *directPacketWriter) WritePacket(buffer *buf.Buffer, addr M.Socksaddr) error {
return w.writer.WritePacket(buffer, w.source)
} }

View file

@ -4,7 +4,6 @@ import (
std_bufio "bufio" std_bufio "bufio"
"context" "context"
"net" "net"
"os"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/tls" "github.com/sagernet/sing-box/common/tls"
@ -20,8 +19,8 @@ import (
) )
var ( var (
_ adapter.Inbound = (*HTTP)(nil) _ adapter.Inbound = (*HTTP)(nil)
_ adapter.InjectableInbound = (*HTTP)(nil) _ adapter.TCPInjectableInbound = (*HTTP)(nil)
) )
type HTTP struct { type HTTP struct {
@ -72,7 +71,15 @@ func (h *HTTP) Close() error {
) )
} }
func (h *HTTP) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (h *HTTP) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
err := h.newConnection(ctx, conn, metadata, onClose)
N.CloseOnHandshakeFailure(conn, onClose, err)
if err != nil {
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
}
}
func (h *HTTP) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) error {
var err error var err error
if h.tlsConfig != nil { if h.tlsConfig != nil {
conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig) conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
@ -80,35 +87,33 @@ func (h *HTTP) NewConnection(ctx context.Context, conn net.Conn, metadata adapte
return err return err
} }
} }
return http.HandleConnection(ctx, conn, std_bufio.NewReader(conn), h.authenticator, h.upstreamUserHandler(metadata), adapter.UpstreamMetadata(metadata)) return http.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, nil, h.upstreamUserHandlerEx(metadata), metadata.Source, onClose)
} }
func (h *HTTP) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { func (a *myInboundAdapter) upstreamUserHandlerEx(metadata adapter.InboundContext) adapter.UpstreamHandlerAdapterEx {
return os.ErrInvalid return adapter.NewUpstreamHandlerEx(metadata, a.newUserConnection, a.streamUserPacketConnection)
} }
func (a *myInboundAdapter) upstreamUserHandler(metadata adapter.InboundContext) adapter.UpstreamHandlerAdapter { func (a *myInboundAdapter) newUserConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
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) user, loaded := auth.UserFromContext[string](ctx)
if !loaded { if !loaded {
a.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination) a.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
return a.router.RouteConnection(ctx, conn, metadata) a.router.RouteConnectionEx(ctx, conn, metadata, onClose)
return
} }
metadata.User = user metadata.User = user
a.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination) a.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination)
return a.router.RouteConnection(ctx, conn, metadata) a.router.RouteConnectionEx(ctx, conn, metadata, onClose)
} }
func (a *myInboundAdapter) streamUserPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { func (a *myInboundAdapter) streamUserPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
user, loaded := auth.UserFromContext[string](ctx) user, loaded := auth.UserFromContext[string](ctx)
if !loaded { if !loaded {
a.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination) a.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
return a.router.RoutePacketConnection(ctx, conn, metadata) a.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
return
} }
metadata.User = user metadata.User = user
a.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination) a.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
return a.router.RoutePacketConnection(ctx, conn, metadata) a.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
} }

View file

@ -4,7 +4,6 @@ import (
std_bufio "bufio" std_bufio "bufio"
"context" "context"
"net" "net"
"os"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/uot" "github.com/sagernet/sing-box/common/uot"
@ -12,6 +11,7 @@ import (
"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/auth" "github.com/sagernet/sing/common/auth"
E "github.com/sagernet/sing/common/exceptions"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/protocol/http" "github.com/sagernet/sing/protocol/http"
"github.com/sagernet/sing/protocol/socks" "github.com/sagernet/sing/protocol/socks"
@ -20,8 +20,8 @@ import (
) )
var ( var (
_ adapter.Inbound = (*Mixed)(nil) _ adapter.Inbound = (*Mixed)(nil)
_ adapter.InjectableInbound = (*Mixed)(nil) _ adapter.TCPInjectableInbound = (*Mixed)(nil)
) )
type Mixed struct { type Mixed struct {
@ -47,20 +47,24 @@ func NewMixed(ctx context.Context, router adapter.Router, logger log.ContextLogg
return inbound return inbound
} }
func (h *Mixed) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (h *Mixed) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
reader := std_bufio.NewReader(conn) err := h.newConnection(ctx, conn, metadata, onClose)
headerBytes, err := reader.Peek(1) N.CloseOnHandshakeFailure(conn, onClose, err)
if err != nil { if err != nil {
return err h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
}
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 { func (h *Mixed) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) error {
return os.ErrInvalid reader := std_bufio.NewReader(conn)
headerBytes, err := reader.Peek(1)
if err != nil {
return E.Cause(err, "peek first byte")
}
switch headerBytes[0] {
case socks4.Version, socks5.Version:
return socks.HandleConnectionEx(ctx, conn, reader, h.authenticator, nil, h.upstreamUserHandlerEx(metadata), metadata.Source, metadata.Destination, onClose)
default:
return http.HandleConnectionEx(ctx, conn, reader, h.authenticator, nil, h.upstreamUserHandlerEx(metadata), metadata.Source, onClose)
}
} }

View file

@ -17,6 +17,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/transport/v2rayhttp"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/auth" "github.com/sagernet/sing/common/auth"
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
@ -164,13 +165,13 @@ func (n *Naive) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
n.badRequest(ctx, request, E.New("hijack failed")) n.badRequest(ctx, request, E.New("hijack failed"))
return return
} }
n.newConnection(ctx, &naiveH1Conn{Conn: conn}, userName, source, destination) n.newConnection(ctx, false, &naiveH1Conn{Conn: conn}, userName, source, destination)
} else { } else {
n.newConnection(ctx, &naiveH2Conn{reader: request.Body, writer: writer, flusher: writer.(http.Flusher)}, userName, source, destination) n.newConnection(ctx, true, &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) { func (n *Naive) newConnection(ctx context.Context, waitForClose bool, conn net.Conn, userName string, source M.Socksaddr, destination M.Socksaddr) {
if userName != "" { if userName != "" {
n.logger.InfoContext(ctx, "[", userName, "] inbound connection from ", source) n.logger.InfoContext(ctx, "[", userName, "] inbound connection from ", source)
n.logger.InfoContext(ctx, "[", userName, "] inbound connection to ", destination) n.logger.InfoContext(ctx, "[", userName, "] inbound connection to ", destination)
@ -178,19 +179,26 @@ func (n *Naive) newConnection(ctx context.Context, conn net.Conn, userName strin
n.logger.InfoContext(ctx, "inbound connection from ", source) n.logger.InfoContext(ctx, "inbound connection from ", source)
n.logger.InfoContext(ctx, "inbound connection to ", destination) n.logger.InfoContext(ctx, "inbound connection to ", destination)
} }
hErr := n.router.RouteConnection(ctx, conn, n.createMetadata(conn, adapter.InboundContext{ metadata := n.createMetadata(conn, adapter.InboundContext{
Source: source, Source: source,
Destination: destination, Destination: destination,
User: userName, User: userName,
})) })
if hErr != nil { if !waitForClose {
conn.Close() n.router.RouteConnectionEx(ctx, conn, metadata, nil)
n.NewError(ctx, E.Cause(hErr, "process connection from ", source)) } else {
done := make(chan struct{})
wrapper := v2rayhttp.NewHTTP2Wrapper(conn)
n.router.RouteConnectionEx(ctx, conn, metadata, N.OnceClose(func(it error) {
close(done)
}))
<-done
wrapper.CloseWrapper()
} }
} }
func (n *Naive) badRequest(ctx context.Context, request *http.Request, err error) { func (n *Naive) badRequest(ctx context.Context, request *http.Request, err error) {
n.NewError(ctx, E.Cause(err, "process connection from ", request.RemoteAddr)) n.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", request.RemoteAddr))
} }
func rejectHTTP(writer http.ResponseWriter, statusCode int) { func rejectHTTP(writer http.ResponseWriter, statusCode int) {

View file

@ -9,7 +9,6 @@ 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"
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"
) )
@ -34,11 +33,13 @@ func NewRedirect(ctx context.Context, router adapter.Router, logger log.ContextL
return redirect return redirect
} }
func (r *Redirect) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (r *Redirect) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
destination, err := redir.GetOriginalDestination(conn) destination, err := redir.GetOriginalDestination(conn)
if err != nil { if err != nil {
return E.Cause(err, "get redirect destination") conn.Close()
r.logger.ErrorContext(ctx, "process connection from ", conn.RemoteAddr(), ": get redirect destination: ", err)
return
} }
metadata.Destination = M.SocksaddrFromNetIP(destination) metadata.Destination = M.SocksaddrFromNetIP(destination)
return r.newConnection(ctx, conn, metadata) r.newConnectionEx(ctx, conn, metadata, onClose)
} }

View file

@ -3,7 +3,6 @@ package inbound
import ( import (
"context" "context"
"net" "net"
"os"
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@ -18,6 +17,7 @@ import (
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"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"
"github.com/sagernet/sing/common/ntp" "github.com/sagernet/sing/common/ntp"
) )
@ -36,8 +36,8 @@ func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
} }
var ( var (
_ adapter.Inbound = (*Shadowsocks)(nil) _ adapter.Inbound = (*Shadowsocks)(nil)
_ adapter.InjectableInbound = (*Shadowsocks)(nil) _ adapter.TCPInjectableInbound = (*Shadowsocks)(nil)
) )
type Shadowsocks struct { type Shadowsocks struct {
@ -74,11 +74,11 @@ func newShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
} }
switch { switch {
case options.Method == shadowsocks.MethodNone: case options.Method == shadowsocks.MethodNone:
inbound.service = shadowsocks.NewNoneService(int64(udpTimeout.Seconds()), inbound.upstreamContextHandler()) inbound.service = shadowsocks.NewNoneService(int64(udpTimeout.Seconds()), adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, inbound))
case common.Contains(shadowaead.List, options.Method): case common.Contains(shadowaead.List, options.Method):
inbound.service, err = shadowaead.NewService(options.Method, nil, options.Password, int64(udpTimeout.Seconds()), inbound.upstreamContextHandler()) inbound.service, err = shadowaead.NewService(options.Method, nil, options.Password, int64(udpTimeout.Seconds()), adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, inbound))
case common.Contains(shadowaead_2022.List, options.Method): 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)) inbound.service, err = shadowaead_2022.NewServiceWithPassword(options.Method, options.Password, int64(udpTimeout.Seconds()), adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, inbound), ntp.TimeFuncFromContext(ctx))
default: default:
err = E.New("unsupported method: ", options.Method) err = E.New("unsupported method: ", options.Method)
} }
@ -86,14 +86,29 @@ func newShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
return inbound, err return inbound, err
} }
func (h *Shadowsocks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (h *Shadowsocks) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata)) err := h.service.NewConnection(ctx, conn, adapter.UpstreamMetadata(metadata))
N.CloseOnHandshakeFailure(conn, onClose, err)
if err != nil {
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
}
} }
func (h *Shadowsocks) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext) error { func (h *Shadowsocks) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
return h.service.NewPacket(adapter.WithContext(ctx, &metadata), conn, buffer, adapter.UpstreamMetadata(metadata)) err := h.service.NewPacket(h.ctx, h.packetConn(), buffer, M.Metadata{Source: source})
if err != nil {
h.logger.Error(E.Cause(err, "process packet from ", source))
}
} }
func (h *Shadowsocks) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { func (h *Shadowsocks) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return os.ErrInvalid h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
return h.router.RouteConnection(ctx, conn, h.createMetadata(conn, metadata))
}
func (h *Shadowsocks) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
ctx = log.ContextWithNewID(ctx)
h.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
return h.router.RoutePacketConnection(ctx, conn, h.createPacketMetadata(conn, metadata))
} }

View file

@ -20,13 +20,14 @@ 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"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/ntp" "github.com/sagernet/sing/common/ntp"
) )
var ( var (
_ adapter.Inbound = (*ShadowsocksMulti)(nil) _ adapter.Inbound = (*ShadowsocksMulti)(nil)
_ adapter.InjectableInbound = (*ShadowsocksMulti)(nil) _ adapter.TCPInjectableInbound = (*ShadowsocksMulti)(nil)
) )
type ShadowsocksMulti struct { type ShadowsocksMulti struct {
@ -66,14 +67,15 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.
options.Method, options.Method,
options.Password, options.Password,
int64(udpTimeout.Seconds()), int64(udpTimeout.Seconds()),
adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound), adapter.NewUpstreamHandler(adapter.InboundContext{}, 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.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound)) adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, inbound),
)
} else { } else {
return nil, E.New("unsupported method: " + options.Method) return nil, E.New("unsupported method: " + options.Method)
} }
@ -94,16 +96,19 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.
return inbound, err return inbound, err
} }
func (h *ShadowsocksMulti) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (h *ShadowsocksMulti) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata)) err := h.service.NewConnection(ctx, conn, adapter.UpstreamMetadata(metadata))
N.CloseOnHandshakeFailure(conn, onClose, err)
if err != nil {
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
}
} }
func (h *ShadowsocksMulti) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext) error { func (h *ShadowsocksMulti) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
return h.service.NewPacket(adapter.WithContext(ctx, &metadata), conn, buffer, adapter.UpstreamMetadata(metadata)) err := h.service.NewPacket(h.ctx, h.packetConn(), buffer, M.Metadata{Source: source})
} if err != nil {
h.logger.Error(E.Cause(err, "process packet from ", source))
func (h *ShadowsocksMulti) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { }
return os.ErrInvalid
} }
func (h *ShadowsocksMulti) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (h *ShadowsocksMulti) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
@ -118,7 +123,7 @@ func (h *ShadowsocksMulti) newConnection(ctx context.Context, conn net.Conn, met
metadata.User = user metadata.User = user
} }
h.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination) h.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination)
return h.router.RouteConnection(ctx, conn, metadata) return h.router.RouteConnection(ctx, conn, h.createMetadata(conn, metadata))
} }
func (h *ShadowsocksMulti) 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 {
@ -135,5 +140,5 @@ func (h *ShadowsocksMulti) newPacketConnection(ctx context.Context, conn N.Packe
ctx = log.ContextWithNewID(ctx) ctx = log.ContextWithNewID(ctx)
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection from ", metadata.Source) h.logger.InfoContext(ctx, "[", user, "] inbound packet connection from ", metadata.Source)
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination) h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
return h.router.RoutePacketConnection(ctx, conn, metadata) return h.router.RoutePacketConnection(ctx, conn, h.createPacketMetadata(conn, metadata))
} }

View file

@ -16,13 +16,15 @@ import (
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/auth" "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"
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
) )
var ( var (
_ adapter.Inbound = (*ShadowsocksRelay)(nil) _ adapter.Inbound = (*ShadowsocksRelay)(nil)
_ adapter.InjectableInbound = (*ShadowsocksRelay)(nil) _ adapter.TCPInjectableInbound = (*ShadowsocksRelay)(nil)
) )
type ShadowsocksRelay struct { type ShadowsocksRelay struct {
@ -61,7 +63,7 @@ func newShadowsocksRelay(ctx context.Context, router adapter.Router, logger log.
options.Method, options.Method,
options.Password, options.Password,
int64(udpTimeout.Seconds()), int64(udpTimeout.Seconds()),
adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound), adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, inbound),
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -79,16 +81,19 @@ func newShadowsocksRelay(ctx context.Context, router adapter.Router, logger log.
return inbound, err return inbound, err
} }
func (h *ShadowsocksRelay) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (h *ShadowsocksRelay) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata)) err := h.service.NewConnection(ctx, conn, adapter.UpstreamMetadata(metadata))
N.CloseOnHandshakeFailure(conn, onClose, err)
if err != nil {
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
}
} }
func (h *ShadowsocksRelay) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext) error { func (h *ShadowsocksRelay) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
return h.service.NewPacket(adapter.WithContext(ctx, &metadata), conn, buffer, adapter.UpstreamMetadata(metadata)) err := h.service.NewPacket(h.ctx, h.packetConn(), buffer, M.Metadata{Source: source})
} if err != nil {
h.logger.Error(E.Cause(err, "process packet from ", source))
func (h *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 { func (h *ShadowsocksRelay) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
@ -103,7 +108,7 @@ func (h *ShadowsocksRelay) newConnection(ctx context.Context, conn net.Conn, met
metadata.User = destination metadata.User = destination
} }
h.logger.InfoContext(ctx, "[", destination, "] inbound connection to ", metadata.Destination) h.logger.InfoContext(ctx, "[", destination, "] inbound connection to ", metadata.Destination)
return h.router.RouteConnection(ctx, conn, metadata) return h.router.RouteConnection(ctx, conn, h.createMetadata(conn, metadata))
} }
func (h *ShadowsocksRelay) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { func (h *ShadowsocksRelay) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
@ -120,5 +125,5 @@ func (h *ShadowsocksRelay) newPacketConnection(ctx context.Context, conn N.Packe
ctx = log.ContextWithNewID(ctx) ctx = log.ContextWithNewID(ctx)
h.logger.InfoContext(ctx, "[", destination, "] inbound packet connection from ", metadata.Source) h.logger.InfoContext(ctx, "[", destination, "] inbound packet connection from ", metadata.Source)
h.logger.InfoContext(ctx, "[", destination, "] inbound packet connection to ", metadata.Destination) h.logger.InfoContext(ctx, "[", destination, "] inbound packet connection to ", metadata.Destination)
return h.router.RoutePacketConnection(ctx, conn, metadata) return h.router.RoutePacketConnection(ctx, conn, h.createPacketMetadata(conn, metadata))
} }

View file

@ -12,6 +12,7 @@ import (
"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"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
) )
@ -91,3 +92,11 @@ func (h *ShadowTLS) newConnection(ctx context.Context, conn net.Conn, metadata a
} }
return h.router.RouteConnection(ctx, conn, metadata) return h.router.RouteConnection(ctx, conn, metadata)
} }
func (h *ShadowTLS) 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))
}
}

View file

@ -1,9 +1,9 @@
package inbound package inbound
import ( import (
std_bufio "bufio"
"context" "context"
"net" "net"
"os"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/uot" "github.com/sagernet/sing-box/common/uot"
@ -11,13 +11,14 @@ import (
"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/auth" "github.com/sagernet/sing/common/auth"
E "github.com/sagernet/sing/common/exceptions"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/protocol/socks" "github.com/sagernet/sing/protocol/socks"
) )
var ( var (
_ adapter.Inbound = (*Socks)(nil) _ adapter.Inbound = (*Socks)(nil)
_ adapter.InjectableInbound = (*Socks)(nil) _ adapter.TCPInjectableInbound = (*Socks)(nil)
) )
type Socks struct { type Socks struct {
@ -42,10 +43,10 @@ func NewSocks(ctx context.Context, router adapter.Router, logger log.ContextLogg
return inbound return inbound
} }
func (h *Socks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (h *Socks) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
return socks.HandleConnection(ctx, conn, h.authenticator, h.upstreamUserHandler(metadata), adapter.UpstreamMetadata(metadata)) err := socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, nil, h.upstreamUserHandlerEx(metadata), metadata.Source, metadata.Destination, onClose)
} N.CloseOnHandshakeFailure(conn, onClose, err)
if err != nil {
func (h *Socks) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
return os.ErrInvalid }
} }

View file

@ -18,12 +18,12 @@ import (
E "github.com/sagernet/sing/common/exceptions" 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"
"github.com/sagernet/sing/common/udpnat" "github.com/sagernet/sing/common/udpnat2"
) )
type TProxy struct { type TProxy struct {
myInboundAdapter myInboundAdapter
udpNat *udpnat.Service[netip.AddrPort] udpNat *udpnat.Service
} }
func NewTProxy(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TProxyInboundOptions) *TProxy { func NewTProxy(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TProxyInboundOptions) *TProxy {
@ -46,8 +46,7 @@ func NewTProxy(ctx context.Context, router adapter.Router, logger log.ContextLog
} }
tproxy.connHandler = tproxy tproxy.connHandler = tproxy
tproxy.oobPacketHandler = tproxy tproxy.oobPacketHandler = tproxy
tproxy.udpNat = udpnat.New[netip.AddrPort](int64(udpTimeout.Seconds()), tproxy.upstreamContextHandler()) tproxy.udpNat = udpnat.New(tproxy, tproxy.preparePacketConnection, udpTimeout, false)
tproxy.packetUpstream = tproxy.udpNat
return tproxy return tproxy
} }
@ -75,35 +74,43 @@ func (t *TProxy) Start() error {
return nil return nil
} }
func (t *TProxy) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (t *TProxy) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap() metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
return t.newConnection(ctx, conn, metadata) t.newConnectionEx(ctx, conn, metadata, onClose)
} }
func (t *TProxy) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, oob []byte, metadata adapter.InboundContext) error { func (t *TProxy) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
t.newPacketConnectionEx(ctx, conn, t.createPacketMetadataEx(source, destination), onClose)
}
func (t *TProxy) NewPacketEx(buffer *buf.Buffer, oob []byte, source M.Socksaddr) {
destination, err := redir.GetOriginalDestinationFromOOB(oob) destination, err := redir.GetOriginalDestinationFromOOB(oob)
if err != nil { if err != nil {
return E.Cause(err, "get tproxy destination") t.logger.Warn("process packet from ", source, ": get tproxy destination: ", err)
return
} }
metadata.Destination = M.SocksaddrFromNetIP(destination).Unwrap() t.udpNat.NewPacket([][]byte{buffer.Bytes()}, source, M.SocksaddrFromNetIP(destination), nil)
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 { type tproxyPacketWriter struct {
ctx context.Context ctx context.Context
source N.PacketConn source netip.AddrPort
destination M.Socksaddr destination M.Socksaddr
conn *net.UDPConn conn *net.UDPConn
} }
func (t *TProxy) preparePacketConnection(source M.Socksaddr, destination M.Socksaddr, userData any) (bool, context.Context, N.PacketWriter, N.CloseHandlerFunc) {
writer := &tproxyPacketWriter{ctx: t.ctx, source: source.AddrPort(), destination: destination}
return true, t.ctx, writer, func(it error) {
common.Close(common.PtrOrNil(writer.conn))
}
}
func (w *tproxyPacketWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error { func (w *tproxyPacketWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
defer buffer.Release() defer buffer.Release()
conn := w.conn conn := w.conn
if w.destination == destination && conn != nil { if w.destination == destination && conn != nil {
_, err := conn.WriteToUDPAddrPort(buffer.Bytes(), M.AddrPortFromNet(w.source.LocalAddr())) _, err := conn.WriteToUDPAddrPort(buffer.Bytes(), w.source)
if err != nil { if err != nil {
w.conn = nil w.conn = nil
} }
@ -122,9 +129,5 @@ func (w *tproxyPacketWriter) WritePacket(buffer *buf.Buffer, destination M.Socks
} else { } else {
defer udpConn.Close() defer udpConn.Close()
} }
return common.Error(udpConn.WriteToUDPAddrPort(buffer.Bytes(), M.AddrPortFromNet(w.source.LocalAddr()))) return common.Error(udpConn.WriteToUDPAddrPort(buffer.Bytes(), w.source))
}
func (w *tproxyPacketWriter) Close() error {
return common.Close(common.PtrOrNil(w.conn))
} }

View file

@ -22,8 +22,8 @@ import (
) )
var ( var (
_ adapter.Inbound = (*Trojan)(nil) _ adapter.Inbound = (*Trojan)(nil)
_ adapter.InjectableInbound = (*Trojan)(nil) _ adapter.TCPInjectableInbound = (*Trojan)(nil)
) )
type Trojan struct { type Trojan struct {
@ -90,7 +90,7 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
return nil, err return nil, err
} }
if options.Transport != nil { if options.Transport != nil {
inbound.transport, err = v2ray.NewServerTransport(ctx, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, (*trojanTransportHandler)(inbound)) inbound.transport, err = v2ray.NewServerTransport(ctx, logger, 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)
} }
@ -149,11 +149,6 @@ func (h *Trojan) Close() 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 { 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 {
@ -165,8 +160,12 @@ func (h *Trojan) NewConnection(ctx context.Context, conn net.Conn, metadata adap
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 *Trojan) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { func (h *Trojan) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
return os.ErrInvalid 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))
}
} }
func (h *Trojan) 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 {
@ -226,9 +225,6 @@ var _ adapter.V2RayServerTransportHandler = (*trojanTransportHandler)(nil)
type trojanTransportHandler Trojan type trojanTransportHandler Trojan
func (t *trojanTransportHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { func (t *trojanTransportHandler) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
return (*Trojan)(t).newTransportConnection(ctx, conn, adapter.InboundContext{ (*Trojan)(t).routeTCP(ctx, conn, source, destination, onClose)
Source: metadata.Source,
Destination: metadata.Destination,
})
} }

View file

@ -28,17 +28,18 @@ import (
"go4.org/netipx" "go4.org/netipx"
) )
var _ adapter.Inbound = (*Tun)(nil) var _ adapter.Inbound = (*TUN)(nil)
type Tun struct { type TUN struct {
tag string tag string
ctx context.Context ctx context.Context
router adapter.Router router adapter.Router
logger log.ContextLogger logger log.ContextLogger
// Deprecated
inboundOptions option.InboundOptions inboundOptions option.InboundOptions
tunOptions tun.Options tunOptions tun.Options
endpointIndependentNat bool endpointIndependentNat bool
udpTimeout int64 udpTimeout time.Duration
stack string stack string
tunIf tun.Tun tunIf tun.Tun
tunStack tun.Stack tunStack tun.Stack
@ -53,7 +54,7 @@ type Tun struct {
routeExcludeAddressSet []*netipx.IPSet routeExcludeAddressSet []*netipx.IPSet
} }
func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions, platformInterface platform.Interface) (*Tun, error) { func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions, platformInterface platform.Interface) (*TUN, error) {
address := options.Address address := options.Address
var deprecatedAddressUsed bool var deprecatedAddressUsed bool
//nolint:staticcheck //nolint:staticcheck
@ -162,7 +163,7 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
outputMark = tun.DefaultAutoRedirectOutputMark outputMark = tun.DefaultAutoRedirectOutputMark
} }
inbound := &Tun{ inbound := &TUN{
tag: tag, tag: tag,
ctx: ctx, ctx: ctx,
router: router, router: router,
@ -194,7 +195,7 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
InterfaceMonitor: router.InterfaceMonitor(), InterfaceMonitor: router.InterfaceMonitor(),
}, },
endpointIndependentNat: options.EndpointIndependentNat, endpointIndependentNat: options.EndpointIndependentNat,
udpTimeout: int64(udpTimeout.Seconds()), udpTimeout: udpTimeout,
stack: options.Stack, stack: options.Stack,
platformInterface: platformInterface, platformInterface: platformInterface,
platformOptions: common.PtrValueOrDefault(options.Platform), platformOptions: common.PtrValueOrDefault(options.Platform),
@ -207,7 +208,7 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
inbound.autoRedirect, err = tun.NewAutoRedirect(tun.AutoRedirectOptions{ inbound.autoRedirect, err = tun.NewAutoRedirect(tun.AutoRedirectOptions{
TunOptions: &inbound.tunOptions, TunOptions: &inbound.tunOptions,
Context: ctx, Context: ctx,
Handler: inbound, Handler: (*autoRedirectHandler)(inbound),
Logger: logger, Logger: logger,
NetworkMonitor: router.NetworkMonitor(), NetworkMonitor: router.NetworkMonitor(),
InterfaceFinder: router.InterfaceFinder(), InterfaceFinder: router.InterfaceFinder(),
@ -283,17 +284,17 @@ func parseRange(uidRanges []ranges.Range[uint32], rangeList []string) ([]ranges.
return uidRanges, nil return uidRanges, nil
} }
func (t *Tun) Type() string { func (t *TUN) Type() string {
return C.TypeTun return C.TypeTun
} }
func (t *Tun) Tag() string { func (t *TUN) Tag() string {
return t.tag return t.tag
} }
func (t *Tun) Start() error { func (t *TUN) Start() error {
if C.IsAndroid && t.platformInterface == nil { if C.IsAndroid && t.platformInterface == nil {
t.tunOptions.BuildAndroidRules(t.router.PackageManager(), t) t.tunOptions.BuildAndroidRules(t.router.PackageManager())
} }
if t.tunOptions.Name == "" { if t.tunOptions.Name == "" {
t.tunOptions.Name = tun.CalculateInterfaceName("") t.tunOptions.Name = tun.CalculateInterfaceName("")
@ -327,7 +328,6 @@ func (t *Tun) Start() error {
Context: t.ctx, Context: t.ctx,
Tun: tunInterface, Tun: tunInterface,
TunOptions: t.tunOptions, TunOptions: t.tunOptions,
EndpointIndependentNat: t.endpointIndependentNat,
UDPTimeout: t.udpTimeout, UDPTimeout: t.udpTimeout,
Handler: t, Handler: t,
Logger: t.logger, Logger: t.logger,
@ -349,7 +349,7 @@ func (t *Tun) Start() error {
return nil return nil
} }
func (t *Tun) PostStart() error { func (t *TUN) PostStart() error {
monitor := taskmonitor.New(t.logger, C.StartTimeout) monitor := taskmonitor.New(t.logger, C.StartTimeout)
if t.autoRedirect != nil { if t.autoRedirect != nil {
t.routeAddressSet = common.FlatMap(t.routeRuleSet, adapter.RuleSet.ExtractIPSet) t.routeAddressSet = common.FlatMap(t.routeRuleSet, adapter.RuleSet.ExtractIPSet)
@ -388,7 +388,7 @@ func (t *Tun) PostStart() error {
return nil return nil
} }
func (t *Tun) updateRouteAddressSet(it adapter.RuleSet) { func (t *TUN) updateRouteAddressSet(it adapter.RuleSet) {
t.routeAddressSet = common.FlatMap(t.routeRuleSet, adapter.RuleSet.ExtractIPSet) t.routeAddressSet = common.FlatMap(t.routeRuleSet, adapter.RuleSet.ExtractIPSet)
t.routeExcludeAddressSet = common.FlatMap(t.routeExcludeRuleSet, adapter.RuleSet.ExtractIPSet) t.routeExcludeAddressSet = common.FlatMap(t.routeExcludeRuleSet, adapter.RuleSet.ExtractIPSet)
t.autoRedirect.UpdateRouteAddressSet() t.autoRedirect.UpdateRouteAddressSet()
@ -396,7 +396,7 @@ func (t *Tun) updateRouteAddressSet(it adapter.RuleSet) {
t.routeExcludeAddressSet = nil t.routeExcludeAddressSet = nil
} }
func (t *Tun) Close() error { func (t *TUN) Close() error {
return common.Close( return common.Close(
t.tunStack, t.tunStack,
t.tunIf, t.tunIf,
@ -404,44 +404,48 @@ func (t *Tun) Close() error {
) )
} }
func (t *Tun) NewConnection(ctx context.Context, conn net.Conn, upstreamMetadata M.Metadata) error { func (t *TUN) PrepareConnection(source M.Socksaddr, destination M.Socksaddr) error {
ctx = log.ContextWithNewID(ctx) // TODO: implement rejects
var metadata adapter.InboundContext
metadata.Inbound = t.tag
metadata.InboundType = C.TypeTun
metadata.Source = upstreamMetadata.Source
metadata.Destination = upstreamMetadata.Destination
metadata.InboundOptions = t.inboundOptions
if upstreamMetadata.Protocol != "" {
t.logger.InfoContext(ctx, "inbound ", upstreamMetadata.Protocol, " connection from ", metadata.Source)
} else {
t.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
}
t.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
err := t.router.RouteConnection(ctx, conn, metadata)
if err != nil {
t.NewError(ctx, err)
}
return nil return nil
} }
func (t *Tun) NewPacketConnection(ctx context.Context, conn N.PacketConn, upstreamMetadata M.Metadata) error { func (t *TUN) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
ctx = log.ContextWithNewID(ctx) ctx = log.ContextWithNewID(ctx)
var metadata adapter.InboundContext var metadata adapter.InboundContext
metadata.Inbound = t.tag metadata.Inbound = t.tag
metadata.InboundType = C.TypeTun metadata.InboundType = C.TypeTun
metadata.Source = upstreamMetadata.Source metadata.Source = source
metadata.Destination = upstreamMetadata.Destination metadata.Destination = destination
metadata.InboundOptions = t.inboundOptions
t.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
t.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
t.router.RouteConnectionEx(ctx, conn, metadata, onClose)
}
func (t *TUN) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
ctx = log.ContextWithNewID(ctx)
var metadata adapter.InboundContext
metadata.Inbound = t.tag
metadata.InboundType = C.TypeTun
metadata.Source = source
metadata.Destination = destination
metadata.InboundOptions = t.inboundOptions metadata.InboundOptions = t.inboundOptions
t.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source) t.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
t.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination) t.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
err := t.router.RoutePacketConnection(ctx, conn, metadata) t.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
if err != nil {
t.NewError(ctx, err)
}
return nil
} }
func (t *Tun) NewError(ctx context.Context, err error) { type autoRedirectHandler TUN
NewError(t.logger, ctx, err)
func (t *autoRedirectHandler) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
ctx = log.ContextWithNewID(ctx)
var metadata adapter.InboundContext
metadata.Inbound = t.tag
metadata.InboundType = C.TypeTun
metadata.Source = source
metadata.Destination = destination
metadata.InboundOptions = t.inboundOptions
t.logger.InfoContext(ctx, "inbound redirect connection from ", metadata.Source)
t.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
t.router.RouteConnectionEx(ctx, conn, metadata, onClose)
} }

View file

@ -25,8 +25,8 @@ import (
) )
var ( var (
_ adapter.Inbound = (*VLESS)(nil) _ adapter.Inbound = (*VLESS)(nil)
_ adapter.InjectableInbound = (*VLESS)(nil) _ adapter.TCPInjectableInbound = (*VLESS)(nil)
) )
type VLESS struct { type VLESS struct {
@ -73,7 +73,7 @@ func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogg
} }
} }
if options.Transport != nil { if options.Transport != nil {
inbound.transport, err = v2ray.NewServerTransport(ctx, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, (*vlessTransportHandler)(inbound)) inbound.transport, err = v2ray.NewServerTransport(ctx, logger, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, (*vlessTransportHandler)(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)
} }
@ -128,11 +128,6 @@ func (h *VLESS) Close() error {
) )
} }
func (h *VLESS) newTransportConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
h.injectTCP(conn, metadata)
return nil
}
func (h *VLESS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (h *VLESS) 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 {
@ -144,8 +139,12 @@ func (h *VLESS) NewConnection(ctx context.Context, conn net.Conn, metadata adapt
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata)) return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
} }
func (h *VLESS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { func (h *VLESS) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
return os.ErrInvalid 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))
}
} }
func (h *VLESS) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (h *VLESS) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
@ -188,9 +187,6 @@ var _ adapter.V2RayServerTransportHandler = (*vlessTransportHandler)(nil)
type vlessTransportHandler VLESS type vlessTransportHandler VLESS
func (t *vlessTransportHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { func (t *vlessTransportHandler) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
return (*VLESS)(t).newTransportConnection(ctx, conn, adapter.InboundContext{ t.routeTCP(ctx, conn, source, destination, onClose)
Source: metadata.Source,
Destination: metadata.Destination,
})
} }

View file

@ -25,8 +25,8 @@ import (
) )
var ( var (
_ adapter.Inbound = (*VMess)(nil) _ adapter.Inbound = (*VMess)(nil)
_ adapter.InjectableInbound = (*VMess)(nil) _ adapter.TCPInjectableInbound = (*VMess)(nil)
) )
type VMess struct { type VMess struct {
@ -83,7 +83,7 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
} }
} }
if options.Transport != nil { if options.Transport != nil {
inbound.transport, err = v2ray.NewServerTransport(ctx, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, (*vmessTransportHandler)(inbound)) inbound.transport, err = v2ray.NewServerTransport(ctx, logger, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, (*vmessTransportHandler)(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)
} }
@ -142,11 +142,6 @@ func (h *VMess) Close() error {
) )
} }
func (h *VMess) newTransportConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
h.injectTCP(conn, metadata)
return nil
}
func (h *VMess) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (h *VMess) 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 {
@ -158,8 +153,12 @@ func (h *VMess) NewConnection(ctx context.Context, conn net.Conn, metadata adapt
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata)) return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
} }
func (h *VMess) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { func (h *VMess) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
return os.ErrInvalid 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))
}
} }
func (h *VMess) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (h *VMess) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
@ -202,9 +201,6 @@ var _ adapter.V2RayServerTransportHandler = (*vmessTransportHandler)(nil)
type vmessTransportHandler VMess type vmessTransportHandler VMess
func (t *vmessTransportHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { func (t *vmessTransportHandler) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
return (*VMess)(t).newTransportConnection(ctx, conn, adapter.InboundContext{ (*VMess)(t).routeTCP(ctx, conn, source, destination, onClose)
Source: metadata.Source,
Destination: metadata.Destination,
})
} }

View file

@ -11,6 +11,7 @@ import (
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/transport/v2ray" "github.com/sagernet/sing-box/transport/v2ray"
"github.com/sagernet/sing-dns" "github.com/sagernet/sing-dns"
"github.com/sagernet/sing/common/logger"
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"
) )
@ -20,7 +21,7 @@ func init() {
return nil, C.ErrQUICNotIncluded return nil, C.ErrQUICNotIncluded
}) })
v2ray.RegisterQUICConstructor( v2ray.RegisterQUICConstructor(
func(ctx context.Context, options option.V2RayQUICOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (adapter.V2RayServerTransport, error) { func(ctx context.Context, logger logger.ContextLogger, options option.V2RayQUICOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (adapter.V2RayServerTransport, error) {
return nil, C.ErrQUICNotIncluded return nil, C.ErrQUICNotIncluded
}, },
func(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayQUICOptions, tlsConfig tls.Config) (adapter.V2RayClientTransport, error) { func(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayQUICOptions, tlsConfig tls.Config) (adapter.V2RayClientTransport, error) {

View file

@ -98,6 +98,7 @@ func (h *Inbound) UnmarshalJSON(bytes []byte) error {
return nil return nil
} }
// Deprecated: Use rule action instead
type InboundOptions struct { type InboundOptions struct {
SniffEnabled bool `json:"sniff,omitempty"` SniffEnabled bool `json:"sniff,omitempty"`
SniffOverrideDestination bool `json:"sniff_override_destination,omitempty"` SniffOverrideDestination bool `json:"sniff_override_destination,omitempty"`

View file

@ -64,7 +64,7 @@ func (r Rule) IsValid() bool {
} }
} }
type DefaultRule struct { type RawDefaultRule struct {
Inbound Listable[string] `json:"inbound,omitempty"` Inbound Listable[string] `json:"inbound,omitempty"`
IPVersion int `json:"ip_version,omitempty"` IPVersion int `json:"ip_version,omitempty"`
Network Listable[string] `json:"network,omitempty"` Network Listable[string] `json:"network,omitempty"`
@ -98,26 +98,58 @@ type DefaultRule struct {
RuleSet Listable[string] `json:"rule_set,omitempty"` RuleSet Listable[string] `json:"rule_set,omitempty"`
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"` RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
Invert bool `json:"invert,omitempty"` Invert bool `json:"invert,omitempty"`
Outbound string `json:"outbound,omitempty"`
// Deprecated: renamed to rule_set_ip_cidr_match_source // Deprecated: renamed to rule_set_ip_cidr_match_source
Deprecated_RulesetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"` Deprecated_RulesetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"`
} }
type DefaultRule struct {
RawDefaultRule
RuleAction
}
func (r *DefaultRule) MarshalJSON() ([]byte, error) {
return MarshallObjects(r.RawDefaultRule, r.RuleAction)
}
func (r *DefaultRule) UnmarshalJSON(data []byte) error {
err := json.Unmarshal(data, &r.RawDefaultRule)
if err != nil {
return err
}
return UnmarshallExcluded(data, &r.RawDefaultRule, &r.RuleAction)
}
func (r *DefaultRule) IsValid() bool { func (r *DefaultRule) IsValid() bool {
var defaultValue DefaultRule var defaultValue DefaultRule
defaultValue.Invert = r.Invert defaultValue.Invert = r.Invert
defaultValue.Outbound = r.Outbound defaultValue.Action = r.Action
return !reflect.DeepEqual(r, defaultValue) return !reflect.DeepEqual(r, defaultValue)
} }
type LogicalRule struct { type _LogicalRule struct {
Mode string `json:"mode"` Mode string `json:"mode"`
Rules []Rule `json:"rules,omitempty"` Rules []Rule `json:"rules,omitempty"`
Invert bool `json:"invert,omitempty"` Invert bool `json:"invert,omitempty"`
Outbound string `json:"outbound,omitempty"`
} }
func (r LogicalRule) IsValid() bool { type LogicalRule struct {
_LogicalRule
RuleAction
}
func (r *LogicalRule) MarshalJSON() ([]byte, error) {
return MarshallObjects(r._LogicalRule, r.RuleAction)
}
func (r *LogicalRule) UnmarshalJSON(data []byte) error {
err := json.Unmarshal(data, &r._LogicalRule)
if err != nil {
return err
}
return UnmarshallExcluded(data, &r._LogicalRule, &r.RuleAction)
}
func (r *LogicalRule) IsValid() bool {
return len(r.Rules) > 0 && common.All(r.Rules, Rule.IsValid) return len(r.Rules) > 0 && common.All(r.Rules, Rule.IsValid)
} }

166
option/rule_action.go Normal file
View file

@ -0,0 +1,166 @@
package option
import (
C "github.com/sagernet/sing-box/constant"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/json"
)
type _RuleAction struct {
Action string `json:"action,omitempty"`
RouteOptions RouteActionOptions `json:"-"`
RejectOptions RejectActionOptions `json:"-"`
SniffOptions RouteActionSniff `json:"-"`
ResolveOptions RouteActionResolve `json:"-"`
}
type RuleAction _RuleAction
func (r RuleAction) MarshalJSON() ([]byte, error) {
var v any
switch r.Action {
case C.RuleActionTypeRoute:
r.Action = ""
v = r.RouteOptions
case C.RuleActionTypeReturn:
v = nil
case C.RuleActionTypeReject:
v = r.RejectOptions
case C.RuleActionTypeHijackDNS:
v = nil
case C.RuleActionTypeSniff:
v = r.SniffOptions
case C.RuleActionTypeResolve:
v = r.ResolveOptions
default:
return nil, E.New("unknown rule action: " + r.Action)
}
if v == nil {
return MarshallObjects((_RuleAction)(r))
}
return MarshallObjects((_RuleAction)(r), v)
}
func (r *RuleAction) UnmarshalJSON(data []byte) error {
err := json.Unmarshal(data, (*_RuleAction)(r))
if err != nil {
return err
}
var v any
switch r.Action {
case "", C.RuleActionTypeRoute:
r.Action = C.RuleActionTypeRoute
v = &r.RouteOptions
case C.RuleActionTypeReturn:
v = nil
case C.RuleActionTypeReject:
v = &r.RejectOptions
case C.RuleActionTypeHijackDNS:
v = nil
case C.RuleActionTypeSniff:
v = &r.SniffOptions
case C.RuleActionTypeResolve:
v = &r.ResolveOptions
default:
return E.New("unknown rule action: " + r.Action)
}
if v == nil {
// check unknown fields
return json.UnmarshalDisallowUnknownFields(data, &_RuleAction{})
}
return UnmarshallExcluded(data, (*_RuleAction)(r), v)
}
type _DNSRuleAction struct {
Action string `json:"action,omitempty"`
RouteOptions DNSRouteActionOptions `json:"-"`
RejectOptions RejectActionOptions `json:"-"`
SniffOptions RouteActionSniff `json:"-"`
ResolveOptions RouteActionResolve `json:"-"`
}
type DNSRuleAction _DNSRuleAction
func (r DNSRuleAction) MarshalJSON() ([]byte, error) {
var v any
switch r.Action {
case C.RuleActionTypeRoute:
r.Action = ""
v = r.RouteOptions
case C.RuleActionTypeReturn:
v = nil
case C.RuleActionTypeReject:
v = r.RejectOptions
default:
return nil, E.New("unknown DNS rule action: " + r.Action)
}
if v == nil {
return MarshallObjects((_DNSRuleAction)(r))
}
return MarshallObjects((_DNSRuleAction)(r), v)
}
func (r *DNSRuleAction) UnmarshalJSON(data []byte) error {
err := json.Unmarshal(data, (*_DNSRuleAction)(r))
if err != nil {
return err
}
var v any
switch r.Action {
case "", C.RuleActionTypeRoute:
r.Action = C.RuleActionTypeRoute
v = &r.RouteOptions
case C.RuleActionTypeReturn:
v = nil
case C.RuleActionTypeReject:
v = &r.RejectOptions
default:
return E.New("unknown DNS rule action: " + r.Action)
}
if v == nil {
// check unknown fields
return json.UnmarshalDisallowUnknownFields(data, &_DNSRuleAction{})
}
return UnmarshallExcluded(data, (*_DNSRuleAction)(r), v)
}
type RouteActionOptions struct {
Outbound string `json:"outbound"`
UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"`
}
type DNSRouteActionOptions struct {
Server string `json:"server"`
DisableCache bool `json:"disable_cache,omitempty"`
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
ClientSubnet *AddrPrefix `json:"client_subnet,omitempty"`
}
type RejectActionOptions struct {
Method RejectMethod `json:"method,omitempty"`
}
type RejectMethod string
func (m *RejectMethod) UnmarshalJSON(bytes []byte) error {
err := json.Unmarshal(bytes, (*string)(m))
if err != nil {
return err
}
switch *m {
case C.RuleActionRejectMethodDefault, C.RuleActionRejectMethodPortUnreachable, C.RuleActionRejectMethodDrop:
return nil
default:
return E.New("unknown reject method: " + *m)
}
}
type RouteActionSniff struct {
Sniffer Listable[string] `json:"sniffer,omitempty"`
Timeout Duration `json:"timeout,omitempty"`
}
type RouteActionResolve struct {
Strategy DomainStrategy `json:"strategy,omitempty"`
Server string `json:"server,omitempty"`
}

View file

@ -64,7 +64,7 @@ func (r DNSRule) IsValid() bool {
} }
} }
type DefaultDNSRule struct { type RawDefaultDNSRule struct {
Inbound Listable[string] `json:"inbound,omitempty"` Inbound Listable[string] `json:"inbound,omitempty"`
IPVersion int `json:"ip_version,omitempty"` IPVersion int `json:"ip_version,omitempty"`
QueryType Listable[DNSQueryType] `json:"query_type,omitempty"` QueryType Listable[DNSQueryType] `json:"query_type,omitempty"`
@ -100,35 +100,58 @@ type DefaultDNSRule struct {
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"` RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
RuleSetIPCIDRAcceptEmpty bool `json:"rule_set_ip_cidr_accept_empty,omitempty"` RuleSetIPCIDRAcceptEmpty bool `json:"rule_set_ip_cidr_accept_empty,omitempty"`
Invert bool `json:"invert,omitempty"` Invert bool `json:"invert,omitempty"`
Server string `json:"server,omitempty"`
DisableCache bool `json:"disable_cache,omitempty"`
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
ClientSubnet *AddrPrefix `json:"client_subnet,omitempty"`
// Deprecated: renamed to rule_set_ip_cidr_match_source // Deprecated: renamed to rule_set_ip_cidr_match_source
Deprecated_RulesetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"` Deprecated_RulesetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"`
} }
type DefaultDNSRule struct {
RawDefaultDNSRule
DNSRuleAction
}
func (r *DefaultDNSRule) MarshalJSON() ([]byte, error) {
return MarshallObjects(r.RawDefaultDNSRule, r.DNSRuleAction)
}
func (r *DefaultDNSRule) UnmarshalJSON(data []byte) error {
err := json.Unmarshal(data, &r.RawDefaultDNSRule)
if err != nil {
return err
}
return UnmarshallExcluded(data, &r.RawDefaultDNSRule, &r.DNSRuleAction)
}
func (r *DefaultDNSRule) IsValid() bool { func (r *DefaultDNSRule) IsValid() bool {
var defaultValue DefaultDNSRule var defaultValue DefaultDNSRule
defaultValue.Invert = r.Invert defaultValue.Invert = r.Invert
defaultValue.Server = r.Server defaultValue.DNSRuleAction = r.DNSRuleAction
defaultValue.DisableCache = r.DisableCache
defaultValue.RewriteTTL = r.RewriteTTL
defaultValue.ClientSubnet = r.ClientSubnet
return !reflect.DeepEqual(r, defaultValue) return !reflect.DeepEqual(r, defaultValue)
} }
type LogicalDNSRule struct { type _LogicalDNSRule struct {
Mode string `json:"mode"` Mode string `json:"mode"`
Rules []DNSRule `json:"rules,omitempty"` Rules []DNSRule `json:"rules,omitempty"`
Invert bool `json:"invert,omitempty"` Invert bool `json:"invert,omitempty"`
Server string `json:"server,omitempty"`
DisableCache bool `json:"disable_cache,omitempty"`
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
ClientSubnet *AddrPrefix `json:"client_subnet,omitempty"`
} }
func (r LogicalDNSRule) IsValid() bool { type LogicalDNSRule struct {
_LogicalDNSRule
DNSRuleAction
}
func (r *LogicalDNSRule) MarshalJSON() ([]byte, error) {
return MarshallObjects(r._LogicalDNSRule, r.DNSRuleAction)
}
func (r *LogicalDNSRule) UnmarshalJSON(data []byte) error {
err := json.Unmarshal(data, &r._LogicalDNSRule)
if err != nil {
return err
}
return UnmarshallExcluded(data, &r._LogicalDNSRule, &r.DNSRuleAction)
}
func (r *LogicalDNSRule) IsValid() bool {
return len(r.Rules) > 0 && common.All(r.Rules, DNSRule.IsValid) return len(r.Rules) > 0 && common.All(r.Rules, DNSRule.IsValid)
} }

View file

@ -81,8 +81,11 @@ func (a *AddrPrefix) UnmarshalJSON(content []byte) error {
return prefixErr return prefixErr
} }
func (a AddrPrefix) Build() netip.Prefix { func (a *AddrPrefix) Build() netip.Prefix {
return netip.Prefix(a) if a == nil {
return netip.Prefix{}
}
return netip.Prefix(*a)
} }
type NetworkList string type NetworkList string
@ -143,12 +146,29 @@ func (l *Listable[T]) UnmarshalJSON(content []byte) error {
type DomainStrategy dns.DomainStrategy type DomainStrategy dns.DomainStrategy
func (s DomainStrategy) String() string {
switch dns.DomainStrategy(s) {
case dns.DomainStrategyAsIS:
return ""
case dns.DomainStrategyPreferIPv4:
return "prefer_ipv4"
case dns.DomainStrategyPreferIPv6:
return "prefer_ipv6"
case dns.DomainStrategyUseIPv4:
return "ipv4_only"
case dns.DomainStrategyUseIPv6:
return "ipv6_only"
default:
panic(E.New("unknown domain strategy: ", s))
}
}
func (s DomainStrategy) MarshalJSON() ([]byte, error) { func (s DomainStrategy) MarshalJSON() ([]byte, error) {
var value string var value string
switch dns.DomainStrategy(s) { switch dns.DomainStrategy(s) {
case dns.DomainStrategyAsIS: case dns.DomainStrategyAsIS:
value = "" value = ""
// value = "AsIS" // value = "as_is"
case dns.DomainStrategyPreferIPv4: case dns.DomainStrategyPreferIPv4:
value = "prefer_ipv4" value = "prefer_ipv4"
case dns.DomainStrategyPreferIPv6: case dns.DomainStrategyPreferIPv6:

View file

@ -39,12 +39,14 @@ func (h *Block) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.
return nil, io.EOF return nil, io.EOF
} }
// Deprecated
func (h *Block) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (h *Block) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
conn.Close() conn.Close()
h.logger.InfoContext(ctx, "blocked connection to ", metadata.Destination) h.logger.InfoContext(ctx, "blocked connection to ", metadata.Destination)
return nil return nil
} }
// Deprecated
func (h *Block) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { func (h *Block) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
conn.Close() conn.Close()
h.logger.InfoContext(ctx, "blocked packet connection to ", metadata.Destination) h.logger.InfoContext(ctx, "blocked packet connection to ", metadata.Destination)

View file

@ -69,7 +69,7 @@ func NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata a
if err != nil { if err != nil {
return N.ReportHandshakeFailure(conn, err) return N.ReportHandshakeFailure(conn, err)
} }
err = N.ReportHandshakeSuccess(conn) err = N.ReportConnHandshakeSuccess(conn, outConn)
if err != nil { if err != nil {
outConn.Close() outConn.Close()
return err return err
@ -96,7 +96,7 @@ func NewDirectConnection(ctx context.Context, router adapter.Router, this N.Dial
if err != nil { if err != nil {
return N.ReportHandshakeFailure(conn, err) return N.ReportHandshakeFailure(conn, err)
} }
err = N.ReportHandshakeSuccess(conn) err = N.ReportConnHandshakeSuccess(conn, outConn)
if err != nil { if err != nil {
outConn.Close() outConn.Close()
return err return err
@ -117,14 +117,14 @@ func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn,
if err != nil { if err != nil {
return N.ReportHandshakeFailure(conn, err) return N.ReportHandshakeFailure(conn, err)
} }
err = N.ReportHandshakeSuccess(conn) err = N.ReportPacketConnHandshakeSuccess(conn, outConn)
if err != nil { if err != nil {
outConn.Close() outConn.Close()
return err return err
} }
if destinationAddress.IsValid() { if destinationAddress.IsValid() {
if metadata.Destination.IsFqdn() { if metadata.Destination.IsFqdn() {
if metadata.InboundOptions.UDPDisableDomainUnmapping { if metadata.UDPDisableDomainUnmapping {
outConn = bufio.NewUnidirectionalNATPacketConn(bufio.NewPacketConn(outConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination) outConn = bufio.NewUnidirectionalNATPacketConn(bufio.NewPacketConn(outConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination)
} else { } else {
outConn = bufio.NewNATPacketConn(bufio.NewPacketConn(outConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination) outConn = bufio.NewNATPacketConn(bufio.NewPacketConn(outConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination)
@ -165,7 +165,7 @@ func NewDirectPacketConnection(ctx context.Context, router adapter.Router, this
if err != nil { if err != nil {
return N.ReportHandshakeFailure(conn, err) return N.ReportHandshakeFailure(conn, err)
} }
err = N.ReportHandshakeSuccess(conn) err = N.ReportPacketConnHandshakeSuccess(conn, outConn)
if err != nil { if err != nil {
outConn.Close() outConn.Close()
return err return err

View file

@ -30,7 +30,7 @@ type Direct struct {
fallbackDelay time.Duration fallbackDelay time.Duration
overrideOption int overrideOption int
overrideDestination M.Socksaddr overrideDestination M.Socksaddr
loopBack *loopBackDetector // loopBack *loopBackDetector
} }
func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, options option.DirectOutboundOptions) (*Direct, error) { func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, options option.DirectOutboundOptions) (*Direct, error) {
@ -51,7 +51,7 @@ func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, opti
domainStrategy: dns.DomainStrategy(options.DomainStrategy), domainStrategy: dns.DomainStrategy(options.DomainStrategy),
fallbackDelay: time.Duration(options.FallbackDelay), fallbackDelay: time.Duration(options.FallbackDelay),
dialer: outboundDialer, dialer: outboundDialer,
loopBack: newLoopBackDetector(router), // loopBack: newLoopBackDetector(router),
} }
if options.ProxyProtocol != 0 { if options.ProxyProtocol != 0 {
return nil, E.New("Proxy Protocol is deprecated and removed in sing-box 1.6.0") return nil, E.New("Proxy Protocol is deprecated and removed in sing-box 1.6.0")
@ -90,11 +90,12 @@ func (h *Direct) DialContext(ctx context.Context, network string, destination M.
case N.NetworkUDP: case N.NetworkUDP:
h.logger.InfoContext(ctx, "outbound packet connection to ", destination) h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
} }
conn, err := h.dialer.DialContext(ctx, network, destination) /*conn, err := h.dialer.DialContext(ctx, network, destination)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return h.loopBack.NewConn(conn), nil return h.loopBack.NewConn(conn), nil*/
return h.dialer.DialContext(ctx, network, destination)
} }
func (h *Direct) DialParallel(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr) (net.Conn, error) { func (h *Direct) DialParallel(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr) (net.Conn, error) {
@ -148,14 +149,14 @@ func (h *Direct) ListenPacket(ctx context.Context, destination M.Socksaddr) (net
if err != nil { if err != nil {
return nil, err return nil, err
} }
conn = h.loopBack.NewPacketConn(bufio.NewPacketConn(conn), destination) // conn = h.loopBack.NewPacketConn(bufio.NewPacketConn(conn), destination)
if originDestination != destination { if originDestination != destination {
conn = bufio.NewNATPacketConn(bufio.NewPacketConn(conn), destination, originDestination) conn = bufio.NewNATPacketConn(bufio.NewPacketConn(conn), destination, originDestination)
} }
return conn, nil return conn, nil
} }
func (h *Direct) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { /*func (h *Direct) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
if h.loopBack.CheckConn(metadata.Source.AddrPort(), M.AddrPortFromNet(conn.LocalAddr())) { if h.loopBack.CheckConn(metadata.Source.AddrPort(), M.AddrPortFromNet(conn.LocalAddr())) {
return E.New("reject loopback connection to ", metadata.Destination) return E.New("reject loopback connection to ", metadata.Destination)
} }
@ -168,3 +169,4 @@ func (h *Direct) NewPacketConnection(ctx context.Context, conn N.PacketConn, met
} }
return NewPacketConnection(ctx, h, conn, metadata) return NewPacketConnection(ctx, h, conn, metadata)
} }
*/

View file

@ -45,6 +45,7 @@ func (d *DNS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.Pa
return nil, os.ErrInvalid return nil, os.ErrInvalid
} }
// Deprecated
func (d *DNS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (d *DNS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
metadata.Destination = M.Socksaddr{} metadata.Destination = M.Socksaddr{}
defer conn.Close() defer conn.Close()
@ -97,6 +98,7 @@ func (d *DNS) handleConnection(ctx context.Context, conn net.Conn, metadata adap
return nil return nil
} }
// Deprecated
func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
metadata.Destination = M.Socksaddr{} metadata.Destination = M.Socksaddr{}
var reader N.PacketReader = conn var reader N.PacketReader = conn

View file

@ -64,11 +64,3 @@ func (h *HTTP) DialContext(ctx context.Context, network string, destination M.So
func (h *HTTP) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { func (h *HTTP) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
return nil, os.ErrInvalid return nil, os.ErrInvalid
} }
func (h *HTTP) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return NewConnection(ctx, h, conn, metadata)
}
func (h *HTTP) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
return os.ErrInvalid
}

View file

@ -122,14 +122,6 @@ func (h *Hysteria) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
return h.client.ListenPacket(ctx, destination) return h.client.ListenPacket(ctx, destination)
} }
func (h *Hysteria) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return NewConnection(ctx, h, conn, metadata)
}
func (h *Hysteria) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
return NewPacketConnection(ctx, h, conn, metadata)
}
func (h *Hysteria) InterfaceUpdated() { func (h *Hysteria) InterfaceUpdated() {
h.client.CloseWithError(E.New("network changed")) h.client.CloseWithError(E.New("network changed"))
} }

View file

@ -108,14 +108,6 @@ func (h *Hysteria2) ListenPacket(ctx context.Context, destination M.Socksaddr) (
return h.client.ListenPacket(ctx) return h.client.ListenPacket(ctx)
} }
func (h *Hysteria2) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return NewConnection(ctx, h, conn, metadata)
}
func (h *Hysteria2) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
return NewPacketConnection(ctx, h, conn, metadata)
}
func (h *Hysteria2) InterfaceUpdated() { func (h *Hysteria2) InterfaceUpdated() {
h.client.CloseWithError(E.New("network changed")) h.client.CloseWithError(E.New("network changed"))
} }

View file

@ -94,6 +94,9 @@ func (l *ProxyListener) acceptLoop() {
} }
} }
// TODO: migrate to new api
//
//nolint:staticcheck
func (l *ProxyListener) accept(ctx context.Context, conn *net.TCPConn) error { func (l *ProxyListener) accept(ctx context.Context, conn *net.TCPConn) error {
return socks.HandleConnection(ctx, conn, l.authenticator, l, M.Metadata{}) return socks.HandleConnection(ctx, conn, l.authenticator, l, M.Metadata{})
} }

View file

@ -146,14 +146,26 @@ func (s *Selector) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
return s.interruptGroup.NewPacketConn(conn, interrupt.IsExternalConnectionFromContext(ctx)), nil return s.interruptGroup.NewPacketConn(conn, interrupt.IsExternalConnectionFromContext(ctx)), nil
} }
// TODO
// Deprecated
func (s *Selector) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (s *Selector) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
ctx = interrupt.ContextWithIsExternalConnection(ctx) ctx = interrupt.ContextWithIsExternalConnection(ctx)
return s.selected.NewConnection(ctx, conn, metadata) if legacyHandler, ok := s.selected.(adapter.ConnectionHandler); ok {
return legacyHandler.NewConnection(ctx, conn, metadata)
} else {
return NewConnection(ctx, s.selected, conn, metadata)
}
} }
// TODO
// Deprecated
func (s *Selector) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { func (s *Selector) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
ctx = interrupt.ContextWithIsExternalConnection(ctx) ctx = interrupt.ContextWithIsExternalConnection(ctx)
return s.selected.NewPacketConnection(ctx, conn, metadata) if legacyHandler, ok := s.selected.(adapter.PacketConnectionHandler); ok {
return legacyHandler.NewPacketConnection(ctx, conn, metadata)
} else {
return NewPacketConnection(ctx, s.selected, conn, metadata)
}
} }
func RealTag(detour adapter.Outbound) string { func RealTag(detour adapter.Outbound) string {

View file

@ -125,14 +125,6 @@ func (h *Shadowsocks) ListenPacket(ctx context.Context, destination M.Socksaddr)
} }
} }
func (h *Shadowsocks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return NewConnection(ctx, h, conn, metadata)
}
func (h *Shadowsocks) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
return NewPacketConnection(ctx, h, conn, metadata)
}
func (h *Shadowsocks) InterfaceUpdated() { func (h *Shadowsocks) InterfaceUpdated() {
if h.multiplexDialer != nil { if h.multiplexDialer != nil {
h.multiplexDialer.Reset() h.multiplexDialer.Reset()

View file

@ -106,11 +106,3 @@ func (h *ShadowTLS) DialContext(ctx context.Context, network string, destination
func (h *ShadowTLS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { func (h *ShadowTLS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
return nil, os.ErrInvalid return nil, os.ErrInvalid
} }
func (h *ShadowTLS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return NewConnection(ctx, h, conn, metadata)
}
func (h *ShadowTLS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
return os.ErrInvalid
}

View file

@ -113,6 +113,8 @@ func (h *Socks) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.
return h.client.ListenPacket(ctx, destination) return h.client.ListenPacket(ctx, destination)
} }
// TODO
// Deprecated
func (h *Socks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (h *Socks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
if h.resolve { if h.resolve {
return NewDirectConnection(ctx, h.router, h, conn, metadata, dns.DomainStrategyUseIPv4) return NewDirectConnection(ctx, h.router, h, conn, metadata, dns.DomainStrategyUseIPv4)
@ -121,6 +123,8 @@ func (h *Socks) NewConnection(ctx context.Context, conn net.Conn, metadata adapt
} }
} }
// TODO
// Deprecated
func (h *Socks) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { func (h *Socks) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
if h.resolve { if h.resolve {
return NewDirectPacketConnection(ctx, h.router, h, conn, metadata, dns.DomainStrategyUseIPv4) return NewDirectPacketConnection(ctx, h.router, h, conn, metadata, dns.DomainStrategyUseIPv4)

View file

@ -199,11 +199,3 @@ func (s *SSH) DialContext(ctx context.Context, network string, destination M.Soc
func (s *SSH) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { func (s *SSH) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
return nil, os.ErrInvalid return nil, os.ErrInvalid
} }
func (s *SSH) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return NewConnection(ctx, s, conn, metadata)
}
func (s *SSH) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
return os.ErrInvalid
}

View file

@ -211,11 +211,3 @@ func (t *Tor) DialContext(ctx context.Context, network string, destination M.Soc
func (t *Tor) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { func (t *Tor) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
return nil, os.ErrInvalid return nil, os.ErrInvalid
} }
func (t *Tor) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return NewConnection(ctx, t, conn, metadata)
}
func (t *Tor) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
return os.ErrInvalid
}

View file

@ -99,14 +99,6 @@ func (h *Trojan) ListenPacket(ctx context.Context, destination M.Socksaddr) (net
} }
} }
func (h *Trojan) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return NewConnection(ctx, h, conn, metadata)
}
func (h *Trojan) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
return NewPacketConnection(ctx, h, conn, metadata)
}
func (h *Trojan) InterfaceUpdated() { func (h *Trojan) InterfaceUpdated() {
if h.transport != nil { if h.transport != nil {
h.transport.Close() h.transport.Close()

View file

@ -136,14 +136,6 @@ func (h *TUIC) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.P
} }
} }
func (h *TUIC) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return NewConnection(ctx, h, conn, metadata)
}
func (h *TUIC) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
return NewPacketConnection(ctx, h, conn, metadata)
}
func (h *TUIC) InterfaceUpdated() { func (h *TUIC) InterfaceUpdated() {
_ = h.client.CloseWithError(E.New("network changed")) _ = h.client.CloseWithError(E.New("network changed"))
} }

View file

@ -167,11 +167,15 @@ func (s *URLTest) ListenPacket(ctx context.Context, destination M.Socksaddr) (ne
return nil, err return nil, err
} }
// TODO
// Deprecated
func (s *URLTest) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (s *URLTest) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
ctx = interrupt.ContextWithIsExternalConnection(ctx) ctx = interrupt.ContextWithIsExternalConnection(ctx)
return NewConnection(ctx, s, conn, metadata) return NewConnection(ctx, s, conn, metadata)
} }
// TODO
// Deprecated
func (s *URLTest) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { func (s *URLTest) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
ctx = interrupt.ContextWithIsExternalConnection(ctx) ctx = interrupt.ContextWithIsExternalConnection(ctx)
return NewPacketConnection(ctx, s, conn, metadata) return NewPacketConnection(ctx, s, conn, metadata)

View file

@ -118,14 +118,6 @@ func (h *VLESS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.
} }
} }
func (h *VLESS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return NewConnection(ctx, h, conn, metadata)
}
func (h *VLESS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
return NewPacketConnection(ctx, h, conn, metadata)
}
func (h *VLESS) InterfaceUpdated() { func (h *VLESS) InterfaceUpdated() {
if h.transport != nil { if h.transport != nil {
h.transport.Close() h.transport.Close()

View file

@ -146,14 +146,6 @@ func (h *VMess) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.
} }
} }
func (h *VMess) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return NewConnection(ctx, h, conn, metadata)
}
func (h *VMess) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
return NewPacketConnection(ctx, h, conn, metadata)
}
type vmessDialer VMess type vmessDialer VMess
func (h *vmessDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { func (h *vmessDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {

View file

@ -234,10 +234,14 @@ func (w *WireGuard) ListenPacket(ctx context.Context, destination M.Socksaddr) (
return w.tunDevice.ListenPacket(ctx, destination) return w.tunDevice.ListenPacket(ctx, destination)
} }
// TODO
// Deprecated
func (w *WireGuard) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (w *WireGuard) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return NewDirectConnection(ctx, w.router, w, conn, metadata, dns.DomainStrategyAsIS) return NewDirectConnection(ctx, w.router, w, conn, metadata, dns.DomainStrategyAsIS)
} }
// TODO
// Deprecated
func (w *WireGuard) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { func (w *WireGuard) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
return NewDirectPacketConnection(ctx, w.router, w, conn, metadata, dns.DomainStrategyAsIS) return NewDirectPacketConnection(ctx, w.router, w, conn, metadata, dns.DomainStrategyAsIS)
} }

View file

@ -13,6 +13,7 @@ import (
"github.com/sagernet/sing-box/common/geosite" "github.com/sagernet/sing-box/common/geosite"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/experimental/deprecated" "github.com/sagernet/sing-box/experimental/deprecated"
R "github.com/sagernet/sing-box/route/rule"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/rw" "github.com/sagernet/sing/common/rw"
@ -32,7 +33,7 @@ func (r *Router) LoadGeosite(code string) (adapter.Rule, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
rule, err = NewDefaultRule(r.ctx, r, nil, geosite.Compile(items)) rule, err = R.NewDefaultRule(r.ctx, r, nil, geosite.Compile(items))
if err != nil { if err != nil {
return nil, err return nil, err
} }

583
route/route.go Normal file
View file

@ -0,0 +1,583 @@
package route
import (
"context"
"errors"
"net"
"net/netip"
"os"
"os/user"
"strings"
"syscall"
"time"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/conntrack"
"github.com/sagernet/sing-box/common/process"
"github.com/sagernet/sing-box/common/sniff"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/outbound"
"github.com/sagernet/sing-box/route/rule"
"github.com/sagernet/sing-dns"
"github.com/sagernet/sing-mux"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing-vmess"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
"github.com/sagernet/sing/common/bufio/deadline"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/uot"
)
// Deprecated: use RouteConnectionEx instead.
func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return r.routeConnection(ctx, conn, metadata, nil)
}
func (r *Router) RouteConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
err := r.routeConnection(ctx, conn, metadata, onClose)
if err != nil {
N.CloseOnHandshakeFailure(conn, onClose, err)
if E.IsClosedOrCanceled(err) {
r.logger.DebugContext(ctx, "connection closed: ", err)
} else {
r.logger.ErrorContext(ctx, err)
}
}
}
func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) error {
if r.pauseManager.IsDevicePaused() {
return E.New("reject connection to ", metadata.Destination, " while device paused")
}
if metadata.InboundDetour != "" {
if metadata.LastInbound == metadata.InboundDetour {
return E.New("routing loop on detour: ", metadata.InboundDetour)
}
detour := r.inboundByTag[metadata.InboundDetour]
if detour == nil {
return E.New("inbound detour not found: ", metadata.InboundDetour)
}
injectable, isInjectable := detour.(adapter.TCPInjectableInbound)
if !isInjectable {
return E.New("inbound detour is not TCP injectable: ", metadata.InboundDetour)
}
metadata.LastInbound = metadata.Inbound
metadata.Inbound = metadata.InboundDetour
metadata.InboundDetour = ""
injectable.NewConnectionEx(ctx, conn, metadata, onClose)
return nil
}
conntrack.KillerCheck()
metadata.Network = N.NetworkTCP
switch metadata.Destination.Fqdn {
case mux.Destination.Fqdn:
return E.New("global multiplex is deprecated since sing-box v1.7.0, enable multiplex in inbound options instead.")
case vmess.MuxDestination.Fqdn:
return E.New("global multiplex (v2ray legacy) not supported since sing-box v1.7.0.")
case uot.MagicAddress:
return E.New("global UoT not supported since sing-box v1.7.0.")
case uot.LegacyMagicAddress:
return E.New("global UoT (legacy) not supported since sing-box v1.7.0.")
}
if deadline.NeedAdditionalReadDeadline(conn) {
conn = deadline.NewConn(conn)
}
selectedRule, _, buffers, err := r.matchRule(ctx, &metadata, conn, nil, -1)
if err != nil {
return err
}
var selectedOutbound adapter.Outbound
var selectReturn bool
if selectedRule != nil {
switch action := selectedRule.Action().(type) {
case *rule.RuleActionRoute:
var loaded bool
selectedOutbound, loaded = r.Outbound(action.Outbound)
if !loaded {
buf.ReleaseMulti(buffers)
return E.New("outbound not found: ", action.Outbound)
}
case *rule.RuleActionReturn:
selectReturn = true
case *rule.RuleActionReject:
buf.ReleaseMulti(buffers)
var rejectErr error
switch action.Method {
case C.RuleActionRejectMethodDefault:
rejectErr = os.ErrClosed
case C.RuleActionRejectMethodPortUnreachable:
rejectErr = syscall.ECONNREFUSED
case C.RuleActionRejectMethodDrop:
rejectErr = tun.ErrDrop
}
N.CloseOnHandshakeFailure(conn, onClose, rejectErr)
return nil
}
}
if selectedRule == nil || selectReturn {
if r.defaultOutboundForConnection == nil {
buf.ReleaseMulti(buffers)
return E.New("missing default outbound with TCP support")
}
selectedOutbound = r.defaultOutboundForConnection
}
if !common.Contains(selectedOutbound.Network(), N.NetworkTCP) {
buf.ReleaseMulti(buffers)
return E.New("TCP is not supported by outbound: ", selectedOutbound.Tag())
}
for _, buffer := range buffers {
conn = bufio.NewCachedConn(conn, buffer)
}
if r.clashServer != nil {
trackerConn, tracker := r.clashServer.RoutedConnection(ctx, conn, metadata, selectedRule)
defer tracker.Leave()
conn = trackerConn
}
if r.v2rayServer != nil {
if statsService := r.v2rayServer.StatsService(); statsService != nil {
conn = statsService.RoutedConnection(metadata.Inbound, selectedOutbound.Tag(), metadata.User, conn)
}
}
legacyOutbound, isLegacy := selectedOutbound.(adapter.ConnectionHandler)
if isLegacy {
err = legacyOutbound.NewConnection(ctx, conn, metadata)
if err != nil {
conn.Close()
if onClose != nil {
onClose(err)
}
return E.Cause(err, "outbound/", selectedOutbound.Type(), "[", selectedOutbound.Tag(), "]")
} else {
if onClose != nil {
onClose(nil)
}
}
return nil
}
// TODO
err = outbound.NewConnection(ctx, selectedOutbound, conn, metadata)
if err != nil {
conn.Close()
if onClose != nil {
onClose(err)
}
return E.Cause(err, "outbound/", selectedOutbound.Type(), "[", selectedOutbound.Tag(), "]")
} else {
if onClose != nil {
onClose(nil)
}
}
return nil
}
func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
err := r.routePacketConnection(ctx, conn, metadata, nil)
if err != nil {
conn.Close()
if E.IsClosedOrCanceled(err) {
r.logger.DebugContext(ctx, "connection closed: ", err)
} else {
r.logger.ErrorContext(ctx, err)
}
}
return nil
}
func (r *Router) RoutePacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
err := r.routePacketConnection(ctx, conn, metadata, onClose)
if err != nil {
N.CloseOnHandshakeFailure(conn, onClose, err)
if E.IsClosedOrCanceled(err) {
r.logger.DebugContext(ctx, "connection closed: ", err)
} else {
r.logger.ErrorContext(ctx, err)
}
} else if onClose != nil {
onClose(nil)
}
}
func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) error {
if r.pauseManager.IsDevicePaused() {
return E.New("reject packet connection to ", metadata.Destination, " while device paused")
}
if metadata.InboundDetour != "" {
if metadata.LastInbound == metadata.InboundDetour {
return E.New("routing loop on detour: ", metadata.InboundDetour)
}
detour := r.inboundByTag[metadata.InboundDetour]
if detour == nil {
return E.New("inbound detour not found: ", metadata.InboundDetour)
}
injectable, isInjectable := detour.(adapter.UDPInjectableInbound)
if !isInjectable {
return E.New("inbound detour is not UDP injectable: ", metadata.InboundDetour)
}
metadata.LastInbound = metadata.Inbound
metadata.Inbound = metadata.InboundDetour
metadata.InboundDetour = ""
injectable.NewPacketConnectionEx(ctx, conn, metadata, onClose)
return nil
}
conntrack.KillerCheck()
// TODO: move to UoT
metadata.Network = N.NetworkUDP
// Currently we don't have deadline usages for UDP connections
/*if deadline.NeedAdditionalReadDeadline(conn) {
conn = deadline.NewPacketConn(bufio.NewNetPacketConn(conn))
}*/
selectedRule, _, buffers, err := r.matchRule(ctx, &metadata, nil, conn, -1)
if err != nil {
return err
}
var selectedOutbound adapter.Outbound
var selectReturn bool
if selectedRule != nil {
switch action := selectedRule.Action().(type) {
case *rule.RuleActionRoute:
var loaded bool
selectedOutbound, loaded = r.Outbound(action.Outbound)
if !loaded {
buf.ReleaseMulti(buffers)
return E.New("outbound not found: ", action.Outbound)
}
metadata.UDPDisableDomainUnmapping = action.UDPDisableDomainUnmapping
case *rule.RuleActionReturn:
selectReturn = true
case *rule.RuleActionReject:
buf.ReleaseMulti(buffers)
N.CloseOnHandshakeFailure(conn, onClose, syscall.ECONNREFUSED)
return nil
}
}
if selectedRule == nil || selectReturn {
if r.defaultOutboundForPacketConnection == nil {
buf.ReleaseMulti(buffers)
return E.New("missing default outbound with UDP support")
}
selectedOutbound = r.defaultOutboundForPacketConnection
}
if !common.Contains(selectedOutbound.Network(), N.NetworkUDP) {
buf.ReleaseMulti(buffers)
return E.New("UDP is not supported by outbound: ", selectedOutbound.Tag())
}
for _, buffer := range buffers {
// TODO: check if metadata.Destination == packet destination
conn = bufio.NewCachedPacketConn(conn, buffer, metadata.Destination)
}
if r.clashServer != nil {
trackerConn, tracker := r.clashServer.RoutedPacketConnection(ctx, conn, metadata, selectedRule)
defer tracker.Leave()
conn = trackerConn
}
if r.v2rayServer != nil {
if statsService := r.v2rayServer.StatsService(); statsService != nil {
conn = statsService.RoutedPacketConnection(metadata.Inbound, selectedOutbound.Tag(), metadata.User, conn)
}
}
if metadata.FakeIP {
conn = bufio.NewNATPacketConn(bufio.NewNetPacketConn(conn), metadata.OriginDestination, metadata.Destination)
}
legacyOutbound, isLegacy := selectedOutbound.(adapter.PacketConnectionHandler)
if isLegacy {
err = legacyOutbound.NewPacketConnection(ctx, conn, metadata)
N.CloseOnHandshakeFailure(conn, onClose, err)
if err != nil {
return E.Cause(err, "outbound/", selectedOutbound.Type(), "[", selectedOutbound.Tag(), "]")
}
return nil
}
// TODO
err = outbound.NewPacketConnection(ctx, selectedOutbound, conn, metadata)
N.CloseOnHandshakeFailure(conn, onClose, err)
if err != nil {
return E.Cause(err, "outbound/", selectedOutbound.Type(), "[", selectedOutbound.Tag(), "]")
}
return nil
}
func (r *Router) matchRule(
ctx context.Context, metadata *adapter.InboundContext,
inputConn net.Conn, inputPacketConn N.PacketConn, ruleIndex int,
) (selectedRule adapter.Rule, selectedRuleIndex int, buffers []*buf.Buffer, fatalErr error) {
if r.processSearcher != nil && metadata.ProcessInfo == nil {
var originDestination netip.AddrPort
if metadata.OriginDestination.IsValid() {
originDestination = metadata.OriginDestination.AddrPort()
} else if metadata.Destination.IsIP() {
originDestination = metadata.Destination.AddrPort()
}
processInfo, fErr := process.FindProcessInfo(r.processSearcher, ctx, metadata.Network, metadata.Source.AddrPort(), originDestination)
if fErr != nil {
r.logger.InfoContext(ctx, "failed to search process: ", fErr)
} else {
if processInfo.ProcessPath != "" {
r.logger.InfoContext(ctx, "found process path: ", processInfo.ProcessPath)
} else if processInfo.PackageName != "" {
r.logger.InfoContext(ctx, "found package name: ", processInfo.PackageName)
} else if processInfo.UserId != -1 {
if /*needUserName &&*/ true {
osUser, _ := user.LookupId(F.ToString(processInfo.UserId))
if osUser != nil {
processInfo.User = osUser.Username
}
}
if processInfo.User != "" {
r.logger.InfoContext(ctx, "found user: ", processInfo.User)
} else {
r.logger.InfoContext(ctx, "found user id: ", processInfo.UserId)
}
}
metadata.ProcessInfo = processInfo
}
}
if r.fakeIPStore != nil && r.fakeIPStore.Contains(metadata.Destination.Addr) {
domain, loaded := r.fakeIPStore.Lookup(metadata.Destination.Addr)
if !loaded {
fatalErr = E.New("missing fakeip record, try to configure experimental.cache_file")
return
}
metadata.OriginDestination = metadata.Destination
metadata.Destination = M.Socksaddr{
Fqdn: domain,
Port: metadata.Destination.Port,
}
metadata.FakeIP = true
r.logger.DebugContext(ctx, "found fakeip domain: ", domain)
}
if r.dnsReverseMapping != nil && metadata.Domain == "" {
domain, loaded := r.dnsReverseMapping.Query(metadata.Destination.Addr)
if loaded {
metadata.Domain = domain
r.logger.DebugContext(ctx, "found reserve mapped domain: ", metadata.Domain)
}
}
if metadata.Destination.IsIPv4() {
metadata.IPVersion = 4
} else if metadata.Destination.IsIPv6() {
metadata.IPVersion = 6
}
//nolint:staticcheck
if metadata.InboundOptions != common.DefaultValue[option.InboundOptions]() {
if metadata.InboundOptions.SniffEnabled {
newBuffers, newErr := r.actionSniff(ctx, metadata, &rule.RuleActionSniff{
OverrideDestination: metadata.InboundOptions.SniffOverrideDestination,
Timeout: time.Duration(metadata.InboundOptions.SniffTimeout),
}, inputConn, inputPacketConn)
if newErr != nil {
fatalErr = newErr
return
}
buffers = append(buffers, newBuffers...)
}
if dns.DomainStrategy(metadata.InboundOptions.DomainStrategy) != dns.DomainStrategyAsIS {
fatalErr = r.actionResolve(ctx, metadata, &rule.RuleActionResolve{
Strategy: dns.DomainStrategy(metadata.InboundOptions.DomainStrategy),
})
if fatalErr != nil {
return
}
}
if metadata.InboundOptions.UDPDisableDomainUnmapping {
metadata.UDPDisableDomainUnmapping = true
}
metadata.InboundOptions = option.InboundOptions{}
}
match:
for ruleIndex < len(r.rules) {
rules := r.rules
if ruleIndex != -1 {
rules = rules[ruleIndex+1:]
}
var (
currentRule adapter.Rule
currentRuleIndex int
matched bool
)
for currentRuleIndex, currentRule = range rules {
if currentRule.Match(metadata) {
matched = true
break
}
}
if !matched {
break
}
r.logger.DebugContext(ctx, "match[", currentRuleIndex, "] ", currentRule, " => ", currentRule.Action())
switch action := currentRule.Action().(type) {
case *rule.RuleActionSniff:
newBuffers, newErr := r.actionSniff(ctx, metadata, action, inputConn, inputPacketConn)
if newErr != nil {
fatalErr = newErr
return
}
buffers = append(buffers, newBuffers...)
case *rule.RuleActionResolve:
fatalErr = r.actionResolve(ctx, metadata, action)
if fatalErr != nil {
return
}
default:
selectedRule = currentRule
selectedRuleIndex = currentRuleIndex
break match
}
ruleIndex = currentRuleIndex
}
if metadata.Destination.Addr.IsUnspecified() {
newBuffers, newErr := r.actionSniff(ctx, metadata, &rule.RuleActionSniff{}, inputConn, inputPacketConn)
if newErr != nil {
fatalErr = newErr
return
}
buffers = append(buffers, newBuffers...)
}
return
}
func (r *Router) actionSniff(
ctx context.Context, metadata *adapter.InboundContext, action *rule.RuleActionSniff,
inputConn net.Conn, inputPacketConn N.PacketConn,
) (buffers []*buf.Buffer, fatalErr error) {
if sniff.Skip(metadata) {
return
} else if inputConn != nil && len(action.StreamSniffers) > 0 {
buffer := buf.NewPacket()
err := sniff.PeekStream(
ctx,
metadata,
inputConn,
buffer,
action.Timeout,
action.StreamSniffers...,
)
if err == nil {
//goland:noinspection GoDeprecation
if action.OverrideDestination && M.IsDomainName(metadata.Domain) {
metadata.Destination = M.Socksaddr{
Fqdn: metadata.Domain,
Port: metadata.Destination.Port,
}
}
if metadata.Domain != "" && metadata.Client != "" {
r.logger.DebugContext(ctx, "sniffed protocol: ", metadata.Protocol, ", domain: ", metadata.Domain, ", client: ", metadata.Client)
} else if metadata.Domain != "" {
r.logger.DebugContext(ctx, "sniffed protocol: ", metadata.Protocol, ", domain: ", metadata.Domain)
} else {
r.logger.DebugContext(ctx, "sniffed protocol: ", metadata.Protocol)
}
}
if !buffer.IsEmpty() {
buffers = append(buffers, buffer)
} else {
buffer.Release()
}
} else if inputPacketConn != nil && len(action.PacketSniffers) > 0 {
for {
var (
buffer = buf.NewPacket()
destination M.Socksaddr
done = make(chan struct{})
err error
)
go func() {
sniffTimeout := C.ReadPayloadTimeout
if action.Timeout > 0 {
sniffTimeout = action.Timeout
}
inputPacketConn.SetReadDeadline(time.Now().Add(sniffTimeout))
destination, err = inputPacketConn.ReadPacket(buffer)
inputPacketConn.SetReadDeadline(time.Time{})
close(done)
}()
select {
case <-done:
case <-ctx.Done():
inputPacketConn.Close()
fatalErr = ctx.Err()
return
}
if err != nil {
buffer.Release()
if !errors.Is(err, os.ErrDeadlineExceeded) {
fatalErr = err
return
}
} else {
// TODO: maybe always override destination
if metadata.Destination.Addr.IsUnspecified() {
metadata.Destination = destination
}
if len(buffers) > 0 {
err = sniff.PeekPacket(
ctx,
metadata,
buffer.Bytes(),
sniff.QUICClientHello,
)
} else {
err = sniff.PeekPacket(
ctx, metadata,
buffer.Bytes(),
action.PacketSniffers...,
)
}
buffers = append(buffers, buffer)
if E.IsMulti(err, sniff.ErrClientHelloFragmented) && len(buffers) == 0 {
r.logger.DebugContext(ctx, "attempt to sniff fragmented QUIC client hello")
continue
}
if metadata.Protocol != "" {
//goland:noinspection GoDeprecation
if action.OverrideDestination && M.IsDomainName(metadata.Domain) {
metadata.Destination = M.Socksaddr{
Fqdn: metadata.Domain,
Port: metadata.Destination.Port,
}
}
if metadata.Domain != "" && metadata.Client != "" {
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol, ", domain: ", metadata.Domain, ", client: ", metadata.Client)
} else if metadata.Domain != "" {
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol, ", domain: ", metadata.Domain)
} else if metadata.Client != "" {
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol, ", client: ", metadata.Client)
} else {
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol)
}
}
}
break
}
}
return
}
func (r *Router) actionResolve(ctx context.Context, metadata *adapter.InboundContext, action *rule.RuleActionResolve) error {
if metadata.Destination.IsFqdn() {
// TODO: check if WithContext is necessary
addresses, err := r.Lookup(adapter.WithContext(ctx, metadata), metadata.Destination.Fqdn, action.Strategy)
if err != nil {
return err
}
metadata.DestinationAddresses = addresses
r.dnsLogger.DebugContext(ctx, "resolved [", strings.Join(F.MapToString(metadata.DestinationAddresses), " "), "]")
if metadata.Destination.IsIPv4() {
metadata.IPVersion = 4
} else if metadata.Destination.IsIPv6() {
metadata.IPVersion = 6
}
}
return nil
}

View file

@ -8,6 +8,7 @@ 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-dns" "github.com/sagernet/sing-dns"
"github.com/sagernet/sing/common/cache" "github.com/sagernet/sing/common/cache"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
@ -36,15 +37,16 @@ func (m *DNSReverseMapping) Query(address netip.Addr) (string, bool) {
return domain, loaded return domain, loaded
} }
func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, index int, isAddressQuery bool) (context.Context, dns.Transport, dns.DomainStrategy, adapter.DNSRule, int) { func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, ruleIndex int, isAddressQuery bool) (dns.Transport, dns.QueryOptions, adapter.DNSRule, int) {
metadata := adapter.ContextFrom(ctx) metadata := adapter.ContextFrom(ctx)
if metadata == nil { if metadata == nil {
panic("no context") panic("no context")
} }
if index < len(r.dnsRules) { var options dns.QueryOptions
if ruleIndex < len(r.dnsRules) {
dnsRules := r.dnsRules dnsRules := r.dnsRules
if index != -1 { if ruleIndex != -1 {
dnsRules = dnsRules[index+1:] dnsRules = dnsRules[ruleIndex+1:]
} }
for currentRuleIndex, rule := range dnsRules { for currentRuleIndex, rule := range dnsRules {
if rule.WithAddressLimit() && !isAddressQuery { if rule.WithAddressLimit() && !isAddressQuery {
@ -52,43 +54,42 @@ func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, index int, isAd
} }
metadata.ResetRuleCache() metadata.ResetRuleCache()
if rule.Match(metadata) { if rule.Match(metadata) {
detour := rule.Outbound() displayRuleIndex := currentRuleIndex
transport, loaded := r.transportMap[detour] if displayRuleIndex != -1 {
if !loaded { displayRuleIndex += displayRuleIndex + 1
r.dnsLogger.ErrorContext(ctx, "transport not found: ", detour)
continue
} }
_, isFakeIP := transport.(adapter.FakeIPTransport) if routeAction, isRoute := rule.Action().(*R.RuleActionDNSRoute); isRoute {
if isFakeIP && !allowFakeIP { transport, loaded := r.transportMap[routeAction.Server]
continue if !loaded {
} r.dnsLogger.ErrorContext(ctx, "transport not found: ", routeAction.Server)
ruleIndex := currentRuleIndex continue
if index != -1 { }
ruleIndex += index + 1 _, isFakeIP := transport.(adapter.FakeIPTransport)
} if isFakeIP && !allowFakeIP {
r.dnsLogger.DebugContext(ctx, "match[", ruleIndex, "] ", rule.String(), " => ", detour) continue
if isFakeIP || rule.DisableCache() { }
ctx = dns.ContextWithDisableCache(ctx, true) options.DisableCache = isFakeIP || routeAction.DisableCache
} options.RewriteTTL = routeAction.RewriteTTL
if rewriteTTL := rule.RewriteTTL(); rewriteTTL != nil { options.ClientSubnet = routeAction.ClientSubnet
ctx = dns.ContextWithRewriteTTL(ctx, *rewriteTTL) if domainStrategy, dsLoaded := r.transportDomainStrategy[transport]; dsLoaded {
} options.Strategy = domainStrategy
if clientSubnet := rule.ClientSubnet(); clientSubnet != nil { } else {
ctx = dns.ContextWithClientSubnet(ctx, *clientSubnet) options.Strategy = r.defaultDomainStrategy
} }
if domainStrategy, dsLoaded := r.transportDomainStrategy[transport]; dsLoaded { r.dnsLogger.DebugContext(ctx, "match[", displayRuleIndex, "] ", rule.String(), " => ", rule.Action())
return ctx, transport, domainStrategy, rule, ruleIndex return transport, options, rule, currentRuleIndex
} else { } else {
return ctx, transport, r.defaultDomainStrategy, rule, ruleIndex return nil, options, rule, currentRuleIndex
} }
} }
} }
} }
if domainStrategy, dsLoaded := r.transportDomainStrategy[r.defaultTransport]; dsLoaded { if domainStrategy, dsLoaded := r.transportDomainStrategy[r.defaultTransport]; dsLoaded {
return ctx, r.defaultTransport, domainStrategy, nil, -1 options.Strategy = domainStrategy
} else { } else {
return ctx, r.defaultTransport, r.defaultDomainStrategy, nil, -1 options.Strategy = r.defaultDomainStrategy
} }
return r.defaultTransport, options, nil, -1
} }
func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
@ -117,21 +118,18 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
metadata.Domain = fqdnToDomain(message.Question[0].Name) metadata.Domain = fqdnToDomain(message.Question[0].Name)
} }
var ( var (
strategy dns.DomainStrategy options dns.QueryOptions
rule adapter.DNSRule rule adapter.DNSRule
ruleIndex int ruleIndex int
) )
ruleIndex = -1 ruleIndex = -1
for { for {
var ( dnsCtx := adapter.OverrideContext(ctx)
dnsCtx context.Context var addressLimit bool
addressLimit bool transport, options, rule, ruleIndex = r.matchDNS(ctx, true, ruleIndex, isAddressQuery(message))
)
dnsCtx, transport, strategy, rule, ruleIndex = r.matchDNS(ctx, true, ruleIndex, isAddressQuery(message))
dnsCtx = adapter.OverrideContext(dnsCtx)
if rule != nil && rule.WithAddressLimit() { if rule != nil && rule.WithAddressLimit() {
addressLimit = true addressLimit = true
response, err = r.dnsClient.ExchangeWithResponseCheck(dnsCtx, transport, message, strategy, func(response *mDNS.Msg) bool { response, err = r.dnsClient.ExchangeWithResponseCheck(dnsCtx, transport, message, options, func(response *mDNS.Msg) bool {
addresses, addrErr := dns.MessageToAddresses(response) addresses, addrErr := dns.MessageToAddresses(response)
if addrErr != nil { if addrErr != nil {
return false return false
@ -141,7 +139,7 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
}) })
} else { } else {
addressLimit = false addressLimit = false
response, err = r.dnsClient.Exchange(dnsCtx, transport, message, strategy) response, err = r.dnsClient.Exchange(dnsCtx, transport, message, options)
} }
var rejected bool var rejected bool
if err != nil { if err != nil {
@ -199,31 +197,28 @@ func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainS
metadata.Destination = M.Socksaddr{} metadata.Destination = M.Socksaddr{}
metadata.Domain = domain metadata.Domain = domain
var ( var (
transport dns.Transport transport dns.Transport
transportStrategy dns.DomainStrategy options dns.QueryOptions
rule adapter.DNSRule rule adapter.DNSRule
ruleIndex int ruleIndex int
) )
ruleIndex = -1 ruleIndex = -1
for { for {
var ( dnsCtx := adapter.OverrideContext(ctx)
dnsCtx context.Context var addressLimit bool
addressLimit bool transport, options, rule, ruleIndex = r.matchDNS(ctx, false, ruleIndex, true)
) if strategy != dns.DomainStrategyAsIS {
dnsCtx, transport, transportStrategy, rule, ruleIndex = r.matchDNS(ctx, false, ruleIndex, true) options.Strategy = strategy
dnsCtx = adapter.OverrideContext(dnsCtx)
if strategy == dns.DomainStrategyAsIS {
strategy = transportStrategy
} }
if rule != nil && rule.WithAddressLimit() { if rule != nil && rule.WithAddressLimit() {
addressLimit = true addressLimit = true
responseAddrs, err = r.dnsClient.LookupWithResponseCheck(dnsCtx, transport, domain, strategy, func(responseAddrs []netip.Addr) bool { responseAddrs, err = r.dnsClient.LookupWithResponseCheck(dnsCtx, transport, domain, options, func(responseAddrs []netip.Addr) bool {
metadata.DestinationAddresses = responseAddrs metadata.DestinationAddresses = responseAddrs
return rule.MatchAddressLimit(metadata) return rule.MatchAddressLimit(metadata)
}) })
} else { } else {
addressLimit = false addressLimit = false
responseAddrs, err = r.dnsClient.Lookup(dnsCtx, transport, domain, strategy) responseAddrs, err = r.dnsClient.Lookup(dnsCtx, transport, domain, options)
} }
if err != nil { if err != nil {
if errors.Is(err, dns.ErrResponseRejectedCached) { if errors.Is(err, dns.ErrResponseRejectedCached) {

View file

@ -3,11 +3,9 @@ package route
import ( import (
"context" "context"
"errors" "errors"
"net"
"net/netip" "net/netip"
"net/url" "net/url"
"os" "os"
"os/user"
"runtime" "runtime"
"strings" "strings"
"syscall" "syscall"
@ -19,22 +17,16 @@ import (
"github.com/sagernet/sing-box/common/geoip" "github.com/sagernet/sing-box/common/geoip"
"github.com/sagernet/sing-box/common/geosite" "github.com/sagernet/sing-box/common/geosite"
"github.com/sagernet/sing-box/common/process" "github.com/sagernet/sing-box/common/process"
"github.com/sagernet/sing-box/common/sniff"
"github.com/sagernet/sing-box/common/taskmonitor" "github.com/sagernet/sing-box/common/taskmonitor"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/experimental/libbox/platform" "github.com/sagernet/sing-box/experimental/libbox/platform"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/outbound" R "github.com/sagernet/sing-box/route/rule"
"github.com/sagernet/sing-box/transport/fakeip" "github.com/sagernet/sing-box/transport/fakeip"
"github.com/sagernet/sing-dns" "github.com/sagernet/sing-dns"
"github.com/sagernet/sing-mux"
"github.com/sagernet/sing-tun" "github.com/sagernet/sing-tun"
"github.com/sagernet/sing-vmess"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
"github.com/sagernet/sing/common/bufio/deadline"
"github.com/sagernet/sing/common/control" "github.com/sagernet/sing/common/control"
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"
@ -42,7 +34,6 @@ import (
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"
"github.com/sagernet/sing/common/task" "github.com/sagernet/sing/common/task"
"github.com/sagernet/sing/common/uot"
"github.com/sagernet/sing/common/winpowrprof" "github.com/sagernet/sing/common/winpowrprof"
"github.com/sagernet/sing/service" "github.com/sagernet/sing/service"
"github.com/sagernet/sing/service/pause" "github.com/sagernet/sing/service/pause"
@ -154,14 +145,14 @@ func NewRouter(
Logger: router.dnsLogger, Logger: router.dnsLogger,
}) })
for i, ruleOptions := range options.Rules { for i, ruleOptions := range options.Rules {
routeRule, err := NewRule(ctx, router, router.logger, ruleOptions, true) routeRule, err := R.NewRule(ctx, router, router.logger, ruleOptions, true)
if err != nil { if err != nil {
return nil, E.Cause(err, "parse rule[", i, "]") return nil, E.Cause(err, "parse rule[", i, "]")
} }
router.rules = append(router.rules, routeRule) router.rules = append(router.rules, routeRule)
} }
for i, dnsRuleOptions := range dnsOptions.Rules { for i, dnsRuleOptions := range dnsOptions.Rules {
dnsRule, err := NewDNSRule(ctx, router, router.logger, dnsRuleOptions, true) dnsRule, err := R.NewDNSRule(ctx, router, router.logger, dnsRuleOptions, true)
if err != nil { if err != nil {
return nil, E.Cause(err, "parse dns rule[", i, "]") return nil, E.Cause(err, "parse dns rule[", i, "]")
} }
@ -171,7 +162,7 @@ func NewRouter(
if _, exists := router.ruleSetMap[ruleSetOptions.Tag]; exists { if _, exists := router.ruleSetMap[ruleSetOptions.Tag]; exists {
return nil, E.New("duplicate rule-set tag: ", ruleSetOptions.Tag) return nil, E.New("duplicate rule-set tag: ", ruleSetOptions.Tag)
} }
ruleSet, err := NewRuleSet(ctx, router, router.logger, ruleSetOptions) ruleSet, err := R.NewRuleSet(ctx, router, router.logger, ruleSetOptions)
if err != nil { if err != nil {
return nil, E.Cause(err, "parse rule-set[", i, "]") return nil, E.Cause(err, "parse rule-set[", i, "]")
} }
@ -440,8 +431,12 @@ func (r *Router) Initialize(inbounds []adapter.Inbound, outbounds []adapter.Outb
r.defaultOutboundForPacketConnection = defaultOutboundForPacketConnection r.defaultOutboundForPacketConnection = defaultOutboundForPacketConnection
r.outboundByTag = outboundByTag r.outboundByTag = outboundByTag
for i, rule := range r.rules { for i, rule := range r.rules {
if _, loaded := outboundByTag[rule.Outbound()]; !loaded { routeAction, isRoute := rule.Action().(*R.RuleActionRoute)
return E.New("outbound not found for rule[", i, "]: ", rule.Outbound()) if !isRoute {
continue
}
if _, loaded := outboundByTag[routeAction.Outbound]; !loaded {
return E.New("outbound not found for rule[", i, "]: ", routeAction.Outbound)
} }
} }
return nil return nil
@ -807,375 +802,6 @@ func (r *Router) NeedWIFIState() bool {
return r.needWIFIState return r.needWIFIState
} }
func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
if r.pauseManager.IsDevicePaused() {
return E.New("reject connection to ", metadata.Destination, " while device paused")
}
if metadata.InboundDetour != "" {
if metadata.LastInbound == metadata.InboundDetour {
return E.New("routing loop on detour: ", metadata.InboundDetour)
}
detour := r.inboundByTag[metadata.InboundDetour]
if detour == nil {
return E.New("inbound detour not found: ", metadata.InboundDetour)
}
injectable, isInjectable := detour.(adapter.InjectableInbound)
if !isInjectable {
return E.New("inbound detour is not injectable: ", metadata.InboundDetour)
}
if !common.Contains(injectable.Network(), N.NetworkTCP) {
return E.New("inject: TCP unsupported")
}
metadata.LastInbound = metadata.Inbound
metadata.Inbound = metadata.InboundDetour
metadata.InboundDetour = ""
err := injectable.NewConnection(ctx, conn, metadata)
if err != nil {
return E.Cause(err, "inject ", detour.Tag())
}
return nil
}
conntrack.KillerCheck()
metadata.Network = N.NetworkTCP
switch metadata.Destination.Fqdn {
case mux.Destination.Fqdn:
return E.New("global multiplex is deprecated since sing-box v1.7.0, enable multiplex in inbound options instead.")
case vmess.MuxDestination.Fqdn:
return E.New("global multiplex (v2ray legacy) not supported since sing-box v1.7.0.")
case uot.MagicAddress:
return E.New("global UoT not supported since sing-box v1.7.0.")
case uot.LegacyMagicAddress:
return E.New("global UoT (legacy) not supported since sing-box v1.7.0.")
}
if r.fakeIPStore != nil && r.fakeIPStore.Contains(metadata.Destination.Addr) {
domain, loaded := r.fakeIPStore.Lookup(metadata.Destination.Addr)
if !loaded {
return E.New("missing fakeip context")
}
metadata.OriginDestination = metadata.Destination
metadata.Destination = M.Socksaddr{
Fqdn: domain,
Port: metadata.Destination.Port,
}
metadata.FakeIP = true
r.logger.DebugContext(ctx, "found fakeip domain: ", domain)
}
if deadline.NeedAdditionalReadDeadline(conn) {
conn = deadline.NewConn(conn)
}
if metadata.InboundOptions.SniffEnabled && !sniff.Skip(metadata) {
buffer := buf.NewPacket()
err := sniff.PeekStream(
ctx,
&metadata,
conn,
buffer,
time.Duration(metadata.InboundOptions.SniffTimeout),
sniff.TLSClientHello,
sniff.HTTPHost,
sniff.StreamDomainNameQuery,
sniff.SSH,
sniff.BitTorrent,
)
if err == nil {
if metadata.InboundOptions.SniffOverrideDestination && M.IsDomainName(metadata.Domain) {
metadata.Destination = M.Socksaddr{
Fqdn: metadata.Domain,
Port: metadata.Destination.Port,
}
}
if metadata.Domain != "" {
r.logger.DebugContext(ctx, "sniffed protocol: ", metadata.Protocol, ", domain: ", metadata.Domain)
} else {
r.logger.DebugContext(ctx, "sniffed protocol: ", metadata.Protocol)
}
}
if !buffer.IsEmpty() {
conn = bufio.NewCachedConn(conn, buffer)
} else {
buffer.Release()
}
}
if r.dnsReverseMapping != nil && metadata.Domain == "" {
domain, loaded := r.dnsReverseMapping.Query(metadata.Destination.Addr)
if loaded {
metadata.Domain = domain
r.logger.DebugContext(ctx, "found reserve mapped domain: ", metadata.Domain)
}
}
if metadata.Destination.IsFqdn() && dns.DomainStrategy(metadata.InboundOptions.DomainStrategy) != dns.DomainStrategyAsIS {
addresses, err := r.Lookup(adapter.WithContext(ctx, &metadata), metadata.Destination.Fqdn, dns.DomainStrategy(metadata.InboundOptions.DomainStrategy))
if err != nil {
return err
}
metadata.DestinationAddresses = addresses
r.dnsLogger.DebugContext(ctx, "resolved [", strings.Join(F.MapToString(metadata.DestinationAddresses), " "), "]")
}
if metadata.Destination.IsIPv4() {
metadata.IPVersion = 4
} else if metadata.Destination.IsIPv6() {
metadata.IPVersion = 6
}
ctx, matchedRule, detour, err := r.match(ctx, &metadata, r.defaultOutboundForConnection)
if err != nil {
return err
}
if !common.Contains(detour.Network(), N.NetworkTCP) {
return E.New("missing supported outbound, closing connection")
}
if r.clashServer != nil {
trackerConn, tracker := r.clashServer.RoutedConnection(ctx, conn, metadata, matchedRule)
defer tracker.Leave()
conn = trackerConn
}
if r.v2rayServer != nil {
if statsService := r.v2rayServer.StatsService(); statsService != nil {
conn = statsService.RoutedConnection(metadata.Inbound, detour.Tag(), metadata.User, conn)
}
}
return detour.NewConnection(ctx, conn, metadata)
}
func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
if r.pauseManager.IsDevicePaused() {
return E.New("reject packet connection to ", metadata.Destination, " while device paused")
}
if metadata.InboundDetour != "" {
if metadata.LastInbound == metadata.InboundDetour {
return E.New("routing loop on detour: ", metadata.InboundDetour)
}
detour := r.inboundByTag[metadata.InboundDetour]
if detour == nil {
return E.New("inbound detour not found: ", metadata.InboundDetour)
}
injectable, isInjectable := detour.(adapter.InjectableInbound)
if !isInjectable {
return E.New("inbound detour is not injectable: ", metadata.InboundDetour)
}
if !common.Contains(injectable.Network(), N.NetworkUDP) {
return E.New("inject: UDP unsupported")
}
metadata.LastInbound = metadata.Inbound
metadata.Inbound = metadata.InboundDetour
metadata.InboundDetour = ""
err := injectable.NewPacketConnection(ctx, conn, metadata)
if err != nil {
return E.Cause(err, "inject ", detour.Tag())
}
return nil
}
conntrack.KillerCheck()
metadata.Network = N.NetworkUDP
if r.fakeIPStore != nil && r.fakeIPStore.Contains(metadata.Destination.Addr) {
domain, loaded := r.fakeIPStore.Lookup(metadata.Destination.Addr)
if !loaded {
return E.New("missing fakeip context")
}
metadata.OriginDestination = metadata.Destination
metadata.Destination = M.Socksaddr{
Fqdn: domain,
Port: metadata.Destination.Port,
}
metadata.FakeIP = true
r.logger.DebugContext(ctx, "found fakeip domain: ", domain)
}
// Currently we don't have deadline usages for UDP connections
/*if deadline.NeedAdditionalReadDeadline(conn) {
conn = deadline.NewPacketConn(bufio.NewNetPacketConn(conn))
}*/
if metadata.InboundOptions.SniffEnabled || metadata.Destination.Addr.IsUnspecified() {
var bufferList []*buf.Buffer
for {
var (
buffer = buf.NewPacket()
destination M.Socksaddr
done = make(chan struct{})
err error
)
go func() {
sniffTimeout := C.ReadPayloadTimeout
if metadata.InboundOptions.SniffTimeout > 0 {
sniffTimeout = time.Duration(metadata.InboundOptions.SniffTimeout)
}
conn.SetReadDeadline(time.Now().Add(sniffTimeout))
destination, err = conn.ReadPacket(buffer)
conn.SetReadDeadline(time.Time{})
close(done)
}()
select {
case <-done:
case <-ctx.Done():
conn.Close()
return ctx.Err()
}
if err != nil {
buffer.Release()
if !errors.Is(err, os.ErrDeadlineExceeded) {
return err
}
} else {
if metadata.Destination.Addr.IsUnspecified() {
metadata.Destination = destination
}
if metadata.InboundOptions.SniffEnabled {
if len(bufferList) > 0 {
err = sniff.PeekPacket(
ctx,
&metadata,
buffer.Bytes(),
sniff.QUICClientHello,
)
} else {
err = sniff.PeekPacket(
ctx, &metadata,
buffer.Bytes(),
sniff.DomainNameQuery,
sniff.QUICClientHello,
sniff.STUNMessage,
sniff.UTP,
sniff.UDPTracker,
sniff.DTLSRecord)
}
if E.IsMulti(err, sniff.ErrClientHelloFragmented) && len(bufferList) == 0 {
bufferList = append(bufferList, buffer)
r.logger.DebugContext(ctx, "attempt to sniff fragmented QUIC client hello")
continue
}
if metadata.Protocol != "" {
if metadata.InboundOptions.SniffOverrideDestination && M.IsDomainName(metadata.Domain) {
metadata.Destination = M.Socksaddr{
Fqdn: metadata.Domain,
Port: metadata.Destination.Port,
}
}
if metadata.Domain != "" && metadata.Client != "" {
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol, ", domain: ", metadata.Domain, ", client: ", metadata.Client)
} else if metadata.Domain != "" {
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol, ", domain: ", metadata.Domain)
} else if metadata.Client != "" {
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol, ", client: ", metadata.Client)
} else {
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol)
}
}
}
conn = bufio.NewCachedPacketConn(conn, buffer, destination)
}
for _, cachedBuffer := range common.Reverse(bufferList) {
conn = bufio.NewCachedPacketConn(conn, cachedBuffer, destination)
}
break
}
}
if r.dnsReverseMapping != nil && metadata.Domain == "" {
domain, loaded := r.dnsReverseMapping.Query(metadata.Destination.Addr)
if loaded {
metadata.Domain = domain
r.logger.DebugContext(ctx, "found reserve mapped domain: ", metadata.Domain)
}
}
if metadata.Destination.IsFqdn() && dns.DomainStrategy(metadata.InboundOptions.DomainStrategy) != dns.DomainStrategyAsIS {
addresses, err := r.Lookup(adapter.WithContext(ctx, &metadata), metadata.Destination.Fqdn, dns.DomainStrategy(metadata.InboundOptions.DomainStrategy))
if err != nil {
return err
}
metadata.DestinationAddresses = addresses
r.dnsLogger.DebugContext(ctx, "resolved [", strings.Join(F.MapToString(metadata.DestinationAddresses), " "), "]")
}
if metadata.Destination.IsIPv4() {
metadata.IPVersion = 4
} else if metadata.Destination.IsIPv6() {
metadata.IPVersion = 6
}
ctx, matchedRule, detour, err := r.match(ctx, &metadata, r.defaultOutboundForPacketConnection)
if err != nil {
return err
}
if !common.Contains(detour.Network(), N.NetworkUDP) {
return E.New("missing supported outbound, closing packet connection")
}
if r.clashServer != nil {
trackerConn, tracker := r.clashServer.RoutedPacketConnection(ctx, conn, metadata, matchedRule)
defer tracker.Leave()
conn = trackerConn
}
if r.v2rayServer != nil {
if statsService := r.v2rayServer.StatsService(); statsService != nil {
conn = statsService.RoutedPacketConnection(metadata.Inbound, detour.Tag(), metadata.User, conn)
}
}
if metadata.FakeIP {
conn = bufio.NewNATPacketConn(bufio.NewNetPacketConn(conn), metadata.OriginDestination, metadata.Destination)
}
return detour.NewPacketConnection(ctx, conn, metadata)
}
func (r *Router) match(ctx context.Context, metadata *adapter.InboundContext, defaultOutbound adapter.Outbound) (context.Context, adapter.Rule, adapter.Outbound, error) {
matchRule, matchOutbound := r.match0(ctx, metadata, defaultOutbound)
if contextOutbound, loaded := outbound.TagFromContext(ctx); loaded {
if contextOutbound == matchOutbound.Tag() {
return nil, nil, nil, E.New("connection loopback in outbound/", matchOutbound.Type(), "[", matchOutbound.Tag(), "]")
}
}
ctx = outbound.ContextWithTag(ctx, matchOutbound.Tag())
return ctx, matchRule, matchOutbound, nil
}
func (r *Router) match0(ctx context.Context, metadata *adapter.InboundContext, defaultOutbound adapter.Outbound) (adapter.Rule, adapter.Outbound) {
if r.processSearcher != nil {
var originDestination netip.AddrPort
if metadata.OriginDestination.IsValid() {
originDestination = metadata.OriginDestination.AddrPort()
} else if metadata.Destination.IsIP() {
originDestination = metadata.Destination.AddrPort()
}
processInfo, err := process.FindProcessInfo(r.processSearcher, ctx, metadata.Network, metadata.Source.AddrPort(), originDestination)
if err != nil {
r.logger.InfoContext(ctx, "failed to search process: ", err)
} else {
if processInfo.ProcessPath != "" {
r.logger.InfoContext(ctx, "found process path: ", processInfo.ProcessPath)
} else if processInfo.PackageName != "" {
r.logger.InfoContext(ctx, "found package name: ", processInfo.PackageName)
} else if processInfo.UserId != -1 {
if /*needUserName &&*/ true {
osUser, _ := user.LookupId(F.ToString(processInfo.UserId))
if osUser != nil {
processInfo.User = osUser.Username
}
}
if processInfo.User != "" {
r.logger.InfoContext(ctx, "found user: ", processInfo.User)
} else {
r.logger.InfoContext(ctx, "found user id: ", processInfo.UserId)
}
}
metadata.ProcessInfo = processInfo
}
}
for i, rule := range r.rules {
metadata.ResetRuleCache()
if rule.Match(metadata) {
detour := rule.Outbound()
r.logger.DebugContext(ctx, "match[", i, "] ", rule.String(), " => ", detour)
if outbound, loaded := r.Outbound(detour); loaded {
return rule, outbound
}
r.logger.ErrorContext(ctx, "outbound not found: ", detour)
}
}
return nil, defaultOutbound
}
func (r *Router) InterfaceFinder() control.InterfaceFinder { func (r *Router) InterfaceFinder() control.InterfaceFinder {
return r.interfaceFinder return r.interfaceFinder
} }

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"io" "io"
@ -20,7 +20,7 @@ type abstractDefaultRule struct {
allItems []RuleItem allItems []RuleItem
ruleSetItem RuleItem ruleSetItem RuleItem
invert bool invert bool
outbound string action adapter.RuleAction
} }
func (r *abstractDefaultRule) Type() string { func (r *abstractDefaultRule) Type() string {
@ -150,8 +150,8 @@ func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool {
return !r.invert return !r.invert
} }
func (r *abstractDefaultRule) Outbound() string { func (r *abstractDefaultRule) Action() adapter.RuleAction {
return r.outbound return r.action
} }
func (r *abstractDefaultRule) String() string { func (r *abstractDefaultRule) String() string {
@ -163,10 +163,10 @@ func (r *abstractDefaultRule) String() string {
} }
type abstractLogicalRule struct { type abstractLogicalRule struct {
rules []adapter.HeadlessRule rules []adapter.HeadlessRule
mode string mode string
invert bool invert bool
outbound string action adapter.RuleAction
} }
func (r *abstractLogicalRule) Type() string { func (r *abstractLogicalRule) Type() string {
@ -231,8 +231,8 @@ func (r *abstractLogicalRule) Match(metadata *adapter.InboundContext) bool {
} }
} }
func (r *abstractLogicalRule) Outbound() string { func (r *abstractLogicalRule) Action() adapter.RuleAction {
return r.outbound return r.action
} }
func (r *abstractLogicalRule) String() string { func (r *abstractLogicalRule) String() string {

228
route/rule/rule_action.go Normal file
View file

@ -0,0 +1,228 @@
package rule
import (
"net/netip"
"strings"
"time"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/sniff"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-dns"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
)
func NewRuleAction(action option.RuleAction) (adapter.RuleAction, error) {
switch action.Action {
case C.RuleActionTypeRoute:
return &RuleActionRoute{
Outbound: action.RouteOptions.Outbound,
UDPDisableDomainUnmapping: action.RouteOptions.UDPDisableDomainUnmapping,
}, nil
case C.RuleActionTypeReturn:
return &RuleActionReject{}, nil
case C.RuleActionTypeReject:
return &RuleActionReject{
Method: string(action.RejectOptions.Method),
}, nil
case C.RuleActionTypeHijackDNS:
return &RuleActionHijackDNS{}, nil
case C.RuleActionTypeSniff:
sniffAction := &RuleActionSniff{
snifferNames: action.SniffOptions.Sniffer,
Timeout: time.Duration(action.SniffOptions.Timeout),
}
return sniffAction, sniffAction.build()
case C.RuleActionTypeResolve:
return &RuleActionResolve{
Strategy: dns.DomainStrategy(action.ResolveOptions.Strategy),
Server: action.ResolveOptions.Server,
}, nil
default:
panic(F.ToString("unknown rule action: ", action.Action))
}
}
func NewDNSRuleAction(action option.DNSRuleAction) adapter.RuleAction {
switch action.Action {
case C.RuleActionTypeRoute:
return &RuleActionDNSRoute{
Server: action.RouteOptions.Server,
DisableCache: action.RouteOptions.DisableCache,
RewriteTTL: action.RouteOptions.RewriteTTL,
ClientSubnet: action.RouteOptions.ClientSubnet.Build(),
}
case C.RuleActionTypeReturn:
return &RuleActionReturn{}
case C.RuleActionTypeReject:
return &RuleActionReject{
Method: string(action.RejectOptions.Method),
}
default:
panic(F.ToString("unknown rule action: ", action.Action))
}
}
type RuleActionRoute struct {
Outbound string
UDPDisableDomainUnmapping bool
}
func (r *RuleActionRoute) Type() string {
return C.RuleActionTypeRoute
}
func (r *RuleActionRoute) String() string {
return F.ToString("route(", r.Outbound, ")")
}
type RuleActionDNSRoute struct {
Server string
DisableCache bool
RewriteTTL *uint32
ClientSubnet netip.Prefix
}
func (r *RuleActionDNSRoute) Type() string {
return C.RuleActionTypeRoute
}
func (r *RuleActionDNSRoute) String() string {
return F.ToString("route(", r.Server, ")")
}
type RuleActionReturn struct{}
func (r *RuleActionReturn) Type() string {
return C.RuleActionTypeReturn
}
func (r *RuleActionReturn) String() string {
return "return"
}
type RuleActionReject struct {
Method string
}
func (r *RuleActionReject) Type() string {
return C.RuleActionTypeReject
}
func (r *RuleActionReject) String() string {
if r.Method == C.RuleActionRejectMethodDefault {
return "reject"
}
return F.ToString("reject(", r.Method, ")")
}
type RuleActionHijackDNS struct{}
func (r *RuleActionHijackDNS) Type() string {
return C.RuleActionTypeHijackDNS
}
func (r *RuleActionHijackDNS) String() string {
return "hijack-dns"
}
type RuleActionSniff struct {
snifferNames []string
StreamSniffers []sniff.StreamSniffer
PacketSniffers []sniff.PacketSniffer
Timeout time.Duration
// Deprecated
OverrideDestination bool
}
func (r *RuleActionSniff) Type() string {
return C.RuleActionTypeSniff
}
func (r *RuleActionSniff) build() error {
if len(r.StreamSniffers) > 0 || len(r.PacketSniffers) > 0 {
return nil
}
if len(r.snifferNames) > 0 {
for _, name := range r.snifferNames {
switch name {
case C.ProtocolTLS:
r.StreamSniffers = append(r.StreamSniffers, sniff.TLSClientHello)
case C.ProtocolHTTP:
r.StreamSniffers = append(r.StreamSniffers, sniff.HTTPHost)
case C.ProtocolQUIC:
r.PacketSniffers = append(r.PacketSniffers, sniff.QUICClientHello)
case C.ProtocolDNS:
r.StreamSniffers = append(r.StreamSniffers, sniff.StreamDomainNameQuery)
r.PacketSniffers = append(r.PacketSniffers, sniff.DomainNameQuery)
case C.ProtocolSTUN:
r.PacketSniffers = append(r.PacketSniffers, sniff.STUNMessage)
case C.ProtocolBitTorrent:
r.StreamSniffers = append(r.StreamSniffers, sniff.BitTorrent)
r.PacketSniffers = append(r.PacketSniffers, sniff.UTP)
r.PacketSniffers = append(r.PacketSniffers, sniff.UDPTracker)
case C.ProtocolDTLS:
r.PacketSniffers = append(r.PacketSniffers, sniff.DTLSRecord)
case C.ProtocolSSH:
r.StreamSniffers = append(r.StreamSniffers, sniff.SSH)
case C.ProtocolRDP:
r.StreamSniffers = append(r.StreamSniffers, sniff.RDP)
default:
return E.New("unknown sniffer: ", name)
}
}
} else {
r.StreamSniffers = []sniff.StreamSniffer{
sniff.TLSClientHello,
sniff.HTTPHost,
sniff.StreamDomainNameQuery,
sniff.BitTorrent,
sniff.SSH,
sniff.RDP,
}
r.PacketSniffers = []sniff.PacketSniffer{
sniff.DomainNameQuery,
sniff.QUICClientHello,
sniff.STUNMessage,
sniff.UTP,
sniff.UDPTracker,
sniff.DTLSRecord,
}
}
return nil
}
func (r *RuleActionSniff) String() string {
if len(r.snifferNames) == 0 && r.Timeout == 0 {
return "sniff"
} else if len(r.snifferNames) > 0 && r.Timeout == 0 {
return F.ToString("sniff(", strings.Join(r.snifferNames, ","), ")")
} else if len(r.snifferNames) == 0 && r.Timeout > 0 {
return F.ToString("sniff(", r.Timeout.String(), ")")
} else {
return F.ToString("sniff(", strings.Join(r.snifferNames, ","), ",", r.Timeout.String(), ")")
}
}
type RuleActionResolve struct {
Strategy dns.DomainStrategy
Server string
}
func (r *RuleActionResolve) Type() string {
return C.RuleActionTypeResolve
}
func (r *RuleActionResolve) String() string {
if r.Strategy == dns.DomainStrategyAsIS && r.Server == "" {
return F.ToString("resolve")
} else if r.Strategy != dns.DomainStrategyAsIS && r.Server == "" {
return F.ToString("resolve(", option.DomainStrategy(r.Strategy).String(), ")")
} else if r.Strategy == dns.DomainStrategyAsIS && r.Server != "" {
return F.ToString("resolve(", r.Server, ")")
} else {
return F.ToString("resolve(", option.DomainStrategy(r.Strategy).String(), ",", r.Server, ")")
}
}

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"context" "context"
@ -17,16 +17,22 @@ func NewRule(ctx context.Context, router adapter.Router, logger log.ContextLogge
if !options.DefaultOptions.IsValid() { if !options.DefaultOptions.IsValid() {
return nil, E.New("missing conditions") return nil, E.New("missing conditions")
} }
if options.DefaultOptions.Outbound == "" && checkOutbound { switch options.DefaultOptions.Action {
return nil, E.New("missing outbound field") case "", C.RuleActionTypeRoute:
if options.DefaultOptions.RouteOptions.Outbound == "" && checkOutbound {
return nil, E.New("missing outbound field")
}
} }
return NewDefaultRule(ctx, router, logger, options.DefaultOptions) return NewDefaultRule(ctx, router, logger, options.DefaultOptions)
case C.RuleTypeLogical: case C.RuleTypeLogical:
if !options.LogicalOptions.IsValid() { if !options.LogicalOptions.IsValid() {
return nil, E.New("missing conditions") return nil, E.New("missing conditions")
} }
if options.LogicalOptions.Outbound == "" && checkOutbound { switch options.LogicalOptions.Action {
return nil, E.New("missing outbound field") case "", C.RuleActionTypeRoute:
if options.LogicalOptions.RouteOptions.Outbound == "" && checkOutbound {
return nil, E.New("missing outbound field")
}
} }
return NewLogicalRule(ctx, router, logger, options.LogicalOptions) return NewLogicalRule(ctx, router, logger, options.LogicalOptions)
default: default:
@ -46,10 +52,14 @@ type RuleItem interface {
} }
func NewDefaultRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.DefaultRule) (*DefaultRule, error) { func NewDefaultRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.DefaultRule) (*DefaultRule, error) {
action, err := NewRuleAction(options.RuleAction)
if err != nil {
return nil, E.Cause(err, "action")
}
rule := &DefaultRule{ rule := &DefaultRule{
abstractDefaultRule{ abstractDefaultRule{
invert: options.Invert, invert: options.Invert,
outbound: options.Outbound, action: action,
}, },
} }
if len(options.Inbound) > 0 { if len(options.Inbound) > 0 {
@ -244,27 +254,31 @@ type LogicalRule struct {
} }
func NewLogicalRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.LogicalRule) (*LogicalRule, error) { func NewLogicalRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.LogicalRule) (*LogicalRule, error) {
r := &LogicalRule{ action, err := NewRuleAction(options.RuleAction)
if err != nil {
return nil, E.Cause(err, "action")
}
rule := &LogicalRule{
abstractLogicalRule{ abstractLogicalRule{
rules: make([]adapter.HeadlessRule, len(options.Rules)), rules: make([]adapter.HeadlessRule, len(options.Rules)),
invert: options.Invert, invert: options.Invert,
outbound: options.Outbound, action: action,
}, },
} }
switch options.Mode { switch options.Mode {
case C.LogicalTypeAnd: case C.LogicalTypeAnd:
r.mode = C.LogicalTypeAnd rule.mode = C.LogicalTypeAnd
case C.LogicalTypeOr: case C.LogicalTypeOr:
r.mode = C.LogicalTypeOr rule.mode = C.LogicalTypeOr
default: default:
return nil, E.New("unknown logical mode: ", options.Mode) return nil, E.New("unknown logical mode: ", options.Mode)
} }
for i, subRule := range options.Rules { for i, subOptions := range options.Rules {
rule, err := NewRule(ctx, router, logger, subRule, false) subRule, err := NewRule(ctx, router, logger, subOptions, false)
if err != nil { if err != nil {
return nil, E.Cause(err, "sub rule[", i, "]") return nil, E.Cause(err, "sub rule[", i, "]")
} }
r.rules[i] = rule rule.rules[i] = subRule
} }
return r, nil return rule, nil
} }

View file

@ -1,8 +1,7 @@
package route package rule
import ( import (
"context" "context"
"net/netip"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
@ -19,16 +18,22 @@ func NewDNSRule(ctx context.Context, router adapter.Router, logger log.ContextLo
if !options.DefaultOptions.IsValid() { if !options.DefaultOptions.IsValid() {
return nil, E.New("missing conditions") return nil, E.New("missing conditions")
} }
if options.DefaultOptions.Server == "" && checkServer { switch options.DefaultOptions.Action {
return nil, E.New("missing server field") case "", C.RuleActionTypeRoute:
if options.DefaultOptions.RouteOptions.Server == "" && checkServer {
return nil, E.New("missing server field")
}
} }
return NewDefaultDNSRule(ctx, router, logger, options.DefaultOptions) return NewDefaultDNSRule(ctx, router, logger, options.DefaultOptions)
case C.RuleTypeLogical: case C.RuleTypeLogical:
if !options.LogicalOptions.IsValid() { if !options.LogicalOptions.IsValid() {
return nil, E.New("missing conditions") return nil, E.New("missing conditions")
} }
if options.LogicalOptions.Server == "" && checkServer { switch options.LogicalOptions.Action {
return nil, E.New("missing server field") case "", C.RuleActionTypeRoute:
if options.LogicalOptions.RouteOptions.Server == "" && checkServer {
return nil, E.New("missing server field")
}
} }
return NewLogicalDNSRule(ctx, router, logger, options.LogicalOptions) return NewLogicalDNSRule(ctx, router, logger, options.LogicalOptions)
default: default:
@ -40,20 +45,14 @@ var _ adapter.DNSRule = (*DefaultDNSRule)(nil)
type DefaultDNSRule struct { type DefaultDNSRule struct {
abstractDefaultRule abstractDefaultRule
disableCache bool
rewriteTTL *uint32
clientSubnet *netip.Prefix
} }
func NewDefaultDNSRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.DefaultDNSRule) (*DefaultDNSRule, error) { func NewDefaultDNSRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.DefaultDNSRule) (*DefaultDNSRule, error) {
rule := &DefaultDNSRule{ rule := &DefaultDNSRule{
abstractDefaultRule: abstractDefaultRule{ abstractDefaultRule: abstractDefaultRule{
invert: options.Invert, invert: options.Invert,
outbound: options.Server, action: NewDNSRuleAction(options.DNSRuleAction),
}, },
disableCache: options.DisableCache,
rewriteTTL: options.RewriteTTL,
clientSubnet: (*netip.Prefix)(options.ClientSubnet),
} }
if len(options.Inbound) > 0 { if len(options.Inbound) > 0 {
item := NewInboundRule(options.Inbound) item := NewInboundRule(options.Inbound)
@ -245,16 +244,8 @@ func NewDefaultDNSRule(ctx context.Context, router adapter.Router, logger log.Co
return rule, nil return rule, nil
} }
func (r *DefaultDNSRule) DisableCache() bool { func (r *DefaultDNSRule) Action() adapter.RuleAction {
return r.disableCache return r.action
}
func (r *DefaultDNSRule) RewriteTTL() *uint32 {
return r.rewriteTTL
}
func (r *DefaultDNSRule) ClientSubnet() *netip.Prefix {
return r.clientSubnet
} }
func (r *DefaultDNSRule) WithAddressLimit() bool { func (r *DefaultDNSRule) WithAddressLimit() bool {
@ -289,21 +280,15 @@ var _ adapter.DNSRule = (*LogicalDNSRule)(nil)
type LogicalDNSRule struct { type LogicalDNSRule struct {
abstractLogicalRule abstractLogicalRule
disableCache bool
rewriteTTL *uint32
clientSubnet *netip.Prefix
} }
func NewLogicalDNSRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.LogicalDNSRule) (*LogicalDNSRule, error) { func NewLogicalDNSRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.LogicalDNSRule) (*LogicalDNSRule, error) {
r := &LogicalDNSRule{ r := &LogicalDNSRule{
abstractLogicalRule: abstractLogicalRule{ abstractLogicalRule: abstractLogicalRule{
rules: make([]adapter.HeadlessRule, len(options.Rules)), rules: make([]adapter.HeadlessRule, len(options.Rules)),
invert: options.Invert, invert: options.Invert,
outbound: options.Server, action: NewDNSRuleAction(options.DNSRuleAction),
}, },
disableCache: options.DisableCache,
rewriteTTL: options.RewriteTTL,
clientSubnet: (*netip.Prefix)(options.ClientSubnet),
} }
switch options.Mode { switch options.Mode {
case C.LogicalTypeAnd: case C.LogicalTypeAnd:
@ -323,16 +308,8 @@ func NewLogicalDNSRule(ctx context.Context, router adapter.Router, logger log.Co
return r, nil return r, nil
} }
func (r *LogicalDNSRule) DisableCache() bool { func (r *LogicalDNSRule) Action() adapter.RuleAction {
return r.disableCache return r.action
}
func (r *LogicalDNSRule) RewriteTTL() *uint32 {
return r.rewriteTTL
}
func (r *LogicalDNSRule) ClientSubnet() *netip.Prefix {
return r.clientSubnet
} }
func (r *LogicalDNSRule) WithAddressLimit() bool { func (r *LogicalDNSRule) WithAddressLimit() bool {

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"strings" "strings"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"strings" "strings"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"net/netip" "net/netip"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"strings" "strings"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"strings" "strings"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"strings" "strings"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"strings" "strings"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"regexp" "regexp"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"net/netip" "net/netip"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"strings" "strings"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"strings" "strings"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"net/netip" "net/netip"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"strings" "strings"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"strings" "strings"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"strings" "strings"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"strings" "strings"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"strconv" "strconv"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"path/filepath" "path/filepath"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"strings" "strings"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"regexp" "regexp"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"strings" "strings"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"strings" "strings"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"strings" "strings"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"strings" "strings"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"strings" "strings"

View file

@ -1,4 +1,4 @@
package route package rule
import ( import (
"strings" "strings"

Some files were not shown because too many files have changed in this diff Show more