Add resolver for inbound

This commit is contained in:
世界 2022-07-07 23:36:32 +08:00
parent 538a1f5909
commit 9c256afc1a
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
22 changed files with 261 additions and 173 deletions

View file

@ -2,8 +2,11 @@ package adapter
import ( import (
"context" "context"
"net/netip"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
C "github.com/sagernet/sing-box/constant"
) )
type Inbound interface { type Inbound interface {
@ -23,8 +26,10 @@ type InboundContext struct {
// cache // cache
DomainStrategy C.DomainStrategy
SniffEnabled bool SniffEnabled bool
SniffOverrideDestination bool SniffOverrideDestination bool
DestinationAddresses []netip.Addr
SourceGeoIPCode string SourceGeoIPCode string
GeoIPCode string GeoIPCode string
@ -50,5 +55,5 @@ func AppendContext(ctx context.Context) (context.Context, *InboundContext) {
return ctx, metadata return ctx, metadata
} }
metadata = new(InboundContext) metadata = new(InboundContext)
return WithContext(ctx, metadata), nil return WithContext(ctx, metadata), metadata
} }

View file

@ -4,7 +4,6 @@ import (
"context" "context"
"net" "net"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
) )
@ -13,6 +12,6 @@ type Outbound interface {
Tag() string Tag() string
Network() []string Network() []string
N.Dialer N.Dialer
NewConnection(ctx context.Context, conn net.Conn, destination M.Socksaddr) error NewConnection(ctx context.Context, conn net.Conn, metadata InboundContext) error
NewPacketConnection(ctx context.Context, conn N.PacketConn, destination M.Socksaddr) error NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error
} }

View file

