Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7acc0bdd90 |
122
src/main.c
122
src/main.c
@@ -844,58 +844,117 @@ 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) {
|
||||
if (!cJSON_IsArray(filters)) {
|
||||
DEBUG_ERROR("REQ filters is not an array");
|
||||
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
|
||||
if (pss) {
|
||||
time_t current_time = time(NULL);
|
||||
|
||||
// Check if client is currently rate limited due to excessive failed attempts
|
||||
if (pss->rate_limit_until > current_time) {
|
||||
char rate_limit_msg[256];
|
||||
int remaining_seconds = (int)(pss->rate_limit_until - current_time);
|
||||
snprintf(rate_limit_msg, sizeof(rate_limit_msg),
|
||||
"Rate limited due to excessive failed subscription attempts. Try again in %d seconds.", remaining_seconds);
|
||||
// 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];
|
||||
int remaining_seconds = (int)(pss->rate_limit_until - current_time);
|
||||
snprintf(rate_limit_msg, sizeof(rate_limit_msg),
|
||||
"Rate limited due to excessive failed subscription attempts. Try again in %d seconds.", remaining_seconds);
|
||||
|
||||
// Send CLOSED notice for rate limiting
|
||||
cJSON* closed_msg = cJSON_CreateArray();
|
||||
cJSON_AddItemToArray(closed_msg, cJSON_CreateString("CLOSED"));
|
||||
cJSON_AddItemToArray(closed_msg, cJSON_CreateString(sub_id));
|
||||
cJSON_AddItemToArray(closed_msg, cJSON_CreateString("error: rate limited"));
|
||||
cJSON_AddItemToArray(closed_msg, cJSON_CreateString(rate_limit_msg));
|
||||
// Send CLOSED notice for rate limiting
|
||||
cJSON* closed_msg = cJSON_CreateArray();
|
||||
cJSON_AddItemToArray(closed_msg, cJSON_CreateString("CLOSED"));
|
||||
cJSON_AddItemToArray(closed_msg, cJSON_CreateString(sub_id));
|
||||
cJSON_AddItemToArray(closed_msg, cJSON_CreateString("error: rate limited"));
|
||||
cJSON_AddItemToArray(closed_msg, cJSON_CreateString(rate_limit_msg));
|
||||
|
||||
char* closed_str = cJSON_Print(closed_msg);
|
||||
if (closed_str) {
|
||||
size_t closed_len = strlen(closed_str);
|
||||
unsigned char* buf = malloc(LWS_PRE + closed_len);
|
||||
if (buf) {
|
||||
memcpy(buf + LWS_PRE, closed_str, closed_len);
|
||||
lws_write(wsi, buf + LWS_PRE, closed_len, LWS_WRITE_TEXT);
|
||||
free(buf);
|
||||
char* closed_str = cJSON_Print(closed_msg);
|
||||
if (closed_str) {
|
||||
size_t closed_len = strlen(closed_str);
|
||||
unsigned char* buf = malloc(LWS_PRE + closed_len);
|
||||
if (buf) {
|
||||
memcpy(buf + LWS_PRE, closed_str, closed_len);
|
||||
lws_write(wsi, buf + LWS_PRE, closed_len, LWS_WRITE_TEXT);
|
||||
free(buf);
|
||||
}
|
||||
free(closed_str);
|
||||
}
|
||||
free(closed_str);
|
||||
cJSON_Delete(closed_msg);
|
||||
|
||||
// Do NOT increment rate limiting counters here - the client is already rate limited.
|
||||
// Incrementing counters while already blocked would extend the punishment indefinitely,
|
||||
// especially for benign requests like NDK's kind 99999 pings.
|
||||
|
||||
return 0;
|
||||
}
|
||||
cJSON_Delete(closed_msg);
|
||||
|
||||
// Update rate limiting counters
|
||||
pss->failed_subscription_attempts++;
|
||||
pss->last_failed_attempt = current_time;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check session subscription limits
|
||||
if (pss->subscription_count >= g_subscription_manager.max_subscriptions_per_client) {
|
||||
DEBUG_ERROR("Maximum subscriptions per client exceeded");
|
||||
|
||||
// Update rate limiting counters for failed attempt
|
||||
pss->failed_subscription_attempts++;
|
||||
pss->last_failed_attempt = current_time;
|
||||
pss->consecutive_failures++;
|
||||
// Update rate limiting counters for failed attempt (but not for NDK ping)
|
||||
if (!is_ndk_ping) {
|
||||
pss->failed_subscription_attempts++;
|
||||
pss->last_failed_attempt = current_time;
|
||||
pss->consecutive_failures++;
|
||||
}
|
||||
|
||||
// Implement progressive backoff: 1s, 5s, 30s, 300s (5min) based on consecutive failures
|
||||
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);
|
||||
|
||||
// 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);
|
||||
pss->failed_subscription_attempts++;
|
||||
pss->last_failed_attempt = current_time;
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
// Using CRELAY_ prefix to avoid conflicts with nostr_core_lib VERSION macros
|
||||
#define CRELAY_VERSION_MAJOR 1
|
||||
#define CRELAY_VERSION_MINOR 2
|
||||
#define CRELAY_VERSION_PATCH 2
|
||||
#define CRELAY_VERSION "v1.2.2"
|
||||
#define CRELAY_VERSION_PATCH 3
|
||||
#define CRELAY_VERSION "v1.2.3"
|
||||
|
||||
// Relay metadata (authoritative source for NIP-11 information)
|
||||
#define RELAY_NAME "C-Relay"
|
||||
|
||||
Reference in New Issue
Block a user