Fix epoll dispatch dropping INotify events, ConsoleServer self-deadlock, and add TLS SNI support

- INotify/Thread epoll dispatch was silently dropping every live
  core::INotify event (an isKnownINotify registry fixes it) - this had
  broken mail delivery for over a week, masked by routine restarts.
- TCPServer::lock self-deadlocked every admin-console connection via a
  plain std::mutex being re-entered; switched to recursive_mutex.
- TLS.cpp/h: SNI-based certificate selection (addSNIContext), the
  groundwork for multi-tenant TLS (qualityiron.vegas alongside
  barant.com on one APIServer process).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Brad ArantandClaude Sonnet 5 committed 2026-10-06 17:38:37 +00:00
1 parent d058322cc6
commit 1d3d30cd05
8 files changed
+222 -55

No files matched your search

+45 -1
View File
@@ -4,6 +4,8 @@
#include "ZString.h"
#include "TLSInfo.h"
#include <openssl/ssl.h>
#include <map>
#include <string>
namespace core {
@@ -46,6 +48,26 @@ namespace core {
void createContext(TLSInfo *tlsInfo);
///
/// Registers a second (or third, etc.) certificate/key pair for a
/// specific hostname on THIS SAME listening socket, via TLS's
/// Server Name Indication (SNI) extension - lets one TCPServer
/// terminate TLS for multiple unrelated domains, each with its own
/// real certificate, without needing a separate listening port or
/// process per domain. Must be called after createContext() (or the
/// TLSInfo* constructor, which calls it) has already established the
/// base `ctx` - that base context keeps serving as both the
/// fallback for hostnames not registered here AND the cert offered
/// to any client that sends no SNI hostname at all (a bare-IP
/// request, or any TLS client old enough to predate SNI). Callers
/// that never call this at all (every existing single-site
/// TLSInfo* user - BARANTMail, Zephora, ClockPlanet, and a
/// single-site APIServer config) see zero behavior change: the SNI
/// callback is only ever registered on `ctx` the first time this is
/// called, not unconditionally in createContext().
///
void addSNIContext(const std::string &hostname, TLSInfo *tlsInfo);
// A never-configured TLS object (either the listening TCPServer
// itself when constructed with tlsInfo=NULL, or a TCPSession spawned
// from one via TLS(SSL_CTX*)) must default to a real NULL here, not
@@ -78,7 +100,29 @@ namespace core {
char *sip_key = (char *)"../testkeys/certs/pbxserver.key";
bool initialized = false;
SSL *ssl;
// The actual cert/key-loading + cipher/session-cache setup shared
// by both the base context (createContext(), unchanged) and every
// additional per-hostname context addSNIContext() builds - factored
// out so there's exactly one place that does this, not two copies
// drifting apart.
SSL_CTX *buildContext(TLSInfo *tlsInfo);
// Owns every SSL_CTX* built by addSNIContext() (the base `ctx` is
// owned/freed the same way it always was, untouched by this map).
// Keyed by hostname exactly as configured - matched case-
// insensitively against the ClientHello's SNI value in the
// callback (see TLS.cpp).
std::map<std::string, SSL_CTX *> sniContexts;
bool sniCallbackRegistered = false;
// OpenSSL's own SSL_CTX_set_tlsext_servername_callback signature -
// a static member (not a free function) so it can reach the owning
// TLS instance's private sniContexts via the `arg` parameter
// (SSL_CTX_set_tlsext_servername_arg(ctx, this), set alongside the
// callback itself in addSNIContext()).
static int sniCallback(SSL *ssl, int *ad, void *arg);
};
}