@ -10,16 +10,18 @@ import (
) )
func New(router adapter.Router, options option.DialerOptions) N.Dialer { func New(router adapter.Router, options option.DialerOptions) N.Dialer {
domainStrategy := C.DomainStrategy(options.DomainStrategy)
var dialer N.Dialer
if options.Detour == "" { if options.Detour == "" {
dialer = NewDefault(options) return NewDefault(options)
dialer = NewResolveDialer(router, dialer, domainStrategy)
} else { } else {
dialer = NewDetour(router, options.Detour) return NewDetour(router, options.Detour)
if domainStrategy != C.DomainStrategyAsIS {
dialer = NewResolveDialer(router, dialer, domainStrategy)
} }
}
func NewOutbound(router adapter.Router, options option.OutboundDialerOptions) N.Dialer {
dialer := New(router, options.DialerOptions)
domainStrategy := C.DomainStrategy(options.DomainStrategy)
if domainStrategy != C.DomainStrategyAsIS || options.Detour == "" && !C.CGO_ENABLED {
dialer = NewResolveDialer(router, dialer, domainStrategy)
} }
if options.OverrideOptions.IsValid() { if options.OverrideOptions.IsValid() {
dialer = NewOverride(dialer, common.PtrValueOrDefault(options.OverrideOptions)) dialer = NewOverride(dialer, common.PtrValueOrDefault(options.OverrideOptions))

View file

@ -5,7 +5,6 @@ import (
"net" "net"
"net/netip" "net/netip"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
@ -41,16 +40,7 @@ func (d *ResolveDialer) DialContext(ctx context.Context, network string, destina
if err != nil { if err != nil {
return nil, err return nil, err
} }
var conn net.Conn return DialSerial(ctx, d.dialer, network, destination, addresses)
var connErrors []error
for _, address := range addresses {
conn, err = d.dialer.DialContext(ctx, network, M.SocksaddrFromAddrPort(address, destination.Port))
if err != nil {
connErrors = append(connErrors, err)
}
return conn, nil
}
return nil, E.Errors(connErrors...)
} }
func (d *ResolveDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { func (d *ResolveDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
@ -67,16 +57,7 @@ func (d *ResolveDialer) ListenPacket(ctx context.Context, destination M.Socksadd
if err != nil { if err != nil {
return nil, err return nil, err
} }
var conn net.PacketConn return ListenSerial(ctx, d.dialer, destination, addresses)
var connErrors []error
for _, address := range addresses {
conn, err = d.dialer.ListenPacket(ctx, M.SocksaddrFromAddrPort(address, destination.Port))
if err != nil {
connErrors = append(connErrors, err)
}
return conn, nil
}
return nil, E.Errors(connErrors...)
} }
func (d *ResolveDialer) Upstream() any { func (d *ResolveDialer) Upstream() any {

39
common/dialer/serial.go Normal file
View file

@ -0,0 +1,39 @@
package dialer
import (
"context"
"net"
"net/netip"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
func DialSerial(ctx context.Context, dialer N.Dialer, network string, destination M.Socksaddr, destinationAddresses []netip.Addr) (net.Conn, error) {
var conn net.Conn
var err error
var connErrors []error
for _, address := range destinationAddresses {
conn, err = dialer.DialContext(ctx, network, M.SocksaddrFromAddrPort(address, destination.Port))
if err != nil {
connErrors = append(connErrors, err)
}
return conn, nil
}
return nil, E.Errors(connErrors...)
}
func ListenSerial(ctx context.Context, dialer N.Dialer, destination M.Socksaddr, destinationAddresses []netip.Addr) (net.PacketConn, error) {
var conn net.PacketConn
var err error
var connErrors []error
for _, address := range destinationAddresses {
conn, err = dialer.ListenPacket(ctx, M.SocksaddrFromAddrPort(address, destination.Port))
if err != nil {
connErrors = append(connErrors, err)
}
return conn, nil
}
return nil, E.Errors(connErrors...)
}

View file

@ -77,9 +77,14 @@ func (r *Reader) readMetadata() error {
} }
func (r *Reader) Read(code string) ([]Item, error) { func (r *Reader) Read(code string) ([]Item, error) {
if _, exists := r.domainIndex[code]; !exists { index, exists := r.domainIndex[code]
if !exists {
return nil, E.New("code ", code, " not exists!") return nil, E.New("code ", code, " not exists!")
} }
_, err := r.reader.Seek(int64(index), io.SeekCurrent)
if err != nil {
return nil, err
}
counter := &rw.ReadCounter{Reader: r.reader} counter := &rw.ReadCounter{Reader: r.reader}
domain := make([]Item, r.domainLength[code]) domain := make([]Item, r.domainLength[code])
for i := range domain { for i := range domain {
@ -97,7 +102,7 @@ func (r *Reader) Read(code string) ([]Item, error) {
} }
domain[i] = item domain[i] = item
} }
_, err := r.reader.Seek(int64(r.domainIndex[code])-counter.Count(), io.SeekCurrent) _, err = r.reader.Seek(int64(-index)-counter.Count(), io.SeekCurrent)
return domain, err return domain, err
} }

3
constant/cgo.go Normal file
View file

@ -0,0 +1,3 @@
package constant
const CGO_ENABLED = true

5
constant/cgo_disabled.go Normal file
View file

@ -0,0 +1,5 @@
//go:build !cgo
package constant
const CGO_ENABLED = false

View file

@ -4,11 +4,11 @@ import (
"context" "context"
"net" "net"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
) )
@ -31,16 +31,7 @@ func (d *DialerWrapper) DialContext(ctx context.Context, network string, destina
if err != nil { if err != nil {
return nil, err return nil, err
} }
var conn net.Conn return dialer.DialSerial(ctx, d.dialer, network, destination, addresses)
var connErrors []error
for _, address := range addresses {
conn, err = d.dialer.DialContext(ctx, network, M.SocksaddrFromAddrPort(address, destination.Port))
if err != nil {
connErrors = append(connErrors, err)
}
return conn, nil
}
return nil, E.Errors(connErrors...)
} }
func (d *DialerWrapper) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { func (d *DialerWrapper) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
@ -51,16 +42,7 @@ func (d *DialerWrapper) ListenPacket(ctx context.Context, destination M.Socksadd
if err != nil { if err != nil {
return nil, err return nil, err
} }
var conn net.PacketConn return dialer.ListenSerial(ctx, d.dialer, destination, addresses)
var connErrors []error
for _, address := range addresses {
conn, err = d.dialer.ListenPacket(ctx, M.SocksaddrFromAddrPort(address, destination.Port))
if err != nil {
connErrors = append(connErrors, err)
}
return conn, nil
}
return nil, E.Errors(connErrors...)
} }
func (d *DialerWrapper) Upstream() any { func (d *DialerWrapper) Upstream() any {

View file

@ -136,6 +136,7 @@ func (a *myInboundAdapter) loopTCPIn() {
metadata.Inbound = a.tag metadata.Inbound = a.tag
metadata.SniffEnabled = a.listenOptions.SniffEnabled metadata.SniffEnabled = a.listenOptions.SniffEnabled
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
metadata.DomainStrategy = C.DomainStrategy(a.listenOptions.DomainStrategy)
metadata.Network = C.NetworkTCP metadata.Network = C.NetworkTCP
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr()) metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr())
a.logger.WithContext(ctx).Info("inbound connection from ", metadata.Source) a.logger.WithContext(ctx).Info("inbound connection from ", metadata.Source)
@ -167,6 +168,7 @@ func (a *myInboundAdapter) loopUDPIn() {
metadata.Inbound = a.tag metadata.Inbound = a.tag
metadata.SniffEnabled = a.listenOptions.SniffEnabled metadata.SniffEnabled = a.listenOptions.SniffEnabled
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
metadata.DomainStrategy = C.DomainStrategy(a.listenOptions.DomainStrategy)
metadata.Network = C.NetworkUDP metadata.Network = C.NetworkUDP
metadata.Source = M.SocksaddrFromNetIP(addr) metadata.Source = M.SocksaddrFromNetIP(addr)
err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata) err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata)
@ -191,6 +193,7 @@ func (a *myInboundAdapter) loopUDPInThreadSafe() {
metadata.Inbound = a.tag metadata.Inbound = a.tag
metadata.SniffEnabled = a.listenOptions.SniffEnabled metadata.SniffEnabled = a.listenOptions.SniffEnabled
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
metadata.DomainStrategy = C.DomainStrategy(a.listenOptions.DomainStrategy)
metadata.Network = C.NetworkUDP metadata.Network = C.NetworkUDP
metadata.Source = M.SocksaddrFromNetIP(addr) metadata.Source = M.SocksaddrFromNetIP(addr)
err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata) err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata)

View file

@ -85,6 +85,7 @@ type ListenOptions struct {
UDPTimeout int64 `json:"udp_timeout,omitempty"` UDPTimeout int64 `json:"udp_timeout,omitempty"`
SniffEnabled bool `json:"sniff,omitempty"` SniffEnabled bool `json:"sniff,omitempty"`
SniffOverrideDestination bool `json:"sniff_override_destination,omitempty"` SniffOverrideDestination bool `json:"sniff_override_destination,omitempty"`
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
} }
type SimpleInboundOptions struct { type SimpleInboundOptions struct {

View file

@ -74,6 +74,10 @@ type DialerOptions struct {
ReuseAddr bool `json:"reuse_addr,omitempty"` ReuseAddr bool `json:"reuse_addr,omitempty"`
ConnectTimeout int `json:"connect_timeout,omitempty"` ConnectTimeout int `json:"connect_timeout,omitempty"`
TCPFastOpen bool `json:"tcp_fast_open,omitempty"` TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
}
type OutboundDialerOptions struct {
DialerOptions
OverrideOptions *OverrideStreamOptions `json:"override,omitempty"` OverrideOptions *OverrideStreamOptions `json:"override,omitempty"`
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"` DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
} }
@ -99,13 +103,13 @@ func (o ServerOptions) Build() M.Socksaddr {
} }
type DirectOutboundOptions struct { type DirectOutboundOptions struct {
DialerOptions OutboundDialerOptions
OverrideAddress string `json:"override_address,omitempty"` OverrideAddress string `json:"override_address,omitempty"`
OverridePort uint16 `json:"override_port,omitempty"` OverridePort uint16 `json:"override_port,omitempty"`
} }
type SocksOutboundOptions struct { type SocksOutboundOptions struct {
DialerOptions OutboundDialerOptions
ServerOptions ServerOptions
Version string `json:"version,omitempty"` Version string `json:"version,omitempty"`
Username string `json:"username,omitempty"` Username string `json:"username,omitempty"`
@ -114,14 +118,14 @@ type SocksOutboundOptions struct {
} }
type HTTPOutboundOptions struct { type HTTPOutboundOptions struct {
DialerOptions OutboundDialerOptions
ServerOptions ServerOptions
Username string `json:"username,omitempty"` Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"` Password string `json:"password,omitempty"`
} }
type ShadowsocksOutboundOptions struct { type ShadowsocksOutboundOptions struct {
DialerOptions OutboundDialerOptions
ServerOptions ServerOptions
Method string `json:"method"` Method string `json:"method"`
Password string `json:"password"` Password string `json:"password"`

View file

@ -100,13 +100,13 @@ func (s DomainStrategy) MarshalJSON() ([]byte, error) {
value = "" value = ""
// value = "AsIS" // value = "AsIS"
case C.DomainStrategyPreferIPv4: case C.DomainStrategyPreferIPv4:
value = "PreferIPv4" value = "prefer_ipv4"
case C.DomainStrategyPreferIPv6: case C.DomainStrategyPreferIPv6:
value = "PreferIPv6" value = "prefer_ipv6"
case C.DomainStrategyUseIPv4: case C.DomainStrategyUseIPv4:
value = "UseIPv4" value = "ipv4_only"
case C.DomainStrategyUseIPv6: case C.DomainStrategyUseIPv6:
value = "UseIPv6" value = "ipv6_only"
default: default:
return nil, E.New("unknown domain strategy: ", s) return nil, E.New("unknown domain strategy: ", s)
} }
@ -122,13 +122,13 @@ func (s *DomainStrategy) UnmarshalJSON(bytes []byte) error {
switch value { switch value {
case "", "AsIS": case "", "AsIS":
*s = DomainStrategy(C.DomainStrategyAsIS) *s = DomainStrategy(C.DomainStrategyAsIS)
case "PreferIPv4": case "prefer_ipv4":
*s = DomainStrategy(C.DomainStrategyPreferIPv4) *s = DomainStrategy(C.DomainStrategyPreferIPv4)
case "PreferIPv6": case "prefer_ipv6":
*s = DomainStrategy(C.DomainStrategyPreferIPv6) *s = DomainStrategy(C.DomainStrategyPreferIPv6)
case "UseIPv4": case "ipv4_only":
*s = DomainStrategy(C.DomainStrategyUseIPv4) *s = DomainStrategy(C.DomainStrategyUseIPv4)
case "UseIPv6": case "ipv6_only":
*s = DomainStrategy(C.DomainStrategyUseIPv6) *s = DomainStrategy(C.DomainStrategyUseIPv6)
default: default:
return E.New("unknown domain strategy: ", value) return E.New("unknown domain strategy: ", value)

View file

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

View file

@ -10,7 +10,11 @@ import (
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
) )
@ -33,6 +37,51 @@ func (a *myOutboundAdapter) Network() []string {
return a.network return a.network
} }
func NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata adapter.InboundContext) error {
ctx = adapter.WithContext(ctx, &metadata)
var outConn net.Conn
var err error
if len(metadata.DestinationAddresses) > 0 {
outConn, err = dialer.DialSerial(ctx, this, C.NetworkTCP, metadata.Destination, metadata.DestinationAddresses)
} else {
outConn, err = this.DialContext(ctx, C.NetworkTCP, metadata.Destination)
}
if err != nil {
return err
}
return bufio.CopyConn(ctx, conn, outConn)
}
func NewEarlyConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata adapter.InboundContext) error {
ctx = adapter.WithContext(ctx, &metadata)
var outConn net.Conn
var err error
if len(metadata.DestinationAddresses) > 0 {
outConn, err = dialer.DialSerial(ctx, this, C.NetworkTCP, metadata.Destination, metadata.DestinationAddresses)
} else {
outConn, err = this.DialContext(ctx, C.NetworkTCP, metadata.Destination)
}
if err != nil {
return err
}
return CopyEarlyConn(ctx, conn, outConn)
}
func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn, metadata adapter.InboundContext) error {
ctx = adapter.WithContext(ctx, &metadata)
var outConn net.PacketConn
var err error
if len(metadata.DestinationAddresses) > 0 {
outConn, err = dialer.ListenSerial(ctx, this, metadata.Destination, metadata.DestinationAddresses)
} else {
outConn, err = this.ListenPacket(ctx, metadata.Destination)
}
if err != nil {
return err
}
return bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(outConn))
}
func CopyEarlyConn(ctx context.Context, conn net.Conn, serverConn net.Conn) error { func CopyEarlyConn(ctx context.Context, conn net.Conn, serverConn net.Conn) error {
_payload := buf.StackNew() _payload := buf.StackNew()
payload := common.Dup(_payload) payload := common.Dup(_payload)

View file

@ -4,7 +4,6 @@ import (
"context" "context"
"net" "net"
"github.com/sagernet/sing/common/bufio"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
@ -32,7 +31,7 @@ func NewDirect(router adapter.Router, logger log.Logger, tag string, options opt
tag: tag, tag: tag,
network: []string{C.NetworkTCP, C.NetworkUDP}, network: []string{C.NetworkTCP, C.NetworkUDP},
}, },
dialer: dialer.New(router, options.DialerOptions), dialer: dialer.NewOutbound(router, options.OutboundDialerOptions),
} }
if options.OverrideAddress != "" && options.OverridePort != 0 { if options.OverrideAddress != "" && options.OverridePort != 0 {
outbound.overrideOption = 1 outbound.overrideOption = 1
@ -50,6 +49,7 @@ func NewDirect(router adapter.Router, logger log.Logger, tag string, options opt
func (h *Direct) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { func (h *Direct) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
ctx, metadata := adapter.AppendContext(ctx) ctx, metadata := adapter.AppendContext(ctx)
metadata.Outbound = h.tag metadata.Outbound = h.tag
metadata.Destination = destination
switch h.overrideOption { switch h.overrideOption {
case 1: case 1:
destination = h.overrideDestination destination = h.overrideDestination
@ -72,22 +72,15 @@ func (h *Direct) DialContext(ctx context.Context, network string, destination M.
func (h *Direct) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { func (h *Direct) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
ctx, metadata := adapter.AppendContext(ctx) ctx, metadata := adapter.AppendContext(ctx)
metadata.Outbound = h.tag metadata.Outbound = h.tag
metadata.Destination = destination
h.logger.WithContext(ctx).Info("outbound packet connection") h.logger.WithContext(ctx).Info("outbound packet connection")
return h.dialer.ListenPacket(ctx, destination) return h.dialer.ListenPacket(ctx, destination)
} }
func (h *Direct) NewConnection(ctx context.Context, conn net.Conn, destination M.Socksaddr) error { func (h *Direct) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
outConn, err := h.DialContext(ctx, C.NetworkTCP, destination) return NewConnection(ctx, h, conn, metadata)
if err != nil {
return err
}
return bufio.CopyConn(ctx, conn, outConn)
} }
func (h *Direct) NewPacketConnection(ctx context.Context, conn N.PacketConn, destination M.Socksaddr) error { func (h *Direct) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
outConn, err := h.ListenPacket(ctx, destination) return NewPacketConnection(ctx, h, conn, metadata)
if err != nil {
return err
}
return bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(outConn))
} }

View file

@ -5,7 +5,6 @@ import (
"net" "net"
"os" "os"
"github.com/sagernet/sing/common/bufio"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/protocol/http" "github.com/sagernet/sing/protocol/http"
@ -32,13 +31,14 @@ func NewHTTP(router adapter.Router, logger log.Logger, tag string, options optio
tag: tag, tag: tag,
network: []string{C.NetworkTCP}, network: []string{C.NetworkTCP},
}, },
http.NewClient(dialer.New(router, options.DialerOptions), options.ServerOptions.Build(), options.Username, options.Password), http.NewClient(dialer.NewOutbound(router, options.OutboundDialerOptions), options.ServerOptions.Build(), options.Username, options.Password),
} }
} }
func (h *HTTP) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { func (h *HTTP) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
ctx, metadata := adapter.AppendContext(ctx) ctx, metadata := adapter.AppendContext(ctx)
metadata.Outbound = h.tag metadata.Outbound = h.tag
metadata.Destination = destination
h.logger.WithContext(ctx).Info("outbound connection to ", destination) h.logger.WithContext(ctx).Info("outbound connection to ", destination)
return h.client.DialContext(ctx, network, destination) return h.client.DialContext(ctx, network, destination)
} }
@ -46,17 +46,14 @@ func (h *HTTP) DialContext(ctx context.Context, network string, destination M.So
func (h *HTTP) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { func (h *HTTP) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
ctx, metadata := adapter.AppendContext(ctx) ctx, metadata := adapter.AppendContext(ctx)
metadata.Outbound = h.tag metadata.Outbound = h.tag
metadata.Destination = destination
return nil, os.ErrInvalid return nil, os.ErrInvalid
} }
func (h *HTTP) NewConnection(ctx context.Context, conn net.Conn, destination M.Socksaddr) error { func (h *HTTP) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
outConn, err := h.DialContext(ctx, C.NetworkTCP, destination) return NewConnection(ctx, h, conn, metadata)
if err != nil {
return err
}
return bufio.CopyConn(ctx, conn, outConn)
} }
func (h *HTTP) NewPacketConnection(ctx context.Context, conn N.PacketConn, destination M.Socksaddr) error { func (h *HTTP) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
return os.ErrInvalid return os.ErrInvalid
} }

