mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-16 02:12:22 +08:00
Crazy sekai overturns the small pond
This commit is contained in:
parent
84a102a6ef
commit
7f65ab8166
|
@ -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"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
// Deprecated
|
||||
type ConnectionHandler interface {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
N.TCPConnectionHandler
|
||||
N.UDPConnectionHandler
|
||||
E.Handler
|
||||
}
|
||||
|
||||
type UpstreamHandlerAdapterEx interface {
|
||||
N.TCPConnectionHandlerEx
|
||||
N.UDPConnectionHandlerEx
|
||||
}
|
||||
|
|
|
@ -2,13 +2,11 @@ package adapter
|
|||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/sagernet/sing-box/common/process"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
type Inbound interface {
|
||||
|
@ -17,11 +15,14 @@ type Inbound interface {
|
|||
Tag() string
|
||||
}
|
||||
|
||||
type InjectableInbound interface {
|
||||
type TCPInjectableInbound interface {
|
||||
Inbound
|
||||
Network() []string
|
||||
NewConnection(ctx context.Context, conn net.Conn, metadata InboundContext) error
|
||||
NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error
|
||||
ConnectionHandlerEx
|
||||
}
|
||||
|
||||
type UDPInjectableInbound interface {
|
||||
Inbound
|
||||
PacketConnectionHandlerEx
|
||||
}
|
||||
|
||||
type InboundContext struct {
|
||||
|
@ -46,7 +47,9 @@ type InboundContext struct {
|
|||
InboundDetour string
|
||||
LastInbound string
|
||||
OriginDestination M.Socksaddr
|
||||
// Deprecated
|
||||
InboundOptions option.InboundOptions
|
||||
UDPDisableDomainUnmapping bool
|
||||
DestinationAddresses []netip.Addr
|
||||
SourceGeoIPCode string
|
||||
GeoIPCode string
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
package adapter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
|
@ -15,6 +12,4 @@ type Outbound interface {
|
|||
Network() []string
|
||||
Dependencies() []string
|
||||
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
|
||||
|
||||
ConnectionRouter
|
||||
ConnectionRouterEx
|
||||
|
||||
GeoIPReader() *geoip.Reader
|
||||
LoadGeosite(code string) (Rule, error)
|
||||
|
@ -70,6 +71,18 @@ type Router interface {
|
|||
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 {
|
||||
return service.ContextWith(ctx, router)
|
||||
}
|
||||
|
@ -78,28 +91,6 @@ func RouterFromContext(ctx context.Context) Router {
|
|||
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 {
|
||||
Name() string
|
||||
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"
|
||||
"net"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
type (
|
||||
ConnectionHandlerFunc = func(ctx context.Context, conn net.Conn, metadata InboundContext) error
|
||||
PacketConnectionHandlerFunc = func(ctx context.Context, conn N.PacketConn, metadata InboundContext) error
|
||||
ConnectionHandlerFuncEx = func(ctx context.Context, conn net.Conn, metadata InboundContext, onClose N.CloseHandlerFunc)
|
||||
PacketConnectionHandlerFuncEx = func(ctx context.Context, conn N.PacketConn, metadata InboundContext, onClose N.CloseHandlerFunc)
|
||||
)
|
||||
|
||||
func NewUpstreamHandler(
|
||||
func NewUpstreamHandlerEx(
|
||||
metadata InboundContext,
|
||||
connectionHandler ConnectionHandlerFunc,
|
||||
packetHandler PacketConnectionHandlerFunc,
|
||||
errorHandler E.Handler,
|
||||
) UpstreamHandlerAdapter {
|
||||
return &myUpstreamHandlerWrapper{
|
||||
connectionHandler ConnectionHandlerFuncEx,
|
||||
packetHandler PacketConnectionHandlerFuncEx,
|
||||
) UpstreamHandlerAdapterEx {
|
||||
return &myUpstreamHandlerWrapperEx{
|
||||
metadata: metadata,
|
||||
connectionHandler: connectionHandler,
|
||||
packetHandler: packetHandler,
|
||||
errorHandler: errorHandler,
|
||||
}
|
||||
}
|
||||
|
||||
var _ UpstreamHandlerAdapter = (*myUpstreamHandlerWrapper)(nil)
|
||||
var _ UpstreamHandlerAdapterEx = (*myUpstreamHandlerWrapperEx)(nil)
|
||||
|
||||
type myUpstreamHandlerWrapper struct {
|
||||
type myUpstreamHandlerWrapperEx struct {
|
||||
metadata InboundContext
|
||||
connectionHandler ConnectionHandlerFunc
|
||||
packetHandler PacketConnectionHandlerFunc
|
||||
errorHandler E.Handler
|
||||
connectionHandler ConnectionHandlerFuncEx
|
||||
packetHandler PacketConnectionHandlerFuncEx
|
||||
}
|
||||
|
||||
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
|
||||
if metadata.Source.IsValid() {
|
||||
myMetadata.Source = metadata.Source
|
||||
if source.IsValid() {
|
||||
myMetadata.Source = source
|
||||
}
|
||||
if metadata.Destination.IsValid() {
|
||||
myMetadata.Destination = metadata.Destination
|
||||
if destination.IsValid() {
|
||||
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
|
||||
if metadata.Source.IsValid() {
|
||||
myMetadata.Source = metadata.Source
|
||||
if source.IsValid() {
|
||||
myMetadata.Source = source
|
||||
}
|
||||
if metadata.Destination.IsValid() {
|
||||
myMetadata.Destination = metadata.Destination
|
||||
if destination.IsValid() {
|
||||
myMetadata.Destination = destination
|
||||
}
|
||||
return w.packetHandler(ctx, conn, myMetadata)
|
||||
w.packetHandler(ctx, conn, myMetadata, onClose)
|
||||
}
|
||||
|
||||
func (w *myUpstreamHandlerWrapper) NewError(ctx context.Context, err error) {
|
||||
w.errorHandler.NewError(ctx, err)
|
||||
var _ UpstreamHandlerAdapterEx = (*myUpstreamContextHandlerWrapperEx)(nil)
|
||||
|
||||
type myUpstreamContextHandlerWrapperEx struct {
|
||||
connectionHandler ConnectionHandlerFuncEx
|
||||
packetHandler PacketConnectionHandlerFuncEx
|
||||
}
|
||||
|
||||
func UpstreamMetadata(metadata InboundContext) M.Metadata {
|
||||
return M.Metadata{
|
||||
Source: metadata.Source,
|
||||
Destination: metadata.Destination,
|
||||
}
|
||||
}
|
||||
|
||||
type myUpstreamContextHandlerWrapper struct {
|
||||
connectionHandler ConnectionHandlerFunc
|
||||
packetHandler PacketConnectionHandlerFunc
|
||||
errorHandler E.Handler
|
||||
}
|
||||
|
||||
func NewUpstreamContextHandler(
|
||||
connectionHandler ConnectionHandlerFunc,
|
||||
packetHandler PacketConnectionHandlerFunc,
|
||||
errorHandler E.Handler,
|
||||
) UpstreamHandlerAdapter {
|
||||
return &myUpstreamContextHandlerWrapper{
|
||||
func NewUpstreamContextHandlerEx(
|
||||
connectionHandler ConnectionHandlerFuncEx,
|
||||
packetHandler PacketConnectionHandlerFuncEx,
|
||||
) UpstreamHandlerAdapterEx {
|
||||
return &myUpstreamContextHandlerWrapperEx{
|
||||
connectionHandler: connectionHandler,
|
||||
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)
|
||||
if metadata.Source.IsValid() {
|
||||
myMetadata.Source = metadata.Source
|
||||
if source.IsValid() {
|
||||
myMetadata.Source = source
|
||||
}
|
||||
if metadata.Destination.IsValid() {
|
||||
myMetadata.Destination = metadata.Destination
|
||||
if destination.IsValid() {
|
||||
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)
|
||||
if metadata.Source.IsValid() {
|
||||
myMetadata.Source = metadata.Source
|
||||
if source.IsValid() {
|
||||
myMetadata.Source = source
|
||||
}
|
||||
if metadata.Destination.IsValid() {
|
||||
myMetadata.Destination = metadata.Destination
|
||||
if destination.IsValid() {
|
||||
myMetadata.Destination = destination
|
||||
}
|
||||
return w.packetHandler(ctx, conn, *myMetadata)
|
||||
w.packetHandler(ctx, conn, *myMetadata, onClose)
|
||||
}
|
||||
|
||||
func (w *myUpstreamContextHandlerWrapper) NewError(ctx context.Context, err error) {
|
||||
w.errorHandler.NewError(ctx, err)
|
||||
func NewRouteHandlerEx(
|
||||
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"
|
||||
"net"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
|
@ -16,8 +15,7 @@ type V2RayServerTransport interface {
|
|||
}
|
||||
|
||||
type V2RayServerTransportHandler interface {
|
||||
N.TCPConnectionHandler
|
||||
E.Handler
|
||||
N.TCPConnectionHandlerEx
|
||||
}
|
||||
|
||||
type V2RayClientTransport interface {
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"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"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
|
@ -84,7 +84,7 @@ func ruleSetMatch(sourcePath string, domain string) error {
|
|||
}
|
||||
for i, ruleOptions := range plainRuleSet.Rules {
|
||||
var currentRule adapter.HeadlessRule
|
||||
currentRule, err = route.NewHeadlessRule(nil, ruleOptions)
|
||||
currentRule, err = rule.NewHeadlessRule(nil, ruleOptions)
|
||||
if err != nil {
|
||||
return E.Cause(err, "parse rule_set.rules.[", i, "]")
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/route"
|
||||
"github.com/sagernet/sing-box/route/rule"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -26,7 +26,7 @@ example.arpa
|
|||
`))
|
||||
require.NoError(t, err)
|
||||
require.Len(t, rules, 1)
|
||||
rule, err := route.NewHeadlessRule(nil, rules[0])
|
||||
rule, err := rule.NewHeadlessRule(nil, rules[0])
|
||||
require.NoError(t, err)
|
||||
matchDomain := []string{
|
||||
"example.org",
|
||||
|
@ -85,7 +85,7 @@ func TestHosts(t *testing.T) {
|
|||
`))
|
||||
require.NoError(t, err)
|
||||
require.Len(t, rules, 1)
|
||||
rule, err := route.NewHeadlessRule(nil, rules[0])
|
||||
rule, err := rule.NewHeadlessRule(nil, rules[0])
|
||||
require.NoError(t, err)
|
||||
matchDomain := []string{
|
||||
"google.com",
|
||||
|
@ -115,7 +115,7 @@ www.example.org
|
|||
`))
|
||||
require.NoError(t, err)
|
||||
require.Len(t, rules, 1)
|
||||
rule, err := route.NewHeadlessRule(nil, rules[0])
|
||||
rule, err := rule.NewHeadlessRule(nil, rules[0])
|
||||
require.NoError(t, err)
|
||||
matchDomain := []string{
|
||||
"example.com",
|
||||
|
|
|
@ -15,11 +15,11 @@ import (
|
|||
)
|
||||
|
||||
type Router struct {
|
||||
router adapter.ConnectionRouter
|
||||
router adapter.ConnectionRouterEx
|
||||
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 {
|
||||
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 {
|
||||
if metadata.Destination == mux.Destination {
|
||||
// TODO: check if WithContext is necessary
|
||||
return r.service.NewConnection(adapter.WithContext(ctx, &metadata), conn, adapter.UpstreamMetadata(metadata))
|
||||
} else {
|
||||
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 {
|
||||
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
|
||||
)
|
||||
|
||||
func Skip(metadata adapter.InboundContext) bool {
|
||||
func Skip(metadata *adapter.InboundContext) bool {
|
||||
// skip server first protocols
|
||||
switch metadata.Destination.Port {
|
||||
case 25, 465, 587:
|
||||
|
|
|
@ -13,14 +13,14 @@ import (
|
|||
"github.com/sagernet/sing/common/uot"
|
||||
)
|
||||
|
||||
var _ adapter.ConnectionRouter = (*Router)(nil)
|
||||
var _ adapter.ConnectionRouterEx = (*Router)(nil)
|
||||
|
||||
type Router struct {
|
||||
router adapter.ConnectionRouter
|
||||
router adapter.ConnectionRouterEx
|
||||
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}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
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
|
||||
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{
|
||||
Type: rule.Type(),
|
||||
Payload: rule.String(),
|
||||
Proxy: rule.Outbound(),
|
||||
Proxy: rule.Action().String(),
|
||||
})
|
||||
}
|
||||
|
||||
render.JSON(w, r, render.M{
|
||||
"rules": rules,
|
||||
})
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
R "github.com/sagernet/sing-box/route/rule"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/atomic"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
|
@ -60,7 +61,7 @@ func (t TrackerMetadata) MarshalJSON() ([]byte, error) {
|
|||
}
|
||||
var rule string
|
||||
if t.Rule != nil {
|
||||
rule = F.ToString(t.Rule, " => ", t.Rule.Outbound())
|
||||
rule = F.ToString(t.Rule, " => ", t.Rule.Action())
|
||||
} else {
|
||||
rule = "final"
|
||||
}
|
||||
|
@ -131,19 +132,21 @@ func NewTCPTracker(conn net.Conn, manager *Manager, metadata adapter.InboundCont
|
|||
outbound string
|
||||
outboundType string
|
||||
)
|
||||
if rule == nil {
|
||||
if defaultOutbound, err := router.DefaultOutbound(N.NetworkTCP); err == nil {
|
||||
var action adapter.RuleAction
|
||||
if rule != nil {
|
||||
action = rule.Action()
|
||||
}
|
||||
if routeAction, isRouteAction := action.(*R.RuleActionRoute); isRouteAction {
|
||||
next = routeAction.Outbound
|
||||
} else if defaultOutbound, err := router.DefaultOutbound(N.NetworkTCP); err == nil {
|
||||
next = defaultOutbound.Tag()
|
||||
}
|
||||
} else {
|
||||
next = rule.Outbound()
|
||||
}
|
||||
for {
|
||||
chain = append(chain, next)
|
||||
detour, loaded := router.Outbound(next)
|
||||
if !loaded {
|
||||
break
|
||||
}
|
||||
chain = append(chain, next)
|
||||
outbound = detour.Tag()
|
||||
outboundType = detour.Type()
|
||||
group, isGroup := detour.(adapter.OutboundGroup)
|
||||
|
@ -218,19 +221,21 @@ func NewUDPTracker(conn N.PacketConn, manager *Manager, metadata adapter.Inbound
|
|||
outbound string
|
||||
outboundType string
|
||||
)
|
||||
if rule == nil {
|
||||
if defaultOutbound, err := router.DefaultOutbound(N.NetworkUDP); err == nil {
|
||||
var action adapter.RuleAction
|
||||
if rule != nil {
|
||||
action = rule.Action()
|
||||
}
|
||||
if routeAction, isRouteAction := action.(*R.RuleActionRoute); isRouteAction {
|
||||
next = routeAction.Outbound
|
||||
} else if defaultOutbound, err := router.DefaultOutbound(N.NetworkUDP); err == nil {
|
||||
next = defaultOutbound.Tag()
|
||||
}
|
||||
} else {
|
||||
next = rule.Outbound()
|
||||
}
|
||||
for {
|
||||
chain = append(chain, next)
|
||||
detour, loaded := router.Outbound(next)
|
||||
if !loaded {
|
||||
break
|
||||
}
|
||||
chain = append(chain, next)
|
||||
outbound = detour.Tag()
|
||||
outboundType = detour.Type()
|
||||
group, isGroup := detour.(adapter.OutboundGroup)
|
||||
|
|
14
go.mod
14
go.mod
|
@ -24,17 +24,17 @@ require (
|
|||
github.com/sagernet/cors v1.2.1
|
||||
github.com/sagernet/fswatch v0.1.1
|
||||
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/reality v0.0.0-20230406110435-ee17307e7691
|
||||
github.com/sagernet/sing v0.5.0
|
||||
github.com/sagernet/sing-dns v0.3.0
|
||||
github.com/sagernet/sing-mux v0.2.1
|
||||
github.com/sagernet/sing v0.6.0-alpha.3
|
||||
github.com/sagernet/sing-dns v0.4.0-alpha.1
|
||||
github.com/sagernet/sing-mux v0.3.0-alpha.1
|
||||
github.com/sagernet/sing-quic v0.3.0-rc.2
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
||||
github.com/sagernet/sing-shadowtls v0.1.4
|
||||
github.com/sagernet/sing-tun v0.4.0-rc.5
|
||||
github.com/sagernet/sing-tun v0.6.0-alpha.3
|
||||
github.com/sagernet/sing-vmess v0.1.12
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
|
||||
github.com/sagernet/utls v1.6.7
|
||||
|
@ -66,7 +66,7 @@ require (
|
|||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/gobwas/httphead v0.1.0 // 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/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect
|
||||
github.com/hashicorp/yamux v0.1.2 // indirect
|
||||
|
@ -92,7 +92,7 @@ require (
|
|||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/sync v0.9.0 // indirect
|
||||
golang.org/x/text v0.20.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
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||
|
|
28
go.sum
28
go.sum
|
@ -33,8 +33,8 @@ 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/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||
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.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
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/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk=
|
||||
|
@ -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/gomobile v0.1.4 h1:WzX9ka+iHdupMgy2Vdich+OAt7TM8C2cZbIbzNjBrJY=
|
||||
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-20240428053021-e691de28565f/go.mod h1:KXmw+ouSJNOsuRpg4wgwwCQuunrGz4yoAqQjsLjc6N0=
|
||||
github.com/sagernet/gvisor v0.0.0-20241021032506-a4324256e4a3 h1:RxEz7LhPNiF/gX/Hg+OXr5lqsM9iVAgmaK1L1vzlDRM=
|
||||
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/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
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/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||
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.5.0/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-dns v0.3.0 h1:uHCIlbCwBxALJwXcEK1d75d7t3vzCSVEQsPfZR1cxQE=
|
||||
github.com/sagernet/sing-dns v0.3.0/go.mod h1:TqLIelI+FAbVEdiTRolhGLOwvhVjY7oT+wezlOJUQ7M=
|
||||
github.com/sagernet/sing-mux v0.2.1 h1:N/3MHymfnFZRd29tE3TaXwPUVVgKvxhtOkiCMLp9HVo=
|
||||
github.com/sagernet/sing-mux v0.2.1/go.mod h1:dm3BWL6NvES9pbib7llpylrq7Gq+LjlzG+0RacdxcyE=
|
||||
github.com/sagernet/sing v0.6.0-alpha.3 h1:GLp9d6Gbt+Ioeplauuzojz1nY2J6moceVGYIOv/h5gA=
|
||||
github.com/sagernet/sing v0.6.0-alpha.3/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-dns v0.4.0-alpha.1 h1:2KlP8DeqtGkULFiZtvG2r7SuoJP6orANFzJwC5vDKvg=
|
||||
github.com/sagernet/sing-dns v0.4.0-alpha.1/go.mod h1:vgHATsm4wdymwpvBZPei8RY+546iGXS6hlWv2x6YKcM=
|
||||
github.com/sagernet/sing-mux v0.3.0-alpha.1 h1:IgNX5bJBpL41gGbp05pdDOvh/b5eUQ6cv9240+Ngipg=
|
||||
github.com/sagernet/sing-mux v0.3.0-alpha.1/go.mod h1:FTcImmdfW38Lz7b+HQ+mxxOth1lz4ao8uEnz+MwIJQE=
|
||||
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-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-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
|
||||
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.4.0-rc.5/go.mod h1:rWu1ZC2lj4+UOOSNiUWjsY0y2fJcOHnLTMGna2n5P2o=
|
||||
github.com/sagernet/sing-tun v0.6.0-alpha.3 h1:KddmFF9ZYC1n+HZzpAZ1StMC5v38ir6hH0PJ7uGpAGE=
|
||||
github.com/sagernet/sing-tun v0.6.0-alpha.3/go.mod h1:R/UaKB1oFIvAeMIH2btQCaDVsfCNomEjfYBSCSA94sw=
|
||||
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/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.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/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/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||
|
|
|
@ -22,13 +22,13 @@ type myInboundAdapter struct {
|
|||
protocol string
|
||||
network []string
|
||||
ctx context.Context
|
||||
router adapter.ConnectionRouter
|
||||
router adapter.ConnectionRouterEx
|
||||
logger log.ContextLogger
|
||||
tag string
|
||||
listenOptions option.ListenOptions
|
||||
connHandler adapter.ConnectionHandler
|
||||
packetHandler adapter.PacketHandler
|
||||
oobPacketHandler adapter.OOBPacketHandler
|
||||
connHandler adapter.ConnectionHandlerEx
|
||||
packetHandler adapter.PacketHandlerEx
|
||||
oobPacketHandler adapter.OOBPacketHandlerEx
|
||||
packetUpstream any
|
||||
|
||||
// http mixed
|
||||
|
@ -55,10 +55,6 @@ func (a *myInboundAdapter) Tag() string {
|
|||
return a.tag
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) Network() []string {
|
||||
return a.network
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) Start() error {
|
||||
var err error
|
||||
if common.Contains(a.network, N.NetworkTCP) {
|
||||
|
@ -150,6 +146,31 @@ func (a *myInboundAdapter) newPacketConnection(ctx context.Context, conn N.Packe
|
|||
return a.router.RoutePacketConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) upstreamHandlerEx(metadata adapter.InboundContext) adapter.UpstreamHandlerAdapterEx {
|
||||
return adapter.NewUpstreamHandlerEx(metadata, a.newConnectionEx, a.streamPacketConnectionEx)
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) upstreamContextHandlerEx() adapter.UpstreamHandlerAdapterEx {
|
||||
return adapter.NewUpstreamContextHandlerEx(a.newConnectionEx, a.newPacketConnectionEx)
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) newConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
a.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||
a.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) newPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
a.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
|
||||
a.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||
a.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) streamPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
a.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||
a.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) createMetadata(conn net.Conn, metadata adapter.InboundContext) adapter.InboundContext {
|
||||
metadata.Inbound = a.tag
|
||||
metadata.InboundType = a.protocol
|
||||
|
@ -167,25 +188,17 @@ func (a *myInboundAdapter) createMetadata(conn net.Conn, metadata adapter.Inboun
|
|||
return metadata
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) createPacketMetadata(conn N.PacketConn, metadata adapter.InboundContext) adapter.InboundContext {
|
||||
metadata.Inbound = a.tag
|
||||
metadata.InboundType = a.protocol
|
||||
metadata.InboundDetour = a.listenOptions.Detour
|
||||
metadata.InboundOptions = a.listenOptions.InboundOptions
|
||||
if !metadata.Destination.IsValid() {
|
||||
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
|
||||
// Deprecated: don't use
|
||||
func (a *myInboundAdapter) newError(err error) {
|
||||
a.logger.Error(err)
|
||||
}
|
||||
|
||||
// Deprecated: don't use
|
||||
func (a *myInboundAdapter) NewError(ctx context.Context, err error) {
|
||||
NewError(a.logger, ctx, err)
|
||||
}
|
||||
|
||||
// Deprecated: don't use
|
||||
func NewError(logger log.ContextLogger, ctx context.Context, err error) {
|
||||
common.Close(err)
|
||||
if E.IsClosedOrCanceled(err) {
|
||||
|
|
|
@ -71,18 +71,14 @@ func (a *myInboundAdapter) injectTCP(conn net.Conn, metadata adapter.InboundCont
|
|||
ctx := log.ContextWithNewID(a.ctx)
|
||||
metadata = a.createMetadata(conn, metadata)
|
||||
a.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||
hErr := a.connHandler.NewConnection(ctx, conn, metadata)
|
||||
if hErr != nil {
|
||||
conn.Close()
|
||||
a.NewError(ctx, E.Cause(hErr, "process connection from ", metadata.Source))
|
||||
}
|
||||
a.connHandler.NewConnectionEx(ctx, conn, metadata, nil)
|
||||
}
|
||||
|
||||
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)
|
||||
hErr := a.newConnection(ctx, conn, metadata)
|
||||
if hErr != nil {
|
||||
conn.Close()
|
||||
a.NewError(ctx, E.Cause(hErr, "process connection from ", metadata.Source))
|
||||
}
|
||||
a.connHandler.NewConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
|
|
@ -42,7 +42,6 @@ func (a *myInboundAdapter) loopUDPIn() {
|
|||
defer buffer.Release()
|
||||
buffer.IncRef()
|
||||
defer buffer.DecRef()
|
||||
packetService := (*myInboundPacketAdapter)(a)
|
||||
for {
|
||||
buffer.Reset()
|
||||
n, addr, err := a.udpConn.ReadFromUDPAddrPort(buffer.FreeBytes())
|
||||
|
@ -50,16 +49,7 @@ func (a *myInboundAdapter) loopUDPIn() {
|
|||
return
|
||||
}
|
||||
buffer.Truncate(n)
|
||||
var metadata adapter.InboundContext
|
||||
metadata.Inbound = a.tag
|
||||
metadata.InboundType = a.protocol
|
||||
metadata.InboundOptions = a.listenOptions.InboundOptions
|
||||
metadata.Source = M.SocksaddrFromNetIP(addr).Unwrap()
|
||||
metadata.OriginDestination = a.udpAddr
|
||||
err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata)
|
||||
if err != nil {
|
||||
a.newError(E.Cause(err, "process packet from ", metadata.Source))
|
||||
}
|
||||
a.packetHandler.NewPacketEx(buffer, M.SocksaddrFromNetIP(addr).Unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,7 +59,6 @@ func (a *myInboundAdapter) loopUDPOOBIn() {
|
|||
defer buffer.Release()
|
||||
buffer.IncRef()
|
||||
defer buffer.DecRef()
|
||||
packetService := (*myInboundPacketAdapter)(a)
|
||||
oob := make([]byte, 1024)
|
||||
for {
|
||||
buffer.Reset()
|
||||
|
@ -78,22 +67,12 @@ func (a *myInboundAdapter) loopUDPOOBIn() {
|
|||
return
|
||||
}
|
||||
buffer.Truncate(n)
|
||||
var metadata adapter.InboundContext
|
||||
metadata.Inbound = a.tag
|
||||
metadata.InboundType = a.protocol
|
||||
metadata.InboundOptions = a.listenOptions.InboundOptions
|
||||
metadata.Source = M.SocksaddrFromNetIP(addr).Unwrap()
|
||||
metadata.OriginDestination = a.udpAddr
|
||||
err = a.oobPacketHandler.NewPacket(a.ctx, packetService, buffer, oob[:oobN], metadata)
|
||||
if err != nil {
|
||||
a.newError(E.Cause(err, "process packet from ", metadata.Source))
|
||||
}
|
||||
a.oobPacketHandler.NewPacketEx(buffer, oob[:oobN], M.SocksaddrFromNetIP(addr).Unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) loopUDPInThreadSafe() {
|
||||
defer close(a.packetOutboundClosed)
|
||||
packetService := (*myInboundPacketAdapter)(a)
|
||||
for {
|
||||
buffer := buf.NewPacket()
|
||||
n, addr, err := a.udpConn.ReadFromUDPAddrPort(buffer.FreeBytes())
|
||||
|
@ -102,23 +81,12 @@ func (a *myInboundAdapter) loopUDPInThreadSafe() {
|
|||
return
|
||||
}
|
||||
buffer.Truncate(n)
|
||||
var metadata adapter.InboundContext
|
||||
metadata.Inbound = a.tag
|
||||
metadata.InboundType = a.protocol
|
||||
metadata.InboundOptions = a.listenOptions.InboundOptions
|
||||
metadata.Source = M.SocksaddrFromNetIP(addr).Unwrap()
|
||||
metadata.OriginDestination = a.udpAddr
|
||||
err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata)
|
||||
if err != nil {
|
||||
buffer.Release()
|
||||
a.newError(E.Cause(err, "process packet from ", metadata.Source))
|
||||
}
|
||||
a.packetHandler.NewPacketEx(buffer, M.SocksaddrFromNetIP(addr).Unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) loopUDPOOBInThreadSafe() {
|
||||
defer close(a.packetOutboundClosed)
|
||||
packetService := (*myInboundPacketAdapter)(a)
|
||||
oob := make([]byte, 1024)
|
||||
for {
|
||||
buffer := buf.NewPacket()
|
||||
|
@ -128,17 +96,7 @@ func (a *myInboundAdapter) loopUDPOOBInThreadSafe() {
|
|||
return
|
||||
}
|
||||
buffer.Truncate(n)
|
||||
var metadata adapter.InboundContext
|
||||
metadata.Inbound = a.tag
|
||||
metadata.InboundType = a.protocol
|
||||
metadata.InboundOptions = a.listenOptions.InboundOptions
|
||||
metadata.Source = M.SocksaddrFromNetIP(addr).Unwrap()
|
||||
metadata.OriginDestination = a.udpAddr
|
||||
err = a.oobPacketHandler.NewPacket(a.ctx, packetService, buffer, oob[:oobN], metadata)
|
||||
if err != nil {
|
||||
buffer.Release()
|
||||
a.newError(E.Cause(err, "process packet from ", metadata.Source))
|
||||
}
|
||||
a.oobPacketHandler.NewPacketEx(buffer, oob[:oobN], M.SocksaddrFromNetIP(addr).Unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,7 +106,7 @@ func (a *myInboundAdapter) loopUDPOut() {
|
|||
case packet := <-a.packetOutbound:
|
||||
err := a.writePacket(packet.buffer, packet.destination)
|
||||
if err != nil && !E.IsClosed(err) {
|
||||
a.newError(E.New("write back udp: ", err))
|
||||
a.logger.Error(E.New("write back udp: ", err))
|
||||
}
|
||||
continue
|
||||
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 {
|
||||
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()))
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ package inbound
|
|||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
|
@ -13,14 +12,14 @@ import (
|
|||
"github.com/sagernet/sing/common/buf"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/udpnat"
|
||||
"github.com/sagernet/sing/common/udpnat2"
|
||||
)
|
||||
|
||||
var _ adapter.Inbound = (*Direct)(nil)
|
||||
|
||||
type Direct struct {
|
||||
myInboundAdapter
|
||||
udpNat *udpnat.Service[netip.AddrPort]
|
||||
udpNat *udpnat.Service
|
||||
overrideOption int
|
||||
overrideDestination M.Socksaddr
|
||||
}
|
||||
|
@ -54,10 +53,9 @@ func NewDirect(ctx context.Context, router adapter.Router, logger log.ContextLog
|
|||
} else {
|
||||
udpTimeout = C.UDPTimeout
|
||||
}
|
||||
inbound.udpNat = udpnat.New[netip.AddrPort](int64(udpTimeout.Seconds()), adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound))
|
||||
inbound.udpNat = udpnat.New(inbound, inbound.preparePacketConnection, udpTimeout, false)
|
||||
inbound.connHandler = inbound
|
||||
inbound.packetHandler = inbound
|
||||
inbound.packetUpstream = inbound.udpNat
|
||||
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)
|
||||
}
|
||||
|
||||
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 {
|
||||
case 1:
|
||||
metadata.Destination = d.overrideDestination
|
||||
destination = d.overrideDestination
|
||||
case 2:
|
||||
destination := d.overrideDestination
|
||||
destination.Port = metadata.Destination.Port
|
||||
metadata.Destination = destination
|
||||
destination = d.overrideDestination
|
||||
destination.Port = source.Port
|
||||
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) {
|
||||
return adapter.WithContext(log.ContextWithNewID(ctx), &metadata), &udpnat.DirectBackWriter{Source: conn, Nat: natConn}
|
||||
})
|
||||
return nil
|
||||
d.udpNat.NewPacket([][]byte{buffer.Bytes()}, source, destination, nil)
|
||||
}
|
||||
|
||||
func (d *Direct) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
return d.router.RouteConnection(ctx, conn, metadata)
|
||||
func (d *Direct) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
d.newConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
func (d *Direct) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
d.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
|
||||
return d.router.RoutePacketConnection(ctx, conn, metadata)
|
||||
func (d *Direct) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
d.newPacketConnectionEx(ctx, conn, d.createPacketMetadataEx(source, destination), onClose)
|
||||
}
|
||||
|
||||
func (d *Direct) preparePacketConnection(source M.Socksaddr, destination M.Socksaddr, userData any) (bool, context.Context, N.PacketWriter, N.CloseHandlerFunc) {
|
||||
return true, d.ctx, &directPacketWriter{d.packetConn(), source}, nil
|
||||
}
|
||||
|
||||
type directPacketWriter struct {
|
||||
writer N.PacketWriter
|
||||
source M.Socksaddr
|
||||
}
|
||||
|
||||
func (w *directPacketWriter) WritePacket(buffer *buf.Buffer, addr M.Socksaddr) error {
|
||||
return w.writer.WritePacket(buffer, w.source)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
std_bufio "bufio"
|
||||
"context"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
|
@ -21,7 +20,7 @@ import (
|
|||
|
||||
var (
|
||||
_ adapter.Inbound = (*HTTP)(nil)
|
||||
_ adapter.InjectableInbound = (*HTTP)(nil)
|
||||
_ adapter.TCPInjectableInbound = (*HTTP)(nil)
|
||||
)
|
||||
|
||||
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
|
||||
if h.tlsConfig != nil {
|
||||
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 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 {
|
||||
return os.ErrInvalid
|
||||
func (a *myInboundAdapter) upstreamUserHandlerEx(metadata adapter.InboundContext) adapter.UpstreamHandlerAdapterEx {
|
||||
return adapter.NewUpstreamHandlerEx(metadata, a.newUserConnection, a.streamUserPacketConnection)
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) upstreamUserHandler(metadata adapter.InboundContext) adapter.UpstreamHandlerAdapter {
|
||||
return adapter.NewUpstreamHandler(metadata, a.newUserConnection, a.streamUserPacketConnection, a)
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) newUserConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
func (a *myInboundAdapter) newUserConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
user, loaded := auth.UserFromContext[string](ctx)
|
||||
if !loaded {
|
||||
a.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||
return a.router.RouteConnection(ctx, conn, metadata)
|
||||
a.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
||||
return
|
||||
}
|
||||
metadata.User = user
|
||||
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)
|
||||
if !loaded {
|
||||
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
|
||||
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"
|
||||
"context"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/uot"
|
||||
|
@ -12,6 +11,7 @@ import (
|
|||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/protocol/http"
|
||||
"github.com/sagernet/sing/protocol/socks"
|
||||
|
@ -21,7 +21,7 @@ import (
|
|||
|
||||
var (
|
||||
_ adapter.Inbound = (*Mixed)(nil)
|
||||
_ adapter.InjectableInbound = (*Mixed)(nil)
|
||||
_ adapter.TCPInjectableInbound = (*Mixed)(nil)
|
||||
)
|
||||
|
||||
type Mixed struct {
|
||||
|
@ -47,20 +47,24 @@ func NewMixed(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
|||
return inbound
|
||||
}
|
||||
|
||||
func (h *Mixed) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
reader := std_bufio.NewReader(conn)
|
||||
headerBytes, err := reader.Peek(1)
|
||||
func (h *Mixed) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
err := h.newConnection(ctx, conn, metadata, onClose)
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch headerBytes[0] {
|
||||
case socks4.Version, socks5.Version:
|
||||
return socks.HandleConnection0(ctx, conn, reader, h.authenticator, h.upstreamUserHandler(metadata), adapter.UpstreamMetadata(metadata))
|
||||
default:
|
||||
return http.HandleConnection(ctx, conn, reader, h.authenticator, h.upstreamUserHandler(metadata), adapter.UpstreamMetadata(metadata))
|
||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Mixed) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
return os.ErrInvalid
|
||||
func (h *Mixed) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) error {
|
||||
reader := std_bufio.NewReader(conn)
|
||||
headerBytes, err := reader.Peek(1)
|
||||
if err != nil {
|
||||
return E.Cause(err, "peek first byte")
|
||||
}
|
||||
switch headerBytes[0] {
|
||||
case socks4.Version, socks5.Version:
|
||||
return socks.HandleConnectionEx(ctx, conn, reader, h.authenticator, nil, h.upstreamUserHandlerEx(metadata), metadata.Source, metadata.Destination, onClose)
|
||||
default:
|
||||
return http.HandleConnectionEx(ctx, conn, reader, h.authenticator, nil, h.upstreamUserHandlerEx(metadata), metadata.Source, onClose)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/v2rayhttp"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
|
@ -164,13 +165,13 @@ func (n *Naive) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
|||
n.badRequest(ctx, request, E.New("hijack failed"))
|
||||
return
|
||||
}
|
||||
n.newConnection(ctx, &naiveH1Conn{Conn: conn}, userName, source, destination)
|
||||
n.newConnection(ctx, false, &naiveH1Conn{Conn: conn}, userName, source, destination)
|
||||
} 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 != "" {
|
||||
n.logger.InfoContext(ctx, "[", userName, "] inbound connection from ", source)
|
||||
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 to ", destination)
|
||||
}
|
||||
hErr := n.router.RouteConnection(ctx, conn, n.createMetadata(conn, adapter.InboundContext{
|
||||
metadata := n.createMetadata(conn, adapter.InboundContext{
|
||||
Source: source,
|
||||
Destination: destination,
|
||||
User: userName,
|
||||
})
|
||||
if !waitForClose {
|
||||
n.router.RouteConnectionEx(ctx, conn, metadata, nil)
|
||||
} else {
|
||||
done := make(chan struct{})
|
||||
wrapper := v2rayhttp.NewHTTP2Wrapper(conn)
|
||||
n.router.RouteConnectionEx(ctx, conn, metadata, N.OnceClose(func(it error) {
|
||||
close(done)
|
||||
}))
|
||||
if hErr != nil {
|
||||
conn.Close()
|
||||
n.NewError(ctx, E.Cause(hErr, "process connection from ", source))
|
||||
<-done
|
||||
wrapper.CloseWrapper()
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
@ -34,11 +33,13 @@ func NewRedirect(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||
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)
|
||||
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)
|
||||
return r.newConnection(ctx, conn, metadata)
|
||||
r.newConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package inbound
|
|||
import (
|
||||
"context"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
|
@ -18,6 +17,7 @@ import (
|
|||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/ntp"
|
||||
)
|
||||
|
@ -37,7 +37,7 @@ func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
|
|||
|
||||
var (
|
||||
_ adapter.Inbound = (*Shadowsocks)(nil)
|
||||
_ adapter.InjectableInbound = (*Shadowsocks)(nil)
|
||||
_ adapter.TCPInjectableInbound = (*Shadowsocks)(nil)
|
||||
)
|
||||
|
||||
type Shadowsocks struct {
|
||||
|
@ -74,11 +74,11 @@ func newShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
|
|||
}
|
||||
switch {
|
||||
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):
|
||||
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):
|
||||
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:
|
||||
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
|
||||
}
|
||||
|
||||
func (h *Shadowsocks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
|
||||
func (h *Shadowsocks) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
err := h.service.NewConnection(ctx, conn, adapter.UpstreamMetadata(metadata))
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil {
|
||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Shadowsocks) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext) error {
|
||||
return h.service.NewPacket(adapter.WithContext(ctx, &metadata), conn, buffer, adapter.UpstreamMetadata(metadata))
|
||||
func (h *Shadowsocks) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
|
||||
err := h.service.NewPacket(h.ctx, h.packetConn(), buffer, M.Metadata{Source: source})
|
||||
if err != nil {
|
||||
h.logger.Error(E.Cause(err, "process packet from ", source))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Shadowsocks) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
return os.ErrInvalid
|
||||
func (h *Shadowsocks) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||
return h.router.RouteConnection(ctx, conn, h.createMetadata(conn, metadata))
|
||||
}
|
||||
|
||||
func (h *Shadowsocks) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
h.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
|
||||
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||
return h.router.RoutePacketConnection(ctx, conn, h.createPacketMetadata(conn, metadata))
|
||||
}
|
||||
|
|
|
@ -20,13 +20,14 @@ import (
|
|||
"github.com/sagernet/sing/common/buf"
|
||||
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/ntp"
|
||||
)
|
||||
|
||||
var (
|
||||
_ adapter.Inbound = (*ShadowsocksMulti)(nil)
|
||||
_ adapter.InjectableInbound = (*ShadowsocksMulti)(nil)
|
||||
_ adapter.TCPInjectableInbound = (*ShadowsocksMulti)(nil)
|
||||
)
|
||||
|
||||
type ShadowsocksMulti struct {
|
||||
|
@ -66,14 +67,15 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.
|
|||
options.Method,
|
||||
options.Password,
|
||||
int64(udpTimeout.Seconds()),
|
||||
adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound),
|
||||
adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, inbound),
|
||||
ntp.TimeFuncFromContext(ctx),
|
||||
)
|
||||
} else if common.Contains(shadowaead.List, options.Method) {
|
||||
service, err = shadowaead.NewMultiService[int](
|
||||
options.Method,
|
||||
int64(udpTimeout.Seconds()),
|
||||
adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound))
|
||||
adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, inbound),
|
||||
)
|
||||
} else {
|
||||
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
|
||||
}
|
||||
|
||||
func (h *ShadowsocksMulti) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
|
||||
func (h *ShadowsocksMulti) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
err := h.service.NewConnection(ctx, conn, adapter.UpstreamMetadata(metadata))
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil {
|
||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *ShadowsocksMulti) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext) error {
|
||||
return h.service.NewPacket(adapter.WithContext(ctx, &metadata), conn, buffer, adapter.UpstreamMetadata(metadata))
|
||||
}
|
||||
|
||||
func (h *ShadowsocksMulti) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
return os.ErrInvalid
|
||||
func (h *ShadowsocksMulti) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
|
||||
err := h.service.NewPacket(h.ctx, h.packetConn(), buffer, M.Metadata{Source: source})
|
||||
if err != nil {
|
||||
h.logger.Error(E.Cause(err, "process packet from ", source))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *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
|
||||
}
|
||||
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 {
|
||||
|
@ -135,5 +140,5 @@ func (h *ShadowsocksMulti) newPacketConnection(ctx context.Context, conn N.Packe
|
|||
ctx = log.ContextWithNewID(ctx)
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection from ", metadata.Source)
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
||||
return h.router.RoutePacketConnection(ctx, conn, 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/auth"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
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"
|
||||
)
|
||||
|
||||
var (
|
||||
_ adapter.Inbound = (*ShadowsocksRelay)(nil)
|
||||
_ adapter.InjectableInbound = (*ShadowsocksRelay)(nil)
|
||||
_ adapter.TCPInjectableInbound = (*ShadowsocksRelay)(nil)
|
||||
)
|
||||
|
||||
type ShadowsocksRelay struct {
|
||||
|
@ -61,7 +63,7 @@ func newShadowsocksRelay(ctx context.Context, router adapter.Router, logger log.
|
|||
options.Method,
|
||||
options.Password,
|
||||
int64(udpTimeout.Seconds()),
|
||||
adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound),
|
||||
adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, inbound),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -79,16 +81,19 @@ func newShadowsocksRelay(ctx context.Context, router adapter.Router, logger log.
|
|||
return inbound, err
|
||||
}
|
||||
|
||||
func (h *ShadowsocksRelay) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
|
||||
func (h *ShadowsocksRelay) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
err := h.service.NewConnection(ctx, conn, adapter.UpstreamMetadata(metadata))
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil {
|
||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *ShadowsocksRelay) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext) error {
|
||||
return h.service.NewPacket(adapter.WithContext(ctx, &metadata), conn, buffer, adapter.UpstreamMetadata(metadata))
|
||||
}
|
||||
|
||||
func (h *ShadowsocksRelay) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
return os.ErrInvalid
|
||||
func (h *ShadowsocksRelay) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
|
||||
err := h.service.NewPacket(h.ctx, h.packetConn(), buffer, M.Metadata{Source: source})
|
||||
if err != nil {
|
||||
h.logger.Error(E.Cause(err, "process packet from ", source))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *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
|
||||
}
|
||||
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 {
|
||||
|
@ -120,5 +125,5 @@ func (h *ShadowsocksRelay) newPacketConnection(ctx context.Context, conn N.Packe
|
|||
ctx = log.ContextWithNewID(ctx)
|
||||
h.logger.InfoContext(ctx, "[", destination, "] inbound packet connection from ", metadata.Source)
|
||||
h.logger.InfoContext(ctx, "[", destination, "] inbound packet connection to ", metadata.Destination)
|
||||
return h.router.RoutePacketConnection(ctx, conn, 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/common"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
import (
|
||||
std_bufio "bufio"
|
||||
"context"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/uot"
|
||||
|
@ -11,13 +11,14 @@ import (
|
|||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/protocol/socks"
|
||||
)
|
||||
|
||||
var (
|
||||
_ adapter.Inbound = (*Socks)(nil)
|
||||
_ adapter.InjectableInbound = (*Socks)(nil)
|
||||
_ adapter.TCPInjectableInbound = (*Socks)(nil)
|
||||
)
|
||||
|
||||
type Socks struct {
|
||||
|
@ -42,10 +43,10 @@ func NewSocks(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
|||
return inbound
|
||||
}
|
||||
|
||||
func (h *Socks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
return socks.HandleConnection(ctx, conn, h.authenticator, h.upstreamUserHandler(metadata), adapter.UpstreamMetadata(metadata))
|
||||
}
|
||||
|
||||
func (h *Socks) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
return os.ErrInvalid
|
||||
func (h *Socks) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
err := socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, nil, h.upstreamUserHandlerEx(metadata), metadata.Source, metadata.Destination, onClose)
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil {
|
||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,12 +18,12 @@ import (
|
|||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/udpnat"
|
||||
"github.com/sagernet/sing/common/udpnat2"
|
||||
)
|
||||
|
||||
type TProxy struct {
|
||||
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 {
|
||||
|
@ -46,8 +46,7 @@ func NewTProxy(ctx context.Context, router adapter.Router, logger log.ContextLog
|
|||
}
|
||||
tproxy.connHandler = tproxy
|
||||
tproxy.oobPacketHandler = tproxy
|
||||
tproxy.udpNat = udpnat.New[netip.AddrPort](int64(udpTimeout.Seconds()), tproxy.upstreamContextHandler())
|
||||
tproxy.packetUpstream = tproxy.udpNat
|
||||
tproxy.udpNat = udpnat.New(tproxy, tproxy.preparePacketConnection, udpTimeout, false)
|
||||
return tproxy
|
||||
}
|
||||
|
||||
|
@ -75,35 +74,43 @@ func (t *TProxy) Start() error {
|
|||
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()
|
||||
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)
|
||||
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.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
|
||||
t.udpNat.NewPacket([][]byte{buffer.Bytes()}, source, M.SocksaddrFromNetIP(destination), nil)
|
||||
}
|
||||
|
||||
type tproxyPacketWriter struct {
|
||||
ctx context.Context
|
||||
source N.PacketConn
|
||||
source netip.AddrPort
|
||||
destination M.Socksaddr
|
||||
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 {
|
||||
defer buffer.Release()
|
||||
conn := w.conn
|
||||
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 {
|
||||
w.conn = nil
|
||||
}
|
||||
|
@ -122,9 +129,5 @@ func (w *tproxyPacketWriter) WritePacket(buffer *buf.Buffer, destination M.Socks
|
|||
} else {
|
||||
defer udpConn.Close()
|
||||
}
|
||||
return common.Error(udpConn.WriteToUDPAddrPort(buffer.Bytes(), M.AddrPortFromNet(w.source.LocalAddr())))
|
||||
}
|
||||
|
||||
func (w *tproxyPacketWriter) Close() error {
|
||||
return common.Close(common.PtrOrNil(w.conn))
|
||||
return common.Error(udpConn.WriteToUDPAddrPort(buffer.Bytes(), w.source))
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ import (
|
|||
|
||||
var (
|
||||
_ adapter.Inbound = (*Trojan)(nil)
|
||||
_ adapter.InjectableInbound = (*Trojan)(nil)
|
||||
_ adapter.TCPInjectableInbound = (*Trojan)(nil)
|
||||
)
|
||||
|
||||
type Trojan struct {
|
||||
|
@ -90,7 +90,7 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
|
|||
return nil, err
|
||||
}
|
||||
if options.Transport != nil {
|
||||
inbound.transport, err = v2ray.NewServerTransport(ctx, 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 {
|
||||
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 {
|
||||
var err error
|
||||
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))
|
||||
}
|
||||
|
||||
func (h *Trojan) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
return os.ErrInvalid
|
||||
func (h *Trojan) 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))
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
func (t *trojanTransportHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
return (*Trojan)(t).newTransportConnection(ctx, conn, adapter.InboundContext{
|
||||
Source: metadata.Source,
|
||||
Destination: metadata.Destination,
|
||||
})
|
||||
func (t *trojanTransportHandler) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
(*Trojan)(t).routeTCP(ctx, conn, source, destination, onClose)
|
||||
}
|
||||
|
|
|
@ -28,17 +28,18 @@ import (
|
|||
"go4.org/netipx"
|
||||
)
|
||||
|
||||
var _ adapter.Inbound = (*Tun)(nil)
|
||||
var _ adapter.Inbound = (*TUN)(nil)
|
||||
|
||||
type Tun struct {
|
||||
type TUN struct {
|
||||
tag string
|
||||
ctx context.Context
|
||||
router adapter.Router
|
||||
logger log.ContextLogger
|
||||
// Deprecated
|
||||
inboundOptions option.InboundOptions
|
||||
tunOptions tun.Options
|
||||
endpointIndependentNat bool
|
||||
udpTimeout int64
|
||||
udpTimeout time.Duration
|
||||
stack string
|
||||
tunIf tun.Tun
|
||||
tunStack tun.Stack
|
||||
|
@ -53,7 +54,7 @@ type Tun struct {
|
|||
routeExcludeAddressSet []*netipx.IPSet
|
||||
}
|
||||
|
||||
func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions, platformInterface platform.Interface) (*Tun, error) {
|
||||
func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions, platformInterface platform.Interface) (*TUN, error) {
|
||||
address := options.Address
|
||||
var deprecatedAddressUsed bool
|
||||
//nolint:staticcheck
|
||||
|
@ -162,7 +163,7 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
|
|||
outputMark = tun.DefaultAutoRedirectOutputMark
|
||||
}
|
||||
|
||||
inbound := &Tun{
|
||||
inbound := &TUN{
|
||||
tag: tag,
|
||||
ctx: ctx,
|
||||
router: router,
|
||||
|
@ -194,7 +195,7 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
|
|||
InterfaceMonitor: router.InterfaceMonitor(),
|
||||
},
|
||||
endpointIndependentNat: options.EndpointIndependentNat,
|
||||
udpTimeout: int64(udpTimeout.Seconds()),
|
||||
udpTimeout: udpTimeout,
|
||||
stack: options.Stack,
|
||||
platformInterface: platformInterface,
|
||||
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{
|
||||
TunOptions: &inbound.tunOptions,
|
||||
Context: ctx,
|
||||
Handler: inbound,
|
||||
Handler: (*autoRedirectHandler)(inbound),
|
||||
Logger: logger,
|
||||
NetworkMonitor: router.NetworkMonitor(),
|
||||
InterfaceFinder: router.InterfaceFinder(),
|
||||
|
@ -283,17 +284,17 @@ func parseRange(uidRanges []ranges.Range[uint32], rangeList []string) ([]ranges.
|
|||
return uidRanges, nil
|
||||
}
|
||||
|
||||
func (t *Tun) Type() string {
|
||||
func (t *TUN) Type() string {
|
||||
return C.TypeTun
|
||||
}
|
||||
|
||||
func (t *Tun) Tag() string {
|
||||
func (t *TUN) Tag() string {
|
||||
return t.tag
|
||||
}
|
||||
|
||||
func (t *Tun) Start() error {
|
||||
func (t *TUN) Start() error {
|
||||
if C.IsAndroid && t.platformInterface == nil {
|
||||
t.tunOptions.BuildAndroidRules(t.router.PackageManager(), t)
|
||||
t.tunOptions.BuildAndroidRules(t.router.PackageManager())
|
||||
}
|
||||
if t.tunOptions.Name == "" {
|
||||
t.tunOptions.Name = tun.CalculateInterfaceName("")
|
||||
|
@ -327,7 +328,6 @@ func (t *Tun) Start() error {
|
|||
Context: t.ctx,
|
||||
Tun: tunInterface,
|
||||
TunOptions: t.tunOptions,
|
||||
EndpointIndependentNat: t.endpointIndependentNat,
|
||||
UDPTimeout: t.udpTimeout,
|
||||
Handler: t,
|
||||
Logger: t.logger,
|
||||
|
@ -349,7 +349,7 @@ func (t *Tun) Start() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (t *Tun) PostStart() error {
|
||||
func (t *TUN) PostStart() error {
|
||||
monitor := taskmonitor.New(t.logger, C.StartTimeout)
|
||||
if t.autoRedirect != nil {
|
||||
t.routeAddressSet = common.FlatMap(t.routeRuleSet, adapter.RuleSet.ExtractIPSet)
|
||||
|
@ -388,7 +388,7 @@ func (t *Tun) PostStart() error {
|
|||
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.routeExcludeAddressSet = common.FlatMap(t.routeExcludeRuleSet, adapter.RuleSet.ExtractIPSet)
|
||||
t.autoRedirect.UpdateRouteAddressSet()
|
||||
|
@ -396,7 +396,7 @@ func (t *Tun) updateRouteAddressSet(it adapter.RuleSet) {
|
|||
t.routeExcludeAddressSet = nil
|
||||
}
|
||||
|
||||
func (t *Tun) Close() error {
|
||||
func (t *TUN) Close() error {
|
||||
return common.Close(
|
||||
t.tunStack,
|
||||
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 {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
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)
|
||||
}
|
||||
func (t *TUN) PrepareConnection(source M.Socksaddr, destination M.Socksaddr) error {
|
||||
// TODO: implement rejects
|
||||
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)
|
||||
var metadata adapter.InboundContext
|
||||
metadata.Inbound = t.tag
|
||||
metadata.InboundType = C.TypeTun
|
||||
metadata.Source = upstreamMetadata.Source
|
||||
metadata.Destination = upstreamMetadata.Destination
|
||||
metadata.Source = source
|
||||
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
|
||||
t.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
|
||||
t.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||
err := t.router.RoutePacketConnection(ctx, conn, metadata)
|
||||
if err != nil {
|
||||
t.NewError(ctx, err)
|
||||
}
|
||||
return nil
|
||||
t.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
func (t *Tun) NewError(ctx context.Context, err error) {
|
||||
NewError(t.logger, ctx, err)
|
||||
type autoRedirectHandler TUN
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ import (
|
|||
|
||||
var (
|
||||
_ adapter.Inbound = (*VLESS)(nil)
|
||||
_ adapter.InjectableInbound = (*VLESS)(nil)
|
||||
_ adapter.TCPInjectableInbound = (*VLESS)(nil)
|
||||
)
|
||||
|
||||
type VLESS struct {
|
||||
|
@ -73,7 +73,7 @@ func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
|||
}
|
||||
}
|
||||
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 {
|
||||
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 {
|
||||
var err error
|
||||
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))
|
||||
}
|
||||
|
||||
func (h *VLESS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
return os.ErrInvalid
|
||||
func (h *VLESS) 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))
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
func (t *vlessTransportHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
return (*VLESS)(t).newTransportConnection(ctx, conn, adapter.InboundContext{
|
||||
Source: metadata.Source,
|
||||
Destination: metadata.Destination,
|
||||
})
|
||||
func (t *vlessTransportHandler) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
t.routeTCP(ctx, conn, source, destination, onClose)
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ import (
|
|||
|
||||
var (
|
||||
_ adapter.Inbound = (*VMess)(nil)
|
||||
_ adapter.InjectableInbound = (*VMess)(nil)
|
||||
_ adapter.TCPInjectableInbound = (*VMess)(nil)
|
||||
)
|
||||
|
||||
type VMess struct {
|
||||
|
@ -83,7 +83,7 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
|||
}
|
||||
}
|
||||
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 {
|
||||
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 {
|
||||
var err error
|
||||
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))
|
||||
}
|
||||
|
||||
func (h *VMess) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
return os.ErrInvalid
|
||||
func (h *VMess) 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))
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
func (t *vmessTransportHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
return (*VMess)(t).newTransportConnection(ctx, conn, adapter.InboundContext{
|
||||
Source: metadata.Source,
|
||||
Destination: metadata.Destination,
|
||||
})
|
||||
func (t *vmessTransportHandler) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
(*VMess)(t).routeTCP(ctx, conn, source, destination, onClose)
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/v2ray"
|
||||
"github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
@ -20,7 +21,7 @@ func init() {
|
|||
return nil, C.ErrQUICNotIncluded
|
||||
})
|
||||
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
|
||||
},
|
||||
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
|
||||
}
|
||||
|
||||
// Deprecated: Use rule action instead
|
||||
type InboundOptions struct {
|
||||
SniffEnabled bool `json:"sniff,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"`
|
||||
IPVersion int `json:"ip_version,omitempty"`
|
||||
Network Listable[string] `json:"network,omitempty"`
|
||||
|
@ -98,26 +98,58 @@ type DefaultRule struct {
|
|||
RuleSet Listable[string] `json:"rule_set,omitempty"`
|
||||
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
Outbound string `json:"outbound,omitempty"`
|
||||
|
||||
// Deprecated: renamed to rule_set_ip_cidr_match_source
|
||||
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 {
|
||||
var defaultValue DefaultRule
|
||||
defaultValue.Invert = r.Invert
|
||||
defaultValue.Outbound = r.Outbound
|
||||
defaultValue.Action = r.Action
|
||||
return !reflect.DeepEqual(r, defaultValue)
|
||||
}
|
||||
|
||||
type LogicalRule struct {
|
||||
type _LogicalRule struct {
|
||||
Mode string `json:"mode"`
|
||||
Rules []Rule `json:"rules,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)
|
||||
}
|
||||
|
|
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"`
|
||||
IPVersion int `json:"ip_version,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"`
|
||||
RuleSetIPCIDRAcceptEmpty bool `json:"rule_set_ip_cidr_accept_empty,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_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 {
|
||||
var defaultValue DefaultDNSRule
|
||||
defaultValue.Invert = r.Invert
|
||||
defaultValue.Server = r.Server
|
||||
defaultValue.DisableCache = r.DisableCache
|
||||
defaultValue.RewriteTTL = r.RewriteTTL
|
||||
defaultValue.ClientSubnet = r.ClientSubnet
|
||||
defaultValue.DNSRuleAction = r.DNSRuleAction
|
||||
return !reflect.DeepEqual(r, defaultValue)
|
||||
}
|
||||
|
||||
type LogicalDNSRule struct {
|
||||
type _LogicalDNSRule struct {
|
||||
Mode string `json:"mode"`
|
||||
Rules []DNSRule `json:"rules,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)
|
||||
}
|
||||
|
|
|
@ -81,8 +81,11 @@ func (a *AddrPrefix) UnmarshalJSON(content []byte) error {
|
|||
return prefixErr
|
||||
}
|
||||
|
||||
func (a AddrPrefix) Build() netip.Prefix {
|
||||
return netip.Prefix(a)
|
||||
func (a *AddrPrefix) Build() netip.Prefix {
|
||||
if a == nil {
|
||||
return netip.Prefix{}
|
||||
}
|
||||
return netip.Prefix(*a)
|
||||
}
|
||||
|
||||
type NetworkList string
|
||||
|
@ -143,12 +146,29 @@ func (l *Listable[T]) UnmarshalJSON(content []byte) error {
|
|||
|
||||
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) {
|
||||
var value string
|
||||
switch dns.DomainStrategy(s) {
|
||||
case dns.DomainStrategyAsIS:
|
||||
value = ""
|
||||
// value = "AsIS"
|
||||
// value = "as_is"
|
||||
case dns.DomainStrategyPreferIPv4:
|
||||
value = "prefer_ipv4"
|
||||
case dns.DomainStrategyPreferIPv6:
|
||||
|
|
|
@ -39,12 +39,14 @@ func (h *Block) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.
|
|||
return nil, io.EOF
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func (h *Block) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
conn.Close()
|
||||
h.logger.InfoContext(ctx, "blocked connection to ", metadata.Destination)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func (h *Block) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
conn.Close()
|
||||
h.logger.InfoContext(ctx, "blocked packet connection to ", metadata.Destination)
|
||||
|
|
|
@ -69,7 +69,7 @@ func NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata a
|
|||
if err != nil {
|
||||
return N.ReportHandshakeFailure(conn, err)
|
||||
}
|
||||
err = N.ReportHandshakeSuccess(conn)
|
||||
err = N.ReportConnHandshakeSuccess(conn, outConn)
|
||||
if err != nil {
|
||||
outConn.Close()
|
||||
return err
|
||||
|
@ -96,7 +96,7 @@ func NewDirectConnection(ctx context.Context, router adapter.Router, this N.Dial
|
|||
if err != nil {
|
||||
return N.ReportHandshakeFailure(conn, err)
|
||||
}
|
||||
err = N.ReportHandshakeSuccess(conn)
|
||||
err = N.ReportConnHandshakeSuccess(conn, outConn)
|
||||
if err != nil {
|
||||
outConn.Close()
|
||||
return err
|
||||
|
@ -117,14 +117,14 @@ func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn,
|
|||
if err != nil {
|
||||
return N.ReportHandshakeFailure(conn, err)
|
||||
}
|
||||
err = N.ReportHandshakeSuccess(conn)
|
||||
err = N.ReportPacketConnHandshakeSuccess(conn, outConn)
|
||||
if err != nil {
|
||||
outConn.Close()
|
||||
return err
|
||||
}
|
||||
if destinationAddress.IsValid() {
|
||||
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)
|
||||
} else {
|
||||
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 {
|
||||
return N.ReportHandshakeFailure(conn, err)
|
||||
}
|
||||
err = N.ReportHandshakeSuccess(conn)
|
||||
err = N.ReportPacketConnHandshakeSuccess(conn, outConn)
|
||||
if err != nil {
|
||||
outConn.Close()
|
||||
return err
|
||||
|
|
|
@ -30,7 +30,7 @@ type Direct struct {
|
|||
fallbackDelay time.Duration
|
||||
overrideOption int
|
||||
overrideDestination M.Socksaddr
|
||||
loopBack *loopBackDetector
|
||||
// loopBack *loopBackDetector
|
||||
}
|
||||
|
||||
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),
|
||||
fallbackDelay: time.Duration(options.FallbackDelay),
|
||||
dialer: outboundDialer,
|
||||
loopBack: newLoopBackDetector(router),
|
||||
// loopBack: newLoopBackDetector(router),
|
||||
}
|
||||
if options.ProxyProtocol != 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:
|
||||
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 {
|
||||
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) {
|
||||
|
@ -148,14 +149,14 @@ func (h *Direct) ListenPacket(ctx context.Context, destination M.Socksaddr) (net
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn = h.loopBack.NewPacketConn(bufio.NewPacketConn(conn), destination)
|
||||
// conn = h.loopBack.NewPacketConn(bufio.NewPacketConn(conn), destination)
|
||||
if originDestination != destination {
|
||||
conn = bufio.NewNATPacketConn(bufio.NewPacketConn(conn), destination, originDestination)
|
||||
}
|
||||
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())) {
|
||||
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)
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -45,6 +45,7 @@ func (d *DNS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.Pa
|
|||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func (d *DNS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
metadata.Destination = M.Socksaddr{}
|
||||
defer conn.Close()
|
||||
|
@ -97,6 +98,7 @@ func (d *DNS) handleConnection(ctx context.Context, conn net.Conn, metadata adap
|
|||
return nil
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
metadata.Destination = M.Socksaddr{}
|
||||
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) {
|
||||
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)
|
||||
}
|
||||
|
||||
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() {
|
||||
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)
|
||||
}
|
||||
|
||||
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() {
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
// TODO
|
||||
// Deprecated
|
||||
func (s *Selector) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
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 {
|
||||
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 {
|
||||
|
|
|
@ -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() {
|
||||
if h.multiplexDialer != nil {
|
||||
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) {
|
||||
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)
|
||||
}
|
||||
|
||||
// TODO
|
||||
// Deprecated
|
||||
func (h *Socks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
if h.resolve {
|
||||
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 {
|
||||
if h.resolve {
|
||||
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) {
|
||||
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) {
|
||||
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() {
|
||||
if h.transport != nil {
|
||||
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() {
|
||||
_ = 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
|
||||
}
|
||||
|
||||
// TODO
|
||||
// Deprecated
|
||||
func (s *URLTest) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
||||
return NewConnection(ctx, s, conn, metadata)
|
||||
}
|
||||
|
||||
// TODO
|
||||
// Deprecated
|
||||
func (s *URLTest) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
||||
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() {
|
||||
if h.transport != nil {
|
||||
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
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// TODO
|
||||
// Deprecated
|
||||
func (w *WireGuard) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
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 {
|
||||
return NewDirectPacketConnection(ctx, w.router, w, conn, metadata, dns.DomainStrategyAsIS)
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/sagernet/sing-box/common/geosite"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/experimental/deprecated"
|
||||
R "github.com/sagernet/sing-box/route/rule"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
|
@ -32,7 +33,7 @@ func (r *Router) LoadGeosite(code string) (adapter.Rule, error) {
|
|||
if err != nil {
|
||||
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 {
|
||||
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"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
R "github.com/sagernet/sing-box/route/rule"
|
||||
"github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing/common/cache"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
@ -36,15 +37,16 @@ func (m *DNSReverseMapping) Query(address netip.Addr) (string, bool) {
|
|||
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)
|
||||
if metadata == nil {
|
||||
panic("no context")
|
||||
}
|
||||
if index < len(r.dnsRules) {
|
||||
var options dns.QueryOptions
|
||||
if ruleIndex < len(r.dnsRules) {
|
||||
dnsRules := r.dnsRules
|
||||
if index != -1 {
|
||||
dnsRules = dnsRules[index+1:]
|
||||
if ruleIndex != -1 {
|
||||
dnsRules = dnsRules[ruleIndex+1:]
|
||||
}
|
||||
for currentRuleIndex, rule := range dnsRules {
|
||||
if rule.WithAddressLimit() && !isAddressQuery {
|
||||
|
@ -52,43 +54,42 @@ func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, index int, isAd
|
|||
}
|
||||
metadata.ResetRuleCache()
|
||||
if rule.Match(metadata) {
|
||||
detour := rule.Outbound()
|
||||
transport, loaded := r.transportMap[detour]
|
||||
displayRuleIndex := currentRuleIndex
|
||||
if displayRuleIndex != -1 {
|
||||
displayRuleIndex += displayRuleIndex + 1
|
||||
}
|
||||
if routeAction, isRoute := rule.Action().(*R.RuleActionDNSRoute); isRoute {
|
||||
transport, loaded := r.transportMap[routeAction.Server]
|
||||
if !loaded {
|
||||
r.dnsLogger.ErrorContext(ctx, "transport not found: ", detour)
|
||||
r.dnsLogger.ErrorContext(ctx, "transport not found: ", routeAction.Server)
|
||||
continue
|
||||
}
|
||||
_, isFakeIP := transport.(adapter.FakeIPTransport)
|
||||
if isFakeIP && !allowFakeIP {
|
||||
continue
|
||||
}
|
||||
ruleIndex := currentRuleIndex
|
||||
if index != -1 {
|
||||
ruleIndex += index + 1
|
||||
}
|
||||
r.dnsLogger.DebugContext(ctx, "match[", ruleIndex, "] ", rule.String(), " => ", detour)
|
||||
if isFakeIP || rule.DisableCache() {
|
||||
ctx = dns.ContextWithDisableCache(ctx, true)
|
||||
}
|
||||
if rewriteTTL := rule.RewriteTTL(); rewriteTTL != nil {
|
||||
ctx = dns.ContextWithRewriteTTL(ctx, *rewriteTTL)
|
||||
}
|
||||
if clientSubnet := rule.ClientSubnet(); clientSubnet != nil {
|
||||
ctx = dns.ContextWithClientSubnet(ctx, *clientSubnet)
|
||||
}
|
||||
options.DisableCache = isFakeIP || routeAction.DisableCache
|
||||
options.RewriteTTL = routeAction.RewriteTTL
|
||||
options.ClientSubnet = routeAction.ClientSubnet
|
||||
if domainStrategy, dsLoaded := r.transportDomainStrategy[transport]; dsLoaded {
|
||||
return ctx, transport, domainStrategy, rule, ruleIndex
|
||||
options.Strategy = domainStrategy
|
||||
} else {
|
||||
return ctx, transport, r.defaultDomainStrategy, rule, ruleIndex
|
||||
options.Strategy = r.defaultDomainStrategy
|
||||
}
|
||||
r.dnsLogger.DebugContext(ctx, "match[", displayRuleIndex, "] ", rule.String(), " => ", rule.Action())
|
||||
return transport, options, rule, currentRuleIndex
|
||||
} else {
|
||||
return nil, options, rule, currentRuleIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if domainStrategy, dsLoaded := r.transportDomainStrategy[r.defaultTransport]; dsLoaded {
|
||||
return ctx, r.defaultTransport, domainStrategy, nil, -1
|
||||
options.Strategy = domainStrategy
|
||||
} 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) {
|
||||
|
@ -117,21 +118,18 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
|
|||
metadata.Domain = fqdnToDomain(message.Question[0].Name)
|
||||
}
|
||||
var (
|
||||
strategy dns.DomainStrategy
|
||||
options dns.QueryOptions
|
||||
rule adapter.DNSRule
|
||||
ruleIndex int
|
||||
)
|
||||
ruleIndex = -1
|
||||
for {
|
||||
var (
|
||||
dnsCtx context.Context
|
||||
addressLimit bool
|
||||
)
|
||||
dnsCtx, transport, strategy, rule, ruleIndex = r.matchDNS(ctx, true, ruleIndex, isAddressQuery(message))
|
||||
dnsCtx = adapter.OverrideContext(dnsCtx)
|
||||
dnsCtx := adapter.OverrideContext(ctx)
|
||||
var addressLimit bool
|
||||
transport, options, rule, ruleIndex = r.matchDNS(ctx, true, ruleIndex, isAddressQuery(message))
|
||||
if rule != nil && rule.WithAddressLimit() {
|
||||
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)
|
||||
if addrErr != nil {
|
||||
return false
|
||||
|
@ -141,7 +139,7 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
|
|||
})
|
||||
} else {
|
||||
addressLimit = false
|
||||
response, err = r.dnsClient.Exchange(dnsCtx, transport, message, strategy)
|
||||
response, err = r.dnsClient.Exchange(dnsCtx, transport, message, options)
|
||||
}
|
||||
var rejected bool
|
||||
if err != nil {
|
||||
|
@ -200,30 +198,27 @@ func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainS
|
|||
metadata.Domain = domain
|
||||
var (
|
||||
transport dns.Transport
|
||||
transportStrategy dns.DomainStrategy
|
||||
options dns.QueryOptions
|
||||
rule adapter.DNSRule
|
||||
ruleIndex int
|
||||
)
|
||||
ruleIndex = -1
|
||||
for {
|
||||
var (
|
||||
dnsCtx context.Context
|
||||
addressLimit bool
|
||||
)
|
||||
dnsCtx, transport, transportStrategy, rule, ruleIndex = r.matchDNS(ctx, false, ruleIndex, true)
|
||||
dnsCtx = adapter.OverrideContext(dnsCtx)
|
||||
if strategy == dns.DomainStrategyAsIS {
|
||||
strategy = transportStrategy
|
||||
dnsCtx := adapter.OverrideContext(ctx)
|
||||
var addressLimit bool
|
||||
transport, options, rule, ruleIndex = r.matchDNS(ctx, false, ruleIndex, true)
|
||||
if strategy != dns.DomainStrategyAsIS {
|
||||
options.Strategy = strategy
|
||||
}
|
||||
if rule != nil && rule.WithAddressLimit() {
|
||||
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
|
||||
return rule.MatchAddressLimit(metadata)
|
||||
})
|
||||
} else {
|
||||
addressLimit = false
|
||||
responseAddrs, err = r.dnsClient.Lookup(dnsCtx, transport, domain, strategy)
|
||||
responseAddrs, err = r.dnsClient.Lookup(dnsCtx, transport, domain, options)
|
||||
}
|
||||
if err != nil {
|
||||
if errors.Is(err, dns.ErrResponseRejectedCached) {
|
394
route/router.go
394
route/router.go
|
@ -3,11 +3,9 @@ package route
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/user"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
@ -19,22 +17,16 @@ import (
|
|||
"github.com/sagernet/sing-box/common/geoip"
|
||||
"github.com/sagernet/sing-box/common/geosite"
|
||||
"github.com/sagernet/sing-box/common/process"
|
||||
"github.com/sagernet/sing-box/common/sniff"
|
||||
"github.com/sagernet/sing-box/common/taskmonitor"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"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-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"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
|
@ -42,7 +34,6 @@ import (
|
|||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/ntp"
|
||||
"github.com/sagernet/sing/common/task"
|
||||
"github.com/sagernet/sing/common/uot"
|
||||
"github.com/sagernet/sing/common/winpowrprof"
|
||||
"github.com/sagernet/sing/service"
|
||||
"github.com/sagernet/sing/service/pause"
|
||||
|
@ -154,14 +145,14 @@ func NewRouter(
|
|||
Logger: router.dnsLogger,
|
||||
})
|
||||
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 {
|
||||
return nil, E.Cause(err, "parse rule[", i, "]")
|
||||
}
|
||||
router.rules = append(router.rules, routeRule)
|
||||
}
|
||||
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 {
|
||||
return nil, E.Cause(err, "parse dns rule[", i, "]")
|
||||
}
|
||||
|
@ -171,7 +162,7 @@ func NewRouter(
|
|||
if _, exists := router.ruleSetMap[ruleSetOptions.Tag]; exists {
|
||||
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 {
|
||||
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.outboundByTag = outboundByTag
|
||||
for i, rule := range r.rules {
|
||||
if _, loaded := outboundByTag[rule.Outbound()]; !loaded {
|
||||
return E.New("outbound not found for rule[", i, "]: ", rule.Outbound())
|
||||
routeAction, isRoute := rule.Action().(*R.RuleActionRoute)
|
||||
if !isRoute {
|
||||
continue
|
||||
}
|
||||
if _, loaded := outboundByTag[routeAction.Outbound]; !loaded {
|
||||
return E.New("outbound not found for rule[", i, "]: ", routeAction.Outbound)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -807,375 +802,6 @@ func (r *Router) NeedWIFIState() bool {
|
|||
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 {
|
||||
return r.interfaceFinder
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
@ -20,7 +20,7 @@ type abstractDefaultRule struct {
|
|||
allItems []RuleItem
|
||||
ruleSetItem RuleItem
|
||||
invert bool
|
||||
outbound string
|
||||
action adapter.RuleAction
|
||||
}
|
||||
|
||||
func (r *abstractDefaultRule) Type() string {
|
||||
|
@ -150,8 +150,8 @@ func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool {
|
|||
return !r.invert
|
||||
}
|
||||
|
||||
func (r *abstractDefaultRule) Outbound() string {
|
||||
return r.outbound
|
||||
func (r *abstractDefaultRule) Action() adapter.RuleAction {
|
||||
return r.action
|
||||
}
|
||||
|
||||
func (r *abstractDefaultRule) String() string {
|
||||
|
@ -166,7 +166,7 @@ type abstractLogicalRule struct {
|
|||
rules []adapter.HeadlessRule
|
||||
mode string
|
||||
invert bool
|
||||
outbound string
|
||||
action adapter.RuleAction
|
||||
}
|
||||
|
||||
func (r *abstractLogicalRule) Type() string {
|
||||
|
@ -231,8 +231,8 @@ func (r *abstractLogicalRule) Match(metadata *adapter.InboundContext) bool {
|
|||
}
|
||||
}
|
||||
|
||||
func (r *abstractLogicalRule) Outbound() string {
|
||||
return r.outbound
|
||||
func (r *abstractLogicalRule) Action() adapter.RuleAction {
|
||||
return r.action
|
||||
}
|
||||
|
||||
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 (
|
||||
"context"
|
||||
|
@ -17,17 +17,23 @@ func NewRule(ctx context.Context, router adapter.Router, logger log.ContextLogge
|
|||
if !options.DefaultOptions.IsValid() {
|
||||
return nil, E.New("missing conditions")
|
||||
}
|
||||
if options.DefaultOptions.Outbound == "" && checkOutbound {
|
||||
switch options.DefaultOptions.Action {
|
||||
case "", C.RuleActionTypeRoute:
|
||||
if options.DefaultOptions.RouteOptions.Outbound == "" && checkOutbound {
|
||||
return nil, E.New("missing outbound field")
|
||||
}
|
||||
}
|
||||
return NewDefaultRule(ctx, router, logger, options.DefaultOptions)
|
||||
case C.RuleTypeLogical:
|
||||
if !options.LogicalOptions.IsValid() {
|
||||
return nil, E.New("missing conditions")
|
||||
}
|
||||
if options.LogicalOptions.Outbound == "" && checkOutbound {
|
||||
switch options.LogicalOptions.Action {
|
||||
case "", C.RuleActionTypeRoute:
|
||||
if options.LogicalOptions.RouteOptions.Outbound == "" && checkOutbound {
|
||||
return nil, E.New("missing outbound field")
|
||||
}
|
||||
}
|
||||
return NewLogicalRule(ctx, router, logger, options.LogicalOptions)
|
||||
default:
|
||||
return nil, E.New("unknown rule type: ", options.Type)
|
||||
|
@ -46,10 +52,14 @@ type RuleItem interface {
|
|||
}
|
||||
|
||||
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{
|
||||
abstractDefaultRule{
|
||||
invert: options.Invert,
|
||||
outbound: options.Outbound,
|
||||
action: action,
|
||||
},
|
||||
}
|
||||
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) {
|
||||
r := &LogicalRule{
|
||||
action, err := NewRuleAction(options.RuleAction)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "action")
|
||||
}
|
||||
rule := &LogicalRule{
|
||||
abstractLogicalRule{
|
||||
rules: make([]adapter.HeadlessRule, len(options.Rules)),
|
||||
invert: options.Invert,
|
||||
outbound: options.Outbound,
|
||||
action: action,
|
||||
},
|
||||
}
|
||||
switch options.Mode {
|
||||
case C.LogicalTypeAnd:
|
||||
r.mode = C.LogicalTypeAnd
|
||||
rule.mode = C.LogicalTypeAnd
|
||||
case C.LogicalTypeOr:
|
||||
r.mode = C.LogicalTypeOr
|
||||
rule.mode = C.LogicalTypeOr
|
||||
default:
|
||||
return nil, E.New("unknown logical mode: ", options.Mode)
|
||||
}
|
||||
for i, subRule := range options.Rules {
|
||||
rule, err := NewRule(ctx, router, logger, subRule, false)
|
||||
for i, subOptions := range options.Rules {
|
||||
subRule, err := NewRule(ctx, router, logger, subOptions, false)
|
||||
if err != nil {
|
||||
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 (
|
||||
"context"
|
||||
"net/netip"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
|
@ -19,17 +18,23 @@ func NewDNSRule(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||
if !options.DefaultOptions.IsValid() {
|
||||
return nil, E.New("missing conditions")
|
||||
}
|
||||
if options.DefaultOptions.Server == "" && checkServer {
|
||||
switch options.DefaultOptions.Action {
|
||||
case "", C.RuleActionTypeRoute:
|
||||
if options.DefaultOptions.RouteOptions.Server == "" && checkServer {
|
||||
return nil, E.New("missing server field")
|
||||
}
|
||||
}
|
||||
return NewDefaultDNSRule(ctx, router, logger, options.DefaultOptions)
|
||||
case C.RuleTypeLogical:
|
||||
if !options.LogicalOptions.IsValid() {
|
||||
return nil, E.New("missing conditions")
|
||||
}
|
||||
if options.LogicalOptions.Server == "" && checkServer {
|
||||
switch options.LogicalOptions.Action {
|
||||
case "", C.RuleActionTypeRoute:
|
||||
if options.LogicalOptions.RouteOptions.Server == "" && checkServer {
|
||||
return nil, E.New("missing server field")
|
||||
}
|
||||
}
|
||||
return NewLogicalDNSRule(ctx, router, logger, options.LogicalOptions)
|
||||
default:
|
||||
return nil, E.New("unknown rule type: ", options.Type)
|
||||
|
@ -40,20 +45,14 @@ var _ adapter.DNSRule = (*DefaultDNSRule)(nil)
|
|||
|
||||
type DefaultDNSRule struct {
|
||||
abstractDefaultRule
|
||||
disableCache bool
|
||||
rewriteTTL *uint32
|
||||
clientSubnet *netip.Prefix
|
||||
}
|
||||
|
||||
func NewDefaultDNSRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.DefaultDNSRule) (*DefaultDNSRule, error) {
|
||||
rule := &DefaultDNSRule{
|
||||
abstractDefaultRule: abstractDefaultRule{
|
||||
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 {
|
||||
item := NewInboundRule(options.Inbound)
|
||||
|
@ -245,16 +244,8 @@ func NewDefaultDNSRule(ctx context.Context, router adapter.Router, logger log.Co
|
|||
return rule, nil
|
||||
}
|
||||
|
||||
func (r *DefaultDNSRule) DisableCache() bool {
|
||||
return r.disableCache
|
||||
}
|
||||
|
||||
func (r *DefaultDNSRule) RewriteTTL() *uint32 {
|
||||
return r.rewriteTTL
|
||||
}
|
||||
|
||||
func (r *DefaultDNSRule) ClientSubnet() *netip.Prefix {
|
||||
return r.clientSubnet
|
||||
func (r *DefaultDNSRule) Action() adapter.RuleAction {
|
||||
return r.action
|
||||
}
|
||||
|
||||
func (r *DefaultDNSRule) WithAddressLimit() bool {
|
||||
|
@ -289,9 +280,6 @@ var _ adapter.DNSRule = (*LogicalDNSRule)(nil)
|
|||
|
||||
type LogicalDNSRule struct {
|
||||
abstractLogicalRule
|
||||
disableCache bool
|
||||
rewriteTTL *uint32
|
||||
clientSubnet *netip.Prefix
|
||||
}
|
||||
|
||||
func NewLogicalDNSRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.LogicalDNSRule) (*LogicalDNSRule, error) {
|
||||
|
@ -299,11 +287,8 @@ func NewLogicalDNSRule(ctx context.Context, router adapter.Router, logger log.Co
|
|||
abstractLogicalRule: abstractLogicalRule{
|
||||
rules: make([]adapter.HeadlessRule, len(options.Rules)),
|
||||
invert: options.Invert,
|
||||
outbound: options.Server,
|
||||
action: NewDNSRuleAction(options.DNSRuleAction),
|
||||
},
|
||||
disableCache: options.DisableCache,
|
||||
rewriteTTL: options.RewriteTTL,
|
||||
clientSubnet: (*netip.Prefix)(options.ClientSubnet),
|
||||
}
|
||||
switch options.Mode {
|
||||
case C.LogicalTypeAnd:
|
||||
|
@ -323,16 +308,8 @@ func NewLogicalDNSRule(ctx context.Context, router adapter.Router, logger log.Co
|
|||
return r, nil
|
||||
}
|
||||
|
||||
func (r *LogicalDNSRule) DisableCache() bool {
|
||||
return r.disableCache
|
||||
}
|
||||
|
||||
func (r *LogicalDNSRule) RewriteTTL() *uint32 {
|
||||
return r.rewriteTTL
|
||||
}
|
||||
|
||||
func (r *LogicalDNSRule) ClientSubnet() *netip.Prefix {
|
||||
return r.clientSubnet
|
||||
func (r *LogicalDNSRule) Action() adapter.RuleAction {
|
||||
return r.action
|
||||
}
|
||||
|
||||
func (r *LogicalDNSRule) WithAddressLimit() bool {
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing-box/adapter"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"strings"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"strings"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"net/netip"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"strings"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"strings"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"strings"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"strings"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"regexp"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"net/netip"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"strings"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"strings"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"net/netip"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing-box/adapter"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"strings"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"strings"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"strings"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"strings"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"strconv"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"path/filepath"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"strings"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"regexp"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"strings"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"strings"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"strings"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"strings"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"strings"
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package rule
|
||||
|
||||
import (
|
||||
"strings"
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user