mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-29 20:11:28 +00:00
Crazy sekai overturns the small pond
This commit is contained in:
parent
f1c76c4dde
commit
71bc3d4b53
|
@ -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)
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
38
adapter/rule.go
Normal 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
216
adapter/upstream_legacy.go
Normal 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)
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -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"
|
||||||
|
@ -84,7 +84,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, "]")
|
||||||
}
|
}
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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:
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -22,3 +22,18 @@ const (
|
||||||
RuleSetVersion1 = 1 + iota
|
RuleSetVersion1 = 1 + iota
|
||||||
RuleSetVersion2
|
RuleSetVersion2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
RuleActionTypeRoute = "route"
|
||||||
|
RuleActionTypeReturn = "return"
|
||||||
|
RuleActionTypeReject = "reject"
|
||||||
|
RuleActionTypeHijackDNS = "hijack-dns"
|
||||||
|
RuleActionTypeSniff = "sniff"
|
||||||
|
RuleActionTypeResolve = "resolve"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
RuleActionRejectMethodDefault = "default"
|
||||||
|
RuleActionRejectMethodPortUnreachable = "port-unreachable"
|
||||||
|
RuleActionRejectMethodDrop = "drop"
|
||||||
|
)
|
||||||
|
|
|
@ -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,
|
||||||
})
|
})
|
||||||
|
|
|
@ -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)
|
||||||
|
|
16
go.mod
16
go.mod
|
@ -24,17 +24,17 @@ require (
|
||||||
github.com/sagernet/cors v1.2.1
|
github.com/sagernet/cors v1.2.1
|
||||||
github.com/sagernet/fswatch v0.1.1
|
github.com/sagernet/fswatch v0.1.1
|
||||||
github.com/sagernet/gomobile v0.1.4
|
github.com/sagernet/gomobile v0.1.4
|
||||||
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f
|
github.com/sagernet/gvisor v0.0.0-20241021032506-a4324256e4a3
|
||||||
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.0
|
github.com/sagernet/sing v0.6.0-alpha.1
|
||||||
github.com/sagernet/sing-dns v0.3.0
|
github.com/sagernet/sing-dns v0.3.1-0.20241105104342-1914f319ddab
|
||||||
github.com/sagernet/sing-mux v0.2.0
|
github.com/sagernet/sing-mux v0.2.1-0.20241020175909-fe6153f7a9ec
|
||||||
github.com/sagernet/sing-quic v0.3.0-rc.2
|
github.com/sagernet/sing-quic v0.3.0-rc.2
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7
|
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
||||||
github.com/sagernet/sing-shadowtls v0.1.4
|
github.com/sagernet/sing-shadowtls v0.1.4
|
||||||
github.com/sagernet/sing-tun v0.4.0-rc.5
|
github.com/sagernet/sing-tun v0.5.0-alpha.1
|
||||||
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
|
||||||
|
@ -66,10 +66,10 @@ require (
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||||
github.com/gobwas/httphead v0.1.0 // indirect
|
github.com/gobwas/httphead v0.1.0 // indirect
|
||||||
github.com/gobwas/pool v0.2.1 // indirect
|
github.com/gobwas/pool v0.2.1 // indirect
|
||||||
github.com/google/btree v1.1.2 // indirect
|
github.com/google/btree v1.1.3 // indirect
|
||||||
github.com/google/go-cmp v0.6.0 // indirect
|
github.com/google/go-cmp v0.6.0 // indirect
|
||||||
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect
|
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect
|
||||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
github.com/hashicorp/yamux v0.1.2 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/josharian/native v1.1.0 // indirect
|
github.com/josharian/native v1.1.0 // indirect
|
||||||
github.com/klauspost/compress v1.17.4 // indirect
|
github.com/klauspost/compress v1.17.4 // indirect
|
||||||
|
@ -92,7 +92,7 @@ require (
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
golang.org/x/sync v0.8.0 // indirect
|
golang.org/x/sync v0.8.0 // indirect
|
||||||
golang.org/x/text v0.19.0 // indirect
|
golang.org/x/text v0.19.0 // indirect
|
||||||
golang.org/x/time v0.5.0 // indirect
|
golang.org/x/time v0.7.0 // indirect
|
||||||
golang.org/x/tools v0.24.0 // indirect
|
golang.org/x/tools v0.24.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||||
|
|
32
go.sum
32
go.sum
|
@ -33,14 +33,14 @@ github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm
|
||||||
github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk=
|
github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk=
|
||||||
github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk=
|
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk=
|
||||||
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||||
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
|
||||||
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 h1:9K06NfxkBh25x56yVhWWlKFE8YpicaSfHwoV8SFbueA=
|
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 h1:9K06NfxkBh25x56yVhWWlKFE8YpicaSfHwoV8SFbueA=
|
||||||
|
@ -104,8 +104,8 @@ github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQ
|
||||||
github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o=
|
github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o=
|
||||||
github.com/sagernet/gomobile v0.1.4 h1:WzX9ka+iHdupMgy2Vdich+OAt7TM8C2cZbIbzNjBrJY=
|
github.com/sagernet/gomobile v0.1.4 h1:WzX9ka+iHdupMgy2Vdich+OAt7TM8C2cZbIbzNjBrJY=
|
||||||
github.com/sagernet/gomobile v0.1.4/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E=
|
github.com/sagernet/gomobile v0.1.4/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E=
|
||||||
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f h1:NkhuupzH5ch7b/Y/6ZHJWrnNLoiNnSJaow6DPb8VW2I=
|
github.com/sagernet/gvisor v0.0.0-20241021032506-a4324256e4a3 h1:RxEz7LhPNiF/gX/Hg+OXr5lqsM9iVAgmaK1L1vzlDRM=
|
||||||
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f/go.mod h1:KXmw+ouSJNOsuRpg4wgwwCQuunrGz4yoAqQjsLjc6N0=
|
github.com/sagernet/gvisor v0.0.0-20241021032506-a4324256e4a3/go.mod h1:ehZwnT2UpmOWAHFL48XdBhnd4Qu4hN2O3Ji0us3ZHMw=
|
||||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
|
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
|
||||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||||
github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
|
github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
|
||||||
|
@ -115,12 +115,12 @@ 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.0 h1:soo2wVwLcieKWWKIksFNK6CCAojUgAppqQVwyRYGkEM=
|
github.com/sagernet/sing v0.6.0-alpha.1 h1:dFbrjRWOTTrkezGWcJmpC9AwDU09nigSVhEibHQxKV4=
|
||||||
github.com/sagernet/sing v0.5.0/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing v0.6.0-alpha.1/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||||
github.com/sagernet/sing-dns v0.3.0 h1:uHCIlbCwBxALJwXcEK1d75d7t3vzCSVEQsPfZR1cxQE=
|
github.com/sagernet/sing-dns v0.3.1-0.20241105104342-1914f319ddab h1:djP4EY/KM5T62xscormLflVi7eDlHv6p7md1FHMSArE=
|
||||||
github.com/sagernet/sing-dns v0.3.0/go.mod h1:TqLIelI+FAbVEdiTRolhGLOwvhVjY7oT+wezlOJUQ7M=
|
github.com/sagernet/sing-dns v0.3.1-0.20241105104342-1914f319ddab/go.mod h1:TqLIelI+FAbVEdiTRolhGLOwvhVjY7oT+wezlOJUQ7M=
|
||||||
github.com/sagernet/sing-mux v0.2.0 h1:4C+vd8HztJCWNYfufvgL49xaOoOHXty2+EAjnzN3IYo=
|
github.com/sagernet/sing-mux v0.2.1-0.20241020175909-fe6153f7a9ec h1:6Fd/VsEsw9qIjaGi1IBTZSb4b4v5JYtNcoiBtGsQC48=
|
||||||
github.com/sagernet/sing-mux v0.2.0/go.mod h1:khzr9AOPocLa+g53dBplwNDz4gdsyx/YM3swtAhlkHQ=
|
github.com/sagernet/sing-mux v0.2.1-0.20241020175909-fe6153f7a9ec/go.mod h1:RSwqqHwbtTOX3vs6ms8vMtBGH/0ZNyLm/uwt6TlmR84=
|
||||||
github.com/sagernet/sing-quic v0.3.0-rc.2 h1:7vcC4bdS1GBJzHZhfmJiH0CfzQ4mYLUW51Z2RNHcGwc=
|
github.com/sagernet/sing-quic v0.3.0-rc.2 h1:7vcC4bdS1GBJzHZhfmJiH0CfzQ4mYLUW51Z2RNHcGwc=
|
||||||
github.com/sagernet/sing-quic v0.3.0-rc.2/go.mod h1:3UOq51WVqzra7eCgod7t4hqnTaOiZzFUci9avMrtOqs=
|
github.com/sagernet/sing-quic v0.3.0-rc.2/go.mod h1:3UOq51WVqzra7eCgod7t4hqnTaOiZzFUci9avMrtOqs=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
||||||
|
@ -129,8 +129,8 @@ github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wK
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
||||||
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
|
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
|
||||||
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
|
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
|
||||||
github.com/sagernet/sing-tun v0.4.0-rc.5 h1:d4o36GbVZtsucpt5On7CjTFSx4zx82DL8OlGR7rX7vY=
|
github.com/sagernet/sing-tun v0.5.0-alpha.1 h1:Na5uwSXizchIXDqeo1hwcs1O9+ekx5MjT74NwVh0mXc=
|
||||||
github.com/sagernet/sing-tun v0.4.0-rc.5/go.mod h1:rWu1ZC2lj4+UOOSNiUWjsY0y2fJcOHnLTMGna2n5P2o=
|
github.com/sagernet/sing-tun v0.5.0-alpha.1/go.mod h1:I1exfctioZX1nDm+RtU72bvy17bExvIjsVZVst8Ck7M=
|
||||||
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=
|
||||||
|
@ -198,8 +198,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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"`
|
||||||
|
|
|
@ -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
166
option/rule_action.go
Normal 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"`
|
||||||
|
}
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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{})
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,14 +142,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 {
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
583
route/route.go
Normal 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
|
||||||
|
}
|
|
@ -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) {
|
394
route/router.go
394
route/router.go
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
228
route/rule/rule_action.go
Normal 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, ")")
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
}
|
}
|
|
@ -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 {
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/netip"
|
"net/netip"
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/netip"
|
"net/netip"
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/netip"
|
"net/netip"
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"path/filepath"
|
"path/filepath"
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
|
@ -1,4 +1,4 @@
|
||||||
package route
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
|
@ -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
Loading…
Reference in a new issue