This commit is contained in:
defnax 2026-09-03 20:03:46 +02:00 committed by GitHub
commit b0d1fd1c5c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 194 additions and 69 deletions

View File

@ -235,6 +235,8 @@ JsonApiServer::JsonApiServer(): configMutex("JsonApiServer config"),
std::string locationName;
std::string pgpName;
std::string password;
bool makeHidden = false;
bool makeAutoTor = false;
// JSON API only
std::string apiUser;
@ -249,6 +251,8 @@ JsonApiServer::JsonApiServer(): configMutex("JsonApiServer config"),
RS_SERIAL_PROCESS(locationName);
RS_SERIAL_PROCESS(pgpName);
RS_SERIAL_PROCESS(password);
RS_SERIAL_PROCESS(makeHidden);
RS_SERIAL_PROCESS(makeAutoTor);
// JSON API only
RS_SERIAL_PROCESS(apiUser);
@ -265,7 +269,8 @@ JsonApiServer::JsonApiServer(): configMutex("JsonApiServer config"),
if(!retval) // call retroshare C++ API
retval = rsLoginHelper->createLocationV2(
locationId, pgpId, locationName, pgpName, password );
locationId, pgpId, locationName, pgpName, password,
makeHidden, makeAutoTor );
if(!retval) retval = authorizeUser(apiUser, apiPass);

View File

@ -498,7 +498,9 @@ public:
RsPgpId& pgpId,
const std::string& locationName,
const std::string& pgpName,
const std::string& password
const std::string& password,
bool makeHidden = false,
bool makeAutoTor = false
/* JSON API only
* const std::string& apiUser
* const std::string& apiPass */ );

View File

