summaryrefslogtreecommitdiffstats
path: root/download_remailers.php
diff options
context:
space:
mode:
authorGab <24553253+gabrix73@users.noreply.github.com>2026-08-14 20:39:22 +0200
committerGab <24553253+gabrix73@users.noreply.github.com>2026-08-14 20:39:22 +0200
commit43fbddf016f94f4ba006d82c9a67dca61b5852a1 (patch)
tree70054d5fa8a34bb094b8c3ef8ddc9c1fcea3b3d5 /download_remailers.php
parent994071243fc80990cf09e820b671006e9bd31c76 (diff)
downloadyamnweb-43fbddf016f94f4ba006d82c9a67dca61b5852a1.tar.gz
yamnweb-43fbddf016f94f4ba006d82c9a67dca61b5852a1.tar.xz
yamnweb-43fbddf016f94f4ba006d82c9a67dca61b5852a1.zip
Complete Nym and YAMN transport integration
Diffstat (limited to 'download_remailers.php')
-rw-r--r--download_remailers.php104
1 files changed, 91 insertions, 13 deletions
diff --git a/download_remailers.php b/download_remailers.php
index d601ac6..babfcbe 100644
--- a/download_remailers.php
+++ b/download_remailers.php
@@ -26,16 +26,23 @@ class SecureRemailerDownloader {
private $storageDir = '/opt/yamn-data/cache';
private $remailersFile = '/opt/yamn-data/cache/remailers.txt';
private $pubringFile = '/opt/yamn-master/pubring.mix'; // Yamn expects it here
+ private $statsAttemptFile = '/opt/yamn-data/cache/remailers.last-attempt';
+ private $keyringAttemptFile = '/opt/yamn-data/cache/pubring.last-attempt';
private $backupDir = '/opt/yamn-data/backups';
private $logFile = '/var/log/yamn_download.log';
- // Update interval with jitter (20-28 hours instead of exactly 24)
- private $minUpdateInterval = 72000; // 20 hours
- private $maxUpdateInterval = 100800; // 28 hours
+ // Reuse local data for a full day before contacting a pinger again.
+ private $updateInterval = 86400;
// GPG verification (if available)
private $verifyGPG = false;
private $gpgKeyId = null;
+
+ // Result of the most recent refresh attempt for each data set.
+ private $lastUpdateStatus = [
+ 'stats' => 'not-run',
+ 'keyring' => 'not-run'
+ ];
public function __construct() {
// Create directories if they don't exist
@@ -51,6 +58,8 @@ class SecureRemailerDownloader {
* Main download function with all security checks
*/
public function downloadRemailers() {
+ $this->resetUpdateStatus();
+
// Download both stats and keyring
$statsResult = $this->downloadStats();
$keyringResult = $this->downloadKeyring();
@@ -62,11 +71,20 @@ class SecureRemailerDownloader {
* Download remailer stats (mlist.txt)
*/
private function downloadStats(bool $force = false) {
- // Check if update is needed (with randomized interval)
+ // Check whether local data is older than one day.
if (!$force && !$this->needsUpdate($this->remailersFile)) {
$this->log("Stats update not needed yet");
+ $this->lastUpdateStatus['stats'] = 'fresh';
+ return true;
+ }
+
+ if (!$force && $this->attemptIsRecent($this->statsAttemptFile)) {
+ $this->log("Stats refresh already attempted within the last day");
+ $this->lastUpdateStatus['stats'] = 'deferred';
return true;
}
+
+ $this->markAttempt($this->statsAttemptFile);
// Add random delay to prevent timing correlation
$this->randomDelay(5, 60);
@@ -74,6 +92,7 @@ class SecureRemailerDownloader {
// Verify Tor is working
if (!$this->checkTorConnection()) {
$this->log("ERROR: Tor connection not available", 'ERROR');
+ $this->lastUpdateStatus['stats'] = 'failed';
return false;
}
@@ -97,11 +116,13 @@ class SecureRemailerDownloader {
if (!$success) {
$this->log("ERROR: Failed to download stats from all sources", 'ERROR');
$this->restoreBackup($this->remailersFile, 'remailers');
+ $this->lastUpdateStatus['stats'] = 'failed';
return false;
}
-
+
$this->log("Remailer stats updated successfully");
$this->cleanOldBackups('remailers');
+ $this->lastUpdateStatus['stats'] = 'updated';
return true;
}
@@ -110,11 +131,20 @@ class SecureRemailerDownloader {
* Download public keyring (pubring.mix)
*/
private function downloadKeyring(bool $force = false) {
- // Check if update is needed (with randomized interval)
+ // Check whether local data is older than one day.
if (!$force && !$this->needsUpdate($this->pubringFile)) {
$this->log("Keyring update not needed yet");
+ $this->lastUpdateStatus['keyring'] = 'fresh';
return true;
}
+
+ if (!$force && $this->attemptIsRecent($this->keyringAttemptFile)) {
+ $this->log("Keyring refresh already attempted within the last day");
+ $this->lastUpdateStatus['keyring'] = 'deferred';
+ return true;
+ }
+
+ $this->markAttempt($this->keyringAttemptFile);
// Add random delay to prevent timing correlation
$this->randomDelay(5, 60);
@@ -122,6 +152,7 @@ class SecureRemailerDownloader {
// Verify Tor is working
if (!$this->checkTorConnection()) {
$this->log("ERROR: Tor connection not available", 'ERROR');
+ $this->lastUpdateStatus['keyring'] = 'failed';
return false;
}
@@ -153,17 +184,19 @@ class SecureRemailerDownloader {
if (!$success) {
$this->log("ERROR: Failed to download keyring from all sources", 'ERROR');
$this->restoreBackup($this->pubringFile, 'pubring');
+ $this->lastUpdateStatus['keyring'] = 'failed';
return false;
}
-
+
$this->log("Public keyring updated successfully");
$this->cleanOldBackups('pubring');
+ $this->lastUpdateStatus['keyring'] = 'updated';
return true;
}
/**
- * Check if update is needed with randomized interval
+ * Check if a local data file is older than one day.
*/
private function needsUpdate($filepath) {
if (!file_exists($filepath)) {
@@ -172,10 +205,27 @@ class SecureRemailerDownloader {
$fileAge = time() - filemtime($filepath);
- // Random threshold between 20-28 hours
- $threshold = mt_rand($this->minUpdateInterval, $this->maxUpdateInterval);
-
- return $fileAge > $threshold;
+ return $fileAge > $this->updateInterval;
+ }
+
+ /**
+ * Avoid retrying an unavailable source on every browser reload.
+ */
+ private function attemptIsRecent($filepath) {
+ return file_exists($filepath)
+ && (time() - filemtime($filepath)) <= $this->updateInterval;
+ }
+
+ /**
+ * Record an update attempt before opening any network connection.
+ */
+ private function markAttempt($filepath) {
+ if (file_put_contents($filepath, (string) time(), LOCK_EX) === false) {
+ $this->log("Unable to record refresh attempt", 'WARNING');
+ return;
+ }
+
+ chmod($filepath, 0600);
}
/**
@@ -538,11 +588,39 @@ class SecureRemailerDownloader {
* Force update of both stats and keyring
*/
public function forceUpdate() {
+ $this->resetUpdateStatus();
+
$statsResult = $this->downloadStats(true);
$keyringResult = $this->downloadKeyring(true);
-
+
return $statsResult && $keyringResult;
}
+
+ /**
+ * Return the result of the latest refresh attempt.
+ *
+ * Values are: fresh, updated, deferred, failed, or not-run.
+ */
+ public function getLastUpdateStatus() {
+ return $this->lastUpdateStatus;
+ }
+
+ /**
+ * Whether the existing statistics and keyring can still be used.
+ */
+ public function hasUsableData() {
+ return file_exists($this->remailersFile)
+ && filesize($this->remailersFile) > 0
+ && file_exists($this->pubringFile)
+ && filesize($this->pubringFile) > 0;
+ }
+
+ private function resetUpdateStatus() {
+ $this->lastUpdateStatus = [
+ 'stats' => 'not-run',
+ 'keyring' => 'not-run'
+ ];
+ }
/**
* Get status of downloaded files