Compare commits

..

1 Commits

Author SHA1 Message Date
Your Name
7acc0bdd90 v1.2.3 - Handle rate limiting properly on kind 99999 request 2026-02-09 07:49:43 -04:00
2 changed files with 93 additions and 33 deletions

View File

@@ -844,18 +844,74 @@ cJSON* retrieve_event(const char* event_id) {
///////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////
/**
* Check if filters contain only kind 99999 (NDK ping)
* Returns 1 if all filters only request kind 99999, 0 otherwise
*/
static int is_only_kind_99999_request(cJSON* filters) {
if (!filters || !cJSON_IsArray(filters)) {
return 0;
}
int filter_count = cJSON_GetArraySize(filters);
if (filter_count == 0) {
return 0;
}
for (int i = 0; i < filter_count; i++) {
cJSON* filter = cJSON_GetArrayItem(filters, i);
if (!filter || !cJSON_IsObject(filter)) {
return 0;
}
cJSON* kinds = cJSON_GetObjectItem(filter, "kinds");
if (!kinds || !cJSON_IsArray(kinds)) {
// Filter has no kinds or kinds is not an array - not a pure 99999 request
return 0;
}
int kinds_count = cJSON_GetArraySize(kinds);
if (kinds_count == 0) {
return 0;
}
for (int j = 0; j < kinds_count; j++) {
cJSON* kind_item = cJSON_GetArrayItem(kinds, j);
if (!cJSON_IsNumber(kind_item)) {
return 0;
}
int kind_val = (int)cJSON_GetNumberValue(kind_item);
if (kind_val != 99999) {
return 0;
}
}
}
return 1; // All filters only contain kind 99999
}
int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, struct per_session_data *pss) { int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, struct per_session_data *pss) {
if (!cJSON_IsArray(filters)) { if (!cJSON_IsArray(filters)) {
DEBUG_ERROR("REQ filters is not an array"); DEBUG_ERROR("REQ filters is not an array");
return 0; return 0;
} }
// Check if this is a kind 99999 (NDK ping) request - these should never count toward rate limiting
int is_ndk_ping = is_only_kind_99999_request(filters);
// EARLY SUBSCRIPTION LIMIT CHECK - Check limits BEFORE any processing // EARLY SUBSCRIPTION LIMIT CHECK - Check limits BEFORE any processing
if (pss) { if (pss) {
time_t current_time = time(NULL); time_t current_time = time(NULL);
// Check if client is currently rate limited due to excessive failed attempts // Check if client is currently rate limited due to excessive failed attempts
if (pss->rate_limit_until > current_time) { if (pss->rate_limit_until > current_time) {
// NDK ping (kind 99999) should not be blocked by rate limiting at all
if (is_ndk_ping) {
// Allow the request through - it will be handled normally and get a proper response
// The subscription will be created but no events will match (which is correct)
DEBUG_TRACE("Allowing kind 99999 NDK ping through despite rate limit");
// Fall through to normal processing (skip rate limit block)
} else {
char rate_limit_msg[256]; char rate_limit_msg[256];
int remaining_seconds = (int)(pss->rate_limit_until - current_time); int remaining_seconds = (int)(pss->rate_limit_until - current_time);
snprintf(rate_limit_msg, sizeof(rate_limit_msg), snprintf(rate_limit_msg, sizeof(rate_limit_msg),
@@ -881,21 +937,24 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
} }
cJSON_Delete(closed_msg); cJSON_Delete(closed_msg);
// Update rate limiting counters // Do NOT increment rate limiting counters here - the client is already rate limited.
pss->failed_subscription_attempts++; // Incrementing counters while already blocked would extend the punishment indefinitely,
pss->last_failed_attempt = current_time; // especially for benign requests like NDK's kind 99999 pings.
return 0; return 0;
} }
}
// Check session subscription limits // Check session subscription limits
if (pss->subscription_count >= g_subscription_manager.max_subscriptions_per_client) { if (pss->subscription_count >= g_subscription_manager.max_subscriptions_per_client) {
DEBUG_ERROR("Maximum subscriptions per client exceeded"); DEBUG_ERROR("Maximum subscriptions per client exceeded");
// Update rate limiting counters for failed attempt // Update rate limiting counters for failed attempt (but not for NDK ping)
if (!is_ndk_ping) {
pss->failed_subscription_attempts++; pss->failed_subscription_attempts++;
pss->last_failed_attempt = current_time; pss->last_failed_attempt = current_time;
pss->consecutive_failures++; pss->consecutive_failures++;
}
// Implement progressive backoff: 1s, 5s, 30s, 300s (5min) based on consecutive failures // Implement progressive backoff: 1s, 5s, 30s, 300s (5min) based on consecutive failures
int backoff_seconds = 1; int backoff_seconds = 1;
@@ -1010,7 +1069,8 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
cJSON_Delete(closed_msg); cJSON_Delete(closed_msg);
// Update rate limiting counters for failed attempt (global limit reached) // Update rate limiting counters for failed attempt (global limit reached)
if (pss) { // Do not count NDK ping (kind 99999) toward rate limiting
if (pss && !is_ndk_ping) {
time_t current_time = time(NULL); time_t current_time = time(NULL);
pss->failed_subscription_attempts++; pss->failed_subscription_attempts++;
pss->last_failed_attempt = current_time; pss->last_failed_attempt = current_time;

View File

@@ -13,8 +13,8 @@
// Using CRELAY_ prefix to avoid conflicts with nostr_core_lib VERSION macros // Using CRELAY_ prefix to avoid conflicts with nostr_core_lib VERSION macros
#define CRELAY_VERSION_MAJOR 1 #define CRELAY_VERSION_MAJOR 1
#define CRELAY_VERSION_MINOR 2 #define CRELAY_VERSION_MINOR 2
#define CRELAY_VERSION_PATCH 2 #define CRELAY_VERSION_PATCH 3
#define CRELAY_VERSION "v1.2.2" #define CRELAY_VERSION "v1.2.3"
// Relay metadata (authoritative source for NIP-11 information) // Relay metadata (authoritative source for NIP-11 information)
#define RELAY_NAME "C-Relay" #define RELAY_NAME "C-Relay"