@ -128,6 +128,30 @@ public:
*/
static RsTorConnectivityStatus torConnectivityStatus() ;
/**
* Configure TorManager to attach to an already running Tor instance.
* This must be called before start(). The external process is never
* stopped or taken over by RetroShare.
*
* This endpoint is available before account login because the Tor
* connection must be configured before hidden-node initialization starts.
*
* @param[in] controlAddress Tor control listener address, usually
* `127.0.0.1`
* @param[in] controlPort Tor control listener TCP port
* @param[in] controlPassword Tor control password, or an empty string for
* null/cookie authentication
* @param[in] socksAddress Tor SOCKS listener address, usually `127.0.0.1`
* @param[in] socksPort Tor SOCKS listener TCP port
* @return true if the external Tor configuration was accepted
*
* @jsonapi{development,unauthenticated}
*/
static bool setExternalTorConnection(
const std::string& controlAddress, uint16_t controlPort,
const std::string& controlPassword,
const std::string& socksAddress, uint16_t socksPort );
static void setTorDataDirectory(const std::string& dir);
static void setHiddenServiceDirectory(const std::string& dir);
@ -162,12 +186,14 @@ public:
/*!
* \brief getHiddenServiceInfo
* Gets information about the hidden service setup by RS to run.
* \param service_id
* \param service_onion_address
* \param service_port
* \param service_target_address
* \param target_port
* \return
* \param[out] service_id Tor service identifier without the `.onion` suffix
* \param[out] service_onion_address Complete generated onion hostname
* \param[out] service_port Public onion-service port
* \param[out] service_target_address Local address receiving Tor traffic
* \param[out] target_port Local port receiving Tor traffic
* \return true when an AutoTor hidden service is available
*
* @jsonapi{development}
*/
static bool getHiddenServiceInfo(std::string& service_id,
std::string& service_onion_address,

View File

@ -2162,6 +2162,15 @@ bool RsInit::startAutoTor()
std::string service_id;
RsTor::setupHiddenService();
// If the connection to the Tor control port cannot be established at all
// (e.g. an external Tor that is not running, or a wrong control port),
// fail after a while instead of waiting forever. Once the control link
// has been seen up, wait indefinitely for Tor to bootstrap, as before.
const time_t control_connect_timeout = 30; // seconds
time_t start_time = time(nullptr);
bool control_link_seen = false;
while(RsTor::torStatus() != RsTorStatus::READY && RsTor::getHiddenServiceStatus(service_id) != RsTorHiddenServiceStatus::ONLINE) // runs until some status is reached: either tor works, or it fails.
{
rstime::rs_usleep(0.5*1000*1000) ;
@ -2174,6 +2183,24 @@ bool RsInit::startAutoTor()
std::cerr << "(EE) Tor hidden service cannot be started: " << error_msg << std::endl;
return false;
}
if(!control_link_seen)
switch(RsTor::torConnectivityStatus())
{
case RsTorConnectivityStatus::SOCKET_CONNECTED:
case RsTorConnectivityStatus::AUTHENTICATING:
case RsTorConnectivityStatus::AUTHENTICATED:
case RsTorConnectivityStatus::HIDDEN_SERVICE_READY:
control_link_seen = true;
break;
default:
if(time(nullptr) > start_time + control_connect_timeout)
{
std::cerr << "(EE) Cannot establish a connection to the Tor control port. Giving up." << std::endl;
return false;
}
break;
}
// process Qt event loop to deal with messages of online/offline info
// QCoreApplication::processEvents();
}
@ -2236,7 +2263,8 @@ void RsLoginHelper::getLocations(std::vector<RsLoginHelper::Location>& store)
std::error_condition RsLoginHelper::createLocationV2(
RsPeerId& locationId, RsPgpId& pgpId,
const std::string& locationName, const std::string& pgpName,
const std::string& password )
const std::string& password,
bool makeHidden, bool makeAutoTor )
{
if(isLoggedIn()) return RsInitErrorNum::ALREADY_LOGGED_IN;
if(locationName.empty()) return RsInitErrorNum::INVALID_LOCATION_NAME;
@ -2256,7 +2284,7 @@ std::error_condition RsLoginHelper::createLocationV2(
RsLoginHandler::cachePgpPassphrase(password);
bool ret = RsAccounts::createNewAccount(
pgpId, "", locationName, "", false, false, sslPassword,
pgpId, "", locationName, "", makeHidden, makeAutoTor, sslPassword,
locationId, errorMessage );
if(!ret)
{

View File

@ -211,6 +211,8 @@ void TorControl::connect(const std::string &address, uint16_t port)
setStatus(SocketConnected);
setTorStatus(TorOffline); // connected and running, but not yet ready
}
else
setStatus(NotConnected); // so that the owner can try connecting again
}
void TorControl::reconnect()
@ -221,7 +223,14 @@ void TorControl::reconnect()
return;
setStatus(Connecting);
mSocket->connectToHost(mTorAddress, mControlPort);
if(mSocket->connectToHost(mTorAddress, mControlPort))
{
setStatus(SocketConnected);
setTorStatus(TorOffline); // connected and running, but not yet ready
}
else
setStatus(NotConnected); // so that the owner can try connecting again
}
void TorControl::authenticateReply(TorControlCommand *sender)

View File

