Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e833dcefd4 | ||
|
|
29680f0ee8 |
@@ -218,9 +218,13 @@ button:disabled {
|
||||
.config-actions-cell {
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
text-align: center;
|
||||
text-align: center !important;
|
||||
font-weight: bold;
|
||||
vertical-align: middle;
|
||||
width: 60px;
|
||||
min-width: 60px;
|
||||
max-width: 60px;
|
||||
padding: 8px 4px;
|
||||
}
|
||||
|
||||
.config-actions-cell:hover {
|
||||
|
||||
175
api/index.html
175
api/index.html
@@ -86,99 +86,12 @@
|
||||
|
||||
</div> <!-- End Main Sections Wrapper -->
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Testing Section -->
|
||||
<div id="div_config" class="section flex-section" style="display: none;">
|
||||
<h2>RELAY CONFIGURATION</h2>
|
||||
<div id="config-display" class="hidden">
|
||||
<div class="config-table-container">
|
||||
<table class="config-table" id="config-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Parameter</th>
|
||||
<th>Value</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="config-table-body">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="inline-buttons">
|
||||
<button type="button" id="fetch-config-btn">REFRESH</button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Auth Rules Management - Moved after configuration -->
|
||||
<div class="section flex-section" id="authRulesSection" style="display: none;">
|
||||
<div class="section-header">
|
||||
<h2>AUTH RULES MANAGEMENT</h2>
|
||||
</div>
|
||||
|
||||
<!-- Auth Rules Table -->
|
||||
<div id="authRulesTableContainer" style="display: none;">
|
||||
<table class="config-table" id="authRulesTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Rule Type</th>
|
||||
<th>Pattern Type</th>
|
||||
<th>Pattern Value</th>
|
||||
<th>Action</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="authRulesTableBody">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Simplified Auth Rule Input Section -->
|
||||
<div id="authRuleInputSections" style="display: block;">
|
||||
|
||||
<!-- Combined Pubkey Auth Rule Section -->
|
||||
|
||||
|
||||
<div class="input-group">
|
||||
<label for="authRulePubkey">Pubkey (nsec or hex):</label>
|
||||
<input type="text" id="authRulePubkey" placeholder="nsec1... or 64-character hex pubkey">
|
||||
|
||||
</div>
|
||||
<div id="whitelistWarning" class="warning-box" style="display: none;">
|
||||
<strong>⚠️ WARNING:</strong> Adding whitelist rules changes relay behavior to whitelist-only
|
||||
mode.
|
||||
Only whitelisted users will be able to interact with the relay.
|
||||
</div>
|
||||
<div class="inline-buttons">
|
||||
<button type="button" id="addWhitelistBtn" onclick="addWhitelistRule()">ADD TO
|
||||
WHITELIST</button>
|
||||
<button type="button" id="addBlacklistBtn" onclick="addBlacklistRule()">ADD TO
|
||||
BLACKLIST</button>
|
||||
<button type="button" id="refreshAuthRulesBtn">REFRESH</button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- DATABASE STATISTICS Section -->
|
||||
<div class="section">
|
||||
<div class="section flex-section" id="databaseStatisticsSection" style="display: none;">
|
||||
<div class="section-header">
|
||||
<h2>DATABASE STATISTICS</h2>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- Database Overview Table -->
|
||||
<div class="input-group">
|
||||
@@ -300,6 +213,90 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Testing Section -->
|
||||
<div id="div_config" class="section flex-section" style="display: none;">
|
||||
<h2>RELAY CONFIGURATION</h2>
|
||||
<div id="config-display" class="hidden">
|
||||
<div class="config-table-container">
|
||||
<table class="config-table" id="config-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Parameter</th>
|
||||
<th>Value</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="config-table-body">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="inline-buttons">
|
||||
<button type="button" id="fetch-config-btn">REFRESH</button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Auth Rules Management - Moved after configuration -->
|
||||
<div class="section flex-section" id="authRulesSection" style="display: none;">
|
||||
<div class="section-header">
|
||||
<h2>AUTH RULES MANAGEMENT</h2>
|
||||
</div>
|
||||
|
||||
<!-- Auth Rules Table -->
|
||||
<div id="authRulesTableContainer" style="display: none;">
|
||||
<table class="config-table" id="authRulesTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Rule Type</th>
|
||||
<th>Pattern Type</th>
|
||||
<th>Pattern Value</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="authRulesTableBody">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Simplified Auth Rule Input Section -->
|
||||
<div id="authRuleInputSections" style="display: block;">
|
||||
|
||||
<!-- Combined Pubkey Auth Rule Section -->
|
||||
|
||||
|
||||
<div class="input-group">
|
||||
<label for="authRulePubkey">Pubkey (nsec or hex):</label>
|
||||
<input type="text" id="authRulePubkey" placeholder="nsec1... or 64-character hex pubkey">
|
||||
|
||||
</div>
|
||||
<div id="whitelistWarning" class="warning-box" style="display: none;">
|
||||
<strong>⚠️ WARNING:</strong> Adding whitelist rules changes relay behavior to whitelist-only
|
||||
mode.
|
||||
Only whitelisted users will be able to interact with the relay.
|
||||
</div>
|
||||
<div class="inline-buttons">
|
||||
<button type="button" id="addWhitelistBtn" onclick="addWhitelistRule()">ADD TO
|
||||
WHITELIST</button>
|
||||
<button type="button" id="addBlacklistBtn" onclick="addBlacklistRule()">ADD TO
|
||||
BLACKLIST</button>
|
||||
<button type="button" id="refreshAuthRulesBtn">REFRESH</button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- NIP-17 DIRECT MESSAGES Section -->
|
||||
<div class="section" id="nip17DMSection" style="display: none;">
|
||||
<div class="section-header">
|
||||
|
||||
86
api/index.js
86
api/index.js
@@ -607,11 +607,13 @@
|
||||
function updateAdminSectionsVisibility() {
|
||||
const divConfig = document.getElementById('div_config');
|
||||
const authRulesSection = document.getElementById('authRulesSection');
|
||||
const databaseStatisticsSection = document.getElementById('databaseStatisticsSection');
|
||||
const nip17DMSection = document.getElementById('nip17DMSection');
|
||||
const shouldShow = isLoggedIn && isRelayConnected;
|
||||
|
||||
if (divConfig) divConfig.style.display = shouldShow ? 'block' : 'none';
|
||||
if (authRulesSection) authRulesSection.style.display = shouldShow ? 'block' : 'none';
|
||||
if (databaseStatisticsSection) databaseStatisticsSection.style.display = shouldShow ? 'block' : 'none';
|
||||
if (nip17DMSection) nip17DMSection.style.display = shouldShow ? 'block' : 'none';
|
||||
}
|
||||
|
||||
@@ -806,7 +808,7 @@
|
||||
|
||||
// Add to inbox
|
||||
const timestamp = new Date(event.created_at * 1000).toLocaleString();
|
||||
addMessageToInbox('received', decryptedContent, timestamp);
|
||||
addMessageToInbox('received', decryptedContent, timestamp, event.pubkey);
|
||||
|
||||
// Log for testing
|
||||
if (typeof logTestEvent === 'function') {
|
||||
@@ -843,7 +845,7 @@
|
||||
|
||||
// Add to inbox
|
||||
const timestamp = new Date(event.created_at * 1000).toLocaleString();
|
||||
addMessageToInbox('received', rumor.content, timestamp);
|
||||
addMessageToInbox('received', rumor.content, timestamp, rumor.pubkey);
|
||||
|
||||
// Log for testing
|
||||
if (typeof logTestEvent === 'function') {
|
||||
@@ -1846,7 +1848,6 @@
|
||||
<td>${rule.rule_type}</td>
|
||||
<td>${rule.pattern_type || rule.operation || '-'}</td>
|
||||
<td style="font-family: 'Courier New', monospace; font-size: 12px; word-break: break-all; max-width: 200px;">${rule.pattern_value || rule.rule_target || '-'}</td>
|
||||
<td>${rule.action || 'allow'}</td>
|
||||
<td>${rule.enabled !== false ? 'Active' : 'Inactive'}</td>
|
||||
<td>
|
||||
<div class="inline-buttons">
|
||||
@@ -2131,7 +2132,7 @@
|
||||
const originalShowMainInterface = showMainInterface;
|
||||
showMainInterface = function () {
|
||||
originalShowMainInterface();
|
||||
showAuthRulesSection();
|
||||
// Removed showAuthRulesSection() call - visibility now handled by updateAdminSectionsVisibility()
|
||||
};
|
||||
|
||||
// Auth rules event handlers
|
||||
@@ -2157,7 +2158,7 @@
|
||||
// STREAMLINED AUTH RULE FUNCTIONS
|
||||
// ================================
|
||||
|
||||
// Utility function to convert nsec to hex pubkey
|
||||
// Utility function to convert nsec to hex pubkey or npub to hex pubkey
|
||||
function nsecToHex(input) {
|
||||
if (!input || input.trim().length === 0) {
|
||||
return null;
|
||||
@@ -2176,11 +2177,17 @@
|
||||
if (window.NostrTools && window.NostrTools.nip19 && window.NostrTools.nip19.decode) {
|
||||
const decoded = window.NostrTools.nip19.decode(trimmed);
|
||||
if (decoded.type === 'nsec') {
|
||||
// Convert bytes to hex
|
||||
const hexPubkey = Array.from(decoded.data)
|
||||
.map(b => b.toString(16).padStart(2, '0'))
|
||||
.join('');
|
||||
return hexPubkey;
|
||||
// Handle different versions of nostr-tools
|
||||
if (typeof decoded.data === 'string') {
|
||||
// v1 style - data is already hex
|
||||
return decoded.data;
|
||||
} else {
|
||||
// v2 style - data is Uint8Array
|
||||
const hexPubkey = Array.from(decoded.data)
|
||||
.map(b => b.toString(16).padStart(2, '0'))
|
||||
.join('');
|
||||
return hexPubkey;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -2189,6 +2196,31 @@
|
||||
}
|
||||
}
|
||||
|
||||
// If it starts with npub1, try to decode to hex
|
||||
if (trimmed.startsWith('npub1')) {
|
||||
try {
|
||||
if (window.NostrTools && window.NostrTools.nip19 && window.NostrTools.nip19.decode) {
|
||||
const decoded = window.NostrTools.nip19.decode(trimmed);
|
||||
if (decoded.type === 'npub') {
|
||||
// Handle different versions of nostr-tools
|
||||
if (typeof decoded.data === 'string') {
|
||||
// v1 style - data is already hex
|
||||
return decoded.data;
|
||||
} else {
|
||||
// v2 style - data is Uint8Array
|
||||
const hexPubkey = Array.from(decoded.data)
|
||||
.map(b => b.toString(16).padStart(2, '0'))
|
||||
.join('');
|
||||
return hexPubkey;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to decode npub:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null; // Invalid format
|
||||
}
|
||||
|
||||
@@ -2204,10 +2236,10 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert nsec to hex if needed
|
||||
// Convert nsec or npub to hex if needed
|
||||
const hexPubkey = nsecToHex(inputValue);
|
||||
if (!hexPubkey) {
|
||||
log('Invalid pubkey format. Please enter nsec1... or 64-character hex', 'ERROR');
|
||||
log('Invalid pubkey format. Please enter nsec1..., npub1..., or 64-character hex', 'ERROR');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2256,10 +2288,10 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert nsec to hex if needed
|
||||
// Convert nsec or npub to hex if needed
|
||||
const hexPubkey = nsecToHex(inputValue);
|
||||
if (!hexPubkey) {
|
||||
log('Invalid pubkey format. Please enter nsec1... or 64-character hex', 'ERROR');
|
||||
log('Invalid pubkey format. Please enter nsec1..., npub1..., or 64-character hex', 'ERROR');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3063,21 +3095,32 @@
|
||||
}
|
||||
|
||||
// Add message to inbox display
|
||||
function addMessageToInbox(direction, message, timestamp) {
|
||||
function addMessageToInbox(direction, message, timestamp, pubkey = null) {
|
||||
if (!dmInbox) return;
|
||||
|
||||
const messageDiv = document.createElement('div');
|
||||
messageDiv.className = 'log-entry';
|
||||
|
||||
const directionColor = direction === 'sent' ? '#007bff' : '#28a745';
|
||||
|
||||
|
||||
// Convert newlines to <br> tags for proper HTML display
|
||||
const formattedMessage = message.replace(/\n/g, '<br>');
|
||||
|
||||
|
||||
// Add pubkey display for received messages
|
||||
let pubkeyDisplay = '';
|
||||
if (pubkey && direction === 'received') {
|
||||
try {
|
||||
const npub = window.NostrTools.nip19.npubEncode(pubkey);
|
||||
pubkeyDisplay = ` <span style="color: #666; font-size: 11px;">(${npub})</span>`;
|
||||
} catch (error) {
|
||||
console.error('Failed to encode pubkey to npub:', error);
|
||||
}
|
||||
}
|
||||
|
||||
messageDiv.innerHTML = `
|
||||
<span class="log-timestamp">${timestamp}</span>
|
||||
<span style="color: ${directionColor}; font-weight: bold;">[${direction.toUpperCase()}]</span>
|
||||
<span style="white-space: pre-wrap;">${formattedMessage}</span>
|
||||
<span style="white-space: pre-wrap;">${formattedMessage}${pubkeyDisplay}</span>
|
||||
`;
|
||||
|
||||
// Remove the "No messages received yet" placeholder if it exists
|
||||
@@ -3397,10 +3440,10 @@
|
||||
|
||||
data.top_pubkeys.forEach((pubkey, index) => {
|
||||
const row = document.createElement('tr');
|
||||
const shortPubkey = pubkey.pubkey ? pubkey.pubkey.substring(0, 16) + '...' : '-';
|
||||
const npub = pubkey.pubkey ? window.NostrTools.nip19.npubEncode(pubkey.pubkey) : '-';
|
||||
row.innerHTML = `
|
||||
<td>${index + 1}</td>
|
||||
<td style="font-family: 'Courier New', monospace; font-size: 12px;">${shortPubkey}</td>
|
||||
<td style="font-family: 'Courier New', monospace; font-size: 12px; word-break: break-all;">${npub}</td>
|
||||
<td>${pubkey.event_count}</td>
|
||||
<td>${pubkey.percentage}%</td>
|
||||
`;
|
||||
@@ -3532,6 +3575,9 @@
|
||||
// Initialize login/logout button state
|
||||
updateLoginLogoutButton();
|
||||
|
||||
// Ensure admin sections are hidden by default on page load
|
||||
updateAdminSectionsVisibility();
|
||||
|
||||
setTimeout(() => {
|
||||
initializeApp();
|
||||
// Enhance SimplePool for testing after initialization
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# C-Relay Static Binary Deployment Script
|
||||
# Deploys build/c_relay_static_x86_64 to server via sshlt
|
||||
# Usage: ./deploy_static.sh [--debug-level=N] [-d=N]
|
||||
# Deploys build/c_relay_static_x86_64 to server via ssh
|
||||
|
||||
set -e
|
||||
|
||||
@@ -11,44 +10,6 @@ LOCAL_BINARY="build/c_relay_static_x86_64"
|
||||
REMOTE_BINARY_PATH="/usr/local/bin/c_relay/c_relay"
|
||||
SERVICE_NAME="c-relay"
|
||||
|
||||
# Default debug level
|
||||
DEBUG_LEVEL=0
|
||||
|
||||
# Parse command line arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--debug-level=*)
|
||||
DEBUG_LEVEL="${1#*=}"
|
||||
shift
|
||||
;;
|
||||
-d=*)
|
||||
DEBUG_LEVEL="${1#*=}"
|
||||
shift
|
||||
;;
|
||||
--debug-level)
|
||||
DEBUG_LEVEL="$2"
|
||||
shift 2
|
||||
;;
|
||||
-d)
|
||||
DEBUG_LEVEL="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
echo "Usage: $0 [--debug-level=N] [-d=N]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Validate debug level
|
||||
if ! [[ "$DEBUG_LEVEL" =~ ^[0-5]$ ]]; then
|
||||
echo "Error: Debug level must be 0-5, got: $DEBUG_LEVEL"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Deploying with debug level: $DEBUG_LEVEL"
|
||||
|
||||
# Create backup
|
||||
ssh ubuntu@laantungir.com "sudo cp '$REMOTE_BINARY_PATH' '${REMOTE_BINARY_PATH}.backup.$(date +%Y%m%d_%H%M%S)'" 2>/dev/null || true
|
||||
|
||||
@@ -60,9 +21,6 @@ ssh ubuntu@laantungir.com "sudo mv '/tmp/c_relay.tmp' '$REMOTE_BINARY_PATH'"
|
||||
ssh ubuntu@laantungir.com "sudo chown c-relay:c-relay '$REMOTE_BINARY_PATH'"
|
||||
ssh ubuntu@laantungir.com "sudo chmod +x '$REMOTE_BINARY_PATH'"
|
||||
|
||||
# Update systemd service environment variable
|
||||
ssh ubuntu@laantungir.com "sudo sed -i 's/Environment=DEBUG_LEVEL=.*/Environment=DEBUG_LEVEL=$DEBUG_LEVEL/' /etc/systemd/system/c-relay.service"
|
||||
|
||||
# Reload systemd and restart service
|
||||
ssh ubuntu@laantungir.com "sudo systemctl daemon-reload"
|
||||
ssh ubuntu@laantungir.com "sudo systemctl restart '$SERVICE_NAME'"
|
||||
|
||||
44
notes.txt
Normal file
44
notes.txt
Normal file
@@ -0,0 +1,44 @@
|
||||
|
||||
Task Completed
|
||||
Perfect! The core dump limit is now set correctly. You can see both:
|
||||
|
||||
LimitCORE=infinity
|
||||
LimitCORESoft=infinity
|
||||
What happens next:
|
||||
The relay is now configured to generate core dumps when it crashes
|
||||
Wait for the next crash to occur (based on your logs, it seems to happen intermittently)
|
||||
When it crashes, the core dump will be automatically captured by systemd
|
||||
After the next crash, analyze it:
|
||||
# List all core dumps (most recent first)
|
||||
sudo coredumpctl list
|
||||
|
||||
# View info about the most recent c-relay crash
|
||||
sudo coredumpctl info c-relay
|
||||
|
||||
# Load the core dump in gdb for detailed analysis
|
||||
sudo coredumpctl gdb c-relay
|
||||
|
||||
Inside gdb, run these commands:
|
||||
(gdb) bt full # Full backtrace with all variables
|
||||
(gdb) info threads # Show all threads
|
||||
(gdb) thread apply all bt # Backtrace for all threads
|
||||
(gdb) frame 0 # Go to crash frame
|
||||
(gdb) info locals # Show local variables
|
||||
(gdb) list # Show source code around crash
|
||||
|
||||
The backtrace will show you exactly which line of code caused the segmentation fault and the state of all variables at that moment, which should pinpoint whether it's the subscription management issue, buffer overflow, or something else.
|
||||
|
||||
You're all set up now - just wait for the next crash and then run the coredumpctl commands to analyze it.
|
||||
|
||||
|
||||
--------------------------
|
||||
|
||||
|
||||
Even simpler: Use this one-liner
|
||||
# Start relay and immediately attach gdb
|
||||
cd /usr/local/bin/c_relay
|
||||
sudo -u c-relay ./c_relay --debug-level=5 & sleep 2 && sudo gdb -p $(pgrep c_relay)
|
||||
|
||||
Once gdb attaches, type continue and wait for the crash. This way the relay starts normally and gdb just monitors it.
|
||||
|
||||
Which approach would you like to try?
|
||||
36
src/config.c
36
src/config.c
@@ -64,7 +64,7 @@ int process_admin_config_event(cJSON* event, char* error_message, size_t error_s
|
||||
// Forward declaration for relay info initialization
|
||||
void init_relay_info(void);
|
||||
int add_auth_rule_from_config(const char* rule_type, const char* pattern_type,
|
||||
const char* pattern_value, const char* action);
|
||||
const char* pattern_value);
|
||||
int remove_auth_rule_from_config(const char* rule_type, const char* pattern_type,
|
||||
const char* pattern_value);
|
||||
int is_config_table_ready(void);
|
||||
@@ -2067,28 +2067,27 @@ int process_admin_auth_event(cJSON* event, char* error_message, size_t error_siz
|
||||
|
||||
// Add auth rule from configuration
|
||||
int add_auth_rule_from_config(const char* rule_type, const char* pattern_type,
|
||||
const char* pattern_value, const char* action) {
|
||||
if (!g_db || !rule_type || !pattern_type || !pattern_value || !action) {
|
||||
const char* pattern_value) {
|
||||
if (!g_db || !rule_type || !pattern_type || !pattern_value) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char* sql = "INSERT INTO auth_rules (rule_type, pattern_type, pattern_value, action) "
|
||||
"VALUES (?, ?, ?, ?)";
|
||||
|
||||
|
||||
const char* sql = "INSERT INTO auth_rules (rule_type, pattern_type, pattern_value) "
|
||||
"VALUES (?, ?, ?)";
|
||||
|
||||
sqlite3_stmt* stmt;
|
||||
int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
sqlite3_bind_text(stmt, 1, rule_type, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 2, pattern_type, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 3, pattern_value, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 4, action, -1, SQLITE_STATIC);
|
||||
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
|
||||
return (rc == SQLITE_DONE) ? 0 : -1;
|
||||
}
|
||||
|
||||
@@ -2725,13 +2724,13 @@ int handle_auth_query_unified(cJSON* event, const char* query_type, char* error_
|
||||
|
||||
// Build appropriate SQL query based on query type
|
||||
if (strcmp(query_type, "all") == 0) {
|
||||
sql = "SELECT rule_type, pattern_type, pattern_value, action FROM auth_rules ORDER BY rule_type, pattern_type";
|
||||
sql = "SELECT rule_type, pattern_type, pattern_value FROM auth_rules WHERE active = 1 ORDER BY rule_type, pattern_type";
|
||||
}
|
||||
else if (strcmp(query_type, "whitelist") == 0) {
|
||||
sql = "SELECT rule_type, pattern_type, pattern_value, action FROM auth_rules WHERE rule_type LIKE '%whitelist%' ORDER BY pattern_type";
|
||||
sql = "SELECT rule_type, pattern_type, pattern_value FROM auth_rules WHERE rule_type LIKE '%whitelist%' AND active = 1 ORDER BY pattern_type";
|
||||
}
|
||||
else if (strcmp(query_type, "blacklist") == 0) {
|
||||
sql = "SELECT rule_type, pattern_type, pattern_value, action FROM auth_rules WHERE rule_type LIKE '%blacklist%' ORDER BY pattern_type";
|
||||
sql = "SELECT rule_type, pattern_type, pattern_value FROM auth_rules WHERE rule_type LIKE '%blacklist%' AND active = 1 ORDER BY pattern_type";
|
||||
}
|
||||
else if (strcmp(query_type, "pattern") == 0) {
|
||||
// Get pattern value from tags
|
||||
@@ -2740,7 +2739,7 @@ int handle_auth_query_unified(cJSON* event, const char* query_type, char* error_
|
||||
snprintf(error_message, error_size, "invalid: pattern query requires pattern value");
|
||||
return -1;
|
||||
}
|
||||
sql = "SELECT rule_type, pattern_type, pattern_value, action FROM auth_rules WHERE pattern_value = ? ORDER BY rule_type, pattern_type";
|
||||
sql = "SELECT rule_type, pattern_type, pattern_value FROM auth_rules WHERE pattern_value = ? AND active = 1 ORDER BY rule_type, pattern_type";
|
||||
use_pattern_param = 1;
|
||||
}
|
||||
else {
|
||||
@@ -2775,7 +2774,6 @@ int handle_auth_query_unified(cJSON* event, const char* query_type, char* error_
|
||||
const char* rule_type = (const char*)sqlite3_column_text(stmt, 0);
|
||||
const char* pattern_type = (const char*)sqlite3_column_text(stmt, 1);
|
||||
const char* pattern_value_result = (const char*)sqlite3_column_text(stmt, 2);
|
||||
const char* action = (const char*)sqlite3_column_text(stmt, 3);
|
||||
|
||||
// printf(" %s %s:%s -> %s\n",
|
||||
// rule_type ? rule_type : "",
|
||||
@@ -2788,7 +2786,7 @@ int handle_auth_query_unified(cJSON* event, const char* query_type, char* error_
|
||||
cJSON_AddStringToObject(rule_obj, "rule_type", rule_type ? rule_type : "");
|
||||
cJSON_AddStringToObject(rule_obj, "pattern_type", pattern_type ? pattern_type : "");
|
||||
cJSON_AddStringToObject(rule_obj, "pattern_value", pattern_value_result ? pattern_value_result : "");
|
||||
cJSON_AddStringToObject(rule_obj, "action", action ? action : "allow");
|
||||
cJSON_AddStringToObject(rule_obj, "action", "allow"); // Simplified: rule_type determines behavior
|
||||
cJSON_AddItemToArray(results_array, rule_obj);
|
||||
|
||||
rule_count++;
|
||||
@@ -3314,7 +3312,7 @@ int handle_auth_rule_modification_unified(cJSON* event, char* error_message, siz
|
||||
|
||||
// Process auth rule: ["blacklist"|"whitelist", "pubkey"|"hash", "value"]
|
||||
if (strcmp(rule_type, "blacklist") == 0 || strcmp(rule_type, "whitelist") == 0) {
|
||||
if (add_auth_rule_from_config(rule_type, pattern_type, pattern_value, "allow") == 0) {
|
||||
if (add_auth_rule_from_config(rule_type, pattern_type, pattern_value) == 0) {
|
||||
rules_processed++;
|
||||
|
||||
// Add processed rule to response array
|
||||
@@ -3322,7 +3320,7 @@ int handle_auth_rule_modification_unified(cJSON* event, char* error_message, siz
|
||||
cJSON_AddStringToObject(rule_obj, "rule_type", rule_type);
|
||||
cJSON_AddStringToObject(rule_obj, "pattern_type", pattern_type);
|
||||
cJSON_AddStringToObject(rule_obj, "pattern_value", pattern_value);
|
||||
cJSON_AddStringToObject(rule_obj, "action", "allow");
|
||||
cJSON_AddStringToObject(rule_obj, "action", "allow"); // Simplified: rule_type determines behavior
|
||||
cJSON_AddStringToObject(rule_obj, "status", "added");
|
||||
cJSON_AddItemToArray(processed_rules, rule_obj);
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ cJSON* build_query_response(const char* query_type, cJSON* results_array, int to
|
||||
|
||||
// Auth rules management functions
|
||||
int add_auth_rule_from_config(const char* rule_type, const char* pattern_type,
|
||||
const char* pattern_value, const char* action);
|
||||
const char* pattern_value);
|
||||
int remove_auth_rule_from_config(const char* rule_type, const char* pattern_type,
|
||||
const char* pattern_value);
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -15,6 +15,7 @@
|
||||
#include "../nostr_core_lib/nostr_core/nip013.h" // NIP-13: Proof of Work
|
||||
#include "../nostr_core_lib/nostr_core/nostr_common.h"
|
||||
#include "../nostr_core_lib/nostr_core/utils.h"
|
||||
#include "debug.h" // C-relay debug system
|
||||
#include "config.h" // C-relay configuration system
|
||||
#include <sqlite3.h>
|
||||
#include <stdio.h>
|
||||
@@ -531,6 +532,8 @@ int check_database_auth_rules(const char *pubkey, const char *operation __attrib
|
||||
sqlite3_stmt *stmt = NULL;
|
||||
int rc;
|
||||
|
||||
DEBUG_TRACE("Checking auth rules for pubkey: %s", pubkey);
|
||||
|
||||
if (!pubkey) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
@@ -547,19 +550,21 @@ int check_database_auth_rules(const char *pubkey, const char *operation __attrib
|
||||
|
||||
// Step 1: Check pubkey blacklist (highest priority)
|
||||
const char *blacklist_sql =
|
||||
"SELECT rule_type, action FROM auth_rules WHERE rule_type = "
|
||||
"'blacklist' AND pattern_type = 'pubkey' AND pattern_value = ? LIMIT 1";
|
||||
"SELECT rule_type FROM auth_rules WHERE rule_type = "
|
||||
"'blacklist' AND pattern_type = 'pubkey' AND pattern_value = ? AND active = 1 LIMIT 1";
|
||||
DEBUG_TRACE("Blacklist SQL: %s", blacklist_sql);
|
||||
rc = sqlite3_prepare_v2(db, blacklist_sql, -1, &stmt, NULL);
|
||||
if (rc == SQLITE_OK) {
|
||||
sqlite3_bind_text(stmt, 1, pubkey, -1, SQLITE_STATIC);
|
||||
|
||||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
const char *action = (const char *)sqlite3_column_text(stmt, 1);
|
||||
int step_result = sqlite3_step(stmt);
|
||||
DEBUG_TRACE("Blacklist query result: %s", step_result == SQLITE_ROW ? "FOUND" : "NOT_FOUND");
|
||||
|
||||
if (step_result == SQLITE_ROW) {
|
||||
DEBUG_TRACE("BLACKLIST HIT: Denying access for pubkey: %s", pubkey);
|
||||
// Set specific violation details for status code mapping
|
||||
strcpy(g_last_rule_violation.violation_type, "pubkey_blacklist");
|
||||
sprintf(g_last_rule_violation.reason, "Public key blacklisted: %s",
|
||||
action ? action : "PUBKEY_BLACKLIST");
|
||||
sprintf(g_last_rule_violation.reason, "Public key blacklisted");
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_close(db);
|
||||
@@ -571,19 +576,16 @@ int check_database_auth_rules(const char *pubkey, const char *operation __attrib
|
||||
// Step 2: Check hash blacklist
|
||||
if (resource_hash) {
|
||||
const char *hash_blacklist_sql =
|
||||
"SELECT rule_type, action FROM auth_rules WHERE rule_type = "
|
||||
"'blacklist' AND pattern_type = 'hash' AND pattern_value = ? LIMIT 1";
|
||||
"SELECT rule_type FROM auth_rules WHERE rule_type = "
|
||||
"'blacklist' AND pattern_type = 'hash' AND pattern_value = ? AND active = 1 LIMIT 1";
|
||||
rc = sqlite3_prepare_v2(db, hash_blacklist_sql, -1, &stmt, NULL);
|
||||
if (rc == SQLITE_OK) {
|
||||
sqlite3_bind_text(stmt, 1, resource_hash, -1, SQLITE_STATIC);
|
||||
|
||||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
const char *action = (const char *)sqlite3_column_text(stmt, 1);
|
||||
|
||||
// Set specific violation details for status code mapping
|
||||
strcpy(g_last_rule_violation.violation_type, "hash_blacklist");
|
||||
sprintf(g_last_rule_violation.reason, "File hash blacklisted: %s",
|
||||
action ? action : "HASH_BLACKLIST");
|
||||
sprintf(g_last_rule_violation.reason, "File hash blacklisted");
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_close(db);
|
||||
@@ -595,8 +597,8 @@ int check_database_auth_rules(const char *pubkey, const char *operation __attrib
|
||||
|
||||
// Step 3: Check pubkey whitelist
|
||||
const char *whitelist_sql =
|
||||
"SELECT rule_type, action FROM auth_rules WHERE rule_type = "
|
||||
"'whitelist' AND pattern_type = 'pubkey' AND pattern_value = ? LIMIT 1";
|
||||
"SELECT rule_type FROM auth_rules WHERE rule_type = "
|
||||
"'whitelist' AND pattern_type = 'pubkey' AND pattern_value = ? AND active = 1 LIMIT 1";
|
||||
rc = sqlite3_prepare_v2(db, whitelist_sql, -1, &stmt, NULL);
|
||||
if (rc == SQLITE_OK) {
|
||||
sqlite3_bind_text(stmt, 1, pubkey, -1, SQLITE_STATIC);
|
||||
@@ -612,7 +614,7 @@ int check_database_auth_rules(const char *pubkey, const char *operation __attrib
|
||||
// Step 4: Check if any whitelist rules exist - if yes, deny by default
|
||||
const char *whitelist_exists_sql =
|
||||
"SELECT COUNT(*) FROM auth_rules WHERE rule_type = 'whitelist' "
|
||||
"AND pattern_type = 'pubkey' LIMIT 1";
|
||||
"AND pattern_type = 'pubkey' AND active = 1 LIMIT 1";
|
||||
rc = sqlite3_prepare_v2(db, whitelist_exists_sql, -1, &stmt, NULL);
|
||||
if (rc == SQLITE_OK) {
|
||||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
|
||||
@@ -142,8 +142,6 @@ CREATE TABLE auth_rules (\n\
|
||||
rule_type TEXT NOT NULL CHECK (rule_type IN ('whitelist', 'blacklist', 'rate_limit', 'auth_required')),\n\
|
||||
pattern_type TEXT NOT NULL CHECK (pattern_type IN ('pubkey', 'kind', 'ip', 'global')),\n\
|
||||
pattern_value TEXT,\n\
|
||||
action TEXT NOT NULL CHECK (action IN ('allow', 'deny', 'require_auth', 'rate_limit')),\n\
|
||||
parameters TEXT, -- JSON parameters for rate limiting, etc.\n\
|
||||
active INTEGER NOT NULL DEFAULT 1,\n\
|
||||
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),\n\
|
||||
updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))\n\
|
||||
|
||||
@@ -622,7 +622,8 @@ int broadcast_event_to_subscriptions(cJSON* event) {
|
||||
subscription_t* update_sub = g_subscription_manager.active_subscriptions;
|
||||
while (update_sub) {
|
||||
if (update_sub->wsi == current_temp->wsi &&
|
||||
strcmp(update_sub->id, current_temp->id) == 0) {
|
||||
strcmp(update_sub->id, current_temp->id) == 0 &&
|
||||
update_sub->active) { // Add active check to prevent use-after-free
|
||||
update_sub->events_sent++;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -516,8 +516,33 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso
|
||||
if (event_kind == 23456) {
|
||||
if (admin_result != 0) {
|
||||
char error_result_msg[512];
|
||||
snprintf(error_result_msg, sizeof(error_result_msg),
|
||||
"ERROR: Kind %d event processing failed: %s", event_kind, admin_error);
|
||||
if (strlen(admin_error) > 0) {
|
||||
// Safely truncate admin_error if too long
|
||||
size_t max_error_len = sizeof(error_result_msg) - 50; // Leave room for prefix
|
||||
size_t error_len = strlen(admin_error);
|
||||
if (error_len > max_error_len) {
|
||||
error_len = max_error_len;
|
||||
}
|
||||
char truncated_error[512];
|
||||
memcpy(truncated_error, admin_error, error_len);
|
||||
truncated_error[error_len] = '\0';
|
||||
|
||||
// Use a safer approach to avoid truncation warning
|
||||
size_t prefix_len = snprintf(error_result_msg, sizeof(error_result_msg),
|
||||
"ERROR: Kind %d event processing failed: ", event_kind);
|
||||
if (prefix_len < sizeof(error_result_msg)) {
|
||||
size_t remaining = sizeof(error_result_msg) - prefix_len;
|
||||
size_t copy_len = strlen(truncated_error);
|
||||
if (copy_len >= remaining) {
|
||||
copy_len = remaining - 1;
|
||||
}
|
||||
memcpy(error_result_msg + prefix_len, truncated_error, copy_len);
|
||||
error_result_msg[prefix_len + copy_len] = '\0';
|
||||
}
|
||||
} else {
|
||||
snprintf(error_result_msg, sizeof(error_result_msg),
|
||||
"ERROR: Kind %d event processing failed", event_kind);
|
||||
}
|
||||
DEBUG_ERROR(error_result_msg);
|
||||
}
|
||||
}
|
||||
|
||||
40
tests/post_events.sh
Executable file
40
tests/post_events.sh
Executable file
@@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Test script to post kind 1 events to the relay every second
|
||||
# Cycles through three different secret keys
|
||||
# Content includes current timestamp
|
||||
|
||||
# Array of secret keys to cycle through
|
||||
SECRET_KEYS=(
|
||||
"3fdd8227a920c2385559400b2b14e464f22e80df312a73cc7a86e1d7e91d608f"
|
||||
"a156011cd65b71f84b4a488ac81687f2aed57e490b31c28f58195d787030db60"
|
||||
"1618aaa21f5bd45c5ffede0d9a60556db67d4a046900e5f66b0bae5c01c801fb"
|
||||
)
|
||||
|
||||
RELAY_URL="ws://localhost:8888"
|
||||
KEY_INDEX=0
|
||||
|
||||
echo "Starting event posting test to $RELAY_URL"
|
||||
echo "Press Ctrl+C to stop"
|
||||
|
||||
while true; do
|
||||
# Get current timestamp
|
||||
TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S UTC")
|
||||
|
||||
# Get current secret key
|
||||
CURRENT_KEY=${SECRET_KEYS[$KEY_INDEX]}
|
||||
|
||||
# Create content with timestamp
|
||||
CONTENT="Test event at $TIMESTAMP"
|
||||
|
||||
echo "[$TIMESTAMP] Posting event with key ${KEY_INDEX}: ${CURRENT_KEY:0:16}..."
|
||||
|
||||
# Post event using nak
|
||||
nak event -c "$CONTENT" --sec "$CURRENT_KEY" "$RELAY_URL"
|
||||
|
||||
# Cycle to next key
|
||||
KEY_INDEX=$(( (KEY_INDEX + 1) % ${#SECRET_KEYS[@]} ))
|
||||
|
||||
# Wait 1 second
|
||||
sleep 1
|
||||
done
|
||||
Reference in New Issue
Block a user