diff --git a/c-relay-1.1.0.tar.gz b/c-relay-1.1.0.tar.gz new file mode 100644 index 0000000..f602976 Binary files /dev/null and b/c-relay-1.1.0.tar.gz differ diff --git a/relay.pid b/relay.pid index ded5610..fc8cd66 100644 --- a/relay.pid +++ b/relay.pid @@ -1 +1 @@ -1683148 +1688521 diff --git a/src/main.c b/src/main.c index 83289c2..fd6c141 100644 --- a/src/main.c +++ b/src/main.c @@ -132,7 +132,7 @@ static void free_bind_params(char** params, int count) { int is_authorized_admin_event(cJSON* event, char* error_message, size_t error_size); // Forward declaration for NOTICE message support -void send_notice_message(struct lws* wsi, const char* message); +void send_notice_message(struct lws* wsi, struct per_session_data* pss, const char* message); // Forward declarations for NIP-42 authentication functions void send_nip42_auth_challenge(struct lws* wsi, struct per_session_data* pss); @@ -207,7 +207,7 @@ void signal_handler(int sig) { ///////////////////////////////////////////////////////////////////////////////////////// // Send NOTICE message to client (NIP-01) -void send_notice_message(struct lws* wsi, const char* message) { +void send_notice_message(struct lws* wsi, struct per_session_data* pss, const char* message) { if (!wsi || !message) return; cJSON* notice_msg = cJSON_CreateArray(); @@ -218,7 +218,7 @@ void send_notice_message(struct lws* wsi, const char* message) { if (msg_str) { size_t msg_len = strlen(msg_str); // Use proper message queue system instead of direct lws_write - if (queue_message(wsi, NULL, msg_str, msg_len, LWS_WRITE_TEXT) != 0) { + if (queue_message(wsi, pss, msg_str, msg_len, LWS_WRITE_TEXT) != 0) { DEBUG_ERROR("Failed to queue NOTICE message"); } free(msg_str); diff --git a/src/main.h b/src/main.h index 86db098..0b2c3d5 100644 --- a/src/main.h +++ b/src/main.h @@ -12,8 +12,8 @@ // Version information (auto-updated by build system) #define VERSION_MAJOR 1 #define VERSION_MINOR 1 -#define VERSION_PATCH 0 -#define VERSION "v1.1.0" +#define VERSION_PATCH 1 +#define VERSION "v1.1.1" // Avoid VERSION_MAJOR redefinition warning from nostr_core_lib #undef VERSION_MAJOR diff --git a/src/nip042.c b/src/nip042.c index 0f59ec6..ae1830d 100644 --- a/src/nip042.c +++ b/src/nip042.c @@ -16,7 +16,7 @@ // Forward declaration for notice message function -void send_notice_message(struct lws* wsi, const char* message); +void send_notice_message(struct lws* wsi, struct per_session_data* pss, const char* message); // Forward declarations for NIP-42 functions from request_validator.c int nostr_nip42_generate_challenge(char *challenge_buffer, size_t buffer_size); @@ -34,7 +34,7 @@ void send_nip42_auth_challenge(struct lws* wsi, struct per_session_data* pss) { char challenge[65]; if (nostr_nip42_generate_challenge(challenge, sizeof(challenge)) != 0) { DEBUG_ERROR("Failed to generate NIP-42 challenge"); - send_notice_message(wsi, "Authentication temporarily unavailable"); + send_notice_message(wsi, pss, "Authentication temporarily unavailable"); return; } @@ -71,7 +71,7 @@ void handle_nip42_auth_signed_event(struct lws* wsi, struct per_session_data* ps // Serialize event for validation char* event_json = cJSON_Print(auth_event); if (!event_json) { - send_notice_message(wsi, "Invalid authentication event format"); + send_notice_message(wsi, pss, "Invalid authentication event format"); return; } @@ -86,7 +86,7 @@ void handle_nip42_auth_signed_event(struct lws* wsi, struct per_session_data* ps time_t current_time = time(NULL); if (current_time > challenge_expires) { free(event_json); - send_notice_message(wsi, "Authentication challenge expired, please retry"); + send_notice_message(wsi, pss, "Authentication challenge expired, please retry"); DEBUG_WARN("NIP-42 authentication failed: challenge expired"); return; } @@ -127,7 +127,7 @@ void handle_nip42_auth_signed_event(struct lws* wsi, struct per_session_data* ps pss->auth_challenge_sent = 0; pthread_mutex_unlock(&pss->session_lock); - send_notice_message(wsi, "NIP-42 authentication successful"); + send_notice_message(wsi, pss, "NIP-42 authentication successful"); } else { // Authentication failed char error_msg[256]; @@ -135,7 +135,7 @@ void handle_nip42_auth_signed_event(struct lws* wsi, struct per_session_data* ps "NIP-42 authentication failed (error code: %d)", result); DEBUG_WARN(error_msg); - send_notice_message(wsi, "NIP-42 authentication failed - invalid signature or challenge"); + send_notice_message(wsi, pss, "NIP-42 authentication failed - invalid signature or challenge"); } } @@ -146,5 +146,5 @@ void handle_nip42_auth_challenge_response(struct lws* wsi, struct per_session_da // NIP-42 doesn't typically use challenge responses from client to server // This is reserved for potential future use or protocol extensions DEBUG_WARN("Received unexpected challenge response from client (not part of standard NIP-42 flow)"); - send_notice_message(wsi, "Challenge responses are not supported - please send signed authentication event"); + send_notice_message(wsi, pss, "Challenge responses are not supported - please send signed authentication event"); } diff --git a/src/websockets.c b/src/websockets.c index 8200427..60ea224 100644 --- a/src/websockets.c +++ b/src/websockets.c @@ -94,7 +94,7 @@ void record_malformed_request(struct per_session_data *pss); int validate_filter_array(cJSON* filters, char* error_message, size_t error_size); // Forward declarations for NOTICE message support -void send_notice_message(struct lws* wsi, const char* message); +void send_notice_message(struct lws* wsi, struct per_session_data* pss, const char* message); // Configuration functions from config.c extern int get_config_bool(const char* key, int default_value); @@ -475,7 +475,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso // Check if client is rate limited for malformed requests if (is_client_rate_limited_for_malformed_requests(pss)) { - send_notice_message(wsi, "error: too many malformed requests - temporarily blocked"); + send_notice_message(wsi, pss, "error: too many malformed requests - temporarily blocked"); return 0; } @@ -522,7 +522,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso pss->reassembly_size = 0; pss->reassembly_capacity = 0; pss->reassembly_active = 0; - send_notice_message(wsi, "error: message too large - memory allocation failed"); + send_notice_message(wsi, pss, "error: message too large - memory allocation failed"); return 0; } pss->reassembly_buffer = new_buffer; @@ -895,7 +895,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso if (!pss->auth_challenge_sent) { send_nip42_auth_challenge(wsi, pss); } else { - send_notice_message(wsi, "NIP-42 authentication required for subscriptions"); + send_notice_message(wsi, pss, "NIP-42 authentication required for subscriptions"); DEBUG_WARN("REQ rejected: NIP-42 authentication required"); } cJSON_Delete(json); @@ -917,7 +917,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso // Validate subscription ID before processing if (!subscription_id) { DEBUG_TRACE("REQ rejected: NULL subscription ID"); - send_notice_message(wsi, "error: invalid subscription ID"); + send_notice_message(wsi, pss, "error: invalid subscription ID"); DEBUG_WARN("REQ rejected: NULL subscription ID"); record_malformed_request(pss); cJSON_Delete(json); @@ -929,7 +929,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso // Validate subscription ID if (!validate_subscription_id(subscription_id)) { DEBUG_TRACE("REQ rejected: invalid subscription ID format"); - send_notice_message(wsi, "error: invalid subscription ID"); + send_notice_message(wsi, pss, "error: invalid subscription ID"); DEBUG_WARN("REQ rejected: invalid subscription ID"); cJSON_Delete(json); // Note: complete_message points to reassembly_buffer, which is managed separately @@ -943,7 +943,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso cJSON* filters = cJSON_CreateArray(); if (!filters) { DEBUG_TRACE("REQ failed: could not create filters array"); - send_notice_message(wsi, "error: failed to process filters"); + send_notice_message(wsi, pss, "error: failed to process filters"); DEBUG_ERROR("REQ failed: could not create filters array"); cJSON_Delete(json); // Note: complete_message points to reassembly_buffer, which is managed separately @@ -967,7 +967,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso char filter_error[512] = {0}; if (!validate_filter_array(filters, filter_error, sizeof(filter_error))) { DEBUG_TRACE("REQ rejected: filter validation failed - %s", filter_error); - send_notice_message(wsi, filter_error); + send_notice_message(wsi, pss, filter_error); DEBUG_WARN("REQ rejected: invalid filters"); record_malformed_request(pss); cJSON_Delete(filters); @@ -1014,7 +1014,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso cJSON_Delete(eose_response); } } else { - send_notice_message(wsi, "error: missing or invalid subscription ID in REQ"); + send_notice_message(wsi, pss, "error: missing or invalid subscription ID in REQ"); DEBUG_WARN("REQ rejected: missing or invalid subscription ID"); } } else if (strcmp(msg_type, "COUNT") == 0) { @@ -1023,7 +1023,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso if (!pss->auth_challenge_sent) { send_nip42_auth_challenge(wsi, pss); } else { - send_notice_message(wsi, "NIP-42 authentication required for count requests"); + send_notice_message(wsi, pss, "NIP-42 authentication required for count requests"); DEBUG_WARN("COUNT rejected: NIP-42 authentication required"); } cJSON_Delete(json); @@ -1051,7 +1051,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso // Validate filters before processing char filter_error[512] = {0}; if (!validate_filter_array(filters, filter_error, sizeof(filter_error))) { - send_notice_message(wsi, filter_error); + send_notice_message(wsi, pss, filter_error); DEBUG_WARN("COUNT rejected: invalid filters"); record_malformed_request(pss); cJSON_Delete(filters); @@ -1074,7 +1074,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso // Validate subscription ID before processing if (!subscription_id) { - send_notice_message(wsi, "error: invalid subscription ID in CLOSE"); + send_notice_message(wsi, pss, "error: invalid subscription ID in CLOSE"); DEBUG_WARN("CLOSE rejected: NULL subscription ID"); cJSON_Delete(json); // Note: complete_message points to reassembly_buffer, which is managed separately @@ -1084,7 +1084,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso // Validate subscription ID if (!validate_subscription_id(subscription_id)) { - send_notice_message(wsi, "error: invalid subscription ID in CLOSE"); + send_notice_message(wsi, pss, "error: invalid subscription ID in CLOSE"); DEBUG_WARN("CLOSE rejected: invalid subscription ID"); cJSON_Delete(json); // Note: complete_message points to reassembly_buffer, which is managed separately @@ -1130,7 +1130,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso // Subscription closed } else { - send_notice_message(wsi, "error: missing or invalid subscription ID in CLOSE"); + send_notice_message(wsi, pss, "error: missing or invalid subscription ID in CLOSE"); DEBUG_WARN("CLOSE rejected: missing or invalid subscription ID"); } } else if (strcmp(msg_type, "AUTH") == 0) { @@ -1145,11 +1145,11 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso // AUTH signed event: ["AUTH", ] (standard NIP-42) handle_nip42_auth_signed_event(wsi, pss, auth_payload); } else { - send_notice_message(wsi, "Invalid AUTH message format"); + send_notice_message(wsi, pss, "Invalid AUTH message format"); DEBUG_WARN("Received AUTH message with invalid payload type"); } } else { - send_notice_message(wsi, "AUTH message requires payload"); + send_notice_message(wsi, pss, "AUTH message requires payload"); DEBUG_WARN("Received AUTH message without payload"); } } else { @@ -1157,7 +1157,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso char unknown_msg[128]; snprintf(unknown_msg, sizeof(unknown_msg), "Unknown message type: %.32s", msg_type); DEBUG_WARN(unknown_msg); - send_notice_message(wsi, "Unknown message type"); + send_notice_message(wsi, pss, "Unknown message type"); } } } @@ -1254,7 +1254,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso snprintf(auth_msg, sizeof(auth_msg), "NIP-42 authentication required for event kind %d", event_kind); } - send_notice_message(wsi, auth_msg); + send_notice_message(wsi, pss, auth_msg); DEBUG_WARN("Event rejected: NIP-42 authentication required for kind"); } cJSON_Delete(json); @@ -1597,7 +1597,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso if (!pss->auth_challenge_sent) { send_nip42_auth_challenge(wsi, pss); } else { - send_notice_message(wsi, "NIP-42 authentication required for subscriptions"); + send_notice_message(wsi, pss, "NIP-42 authentication required for subscriptions"); DEBUG_WARN("REQ rejected: NIP-42 authentication required"); } cJSON_Delete(json); @@ -1618,7 +1618,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso // Validate subscription ID before processing if (!subscription_id) { DEBUG_TRACE("REQ rejected: NULL subscription ID"); - send_notice_message(wsi, "error: invalid subscription ID"); + send_notice_message(wsi, pss, "error: invalid subscription ID"); DEBUG_WARN("REQ rejected: NULL subscription ID"); record_malformed_request(pss); cJSON_Delete(json); @@ -1629,7 +1629,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso // Validate subscription ID if (!validate_subscription_id(subscription_id)) { DEBUG_TRACE("REQ rejected: invalid subscription ID format"); - send_notice_message(wsi, "error: invalid subscription ID"); + send_notice_message(wsi, pss, "error: invalid subscription ID"); DEBUG_WARN("REQ rejected: invalid subscription ID"); cJSON_Delete(json); free(message); @@ -1642,7 +1642,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso cJSON* filters = cJSON_CreateArray(); if (!filters) { DEBUG_TRACE("REQ failed: could not create filters array"); - send_notice_message(wsi, "error: failed to process filters"); + send_notice_message(wsi, pss, "error: failed to process filters"); DEBUG_ERROR("REQ failed: could not create filters array"); cJSON_Delete(json); free(message); @@ -1665,7 +1665,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso char filter_error[512] = {0}; if (!validate_filter_array(filters, filter_error, sizeof(filter_error))) { DEBUG_TRACE("REQ rejected: filter validation failed - %s", filter_error); - send_notice_message(wsi, filter_error); + send_notice_message(wsi, pss, filter_error); DEBUG_WARN("REQ rejected: invalid filters"); record_malformed_request(pss); cJSON_Delete(filters); @@ -1711,7 +1711,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso cJSON_Delete(eose_response); } } else { - send_notice_message(wsi, "error: missing or invalid subscription ID in REQ"); + send_notice_message(wsi, pss, "error: missing or invalid subscription ID in REQ"); DEBUG_WARN("REQ rejected: missing or invalid subscription ID"); } } else if (strcmp(msg_type, "COUNT") == 0) { @@ -1720,7 +1720,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso if (!pss->auth_challenge_sent) { send_nip42_auth_challenge(wsi, pss); } else { - send_notice_message(wsi, "NIP-42 authentication required for count requests"); + send_notice_message(wsi, pss, "NIP-42 authentication required for count requests"); DEBUG_WARN("COUNT rejected: NIP-42 authentication required"); } cJSON_Delete(json); @@ -1747,7 +1747,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso // Validate filters before processing char filter_error[512] = {0}; if (!validate_filter_array(filters, filter_error, sizeof(filter_error))) { - send_notice_message(wsi, filter_error); + send_notice_message(wsi, pss, filter_error); DEBUG_WARN("COUNT rejected: invalid filters"); record_malformed_request(pss); cJSON_Delete(filters); @@ -1769,7 +1769,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso // Validate subscription ID before processing if (!subscription_id) { - send_notice_message(wsi, "error: invalid subscription ID in CLOSE"); + send_notice_message(wsi, pss, "error: invalid subscription ID in CLOSE"); DEBUG_WARN("CLOSE rejected: NULL subscription ID"); cJSON_Delete(json); free(message); @@ -1778,7 +1778,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso // Validate subscription ID if (!validate_subscription_id(subscription_id)) { - send_notice_message(wsi, "error: invalid subscription ID in CLOSE"); + send_notice_message(wsi, pss, "error: invalid subscription ID in CLOSE"); DEBUG_WARN("CLOSE rejected: invalid subscription ID"); cJSON_Delete(json); free(message); @@ -1823,7 +1823,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso // Subscription closed } else { - send_notice_message(wsi, "error: missing or invalid subscription ID in CLOSE"); + send_notice_message(wsi, pss, "error: missing or invalid subscription ID in CLOSE"); DEBUG_WARN("CLOSE rejected: missing or invalid subscription ID"); } } else if (strcmp(msg_type, "AUTH") == 0) { @@ -1838,11 +1838,11 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso // AUTH signed event: ["AUTH", ] (standard NIP-42) handle_nip42_auth_signed_event(wsi, pss, auth_payload); } else { - send_notice_message(wsi, "Invalid AUTH message format"); + send_notice_message(wsi, pss, "Invalid AUTH message format"); DEBUG_WARN("Received AUTH message with invalid payload type"); } } else { - send_notice_message(wsi, "AUTH message requires payload"); + send_notice_message(wsi, pss, "AUTH message requires payload"); DEBUG_WARN("Received AUTH message without payload"); } } else { @@ -1850,7 +1850,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso char unknown_msg[128]; snprintf(unknown_msg, sizeof(unknown_msg), "Unknown message type: %.32s", msg_type); DEBUG_WARN(unknown_msg); - send_notice_message(wsi, "Unknown message type"); + send_notice_message(wsi, pss, "Unknown message type"); } } } diff --git a/tests/invalid_kind_test.sh b/tests/invalid_kind_test.sh new file mode 100755 index 0000000..5151052 --- /dev/null +++ b/tests/invalid_kind_test.sh @@ -0,0 +1,101 @@ +#!/bin/bash + +# Test for invalid kind filter validation and NOTICE response +# This test verifies that the relay properly responds with a NOTICE message +# when a REQ contains an invalid kind value (> 65535 per NIP-01) + +RELAY_URL="ws://localhost:8888" +TEST_NAME="Invalid Kind Filter Test" + +echo "==========================================" +echo "$TEST_NAME" +echo "==========================================" +echo "" + +# Test 1: Send REQ with invalid kind (99999 > 65535) +echo "Test 1: REQ with invalid kind 99999 (should receive NOTICE)" +echo "---" + +RESPONSE=$(timeout 3 websocat "$RELAY_URL" <