40#include <event2/buffer.h>
41#include <event2/bufferevent.h>
42#include <event2/event.h>
43#include <event2/thread.h>
44#include <event2/util.h>
60static const std::string
TOR_SAFE_SERVERKEY =
"Tor safe cookie authentication server-to-controller hash";
62static const std::string
TOR_SAFE_CLIENTKEY =
"Tor safe cookie authentication controller-to-server hash";
104 self->message.lines.push_back(s.substr(4));
108 if (
self->message.code >= 600) {
113 if (!
self->reply_handlers.empty()) {
116 self->reply_handlers.pop_front();
121 self->message.Clear();
128 LogWarning(
"tor: Disconnecting because MAX_LINE_LENGTH exceeded");
218 while (ptr < s.size() && s[ptr] !=
' ') {
219 type.push_back(s[ptr]);
235 std::map<std::string,std::string>
mapping;
237 while (ptr < s.size()) {
238 std::string key, value;
239 while (ptr < s.size() && s[ptr] !=
'=' && s[ptr] !=
' ') {
240 key.push_back(s[ptr]);
244 return std::map<std::string,std::string>();
248 if (ptr < s.size() && s[ptr] ==
'"') {
251 while (ptr < s.size() && (
escape_next || s[ptr] !=
'"')) {
254 value.push_back(s[ptr]);
258 return std::map<std::string,std::string>();
271 for (
size_t i = 0; i < value.size(); ++i) {
272 if (value[i] ==
'\\') {
278 if (value[i] ==
'n') {
280 }
else if (value[i] ==
't') {
282 }
else if (value[i] ==
'r') {
284 }
else if (
'0' <= value[i] && value[i] <=
'7') {
289 for (
j = 1;
j < 3 && (i+
j) < value.size() &&
'0' <= value[i+
j] && value[i+
j] <=
'7'; ++
j) {}
293 if (
j == 3 && value[i] >
'3') {
296 const auto end{i +
j};
300 val += value[i++] -
'0';
314 while (ptr < s.size() && s[ptr] !=
' ') {
315 value.push_back(s[ptr]);
319 if (ptr < s.size() && s[ptr] ==
' ')
333 LogWarning(
"tor: Failed to create event for reconnection: out of memory?");
363 for (
const auto& line : reply.
lines) {
364 if (line.starts_with(
"net/listeners/socks=")) {
375 if (
portstr.starts_with(
"127.0.0.1:")) {
385 LogWarning(
"tor: Get SOCKS port command returned nothing");
388 LogWarning(
"tor: Get SOCKS port command failed with unrecognized command (You probably should upgrade Tor)");
390 LogWarning(
"tor: Get SOCKS port command failed; error code %d", reply.
code);
417 return ParseNetwork(n) == NET_ONION;
433 for (
const std::string &s : reply.
lines) {
435 std::map<std::string,std::string>::iterator i;
436 if ((i =
m.find(
"ServiceID")) !=
m.end())
438 if ((i =
m.find(
"PrivateKey")) !=
m.end())
442 LogWarning(
"tor: Error parsing ADD_ONION parameters:");
443 for (
const std::string &s : reply.
lines) {
458 LogWarning(
"tor: Add onion failed with unrecognized command (You probably need to upgrade Tor)");
504static std::vector<uint8_t>
ComputeResponse(
const std::string &key,
const std::vector<uint8_t> &cookie,
const std::vector<uint8_t> &clientNonce,
const std::vector<uint8_t> &
serverNonce)
509 computeHash.Write(clientNonce.data(), clientNonce.size());
520 if (
l.first ==
"AUTHCHALLENGE") {
530 LogWarning(
"tor: ServerNonce is not 32 bytes, as required by spec");
543 LogWarning(
"tor: Invalid reply to AUTHCHALLENGE");
546 LogWarning(
"tor: SAFECOOKIE authentication challenge failed");
560 for (
const std::string &s : reply.
lines) {
562 if (
l.first ==
"AUTH") {
564 std::map<std::string,std::string>::iterator i;
565 if ((i =
m.find(
"METHODS")) !=
m.end()) {
566 std::vector<std::string>
m_vec = SplitString(i->second,
',');
569 if ((i =
m.find(
"COOKIEFILE")) !=
m.end())
571 }
else if (
l.first ==
"VERSION") {
573 std::map<std::string,std::string>::iterator i;
574 if ((i =
m.find(
"Tor")) !=
m.end()) {
579 for (
const std::string &s :
methods) {
589 if (
methods.contains(
"HASHEDPASSWORD")) {
594 LogWarning(
"tor: Password provided with -torpassword, but HASHEDPASSWORD authentication is not available");
596 }
else if (
methods.contains(
"NULL")) {
599 }
else if (
methods.contains(
"SAFECOOKIE")) {
613 LogWarning(
"tor: Authentication cookie %s could not be opened (check permissions)",
cookiefile);
616 }
else if (
methods.contains(
"HASHEDPASSWORD")) {
617 LogWarning(
"tor: The only supported authentication mechanism left is password, but no password provided with -torpassword");
619 LogWarning(
"tor: No supported authentication method");
622 LogWarning(
"tor: Requesting protocol info failed");
631 LogWarning(
"tor: Error sending initial protocolinfo command");
697 LogWarning(
"tor: Unable to create event_base");
709 LogInfo(
"tor: Thread interrupt\n");
712 },
nullptr,
nullptr);
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
Return a vector of strings of the given argument.
fs::path GetDataDirNet() const
Get data directory path with appended network identifier.
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
A hasher class for HMAC-SHA-256.
static const size_t OUTPUT_SIZE
A combination of a network address (CNetAddr) and a (TCP) port.
std::string ToStringAddrPort() const
void Add(Network net) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Low-level handling for Tor control connection.
std::deque< ReplyHandlerCB > reply_handlers
Response handlers.
std::function< void(TorControlConnection &) disconnected)
Callback when connection lost.
bool Connect(const std::string &tor_control_center, const ConnectionCB &connected, const ConnectionCB &disconnected)
Connect to a Tor control port.
std::function< void(TorControlConnection &) connected)
Callback when ready for use.
TorControlConnection(struct event_base *base)
Create a new TorControlConnection.
bool Command(const std::string &cmd, const ReplyHandlerCB &reply_handler)
Send a command, register a handler for the reply.
static void readcb(struct bufferevent *bev, void *ctx)
Libevent handlers: internal.
static void eventcb(struct bufferevent *bev, short what, void *ctx)
void Disconnect()
Disconnect from Tor control port.
struct bufferevent * b_conn
Connection to control socket.
struct event_base * base
Libevent event base.
Reply from Tor, can be single or multi-line.
std::vector< std::string > lines
Controller that connects to Tor control socket, authenticate, then create and maintain an ephemeral o...
TorControlConnection conn
static void reconnect_cb(evutil_socket_t fd, short what, void *arg)
Callback for reconnect timer.
fs::path GetPrivateKeyFile()
Get name of file to store private key in.
std::vector< uint8_t > clientNonce
ClientNonce for SAFECOOKIE auth.
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)
Callback for ADD_ONION result.
const std::string m_tor_control_center
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.
std::vector< uint8_t > cookie
Cookie for SAFECOOKIE auth.
struct event * reconnect_ev
void auth_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for AUTHENTICATE result.
void Reconnect()
Reconnect, after getting disconnected.
void protocolinfo_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for PROTOCOLINFO result.
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.
std::string ToString(const T &t)
Locale-independent version of std::to_string.
void ReplaceAll(std::string &in_out, const std::string &search, const std::string &substitute)
void RemoveLocal(const CService &addr)
bool AddLocal(const CService &addr_, int nScore)
@ NET_ONION
TOR (v2 or v3)
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::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.
static std::vector< uint8_t > ComputeResponse(const std::string &key, const std::vector< uint8_t > &cookie, const std::vector< uint8_t > &clientNonce, const std::vector< uint8_t > &serverNonce)
Compute Tor SAFECOOKIE response.
static const int MAX_LINE_LENGTH
Maximum length for lines received on TorControlConnection.
static const float RECONNECT_TIMEOUT_EXP
Exponential backoff configuration - growth factor.
const std::string DEFAULT_TOR_CONTROL
Default control ip and port.
static void TorControlThread(CService onion_service_target)
std::pair< std::string, std::string > SplitTorReplyLine(const std::string &s)
static const int TOR_REPLY_UNRECOGNIZED
static const std::string TOR_SAFE_SERVERKEY
For computing serverHash in SAFECOOKIE.
static const int TOR_COOKIE_SIZE
Tor cookie size (from control-spec.txt)
static struct event_base * gBase
static const int TOR_NONCE_SIZE
Size of client/server nonce for SAFECOOKIE.
static const float RECONNECT_TIMEOUT_MAX
Maximum reconnect timeout in seconds to prevent excessive delays.
void InterruptTorControl()
std::map< std::string, std::string > ParseTorReplyMapping(const std::string &s)
Parse reply arguments in the form 'METHODS=COOKIE,SAFECOOKIE COOKIEFILE=".../control_auth_cookie"'.
static const int TOR_REPLY_OK
Tor control reply code.
static std::thread torControlThread
static const float RECONNECT_TIMEOUT_START
Exponential backoff configuration - initial timeout in seconds.
CService DefaultOnionServiceTarget(uint16_t port)
void StartTorControl(CService onion_service_target)
constexpr uint16_t DEFAULT_TOR_SOCKS_PORT
Functionality for communicating with Tor.
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::string SanitizeString(std::string_view str, int rule)
Remove unsafe chars.
struct timeval MillisToTimeval(int64_t nTimeout)
Convert milliseconds to a struct timeval for e.g.
constexpr auto Ticks(Dur2 d)
Helper to count the seconds of a duration/time_point.