ZaStoGram/TMessagesProj/jni/mtproxy/MtProxyEndpointPolicy.cpp
loop-uh 3f8f8f27e7 Extract mtproxy module with unified retry authority
Move MTProxy policy engine from tgnet/ to self-contained jni/mtproxy/ module. Consolidate reconnect hold computation into single MtProxyRetryAuthority owner (was split across Connection, ConnectionSocket, endpoint policy, and probe coordinator, causing hold misalignment bugs). Add MtProxyTerminalDiagnostic module with host tests for pre-I/O verdict preservation (fixes 02.07/30.06 clobber livelocks). Auto-generate phase classification (isPreIoTerminalVerdict, needsReconnectBackoff, isObservationFacadePhase, isLocalSchedulerTimeout) from single Python contract into both C++ and Java to eliminate parallel maintenance. Pass native hold (coordinator terminal hold + endpoint cooldown) via JNI to Java so reconnect timer and health store use THE clock, not re-derived ones. Optimize log flushing: batch debug lines, flush errors immediately. Add module boundary guard, host build system (MSVC), and unit tests for retry logic and terminal diagnostic derivation.
2026-07-02 18:25:13 +03:00

556 lines
23 KiB
C++

/*
* This is the source code of tgnet library v. 1.1
* It is licensed under GNU GPL v. 2 or later.
*/
#include "MtProxyEndpointPolicy.h"
#include "MtProxyPhaseContract.h"
#include <algorithm>
#include <arpa/inet.h>
#include <cctype>
#include <cstring>
#include <map>
#include <openssl/rand.h>
#include <pthread.h>
static constexpr int32_t MT_PROXY_HANDSHAKE_PRIORITY_GENERIC = 0;
static constexpr int32_t MT_PROXY_HANDSHAKE_PRIORITY_MEDIA = 1;
static constexpr int64_t MT_PROXY_ENDPOINT_DNS_CACHE_TTL_MS = 30 * 60 * 1000;
static constexpr int64_t MT_PROXY_ENDPOINT_DNS_COALESCE_MS = 750;
static constexpr int64_t MT_PROXY_ENDPOINT_TCP_CONNECT_GATE_MS = 650;
static constexpr int64_t MT_PROXY_ENDPOINT_TCP_CONNECT_GATE_REPEAT_MS = 2200;
static constexpr int64_t MT_PROXY_ENDPOINT_INTERACTIVE_NETWORK_COOLDOWN_MAX_MS = 3500;
static constexpr int64_t MT_PROXY_ENDPOINT_MEDIA_NETWORK_COOLDOWN_MAX_MS = 5000;
static constexpr int64_t MT_PROXY_ENDPOINT_HEAVY_NETWORK_COOLDOWN_MAX_MS = 9000;
static constexpr int64_t MT_PROXY_ENDPOINT_INVALID_SECRET_COOLDOWN_MIN_MS = 15 * 60 * 1000;
static constexpr int64_t MT_PROXY_ENDPOINT_INVALID_SECRET_COOLDOWN_JITTER_MS = 15 * 60 * 1000;
static constexpr int64_t MT_PROXY_ENDPOINT_USABLE_SUCCESS_HOLD_MS = 45 * 1000;
static constexpr int32_t MT_PROXY_ENDPOINT_POST_SUCCESS_DATA_PATH_SHADOWS = 1;
struct MtProxyEndpointResilienceState {
int64_t lastSuccessTime = 0;
int64_t cooldownUntil = 0;
int32_t hostResolveFailures = 0;
int32_t tcpFailures = 0;
int32_t handshakeFailures = 0;
int32_t plainNoResponseFailures = 0;
int32_t postHandshakeFailures = 0;
int32_t postSuccessDataPathShadowCount = 0;
bool secretDomainSanitizedLogged = false;
int32_t activeTcpConnects = 0;
};
struct MtProxyDnsCacheState {
std::string lastGoodIpv4;
int64_t lastGoodIpv4Time = 0;
int64_t resolveInFlightUntil = 0;
};
static pthread_mutex_t mtProxyEndpointPolicyMutex = PTHREAD_MUTEX_INITIALIZER;
static std::map<std::string, MtProxyEndpointResilienceState> proxyEndpointResilience;
static std::map<std::string, MtProxyDnsCacheState> proxyEndpointDnsCache;
static bool isBlockedZeroAddress(const std::string &ip) {
struct in_addr parsedAddress;
if (inet_pton(AF_INET, ip.c_str(), &parsedAddress.s_addr) == 1) {
return parsedAddress.s_addr == 0;
}
struct in6_addr parsedIpv6Address;
static const struct in6_addr anyIpv6Address = IN6ADDR_ANY_INIT;
return inet_pton(AF_INET6, ip.c_str(), &parsedIpv6Address) == 1
&& memcmp(&parsedIpv6Address, &anyIpv6Address, sizeof(parsedIpv6Address)) == 0;
}
static uint32_t endpointSecureRandomUint32() {
uint32_t v;
RAND_bytes((uint8_t *) &v, sizeof(v));
return v;
}
static uint32_t endpointSecureRandomBounded(uint32_t bound) {
if (bound <= 1) {
return 0;
}
uint32_t threshold = (0u - bound) % bound;
uint32_t v;
do {
v = endpointSecureRandomUint32();
} while (v < threshold);
return v % bound;
}
static int64_t cooldownMs(MtProxyEndpointResilienceState &state, const std::string &diagnostic, int32_t mode, int32_t priority) {
mode = normalizeMtProxyConnectionPatternOption(mode);
if (diagnostic == MtProxyPhase::SecretParseInvalidDomainControlChar || diagnostic == MtProxyPhase::SecretParseInvalidDomain) {
return MT_PROXY_ENDPOINT_INVALID_SECRET_COOLDOWN_MIN_MS + endpointSecureRandomBounded((uint32_t) MT_PROXY_ENDPOINT_INVALID_SECRET_COOLDOWN_JITTER_MS);
}
int32_t penalty = 1;
bool networkFailure = diagnostic == "host_resolve_failed"
|| diagnostic == "host_resolve_timeout"
|| diagnostic == MtProxyPhase::TcpNotConnected
|| diagnostic == "tcp_connection_refused"
|| diagnostic == "tcp_connect_timeout";
if (diagnostic == "host_resolve_failed" || diagnostic == "host_resolve_timeout") {
penalty = ++state.hostResolveFailures;
state.tcpFailures = 0;
} else if (diagnostic == MtProxyPhase::TcpNotConnected
|| diagnostic == "tcp_connection_refused"
|| diagnostic == "tcp_connect_timeout") {
penalty = ++state.tcpFailures;
} else if (diagnostic == "mtproxy_packet_sent_no_response" || diagnostic == "tcp_connected_no_pong") {
penalty = ++state.plainNoResponseFailures;
} else if (diagnostic == MtProxyPhase::PostHandshakeNoAppdata || diagnostic == "dropped_early_after_appdata") {
penalty = ++state.postHandshakeFailures;
} else {
penalty = ++state.handshakeFailures;
}
if (penalty > 4) {
penalty = 4;
}
int64_t base;
int64_t jitter;
int64_t maxCooldown;
if (networkFailure) {
bool genericNetworkFailure = priority <= MT_PROXY_HANDSHAKE_PRIORITY_GENERIC;
bool interactiveNetworkFailure = priority <= MT_PROXY_HANDSHAKE_PRIORITY_MEDIA;
if (mode == MT_PROXY_CONNECTION_PATTERN_STRICT) {
base = genericNetworkFailure ? 900 : (interactiveNetworkFailure ? 1300 : 2800);
jitter = endpointSecureRandomBounded(genericNetworkFailure ? 701 : (interactiveNetworkFailure ? 1001 : 2201));
} else if (mode == MT_PROXY_CONNECTION_PATTERN_BROWSER) {
base = genericNetworkFailure ? 650 : (interactiveNetworkFailure ? 950 : 2200);
jitter = endpointSecureRandomBounded(genericNetworkFailure ? 601 : (interactiveNetworkFailure ? 801 : 1601));
} else if (mode == MT_PROXY_CONNECTION_PATTERN_QUIET) {
base = genericNetworkFailure ? 750 : (interactiveNetworkFailure ? 1100 : 2400);
jitter = endpointSecureRandomBounded(genericNetworkFailure ? 651 : (interactiveNetworkFailure ? 901 : 1701));
} else {
base = genericNetworkFailure ? 350 : (interactiveNetworkFailure ? 500 : 900);
jitter = endpointSecureRandomBounded(genericNetworkFailure ? 351 : (interactiveNetworkFailure ? 451 : 801));
}
base += penalty * (genericNetworkFailure ? 250 : (interactiveNetworkFailure ? 450 : 900));
maxCooldown = genericNetworkFailure
? MT_PROXY_ENDPOINT_INTERACTIVE_NETWORK_COOLDOWN_MAX_MS
: (interactiveNetworkFailure ? MT_PROXY_ENDPOINT_MEDIA_NETWORK_COOLDOWN_MAX_MS : MT_PROXY_ENDPOINT_HEAVY_NETWORK_COOLDOWN_MAX_MS);
} else if (mode == MT_PROXY_CONNECTION_PATTERN_STRICT) {
base = 3500 + penalty * 2200;
jitter = endpointSecureRandomBounded(3001);
maxCooldown = 18000;
} else if (mode == MT_PROXY_CONNECTION_PATTERN_BROWSER) {
base = 2200 + penalty * 1600;
jitter = endpointSecureRandomBounded(2201);
maxCooldown = 14000;
} else if (mode == MT_PROXY_CONNECTION_PATTERN_QUIET) {
base = 1800 + penalty * 1200;
jitter = endpointSecureRandomBounded(1601);
maxCooldown = 10000;
} else {
base = 650 + penalty * 450;
jitter = endpointSecureRandomBounded(701);
maxCooldown = 4500;
}
int64_t cooldown = base + jitter;
if (cooldown > maxCooldown) {
cooldown = maxCooldown;
}
return cooldown;
}
static bool failureCanBeShadowedBySuccess(const std::string &diagnostic) {
if (diagnostic == "dropped_early_after_appdata" || diagnostic == "dropped_after_appdata") {
return false;
}
return diagnostic == "host_resolve_failed"
|| diagnostic == "host_resolve_timeout"
|| diagnostic == MtProxyPhase::TcpNotConnected
|| diagnostic == "tcp_connection_refused"
|| diagnostic == "tcp_connect_timeout"
|| diagnostic == "tcp_connected_no_pong"
|| diagnostic == "true_client_hello_timeout"
|| diagnostic == "client_hello_sent_no_server_hello"
|| diagnostic == "tls_alert_after_client_hello"
|| diagnostic == "short_tls_response_after_client_hello"
|| diagnostic == "unrecognized_response_after_client_hello"
|| diagnostic == "unrecognized_tls_response_after_client_hello"
|| diagnostic == MtProxyPhase::ServerHelloHmacMismatch
|| diagnostic == MtProxyPhase::FaketlsNotMtproxyResponse
|| diagnostic == MtProxyPhase::FaketlsNoServerHelloTerminal
|| diagnostic == MtProxyPhase::FaketlsServerClosedTerminal
|| diagnostic == MtProxyPhase::HandshakeProfilesExhausted
|| diagnostic == "mtproxy_packet_sent_no_response"
|| diagnostic == MtProxyPhase::PostHandshakeNoAppdata;
}
static bool failureUsesPostSuccessShadowBudget(const std::string &diagnostic) {
return diagnostic == "tcp_connected_no_pong"
|| diagnostic == "mtproxy_packet_sent_no_response"
|| diagnostic == MtProxyPhase::PostHandshakeNoAppdata;
}
static int64_t usableSuccessRemainingMsLocked(const std::string &key, int64_t now) {
if (key.empty()) {
return 0;
}
auto it = proxyEndpointResilience.find(key);
if (it == proxyEndpointResilience.end() || it->second.lastSuccessTime <= 0) {
return 0;
}
int64_t elapsed = std::max<int64_t>(0, now - it->second.lastSuccessTime);
if (elapsed >= MT_PROXY_ENDPOINT_USABLE_SUCCESS_HOLD_MS) {
return 0;
}
return MT_PROXY_ENDPOINT_USABLE_SUCCESS_HOLD_MS - elapsed;
}
static int64_t shadowFailureByFreshDataPathSuccessLocked(const std::string &key, const std::string &phase, int64_t now, bool consumeBudget) {
if (key.empty() || !failureCanBeShadowedBySuccess(phase)) {
return 0;
}
auto it = proxyEndpointResilience.find(key);
if (it == proxyEndpointResilience.end() || it->second.lastSuccessTime <= 0) {
return 0;
}
int64_t remainingMs = usableSuccessRemainingMsLocked(key, now);
if (remainingMs <= 0) {
return 0;
}
if (!failureUsesPostSuccessShadowBudget(phase)) {
return remainingMs;
}
if (it->second.postSuccessDataPathShadowCount >= MT_PROXY_ENDPOINT_POST_SUCCESS_DATA_PATH_SHADOWS) {
return 0;
}
if (consumeBudget) {
it->second.postSuccessDataPathShadowCount++;
}
return remainingMs;
}
bool MtProxyEndpointPolicy::extractSslipIpv4Address(const std::string &host, struct in_addr *address, std::string *literalAddress) {
static const char suffix[] = ".sslip.io";
static const size_t suffixLength = sizeof(suffix) - 1;
std::string hostKey = host;
std::transform(hostKey.begin(), hostKey.end(), hostKey.begin(), [](unsigned char c) {
return (char) ::tolower(c);
});
if (hostKey.size() <= suffixLength || hostKey.compare(hostKey.size() - suffixLength, suffixLength, suffix) != 0) {
return false;
}
std::string ipv4Address = hostKey.substr(0, hostKey.size() - suffixLength);
if (address != nullptr && inet_pton(AF_INET, ipv4Address.c_str(), &address->s_addr) != 1) {
return false;
}
if (address == nullptr) {
struct in_addr parsedAddress;
if (inet_pton(AF_INET, ipv4Address.c_str(), &parsedAddress.s_addr) != 1) {
return false;
}
}
if (literalAddress != nullptr) {
*literalAddress = ipv4Address;
}
return true;
}
std::string MtProxyEndpointPolicy::networkEndpointKeyFor(const std::string &host, uint16_t port) {
std::string hostKey = host;
std::transform(hostKey.begin(), hostKey.end(), hostKey.begin(), [](unsigned char c) {
return (char) ::tolower(c);
});
return hostKey + ":" + std::to_string((unsigned int) port);
}
std::string MtProxyEndpointPolicy::admissionKeyFor(const std::string &host, uint16_t port, const std::string &domain) {
std::string key = networkEndpointKeyFor(host, port);
if (!domain.empty()) {
key += ":";
key += domain;
}
return key;
}
std::string MtProxyEndpointPolicy::endpointKeyFor(const std::string &host, uint16_t port, const char *secretKind, const std::string &domain) {
std::string key = host + ":" + std::to_string((unsigned int) port);
key += ":";
key += secretKind != nullptr ? secretKind : "unknown";
if (!domain.empty()) {
key += ":";
key += domain;
}
return key;
}
std::string MtProxyEndpointPolicy::dnsCacheKeyFor(const std::string &host, uint16_t port) {
return networkEndpointKeyFor(host, port);
}
std::string MtProxyEndpointPolicy::stateKeyForPhase(const std::string &phase, const std::string &networkEndpointKey, const std::string &endpointKey) {
if ((phase == "host_resolve_failed"
|| phase == "host_resolve_timeout"
|| phase == MtProxyPhase::TcpNotConnected
|| phase == "tcp_connection_refused"
|| phase == "tcp_connect_timeout"
|| phase == "tcp_connected_no_pong"
|| phase == "dropped_early_after_appdata")
&& !networkEndpointKey.empty()) {
return networkEndpointKey;
}
if (!endpointKey.empty()) {
return endpointKey;
}
return networkEndpointKey;
}
bool MtProxyEndpointPolicy::failureNeedsCooldown(const std::string &diagnostic) {
return diagnostic == "host_resolve_failed"
|| diagnostic == "host_resolve_timeout"
|| diagnostic == MtProxyPhase::TcpNotConnected
|| diagnostic == "tcp_connection_refused"
|| diagnostic == "tcp_connect_timeout"
|| diagnostic == "tcp_connected_no_pong"
|| diagnostic == MtProxyPhase::SecretParseInvalidDomainControlChar
|| diagnostic == MtProxyPhase::SecretParseInvalidDomain
|| diagnostic == MtProxyPhase::FaketlsNotMtproxyResponse
|| diagnostic == MtProxyPhase::FaketlsNoServerHelloTerminal
|| diagnostic == MtProxyPhase::FaketlsServerClosedTerminal
|| diagnostic == MtProxyPhase::HandshakeProfilesExhausted
|| diagnostic == "mtproxy_packet_sent_no_response"
|| diagnostic == MtProxyPhase::PostHandshakeNoAppdata
|| diagnostic == "dropped_early_after_appdata";
}
int64_t MtProxyEndpointPolicy::cooldownMs(const std::string &diagnostic, int32_t connectionPatternMode, int32_t priority) {
MtProxyEndpointResilienceState scratch;
return ::cooldownMs(scratch, diagnostic, connectionPatternMode, priority);
}
MtProxyEndpointPolicy::CooldownResult MtProxyEndpointPolicy::readCooldown(const MtProxyEndpointContext &context, int64_t now) {
CooldownResult result;
pthread_mutex_lock(&mtProxyEndpointPolicyMutex);
auto readCooldown = [&](const std::string &key) {
if (key.empty()) {
return;
}
auto it = proxyEndpointResilience.find(key);
if (it != proxyEndpointResilience.end() && it->second.cooldownUntil > result.cooldownUntil) {
result.cooldownUntil = it->second.cooldownUntil;
result.key = key;
}
};
readCooldown(context.networkEndpointKey);
readCooldown(context.endpointKey);
pthread_mutex_unlock(&mtProxyEndpointPolicyMutex);
result.active = result.cooldownUntil > now;
if (result.active) {
result.remainingMs = result.cooldownUntil - now;
}
return result;
}
MtProxyEndpointPolicy::TcpConnectGateResult MtProxyEndpointPolicy::beginTcpConnect(const std::string &networkEndpointKey, bool wasReady) {
TcpConnectGateResult result;
if (networkEndpointKey.empty()) {
return result;
}
pthread_mutex_lock(&mtProxyEndpointPolicyMutex);
MtProxyEndpointResilienceState &state = proxyEndpointResilience[networkEndpointKey];
result.activeTcpConnects = state.activeTcpConnects;
if (result.activeTcpConnects > 0) {
result.shouldDelay = true;
} else {
state.activeTcpConnects++;
result.activeTcpConnects = state.activeTcpConnects;
}
pthread_mutex_unlock(&mtProxyEndpointPolicyMutex);
if (result.shouldDelay) {
result.delayMs = (uint32_t) ((wasReady ? MT_PROXY_ENDPOINT_TCP_CONNECT_GATE_REPEAT_MS : MT_PROXY_ENDPOINT_TCP_CONNECT_GATE_MS) + endpointSecureRandomBounded(wasReady ? 701 : 351));
}
return result;
}
int32_t MtProxyEndpointPolicy::releaseTcpConnect(const std::string &networkEndpointKey) {
if (networkEndpointKey.empty()) {
return 0;
}
int32_t activeTcpConnects = 0;
pthread_mutex_lock(&mtProxyEndpointPolicyMutex);
MtProxyEndpointResilienceState &state = proxyEndpointResilience[networkEndpointKey];
if (state.activeTcpConnects > 0) {
state.activeTcpConnects--;
}
activeTcpConnects = state.activeTcpConnects;
pthread_mutex_unlock(&mtProxyEndpointPolicyMutex);
return activeTcpConnects;
}
MtProxyEndpointPolicy::DnsCoalesceResult MtProxyEndpointPolicy::beginDnsCoalesce(const std::string &dnsCacheKey, int64_t now) {
DnsCoalesceResult result;
if (dnsCacheKey.empty()) {
return result;
}
int64_t resolveInFlightUntil = 0;
pthread_mutex_lock(&mtProxyEndpointPolicyMutex);
MtProxyDnsCacheState &state = proxyEndpointDnsCache[dnsCacheKey];
if (state.resolveInFlightUntil > now) {
resolveInFlightUntil = state.resolveInFlightUntil;
result.shouldDelay = true;
} else {
state.resolveInFlightUntil = now + MT_PROXY_ENDPOINT_DNS_COALESCE_MS;
}
pthread_mutex_unlock(&mtProxyEndpointPolicyMutex);
if (result.shouldDelay) {
result.delayMs = (uint32_t) std::min<int64_t>(MT_PROXY_ENDPOINT_DNS_COALESCE_MS, std::max<int64_t>(50, resolveInFlightUntil - now));
}
return result;
}
bool MtProxyEndpointPolicy::useCachedHostAddress(const std::string &dnsCacheKey, int64_t now, std::string *cachedIpv4) {
if (cachedIpv4 == nullptr || dnsCacheKey.empty()) {
return false;
}
pthread_mutex_lock(&mtProxyEndpointPolicyMutex);
auto it = proxyEndpointDnsCache.find(dnsCacheKey);
if (it != proxyEndpointDnsCache.end()
&& !it->second.lastGoodIpv4.empty()
&& now - it->second.lastGoodIpv4Time <= MT_PROXY_ENDPOINT_DNS_CACHE_TTL_MS) {
*cachedIpv4 = it->second.lastGoodIpv4;
}
pthread_mutex_unlock(&mtProxyEndpointPolicyMutex);
return !cachedIpv4->empty();
}
void MtProxyEndpointPolicy::storeResolvedAddress(const std::string &dnsCacheKey, const std::string &ip, int64_t now) {
if (dnsCacheKey.empty() || ip.empty()) {
return;
}
if (isBlockedZeroAddress(ip)) {
return;
}
pthread_mutex_lock(&mtProxyEndpointPolicyMutex);
MtProxyDnsCacheState &state = proxyEndpointDnsCache[dnsCacheKey];
state.lastGoodIpv4 = ip;
state.lastGoodIpv4Time = now;
state.resolveInFlightUntil = 0;
pthread_mutex_unlock(&mtProxyEndpointPolicyMutex);
}
MtProxyEndpointPolicy::FailureResult MtProxyEndpointPolicy::recordFailure(const MtProxyEndpointContext &context, const std::string &phase, int64_t now) {
FailureResult result;
bool needsCooldown = failureNeedsCooldown(phase);
if (!needsCooldown) {
return result;
}
result.stateKey = stateKeyForPhase(phase, context.networkEndpointKey, context.endpointKey);
if (result.stateKey.empty()) {
return result;
}
pthread_mutex_lock(&mtProxyEndpointPolicyMutex);
if (failureCanBeShadowedBySuccess(phase)) {
result.usableSuccessRemainingMs = shadowFailureByFreshDataPathSuccessLocked(result.stateKey, phase, now, true);
if (result.usableSuccessRemainingMs > 0) {
result.shadowedByUsableSuccess = true;
result.recorded = true;
pthread_mutex_unlock(&mtProxyEndpointPolicyMutex);
return result;
}
}
MtProxyEndpointResilienceState &state = proxyEndpointResilience[result.stateKey];
if (needsCooldown) {
result.cooldownMs = ::cooldownMs(state, phase, context.connectionPatternMode, context.priority);
if (result.cooldownMs > 0 && state.cooldownUntil < now + result.cooldownMs) {
state.cooldownUntil = now + result.cooldownMs;
}
}
pthread_mutex_unlock(&mtProxyEndpointPolicyMutex);
result.recorded = true;
return result;
}
int64_t MtProxyEndpointPolicy::freshDataPathSuccessRemainingMs(const MtProxyEndpointContext &context, int64_t now) {
int64_t remainingMs = 0;
pthread_mutex_lock(&mtProxyEndpointPolicyMutex);
remainingMs = std::max(remainingMs, usableSuccessRemainingMsLocked(context.networkEndpointKey, now));
remainingMs = std::max(remainingMs, usableSuccessRemainingMsLocked(context.endpointKey, now));
pthread_mutex_unlock(&mtProxyEndpointPolicyMutex);
return remainingMs;
}
int64_t MtProxyEndpointPolicy::shadowFailureByFreshDataPathSuccess(const MtProxyEndpointContext &context, const std::string &phase, int64_t now) {
int64_t remainingMs = 0;
pthread_mutex_lock(&mtProxyEndpointPolicyMutex);
remainingMs = shadowFailureByFreshDataPathSuccessLocked(context.endpointKey, phase, now, true);
if (remainingMs <= 0) {
remainingMs = shadowFailureByFreshDataPathSuccessLocked(context.networkEndpointKey, phase, now, true);
}
pthread_mutex_unlock(&mtProxyEndpointPolicyMutex);
return remainingMs;
}
void MtProxyEndpointPolicy::recordHandshakeOk(const MtProxyEndpointContext &context, const char *reason) {
if (reason == nullptr || strcmp(reason, "server_hello_hmac_ok") != 0 || context.networkEndpointKey.empty()) {
return;
}
pthread_mutex_lock(&mtProxyEndpointPolicyMutex);
auto it = proxyEndpointResilience.find(context.networkEndpointKey);
if (it != proxyEndpointResilience.end()) {
MtProxyEndpointResilienceState &state = it->second;
bool onlyNetworkTransportFailures = state.handshakeFailures == 0
&& state.plainNoResponseFailures == 0
&& state.postHandshakeFailures == 0;
state.hostResolveFailures = 0;
state.tcpFailures = 0;
if (onlyNetworkTransportFailures) {
state.cooldownUntil = 0;
}
}
pthread_mutex_unlock(&mtProxyEndpointPolicyMutex);
}
MtProxyEndpointPolicy::DataPathSuccessResult MtProxyEndpointPolicy::recordDataPathSuccess(const MtProxyEndpointContext &context, const char *reason, int64_t now) {
DataPathSuccessResult result;
if (reason == nullptr
|| (strcmp(reason, "first_tls_app_recv") != 0
&& strcmp(reason, "first_mtproxy_packet_recv") != 0)) {
return result;
}
pthread_mutex_lock(&mtProxyEndpointPolicyMutex);
resetStateForKey(context.networkEndpointKey, now, false);
if (!context.endpointKey.empty()) {
resetStateForKey(context.endpointKey, now, false);
}
pthread_mutex_unlock(&mtProxyEndpointPolicyMutex);
result.accepted = true;
return result;
}
bool MtProxyEndpointPolicy::recordSecretDomainSanitized(const std::string &endpointKey) {
if (endpointKey.empty()) {
return true;
}
bool shouldLog = false;
pthread_mutex_lock(&mtProxyEndpointPolicyMutex);
MtProxyEndpointResilienceState &state = proxyEndpointResilience[endpointKey];
if (!state.secretDomainSanitizedLogged) {
state.secretDomainSanitizedLogged = true;
shouldLog = true;
}
pthread_mutex_unlock(&mtProxyEndpointPolicyMutex);
return shouldLog;
}
void MtProxyEndpointPolicy::resetStateForKey(const std::string &key, int64_t now, bool resetRecipe) {
if (key.empty()) {
return;
}
MtProxyEndpointResilienceState &state = proxyEndpointResilience[key];
state.lastSuccessTime = now;
state.cooldownUntil = 0;
state.hostResolveFailures = 0;
state.tcpFailures = 0;
state.postSuccessDataPathShadowCount = 0;
if (resetRecipe) {
state.handshakeFailures = 0;
state.plainNoResponseFailures = 0;
state.postHandshakeFailures = 0;
}
}