mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-04-04 12:27:36 +03:00
refactor: Modular inbounds/outbounds
This commit is contained in:
parent
9f7683818f
commit
e233fd4fe5
152 changed files with 3116 additions and 2926 deletions
136
common/listener/listener.go
Normal file
136
common/listener/listener.go
Normal file
|
@ -0,0 +1,136 @@
|
|||
package listener
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/settings"
|
||||
"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"
|
||||
)
|
||||
|
||||
type Listener struct {
|
||||
ctx context.Context
|
||||
logger logger.ContextLogger
|
||||
network []string
|
||||
listenOptions option.ListenOptions
|
||||
connHandler adapter.ConnectionHandlerEx
|
||||
packetHandler adapter.PacketHandlerEx
|
||||
oobPacketHandler adapter.OOBPacketHandlerEx
|
||||
threadUnsafePacketWriter bool
|
||||
disablePacketOutput bool
|
||||
setSystemProxy bool
|
||||
systemProxySOCKS bool
|
||||
|
||||
tcpListener net.Listener
|
||||
systemProxy settings.SystemProxy
|
||||
udpConn *net.UDPConn
|
||||
udpAddr M.Socksaddr
|
||||
packetOutbound chan *N.PacketBuffer
|
||||
packetOutboundClosed chan struct{}
|
||||
shutdown atomic.Bool
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
Context context.Context
|
||||
Logger logger.ContextLogger
|
||||
Network []string
|
||||
Listen option.ListenOptions
|
||||
ConnectionHandler adapter.ConnectionHandlerEx
|
||||
PacketHandler adapter.PacketHandlerEx
|
||||
OOBPacketHandler adapter.OOBPacketHandlerEx
|
||||
ThreadUnsafePacketWriter bool
|
||||
DisablePacketOutput bool
|
||||
SetSystemProxy bool
|
||||
SystemProxySOCKS bool
|
||||
}
|
||||
|
||||
func New(
|
||||
options Options,
|
||||
) *Listener {
|
||||
return &Listener{
|
||||
ctx: options.Context,
|
||||
logger: options.Logger,
|
||||
network: options.Network,
|
||||
listenOptions: options.Listen,
|
||||
connHandler: options.ConnectionHandler,
|
||||
packetHandler: options.PacketHandler,
|
||||
oobPacketHandler: options.OOBPacketHandler,
|
||||
threadUnsafePacketWriter: options.ThreadUnsafePacketWriter,
|
||||
disablePacketOutput: options.DisablePacketOutput,
|
||||
setSystemProxy: options.SetSystemProxy,
|
||||
systemProxySOCKS: options.SystemProxySOCKS,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Listener) Start() error {
|
||||
if common.Contains(l.network, N.NetworkTCP) {
|
||||
_, err := l.ListenTCP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go l.loopTCPIn()
|
||||
}
|
||||
if common.Contains(l.network, N.NetworkUDP) {
|
||||
_, err := l.ListenUDP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.packetOutboundClosed = make(chan struct{})
|
||||
l.packetOutbound = make(chan *N.PacketBuffer, 64)
|
||||
go l.loopUDPIn()
|
||||
if !l.disablePacketOutput {
|
||||
go l.loopUDPOut()
|
||||
}
|
||||
}
|
||||
if l.setSystemProxy {
|
||||
listenPort := M.SocksaddrFromNet(l.tcpListener.Addr()).Port
|
||||
var listenAddrString string
|
||||
listenAddr := l.listenOptions.Listen.Build()
|
||||
if listenAddr.IsUnspecified() {
|
||||
listenAddrString = "127.0.0.1"
|
||||
} else {
|
||||
listenAddrString = listenAddr.String()
|
||||
}
|
||||
systemProxy, err := settings.NewSystemProxy(l.ctx, M.ParseSocksaddrHostPort(listenAddrString, listenPort), l.systemProxySOCKS)
|
||||
if err != nil {
|
||||
return E.Cause(err, "initialize system proxy")
|
||||
}
|
||||
err = systemProxy.Enable()
|
||||
if err != nil {
|
||||
return E.Cause(err, "set system proxy")
|
||||
}
|
||||
l.systemProxy = systemProxy
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Listener) Close() error {
|
||||
l.shutdown.Store(true)
|
||||
var err error
|
||||
if l.systemProxy != nil && l.systemProxy.IsEnabled() {
|
||||
err = l.systemProxy.Disable()
|
||||
}
|
||||
return E.Errors(err, common.Close(
|
||||
l.tcpListener,
|
||||
common.PtrOrNil(l.udpConn),
|
||||
))
|
||||
}
|
||||
|
||||
func (l *Listener) TCPListener() net.Listener {
|
||||
return l.tcpListener
|
||||
}
|
||||
|
||||
func (l *Listener) UDPConn() *net.UDPConn {
|
||||
return l.udpConn
|
||||
}
|
||||
|
||||
func (l *Listener) ListenOptions() option.ListenOptions {
|
||||
return l.listenOptions
|
||||
}
|
11
common/listener/listener_go121.go
Normal file
11
common/listener/listener_go121.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
//go:build go1.21
|
||||
|
||||
package listener
|
||||
|
||||
import "net"
|
||||
|
||||
const go121Available = true
|
||||
|
||||
func setMultiPathTCP(listenConfig *net.ListenConfig) {
|
||||
listenConfig.SetMultipathTCP(true)
|
||||
}
|
16
common/listener/listener_go123.go
Normal file
16
common/listener/listener_go123.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
//go:build go1.23
|
||||
|
||||
package listener
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
func setKeepAliveConfig(listener *net.ListenConfig, idle time.Duration, interval time.Duration) {
|
||||
listener.KeepAliveConfig = net.KeepAliveConfig{
|
||||
Enable: true,
|
||||
Idle: idle,
|
||||
Interval: interval,
|
||||
}
|
||||
}
|
10
common/listener/listener_nongo121.go
Normal file
10
common/listener/listener_nongo121.go
Normal file
|
@ -0,0 +1,10 @@
|
|||
//go:build !go1.21
|
||||
|
||||
package listener
|
||||
|
||||
import "net"
|
||||
|
||||
const go121Available = false
|
||||
|
||||
func setMultiPathTCP(listenConfig *net.ListenConfig) {
|
||||
}
|
15
common/listener/listener_nongo123.go
Normal file
15
common/listener/listener_nongo123.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
//go:build !go1.23
|
||||
|
||||
package listener
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing/common/control"
|
||||
)
|
||||
|
||||
func setKeepAliveConfig(listener *net.ListenConfig, idle time.Duration, interval time.Duration) {
|
||||
listener.KeepAlive = idle
|
||||
listener.Control = control.Append(listener.Control, control.SetKeepAlivePeriod(idle, interval))
|
||||
}
|
85
common/listener/listener_tcp.go
Normal file
85
common/listener/listener_tcp.go
Normal file
|
@ -0,0 +1,85 @@
|
|||
package listener
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"github.com/metacubex/tfo-go"
|
||||
)
|
||||
|
||||
func (l *Listener) ListenTCP() (net.Listener, error) {
|
||||
var err error
|
||||
bindAddr := M.SocksaddrFrom(l.listenOptions.Listen.Build(), l.listenOptions.ListenPort)
|
||||
var tcpListener net.Listener
|
||||
var listenConfig net.ListenConfig
|
||||
if l.listenOptions.TCPKeepAlive >= 0 {
|
||||
keepIdle := time.Duration(l.listenOptions.TCPKeepAlive)
|
||||
if keepIdle == 0 {
|
||||
keepIdle = C.TCPKeepAliveInitial
|
||||
}
|
||||
keepInterval := time.Duration(l.listenOptions.TCPKeepAliveInterval)
|
||||
if keepInterval == 0 {
|
||||
keepInterval = C.TCPKeepAliveInterval
|
||||
}
|
||||
setKeepAliveConfig(&listenConfig, keepIdle, keepInterval)
|
||||
}
|
||||
if l.listenOptions.TCPMultiPath {
|
||||
if !go121Available {
|
||||
return nil, E.New("MultiPath TCP requires go1.21, please recompile your binary.")
|
||||
}
|
||||
setMultiPathTCP(&listenConfig)
|
||||
}
|
||||
if l.listenOptions.TCPFastOpen {
|
||||
var tfoConfig tfo.ListenConfig
|
||||
tfoConfig.ListenConfig = listenConfig
|
||||
tcpListener, err = tfoConfig.Listen(l.ctx, M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.String())
|
||||
} else {
|
||||
tcpListener, err = listenConfig.Listen(l.ctx, M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.String())
|
||||
}
|
||||
if err == nil {
|
||||
l.logger.Info("tcp server started at ", tcpListener.Addr())
|
||||
}
|
||||
//nolint:staticcheck
|
||||
if l.listenOptions.ProxyProtocol || l.listenOptions.ProxyProtocolAcceptNoHeader {
|
||||
return nil, E.New("Proxy Protocol is deprecated and removed in sing-box 1.6.0")
|
||||
}
|
||||
l.tcpListener = tcpListener
|
||||
return tcpListener, err
|
||||
}
|
||||
|
||||
func (l *Listener) loopTCPIn() {
|
||||
tcpListener := l.tcpListener
|
||||
var metadata adapter.InboundContext
|
||||
for {
|
||||
conn, err := tcpListener.Accept()
|
||||
if err != nil {
|
||||
//nolint:staticcheck
|
||||
if netError, isNetError := err.(net.Error); isNetError && netError.Temporary() {
|
||||
l.logger.Error(err)
|
||||
continue
|
||||
}
|
||||
if l.shutdown.Load() && E.IsClosed(err) {
|
||||
return
|
||||
}
|
||||
l.tcpListener.Close()
|
||||
l.logger.Error("tcp listener closed: ", err)
|
||||
continue
|
||||
}
|
||||
//nolint:staticcheck
|
||||
metadata.InboundDetour = l.listenOptions.Detour
|
||||
//nolint:staticcheck
|
||||
metadata.InboundOptions = l.listenOptions.InboundOptions
|
||||
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr()).Unwrap()
|
||||
metadata.OriginDestination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
|
||||
ctx := log.ContextWithNewID(l.ctx)
|
||||
l.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||
go l.connHandler.NewConnectionEx(ctx, conn, metadata, nil)
|
||||
}
|
||||
}
|
153
common/listener/listener_udp.go
Normal file
153
common/listener/listener_udp.go
Normal file
|
@ -0,0 +1,153 @@
|
|||
package listener
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
func (l *Listener) ListenUDP() (net.PacketConn, error) {
|
||||
bindAddr := M.SocksaddrFrom(l.listenOptions.Listen.Build(), l.listenOptions.ListenPort)
|
||||
var lc net.ListenConfig
|
||||
var udpFragment bool
|
||||
if l.listenOptions.UDPFragment != nil {
|
||||
udpFragment = *l.listenOptions.UDPFragment
|
||||
} else {
|
||||
udpFragment = l.listenOptions.UDPFragmentDefault
|
||||
}
|
||||
if !udpFragment {
|
||||
lc.Control = control.Append(lc.Control, control.DisableUDPFragment())
|
||||
}
|
||||
udpConn, err := lc.ListenPacket(l.ctx, M.NetworkFromNetAddr(N.NetworkUDP, bindAddr.Addr), bindAddr.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l.udpConn = udpConn.(*net.UDPConn)
|
||||
l.udpAddr = bindAddr
|
||||
l.logger.Info("udp server started at ", udpConn.LocalAddr())
|
||||
return udpConn, err
|
||||
}
|
||||
|
||||
func (l *Listener) UDPAddr() M.Socksaddr {
|
||||
return l.udpAddr
|
||||
}
|
||||
|
||||
func (l *Listener) PacketWriter() N.PacketWriter {
|
||||
return (*packetWriter)(l)
|
||||
}
|
||||
|
||||
func (l *Listener) loopUDPIn() {
|
||||
defer close(l.packetOutboundClosed)
|
||||
var buffer *buf.Buffer
|
||||
if !l.threadUnsafePacketWriter {
|
||||
buffer = buf.NewPacket()
|
||||
defer buffer.Release()
|
||||
buffer.IncRef()
|
||||
defer buffer.DecRef()
|
||||
}
|
||||
if l.oobPacketHandler != nil {
|
||||
oob := make([]byte, 1024)
|
||||
for {
|
||||
if l.threadUnsafePacketWriter {
|
||||
buffer = buf.NewPacket()
|
||||
} else {
|
||||
buffer.Reset()
|
||||
}
|
||||
n, oobN, _, addr, err := l.udpConn.ReadMsgUDPAddrPort(buffer.FreeBytes(), oob)
|
||||
if err != nil {
|
||||
if l.threadUnsafePacketWriter {
|
||||
buffer.Release()
|
||||
}
|
||||
if l.shutdown.Load() && E.IsClosed(err) {
|
||||
return
|
||||
}
|
||||
l.udpConn.Close()
|
||||
l.logger.Error("udp listener closed: ", err)
|
||||
return
|
||||
}
|
||||
buffer.Truncate(n)
|
||||
l.oobPacketHandler.NewPacketEx(buffer, oob[:oobN], M.SocksaddrFromNetIP(addr).Unwrap())
|
||||
}
|
||||
} else {
|
||||
for {
|
||||
if l.threadUnsafePacketWriter {
|
||||
buffer = buf.NewPacket()
|
||||
} else {
|
||||
buffer.Reset()
|
||||
}
|
||||
n, addr, err := l.udpConn.ReadFromUDPAddrPort(buffer.FreeBytes())
|
||||
if err != nil {
|
||||
if l.threadUnsafePacketWriter {
|
||||
buffer.Release()
|
||||
}
|
||||
if l.shutdown.Load() && E.IsClosed(err) {
|
||||
return
|
||||
}
|
||||
l.udpConn.Close()
|
||||
l.logger.Error("udp listener closed: ", err)
|
||||
return
|
||||
}
|
||||
buffer.Truncate(n)
|
||||
l.packetHandler.NewPacketEx(buffer, M.SocksaddrFromNetIP(addr).Unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Listener) loopUDPOut() {
|
||||
for {
|
||||
select {
|
||||
case packet := <-l.packetOutbound:
|
||||
destination := packet.Destination.AddrPort()
|
||||
_, err := l.udpConn.WriteToUDPAddrPort(packet.Buffer.Bytes(), destination)
|
||||
packet.Buffer.Release()
|
||||
N.PutPacketBuffer(packet)
|
||||
if err != nil {
|
||||
if l.shutdown.Load() && E.IsClosed(err) {
|
||||
return
|
||||
}
|
||||
l.udpConn.Close()
|
||||
l.logger.Error("udp listener write back: ", destination, ": ", err)
|
||||
return
|
||||
}
|
||||
continue
|
||||
case <-l.packetOutboundClosed:
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case packet := <-l.packetOutbound:
|
||||
packet.Buffer.Release()
|
||||
N.PutPacketBuffer(packet)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type packetWriter Listener
|
||||
|
||||
func (w *packetWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
packet := N.NewPacketBuffer()
|
||||
packet.Buffer = buffer
|
||||
packet.Destination = destination
|
||||
select {
|
||||
case w.packetOutbound <- packet:
|
||||
return nil
|
||||
default:
|
||||
buffer.Release()
|
||||
N.PutPacketBuffer(packet)
|
||||
if w.shutdown.Load() {
|
||||
return os.ErrClosed
|
||||
}
|
||||
w.logger.Trace("dropped packet to ", destination)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (w *packetWriter) WriteIsThreadUnsafe() {
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue