Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4ef71d673 | ||
|
|
086d2af56c |
@@ -1,19 +1,12 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
# Restart the service
|
||||||
|
sudo systemctl stop c-relay.service
|
||||||
|
|
||||||
# Copy the binary to the deployment location
|
# Copy the binary to the deployment location
|
||||||
cp build/c_relay_x86 ~/Storage/c_relay/crelay
|
cp build/c_relay_static_x86_64 ~/Storage/c_relay/crelay
|
||||||
|
|
||||||
# Copy the service file to systemd (use the main service file)
|
|
||||||
sudo cp systemd/c-relay.service /etc/systemd/system/c-relay-local.service
|
|
||||||
|
|
||||||
# Reload systemd daemon to pick up the new service
|
|
||||||
sudo systemctl daemon-reload
|
|
||||||
|
|
||||||
# Enable the service (if not already enabled)
|
|
||||||
sudo systemctl enable c-relay-local.service
|
|
||||||
|
|
||||||
# Restart the service
|
# Restart the service
|
||||||
sudo systemctl restart c-relay-local.service
|
sudo systemctl restart c-relay.service
|
||||||
|
|
||||||
# Show service status
|
# Show service status
|
||||||
sudo systemctl status c-relay-local.service --no-pager -l
|
sudo systemctl status c-relay.service --no-pager -l
|
||||||
|
|||||||
30
src/config.c
30
src/config.c
@@ -2553,20 +2553,46 @@ int handle_kind_23456_unified(cJSON* event, char* error_message, size_t error_si
|
|||||||
snprintf(error_message, error_size, "error: failed to convert sender public key");
|
snprintf(error_message, error_size, "error: failed to convert sender public key");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEBUG_LOG("=== NIP-44 DECRYPTION DEBUG (Level 5) ===");
|
||||||
|
DEBUG_LOG("Relay privkey (first 16 chars): %.16s...", relay_privkey);
|
||||||
|
DEBUG_LOG("Sender pubkey: %s", sender_pubkey);
|
||||||
|
DEBUG_LOG("Content length: %zu", strlen(content));
|
||||||
|
DEBUG_LOG("Content (first 100 chars): %.100s%s", content, strlen(content) > 100 ? "..." : "");
|
||||||
|
|
||||||
// Perform NIP-44 decryption (relay as recipient, admin as sender)
|
// Perform NIP-44 decryption (relay as recipient, admin as sender)
|
||||||
|
DEBUG_LOG("Calling nostr_nip44_decrypt...");
|
||||||
|
DEBUG_LOG(" relay_privkey_bytes (first 8 bytes hex): %02x%02x%02x%02x%02x%02x%02x%02x...",
|
||||||
|
relay_privkey_bytes[0], relay_privkey_bytes[1], relay_privkey_bytes[2], relay_privkey_bytes[3],
|
||||||
|
relay_privkey_bytes[4], relay_privkey_bytes[5], relay_privkey_bytes[6], relay_privkey_bytes[7]);
|
||||||
|
DEBUG_LOG(" sender_pubkey_bytes (first 8 bytes hex): %02x%02x%02x%02x%02x%02x%02x%02x...",
|
||||||
|
sender_pubkey_bytes[0], sender_pubkey_bytes[1], sender_pubkey_bytes[2], sender_pubkey_bytes[3],
|
||||||
|
sender_pubkey_bytes[4], sender_pubkey_bytes[5], sender_pubkey_bytes[6], sender_pubkey_bytes[7]);
|
||||||
|
|
||||||
char decrypted_text[16384]; // Buffer for decrypted content (16KB)
|
char decrypted_text[16384]; // Buffer for decrypted content (16KB)
|
||||||
int decrypt_result = nostr_nip44_decrypt(relay_privkey_bytes, sender_pubkey_bytes, content, decrypted_text, sizeof(decrypted_text));
|
int decrypt_result = nostr_nip44_decrypt(relay_privkey_bytes, sender_pubkey_bytes, content, decrypted_text, sizeof(decrypted_text));
|
||||||
|
|
||||||
|
DEBUG_LOG("nostr_nip44_decrypt returned: %d (NOSTR_SUCCESS=%d)", decrypt_result, NOSTR_SUCCESS);
|
||||||
|
|
||||||
// Clean up private key immediately after use
|
// Clean up private key immediately after use
|
||||||
memset(relay_privkey_bytes, 0, 32);
|
memset(relay_privkey_bytes, 0, 32);
|
||||||
free(relay_privkey);
|
free(relay_privkey);
|
||||||
|
|
||||||
if (decrypt_result != NOSTR_SUCCESS) {
|
if (decrypt_result != NOSTR_SUCCESS) {
|
||||||
DEBUG_ERROR("error: NIP-44 decryption failed");
|
DEBUG_ERROR("error: NIP-44 decryption failed with code %d", decrypt_result);
|
||||||
snprintf(error_message, error_size, "error: NIP-44 decryption failed");
|
DEBUG_ERROR(" This means the encrypted content cannot be decrypted with the provided keys");
|
||||||
|
DEBUG_ERROR(" Possible causes:");
|
||||||
|
DEBUG_ERROR(" 1. Content was encrypted for a different relay pubkey");
|
||||||
|
DEBUG_ERROR(" 2. Content format is incompatible (wrong NIP-44 version)");
|
||||||
|
DEBUG_ERROR(" 3. Content is corrupted or malformed");
|
||||||
|
snprintf(error_message, error_size, "error: NIP-44 decryption failed (code: %d)", decrypt_result);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEBUG_LOG("✓ NIP-44 decryption successful!");
|
||||||
|
DEBUG_LOG("Decrypted text length: %zu", strlen(decrypted_text));
|
||||||
|
DEBUG_LOG("Decrypted text (first 200 chars): %.200s%s", decrypted_text, strlen(decrypted_text) > 200 ? "..." : "");
|
||||||
|
DEBUG_LOG("=== END NIP-44 DECRYPTION DEBUG ===");
|
||||||
|
|
||||||
// Parse decrypted content as command array directly (NOT as NIP-17 inner event)
|
// Parse decrypted content as command array directly (NOT as NIP-17 inner event)
|
||||||
// Kind 23456 events contain direct command arrays: ["command_name", arg1, arg2, ...]
|
// Kind 23456 events contain direct command arrays: ["command_name", arg1, arg2, ...]
|
||||||
|
|||||||
@@ -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 0
|
#define CRELAY_VERSION_PATCH 2
|
||||||
#define CRELAY_VERSION "v1.2.0"
|
#define CRELAY_VERSION "v1.2.2"
|
||||||
|
|
||||||
// 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"
|
||||||
|
|||||||
@@ -265,11 +265,11 @@ int validate_subscription_id(const char* sub_id) {
|
|||||||
return 0; // Empty or too long
|
return 0; // Empty or too long
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for valid characters (alphanumeric, underscore, hyphen, colon, comma)
|
// Check for valid characters (alphanumeric, underscore, hyphen, colon, comma, plus)
|
||||||
for (size_t i = 0; i < len; i++) {
|
for (size_t i = 0; i < len; i++) {
|
||||||
char c = sub_id[i];
|
char c = sub_id[i];
|
||||||
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
||||||
(c >= '0' && c <= '9') || c == '_' || c == '-' || c == ':' || c == ',')) {
|
(c >= '0' && c <= '9') || c == '_' || c == '-' || c == ':' || c == ',' || c == '+')) {
|
||||||
return 0; // Invalid character
|
return 0; // Invalid character
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1456,6 +1456,10 @@ int validate_search_term(const char* search_term, char* error_message, size_t er
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate all filter values in a filter object
|
* Validate all filter values in a filter object
|
||||||
|
* Returns:
|
||||||
|
* 1 = valid
|
||||||
|
* 0 = invalid (malformed, should count toward rate limit)
|
||||||
|
* -1 = invalid but benign (e.g., kind 99999 from NDK ping, should not count toward rate limit)
|
||||||
*/
|
*/
|
||||||
int validate_filter_values(cJSON* filter_json, char* error_message, size_t error_size) {
|
int validate_filter_values(cJSON* filter_json, char* error_message, size_t error_size) {
|
||||||
if (!filter_json || !cJSON_IsObject(filter_json)) {
|
if (!filter_json || !cJSON_IsObject(filter_json)) {
|
||||||
@@ -1463,6 +1467,8 @@ int validate_filter_values(cJSON* filter_json, char* error_message, size_t error
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int has_kind_99999 = 0; // Track if we encounter kind 99999 (NDK ping)
|
||||||
|
|
||||||
// Validate kinds array
|
// Validate kinds array
|
||||||
cJSON* kinds = cJSON_GetObjectItem(filter_json, "kinds");
|
cJSON* kinds = cJSON_GetObjectItem(filter_json, "kinds");
|
||||||
if (kinds) {
|
if (kinds) {
|
||||||
@@ -1485,11 +1491,25 @@ int validate_filter_values(cJSON* filter_json, char* error_message, size_t error
|
|||||||
}
|
}
|
||||||
|
|
||||||
int kind_val = (int)cJSON_GetNumberValue(kind_item);
|
int kind_val = (int)cJSON_GetNumberValue(kind_item);
|
||||||
|
|
||||||
|
// Special case: kind 99999 is used by NDK for ping/connectivity checks
|
||||||
|
// We reject it but don't count it as a malformed request
|
||||||
|
if (kind_val == 99999) {
|
||||||
|
has_kind_99999 = 1;
|
||||||
|
snprintf(error_message, error_size, "kinds[%d]: invalid event kind %d (used by NDK for ping)", i, kind_val);
|
||||||
|
continue; // Continue checking other kinds
|
||||||
|
}
|
||||||
|
|
||||||
if (kind_val < 0 || kind_val > 65535) { // Reasonable range for event kinds
|
if (kind_val < 0 || kind_val > 65535) { // Reasonable range for event kinds
|
||||||
snprintf(error_message, error_size, "kinds[%d]: invalid event kind %d", i, kind_val);
|
snprintf(error_message, error_size, "kinds[%d]: invalid event kind %d", i, kind_val);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we only found kind 99999 and no other validation errors, return -1 (benign error)
|
||||||
|
if (has_kind_99999) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate authors array
|
// Validate authors array
|
||||||
|
|||||||
@@ -975,11 +975,15 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso
|
|||||||
|
|
||||||
// Validate filters before processing
|
// Validate filters before processing
|
||||||
char filter_error[512] = {0};
|
char filter_error[512] = {0};
|
||||||
if (!validate_filter_array(filters, filter_error, sizeof(filter_error))) {
|
int validation_result = validate_filter_array(filters, filter_error, sizeof(filter_error));
|
||||||
|
if (validation_result <= 0) {
|
||||||
DEBUG_TRACE("REQ rejected: filter validation failed - %s", filter_error);
|
DEBUG_TRACE("REQ rejected: filter validation failed - %s", filter_error);
|
||||||
send_notice_message(wsi, pss, filter_error);
|
send_notice_message(wsi, pss, filter_error);
|
||||||
DEBUG_WARN("REQ rejected: invalid filters");
|
DEBUG_WARN("REQ rejected: invalid filters");
|
||||||
record_malformed_request(pss);
|
// Only record as malformed if it's a true error (0), not benign error (-1)
|
||||||
|
if (validation_result == 0) {
|
||||||
|
record_malformed_request(pss);
|
||||||
|
}
|
||||||
cJSON_Delete(filters);
|
cJSON_Delete(filters);
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
// Note: complete_message points to reassembly_buffer, which is managed separately
|
// Note: complete_message points to reassembly_buffer, which is managed separately
|
||||||
@@ -1060,10 +1064,14 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso
|
|||||||
|
|
||||||
// Validate filters before processing
|
// Validate filters before processing
|
||||||
char filter_error[512] = {0};
|
char filter_error[512] = {0};
|
||||||
if (!validate_filter_array(filters, filter_error, sizeof(filter_error))) {
|
int validation_result = validate_filter_array(filters, filter_error, sizeof(filter_error));
|
||||||
|
if (validation_result <= 0) {
|
||||||
send_notice_message(wsi, pss, filter_error);
|
send_notice_message(wsi, pss, filter_error);
|
||||||
DEBUG_WARN("COUNT rejected: invalid filters");
|
DEBUG_WARN("COUNT rejected: invalid filters");
|
||||||
record_malformed_request(pss);
|
// Only record as malformed if it's a true error (0), not benign error (-1)
|
||||||
|
if (validation_result == 0) {
|
||||||
|
record_malformed_request(pss);
|
||||||
|
}
|
||||||
cJSON_Delete(filters);
|
cJSON_Delete(filters);
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
// Note: complete_message points to reassembly_buffer, which is managed separately
|
// Note: complete_message points to reassembly_buffer, which is managed separately
|
||||||
@@ -1673,11 +1681,15 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso
|
|||||||
|
|
||||||
// Validate filters before processing
|
// Validate filters before processing
|
||||||
char filter_error[512] = {0};
|
char filter_error[512] = {0};
|
||||||
if (!validate_filter_array(filters, filter_error, sizeof(filter_error))) {
|
int validation_result = validate_filter_array(filters, filter_error, sizeof(filter_error));
|
||||||
|
if (validation_result <= 0) {
|
||||||
DEBUG_TRACE("REQ rejected: filter validation failed - %s", filter_error);
|
DEBUG_TRACE("REQ rejected: filter validation failed - %s", filter_error);
|
||||||
send_notice_message(wsi, pss, filter_error);
|
send_notice_message(wsi, pss, filter_error);
|
||||||
DEBUG_WARN("REQ rejected: invalid filters");
|
DEBUG_WARN("REQ rejected: invalid filters");
|
||||||
record_malformed_request(pss);
|
// Only record as malformed if it's a true error (0), not benign error (-1)
|
||||||
|
if (validation_result == 0) {
|
||||||
|
record_malformed_request(pss);
|
||||||
|
}
|
||||||
cJSON_Delete(filters);
|
cJSON_Delete(filters);
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
free(message);
|
free(message);
|
||||||
@@ -1756,10 +1768,14 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso
|
|||||||
|
|
||||||
// Validate filters before processing
|
// Validate filters before processing
|
||||||
char filter_error[512] = {0};
|
char filter_error[512] = {0};
|
||||||
if (!validate_filter_array(filters, filter_error, sizeof(filter_error))) {
|
int validation_result = validate_filter_array(filters, filter_error, sizeof(filter_error));
|
||||||
|
if (validation_result <= 0) {
|
||||||
send_notice_message(wsi, pss, filter_error);
|
send_notice_message(wsi, pss, filter_error);
|
||||||
DEBUG_WARN("COUNT rejected: invalid filters");
|
DEBUG_WARN("COUNT rejected: invalid filters");
|
||||||
record_malformed_request(pss);
|
// Only record as malformed if it's a true error (0), not benign error (-1)
|
||||||
|
if (validation_result == 0) {
|
||||||
|
record_malformed_request(pss);
|
||||||
|
}
|
||||||
cJSON_Delete(filters);
|
cJSON_Delete(filters);
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
free(message);
|
free(message);
|
||||||
@@ -2871,6 +2887,10 @@ int is_valid_hex_string(const char* str, size_t expected_len) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate a filter array for REQ and COUNT messages
|
* Validate a filter array for REQ and COUNT messages
|
||||||
|
* Returns:
|
||||||
|
* 1 = valid
|
||||||
|
* 0 = invalid (malformed, should count toward rate limit)
|
||||||
|
* -1 = invalid but benign (e.g., kind 99999 from NDK ping, should not count toward rate limit)
|
||||||
*/
|
*/
|
||||||
int validate_filter_array(cJSON* filters, char* error_message, size_t error_size) {
|
int validate_filter_array(cJSON* filters, char* error_message, size_t error_size) {
|
||||||
if (!filters || !cJSON_IsArray(filters)) {
|
if (!filters || !cJSON_IsArray(filters)) {
|
||||||
@@ -2884,6 +2904,8 @@ int validate_filter_array(cJSON* filters, char* error_message, size_t error_size
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int has_kind_99999 = 0; // Track if we encounter kind 99999 (NDK ping)
|
||||||
|
|
||||||
// Validate each filter object
|
// Validate each filter object
|
||||||
for (int i = 0; i < filter_count; i++) {
|
for (int i = 0; i < filter_count; i++) {
|
||||||
cJSON* filter = cJSON_GetArrayItem(filters, i);
|
cJSON* filter = cJSON_GetArrayItem(filters, i);
|
||||||
@@ -2964,6 +2986,15 @@ int validate_filter_array(cJSON* filters, char* error_message, size_t error_size
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
int kind_val = (int)cJSON_GetNumberValue(kind);
|
int kind_val = (int)cJSON_GetNumberValue(kind);
|
||||||
|
|
||||||
|
// Special case: kind 99999 is used by NDK (Nostr Development Kit) for ping/connectivity checks
|
||||||
|
// We reject it but don't count it as a malformed request to avoid rate limiting NDK clients
|
||||||
|
if (kind_val == 99999) {
|
||||||
|
has_kind_99999 = 1;
|
||||||
|
snprintf(error_message, error_size, "error: invalid kind value %d (NDK ping)", kind_val);
|
||||||
|
continue; // Continue checking other kinds
|
||||||
|
}
|
||||||
|
|
||||||
if (kind_val < 0 || kind_val > MAX_KIND_VALUE) {
|
if (kind_val < 0 || kind_val > MAX_KIND_VALUE) {
|
||||||
snprintf(error_message, error_size, "error: invalid kind value %d", kind_val);
|
snprintf(error_message, error_size, "error: invalid kind value %d", kind_val);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -3041,5 +3072,10 @@ int validate_filter_array(cJSON* filters, char* error_message, size_t error_size
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we found kind 99999 (NDK ping), return -1 to indicate benign error
|
||||||
|
if (has_kind_99999) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return 1; // All filters valid
|
return 1; // All filters valid
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user