sing-box/protocol/masque/server.go

343 lines
12 KiB
Go

package masque
import (
"context"
"math"
"net"
"net/netip"
"slices"
"sync/atomic"
"time"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/adapter/endpoint"
"github.com/sagernet/sing-box/common/dialer"
"github.com/sagernet/sing-box/common/iponly"
"github.com/sagernet/sing-box/common/listener"
"github.com/sagernet/sing-box/common/tls"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/service/oomkiller"
"github.com/sagernet/sing-box/transport/device"
"github.com/sagernet/sing-box/transport/http"
"github.com/sagernet/sing-box/transport/masque"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/auth"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/service"
)
var (
_ adapter.OutboundWithPreferredRoutes = (*ServerEndpoint)(nil)
_ adapter.FlowOutbound = (*ServerEndpoint)(nil)
_ adapter.ConnectionHandler = (*serverConnectionHandler)(nil)
_ dialer.PacketDialerWithDestination = (*ServerEndpoint)(nil)
_ masque.ServerHandler = (*ServerEndpoint)(nil)
)
type ServerEndpoint struct {
endpointBase
ctx context.Context
dnsRouter adapter.DNSRouter
listener *listener.Listener
httpServer *http.Server
tlsConfig tls.ServerConfig
http3 bool
quicOptions option.QUICOptions
server *masque.Server
deviceOptions *device.Options
device device.Device
localAddresses []netip.Prefix
started atomic.Bool
}
func NewServerEndpoint(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.MASQUEServerEndpointOptions) (adapter.Endpoint, error) {
if options.MTU == 0 {
options.MTU = masque.DefaultMTU
}
versions := options.Versions()
if len(options.Version) == 0 && http.ConfigureHTTP3ListenerFunc == nil {
logger.Warn("QUIC is not included in this build, HTTP/3 is disabled")
versions = []int{1, 2}
}
serveHTTP1 := slices.Contains(versions, 1)
serveHTTP2 := slices.Contains(versions, 2)
serveHTTP3 := slices.Contains(versions, 3)
if serveHTTP3 && (options.TLS == nil || !options.TLS.Enabled) {
return nil, E.New("TLS is required for HTTP/3")
}
if options.HTTP3Options.InitialPacketSize == 0 {
options.HTTP3Options.InitialPacketSize = min(int(options.MTU)+masque.QUICPacketOverhead, math.MaxUint16)
}
serverEndpoint := &ServerEndpoint{
endpointBase: endpointBase{
Adapter: endpoint.NewAdapter(C.TypeMASQUEServer, tag, []string{N.NetworkTCP, N.NetworkUDP, N.NetworkICMP}, nil),
router: router,
logger: logger,
},
ctx: ctx,
dnsRouter: service.FromContext[adapter.DNSRouter](ctx),
http3: serveHTTP3,
quicOptions: options.HTTP3Options,
localAddresses: options.Address,
}
server, err := masque.NewServer(masque.ServerOptions{
Context: ctx,
Logger: logger,
Path: options.Path,
Address: options.Address,
AdvertiseRoutes: options.AdvertiseRoutes,
Resolve: serverEndpoint.resolve,
Handler: serverEndpoint,
})
if err != nil {
return nil, err
}
serverEndpoint.server = server
serverEndpoint.httpServer = http.NewServer(http.ServerOptions{
Authenticator: auth.NewAuthenticator(options.Users),
Logger: logger,
HTTP1: serveHTTP1,
HTTP2: serveHTTP2,
HTTP2Options: options.HTTP2Options,
Tunnels: map[string]http.TunnelHandler{"connect-ip": server},
})
if options.TLS != nil {
tlsConfig, tlsErr := tls.NewServerWithOptions(tls.ServerOptions{
Context: ctx,
Logger: logger,
Options: common.PtrValueOrDefault(options.TLS),
KTLSCompatible: true,
})
if tlsErr != nil {
return nil, tlsErr
}
if tlsConfig != nil {
serverEndpoint.httpServer.ConfigureTLS(tlsConfig)
}
serverEndpoint.tlsConfig = tlsConfig
}
var network []string
if serveHTTP1 || serveHTTP2 {
network = []string{N.NetworkTCP}
}
serverEndpoint.listener = listener.New(listener.Options{
Context: ctx,
Logger: logger,
Network: network,
Listen: options.ListenOptions,
ConnectionHandler: (*serverConnectionHandler)(serverEndpoint),
})
serverEndpoint.deviceOptions = newDeviceOptions(ctx, logger, serverEndpoint, options.MASQUEEndpointOptions, time.Duration(options.UDPTimeout), options.Address)
serverEndpoint.deviceOptions.Route = server.RouteOutbound
return serverEndpoint, nil
}
func (s *ServerEndpoint) resolve(ctx context.Context, domain string) ([]netip.Addr, error) {
return s.dnsRouter.Lookup(ctx, domain, adapter.DNSQueryOptions{})
}
func (s *ServerEndpoint) Start(stage adapter.StartStage, scope *adapter.Scope) error {
switch stage {
case adapter.StartStateInitialize:
s.deviceOptions.MemoryPressure = oomkiller.MemoryPressure(s.ctx)
tunnelDevice, err := device.New(*s.deviceOptions)
if err != nil {
return err
}
scope.Add(tunnelDevice.Close)
tunnelDevice.SetPacketWriter(s.writePacketBuffers)
s.device = tunnelDevice
s.deviceOptions = nil
scope.Add(s.server.Close)
case adapter.StartStateStart:
if s.tlsConfig != nil {
err := s.tlsConfig.Start()
if err != nil {
return E.Cause(err, "create TLS config")
}
scope.Add(s.tlsConfig.Close)
}
err := s.device.Start()
if err != nil {
return E.Cause(err, "start device")
}
err = s.listener.Start()
if err != nil {
return err
}
scope.Add(s.listener.Close)
if s.http3 {
http3Server, listenErr := s.httpServer.ListenHTTP3(s.ctx, s.logger, s.listener, nil, s.tlsConfig, s.quicOptions)
if listenErr != nil {
return listenErr
}
scope.Add(http3Server.Close)
}
s.started.Store(true)
scope.Add(func() error {
s.started.Store(false)
return nil
})
}
return nil
}
type serverConnectionHandler ServerEndpoint
func (h *serverConnectionHandler) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
if h.tlsConfig != nil {
tlsConn, err := tls.ServerHandshake(ctx, conn, h.tlsConfig)
if err != nil {
N.CloseOnHandshakeFailure(conn, onClose, err)
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source, ": TLS handshake"))
return
}
conn = tlsConn
}
h.httpServer.ServeConnection(ctx, conn, http.NewReader(conn), nil, metadata.Source, onClose)
}
func (s *ServerEndpoint) WriteInboundBuffers(packetBuffers []*buf.Buffer) error {
err := s.device.WriteInboundBuffers(packetBuffers)
buf.ReleaseMulti(packetBuffers)
return err
}
func (s *ServerEndpoint) FrontHeadroom() int {
return s.device.FrontHeadroom()
}
func (s *ServerEndpoint) NewOutboundQueue(handler func(packetBuffers []*buf.Buffer)) *tun.OutboundQueue {
return s.device.NewOutboundQueue(handler)
}
func (s *ServerEndpoint) PreMatchFlow(network string, destination netip.Addr) adapter.PreMatchAction {
return adapter.PreMatchFlow
}
func (s *ServerEndpoint) PortAddresses() (netip.Addr, netip.Addr) {
return s.device.PortAddresses()
}
func (s *ServerEndpoint) PortMTU() uint32 {
return s.device.PortMTU()
}
func (s *ServerEndpoint) UpstreamPort() any {
return s.device
}
func (s *ServerEndpoint) AttachReturn(returnPath tun.Return) error {
return s.device.AttachReturn(returnPath)
}
func (s *ServerEndpoint) DetachReturn(returnPath tun.Return) error {
return s.device.DetachReturn(returnPath)
}
func (s *ServerEndpoint) JudgeFlow(network uint8, source netip.AddrPort, destination netip.AddrPort, firstPacket []byte) tun.FlowVerdict {
return s.judgeFlow(s, s.localAddresses, network, source, destination, firstPacket)
}
func (s *ServerEndpoint) NewDNSPacket(payload []byte, source M.Socksaddr, destination M.Socksaddr, writer N.PacketWriter) {
s.newDNSPacket(log.ContextWithNewID(s.ctx), s, payload, source, destination, writer)
}
func (s *ServerEndpoint) WritePackets(packets [][]byte) error {
if !s.started.Load() {
return E.New("endpoint is not ready yet")
}
return s.server.WritePacketBuffers(common.Map(packets, func(packet []byte) *buf.Buffer {
packetBuffer := buf.NewSize(masque.PacketHeadroom + len(packet))
packetBuffer.Resize(masque.PacketHeadroom, 0)
common.Must1(packetBuffer.Write(packet))
return packetBuffer
}), true)
}
func (s *ServerEndpoint) writePacketBuffers(packetBuffers []*buf.Buffer) error {
return s.server.WritePacketBuffers(packetBuffers, false)
}
func (s *ServerEndpoint) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
s.newConnection(ctx, s, s.localAddresses, conn, source, destination, onClose)
}
func (s *ServerEndpoint) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
s.newPacketConnection(ctx, s, s.localAddresses, conn, source, destination, onClose)
}
func (s *ServerEndpoint) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
switch network {
case N.NetworkTCP:
s.logger.InfoContext(ctx, "outbound connection to ", destination)
case N.NetworkUDP:
s.logger.InfoContext(ctx, "outbound packet connection to ", destination)
}
if !s.started.Load() {
return nil, E.New("endpoint is not ready yet")
}
if destination.IsDomain() {
destinationAddresses, err := s.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{})
if err != nil {
return nil, err
}
return N.DialSerial(ctx, s.device, network, destination, destinationAddresses)
}
if !destination.Addr.IsValid() {
return nil, E.New("invalid destination: ", destination)
}
return s.device.DialContext(ctx, network, destination)
}
func (s *ServerEndpoint) ListenPacketWithDestination(ctx context.Context, destination M.Socksaddr) (net.PacketConn, netip.Addr, error) {
s.logger.InfoContext(ctx, "outbound packet connection to ", destination)
if !s.started.Load() {
return nil, netip.Addr{}, E.New("endpoint is not ready yet")
}
if destination.IsDomain() {
destinationAddresses, err := s.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{})
if err != nil {
return nil, netip.Addr{}, err
}
packetConn, destinationAddress, err := N.ListenSerial(ctx, s.device, destination, destinationAddresses)
if err != nil {
return nil, netip.Addr{}, err
}
return iponly.NewPacketConn(s.logger, packetConn), destinationAddress, nil
}
packetConn, err := s.device.ListenPacket(ctx, destination)
if err != nil {
return nil, netip.Addr{}, err
}
if destination.IsIP() {
return iponly.NewPacketConn(s.logger, packetConn), destination.Addr, nil
}
return iponly.NewPacketConn(s.logger, packetConn), netip.Addr{}, nil
}
func (s *ServerEndpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
packetConn, destinationAddress, err := s.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 (s *ServerEndpoint) PreferredDomain(metadata *adapter.InboundContext, domain string) bool {
return false
}
func (s *ServerEndpoint) PreferredAddress(metadata *adapter.InboundContext, address netip.Addr) bool {
return s.started.Load() && s.server.Contains(address)
}