mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-05-14 00:51:12 +03:00
Update sing-box core
This commit is contained in:
@@ -122,7 +122,6 @@ func (h *inboundHandler) NewConnectionEx(ctx context.Context, conn net.Conn, sou
|
||||
//nolint:staticcheck
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
//nolint:staticcheck
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
metadata.Source = source
|
||||
metadata.Destination = destination.Unwrap()
|
||||
if userName, _ := auth.UserFromContext[string](ctx); userName != "" {
|
||||
|
||||
@@ -27,7 +27,7 @@ func RegisterOutbound(registry *outbound.Registry) {
|
||||
|
||||
type Outbound struct {
|
||||
outbound.Adapter
|
||||
dialer N.Dialer
|
||||
dialer tls.Dialer
|
||||
server M.Socksaddr
|
||||
tlsConfig tls.Config
|
||||
client *anytls.Client
|
||||
@@ -52,7 +52,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
return nil, E.New("tcp_fast_open is not supported with anytls outbound")
|
||||
}
|
||||
|
||||
tlsConfig, err := tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
tlsConfig, err := tls.NewClient(ctx, logger, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -66,7 +66,8 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.dialer = outboundDialer
|
||||
|
||||
outbound.dialer = tls.NewDialer(outboundDialer, tlsConfig)
|
||||
|
||||
client, err := anytls.NewClient(ctx, anytls.ClientConfig{
|
||||
Password: options.Password,
|
||||
@@ -99,16 +100,7 @@ func (d anytlsDialer) ListenPacket(ctx context.Context, destination M.Socksaddr)
|
||||
}
|
||||
|
||||
func (h *Outbound) dialOut(ctx context.Context) (net.Conn, error) {
|
||||
conn, err := h.dialer.DialContext(ctx, N.NetworkTCP, h.server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConn, err := tls.ClientHandshake(ctx, conn, h.tlsConfig)
|
||||
if err != nil {
|
||||
common.Close(tlsConn, conn)
|
||||
return nil, err
|
||||
}
|
||||
return tlsConn, nil
|
||||
return h.dialer.DialTLSContext(ctx, h.server)
|
||||
}
|
||||
|
||||
func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
|
||||
@@ -111,7 +111,6 @@ func (i *Inbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
|
||||
//nolint:staticcheck
|
||||
metadata.InboundDetour = i.listener.ListenOptions().Detour
|
||||
//nolint:staticcheck
|
||||
metadata.InboundOptions = i.listener.ListenOptions().InboundOptions
|
||||
metadata.Source = source
|
||||
destination = i.listener.UDPAddr()
|
||||
switch i.overrideOption {
|
||||
|
||||
@@ -13,7 +13,9 @@ import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing-tun/ping"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
@@ -28,17 +30,17 @@ var (
|
||||
_ N.ParallelDialer = (*Outbound)(nil)
|
||||
_ dialer.ParallelNetworkDialer = (*Outbound)(nil)
|
||||
_ dialer.DirectDialer = (*Outbound)(nil)
|
||||
_ adapter.DirectRouteOutbound = (*Outbound)(nil)
|
||||
)
|
||||
|
||||
type Outbound struct {
|
||||
outbound.Adapter
|
||||
logger logger.ContextLogger
|
||||
dialer dialer.ParallelInterfaceDialer
|
||||
domainStrategy C.DomainStrategy
|
||||
fallbackDelay time.Duration
|
||||
overrideOption int
|
||||
overrideDestination M.Socksaddr
|
||||
isEmpty bool
|
||||
ctx context.Context
|
||||
logger logger.ContextLogger
|
||||
dialer dialer.ParallelInterfaceDialer
|
||||
domainStrategy C.DomainStrategy
|
||||
fallbackDelay time.Duration
|
||||
isEmpty bool
|
||||
// loopBack *loopBackDetector
|
||||
}
|
||||
|
||||
@@ -57,31 +59,20 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
return nil, err
|
||||
}
|
||||
outbound := &Outbound{
|
||||
Adapter: outbound.NewAdapterWithDialerOptions(C.TypeDirect, tag, []string{N.NetworkTCP, N.NetworkUDP}, options.DialerOptions),
|
||||
Adapter: outbound.NewAdapterWithDialerOptions(C.TypeDirect, tag, []string{N.NetworkTCP, N.NetworkUDP, N.NetworkICMP}, options.DialerOptions),
|
||||
ctx: ctx,
|
||||
logger: logger,
|
||||
//nolint:staticcheck
|
||||
domainStrategy: C.DomainStrategy(options.DomainStrategy),
|
||||
fallbackDelay: time.Duration(options.FallbackDelay),
|
||||
dialer: outboundDialer.(dialer.ParallelInterfaceDialer),
|
||||
//nolint:staticcheck
|
||||
isEmpty: reflect.DeepEqual(options.DialerOptions, option.DialerOptions{UDPFragmentDefault: true}) && options.OverrideAddress == "" && options.OverridePort == 0,
|
||||
isEmpty: reflect.DeepEqual(options.DialerOptions, option.DialerOptions{UDPFragmentDefault: true}),
|
||||
// loopBack: newLoopBackDetector(router),
|
||||
}
|
||||
//nolint:staticcheck
|
||||
if options.ProxyProtocol != 0 {
|
||||
return nil, E.New("Proxy Protocol is deprecated and removed in sing-box 1.6.0")
|
||||
}
|
||||
//nolint:staticcheck
|
||||
if options.OverrideAddress != "" && options.OverridePort != 0 {
|
||||
outbound.overrideOption = 1
|
||||
outbound.overrideDestination = M.ParseSocksaddrHostPort(options.OverrideAddress, options.OverridePort)
|
||||
} else if options.OverrideAddress != "" {
|
||||
outbound.overrideOption = 2
|
||||
outbound.overrideDestination = M.ParseSocksaddrHostPort(options.OverrideAddress, options.OverridePort)
|
||||
} else if options.OverridePort != 0 {
|
||||
outbound.overrideOption = 3
|
||||
outbound.overrideDestination = M.Socksaddr{Port: options.OverridePort}
|
||||
}
|
||||
return outbound, nil
|
||||
}
|
||||
|
||||
@@ -89,16 +80,6 @@ func (h *Outbound) DialContext(ctx context.Context, network string, destination
|
||||
ctx, metadata := adapter.ExtendContext(ctx)
|
||||
metadata.Outbound = h.Tag()
|
||||
metadata.Destination = destination
|
||||
switch h.overrideOption {
|
||||
case 1:
|
||||
destination = h.overrideDestination
|
||||
case 2:
|
||||
newDestination := h.overrideDestination
|
||||
newDestination.Port = destination.Port
|
||||
destination = newDestination
|
||||
case 3:
|
||||
destination.Port = h.overrideDestination.Port
|
||||
}
|
||||
network = N.NetworkName(network)
|
||||
switch network {
|
||||
case N.NetworkTCP:
|
||||
@@ -118,44 +99,29 @@ func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
|
||||
ctx, metadata := adapter.ExtendContext(ctx)
|
||||
metadata.Outbound = h.Tag()
|
||||
metadata.Destination = destination
|
||||
originDestination := destination
|
||||
switch h.overrideOption {
|
||||
case 1:
|
||||
destination = h.overrideDestination
|
||||
case 2:
|
||||
newDestination := h.overrideDestination
|
||||
newDestination.Port = destination.Port
|
||||
destination = newDestination
|
||||
case 3:
|
||||
destination.Port = h.overrideDestination.Port
|
||||
}
|
||||
if h.overrideOption == 0 {
|
||||
h.logger.InfoContext(ctx, "outbound packet connection")
|
||||
} else {
|
||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||
}
|
||||
h.logger.InfoContext(ctx, "outbound packet connection")
|
||||
conn, err := h.dialer.ListenPacket(ctx, destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// conn = h.loopBack.NewPacketConn(bufio.NewPacketConn(conn), destination)
|
||||
if originDestination != destination {
|
||||
conn = bufio.NewNATPacketConn(bufio.NewPacketConn(conn), destination, originDestination)
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (h *Outbound) NewDirectRouteConnection(metadata adapter.InboundContext, routeContext tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error) {
|
||||
ctx := log.ContextWithNewID(h.ctx)
|
||||
destination, err := ping.ConnectDestination(ctx, h.logger, common.MustCast[*dialer.DefaultDialer](h.dialer).DialerForICMPDestination(metadata.Destination.Addr).Control, metadata.Destination.Addr, routeContext, timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h.logger.InfoContext(ctx, "linked ", metadata.Network, " connection from ", metadata.Source.AddrString(), " to ", metadata.Destination.AddrString())
|
||||
return destination, nil
|
||||
}
|
||||
|
||||
func (h *Outbound) DialParallel(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr) (net.Conn, error) {
|
||||
ctx, metadata := adapter.ExtendContext(ctx)
|
||||
metadata.Outbound = h.Tag()
|
||||
metadata.Destination = destination
|
||||
switch h.overrideOption {
|
||||
case 1, 2:
|
||||
// override address
|
||||
return h.DialContext(ctx, network, destination)
|
||||
case 3:
|
||||
destination.Port = h.overrideDestination.Port
|
||||
}
|
||||
network = N.NetworkName(network)
|
||||
switch network {
|
||||
case N.NetworkTCP:
|
||||
@@ -170,13 +136,6 @@ func (h *Outbound) DialParallelNetwork(ctx context.Context, network string, dest
|
||||
ctx, metadata := adapter.ExtendContext(ctx)
|
||||
metadata.Outbound = h.Tag()
|
||||
metadata.Destination = destination
|
||||
switch h.overrideOption {
|
||||
case 1, 2:
|
||||
// override address
|
||||
return h.DialContext(ctx, network, destination)
|
||||
case 3:
|
||||
destination.Port = h.overrideDestination.Port
|
||||
}
|
||||
network = N.NetworkName(network)
|
||||
switch network {
|
||||
case N.NetworkTCP:
|
||||
@@ -191,21 +150,7 @@ func (h *Outbound) ListenSerialNetworkPacket(ctx context.Context, destination M.
|
||||
ctx, metadata := adapter.ExtendContext(ctx)
|
||||
metadata.Outbound = h.Tag()
|
||||
metadata.Destination = destination
|
||||
switch h.overrideOption {
|
||||
case 1:
|
||||
destination = h.overrideDestination
|
||||
case 2:
|
||||
newDestination := h.overrideDestination
|
||||
newDestination.Port = destination.Port
|
||||
destination = newDestination
|
||||
case 3:
|
||||
destination.Port = h.overrideDestination.Port
|
||||
}
|
||||
if h.overrideOption == 0 {
|
||||
h.logger.InfoContext(ctx, "outbound packet connection")
|
||||
} else {
|
||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||
}
|
||||
h.logger.InfoContext(ctx, "outbound packet connection")
|
||||
conn, newDestination, err := dialer.ListenSerialNetworkPacket(ctx, h.dialer, destination, destinationAddresses, networkStrategy, networkType, fallbackNetworkType, fallbackDelay)
|
||||
if err != nil {
|
||||
return nil, netip.Addr{}, err
|
||||
|
||||
@@ -3,6 +3,7 @@ package group
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/adapter/outbound"
|
||||
@@ -10,6 +11,7 @@ import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
tun "github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
@@ -176,6 +178,14 @@ func (s *Selector) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Selector) NewDirectRouteConnection(metadata adapter.InboundContext, routeContext tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error) {
|
||||
selected := s.selected.Load()
|
||||
if !common.Contains(selected.Network(), metadata.Network) {
|
||||
return nil, E.New(metadata.Network, " is not supported by outbound: ", selected.Tag())
|
||||
}
|
||||
return selected.(adapter.DirectRouteOutbound).NewDirectRouteConnection(metadata, routeContext, timeout)
|
||||
}
|
||||
|
||||
func RealTag(detour adapter.Outbound) string {
|
||||
if group, isGroup := detour.(adapter.OutboundGroup); isGroup {
|
||||
return group.Now()
|
||||
|
||||
@@ -14,6 +14,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-tun"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/batch"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
@@ -172,6 +173,21 @@ func (s *URLTest) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
|
||||
s.connection.NewPacketConnection(ctx, s, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
func (s *URLTest) NewDirectRouteConnection(metadata adapter.InboundContext, routeContext tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error) {
|
||||
s.group.Touch()
|
||||
selected := s.group.selectedOutboundTCP
|
||||
if selected == nil {
|
||||
selected, _ = s.group.Select(N.NetworkTCP)
|
||||
}
|
||||
if selected == nil {
|
||||
return nil, E.New("missing supported outbound")
|
||||
}
|
||||
if !common.Contains(selected.Network(), metadata.Network) {
|
||||
return nil, E.New(metadata.Network, " is not supported by outbound: ", selected.Tag())
|
||||
}
|
||||
return selected.(adapter.DirectRouteOutbound).NewDirectRouteConnection(metadata, routeContext, timeout)
|
||||
}
|
||||
|
||||
type URLTestGroup struct {
|
||||
ctx context.Context
|
||||
router adapter.Router
|
||||
|
||||
@@ -43,7 +43,12 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
authenticator: auth.NewAuthenticator(options.Users),
|
||||
}
|
||||
if options.TLS != nil {
|
||||
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
tlsConfig, err := tls.NewServerWithOptions(tls.ServerOptions{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
Options: common.PtrValueOrDefault(options.TLS),
|
||||
KTLSCompatible: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
detour, err := tls.NewDialerFromOptions(ctx, router, outboundDialer, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
detour, err := tls.NewDialerFromOptions(ctx, logger, outboundDialer, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -118,7 +118,6 @@ func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, source M.S
|
||||
//nolint:staticcheck
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
//nolint:staticcheck
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
metadata.OriginDestination = h.listener.UDPAddr()
|
||||
metadata.Source = source
|
||||
metadata.Destination = destination
|
||||
@@ -141,7 +140,6 @@ func (h *Inbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
|
||||
//nolint:staticcheck
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
//nolint:staticcheck
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
metadata.OriginDestination = h.listener.UDPAddr()
|
||||
metadata.Source = source
|
||||
metadata.Destination = destination
|
||||
|
||||
@@ -43,7 +43,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
if options.TLS == nil || !options.TLS.Enabled {
|
||||
return nil, C.ErrTLSRequired
|
||||
}
|
||||
tlsConfig, err := tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
tlsConfig, err := tls.NewClient(ctx, logger, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -151,7 +151,6 @@ func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, source M.S
|
||||
//nolint:staticcheck
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
//nolint:staticcheck
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
metadata.OriginDestination = h.listener.UDPAddr()
|
||||
metadata.Source = source
|
||||
metadata.Destination = destination
|
||||
@@ -174,7 +173,6 @@ func (h *Inbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
|
||||
//nolint:staticcheck
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
//nolint:staticcheck
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
metadata.OriginDestination = h.listener.UDPAddr()
|
||||
metadata.Source = source
|
||||
metadata.Destination = destination
|
||||
|
||||
@@ -44,7 +44,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
if options.TLS == nil || !options.TLS.Enabled {
|
||||
return nil, C.ErrTLSRequired
|
||||
}
|
||||
tlsConfig, err := tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
tlsConfig, err := tls.NewClient(ctx, logger, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
std_bufio "bufio"
|
||||
"context"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/adapter/inbound"
|
||||
@@ -36,17 +37,30 @@ type Inbound struct {
|
||||
listener *listener.Listener
|
||||
authenticator *auth.Authenticator
|
||||
tlsConfig tls.ServerConfig
|
||||
udpTimeout time.Duration
|
||||
}
|
||||
|
||||
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPMixedInboundOptions) (adapter.Inbound, error) {
|
||||
var udpTimeout time.Duration
|
||||
if options.UDPTimeout != 0 {
|
||||
udpTimeout = time.Duration(options.UDPTimeout)
|
||||
} else {
|
||||
udpTimeout = C.UDPTimeout
|
||||
}
|
||||
inbound := &Inbound{
|
||||
Adapter: inbound.NewAdapter(C.TypeMixed, tag),
|
||||
router: uot.NewRouter(router, logger),
|
||||
logger: logger,
|
||||
authenticator: auth.NewAuthenticator(options.Users),
|
||||
udpTimeout: udpTimeout,
|
||||
}
|
||||
if options.TLS != nil {
|
||||
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
tlsConfig, err := tls.NewServerWithOptions(tls.ServerOptions{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
Options: common.PtrValueOrDefault(options.TLS),
|
||||
KTLSCompatible: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -111,7 +125,7 @@ func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata ada
|
||||
}
|
||||
switch headerBytes[0] {
|
||||
case socks4.Version, socks5.Version:
|
||||
return socks.HandleConnectionEx(ctx, conn, reader, h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), h.listener, metadata.Source, onClose)
|
||||
return socks.HandleConnectionEx(ctx, conn, reader, h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), h.listener, h.udpTimeout, metadata.Source, onClose)
|
||||
default:
|
||||
return http.HandleConnectionEx(ctx, conn, reader, h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import (
|
||||
"golang.org/x/net/http2/h2c"
|
||||
)
|
||||
|
||||
var ConfigureHTTP3ListenerFunc func(listener *listener.Listener, handler http.Handler, tlsConfig tls.ServerConfig, logger logger.Logger) (io.Closer, error)
|
||||
var ConfigureHTTP3ListenerFunc func(ctx context.Context, logger logger.Logger, listener *listener.Listener, handler http.Handler, tlsConfig tls.ServerConfig, options option.NaiveInboundOptions) (io.Closer, error)
|
||||
|
||||
func RegisterInbound(registry *inbound.Registry) {
|
||||
inbound.Register[option.NaiveInboundOptions](registry, C.TypeNaive, NewInbound)
|
||||
@@ -40,6 +40,7 @@ type Inbound struct {
|
||||
ctx context.Context
|
||||
router adapter.ConnectionRouterEx
|
||||
logger logger.ContextLogger
|
||||
options option.NaiveInboundOptions
|
||||
listener *listener.Listener
|
||||
network []string
|
||||
networkIsDefault bool
|
||||
@@ -121,7 +122,7 @@ func (n *Inbound) Start(stage adapter.StartStage) error {
|
||||
}
|
||||
|
||||
if common.Contains(n.network, N.NetworkUDP) {
|
||||
http3Server, err := ConfigureHTTP3ListenerFunc(n.listener, n, n.tlsConfig, n.logger)
|
||||
http3Server, err := ConfigureHTTP3ListenerFunc(n.ctx, n.logger, n.listener, n, n.tlsConfig, n.options)
|
||||
if err == nil {
|
||||
n.h3Server = http3Server
|
||||
} else if len(n.network) > 1 {
|
||||
@@ -208,7 +209,6 @@ func (n *Inbound) newConnection(ctx context.Context, waitForClose bool, conn net
|
||||
//nolint:staticcheck
|
||||
metadata.InboundDetour = n.listener.ListenOptions().Detour
|
||||
//nolint:staticcheck
|
||||
metadata.InboundOptions = n.listener.ListenOptions().InboundOptions
|
||||
metadata.Source = source
|
||||
metadata.Destination = destination
|
||||
metadata.OriginDestination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
|
||||
|
||||
275
protocol/naive/outbound.go
Normal file
275
protocol/naive/outbound.go
Normal file
@@ -0,0 +1,275 @@
|
||||
//go:build with_naive_outbound
|
||||
|
||||
package naive
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/pem"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/cronet-go"
|
||||
_ "github.com/sagernet/cronet-go/all"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/adapter/outbound"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/dns"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/uot"
|
||||
"github.com/sagernet/sing/service"
|
||||
|
||||
mDNS "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func RegisterOutbound(registry *outbound.Registry) {
|
||||
outbound.Register[option.NaiveOutboundOptions](registry, C.TypeNaive, NewOutbound)
|
||||
}
|
||||
|
||||
type Outbound struct {
|
||||
outbound.Adapter
|
||||
ctx context.Context
|
||||
logger logger.ContextLogger
|
||||
client *cronet.NaiveClient
|
||||
uotClient *uot.Client
|
||||
}
|
||||
|
||||
func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.NaiveOutboundOptions) (adapter.Outbound, error) {
|
||||
if options.TLS == nil || !options.TLS.Enabled {
|
||||
return nil, C.ErrTLSRequired
|
||||
}
|
||||
if options.TLS.DisableSNI {
|
||||
return nil, E.New("disable_sni is not supported on naive outbound")
|
||||
}
|
||||
if options.TLS.Insecure {
|
||||
return nil, E.New("insecure is not supported on naive outbound")
|
||||
}
|
||||
if len(options.TLS.ALPN) > 0 {
|
||||
return nil, E.New("alpn is not supported on naive outbound")
|
||||
}
|
||||
if options.TLS.MinVersion != "" {
|
||||
return nil, E.New("min_version is not supported on naive outbound")
|
||||
}
|
||||
if options.TLS.MaxVersion != "" {
|
||||
return nil, E.New("max_version is not supported on naive outbound")
|
||||
}
|
||||
if len(options.TLS.CipherSuites) > 0 {
|
||||
return nil, E.New("cipher_suites is not supported on naive outbound")
|
||||
}
|
||||
if len(options.TLS.CurvePreferences) > 0 {
|
||||
return nil, E.New("curve_preferences is not supported on naive outbound")
|
||||
}
|
||||
if len(options.TLS.ClientCertificate) > 0 || options.TLS.ClientCertificatePath != "" {
|
||||
return nil, E.New("client_certificate is not supported on naive outbound")
|
||||
}
|
||||
if len(options.TLS.ClientKey) > 0 || options.TLS.ClientKeyPath != "" {
|
||||
return nil, E.New("client_key is not supported on naive outbound")
|
||||
}
|
||||
if options.TLS.Fragment || options.TLS.RecordFragment {
|
||||
return nil, E.New("fragment is not supported on naive outbound")
|
||||
}
|
||||
if options.TLS.KernelTx || options.TLS.KernelRx {
|
||||
return nil, E.New("kernel TLS is not supported on naive outbound")
|
||||
}
|
||||
if options.TLS.UTLS != nil && options.TLS.UTLS.Enabled {
|
||||
return nil, E.New("uTLS is not supported on naive outbound")
|
||||
}
|
||||
if options.TLS.Reality != nil && options.TLS.Reality.Enabled {
|
||||
return nil, E.New("reality is not supported on naive outbound")
|
||||
}
|
||||
|
||||
serverAddress := options.ServerOptions.Build()
|
||||
|
||||
var serverName string
|
||||
if options.TLS.ServerName != "" {
|
||||
serverName = options.TLS.ServerName
|
||||
} else {
|
||||
serverName = serverAddress.AddrString()
|
||||
}
|
||||
|
||||
outboundDialer, err := dialer.NewWithOptions(dialer.Options{
|
||||
Context: ctx,
|
||||
Options: options.DialerOptions,
|
||||
RemoteIsDomain: true,
|
||||
ResolverOnDetour: true,
|
||||
NewDialer: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var trustedRootCertificates string
|
||||
if len(options.TLS.Certificate) > 0 {
|
||||
trustedRootCertificates = strings.Join(options.TLS.Certificate, "\n")
|
||||
} else if options.TLS.CertificatePath != "" {
|
||||
content, err := os.ReadFile(options.TLS.CertificatePath)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "read certificate")
|
||||
}
|
||||
trustedRootCertificates = string(content)
|
||||
}
|
||||
|
||||
extraHeaders := make(map[string]string)
|
||||
for key, values := range options.ExtraHeaders.Build() {
|
||||
if len(values) > 0 {
|
||||
extraHeaders[key] = values[0]
|
||||
}
|
||||
}
|
||||
|
||||
dnsRouter := service.FromContext[adapter.DNSRouter](ctx)
|
||||
var dnsResolver cronet.DNSResolverFunc
|
||||
if dnsRouter != nil {
|
||||
dnsResolver = func(dnsContext context.Context, request *mDNS.Msg) *mDNS.Msg {
|
||||
response, err := dnsRouter.Exchange(dnsContext, request, outboundDialer.(dialer.ResolveDialer).QueryOptions())
|
||||
if err != nil {
|
||||
logger.Error("DNS exchange failed: ", err)
|
||||
return dns.FixedResponseStatus(request, mDNS.RcodeServerFailure)
|
||||
}
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
||||
var echEnabled bool
|
||||
var echConfigList []byte
|
||||
var echQueryServerName string
|
||||
if options.TLS.ECH != nil && options.TLS.ECH.Enabled {
|
||||
echEnabled = true
|
||||
echQueryServerName = options.TLS.ECH.QueryServerName
|
||||
var echConfig []byte
|
||||
if len(options.TLS.ECH.Config) > 0 {
|
||||
echConfig = []byte(strings.Join(options.TLS.ECH.Config, "\n"))
|
||||
} else if options.TLS.ECH.ConfigPath != "" {
|
||||
content, err := os.ReadFile(options.TLS.ECH.ConfigPath)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "read ECH config")
|
||||
}
|
||||
echConfig = content
|
||||
}
|
||||
if len(echConfig) > 0 {
|
||||
block, rest := pem.Decode(echConfig)
|
||||
if block == nil || block.Type != "ECH CONFIGS" || len(rest) > 0 {
|
||||
return nil, E.New("invalid ECH configs pem")
|
||||
}
|
||||
echConfigList = block.Bytes
|
||||
}
|
||||
}
|
||||
var quicCongestionControl cronet.QUICCongestionControl
|
||||
switch options.QUICCongestionControl {
|
||||
case "":
|
||||
quicCongestionControl = cronet.QUICCongestionControlDefault
|
||||
case "bbr":
|
||||
quicCongestionControl = cronet.QUICCongestionControlBBR
|
||||
case "bbr2":
|
||||
quicCongestionControl = cronet.QUICCongestionControlBBRv2
|
||||
case "cubic":
|
||||
quicCongestionControl = cronet.QUICCongestionControlCubic
|
||||
case "reno":
|
||||
quicCongestionControl = cronet.QUICCongestionControlReno
|
||||
default:
|
||||
return nil, E.New("unknown quic congestion control: ", options.QUICCongestionControl)
|
||||
}
|
||||
client, err := cronet.NewNaiveClient(cronet.NaiveClientOptions{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
ServerAddress: serverAddress,
|
||||
ServerName: serverName,
|
||||
Username: options.Username,
|
||||
Password: options.Password,
|
||||
InsecureConcurrency: options.InsecureConcurrency,
|
||||
ExtraHeaders: extraHeaders,
|
||||
TrustedRootCertificates: trustedRootCertificates,
|
||||
Dialer: outboundDialer,
|
||||
DNSResolver: dnsResolver,
|
||||
ECHEnabled: echEnabled,
|
||||
ECHConfigList: echConfigList,
|
||||
ECHQueryServerName: echQueryServerName,
|
||||
QUIC: options.QUIC,
|
||||
QUICCongestionControl: quicCongestionControl,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var uotClient *uot.Client
|
||||
uotOptions := common.PtrValueOrDefault(options.UDPOverTCP)
|
||||
if uotOptions.Enabled {
|
||||
uotClient = &uot.Client{
|
||||
Dialer: &naiveDialer{client},
|
||||
Version: uotOptions.Version,
|
||||
}
|
||||
}
|
||||
var networks []string
|
||||
if uotClient != nil {
|
||||
networks = []string{N.NetworkTCP, N.NetworkUDP}
|
||||
} else {
|
||||
networks = []string{N.NetworkTCP}
|
||||
}
|
||||
return &Outbound{
|
||||
Adapter: outbound.NewAdapterWithDialerOptions(C.TypeNaive, tag, networks, options.DialerOptions),
|
||||
ctx: ctx,
|
||||
logger: logger,
|
||||
client: client,
|
||||
uotClient: uotClient,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *Outbound) Start(stage adapter.StartStage) error {
|
||||
if stage != adapter.StartStateStart {
|
||||
return nil
|
||||
}
|
||||
err := h.client.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.logger.Info("NaiveProxy started, version: ", h.client.Engine().Version())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
switch N.NetworkName(network) {
|
||||
case N.NetworkTCP:
|
||||
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
||||
return h.client.DialEarly(ctx, destination)
|
||||
case N.NetworkUDP:
|
||||
if h.uotClient == nil {
|
||||
return nil, E.New("UDP is not supported unless UDP over TCP is enabled")
|
||||
}
|
||||
h.logger.InfoContext(ctx, "outbound UoT packet connection to ", destination)
|
||||
return h.uotClient.DialContext(ctx, network, destination)
|
||||
default:
|
||||
return nil, E.Extend(N.ErrUnknownNetwork, network)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
if h.uotClient == nil {
|
||||
return nil, E.New("UDP is not supported unless UDP over TCP is enabled")
|
||||
}
|
||||
return h.uotClient.ListenPacket(ctx, destination)
|
||||
}
|
||||
|
||||
func (h *Outbound) InterfaceUpdated() {
|
||||
h.client.Engine().CloseAllConnections()
|
||||
}
|
||||
|
||||
func (h *Outbound) Close() error {
|
||||
return h.client.Close()
|
||||
}
|
||||
|
||||
func (h *Outbound) Client() *cronet.NaiveClient {
|
||||
return h.client
|
||||
}
|
||||
|
||||
type naiveDialer struct {
|
||||
*cronet.NaiveClient
|
||||
}
|
||||
|
||||
func (d *naiveDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
return d.NaiveClient.DialEarly(ctx, destination)
|
||||
}
|
||||
@@ -1,21 +1,31 @@
|
||||
package quic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/quic-go"
|
||||
"github.com/sagernet/quic-go/congestion"
|
||||
"github.com/sagernet/quic-go/http3"
|
||||
"github.com/sagernet/sing-box/common/listener"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/protocol/naive"
|
||||
"github.com/sagernet/sing-quic"
|
||||
"github.com/sagernet/sing-quic/congestion_bbr1"
|
||||
"github.com/sagernet/sing-quic/congestion_bbr2"
|
||||
congestion_meta1 "github.com/sagernet/sing-quic/congestion_meta1"
|
||||
congestion_meta2 "github.com/sagernet/sing-quic/congestion_meta2"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
"github.com/sagernet/sing/common/ntp"
|
||||
)
|
||||
|
||||
func init() {
|
||||
naive.ConfigureHTTP3ListenerFunc = func(listener *listener.Listener, handler http.Handler, tlsConfig tls.ServerConfig, logger logger.Logger) (io.Closer, error) {
|
||||
naive.ConfigureHTTP3ListenerFunc = func(ctx context.Context, logger logger.Logger, listener *listener.Listener, handler http.Handler, tlsConfig tls.ServerConfig, options option.NaiveInboundOptions) (io.Closer, error) {
|
||||
err := qtls.ConfigureHTTP3(tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -26,6 +36,67 @@ func init() {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var congestionControl func(conn *quic.Conn) congestion.CongestionControl
|
||||
timeFunc := ntp.TimeFuncFromContext(ctx)
|
||||
if timeFunc == nil {
|
||||
timeFunc = time.Now
|
||||
}
|
||||
switch options.QUICCongestionControl {
|
||||
case "", "bbr":
|
||||
congestionControl = func(conn *quic.Conn) congestion.CongestionControl {
|
||||
return congestion_meta2.NewBbrSender(
|
||||
congestion_meta2.DefaultClock{TimeFunc: timeFunc},
|
||||
congestion.ByteCount(conn.Config().InitialPacketSize),
|
||||
congestion.ByteCount(congestion_meta1.InitialCongestionWindow),
|
||||
)
|
||||
}
|
||||
case "bbr_standard":
|
||||
congestionControl = func(conn *quic.Conn) congestion.CongestionControl {
|
||||
return congestion_bbr1.NewBbrSender(
|
||||
congestion_bbr1.DefaultClock{TimeFunc: timeFunc},
|
||||
congestion.ByteCount(conn.Config().InitialPacketSize),
|
||||
congestion_bbr1.InitialCongestionWindowPackets,
|
||||
congestion_bbr1.MaxCongestionWindowPackets,
|
||||
)
|
||||
}
|
||||
case "bbr2":
|
||||
congestionControl = func(conn *quic.Conn) congestion.CongestionControl {
|
||||
return congestion_bbr2.NewBBR2Sender(
|
||||
congestion_bbr2.DefaultClock{TimeFunc: timeFunc},
|
||||
congestion.ByteCount(conn.Config().InitialPacketSize),
|
||||
0,
|
||||
false,
|
||||
)
|
||||
}
|
||||
case "bbr2_variant":
|
||||
congestionControl = func(conn *quic.Conn) congestion.CongestionControl {
|
||||
return congestion_bbr2.NewBBR2Sender(
|
||||
congestion_bbr2.DefaultClock{TimeFunc: timeFunc},
|
||||
congestion.ByteCount(conn.Config().InitialPacketSize),
|
||||
32*congestion.ByteCount(conn.Config().InitialPacketSize),
|
||||
true,
|
||||
)
|
||||
}
|
||||
case "cubic":
|
||||
congestionControl = func(conn *quic.Conn) congestion.CongestionControl {
|
||||
return congestion_meta1.NewCubicSender(
|
||||
congestion_meta1.DefaultClock{TimeFunc: timeFunc},
|
||||
congestion.ByteCount(conn.Config().InitialPacketSize),
|
||||
false,
|
||||
)
|
||||
}
|
||||
case "reno":
|
||||
congestionControl = func(conn *quic.Conn) congestion.CongestionControl {
|
||||
return congestion_meta1.NewCubicSender(
|
||||
congestion_meta1.DefaultClock{TimeFunc: timeFunc},
|
||||
congestion.ByteCount(conn.Config().InitialPacketSize),
|
||||
true,
|
||||
)
|
||||
}
|
||||
default:
|
||||
return nil, E.New("unknown quic congestion control: ", options.QUICCongestionControl)
|
||||
}
|
||||
|
||||
quicListener, err := qtls.ListenEarly(udpConn, tlsConfig, &quic.Config{
|
||||
MaxIncomingStreams: 1 << 60,
|
||||
Allow0RTT: true,
|
||||
@@ -37,6 +108,10 @@ func init() {
|
||||
|
||||
h3Server := &http3.Server{
|
||||
Handler: handler,
|
||||
ConnContext: func(ctx context.Context, conn *quic.Conn) context.Context {
|
||||
conn.SetCongestionControl(congestionControl(conn))
|
||||
return log.ContextWithNewID(ctx)
|
||||
},
|
||||
}
|
||||
|
||||
go func() {
|
||||
|
||||
@@ -175,7 +175,6 @@ func (h *MultiInbound) newConnection(ctx context.Context, conn net.Conn, metadat
|
||||
//nolint:staticcheck
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
//nolint:staticcheck
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
if h.tracker != nil {
|
||||
conn = h.tracker.TrackConnection(conn, metadata)
|
||||
}
|
||||
@@ -201,7 +200,6 @@ func (h *MultiInbound) newPacketConnection(ctx context.Context, conn N.PacketCon
|
||||
//nolint:staticcheck
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
//nolint:staticcheck
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
if h.tracker != nil {
|
||||
conn = h.tracker.TrackPacketConnection(conn, metadata)
|
||||
}
|
||||
|
||||
@@ -135,7 +135,6 @@ func (h *RelayInbound) newConnection(ctx context.Context, conn net.Conn, metadat
|
||||
//nolint:staticcheck
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
//nolint:staticcheck
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
return h.router.RouteConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
@@ -158,7 +157,6 @@ func (h *RelayInbound) newPacketConnection(ctx context.Context, conn N.PacketCon
|
||||
//nolint:staticcheck
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
//nolint:staticcheck
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
|
||||
@@ -129,7 +129,6 @@ func (h *inboundHandler) NewConnectionEx(ctx context.Context, conn net.Conn, sou
|
||||
//nolint:staticcheck
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
//nolint:staticcheck
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
metadata.Source = source
|
||||
metadata.Destination = destination
|
||||
if userName, _ := auth.UserFromContext[string](ctx); userName != "" {
|
||||
|
||||
@@ -43,7 +43,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
options.TLS.MinVersion = "1.2"
|
||||
options.TLS.MaxVersion = "1.2"
|
||||
}
|
||||
tlsConfig, err := tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
tlsConfig, err := tls.NewClient(ctx, logger, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -61,7 +61,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
return common.Error(tls.ClientHandshake(ctx, conn, tlsConfig))
|
||||
}
|
||||
} else {
|
||||
stdTLSConfig, err := tlsConfig.Config()
|
||||
stdTLSConfig, err := tlsConfig.STDConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
std_bufio "bufio"
|
||||
"context"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/adapter/inbound"
|
||||
@@ -31,14 +32,22 @@ type Inbound struct {
|
||||
logger logger.ContextLogger
|
||||
listener *listener.Listener
|
||||
authenticator *auth.Authenticator
|
||||
udpTimeout time.Duration
|
||||
}
|
||||
|
||||
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SocksInboundOptions) (adapter.Inbound, error) {
|
||||
var udpTimeout time.Duration
|
||||
if options.UDPTimeout != 0 {
|
||||
udpTimeout = time.Duration(options.UDPTimeout)
|
||||
} else {
|
||||
udpTimeout = C.UDPTimeout
|
||||
}
|
||||
inbound := &Inbound{
|
||||
Adapter: inbound.NewAdapter(C.TypeSOCKS, tag),
|
||||
router: uot.NewRouter(router, logger),
|
||||
logger: logger,
|
||||
authenticator: auth.NewAuthenticator(options.Users),
|
||||
udpTimeout: udpTimeout,
|
||||
}
|
||||
inbound.listener = listener.New(listener.Options{
|
||||
Context: ctx,
|
||||
@@ -62,7 +71,7 @@ func (h *Inbound) Close() error {
|
||||
}
|
||||
|
||||
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
err := socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), h.listener, metadata.Source, onClose)
|
||||
err := socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), h.listener, h.udpTimeout, metadata.Source, onClose)
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil {
|
||||
if E.IsClosedOrCanceled(err) {
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -47,8 +46,6 @@ type DNSTransport struct {
|
||||
acceptDefaultResolvers bool
|
||||
dnsRouter adapter.DNSRouter
|
||||
endpointManager adapter.EndpointManager
|
||||
cfg *wgcfg.Config
|
||||
dnsCfg *nDNS.Config
|
||||
endpoint *Endpoint
|
||||
routePrefixes []netip.Prefix
|
||||
routes map[string][]adapter.DNSTransport
|
||||
@@ -83,10 +80,10 @@ func (t *DNSTransport) Start(stage adapter.StartStage) error {
|
||||
if !isTailscale {
|
||||
return E.New("endpoint is not Tailscale: ", t.endpointTag)
|
||||
}
|
||||
if ep.onReconfig != nil {
|
||||
if ep.onReconfigHook != nil {
|
||||
return E.New("only one Tailscale DNS server is allowed for single endpoint")
|
||||
}
|
||||
ep.onReconfig = t.onReconfig
|
||||
ep.onReconfigHook = t.onReconfig
|
||||
t.endpoint = ep
|
||||
return nil
|
||||
}
|
||||
@@ -95,14 +92,6 @@ func (t *DNSTransport) Reset() {
|
||||
}
|
||||
|
||||
func (t *DNSTransport) onReconfig(cfg *wgcfg.Config, routerCfg *router.Config, dnsCfg *nDNS.Config) {
|
||||
if cfg == nil || dnsCfg == nil {
|
||||
return
|
||||
}
|
||||
if (t.cfg != nil && reflect.DeepEqual(t.cfg, cfg)) && (t.dnsCfg != nil && reflect.DeepEqual(t.dnsCfg, dnsCfg)) {
|
||||
return
|
||||
}
|
||||
t.cfg = cfg
|
||||
t.dnsCfg = dnsCfg
|
||||
err := t.updateDNSServers(routerCfg, dnsCfg)
|
||||
if err != nil {
|
||||
t.logger.Error(E.Cause(err, "update DNS servers"))
|
||||
@@ -177,7 +166,7 @@ func (t *DNSTransport) createResolver(directDialer func() N.Dialer, resolver *dn
|
||||
if serverAddr.Port == 0 {
|
||||
serverAddr.Port = 443
|
||||
}
|
||||
tlsConfig := common.Must1(tls.NewClient(t.ctx, serverAddr.AddrString(), option.OutboundTLSOptions{
|
||||
tlsConfig := common.Must1(tls.NewClient(t.ctx, t.logger, serverAddr.AddrString(), option.OutboundTLSOptions{
|
||||
ALPN: []string{http2.NextProtoTLS, "http/1.1"},
|
||||
}))
|
||||
return transport.NewHTTPSRaw(t.TransportAdapter, t.logger, myDialer, serverURL, http.Header{}, serverAddr, tlsConfig), nil
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
@@ -20,16 +21,16 @@ import (
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/adapters/gonet"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/udp"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/icmp"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/adapter/endpoint"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
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/route/rule"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing-tun/ping"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
@@ -41,15 +42,28 @@ import (
|
||||
"github.com/sagernet/sing/common/ntp"
|
||||
"github.com/sagernet/sing/service"
|
||||
"github.com/sagernet/sing/service/filemanager"
|
||||
_ "github.com/sagernet/tailscale/feature/relayserver"
|
||||
"github.com/sagernet/tailscale/ipn"
|
||||
tsDNS "github.com/sagernet/tailscale/net/dns"
|
||||
"github.com/sagernet/tailscale/net/netmon"
|
||||
"github.com/sagernet/tailscale/net/tsaddr"
|
||||
tsTUN "github.com/sagernet/tailscale/net/tstun"
|
||||
"github.com/sagernet/tailscale/tsnet"
|
||||
"github.com/sagernet/tailscale/types/ipproto"
|
||||
"github.com/sagernet/tailscale/types/nettype"
|
||||
"github.com/sagernet/tailscale/version"
|
||||
"github.com/sagernet/tailscale/wgengine"
|
||||
"github.com/sagernet/tailscale/wgengine/filter"
|
||||
"github.com/sagernet/tailscale/wgengine/router"
|
||||
"github.com/sagernet/tailscale/wgengine/wgcfg"
|
||||
|
||||
"go4.org/netipx"
|
||||
)
|
||||
|
||||
var (
|
||||
_ adapter.OutboundWithPreferredRoutes = (*Endpoint)(nil)
|
||||
_ adapter.DirectRouteOutbound = (*Endpoint)(nil)
|
||||
_ dialer.PacketDialerWithDestination = (*Endpoint)(nil)
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -67,19 +81,73 @@ type Endpoint struct {
|
||||
logger logger.ContextLogger
|
||||
dnsRouter adapter.DNSRouter
|
||||
network adapter.NetworkManager
|
||||
platformInterface platform.Interface
|
||||
platformInterface adapter.PlatformInterface
|
||||
server *tsnet.Server
|
||||
stack *stack.Stack
|
||||
icmpForwarder *tun.ICMPForwarder
|
||||
filter *atomic.Pointer[filter.Filter]
|
||||
onReconfig wgengine.ReconfigListener
|
||||
onReconfigHook wgengine.ReconfigListener
|
||||
|
||||
acceptRoutes bool
|
||||
exitNode string
|
||||
exitNodeAllowLANAccess bool
|
||||
advertiseRoutes []netip.Prefix
|
||||
advertiseExitNode bool
|
||||
cfg *wgcfg.Config
|
||||
dnsCfg *tsDNS.Config
|
||||
routeDomains common.TypedValue[map[string]bool]
|
||||
routePrefixes atomic.Pointer[netipx.IPSet]
|
||||
|
||||
acceptRoutes bool
|
||||
exitNode string
|
||||
exitNodeAllowLANAccess bool
|
||||
advertiseRoutes []netip.Prefix
|
||||
advertiseExitNode bool
|
||||
advertiseTags []string
|
||||
relayServerPort *uint16
|
||||
relayServerStaticEndpoints []netip.AddrPort
|
||||
|
||||
udpTimeout time.Duration
|
||||
|
||||
systemInterface bool
|
||||
systemInterfaceName string
|
||||
systemInterfaceMTU uint32
|
||||
systemTun tun.Tun
|
||||
fallbackTCPCloser func()
|
||||
}
|
||||
|
||||
func (t *Endpoint) registerNetstackHandlers() {
|
||||
netstack := t.server.ExportNetstack()
|
||||
if netstack == nil {
|
||||
return
|
||||
}
|
||||
previousTCP := netstack.GetTCPHandlerForFlow
|
||||
netstack.GetTCPHandlerForFlow = func(src, dst netip.AddrPort) (handler func(net.Conn), intercept bool) {
|
||||
if previousTCP != nil {
|
||||
handler, intercept = previousTCP(src, dst)
|
||||
if handler != nil || !intercept {
|
||||
return handler, intercept
|
||||
}
|
||||
}
|
||||
return func(conn net.Conn) {
|
||||
ctx := log.ContextWithNewID(t.ctx)
|
||||
source := M.SocksaddrFrom(src.Addr(), src.Port())
|
||||
destination := M.SocksaddrFrom(dst.Addr(), dst.Port())
|
||||
t.NewConnectionEx(ctx, conn, source, destination, nil)
|
||||
}, true
|
||||
}
|
||||
|
||||
previousUDP := netstack.GetUDPHandlerForFlow
|
||||
netstack.GetUDPHandlerForFlow = func(src, dst netip.AddrPort) (handler func(nettype.ConnPacketConn), intercept bool) {
|
||||
if previousUDP != nil {
|
||||
handler, intercept = previousUDP(src, dst)
|
||||
if handler != nil || !intercept {
|
||||
return handler, intercept
|
||||
}
|
||||
}
|
||||
return func(conn nettype.ConnPacketConn) {
|
||||
ctx := log.ContextWithNewID(t.ctx)
|
||||
source := M.SocksaddrFrom(src.Addr(), src.Port())
|
||||
destination := M.SocksaddrFrom(dst.Addr(), dst.Port())
|
||||
packetConn := bufio.NewPacketConn(conn)
|
||||
t.NewPacketConnectionEx(ctx, packetConn, source, destination, nil)
|
||||
}, true
|
||||
}
|
||||
}
|
||||
|
||||
func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TailscaleEndpointOptions) (adapter.Endpoint, error) {
|
||||
@@ -143,10 +211,11 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
UserLogf: func(format string, args ...any) {
|
||||
logger.Debug(fmt.Sprintf(format, args...))
|
||||
},
|
||||
Ephemeral: options.Ephemeral,
|
||||
AuthKey: options.AuthKey,
|
||||
ControlURL: options.ControlURL,
|
||||
Dialer: &endpointDialer{Dialer: outboundDialer, logger: logger},
|
||||
Ephemeral: options.Ephemeral,
|
||||
AuthKey: options.AuthKey,
|
||||
ControlURL: options.ControlURL,
|
||||
AdvertiseTags: options.AdvertiseTags,
|
||||
Dialer: &endpointDialer{Dialer: outboundDialer, logger: logger},
|
||||
LookupHook: func(ctx context.Context, host string) ([]netip.Addr, error) {
|
||||
return dnsRouter.Lookup(ctx, host, outboundDialer.(dialer.ResolveDialer).QueryOptions())
|
||||
},
|
||||
@@ -165,20 +234,26 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
},
|
||||
}
|
||||
return &Endpoint{
|
||||
Adapter: endpoint.NewAdapter(C.TypeTailscale, tag, []string{N.NetworkTCP, N.NetworkUDP}, nil),
|
||||
ctx: ctx,
|
||||
router: router,
|
||||
logger: logger,
|
||||
dnsRouter: dnsRouter,
|
||||
network: service.FromContext[adapter.NetworkManager](ctx),
|
||||
platformInterface: service.FromContext[platform.Interface](ctx),
|
||||
server: server,
|
||||
acceptRoutes: options.AcceptRoutes,
|
||||
exitNode: options.ExitNode,
|
||||
exitNodeAllowLANAccess: options.ExitNodeAllowLANAccess,
|
||||
advertiseRoutes: options.AdvertiseRoutes,
|
||||
advertiseExitNode: options.AdvertiseExitNode,
|
||||
udpTimeout: udpTimeout,
|
||||
Adapter: endpoint.NewAdapter(C.TypeTailscale, tag, []string{N.NetworkTCP, N.NetworkUDP, N.NetworkICMP}, nil),
|
||||
ctx: ctx,
|
||||
router: router,
|
||||
logger: logger,
|
||||
dnsRouter: dnsRouter,
|
||||
network: service.FromContext[adapter.NetworkManager](ctx),
|
||||
platformInterface: service.FromContext[adapter.PlatformInterface](ctx),
|
||||
server: server,
|
||||
acceptRoutes: options.AcceptRoutes,
|
||||
exitNode: options.ExitNode,
|
||||
exitNodeAllowLANAccess: options.ExitNodeAllowLANAccess,
|
||||
advertiseRoutes: options.AdvertiseRoutes,
|
||||
advertiseExitNode: options.AdvertiseExitNode,
|
||||
advertiseTags: options.AdvertiseTags,
|
||||
relayServerPort: options.RelayServerPort,
|
||||
relayServerStaticEndpoints: options.RelayServerStaticEndpoints,
|
||||
udpTimeout: udpTimeout,
|
||||
systemInterface: options.SystemInterface,
|
||||
systemInterfaceName: options.SystemInterfaceName,
|
||||
systemInterfaceMTU: options.SystemInterfaceMTU,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -214,13 +289,60 @@ func (t *Endpoint) Start(stage adapter.StartStage) error {
|
||||
setAndroidProtectFunc(t.platformInterface)
|
||||
}
|
||||
}
|
||||
if t.systemInterface {
|
||||
mtu := t.systemInterfaceMTU
|
||||
if mtu == 0 {
|
||||
mtu = uint32(tsTUN.DefaultTUNMTU())
|
||||
}
|
||||
tunName := t.systemInterfaceName
|
||||
if tunName == "" {
|
||||
tunName = tun.CalculateInterfaceName("tailscale")
|
||||
}
|
||||
tunOptions := tun.Options{
|
||||
Name: tunName,
|
||||
MTU: mtu,
|
||||
GSO: true,
|
||||
InterfaceScope: true,
|
||||
InterfaceMonitor: t.network.InterfaceMonitor(),
|
||||
InterfaceFinder: t.network.InterfaceFinder(),
|
||||
Logger: t.logger,
|
||||
EXP_ExternalConfiguration: true,
|
||||
}
|
||||
systemTun, err := tun.New(tunOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = systemTun.Start()
|
||||
if err != nil {
|
||||
_ = systemTun.Close()
|
||||
return err
|
||||
}
|
||||
wgTunDevice, err := newTunDeviceAdapter(systemTun, int(mtu), t.logger)
|
||||
if err != nil {
|
||||
_ = systemTun.Close()
|
||||
return err
|
||||
}
|
||||
t.systemTun = systemTun
|
||||
t.server.TunDevice = wgTunDevice
|
||||
}
|
||||
err := t.server.Start()
|
||||
if err != nil {
|
||||
if t.systemTun != nil {
|
||||
_ = t.systemTun.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
if t.onReconfig != nil {
|
||||
t.server.ExportLocalBackend().ExportEngine().(wgengine.ExportedUserspaceEngine).SetOnReconfigListener(t.onReconfig)
|
||||
if t.fallbackTCPCloser == nil {
|
||||
t.fallbackTCPCloser = t.server.RegisterFallbackTCPHandler(func(src, dst netip.AddrPort) (handler func(net.Conn), intercept bool) {
|
||||
return func(conn net.Conn) {
|
||||
ctx := log.ContextWithNewID(t.ctx)
|
||||
source := M.SocksaddrFrom(src.Addr(), src.Port())
|
||||
destination := M.SocksaddrFrom(dst.Addr(), dst.Port())
|
||||
t.NewConnectionEx(ctx, conn, source, destination, nil)
|
||||
}, true
|
||||
})
|
||||
}
|
||||
t.server.ExportLocalBackend().ExportEngine().(wgengine.ExportedUserspaceEngine).SetOnReconfigListener(t.onReconfig)
|
||||
|
||||
ipStack := t.server.ExportNetstack().ExportIPStack()
|
||||
gErr := ipStack.SetSpoofing(tun.DefaultNIC, true)
|
||||
@@ -231,32 +353,39 @@ func (t *Endpoint) Start(stage adapter.StartStage) error {
|
||||
if gErr != nil {
|
||||
return gonet.TranslateNetstackError(gErr)
|
||||
}
|
||||
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tun.NewTCPForwarder(t.ctx, ipStack, t).HandlePacket)
|
||||
udpForwarder := tun.NewUDPForwarder(t.ctx, ipStack, t, t.udpTimeout)
|
||||
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
|
||||
icmpForwarder := tun.NewICMPForwarder(t.ctx, ipStack, t, t.udpTimeout)
|
||||
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber4, icmpForwarder.HandlePacket)
|
||||
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber6, icmpForwarder.HandlePacket)
|
||||
t.stack = ipStack
|
||||
t.icmpForwarder = icmpForwarder
|
||||
t.registerNetstackHandlers()
|
||||
|
||||
localBackend := t.server.ExportLocalBackend()
|
||||
perfs := &ipn.MaskedPrefs{
|
||||
Prefs: ipn.Prefs{
|
||||
RouteAll: t.acceptRoutes,
|
||||
RouteAll: t.acceptRoutes,
|
||||
AdvertiseRoutes: t.advertiseRoutes,
|
||||
},
|
||||
RouteAllSet: true,
|
||||
ExitNodeIPSet: true,
|
||||
AdvertiseRoutesSet: true,
|
||||
}
|
||||
if len(t.advertiseRoutes) > 0 {
|
||||
perfs.AdvertiseRoutes = t.advertiseRoutes
|
||||
RouteAllSet: true,
|
||||
ExitNodeIPSet: true,
|
||||
AdvertiseRoutesSet: true,
|
||||
RelayServerPortSet: true,
|
||||
RelayServerStaticEndpointsSet: true,
|
||||
}
|
||||
if t.advertiseExitNode {
|
||||
perfs.AdvertiseRoutes = append(perfs.AdvertiseRoutes, tsaddr.ExitRoutes()...)
|
||||
}
|
||||
if t.relayServerPort != nil {
|
||||
perfs.RelayServerPort = t.relayServerPort
|
||||
}
|
||||
if len(t.relayServerStaticEndpoints) > 0 {
|
||||
perfs.RelayServerStaticEndpoints = t.relayServerStaticEndpoints
|
||||
}
|
||||
_, err = localBackend.EditPrefs(perfs)
|
||||
if err != nil {
|
||||
return E.Cause(err, "update prefs")
|
||||
}
|
||||
t.filter = localBackend.ExportFilter()
|
||||
|
||||
go t.watchState()
|
||||
return nil
|
||||
}
|
||||
@@ -271,7 +400,7 @@ func (t *Endpoint) watchState() {
|
||||
if authURL != "" {
|
||||
t.logger.Info("Waiting for authentication: ", authURL)
|
||||
if t.platformInterface != nil {
|
||||
err := t.platformInterface.SendNotification(&platform.Notification{
|
||||
err := t.platformInterface.SendNotification(&adapter.Notification{
|
||||
Identifier: "tailscale-authentication",
|
||||
TypeName: "Tailscale Authentication Notifications",
|
||||
TypeID: 10,
|
||||
@@ -324,6 +453,10 @@ func (t *Endpoint) Close() error {
|
||||
if runtime.GOOS == "android" {
|
||||
setAndroidProtectFunc(nil)
|
||||
}
|
||||
if t.fallbackTCPCloser != nil {
|
||||
t.fallbackTCPCloser()
|
||||
t.fallbackTCPCloser = nil
|
||||
}
|
||||
return common.Close(common.PtrOrNil(t.server))
|
||||
}
|
||||
|
||||
@@ -386,19 +519,7 @@ func (t *Endpoint) DialContext(ctx context.Context, network string, destination
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Endpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
t.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||
if destination.IsFqdn() {
|
||||
destinationAddresses, err := t.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
packetConn, _, err := N.ListenSerial(ctx, t, destination, destinationAddresses)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return packetConn, err
|
||||
}
|
||||
func (t *Endpoint) listenPacketWithAddress(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
addr4, addr6 := t.server.TailscaleIPs()
|
||||
bind := tcpip.FullAddress{
|
||||
NIC: 1,
|
||||
@@ -424,7 +545,45 @@ func (t *Endpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
|
||||
return udpConn, nil
|
||||
}
|
||||
|
||||
func (t *Endpoint) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr) error {
|
||||
func (t *Endpoint) ListenPacketWithDestination(ctx context.Context, destination M.Socksaddr) (net.PacketConn, netip.Addr, error) {
|
||||
t.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||
if destination.IsFqdn() {
|
||||
destinationAddresses, err := t.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{})
|
||||
if err != nil {
|
||||
return nil, netip.Addr{}, err
|
||||
}
|
||||
var errors []error
|
||||
for _, address := range destinationAddresses {
|
||||
packetConn, packetErr := t.listenPacketWithAddress(ctx, M.SocksaddrFrom(address, destination.Port))
|
||||
if packetErr == nil {
|
||||
return packetConn, address, nil
|
||||
}
|
||||
errors = append(errors, packetErr)
|
||||
}
|
||||
return nil, netip.Addr{}, E.Errors(errors...)
|
||||
}
|
||||
packetConn, err := t.listenPacketWithAddress(ctx, destination)
|
||||
if err != nil {
|
||||
return nil, netip.Addr{}, err
|
||||
}
|
||||
if destination.IsIP() {
|
||||
return packetConn, destination.Addr, nil
|
||||
}
|
||||
return packetConn, netip.Addr{}, nil
|
||||
}
|
||||
|
||||
func (t *Endpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
packetConn, destinationAddress, err := t.ListenPacketWithDestination(ctx, destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if destinationAddress.IsValid() && destination != M.SocksaddrFrom(destinationAddress, destination.Port) {
|
||||
return bufio.NewNATPacketConn(bufio.NewPacketConn(packetConn), M.SocksaddrFrom(destinationAddress, destination.Port), destination), nil
|
||||
}
|
||||
return packetConn, nil
|
||||
}
|
||||
|
||||
func (t *Endpoint) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr, routeContext tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error) {
|
||||
tsFilter := t.filter.Load()
|
||||
if tsFilter != nil {
|
||||
var ipProto ipproto.Proto
|
||||
@@ -433,22 +592,48 @@ func (t *Endpoint) PrepareConnection(network string, source M.Socksaddr, destina
|
||||
ipProto = ipproto.TCP
|
||||
case N.NetworkUDP:
|
||||
ipProto = ipproto.UDP
|
||||
case N.NetworkICMP:
|
||||
if !destination.IsIPv6() {
|
||||
ipProto = ipproto.ICMPv4
|
||||
} else {
|
||||
ipProto = ipproto.ICMPv6
|
||||
}
|
||||
}
|
||||
response := tsFilter.Check(source.Addr, destination.Addr, destination.Port, ipProto)
|
||||
switch response {
|
||||
case filter.Drop:
|
||||
return syscall.ECONNRESET
|
||||
return nil, syscall.ECONNREFUSED
|
||||
case filter.DropSilently:
|
||||
return tun.ErrDrop
|
||||
return nil, tun.ErrDrop
|
||||
}
|
||||
}
|
||||
return t.router.PreMatch(adapter.InboundContext{
|
||||
var ipVersion uint8
|
||||
if !destination.IsIPv6() {
|
||||
ipVersion = 4
|
||||
} else {
|
||||
ipVersion = 6
|
||||
}
|
||||
routeDestination, err := t.router.PreMatch(adapter.InboundContext{
|
||||
Inbound: t.Tag(),
|
||||
InboundType: t.Type(),
|
||||
IPVersion: ipVersion,
|
||||
Network: network,
|
||||
Source: source,
|
||||
Destination: destination,
|
||||
})
|
||||
}, routeContext, timeout, false)
|
||||
if err != nil {
|
||||
switch {
|
||||
case rule.IsBypassed(err):
|
||||
err = nil
|
||||
case rule.IsRejected(err):
|
||||
t.logger.Trace("reject ", network, " connection from ", source.AddrString(), " to ", destination.AddrString())
|
||||
default:
|
||||
if network == N.NetworkICMP {
|
||||
t.logger.Warn(E.Cause(err, "link ", network, " connection from ", source.AddrString(), " to ", destination.AddrString()))
|
||||
}
|
||||
}
|
||||
}
|
||||
return routeDestination, err
|
||||
}
|
||||
|
||||
func (t *Endpoint) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
@@ -491,10 +676,88 @@ func (t *Endpoint) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
|
||||
t.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
func (t *Endpoint) NewDirectRouteConnection(metadata adapter.InboundContext, routeContext tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error) {
|
||||
inet4Address, inet6Address := t.server.TailscaleIPs()
|
||||
if metadata.Destination.Addr.Is4() && !inet4Address.IsValid() || metadata.Destination.Addr.Is6() && !inet6Address.IsValid() {
|
||||
return nil, E.New("Tailscale is not ready yet")
|
||||
}
|
||||
ctx := log.ContextWithNewID(t.ctx)
|
||||
destination, err := ping.ConnectGVisor(
|
||||
ctx, t.logger,
|
||||
metadata.Source.Addr, metadata.Destination.Addr,
|
||||
routeContext,
|
||||
t.stack,
|
||||
inet4Address, inet6Address,
|
||||
timeout,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.logger.InfoContext(ctx, "linked ", metadata.Network, " connection from ", metadata.Source.AddrString(), " to ", metadata.Destination.AddrString())
|
||||
return destination, nil
|
||||
}
|
||||
|
||||
func (t *Endpoint) PreferredDomain(domain string) bool {
|
||||
routeDomains := t.routeDomains.Load()
|
||||
if routeDomains == nil {
|
||||
return false
|
||||
}
|
||||
return routeDomains[strings.ToLower(domain)]
|
||||
}
|
||||
|
||||
func (t *Endpoint) PreferredAddress(address netip.Addr) bool {
|
||||
routePrefixes := t.routePrefixes.Load()
|
||||
if routePrefixes == nil {
|
||||
return false
|
||||
}
|
||||
return routePrefixes.Contains(address)
|
||||
}
|
||||
|
||||
func (t *Endpoint) Server() *tsnet.Server {
|
||||
return t.server
|
||||
}
|
||||
|
||||
func (t *Endpoint) onReconfig(cfg *wgcfg.Config, routerCfg *router.Config, dnsCfg *tsDNS.Config) {
|
||||
if cfg == nil || dnsCfg == nil {
|
||||
return
|
||||
}
|
||||
if (t.cfg != nil && reflect.DeepEqual(t.cfg, cfg)) && (t.dnsCfg != nil && reflect.DeepEqual(t.dnsCfg, dnsCfg)) {
|
||||
return
|
||||
}
|
||||
var inet4Address, inet6Address netip.Addr
|
||||
for _, address := range cfg.Addresses {
|
||||
if address.Addr().Is4() {
|
||||
inet4Address = address.Addr()
|
||||
} else if address.Addr().Is6() {
|
||||
inet6Address = address.Addr()
|
||||
}
|
||||
}
|
||||
t.icmpForwarder.SetLocalAddresses(inet4Address, inet6Address)
|
||||
t.cfg = cfg
|
||||
t.dnsCfg = dnsCfg
|
||||
|
||||
routeDomains := make(map[string]bool)
|
||||
for fqdn := range dnsCfg.Routes {
|
||||
routeDomains[fqdn.WithoutTrailingDot()] = true
|
||||
}
|
||||
for _, fqdn := range dnsCfg.SearchDomains {
|
||||
routeDomains[fqdn.WithoutTrailingDot()] = true
|
||||
}
|
||||
t.routeDomains.Store(routeDomains)
|
||||
|
||||
var builder netipx.IPSetBuilder
|
||||
for _, peer := range cfg.Peers {
|
||||
for _, allowedIP := range peer.AllowedIPs {
|
||||
builder.AddPrefix(allowedIP)
|
||||
}
|
||||
}
|
||||
t.routePrefixes.Store(common.Must1(builder.IPSet()))
|
||||
|
||||
if t.onReconfigHook != nil {
|
||||
t.onReconfigHook(cfg, routerCfg, dnsCfg)
|
||||
}
|
||||
}
|
||||
|
||||
func addressFromAddr(destination netip.Addr) tcpip.Address {
|
||||
if destination.Is6() {
|
||||
return tcpip.AddrFrom16(destination.As16())
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package tailscale
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/tailscale/net/netns"
|
||||
)
|
||||
|
||||
func setAndroidProtectFunc(platformInterface platform.Interface) {
|
||||
func setAndroidProtectFunc(platformInterface adapter.PlatformInterface) {
|
||||
if platformInterface != nil {
|
||||
netns.SetAndroidProtectFunc(func(fd int) error {
|
||||
return platformInterface.AutoDetectInterfaceControl(fd)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
package tailscale
|
||||
|
||||
import "github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||
import "github.com/sagernet/sing-box/adapter"
|
||||
|
||||
func setAndroidProtectFunc(platformInterface platform.Interface) {
|
||||
func setAndroidProtectFunc(platformInterface adapter.PlatformInterface) {
|
||||
}
|
||||
|
||||
156
protocol/tailscale/tun_device_unix.go
Normal file
156
protocol/tailscale/tun_device_unix.go
Normal file
@@ -0,0 +1,156 @@
|
||||
//go:build !windows
|
||||
|
||||
package tailscale
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
singTun "github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
wgTun "github.com/sagernet/wireguard-go/tun"
|
||||
)
|
||||
|
||||
type tunDeviceAdapter struct {
|
||||
tun singTun.Tun
|
||||
linuxTUN singTun.LinuxTUN
|
||||
events chan wgTun.Event
|
||||
mtu int
|
||||
logger logger.ContextLogger
|
||||
debugTun bool
|
||||
readCount atomic.Uint32
|
||||
writeCount atomic.Uint32
|
||||
closeOnce sync.Once
|
||||
}
|
||||
|
||||
func newTunDeviceAdapter(tun singTun.Tun, mtu int, logger logger.ContextLogger) (wgTun.Device, error) {
|
||||
if tun == nil {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
if mtu == 0 {
|
||||
mtu = 1500
|
||||
}
|
||||
adapter := &tunDeviceAdapter{
|
||||
tun: tun,
|
||||
events: make(chan wgTun.Event, 1),
|
||||
mtu: mtu,
|
||||
logger: logger,
|
||||
debugTun: os.Getenv("SINGBOX_TS_TUN_DEBUG") != "",
|
||||
}
|
||||
if linuxTUN, ok := tun.(singTun.LinuxTUN); ok {
|
||||
adapter.linuxTUN = linuxTUN
|
||||
}
|
||||
adapter.events <- wgTun.EventUp
|
||||
return adapter, nil
|
||||
}
|
||||
|
||||
func (a *tunDeviceAdapter) File() *os.File {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *tunDeviceAdapter) Read(bufs [][]byte, sizes []int, offset int) (count int, err error) {
|
||||
if a.linuxTUN != nil {
|
||||
n, err := a.linuxTUN.BatchRead(bufs, offset-singTun.PacketOffset, sizes)
|
||||
if err == nil {
|
||||
for i := 0; i < n; i++ {
|
||||
a.debugPacket("read", bufs[i][offset:offset+sizes[i]])
|
||||
}
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
if offset < singTun.PacketOffset {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
readBuf := bufs[0][offset-singTun.PacketOffset:]
|
||||
n, err := a.tun.Read(readBuf)
|
||||
if err == nil {
|
||||
if n < singTun.PacketOffset {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
sizes[0] = n - singTun.PacketOffset
|
||||
a.debugPacket("read", readBuf[singTun.PacketOffset:n])
|
||||
return 1, nil
|
||||
}
|
||||
if errors.Is(err, singTun.ErrTooManySegments) {
|
||||
err = wgTun.ErrTooManySegments
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
|
||||
func (a *tunDeviceAdapter) Write(bufs [][]byte, offset int) (count int, err error) {
|
||||
if a.linuxTUN != nil {
|
||||
for i := range bufs {
|
||||
a.debugPacket("write", bufs[i][offset:])
|
||||
}
|
||||
return a.linuxTUN.BatchWrite(bufs, offset)
|
||||
}
|
||||
for _, packet := range bufs {
|
||||
a.debugPacket("write", packet[offset:])
|
||||
if singTun.PacketOffset > 0 {
|
||||
common.ClearArray(packet[offset-singTun.PacketOffset : offset])
|
||||
singTun.PacketFillHeader(packet[offset-singTun.PacketOffset:], singTun.PacketIPVersion(packet[offset:]))
|
||||
}
|
||||
_, err = a.tun.Write(packet[offset-singTun.PacketOffset:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
// WireGuard will not read count.
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (a *tunDeviceAdapter) MTU() (int, error) {
|
||||
return a.mtu, nil
|
||||
}
|
||||
|
||||
func (a *tunDeviceAdapter) Name() (string, error) {
|
||||
return a.tun.Name()
|
||||
}
|
||||
|
||||
func (a *tunDeviceAdapter) Events() <-chan wgTun.Event {
|
||||
return a.events
|
||||
}
|
||||
|
||||
func (a *tunDeviceAdapter) Close() error {
|
||||
var err error
|
||||
a.closeOnce.Do(func() {
|
||||
close(a.events)
|
||||
err = a.tun.Close()
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *tunDeviceAdapter) BatchSize() int {
|
||||
if a.linuxTUN != nil {
|
||||
return a.linuxTUN.BatchSize()
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (a *tunDeviceAdapter) debugPacket(direction string, packet []byte) {
|
||||
if !a.debugTun || a.logger == nil {
|
||||
return
|
||||
}
|
||||
var counter *atomic.Uint32
|
||||
switch direction {
|
||||
case "read":
|
||||
counter = &a.readCount
|
||||
case "write":
|
||||
counter = &a.writeCount
|
||||
default:
|
||||
return
|
||||
}
|
||||
if counter.Add(1) > 8 {
|
||||
return
|
||||
}
|
||||
sample := packet
|
||||
if len(sample) > 64 {
|
||||
sample = sample[:64]
|
||||
}
|
||||
a.logger.Trace("tailscale tun ", direction, " len=", len(packet), " head=", hex.EncodeToString(sample))
|
||||
}
|
||||
117
protocol/tailscale/tun_device_windows.go
Normal file
117
protocol/tailscale/tun_device_windows.go
Normal file
@@ -0,0 +1,117 @@
|
||||
//go:build windows
|
||||
|
||||
package tailscale
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
singTun "github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
wgTun "github.com/sagernet/wireguard-go/tun"
|
||||
)
|
||||
|
||||
type tunDeviceAdapter struct {
|
||||
tun singTun.WinTun
|
||||
nativeTun *singTun.NativeTun
|
||||
events chan wgTun.Event
|
||||
mtu atomic.Int64
|
||||
closeOnce sync.Once
|
||||
}
|
||||
|
||||
func newTunDeviceAdapter(tun singTun.Tun, mtu int, _ logger.ContextLogger) (wgTun.Device, error) {
|
||||
winTun, ok := tun.(singTun.WinTun)
|
||||
if !ok {
|
||||
return nil, errors.New("not a windows tun device")
|
||||
}
|
||||
nativeTun, ok := winTun.(*singTun.NativeTun)
|
||||
if !ok {
|
||||
return nil, errors.New("unsupported windows tun device")
|
||||
}
|
||||
if mtu == 0 {
|
||||
mtu = 1500
|
||||
}
|
||||
adapter := &tunDeviceAdapter{
|
||||
tun: winTun,
|
||||
nativeTun: nativeTun,
|
||||
events: make(chan wgTun.Event, 1),
|
||||
}
|
||||
adapter.mtu.Store(int64(mtu))
|
||||
adapter.events <- wgTun.EventUp
|
||||
return adapter, nil
|
||||
}
|
||||
|
||||
func (a *tunDeviceAdapter) File() *os.File {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *tunDeviceAdapter) Read(bufs [][]byte, sizes []int, offset int) (count int, err error) {
|
||||
packet, release, err := a.tun.ReadPacket()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer release()
|
||||
sizes[0] = copy(bufs[0][offset-singTun.PacketOffset:], packet)
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func (a *tunDeviceAdapter) Write(bufs [][]byte, offset int) (count int, err error) {
|
||||
for _, packet := range bufs {
|
||||
if singTun.PacketOffset > 0 {
|
||||
singTun.PacketFillHeader(packet[offset-singTun.PacketOffset:], singTun.PacketIPVersion(packet[offset:]))
|
||||
}
|
||||
_, err = a.tun.Write(packet[offset-singTun.PacketOffset:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (a *tunDeviceAdapter) MTU() (int, error) {
|
||||
return int(a.mtu.Load()), nil
|
||||
}
|
||||
|
||||
func (a *tunDeviceAdapter) ForceMTU(mtu int) {
|
||||
if mtu <= 0 {
|
||||
return
|
||||
}
|
||||
update := int(a.mtu.Load()) != mtu
|
||||
a.mtu.Store(int64(mtu))
|
||||
if update {
|
||||
select {
|
||||
case a.events <- wgTun.EventMTUUpdate:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *tunDeviceAdapter) LUID() uint64 {
|
||||
if a.nativeTun == nil {
|
||||
return 0
|
||||
}
|
||||
return a.nativeTun.LUID()
|
||||
}
|
||||
|
||||
func (a *tunDeviceAdapter) Name() (string, error) {
|
||||
return a.tun.Name()
|
||||
}
|
||||
|
||||
func (a *tunDeviceAdapter) Events() <-chan wgTun.Event {
|
||||
return a.events
|
||||
}
|
||||
|
||||
func (a *tunDeviceAdapter) Close() error {
|
||||
var err error
|
||||
a.closeOnce.Do(func() {
|
||||
close(a.events)
|
||||
err = a.tun.Close()
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *tunDeviceAdapter) BatchSize() int {
|
||||
return 1
|
||||
}
|
||||
@@ -99,7 +99,7 @@ func (l *ProxyListener) acceptLoop() {
|
||||
}
|
||||
|
||||
func (l *ProxyListener) accept(ctx context.Context, conn *net.TCPConn) error {
|
||||
return socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), l.authenticator, l, nil, M.SocksaddrFromNet(conn.RemoteAddr()), nil)
|
||||
return socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), l.authenticator, l, nil, 0, M.SocksaddrFromNet(conn.RemoteAddr()), nil)
|
||||
}
|
||||
|
||||
func (l *ProxyListener) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
|
||||
@@ -50,7 +50,13 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
users: options.Users,
|
||||
}
|
||||
if options.TLS != nil {
|
||||
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
tlsConfig, err := tls.NewServerWithOptions(tls.ServerOptions{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
Options: common.PtrValueOrDefault(options.TLS),
|
||||
KTLSCompatible: common.PtrValueOrDefault(options.Transport).Type == "" &&
|
||||
!common.PtrValueOrDefault(options.Multiplex).Enabled,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -259,7 +265,6 @@ func (h *inboundTransportHandler) NewConnectionEx(ctx context.Context, conn net.
|
||||
//nolint:staticcheck
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
//nolint:staticcheck
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||
(*Inbound)(h).NewConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ type Outbound struct {
|
||||
key [56]byte
|
||||
multiplexDialer *mux.Client
|
||||
tlsConfig tls.Config
|
||||
tlsDialer tls.Dialer
|
||||
transport adapter.V2RayClientTransport
|
||||
}
|
||||
|
||||
@@ -50,13 +51,21 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
key: trojan.Key(options.Password),
|
||||
}
|
||||
if options.TLS != nil {
|
||||
outbound.tlsConfig, err = tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
outbound.tlsConfig, err = tls.NewClientWithOptions(tls.ClientOptions{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
ServerAddress: options.Server,
|
||||
Options: common.PtrValueOrDefault(options.TLS),
|
||||
KTLSCompatible: common.PtrValueOrDefault(options.Transport).Type == "" &&
|
||||
!common.PtrValueOrDefault(options.Multiplex).Enabled,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.tlsDialer = tls.NewDialer(outboundDialer, outbound.tlsConfig)
|
||||
}
|
||||
if options.Transport != nil {
|
||||
outbound.transport, err = v2ray.NewClientTransport(ctx, outbound.dialer, outbound.serverAddr, common.PtrValueOrDefault(options.Transport), outbound.tlsConfig)
|
||||
outbound.transport, err = v2ray.NewClientTransport(ctx, logger, outbound.dialer, outbound.serverAddr, common.PtrValueOrDefault(options.Transport), outbound.tlsConfig)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create client transport: ", options.Transport.Type)
|
||||
}
|
||||
@@ -121,11 +130,10 @@ func (h *trojanDialer) DialContext(ctx context.Context, network string, destinat
|
||||
var err error
|
||||
if h.transport != nil {
|
||||
conn, err = h.transport.DialContext(ctx)
|
||||
} else if h.tlsDialer != nil {
|
||||
conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr)
|
||||
} else {
|
||||
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
|
||||
if err == nil && h.tlsConfig != nil {
|
||||
conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
common.Close(conn)
|
||||
|
||||
@@ -108,7 +108,6 @@ func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, source M.S
|
||||
//nolint:staticcheck
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
//nolint:staticcheck
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
metadata.OriginDestination = h.listener.UDPAddr()
|
||||
metadata.Source = source
|
||||
metadata.Destination = destination
|
||||
@@ -131,7 +130,6 @@ func (h *Inbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
|
||||
//nolint:staticcheck
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
//nolint:staticcheck
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
metadata.OriginDestination = h.listener.UDPAddr()
|
||||
metadata.Source = source
|
||||
metadata.Destination = destination
|
||||
|
||||
@@ -43,7 +43,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
if options.TLS == nil || !options.TLS.Enabled {
|
||||
return nil, C.ErrTLSRequired
|
||||
}
|
||||
tlsConfig, err := tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
tlsConfig, err := tls.NewClient(ctx, logger, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -14,10 +14,9 @@ import (
|
||||
"github.com/sagernet/sing-box/adapter/inbound"
|
||||
"github.com/sagernet/sing-box/common/taskmonitor"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/experimental/deprecated"
|
||||
"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/route/rule"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
@@ -36,19 +35,17 @@ func RegisterInbound(registry *inbound.Registry) {
|
||||
}
|
||||
|
||||
type Inbound struct {
|
||||
tag string
|
||||
ctx context.Context
|
||||
router adapter.Router
|
||||
networkManager adapter.NetworkManager
|
||||
logger log.ContextLogger
|
||||
//nolint:staticcheck
|
||||
inboundOptions option.InboundOptions
|
||||
tag string
|
||||
ctx context.Context
|
||||
router adapter.Router
|
||||
networkManager adapter.NetworkManager
|
||||
logger log.ContextLogger
|
||||
tunOptions tun.Options
|
||||
udpTimeout time.Duration
|
||||
stack string
|
||||
tunIf tun.Tun
|
||||
tunStack tun.Stack
|
||||
platformInterface platform.Interface
|
||||
platformInterface adapter.PlatformInterface
|
||||
platformOptions option.TunPlatformOptions
|
||||
autoRedirect tun.AutoRedirect
|
||||
routeRuleSet []adapter.RuleSet
|
||||
@@ -60,20 +57,18 @@ type Inbound struct {
|
||||
}
|
||||
|
||||
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions) (adapter.Inbound, error) {
|
||||
//nolint:staticcheck
|
||||
if len(options.Inet4Address) > 0 || len(options.Inet6Address) > 0 ||
|
||||
len(options.Inet4RouteAddress) > 0 || len(options.Inet6RouteAddress) > 0 ||
|
||||
len(options.Inet4RouteExcludeAddress) > 0 || len(options.Inet6RouteExcludeAddress) > 0 {
|
||||
return nil, E.New("legacy tun address fields are deprecated in sing-box 1.10.0 and removed in sing-box 1.12.0")
|
||||
}
|
||||
//nolint:staticcheck
|
||||
if options.GSO {
|
||||
return nil, E.New("GSO option in tun is deprecated in sing-box 1.11.0 and removed in sing-box 1.12.0")
|
||||
}
|
||||
|
||||
address := options.Address
|
||||
var deprecatedAddressUsed bool
|
||||
|
||||
//nolint:staticcheck
|
||||
if len(options.Inet4Address) > 0 {
|
||||
address = append(address, options.Inet4Address...)
|
||||
deprecatedAddressUsed = true
|
||||
}
|
||||
|
||||
//nolint:staticcheck
|
||||
if len(options.Inet6Address) > 0 {
|
||||
address = append(address, options.Inet6Address...)
|
||||
deprecatedAddressUsed = true
|
||||
}
|
||||
inet4Address := common.Filter(address, func(it netip.Prefix) bool {
|
||||
return it.Addr().Is4()
|
||||
})
|
||||
@@ -82,18 +77,6 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
})
|
||||
|
||||
routeAddress := options.RouteAddress
|
||||
|
||||
//nolint:staticcheck
|
||||
if len(options.Inet4RouteAddress) > 0 {
|
||||
routeAddress = append(routeAddress, options.Inet4RouteAddress...)
|
||||
deprecatedAddressUsed = true
|
||||
}
|
||||
|
||||
//nolint:staticcheck
|
||||
if len(options.Inet6RouteAddress) > 0 {
|
||||
routeAddress = append(routeAddress, options.Inet6RouteAddress...)
|
||||
deprecatedAddressUsed = true
|
||||
}
|
||||
inet4RouteAddress := common.Filter(routeAddress, func(it netip.Prefix) bool {
|
||||
return it.Addr().Is4()
|
||||
})
|
||||
@@ -102,18 +85,6 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
})
|
||||
|
||||
routeExcludeAddress := options.RouteExcludeAddress
|
||||
|
||||
//nolint:staticcheck
|
||||
if len(options.Inet4RouteExcludeAddress) > 0 {
|
||||
routeExcludeAddress = append(routeExcludeAddress, options.Inet4RouteExcludeAddress...)
|
||||
deprecatedAddressUsed = true
|
||||
}
|
||||
|
||||
//nolint:staticcheck
|
||||
if len(options.Inet6RouteExcludeAddress) > 0 {
|
||||
routeExcludeAddress = append(routeExcludeAddress, options.Inet6RouteExcludeAddress...)
|
||||
deprecatedAddressUsed = true
|
||||
}
|
||||
inet4RouteExcludeAddress := common.Filter(routeExcludeAddress, func(it netip.Prefix) bool {
|
||||
return it.Addr().Is4()
|
||||
})
|
||||
@@ -121,16 +92,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
return it.Addr().Is6()
|
||||
})
|
||||
|
||||
if deprecatedAddressUsed {
|
||||
deprecated.Report(ctx, deprecated.OptionTUNAddressX)
|
||||
}
|
||||
|
||||
//nolint:staticcheck
|
||||
if options.GSO {
|
||||
deprecated.Report(ctx, deprecated.OptionTUNGSO)
|
||||
}
|
||||
|
||||
platformInterface := service.FromContext[platform.Interface](ctx)
|
||||
platformInterface := service.FromContext[adapter.PlatformInterface](ctx)
|
||||
tunMTU := options.MTU
|
||||
enableGSO := C.IsLinux && options.Stack == "gvisor" && platformInterface == nil && tunMTU > 0 && tunMTU < 49152
|
||||
if tunMTU == 0 {
|
||||
@@ -174,7 +136,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
if ruleIndex == 0 {
|
||||
ruleIndex = tun.DefaultIPRoute2RuleIndex
|
||||
}
|
||||
autoRedirectFallbackRuleIndex := options.AutoRedirectIPRoute2FallbackRuleIndex
|
||||
autoRedirectFallbackRuleIndex := options.AutoRedirectFallbackRuleIndex
|
||||
if autoRedirectFallbackRuleIndex == 0 {
|
||||
autoRedirectFallbackRuleIndex = tun.DefaultIPRoute2AutoRedirectFallbackRuleIndex
|
||||
}
|
||||
@@ -186,6 +148,14 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
if outputMark == 0 {
|
||||
outputMark = tun.DefaultAutoRedirectOutputMark
|
||||
}
|
||||
resetMark := uint32(options.AutoRedirectResetMark)
|
||||
if resetMark == 0 {
|
||||
resetMark = tun.DefaultAutoRedirectResetMark
|
||||
}
|
||||
nfQueue := options.AutoRedirectNFQueue
|
||||
if nfQueue == 0 {
|
||||
nfQueue = tun.DefaultAutoRedirectNFQueue
|
||||
}
|
||||
networkManager := service.FromContext[adapter.NetworkManager](ctx)
|
||||
multiPendingPackets := C.IsDarwin && ((options.Stack == "gvisor" && tunMTU < 32768) || (options.Stack != "gvisor" && options.MTU <= 9000))
|
||||
inbound := &Inbound{
|
||||
@@ -194,7 +164,6 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
router: router,
|
||||
networkManager: networkManager,
|
||||
logger: logger,
|
||||
inboundOptions: options.InboundOptions,
|
||||
tunOptions: tun.Options{
|
||||
Name: options.InterfaceName,
|
||||
MTU: tunMTU,
|
||||
@@ -207,6 +176,9 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
IPRoute2AutoRedirectFallbackRuleIndex: autoRedirectFallbackRuleIndex,
|
||||
AutoRedirectInputMark: inputMark,
|
||||
AutoRedirectOutputMark: outputMark,
|
||||
AutoRedirectResetMark: resetMark,
|
||||
AutoRedirectNFQueue: nfQueue,
|
||||
ExcludeMPTCP: options.ExcludeMPTCP,
|
||||
Inet4LoopbackAddress: common.Filter(options.LoopbackAddress, netip.Addr.Is4),
|
||||
Inet6LoopbackAddress: common.Filter(options.LoopbackAddress, netip.Addr.Is6),
|
||||
StrictRoute: options.StrictRoute,
|
||||
@@ -374,8 +346,8 @@ func (t *Inbound) Start(stage adapter.StartStage) error {
|
||||
}
|
||||
}
|
||||
monitor.Start("open interface")
|
||||
if t.platformInterface != nil {
|
||||
tunInterface, err = t.platformInterface.OpenTun(&tunOptions, t.platformOptions)
|
||||
if t.platformInterface != nil && t.platformInterface.UsePlatformInterface() {
|
||||
tunInterface, err = t.platformInterface.OpenInterface(&tunOptions, t.platformOptions)
|
||||
} else {
|
||||
if HookBeforeCreatePlatformInterface != nil {
|
||||
HookBeforeCreatePlatformInterface()
|
||||
@@ -395,7 +367,7 @@ func (t *Inbound) Start(stage adapter.StartStage) error {
|
||||
)
|
||||
if t.platformInterface != nil {
|
||||
forwarderBindInterface = true
|
||||
includeAllNetworks = t.platformInterface.IncludeAllNetworks()
|
||||
includeAllNetworks = t.platformInterface.NetworkExtensionIncludeAllNetworks()
|
||||
}
|
||||
tunStack, err := tun.NewStack(t.stack, tun.StackOptions{
|
||||
Context: t.ctx,
|
||||
@@ -457,15 +429,34 @@ func (t *Inbound) Close() error {
|
||||
)
|
||||
}
|
||||
|
||||
func (t *Inbound) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr) error {
|
||||
return t.router.PreMatch(adapter.InboundContext{
|
||||
Inbound: t.tag,
|
||||
InboundType: C.TypeTun,
|
||||
Network: network,
|
||||
Source: source,
|
||||
Destination: destination,
|
||||
InboundOptions: t.inboundOptions,
|
||||
})
|
||||
func (t *Inbound) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr, routeContext tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error) {
|
||||
var ipVersion uint8
|
||||
if !destination.IsIPv6() {
|
||||
ipVersion = 4
|
||||
} else {
|
||||
ipVersion = 6
|
||||
}
|
||||
routeDestination, err := t.router.PreMatch(adapter.InboundContext{
|
||||
Inbound: t.tag,
|
||||
InboundType: C.TypeTun,
|
||||
IPVersion: ipVersion,
|
||||
Network: network,
|
||||
Source: source,
|
||||
Destination: destination,
|
||||
}, routeContext, timeout, false)
|
||||
if err != nil {
|
||||
switch {
|
||||
case rule.IsBypassed(err):
|
||||
err = nil
|
||||
case rule.IsRejected(err):
|
||||
t.logger.Trace("reject ", network, " connection from ", source.AddrString(), " to ", destination.AddrString())
|
||||
default:
|
||||
if network == N.NetworkICMP {
|
||||
t.logger.Warn(E.Cause(err, "link ", network, " connection from ", source.AddrString(), " to ", destination.AddrString()))
|
||||
}
|
||||
}
|
||||
}
|
||||
return routeDestination, err
|
||||
}
|
||||
|
||||
func (t *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
@@ -475,8 +466,7 @@ func (t *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, source M.S
|
||||
metadata.InboundType = C.TypeTun
|
||||
metadata.Source = source
|
||||
metadata.Destination = destination
|
||||
//nolint:staticcheck
|
||||
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)
|
||||
@@ -489,8 +479,7 @@ func (t *Inbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
|
||||
metadata.InboundType = C.TypeTun
|
||||
metadata.Source = source
|
||||
metadata.Destination = destination
|
||||
//nolint:staticcheck
|
||||
metadata.InboundOptions = t.inboundOptions
|
||||
|
||||
t.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
|
||||
t.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||
t.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
@@ -498,6 +487,36 @@ func (t *Inbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
|
||||
|
||||
type autoRedirectHandler Inbound
|
||||
|
||||
func (t *autoRedirectHandler) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr, routeContext tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error) {
|
||||
var ipVersion uint8
|
||||
if !destination.IsIPv6() {
|
||||
ipVersion = 4
|
||||
} else {
|
||||
ipVersion = 6
|
||||
}
|
||||
routeDestination, err := t.router.PreMatch(adapter.InboundContext{
|
||||
Inbound: t.tag,
|
||||
InboundType: C.TypeTun,
|
||||
IPVersion: ipVersion,
|
||||
Network: network,
|
||||
Source: source,
|
||||
Destination: destination,
|
||||
}, routeContext, timeout, true)
|
||||
if err != nil {
|
||||
switch {
|
||||
case rule.IsBypassed(err):
|
||||
t.logger.Trace("bypass ", network, " connection from ", source.AddrString(), " to ", destination.AddrString())
|
||||
case rule.IsRejected(err):
|
||||
t.logger.Trace("reject ", network, " connection from ", source.AddrString(), " to ", destination.AddrString())
|
||||
default:
|
||||
if network == N.NetworkICMP {
|
||||
t.logger.Warn(E.Cause(err, "link ", network, " connection from ", source.AddrString(), " to ", destination.AddrString()))
|
||||
}
|
||||
}
|
||||
}
|
||||
return routeDestination, err
|
||||
}
|
||||
|
||||
func (t *autoRedirectHandler) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
var metadata adapter.InboundContext
|
||||
@@ -505,9 +524,12 @@ func (t *autoRedirectHandler) NewConnectionEx(ctx context.Context, conn net.Conn
|
||||
metadata.InboundType = C.TypeTun
|
||||
metadata.Source = source
|
||||
metadata.Destination = destination
|
||||
//nolint:staticcheck
|
||||
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)
|
||||
}
|
||||
|
||||
func (t *autoRedirectHandler) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
panic("unexcepted")
|
||||
}
|
||||
|
||||
@@ -73,7 +73,16 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
}))
|
||||
inbound.service = service
|
||||
if options.TLS != nil {
|
||||
inbound.tlsConfig, err = tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
inbound.tlsConfig, err = tls.NewServerWithOptions(tls.ServerOptions{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
Options: common.PtrValueOrDefault(options.TLS),
|
||||
KTLSCompatible: common.PtrValueOrDefault(options.Transport).Type == "" &&
|
||||
!common.PtrValueOrDefault(options.Multiplex).Enabled &&
|
||||
common.All(options.Users, func(it option.VLESSUser) bool {
|
||||
return it.Flow == ""
|
||||
}),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -341,7 +350,6 @@ func (h *inboundTransportHandler) NewConnectionEx(ctx context.Context, conn net.
|
||||
//nolint:staticcheck
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
//nolint:staticcheck
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||
(*Inbound)(h).newConnectionExInternal(ctx, conn, metadata, onClose, false)
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ type Outbound struct {
|
||||
serverAddr M.Socksaddr
|
||||
multiplexDialer *mux.Client
|
||||
tlsConfig tls.Config
|
||||
tlsDialer tls.Dialer
|
||||
transport adapter.V2RayClientTransport
|
||||
packetAddr bool
|
||||
xudp bool
|
||||
@@ -62,13 +63,22 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
vision: strings.HasPrefix(options.Flow, "xtls-rprx-vision"),
|
||||
}
|
||||
if options.TLS != nil {
|
||||
outbound.tlsConfig, err = tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
outbound.tlsConfig, err = tls.NewClientWithOptions(tls.ClientOptions{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
ServerAddress: options.Server,
|
||||
Options: common.PtrValueOrDefault(options.TLS),
|
||||
KTLSCompatible: common.PtrValueOrDefault(options.Transport).Type == "" &&
|
||||
!common.PtrValueOrDefault(options.Multiplex).Enabled &&
|
||||
options.Flow == "",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.tlsDialer = tls.NewDialer(outboundDialer, outbound.tlsConfig)
|
||||
}
|
||||
if options.Transport != nil {
|
||||
outbound.transport, err = v2ray.NewClientTransport(ctx, outbound.dialer, outbound.serverAddr, common.PtrValueOrDefault(options.Transport), outbound.tlsConfig)
|
||||
outbound.transport, err = v2ray.NewClientTransport(ctx, logger, outbound.dialer, outbound.serverAddr, common.PtrValueOrDefault(options.Transport), outbound.tlsConfig)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create client transport: ", options.Transport.Type)
|
||||
}
|
||||
@@ -188,14 +198,13 @@ func (h *vlessDialer) DialContext(ctx context.Context, network string, destinati
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if h.tlsDialer != nil {
|
||||
conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr)
|
||||
if err == nil && h.vision && baseConn == nil {
|
||||
baseConn = conn
|
||||
}
|
||||
} else {
|
||||
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
|
||||
if err == nil && h.tlsConfig != nil {
|
||||
conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig)
|
||||
if err == nil && h.vision && baseConn == nil {
|
||||
baseConn = conn
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -281,11 +290,10 @@ func (h *vlessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr)
|
||||
var err error
|
||||
if h.transport != nil {
|
||||
conn, err = h.transport.DialContext(ctx)
|
||||
} else if h.tlsDialer != nil {
|
||||
conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr)
|
||||
} else {
|
||||
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
|
||||
if err == nil && h.tlsConfig != nil {
|
||||
conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
common.Close(conn)
|
||||
|
||||
@@ -233,7 +233,6 @@ func (h *inboundTransportHandler) NewConnectionEx(ctx context.Context, conn net.
|
||||
//nolint:staticcheck
|
||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||
//nolint:staticcheck
|
||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||
h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||
(*Inbound)(h).NewConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ type Outbound struct {
|
||||
serverAddr M.Socksaddr
|
||||
multiplexDialer *mux.Client
|
||||
tlsConfig tls.Config
|
||||
tlsDialer tls.Dialer
|
||||
transport adapter.V2RayClientTransport
|
||||
packetAddr bool
|
||||
xudp bool
|
||||
@@ -52,13 +53,16 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
serverAddr: options.ServerOptions.Build(),
|
||||
}
|
||||
if options.TLS != nil {
|
||||
outbound.tlsConfig, err = tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
outbound.tlsConfig, err = tls.NewClient(ctx, logger, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if outbound.tlsConfig != nil {
|
||||
outbound.tlsDialer = tls.NewDialer(outboundDialer, outbound.tlsConfig)
|
||||
}
|
||||
}
|
||||
if options.Transport != nil {
|
||||
outbound.transport, err = v2ray.NewClientTransport(ctx, outbound.dialer, outbound.serverAddr, common.PtrValueOrDefault(options.Transport), outbound.tlsConfig)
|
||||
outbound.transport, err = v2ray.NewClientTransport(ctx, logger, outbound.dialer, outbound.serverAddr, common.PtrValueOrDefault(options.Transport), outbound.tlsConfig)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create client transport: ", options.Transport.Type)
|
||||
}
|
||||
@@ -154,11 +158,10 @@ func (h *vmessDialer) DialContext(ctx context.Context, network string, destinati
|
||||
var err error
|
||||
if h.transport != nil {
|
||||
conn, err = h.transport.DialContext(ctx)
|
||||
} else if h.tlsDialer != nil {
|
||||
conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr)
|
||||
} else {
|
||||
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
|
||||
if err == nil && h.tlsConfig != nil {
|
||||
conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
common.Close(conn)
|
||||
@@ -182,11 +185,10 @@ func (h *vmessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr)
|
||||
var err error
|
||||
if h.transport != nil {
|
||||
conn, err = h.transport.DialContext(ctx)
|
||||
} else if h.tlsDialer != nil {
|
||||
conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr)
|
||||
} else {
|
||||
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
|
||||
if err == nil && h.tlsConfig != nil {
|
||||
conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -12,7 +12,9 @@ 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/rule"
|
||||
"github.com/sagernet/sing-box/transport/wireguard"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
@@ -22,6 +24,11 @@ import (
|
||||
"github.com/sagernet/sing/service"
|
||||
)
|
||||
|
||||
var (
|
||||
_ adapter.OutboundWithPreferredRoutes = (*Endpoint)(nil)
|
||||
_ dialer.PacketDialerWithDestination = (*Endpoint)(nil)
|
||||
)
|
||||
|
||||
func RegisterEndpoint(registry *endpoint.Registry) {
|
||||
endpoint.Register[option.WireGuardEndpointOptions](registry, C.TypeWireGuard, NewEndpoint)
|
||||
}
|
||||
@@ -38,7 +45,7 @@ type Endpoint struct {
|
||||
|
||||
func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.WireGuardEndpointOptions) (adapter.Endpoint, error) {
|
||||
ep := &Endpoint{
|
||||
Adapter: endpoint.NewAdapterWithDialerOptions(C.TypeWireGuard, tag, []string{N.NetworkTCP, N.NetworkUDP}, options.DialerOptions),
|
||||
Adapter: endpoint.NewAdapterWithDialerOptions(C.TypeWireGuard, tag, []string{N.NetworkTCP, N.NetworkUDP, N.NetworkICMP}, options.DialerOptions),
|
||||
ctx: ctx,
|
||||
router: router,
|
||||
dnsRouter: service.FromContext[adapter.DNSRouter](ctx),
|
||||
@@ -150,14 +157,34 @@ func (w *Endpoint) Close() error {
|
||||
return w.endpoint.Close()
|
||||
}
|
||||
|
||||
func (w *Endpoint) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr) error {
|
||||
return w.router.PreMatch(adapter.InboundContext{
|
||||
func (w *Endpoint) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr, routeContext tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error) {
|
||||
var ipVersion uint8
|
||||
if !destination.IsIPv6() {
|
||||
ipVersion = 4
|
||||
} else {
|
||||
ipVersion = 6
|
||||
}
|
||||
routeDestination, err := w.router.PreMatch(adapter.InboundContext{
|
||||
Inbound: w.Tag(),
|
||||
InboundType: w.Type(),
|
||||
IPVersion: ipVersion,
|
||||
Network: network,
|
||||
Source: source,
|
||||
Destination: destination,
|
||||
})
|
||||
}, routeContext, timeout, false)
|
||||
if err != nil {
|
||||
switch {
|
||||
case rule.IsBypassed(err):
|
||||
err = nil
|
||||
case rule.IsRejected(err):
|
||||
w.logger.Trace("reject ", network, " connection from ", source.AddrString(), " to ", destination.AddrString())
|
||||
default:
|
||||
if network == N.NetworkICMP {
|
||||
w.logger.Warn(E.Cause(err, "link ", network, " connection from ", source.AddrString(), " to ", destination.AddrString()))
|
||||
}
|
||||
}
|
||||
}
|
||||
return routeDestination, err
|
||||
}
|
||||
|
||||
func (w *Endpoint) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
@@ -223,18 +250,44 @@ func (w *Endpoint) DialContext(ctx context.Context, network string, destination
|
||||
return w.endpoint.DialContext(ctx, network, destination)
|
||||
}
|
||||
|
||||
func (w *Endpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
func (w *Endpoint) ListenPacketWithDestination(ctx context.Context, destination M.Socksaddr) (net.PacketConn, netip.Addr, error) {
|
||||
w.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||
if destination.IsFqdn() {
|
||||
destinationAddresses, err := w.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, netip.Addr{}, err
|
||||
}
|
||||
packetConn, _, err := N.ListenSerial(ctx, w.endpoint, destination, destinationAddresses)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return packetConn, err
|
||||
return N.ListenSerial(ctx, w.endpoint, destination, destinationAddresses)
|
||||
}
|
||||
return w.endpoint.ListenPacket(ctx, destination)
|
||||
packetConn, err := w.endpoint.ListenPacket(ctx, destination)
|
||||
if err != nil {
|
||||
return nil, netip.Addr{}, err
|
||||
}
|
||||
if destination.IsIP() {
|
||||
return packetConn, destination.Addr, nil
|
||||
}
|
||||
return packetConn, netip.Addr{}, nil
|
||||
}
|
||||
|
||||
func (w *Endpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
packetConn, destinationAddress, err := w.ListenPacketWithDestination(ctx, destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if destinationAddress.IsValid() && destination != M.SocksaddrFrom(destinationAddress, destination.Port) {
|
||||
return bufio.NewNATPacketConn(bufio.NewPacketConn(packetConn), M.SocksaddrFrom(destinationAddress, destination.Port), destination), nil
|
||||
}
|
||||
return packetConn, nil
|
||||
}
|
||||
|
||||
func (w *Endpoint) PreferredDomain(domain string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (w *Endpoint) PreferredAddress(address netip.Addr) bool {
|
||||
return w.endpoint.Lookup(address) != nil
|
||||
}
|
||||
|
||||
func (w *Endpoint) NewDirectRouteConnection(metadata adapter.InboundContext, routeContext tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error) {
|
||||
return w.endpoint.NewDirectRouteConnection(metadata, routeContext, timeout)
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
package wireguard
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
"github.com/sagernet/wireguard-go/conn"
|
||||
)
|
||||
|
||||
func init() {
|
||||
dialer.WgControlFns = conn.ControlFns
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
package wireguard
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/adapter/outbound"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/experimental/deprecated"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/wireguard"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/service"
|
||||
)
|
||||
|
||||
func RegisterOutbound(registry *outbound.Registry) {
|
||||
outbound.Register[option.LegacyWireGuardOutboundOptions](registry, C.TypeWireGuard, NewOutbound)
|
||||
}
|
||||
|
||||
type Outbound struct {
|
||||
outbound.Adapter
|
||||
ctx context.Context
|
||||
dnsRouter adapter.DNSRouter
|
||||
logger logger.ContextLogger
|
||||
localAddresses []netip.Prefix
|
||||
endpoint *wireguard.Endpoint
|
||||
}
|
||||
|
||||
func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.LegacyWireGuardOutboundOptions) (adapter.Outbound, error) {
|
||||
deprecated.Report(ctx, deprecated.OptionWireGuardOutbound)
|
||||
if options.GSO {
|
||||
deprecated.Report(ctx, deprecated.OptionWireGuardGSO)
|
||||
}
|
||||
outbound := &Outbound{
|
||||
Adapter: outbound.NewAdapterWithDialerOptions(C.TypeWireGuard, tag, []string{N.NetworkTCP, N.NetworkUDP}, options.DialerOptions),
|
||||
ctx: ctx,
|
||||
dnsRouter: service.FromContext[adapter.DNSRouter](ctx),
|
||||
logger: logger,
|
||||
localAddresses: options.LocalAddress,
|
||||
}
|
||||
if options.Detour != "" && options.GSO {
|
||||
return nil, E.New("gso is conflict with detour")
|
||||
}
|
||||
outboundDialer, err := dialer.NewWithOptions(dialer.Options{
|
||||
Context: ctx,
|
||||
Options: options.DialerOptions,
|
||||
RemoteIsDomain: options.ServerIsDomain() || common.Any(options.Peers, func(it option.LegacyWireGuardPeer) bool {
|
||||
return it.ServerIsDomain()
|
||||
}),
|
||||
ResolverOnDetour: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
peers := common.Map(options.Peers, func(it option.LegacyWireGuardPeer) wireguard.PeerOptions {
|
||||
return wireguard.PeerOptions{
|
||||
Endpoint: it.ServerOptions.Build(),
|
||||
PublicKey: it.PublicKey,
|
||||
PreSharedKey: it.PreSharedKey,
|
||||
AllowedIPs: it.AllowedIPs,
|
||||
// PersistentKeepaliveInterval: time.Duration(it.PersistentKeepaliveInterval),
|
||||
Reserved: it.Reserved,
|
||||
}
|
||||
})
|
||||
if len(peers) == 0 {
|
||||
peers = []wireguard.PeerOptions{{
|
||||
Endpoint: options.ServerOptions.Build(),
|
||||
PublicKey: options.PeerPublicKey,
|
||||
PreSharedKey: options.PreSharedKey,
|
||||
AllowedIPs: []netip.Prefix{netip.PrefixFrom(netip.IPv4Unspecified(), 0), netip.PrefixFrom(netip.IPv6Unspecified(), 0)},
|
||||
Reserved: options.Reserved,
|
||||
}}
|
||||
}
|
||||
var amnezia *wireguard.AmneziaOptions
|
||||
if options.Amnezia != nil {
|
||||
amnezia = &wireguard.AmneziaOptions{
|
||||
JC: options.Amnezia.JC,
|
||||
JMin: options.Amnezia.JMin,
|
||||
JMax: options.Amnezia.JMax,
|
||||
S1: options.Amnezia.S1,
|
||||
S2: options.Amnezia.S2,
|
||||
S3: options.Amnezia.S3,
|
||||
S4: options.Amnezia.S4,
|
||||
H1: options.Amnezia.H1,
|
||||
H2: options.Amnezia.H2,
|
||||
H3: options.Amnezia.H3,
|
||||
H4: options.Amnezia.H4,
|
||||
I1: options.Amnezia.I1,
|
||||
I2: options.Amnezia.I2,
|
||||
I3: options.Amnezia.I3,
|
||||
I4: options.Amnezia.I4,
|
||||
I5: options.Amnezia.I5,
|
||||
J1: options.Amnezia.J1,
|
||||
J2: options.Amnezia.J2,
|
||||
J3: options.Amnezia.J3,
|
||||
ITime: options.Amnezia.ITime,
|
||||
}
|
||||
}
|
||||
wgEndpoint, err := wireguard.NewEndpoint(wireguard.EndpointOptions{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
System: options.SystemInterface,
|
||||
Dialer: outboundDialer,
|
||||
CreateDialer: func(interfaceName string) N.Dialer {
|
||||
return common.Must1(dialer.NewDefault(ctx, option.DialerOptions{
|
||||
BindInterface: interfaceName,
|
||||
}))
|
||||
},
|
||||
Name: options.InterfaceName,
|
||||
MTU: options.MTU,
|
||||
Address: options.LocalAddress,
|
||||
PrivateKey: options.PrivateKey,
|
||||
ResolvePeer: func(domain string) (netip.Addr, error) {
|
||||
endpointAddresses, lookupErr := outbound.dnsRouter.Lookup(ctx, domain, outboundDialer.(dialer.ResolveDialer).QueryOptions())
|
||||
if lookupErr != nil {
|
||||
return netip.Addr{}, lookupErr
|
||||
}
|
||||
return endpointAddresses[0], nil
|
||||
},
|
||||
Peers: peers,
|
||||
Workers: options.Workers,
|
||||
PreallocatedBuffersPerPool: options.PreallocatedBuffersPerPool,
|
||||
DisablePauses: options.DisablePauses,
|
||||
Amnezia: amnezia,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.endpoint = wgEndpoint
|
||||
return outbound, nil
|
||||
}
|
||||
|
||||
func (o *Outbound) Start(stage adapter.StartStage) error {
|
||||
switch stage {
|
||||
case adapter.StartStateStart:
|
||||
return o.endpoint.Start(false)
|
||||
case adapter.StartStatePostStart:
|
||||
return o.endpoint.Start(true)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Outbound) Close() error {
|
||||
return o.endpoint.Close()
|
||||
}
|
||||
|
||||
func (o *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
switch network {
|
||||
case N.NetworkTCP:
|
||||
o.logger.InfoContext(ctx, "outbound connection to ", destination)
|
||||
case N.NetworkUDP:
|
||||
o.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||
}
|
||||
if destination.IsFqdn() {
|
||||
destinationAddresses, err := o.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return N.DialSerial(ctx, o.endpoint, network, destination, destinationAddresses)
|
||||
} else if !destination.Addr.IsValid() {
|
||||
return nil, E.New("invalid destination: ", destination)
|
||||
}
|
||||
return o.endpoint.DialContext(ctx, network, destination)
|
||||
}
|
||||
|
||||
func (o *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
o.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||
if destination.IsFqdn() {
|
||||
destinationAddresses, err := o.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
packetConn, _, err := N.ListenSerial(ctx, o.endpoint, destination, destinationAddresses)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return packetConn, err
|
||||
}
|
||||
return o.endpoint.ListenPacket(ctx, destination)
|
||||
}
|
||||
Reference in New Issue
Block a user