View file

@ -39,7 +39,7 @@ func NewShadowsocks(router adapter.Router, logger log.Logger, tag string, option
tag: tag, tag: tag,
network: options.Network.Build(), network: options.Network.Build(),
}, },
dialer.New(router, options.DialerOptions), dialer.NewOutbound(router, options.OutboundDialerOptions),
method, method,
options.ServerOptions.Build(), options.ServerOptions.Build(),
}, nil }, nil
@ -48,6 +48,7 @@ func NewShadowsocks(router adapter.Router, logger log.Logger, tag string, option
func (h *Shadowsocks) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { func (h *Shadowsocks) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
ctx, metadata := adapter.AppendContext(ctx) ctx, metadata := adapter.AppendContext(ctx)
metadata.Outbound = h.tag metadata.Outbound = h.tag
metadata.Destination = destination
switch network { switch network {
case C.NetworkTCP: case C.NetworkTCP:
h.logger.WithContext(ctx).Info("outbound connection to ", destination) h.logger.WithContext(ctx).Info("outbound connection to ", destination)
@ -71,6 +72,7 @@ func (h *Shadowsocks) DialContext(ctx context.Context, network string, destinati
func (h *Shadowsocks) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { func (h *Shadowsocks) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
ctx, metadata := adapter.AppendContext(ctx) ctx, metadata := adapter.AppendContext(ctx)
metadata.Outbound = h.tag metadata.Outbound = h.tag
metadata.Destination = destination
h.logger.WithContext(ctx).Info("outbound packet connection to ", h.serverAddr) h.logger.WithContext(ctx).Info("outbound packet connection to ", h.serverAddr)
outConn, err := h.dialer.ListenPacket(ctx, destination) outConn, err := h.dialer.ListenPacket(ctx, destination)
if err != nil { if err != nil {
@ -79,18 +81,10 @@ func (h *Shadowsocks) ListenPacket(ctx context.Context, destination M.Socksaddr)
return h.method.DialPacketConn(&bufio.BindPacketConn{PacketConn: outConn, Addr: h.serverAddr.UDPAddr()}), nil return h.method.DialPacketConn(&bufio.BindPacketConn{PacketConn: outConn, Addr: h.serverAddr.UDPAddr()}), nil
} }
func (h *Shadowsocks) NewConnection(ctx context.Context, conn net.Conn, destination M.Socksaddr) error { func (h *Shadowsocks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
serverConn, err := h.DialContext(ctx, C.NetworkTCP, destination) return NewEarlyConnection(ctx, h, conn, metadata)
if err != nil {
return err
}
return CopyEarlyConn(ctx, conn, serverConn)
} }
func (h *Shadowsocks) NewPacketConnection(ctx context.Context, conn N.PacketConn, destination M.Socksaddr) error { func (h *Shadowsocks) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
serverConn, err := h.ListenPacket(ctx, destination) return NewPacketConnection(ctx, h, conn, metadata)
if err != nil {
return err
}
return bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(serverConn))
} }

View file

@ -4,7 +4,6 @@ import (
"context" "context"
"net" "net"
"github.com/sagernet/sing/common/bufio"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/protocol/socks" "github.com/sagernet/sing/protocol/socks"
@ -24,7 +23,7 @@ type Socks struct {
} }
func NewSocks(router adapter.Router, logger log.Logger, tag string, options option.SocksOutboundOptions) (*Socks, error) { func NewSocks(router adapter.Router, logger log.Logger, tag string, options option.SocksOutboundOptions) (*Socks, error) {
detour := dialer.New(router, options.DialerOptions) detour := dialer.NewOutbound(router, options.OutboundDialerOptions)
var version socks.Version var version socks.Version
var err error var err error
if options.Version != "" { if options.Version != "" {
@ -49,6 +48,7 @@ func NewSocks(router adapter.Router, logger log.Logger, tag string, options opti
func (h *Socks) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { func (h *Socks) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
ctx, metadata := adapter.AppendContext(ctx) ctx, metadata := adapter.AppendContext(ctx)
metadata.Outbound = h.tag metadata.Outbound = h.tag
metadata.Destination = destination
switch network { switch network {
case C.NetworkTCP: case C.NetworkTCP:
h.logger.WithContext(ctx).Info("outbound connection to ", destination) h.logger.WithContext(ctx).Info("outbound connection to ", destination)
@ -63,22 +63,15 @@ func (h *Socks) DialContext(ctx context.Context, network string, destination M.S
func (h *Socks) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { func (h *Socks) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
ctx, metadata := adapter.AppendContext(ctx) ctx, metadata := adapter.AppendContext(ctx)
metadata.Outbound = h.tag metadata.Outbound = h.tag
metadata.Destination = destination
h.logger.WithContext(ctx).Info("outbound packet connection to ", destination) h.logger.WithContext(ctx).Info("outbound packet connection to ", destination)
return h.client.ListenPacket(ctx, destination) return h.client.ListenPacket(ctx, destination)
} }
func (h *Socks) NewConnection(ctx context.Context, conn net.Conn, destination M.Socksaddr) error { func (h *Socks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
outConn, err := h.DialContext(ctx, C.NetworkTCP, destination) return NewConnection(ctx, h, conn, metadata)
if err != nil {
return err
}
return bufio.CopyConn(ctx, conn, outConn)
} }
func (h *Socks) NewPacketConnection(ctx context.Context, conn N.PacketConn, destination M.Socksaddr) error { func (h *Socks) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
outConn, err := h.ListenPacket(ctx, destination) return NewPacketConnection(ctx, h, conn, metadata)
if err != nil {
return err
}
return bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(outConn))
} }

View file

@ -56,6 +56,7 @@ type Router struct {
dnsClient adapter.DNSClient dnsClient adapter.DNSClient
defaultDomainStrategy C.DomainStrategy defaultDomainStrategy C.DomainStrategy
dnsRules []adapter.Rule
defaultTransport adapter.DNSTransport defaultTransport adapter.DNSTransport
transports []adapter.DNSTransport transports []adapter.DNSTransport
@ -69,9 +70,11 @@ func NewRouter(ctx context.Context, logger log.Logger, options option.RouteOptio
dnsLogger: logger.WithPrefix("dns: "), dnsLogger: logger.WithPrefix("dns: "),
outboundByTag: make(map[string]adapter.Outbound), outboundByTag: make(map[string]adapter.Outbound),
rules: make([]adapter.Rule, 0, len(options.Rules)), rules: make([]adapter.Rule, 0, len(options.Rules)),
dnsRules: make([]adapter.Rule, 0, len(dnsOptions.Rules)),
needGeoIPDatabase: hasGeoRule(options.Rules, isGeoIPRule) || hasGeoDNSRule(dnsOptions.Rules, isGeoIPDNSRule), needGeoIPDatabase: hasGeoRule(options.Rules, isGeoIPRule) || hasGeoDNSRule(dnsOptions.Rules, isGeoIPDNSRule),
needGeositeDatabase: hasGeoRule(options.Rules, isGeositeRule) || hasGeoDNSRule(dnsOptions.Rules, isGeositeDNSRule), needGeositeDatabase: hasGeoRule(options.Rules, isGeositeRule) || hasGeoDNSRule(dnsOptions.Rules, isGeositeDNSRule),
geoIPOptions: common.PtrValueOrDefault(options.GeoIP), geoIPOptions: common.PtrValueOrDefault(options.GeoIP),
geositeOptions: common.PtrValueOrDefault(options.Geosite),
defaultDetour: options.Final, defaultDetour: options.Final,
dnsClient: dns.NewClient(dnsOptions.DNSClientOptions), dnsClient: dns.NewClient(dnsOptions.DNSClientOptions),
defaultDomainStrategy: C.DomainStrategy(dnsOptions.Strategy), defaultDomainStrategy: C.DomainStrategy(dnsOptions.Strategy),
@ -88,7 +91,7 @@ func NewRouter(ctx context.Context, logger log.Logger, options option.RouteOptio
if err != nil { if err != nil {
return nil, E.Cause(err, "parse dns rule[", i, "]") return nil, E.Cause(err, "parse dns rule[", i, "]")
} }
router.rules = append(router.rules, dnsRule) router.dnsRules = append(router.dnsRules, dnsRule)
} }
transports := make([]adapter.DNSTransport, len(dnsOptions.Servers)) transports := make([]adapter.DNSTransport, len(dnsOptions.Servers))
dummyTransportMap := make(map[string]adapter.DNSTransport) dummyTransportMap := make(map[string]adapter.DNSTransport)
@ -259,6 +262,12 @@ func (r *Router) Start() error {
return err return err
} }
} }
for _, rule := range r.dnsRules {
err := rule.Start()
if err != nil {
return err
}
}
if r.needGeositeDatabase { if r.needGeositeDatabase {
for _, rule := range r.rules { for _, rule := range r.rules {
err := rule.UpdateGeosite() err := rule.UpdateGeosite()
@ -266,6 +275,12 @@ func (r *Router) Start() error {
r.logger.Error("failed to initialize geosite: ", err) r.logger.Error("failed to initialize geosite: ", err)
} }
} }
for _, rule := range r.dnsRules {
err := rule.UpdateGeosite()
if err != nil {
r.logger.Error("failed to initialize geosite: ", err)
}
}
err := common.Close(r.geositeReader) err := common.Close(r.geositeReader)
if err != nil { if err != nil {
return err return err
@ -275,6 +290,18 @@ func (r *Router) Start() error {
} }
func (r *Router) Close() error { func (r *Router) Close() error {
for _, rule := range r.rules {
err := rule.Close()
if err != nil {
return err
}
}
for _, rule := range r.dnsRules {
err := rule.Close()
if err != nil {
return err
}
}
return common.Close( return common.Close(
common.PtrOrNil(r.geoIPReader), common.PtrOrNil(r.geoIPReader),
) )
@ -325,12 +352,20 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
conn = bufio.NewCachedConn(conn, buffer) conn = bufio.NewCachedConn(conn, buffer)
} }
} }
if metadata.Destination.IsFqdn() && metadata.DomainStrategy != C.DomainStrategyAsIS {
addresses, err := r.Lookup(adapter.WithContext(ctx, &metadata), metadata.Destination.Fqdn, metadata.DomainStrategy)
if err != nil {
return err
}
metadata.DestinationAddresses = addresses
r.dnsLogger.WithContext(ctx).Info("resolved [", strings.Join(common.Map(metadata.DestinationAddresses, F.ToString0[netip.Addr]), " "), "]")
}
detour := r.match(ctx, metadata, r.defaultOutboundForConnection) detour := r.match(ctx, metadata, r.defaultOutboundForConnection)
if !common.Contains(detour.Network(), C.NetworkTCP) { if !common.Contains(detour.Network(), C.NetworkTCP) {
conn.Close() conn.Close()
return E.New("missing supported outbound, closing connection") return E.New("missing supported outbound, closing connection")
} }
return detour.NewConnection(adapter.WithContext(ctx, &metadata), conn, metadata.Destination) return detour.NewConnection(ctx, conn, metadata)
} }
func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
@ -359,12 +394,20 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
} }
conn = bufio.NewCachedPacketConn(conn, buffer, originDestination) conn = bufio.NewCachedPacketConn(conn, buffer, originDestination)
} }
if metadata.Destination.IsFqdn() && metadata.DomainStrategy != C.DomainStrategyAsIS {
addresses, err := r.Lookup(adapter.WithContext(ctx, &metadata), metadata.Destination.Fqdn, metadata.DomainStrategy)
if err != nil {
return err
}
metadata.DestinationAddresses = addresses
r.dnsLogger.WithContext(ctx).Info("resolved [", strings.Join(common.Map(metadata.DestinationAddresses, F.ToString0[netip.Addr]), " "), "]")
}
detour := r.match(ctx, metadata, r.defaultOutboundForPacketConnection) detour := r.match(ctx, metadata, r.defaultOutboundForPacketConnection)
if !common.Contains(detour.Network(), C.NetworkUDP) { if !common.Contains(detour.Network(), C.NetworkUDP) {
conn.Close() conn.Close()
return E.New("missing supported outbound, closing packet connection") return E.New("missing supported outbound, closing packet connection")
} }
return detour.NewPacketConnection(adapter.WithContext(ctx, &metadata), conn, metadata.Destination) return detour.NewPacketConnection(ctx, conn, metadata)
} }
func (r *Router) Exchange(ctx context.Context, message *dnsmessage.Message) (*dnsmessage.Message, error) { func (r *Router) Exchange(ctx context.Context, message *dnsmessage.Message) (*dnsmessage.Message, error) {
@ -397,10 +440,10 @@ func (r *Router) match(ctx context.Context, metadata adapter.InboundContext, def
func (r *Router) matchDNS(ctx context.Context) adapter.DNSTransport { func (r *Router) matchDNS(ctx context.Context) adapter.DNSTransport {
metadata := adapter.ContextFrom(ctx) metadata := adapter.ContextFrom(ctx)
if metadata == nil { if metadata == nil {
r.dnsLogger.WithContext(ctx).Info("no context") r.dnsLogger.WithContext(ctx).Warn("no context")
return r.defaultTransport return r.defaultTransport
} }
for i, rule := range r.rules { for i, rule := range r.dnsRules {
if rule.Match(metadata) { if rule.Match(metadata) {
detour := rule.Outbound() detour := rule.Outbound()
r.dnsLogger.WithContext(ctx).Info("match[", i, "] ", rule.String(), " => ", detour) r.dnsLogger.WithContext(ctx).Info("match[", i, "] ", rule.String(), " => ", detour)

View file

@ -41,14 +41,21 @@ func (r *IPCIDRItem) Match(metadata *adapter.InboundContext) bool {
} }
} }
} else { } else {
if metadata.Destination.IsFqdn() { if metadata.Destination.IsIP() {
return false
}
for _, prefix := range r.prefixes { for _, prefix := range r.prefixes {
if prefix.Contains(metadata.Destination.Addr) { if prefix.Contains(metadata.Destination.Addr) {
return true return true
} }
} }
} else {
for _, address := range metadata.DestinationAddresses {
for _, prefix := range r.prefixes {
if prefix.Contains(address) {
return true
}
}
}
}
} }
return false return false
} }

View file

@ -3,8 +3,6 @@ package route
import ( import (
"strings" "strings"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
) )
@ -36,42 +34,27 @@ func NewGeoIPItem(router adapter.Router, logger log.Logger, isSource bool, codes
func (r *GeoIPItem) Match(metadata *adapter.InboundContext) bool { func (r *GeoIPItem) Match(metadata *adapter.InboundContext) bool {
geoReader := r.router.GeoIPReader() geoReader := r.router.GeoIPReader()
if geoReader == nil { if geoReader == nil {
return r.match(metadata) return false
} }
if r.isSource { if r.isSource {
if metadata.SourceGeoIPCode == "" { if metadata.SourceGeoIPCode == "" {
metadata.SourceGeoIPCode = geoReader.Lookup(metadata.Source.Addr) metadata.SourceGeoIPCode = geoReader.Lookup(metadata.Source.Addr)
} }
return r.codeMap[metadata.SourceGeoIPCode]
} else { } else {
if metadata.Destination.IsFqdn() { if metadata.Destination.IsIP() {
return false
}
if metadata.GeoIPCode == "" { if metadata.GeoIPCode == "" {
metadata.GeoIPCode = geoReader.Lookup(metadata.Destination.Addr) metadata.GeoIPCode = geoReader.Lookup(metadata.Destination.Addr)
} }
}
return r.match(metadata)
}
func (r *GeoIPItem) match(metadata *adapter.InboundContext) bool {
if r.isSource {
if metadata.SourceGeoIPCode == "" {
if !N.IsPublicAddr(metadata.Source.Addr) {
metadata.SourceGeoIPCode = "private"
}
}
return r.codeMap[metadata.SourceGeoIPCode]
} else {
if metadata.Destination.IsFqdn() {
return false
}
if metadata.GeoIPCode == "" {
if !N.IsPublicAddr(metadata.Destination.Addr) {
metadata.GeoIPCode = "private"
}
}
return r.codeMap[metadata.GeoIPCode] return r.codeMap[metadata.GeoIPCode]
} }
for _, address := range metadata.DestinationAddresses {
if r.codeMap[geoReader.Lookup(address)] {
return true
}
}
return false
}
} }
func (r *GeoIPItem) String() string { func (r *GeoIPItem) String() string {