Compare commits

..
6 Commits
6 changed files with 71 additions and 24 deletions
+31 -2
View File
@@ -1338,6 +1338,23 @@ async function subscribeToConfiguration() {
console.log('No configuration events were received');
}
},
async onauth(challenge) {
// NIP-42 authentication: relay sent AUTH challenge, sign and respond
console.log('🔐 NIP-42 AUTH challenge received:', challenge);
try {
if (!window.nostr) {
console.error('NIP-42 auth required but no nostr extension available');
return;
}
const url = relayConnectionUrl.value.trim();
const authEvent = window.NostrTools.nip42.makeAuthEvent(url, challenge);
const signedAuthEvent = await window.nostr.signEvent(authEvent);
console.log('✅ NIP-42 AUTH event signed, responding to challenge');
return signedAuthEvent;
} catch (authError) {
console.error('NIP-42 AUTH failed:', authError.message);
}
},
onclose(reason) {
console.log('Subscription closed:', reason);
// Reset subscription state to allow re-subscription
@@ -5467,9 +5484,21 @@ async function sendAdminCommand(commandArray) {
throw new Error('Event signing failed');
}
// Publish via SimplePool with detailed error diagnostics
// Publish via SimplePool with NIP-42 auth support
const url = relayConnectionUrl.value.trim();
const publishPromises = relayPool.publish([url], signedEvent);
const publishPromises = relayPool.publish([url], signedEvent, {
async onauth(challenge) {
console.log('🔐 NIP-42 AUTH challenge during publish:', challenge);
try {
const authEvent = window.NostrTools.nip42.makeAuthEvent(url, challenge);
const signedAuthEvent = await window.nostr.signEvent(authEvent);
console.log('✅ NIP-42 AUTH signed for publish');
return signedAuthEvent;
} catch (authError) {
console.error('NIP-42 AUTH failed during publish:', authError.message);
}
}
});
// Use Promise.allSettled to capture per-relay outcomes
const results = await Promise.allSettled(publishPromises);
+1 -1
View File
@@ -1 +1 @@
1950899
2039374
File diff suppressed because one or more lines are too long
+17 -2
View File
@@ -196,8 +196,23 @@ void ip_ban_cleanup(void) {
int window_expired = (entry->first_failure == 0 || (now - entry->first_failure) > window_sec * 10);
if (ban_expired && window_expired && entry->failure_count == 0) {
memset(entry, 0, sizeof(ip_ban_entry_t));
cleaned++;
// Retain ban_count for 24 hours after the last ban expired.
// This ensures exponential backoff persists if the IP returns within 24 hours.
// After 24 hours of inactivity, fully clean the entry.
int retain_sec = 86400; // 24 hours
int last_ban_expired_long_ago = (entry->banned_until == 0 ||
(now - entry->banned_until) > retain_sec);
if (last_ban_expired_long_ago) {
// Fully clean — IP has been gone for 24+ hours, start fresh if it returns
memset(entry, 0, sizeof(ip_ban_entry_t));
cleaned++;
} else {
// Keep entry alive but reset transient fields — preserve ban_count
entry->failure_count = 0;
entry->first_failure = 0;
// banned_until and ban_count preserved intentionally
}
}
}
+2 -2
View File
@@ -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 19
#define CRELAY_VERSION "v1.2.19"
#define CRELAY_VERSION_PATCH 25
#define CRELAY_VERSION "v1.2.25"
// Relay metadata (authoritative source for NIP-11 information)
#define RELAY_NAME "C-Relay"
+17 -14
View File
@@ -463,17 +463,6 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso
case LWS_CALLBACK_ESTABLISHED:
DEBUG_TRACE("WebSocket connection established");
// Check IP ban before doing any work — reject banned IPs immediately
{
char check_ip[CLIENT_IP_MAX_LENGTH] = {0};
const char* fwd = lws_get_peer_simple(wsi, check_ip, sizeof(check_ip));
(void)fwd;
if (ip_ban_is_banned(check_ip)) {
DEBUG_LOG("Rejecting banned IP %s at connection establishment", check_ip);
return -1; // Close connection immediately
}
}
memset(pss, 0, sizeof(*pss));
pthread_mutex_init(&pss->session_lock, NULL);
@@ -558,6 +547,14 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso
pss->challenge_created = 0;
pss->challenge_expires = 0;
// Check IP ban using the resolved client IP (which may be from X-Forwarded-For).
// This must happen AFTER pss->client_ip is populated so the same IP string
// is used for both ban recording (at CLOSED) and ban checking (here).
if (ip_ban_is_banned(pss->client_ip)) {
DEBUG_LOG("Rejecting banned IP %s at connection establishment", pss->client_ip);
return -1; // Close connection immediately — no challenge, no processing
}
// Set libwebsockets auth timeout: if NIP-42 auth is required and the client
// doesn't authenticate within nip42_auth_timeout_sec seconds, lws will close
// the connection automatically — even if the client never sends a message.
@@ -2492,11 +2489,17 @@ int start_websocket_relay(int port_override, int strict_port) {
}
}
// Check connection age limits (every 60 seconds)
// Check connection age limits and run IP ban maintenance (every 60 seconds)
int max_connection_seconds = get_config_int("max_connection_seconds", 86400);
if (max_connection_seconds > 0 && (current_time - last_connection_age_check >= 60)) {
if (current_time - last_connection_age_check >= 60) {
last_connection_age_check = current_time;
check_connection_age(max_connection_seconds);
if (max_connection_seconds > 0) {
check_connection_age(max_connection_seconds);
} else {
// Even when connection age limit is disabled, run IP ban maintenance
ip_ban_cleanup();
ip_ban_log_stats();
}
}
}