52static const std::string
TOR_SAFE_SERVERKEY =
"Tor safe cookie authentication server-to-controller hash";
54static const std::string
TOR_SAFE_CLIENTKEY =
"Tor safe cookie authentication controller-to-server hash";
88 : m_interrupt(interrupt)
133 if (!
m_sock)
return false;
136 if (!connected && !
errmsg.empty()) {
144 if (!
m_sock)
return false;
161 if (!
m_sock)
return false;
184 }
catch (
const std::runtime_error&
e) {
185 LogWarning(
"tor: Error processing receive buffer: %s",
e.what());
199 if (line->size() < 4)
continue;
229 if (!
m_sock)
return false;
234 }
catch (
const std::runtime_error&
e) {
235 LogWarning(
"tor: Error sending command: %s",
e.what());
254 while (ptr < s.size() && s[ptr] !=
' ') {
255 type.push_back(s[ptr]);
271 std::map<std::string,std::string>
mapping;
273 while (ptr < s.size()) {
274 std::string key, value;
275 while (ptr < s.size() && s[ptr] !=
'=' && s[ptr] !=
' ') {
276 key.push_back(s[ptr]);
280 return std::map<std::string,std::string>();
284 if (ptr < s.size() && s[ptr] ==
'"') {
287 while (ptr < s.size() && (
escape_next || s[ptr] !=
'"')) {
290 value.push_back(s[ptr]);
294 return std::map<std::string,std::string>();
307 for (
size_t i = 0; i < value.size(); ++i) {
308 if (value[i] ==
'\\') {
314 if (value[i] ==
'n') {
316 }
else if (value[i] ==
't') {
318 }
else if (value[i] ==
'r') {
320 }
else if (
'0' <= value[i] && value[i] <=
'7') {
325 for (
j = 1;
j < 3 && (i+
j) < value.size() &&
'0' <= value[i+
j] && value[i+
j] <=
'7'; ++
j) {}
329 if (
j == 3 && value[i] >
'3') {
332 const auto end{i +
j};
336 val += value[i++] -
'0';
350 while (ptr < s.size() && s[ptr] !=
' ') {
351 value.push_back(s[ptr]);
355 if (ptr < s.size() && s[ptr] ==
' ')
429 for (
const auto& line : reply.
lines) {
430 if (line.starts_with(
"net/listeners/socks=")) {
441 if (
portstr.starts_with(
"127.0.0.1:")) {
451 LogWarning(
"tor: Get SOCKS port command returned nothing");
454 LogWarning(
"tor: Get SOCKS port command failed with unrecognized command (You probably should upgrade Tor)");
456 LogWarning(
"tor: Get SOCKS port command failed; error code %d", reply.
code);
483 return ParseNetwork(n) == NET_ONION;
498 return strprintf(
"ADD_ONION %s%s Port=%i,%s",
510 std::string private_key;
511 for (
const std::string &s : reply.
lines) {
513 std::map<std::string,std::string>::iterator i;
514 if ((i =
m.find(
"ServiceID")) !=
m.end())
516 if ((i =
m.find(
"PrivateKey")) !=
m.end()) {
518 LogWarning(
"tor: ADD_ONION returned a malformed private key");
521 private_key = i->second;
525 LogWarning(
"tor: Error parsing ADD_ONION parameters:");
526 for (
const std::string &s : reply.
lines) {
533 LogWarning(
"tor: ADD_ONION returned an invalid service ID");
536 if (!private_key.empty())
m_private_key = std::move(private_key);
547 LogWarning(
"tor: Add onion failed with unrecognized command (You probably need to upgrade Tor)");
552 add_onion_cb(conn, reply, false);
581 add_onion_cb(conn, reply, true);
619 if (reply.
lines.empty()) {
620 LogWarning(
"tor: AUTHCHALLENGE reply was empty");
624 if (
l.first ==
"AUTHCHALLENGE") {
634 LogWarning(
"tor: ServerNonce is not 32 bytes, as required by spec");
647 LogWarning(
"tor: Invalid reply to AUTHCHALLENGE");
650 LogWarning(
"tor: SAFECOOKIE authentication challenge failed");
664 for (
const std::string &s : reply.
lines) {
666 if (
l.first ==
"AUTH") {
668 std::map<std::string,std::string>::iterator i;
669 if ((i =
m.find(
"METHODS")) !=
m.end()) {
670 std::vector<std::string>
m_vec = SplitString(i->second,
',');
673 if ((i =
m.find(
"COOKIEFILE")) !=
m.end())
675 }
else if (
l.first ==
"VERSION") {
677 std::map<std::string,std::string>::iterator i;
678 if ((i =
m.find(
"Tor")) !=
m.end()) {
683 for (
const std::string &s :
methods) {
693 if (
methods.contains(
"HASHEDPASSWORD")) {
698 LogWarning(
"tor: Password provided with -torpassword, but HASHEDPASSWORD authentication is not available");
700 }
else if (
methods.contains(
"NULL")) {
703 }
else if (
methods.contains(
"SAFECOOKIE")) {
717 LogWarning(
"tor: Authentication cookie %s could not be opened (check permissions)",
cookiefile);
720 }
else if (
methods.contains(
"HASHEDPASSWORD")) {
721 LogWarning(
"tor: The only supported authentication mechanism left is password, but no password provided with -torpassword");
723 LogWarning(
"tor: No supported authentication method");
726 LogWarning(
"tor: Requesting protocol info failed");
735 LogWarning(
"tor: Error sending initial protocolinfo command");
const CChainParams & Params()
Return the currently selected parameters.
#define Assume(val)
Assume is the identity function.
std::vector< std::string > GetArgs(const std::string &strArg) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
Return a vector of strings of the given argument.
std::string GetArg(const std::string &strArg, const std::string &strDefault) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
Return string argument or default value.
fs::path GetDataDirNet() const EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
Get data directory path with appended network identifier.
uint16_t GetDefaultPort() const
A hasher class for HMAC-SHA-256.
static constexpr size_t OUTPUT_SIZE
A combination of a network address (CNetAddr) and a (TCP) port.
std::string ToStringAddrPort() const
A helper class for interruptible sleeps.
virtual bool sleep_for(Clock::duration rel_time) EXCLUSIVE_LOCKS_REQUIRED(!mut)
Sleep for the given duration.
void Add(Network net) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
static constexpr Event RecvEvent
If passed to Wait(), then it will wait for readiness to read from the socket.
static constexpr Event ErrorEvent
Ignored if passed to Wait(), but could be set in the occurred events if an exceptional condition has ...
Low-level handling for Tor control connection.
TorControlReply m_message
Message being received.
std::deque< ReplyHandlerCB > m_reply_handlers
Response handlers.
bool Command(const std::string &cmd, const ReplyHandlerCB &reply_handler)
Send a command, register a handler for the reply.
CThreadInterrupt & m_interrupt
Reference to interrupt object for clean shutdown.
bool WaitForData(std::chrono::milliseconds timeout)
Wait for data to be available on the socket.
std::string m_recv_buffer
Buffer for incoming data.
std::function< void(TorControlConnection &, const TorControlReply &)> ReplyHandlerCB
bool ProcessBuffer()
Process complete lines from the receive buffer.
bool ReceiveAndProcess()
Read available data from socket and process complete replies.
void Disconnect()
Disconnect from Tor control port.
TorControlConnection(CThreadInterrupt &interrupt)
Create a new TorControlConnection.
bool Connect(const std::string &tor_control_center)
Connect to a Tor control port.
std::unique_ptr< Sock > m_sock
Socket for the connection.
bool IsConnected() const
Check if the connection is established.
Reply from Tor, can be single or multi-line.
std::vector< std::string > lines
CThreadInterrupt m_interrupt
fs::path GetPrivateKeyFile()
Get name of file to store private key in.
void connected_cb(TorControlConnection &conn)
Callback after successful connection.
void get_socks_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for GETINFO net/listeners/socks result.
void add_onion_cb(TorControlConnection &conn, const TorControlReply &reply, bool pow_was_enabled)
Callback for ADD_ONION result.
const std::string m_tor_control_center
std::vector< uint8_t > m_client_nonce
ClientNonce for SAFECOOKIE auth.
void disconnected_cb(TorControlConnection &conn)
Callback after connection lost or failed connection attempt.
void authchallenge_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for AUTHCHALLENGE result.
TorControlConnection m_conn
std::atomic< bool > m_reconnect
void Interrupt()
Interrupt the controller thread.
std::string m_private_key
void Join()
Wait for the controller thread to exit.
void auth_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for AUTHENTICATE result.
void protocolinfo_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for PROTOCOLINFO result.
std::chrono::duration< double > m_reconnect_timeout
std::vector< uint8_t > m_cookie
Cookie for SAFECOOKIE auth.
size_t Consumed() const
Returns number of bytes already read from buffer.
std::optional< std::string_view > ReadLine() LIFETIMEBOUND
Returns a string from current iterator position up to (but not including) next and advances iterator...
#define WSAGetLastError()
static std::string PathToString(const path &path)
Convert path object to a byte string.
static path PathFromString(const std::string &string)
Convert byte string to path object.
std::string HexStr(const std::span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
#define LogDebug(category,...)
std::vector< std::string > SplitString(std::string_view str, char sep)
void TraceThread(std::string_view thread_name, std::function< void()> thread_func)
A wrapper for do-something-once thread functions.
void ReplaceAll(std::string &in_out, std::string_view search, std::string_view substitute)
Replace every non-overlapping occurrence of search with substitute, treating both literally; the repl...
std::string ToString(const T &t)
Locale-independent version of std::to_string.
void RemoveLocal(const CService &addr)
bool AddLocal(const CService &addr_, int nScore, bool add_even_if_unreachable)
@ NET_ONION
TOR (v2 or v3)
std::unique_ptr< Sock > ConnectDirectly(const CService &dest, bool manual_connection)
Create a socket and try to connect to the specified service.
bool SetProxy(enum Network net, const Proxy &addrProxy)
std::vector< CService > Lookup(const std::string &name, uint16_t portDefault, bool fAllowLookup, unsigned int nMaxSolutions, DNSLookupFn dns_lookup_function)
Resolve a service string to its corresponding service.
ReachableNets g_reachable_nets
CService LookupNumeric(const std::string &name, uint16_t portDefault, DNSLookupFn dns_lookup_function)
Resolve a service string with a numeric IP to its first corresponding service.
void GetRandBytes(std::span< unsigned char > bytes) noexcept
Generate random data via the internal PRNG.
bool WriteBinaryFile(const fs::path &filename, const std::string &data)
Write contents of std::string to a file.
std::pair< bool, std::string > ReadBinaryFile(const fs::path &filename, size_t maxsize)
Read full contents of a file and return them in a std::string.
std::string NetworkErrorString(int err)
Return readable error string for a network error code.
Utilities for converting data from/to strings.
std::vector< Byte > ParseHex(std::string_view hex_str)
Like TryParseHex, but returns an empty vector on invalid input.
static const std::string TOR_SAFE_CLIENTKEY
For computing clientHash in SAFECOOKIE.
constexpr std::chrono::duration< double > RECONNECT_TIMEOUT_MAX
Maximum reconnect timeout in seconds to prevent excessive delays.
constexpr int TOR_COOKIE_SIZE
Tor cookie size (from control-spec.txt)
constexpr int TOR_NONCE_SIZE
Size of client/server nonce for SAFECOOKIE.
constexpr std::chrono::duration< double > RECONNECT_TIMEOUT_START
Exponential backoff configuration - initial timeout in seconds.
static std::string MakeAddOnionCmd(const std::string &private_key, const std::string &target, bool enable_pow)
const std::string DEFAULT_TOR_CONTROL
Default control ip and port.
std::pair< std::string, std::string > SplitTorReplyLine(const std::string &s)
static const std::string TOR_SAFE_SERVERKEY
For computing server_hash in SAFECOOKIE.
static bool IsWellFormedPrivateKey(std::string_view key)
Whether the value requests a new Ed25519 v3 key or has the expected key format.
static std::vector< uint8_t > ComputeResponse(std::string_view key, std::span< const uint8_t > cookie, std::span< const uint8_t > client_nonce, std::span< const uint8_t > server_nonce)
Compute Tor SAFECOOKIE response.
constexpr int MAX_LINE_COUNT
Maximum number of lines received on TorControlConnection per reply to avoid memory exhaustion.
constexpr double RECONNECT_TIMEOUT_EXP
Exponential backoff configuration - growth factor.
std::map< std::string, std::string > ParseTorReplyMapping(const std::string &s)
Parse reply arguments in the form 'METHODS=COOKIE,SAFECOOKIE COOKIEFILE=".../control_auth_cookie"'.
CService DefaultOnionServiceTarget(uint16_t port)
constexpr std::string_view PRIVATE_KEY_NEW
constexpr std::string_view PRIVATE_KEY_PREFIX
constexpr auto SOCKET_SEND_TIMEOUT
Timeout for socket operations.
constexpr int MAX_LINE_LENGTH
Maximum length for lines received on TorControlConnection.
Functionality for communicating with Tor.
constexpr int TOR_REPLY_SYNTAX_ERROR
Syntax error in command argument.
constexpr uint16_t DEFAULT_TOR_SOCKS_PORT
constexpr int TOR_REPLY_OK
Tor control reply code.
constexpr int TOR_REPLY_UNRECOGNIZED
constexpr int DEFAULT_TOR_CONTROL_PORT
std::pair< std::string, std::string > SplitTorReplyLine(const std::string &s)
std::map< std::string, std::string > ParseTorReplyMapping(const std::string &s)
Parse reply arguments in the form 'METHODS=COOKIE,SAFECOOKIE COOKIEFILE=".../control_auth_cookie"'.
std::optional< std::vector< unsigned char > > DecodeBase64(std::string_view str)
std::string SanitizeString(std::string_view str, int rule)
Remove unsafe chars.
constexpr auto Ticks(Dur2 d)
Helper to count the seconds of a duration/time_point.