@ -38,12 +38,10 @@
#include "util/rsdir.h"
#include "retroshare/rsinit.h"
#include <thread>
#include <mutex>
#if !defined(_WIN32) && !defined(__MINGW32__)
#include <unistd.h>
#endif
#ifdef __linux__
#include <sys/syscall.h>
#endif
#include "TorManager.h"
#include "TorProcess.h"
@ -74,6 +72,13 @@ public:
bool configNeeded;
bool mVerbose;
std::string userProvidedTorExecutablePath;
bool useExternalTor;
std::string externalControlAddress;
uint16_t externalControlPort;
ByteArray externalControlPassword;
std::string externalSocksAddress;
uint16_t externalSocksPort;
time_t lastControlConnectAttempt;
HiddenService *hiddenService ;
@ -123,6 +128,10 @@ TorManagerPrivate::TorManagerPrivate(TorManager *parent)
, control(new TorControl())
, configNeeded(false)
, mVerbose(false)
, useExternalTor(false)
, externalControlPort(0)
, externalSocksPort(0)
, lastControlConnectAttempt(0)
, hiddenService(NULL)
{
control->set_statusChanged_callback([this](int new_status,int /*old_status*/) { controlStatusChanged(new_status); });
@ -371,6 +380,32 @@ std::string TorManager::torExecutablePath() const
return (d==nullptr)?(std::string()):(d->torExecutablePath());
}
bool TorManager::setExternalTorConnection(
const std::string& controlAddress, uint16_t controlPort,
const std::string& controlPassword,
const std::string& socksAddress, uint16_t socksPort )
{
if(isRunning())
{
d->setError("Cannot change Tor connection while TorManager is running.");
return false;
}
if(controlAddress.empty() || controlPort == 0 ||
socksAddress.empty() || socksPort == 0)
{
d->setError("External Tor control and SOCKS endpoints must be valid.");
return false;
}
d->useExternalTor = true;
d->externalControlAddress = controlAddress;
d->externalControlPort = controlPort;
d->externalControlPassword = ByteArray(controlPassword);
d->externalSocksAddress = socksAddress;
d->externalSocksPort = socksPort;
return true;
}
const std::list<std::string>& TorManager::logMessages() const
{
return d->logMessages;
@ -394,43 +429,14 @@ bool TorManager::startTorManager()
//emit errorChanged(); // not needed because there's no error to handle
}
#ifdef TODO
SettingsObject settings("tor");
// If a control port is defined by config or environment, skip launching tor
if (!settings.read("controlPort").isUndefined() ||
!qEnvironmentVariableIsEmpty("TOR_CONTROL_PORT"))
if(d->useExternalTor)
{
QHostAddress address(settings.read("controlAddress").toString());
quint16 port = (quint16)settings.read("controlPort").toInt();
QByteArray password = settings.read("controlPassword").toString().toLatin1();
if (!qEnvironmentVariableIsEmpty("TOR_CONTROL_HOST"))
address = QHostAddress(qgetenv("TOR_CONTROL_HOST"));
if (!qEnvironmentVariableIsEmpty("TOR_CONTROL_PORT")) {
bool ok = false;
port = qgetenv("TOR_CONTROL_PORT").toUShort(&ok);
if (!ok)
port = 0;
}
if (!qEnvironmentVariableIsEmpty("TOR_CONTROL_PASSWD"))
password = qgetenv("TOR_CONTROL_PASSWD");
if (!port) {
d->setError("Invalid control port settings from environment or configuration");
return false;
}
if (address.isNull())
address = QHostAddress::LocalHost;
d->control->setAuthPassword(password);
d->control->connect(address, port);
RsInfo() << "Connecting to external Tor control port at "
<< d->externalControlAddress << ":" << d->externalControlPort;
d->control->setAuthPassword(d->externalControlPassword);
d->control->connect(d->externalControlAddress, d->externalControlPort);
}
else
#endif
{
// Launch a bundled Tor instance
std::string executable = d->torExecutablePath();
@ -503,7 +509,8 @@ bool TorManager::startTorManager()
void TorManager::run()
{
d->process->start();
if(d->process)
d->process->start();
while(!shouldStop())
{
@ -512,7 +519,8 @@ void TorManager::run()
}
d->control->shutdownSync();
d->process->stop();
if(d->process)
d->process->stop();
if(rsEvents)
{
@ -526,10 +534,12 @@ void TorManager::threadTick()
{
static bool authenticated_msg_already_given = false;
d->process->tick();
if(d->process->state() != TorProcess::Ready)
return;
if(d->process)
{
d->process->tick();
if(d->process->state() != TorProcess::Ready)
return;
}
switch(d->control->status())
{
@ -538,8 +548,28 @@ void TorManager::threadTick()
break;
case TorControl::NotConnected:
RsInfo() << "Connecting to tor process at " << d->process->controlHost() << ":" << d->process->controlPort() << "..." ;
d->control->connect(d->process->controlHost(),d->process->controlPort());
{
// The connection attempt is synchronous and threadTick() runs every
// 50ms, so throttle retries when the control port is unreachable
// (e.g. an external Tor that is not started yet).
time_t now = time(nullptr);
if(now < d->lastControlConnectAttempt + 2)
break;
d->lastControlConnectAttempt = now;
if(d->useExternalTor)
{
RsInfo() << "Connecting to external tor at " << d->externalControlAddress << ":" << d->externalControlPort << "..." ;
d->control->connect(d->externalControlAddress,d->externalControlPort);
}
else
{
RsInfo() << "Connecting to tor process at " << d->process->controlHost() << ":" << d->process->controlPort() << "..." ;
d->control->connect(d->process->controlHost(),d->process->controlPort());
}
}
break;
case TorControl::SocketConnected:
@ -551,7 +581,8 @@ void TorManager::threadTick()
setupHiddenService();
}
d->control->setAuthPassword(d->process->controlPassword());
d->control->setAuthPassword(d->useExternalTor
? d->externalControlPassword : d->process->controlPassword());
d->control->authenticate();
RsInfo() << "Authenticating..." ;
break;
@ -588,6 +619,12 @@ bool TorManager::getProxyServerInfo(std::string& proxy_server_adress,uint16_t& p
proxy_server_adress = control()->socksAddress();
proxy_server_port = control()->socksPort();
if(d->useExternalTor && (proxy_server_adress.empty() || proxy_server_port == 0))
{
proxy_server_adress = d->externalSocksAddress;
proxy_server_port = d->externalSocksPort;
}
return proxy_server_port > 1023 ;
}
@ -796,7 +833,17 @@ void TorManagerPrivate::setError(const std::string &message)
bool RsTor::isTorAvailable()
{
return !instance()->d->torExecutablePath().empty();
return instance()->d->useExternalTor || !instance()->d->torExecutablePath().empty();
}
bool RsTor::setExternalTorConnection(
const std::string& controlAddress, uint16_t controlPort,
const std::string& controlPassword,
const std::string& socksAddress, uint16_t socksPort )
{
return instance()->setExternalTorConnection(
controlAddress, controlPort, controlPassword,
socksAddress, socksPort );
}
bool RsTor::getHiddenServiceInfo(std::string& service_id,
@ -840,11 +887,17 @@ void RsTor::setTorExecutablePath(const std::string& e)
std::string RsTor::socksAddress()
{
return instance()->control()->socksAddress();
std::string address;
uint16_t port = 0;
instance()->getProxyServerInfo(address, port);
return address;
}
uint16_t RsTor::socksPort()
{
return instance()->control()->socksPort();
std::string address;
uint16_t port = 0;
instance()->getProxyServerInfo(address, port);
return port;
}
static RsTorStatus torStatus(Tor::TorControl::TorStatus t)
@ -955,17 +1008,14 @@ void RsTor::setHiddenServiceDirectory(const std::string& dir)
instance()->setHiddenServiceDirectory(dir);
}
#ifdef __APPLE__
#include <pthread.h>
#endif
TorManager *RsTor::instance()
{
#ifdef __APPLE__
assert(pthread_main_np() != 0); // On macOS, ensure we are on the main thread
#elif defined(__linux__)
assert(getpid() == syscall(SYS_gettid)); // On Linux, ensure we are on the main thread
#endif
// RsTor methods are exposed through the JSON API, so this can be reached
// from any thread. Protect the lazy initialisation, which the former
// main-thread asserts used to guard.
static std::mutex instanceMutex;
std::unique_lock<std::mutex> lock(instanceMutex);
if(rsTorMgr == nullptr)
rsTorMgr = new TorManager;

View File

@ -62,6 +62,11 @@ public:
std::string torExecutablePath() const;
void setTorExecutablePath(const std::string& tor_exe_full_path) ;
bool setExternalTorConnection(
const std::string& controlAddress, uint16_t controlPort,
const std::string& controlPassword,
const std::string& socksAddress, uint16_t socksPort );
std::string hiddenServiceDirectory() const;
void setHiddenServiceDirectory(const std::string &path);