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) {
|
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) {
|
||||||
char rate_limit_msg[256];
|
// NDK ping (kind 99999) should not be blocked by rate limiting at all
|
||||||
int remaining_seconds = (int)(pss->rate_limit_until - current_time);
|
if (is_ndk_ping) {
|
||||||
snprintf(rate_limit_msg, sizeof(rate_limit_msg),
|
// Allow the request through - it will be handled normally and get a proper response
|
||||||
"Rate limited due to excessive failed subscription attempts. Try again in %d seconds.", remaining_seconds);
|
// 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
|
// Send CLOSED notice for rate limiting
|
||||||
cJSON* closed_msg = cJSON_CreateArray();
|
cJSON* closed_msg = cJSON_CreateArray();
|
||||||
cJSON_AddItemToArray(closed_msg, cJSON_CreateString("CLOSED"));
|
cJSON_AddItemToArray(closed_msg, cJSON_CreateString("CLOSED"));
|
||||||
cJSON_AddItemToArray(closed_msg, cJSON_CreateString(sub_id));
|
cJSON_AddItemToArray(closed_msg, cJSON_CreateString(sub_id));
|
||||||
cJSON_AddItemToArray(closed_msg, cJSON_CreateString("error: rate limited"));
|
cJSON_AddItemToArray(closed_msg, cJSON_CreateString("error: rate limited"));
|
||||||
cJSON_AddItemToArray(closed_msg, cJSON_CreateString(rate_limit_msg));
|
cJSON_AddItemToArray(closed_msg, cJSON_CreateString(rate_limit_msg));
|
||||||
|
|
||||||
char* closed_str = cJSON_Print(closed_msg);
|
char* closed_str = cJSON_Print(closed_msg);
|
||||||
if (closed_str) {
|
if (closed_str) {
|
||||||
size_t closed_len = strlen(closed_str);
|
size_t closed_len = strlen(closed_str);
|
||||||
unsigned char* buf = malloc(LWS_PRE + closed_len);
|
unsigned char* buf = malloc(LWS_PRE + closed_len);
|
||||||
if (buf) {
|
if (buf) {
|
||||||
memcpy(buf + LWS_PRE, closed_str, closed_len);
|
memcpy(buf + LWS_PRE, closed_str, closed_len);
|
||||||
lws_write(wsi, buf + LWS_PRE, closed_len, LWS_WRITE_TEXT);
|
lws_write(wsi, buf + LWS_PRE, closed_len, LWS_WRITE_TEXT);
|
||||||
free(buf);
|
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
|
// 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)
|
||||||
pss->failed_subscription_attempts++;
|
if (!is_ndk_ping) {
|
||||||
pss->last_failed_attempt = current_time;
|
pss->failed_subscription_attempts++;
|
||||||
pss->consecutive_failures++;
|
pss->last_failed_attempt = current_time;
|
||||||
|
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;
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user