merged with fixes

This commit is contained in:
onysd 2026-09-09 02:49:30 +03:00
parent a9e758b712
commit 2f1818d656
176 changed files with 9000 additions and 907 deletions

View file

@ -330,27 +330,21 @@ type Options struct {
// 阻断连接维持消息。可靠响应无法 tracking 时终止该连接durable best-effort update
// 则只丢在线加速并由 difference 恢复。
OutboundTrackedGlobalMaxBytes int64
// OutboundCriticalGlobalMaxBytes is an independent retained-body reserve for
// bootstrap/convergence RPC results. Bulk traffic cannot consume it.
OutboundCriticalGlobalMaxBytes int64
// OutboundWriteGlobalMaxBytes bounds concurrent encrypted wire/codec/obfuscation scratch.
// Scratch is shared and pooled across connections; default 512 MiB.
OutboundWriteGlobalMaxBytes int64
// DC 是本 server 的 DC ID。默认 2。
DC int
// StrictDC turns on exact DC-ID validation for the permanent-key exchange
// (default off = lenient). telesrv is always a single physical backend —
// there is no real multi-DC federation behind it — but the OwpenGram
// client forks intentionally run in "single-server backend" mode, where
// dc_id 1..5 all alias to this one server (see owpengram_servers.cpp /
// ApplyServerToDcOptions in the desktop client) so that any old data
// referencing a specific dc_id still resolves correctly. When tdesktop
// adds a new local account it picks its own starting dc_id (its usual
// multi-DC load-spreading behavior, unrelated to which physical server
// it's actually talking to) — that choice is not guaranteed to equal our
// configured DC. Strict validation would reject those accounts with
// "-444 wrong dc_id" even though they are connecting to the right (and
// only) server; dc_id is a client-side routing label here, not part of
// key derivation, so accepting the mismatch does not weaken the exchange.
// The switch exists for a hypothetical future real multi-DC deployment.
// StrictDC enables DC-label validation during key exchange. It is false by
// default: this single physical backend accepts every wire int32 label for
// permanent and temporary keys, and the label never changes auth-key
// persistence, session identity, or business state. When enabled,
// permanent labels must equal DC and temporary labels may equal +/-DC.
// This diagnostic switch does not itself provide multi-DC isolation.
StrictDC bool
// RSAKey 是 server RSA 私钥用于密钥交换。nil 时无法完成握手。
RSAKey *rsa.PrivateKey
@ -458,6 +452,9 @@ func (o *Options) setDefaults() {
if o.OutboundTrackedGlobalMaxBytes <= 0 {
o.OutboundTrackedGlobalMaxBytes = defaultOutboundTrackedMaxBytes
}
if o.OutboundCriticalGlobalMaxBytes <= 0 {
o.OutboundCriticalGlobalMaxBytes = defaultOutboundCriticalMaxBytes
}
if o.OutboundWriteGlobalMaxBytes <= 0 {
o.OutboundWriteGlobalMaxBytes = defaultOutboundWriteMaxBytes
}
@ -518,13 +515,17 @@ type Server struct {
rpcQueueSize int
rpcTimeout time.Duration
rpcScheduler *inboundRPCScheduler
bulkRPCScheduler *bulkRPCScheduler
rpcDeliveryHooks *rpcDeliveryHookExecutor
frameBudget *inboundFrameBudget
outboundQueueSize int
outboundControlQueueSize int
outboundTrackedBudget *outboundTrackedBudget
outboundControlBudget *outboundTrackedBudget
outboundCriticalBudget *outboundTrackedBudget
outboundScratchPool *outboundScratchPool
outboundOpPool *outboundOpPool
outboundReplayBodyPool *outboundReplayBodyPool
dc int
strictDC bool
@ -581,17 +582,20 @@ func New(opts Options) *Server {
rpcQueueSize: opts.RPCQueueSize,
rpcTimeout: opts.RPCTimeout,
rpcScheduler: newInboundRPCScheduler(opts.RPCGlobalWorkers, opts.RPCGlobalMaxTasks, opts.RPCGlobalMaxBytes),
bulkRPCScheduler: newBulkRPCScheduler(max(1, opts.RPCGlobalWorkers/2)),
rpcDeliveryHooks: newRPCDeliveryHookExecutor(opts.RPCDeliveryHookWorkers, opts.RPCDeliveryHookMaxPending),
frameBudget: newInboundFrameBudget(opts.InboundFrameGlobalMaxBytes),
outboundQueueSize: opts.OutboundQueueSize,
outboundControlQueueSize: opts.OutboundControlQueueSize,
outboundTrackedBudget: newOutboundTrackedBudget(opts.OutboundTrackedGlobalMaxBytes),
outboundControlBudget: newOutboundTrackedBudget(defaultOutboundControlMaxBytes),
outboundCriticalBudget: newOutboundTrackedBudget(opts.OutboundCriticalGlobalMaxBytes),
outboundScratchPool: newOutboundScratchPool(opts.OutboundWriteGlobalMaxBytes),
outboundOpPool: newOutboundOpPool(defaultOutboundOpPoolSize),
outboundReplayBodyPool: newOutboundReplayBodyPool(defaultOutboundReplayBodyClasses),
dc: opts.DC,
strictDC: opts.StrictDC,
key: exchange.PrivateKey{RSA: opts.RSAKey},
pubKeyPEM: rsaPublicKeyPEM(opts.RSAKey),
authKeys: opts.AuthKeys,
conns: conns,
rpc: opts.legacyRPC,
@ -612,6 +616,7 @@ func New(opts Options) *Server {
}),
rpcRewrap: newRPCRewrapRegistry(opts.RPCGlobalMaxTasks),
admission: newAdmissionController(opts.MaxConnections, opts.MaxConnectionsPerIP, opts.MaxConcurrentHandshakes),
pubKeyPEM: rsaPublicKeyPEM(opts.RSAKey),
}
if opts.IdentityDir != "" {
server.identityStore = identity.NewStore(opts.IdentityDir)
@ -662,26 +667,29 @@ func (s *Server) newConnWithLease(lease *physicalTransportLease, key crypto.Auth
func (s *Server) buildConn(tc transport.Conn, lease *physicalTransportLease, key crypto.AuthKey, sessionID, salt int64) *Conn {
c := &Conn{
transport: tc,
transportLease: lease,
writer: tc,
cipher: s.cipher,
msgID: proto.NewMessageIDGen(s.clock.Now),
writeTimeout: s.writeTimeout,
metrics: s.metrics,
now: s.clock.Now,
authKeyID: key.ID,
authKeyHex: hex.EncodeToString(key.ID[:]),
sessionID: sessionID,
salt: salt,
key: key,
createdAt: s.clock.Now(),
outboundQueueSize: s.outboundQueueSize,
outboundControlQueueSize: s.outboundControlQueueSize,
outboundTrackedBudget: s.outboundTrackedBudget,
outboundControlTrackedBudget: s.outboundControlBudget,
outboundScratchPool: s.outboundScratchPool,
rpcDeliveryHooks: s.rpcDeliveryHooks,
transport: tc,
transportLease: lease,
writer: tc,
cipher: s.cipher,
msgID: proto.NewMessageIDGen(s.clock.Now),
writeTimeout: s.writeTimeout,
metrics: s.metrics,
now: s.clock.Now,
authKeyID: key.ID,
authKeyHex: hex.EncodeToString(key.ID[:]),
sessionID: sessionID,
salt: salt,
key: key,
createdAt: s.clock.Now(),
outboundQueueSize: s.outboundQueueSize,
outboundControlQueueSize: s.outboundControlQueueSize,
outboundTrackedBudget: s.outboundTrackedBudget,
outboundControlTrackedBudget: s.outboundControlBudget,
outboundCriticalTrackedBudget: s.outboundCriticalBudget,
outboundScratchPool: s.outboundScratchPool,
outboundOpPool: s.outboundOpPool,
outboundReplayBodyPool: s.outboundReplayBodyPool,
rpcDeliveryHooks: s.rpcDeliveryHooks,
rpcResultAcked: func(conn *Conn, reqMsgID int64) {
// The sole outbound actor invokes this only after resolving a client
// msgs_ack server msg_id through its tracked resend frame. The actor has
@ -708,6 +716,7 @@ func (s *Server) Serve(ctx context.Context, ln net.Listener) error {
defer s.rpcDeliveryHooks.stop(rpcCloseWaitTimeout)
defer s.conns.releaseAllLogicalSessions()
defer s.rpcScheduler.stop(rpcCloseWaitTimeout)
defer s.bulkRPCScheduler.close()
// 只在最外层 listener 包一次,确保 same-port mux 的 sniff/HTTP upgrade 也计入
// raw admission而不是等连接已经分流后才计数。
ln = s.observeRawAccepts(s.admission.wrapListener(ln))
@ -1147,7 +1156,7 @@ func (s *Server) serveConn(ctx context.Context, raw transport.Conn, remote, loca
fetchedKey = &d
}
current, err = s.handleEncrypted(ctx, conn, cs, current, fetchedKey, &b, &plain)
current, err = s.handleEncrypted(ctx, conn, cs, current, remote, fetchedKey, &b, &plain)
if errors.Is(err, errActivationAuthKeyRejected) {
// handleEncrypted writes -404 while its activation claim still owns the
// physical writer, then its deferred abort removes/closes the claim.