39#include <event2/buffer.h>
40#include <event2/bufferevent.h>
41#include <event2/event.h>
42#include <event2/thread.h>
43#include <event2/util.h>
56static const std::string
TOR_SAFE_SERVERKEY =
"Tor safe cookie authentication server-to-controller hash";
58static const std::string
TOR_SAFE_CLIENTKEY =
"Tor safe cookie authentication controller-to-server hash";
86 struct evbuffer *input = bufferevent_get_input(bev);
87 size_t n_read_out = 0;
91 while((line = evbuffer_readln(input, &n_read_out, EVBUFFER_EOL_CRLF)) !=
nullptr)
93 std::string
s(line, n_read_out);
98 self->
message.
code = ToIntegral<int>(
s.substr(0, 3)).value_or(0);
123 LogPrintf(
"tor: Disconnecting because MAX_LINE_LENGTH exceeded\n");
131 if (what & BEV_EVENT_CONNECTED) {
134 }
else if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
135 if (what & BEV_EVENT_ERROR) {
152 if (!control_service.has_value()) {
153 LogPrintf(
"tor: Failed to look up control center %s\n", tor_control_center);
157 struct sockaddr_storage control_address;
158 socklen_t control_address_len =
sizeof(control_address);
159 if (!control_service.value().GetSockAddr(
reinterpret_cast<struct sockaddr*
>(&control_address), &control_address_len)) {
160 LogPrintf(
"tor: Error parsing socket address %s\n", tor_control_center);
165 b_conn = bufferevent_socket_new(
base, -1, BEV_OPT_CLOSE_ON_FREE);
170 bufferevent_enable(
b_conn, EV_READ|EV_WRITE);
175 if (bufferevent_socket_connect(
b_conn,
reinterpret_cast<struct sockaddr*
>(&control_address), control_address_len) < 0) {
176 LogPrintf(
"tor: Error connecting to address %s\n", tor_control_center);
193 struct evbuffer *buf = bufferevent_get_output(
b_conn);
196 evbuffer_add(buf,
cmd.data(),
cmd.size());
197 evbuffer_add(buf,
"\r\n", 2);
213 while (ptr <
s.size() &&
s[ptr] !=
' ') {
214 type.push_back(
s[ptr]);
219 return make_pair(type,
s.substr(ptr));
230 std::map<std::string,std::string> mapping;
232 while (ptr <
s.size()) {
233 std::string key, value;
234 while (ptr <
s.size() &&
s[ptr] !=
'=' &&
s[ptr] !=
' ') {
235 key.push_back(
s[ptr]);
239 return std::map<std::string,std::string>();
243 if (ptr <
s.size() &&
s[ptr] ==
'"') {
245 bool escape_next =
false;
246 while (ptr <
s.size() && (escape_next ||
s[ptr] !=
'"')) {
248 escape_next = (
s[ptr] ==
'\\' && !escape_next);
249 value.push_back(
s[ptr]);
253 return std::map<std::string,std::string>();
265 std::string escaped_value;
266 for (
size_t i = 0; i < value.size(); ++i) {
267 if (value[i] ==
'\\') {
273 if (value[i] ==
'n') {
274 escaped_value.push_back(
'\n');
275 }
else if (value[i] ==
't') {
276 escaped_value.push_back(
'\t');
277 }
else if (value[i] ==
'r') {
278 escaped_value.push_back(
'\r');
279 }
else if (
'0' <= value[i] && value[i] <=
'7') {
284 for (j = 1; j < 3 && (i+j) < value.size() &&
'0' <= value[i+j] && value[i+j] <=
'7'; ++j) {}
288 if (j == 3 && value[i] >
'3') {
291 const auto end{i + j};
295 val += value[i++] -
'0';
297 escaped_value.push_back(
char(val));
301 escaped_value.push_back(value[i]);
304 escaped_value.push_back(value[i]);
307 value = escaped_value;
309 while (ptr <
s.size() &&
s[ptr] !=
' ') {
310 value.push_back(
s[ptr]);
314 if (ptr <
s.size() &&
s[ptr] ==
' ')
316 mapping[key] = value;
323 m_tor_control_center(tor_control_center), conn(base), reconnect(true), reconnect_timeout(
RECONNECT_TIMEOUT_START),
328 LogPrintf(
"tor: Failed to create event for reconnection: out of memory?\n");
356 std::string socks_location;
357 if (reply.
code == 250) {
358 for (
const auto& line : reply.
lines) {
359 if (line.starts_with(
"net/listeners/socks=")) {
360 const std::string port_list_str = line.substr(20);
361 std::vector<std::string> port_list =
SplitString(port_list_str,
' ');
363 for (
auto& portstr : port_list) {
364 if (portstr.empty())
continue;
365 if ((portstr[0] ==
'"' || portstr[0] ==
'\'') && portstr.size() >= 2 && (*portstr.rbegin() == portstr[0])) {
366 portstr = portstr.substr(1, portstr.size() - 2);
367 if (portstr.empty())
continue;
369 socks_location = portstr;
370 if (portstr.starts_with(
"127.0.0.1:")) {
377 if (!socks_location.empty()) {
380 LogPrintf(
"tor: Get SOCKS port command returned nothing\n");
382 }
else if (reply.
code == 510) {
383 LogPrintf(
"tor: Get SOCKS port command failed with unrecognized command (You probably should upgrade Tor)\n");
385 LogPrintf(
"tor: Get SOCKS port command failed; error code %d\n", reply.
code);
390 if (!socks_location.empty()) {
405 const bool onion_allowed_by_onlynet{
407 std::any_of(onlynets.begin(), onlynets.end(), [](
const auto& n) {
408 return ParseNetwork(n) == NET_ONION;
411 if (onion_allowed_by_onlynet) {
422 if (reply.
code == 250) {
424 for (
const std::string &
s : reply.
lines) {
426 std::map<std::string,std::string>::iterator i;
427 if ((i =
m.find(
"ServiceID")) !=
m.end())
429 if ((i =
m.find(
"PrivateKey")) !=
m.end())
433 LogPrintf(
"tor: Error parsing ADD_ONION parameters:\n");
434 for (
const std::string &
s : reply.
lines) {
448 }
else if (reply.
code == 510) {
449 LogPrintf(
"tor: Add onion failed with unrecognized command (You probably need to upgrade Tor)\n");
451 LogPrintf(
"tor: Add onion failed; error code %d\n", reply.
code);
457 if (reply.
code == 250) {
475 LogPrintf(
"tor: Authentication failed\n");
495static 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)
497 CHMAC_SHA256 computeHash((
const uint8_t*)key.data(), key.size());
499 computeHash.
Write(cookie.data(), cookie.size());
500 computeHash.
Write(clientNonce.data(), clientNonce.size());
501 computeHash.
Write(serverNonce.data(), serverNonce.size());
502 computeHash.
Finalize(computedHash.data());
508 if (reply.
code == 250) {
511 if (l.first ==
"AUTHCHALLENGE") {
517 std::vector<uint8_t> serverHash =
ParseHex(
m[
"SERVERHASH"]);
518 std::vector<uint8_t> serverNonce =
ParseHex(
m[
"SERVERNONCE"]);
520 if (serverNonce.size() != 32) {
521 LogPrintf(
"tor: ServerNonce is not 32 bytes, as required by spec\n");
526 if (computedServerHash != serverHash) {
527 LogPrintf(
"tor: ServerHash %s does not match expected ServerHash %s\n",
HexStr(serverHash),
HexStr(computedServerHash));
534 LogPrintf(
"tor: Invalid reply to AUTHCHALLENGE\n");
537 LogPrintf(
"tor: SAFECOOKIE authentication challenge failed\n");
543 if (reply.
code == 250) {
544 std::set<std::string> methods;
545 std::string cookiefile;
551 for (
const std::string &
s : reply.
lines) {
553 if (l.first ==
"AUTH") {
555 std::map<std::string,std::string>::iterator i;
556 if ((i =
m.find(
"METHODS")) !=
m.end()) {
557 std::vector<std::string> m_vec =
SplitString(i->second,
',');
558 methods = std::set<std::string>(m_vec.begin(), m_vec.end());
560 if ((i =
m.find(
"COOKIEFILE")) !=
m.end())
561 cookiefile = i->second;
562 }
else if (l.first ==
"VERSION") {
564 std::map<std::string,std::string>::iterator i;
565 if ((i =
m.find(
"Tor")) !=
m.end()) {
570 for (
const std::string &
s : methods) {
578 std::string torpassword =
gArgs.
GetArg(
"-torpassword",
"");
579 if (!torpassword.empty()) {
580 if (methods.count(
"HASHEDPASSWORD")) {
583 _conn.
Command(
"AUTHENTICATE \"" + torpassword +
"\"", std::bind(&
TorController::auth_cb,
this, std::placeholders::_1, std::placeholders::_2));
585 LogPrintf(
"tor: Password provided with -torpassword, but HASHEDPASSWORD authentication is not available\n");
587 }
else if (methods.count(
"NULL")) {
590 }
else if (methods.count(
"SAFECOOKIE")) {
592 LogDebug(
BCLog::TOR,
"Using SAFECOOKIE authentication, reading cookie authentication from %s\n", cookiefile);
594 if (status_cookie.first && status_cookie.second.size() ==
TOR_COOKIE_SIZE) {
596 cookie = std::vector<uint8_t>(status_cookie.second.begin(), status_cookie.second.end());
601 if (status_cookie.first) {
602 LogPrintf(
"tor: Authentication cookie %s is not exactly %i bytes, as is required by the spec\n", cookiefile,
TOR_COOKIE_SIZE);
604 LogPrintf(
"tor: Authentication cookie %s could not be opened (check permissions)\n", cookiefile);
607 }
else if (methods.count(
"HASHEDPASSWORD")) {
608 LogPrintf(
"tor: The only supported authentication mechanism left is password, but no password provided with -torpassword\n");
610 LogPrintf(
"tor: No supported authentication method\n");
613 LogPrintf(
"tor: Requesting protocol info failed\n");
622 LogPrintf(
"tor: Error sending initial protocolinfo command\n");
673 event_base_dispatch(
gBase);
680 evthread_use_windows_threads();
682 evthread_use_pthreads();
684 gBase = event_base_new();
686 LogPrintf(
"tor: Unable to create event_base\n");
699 event_base_once(
gBase, -1, EV_TIMEOUT, [](evutil_socket_t,
short,
void*) {
700 event_base_loopbreak(
gBase);
701 },
nullptr,
nullptr);
709 event_base_free(
gBase);
716 struct in_addr onion_service_target;
717 onion_service_target.s_addr = htonl(INADDR_LOOPBACK);
718 return {onion_service_target, port};
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.
bool IsArgSet(const std::string &strArg) const
Return true if the given argument has been manually set.
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.
CHMAC_SHA256 & Write(const unsigned char *data, size_t len)
void Finalize(unsigned char hash[OUTPUT_SIZE])
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.
TorControlReply message
Message being received.
std::deque< ReplyHandlerCB > reply_handlers
Response handlers.
bool Connect(const std::string &tor_control_center, const ConnectionCB &connected, const ConnectionCB &disconnected)
Connect to a Tor control port.
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.
std::function< void(TorControlConnection &)> connected
Callback when ready for use.
static void readcb(struct bufferevent *bev, void *ctx)
Libevent handlers: internal.
std::function< void(TorControlConnection &, const TorControlReply &)> ReplyHandlerCB
static void eventcb(struct bufferevent *bev, short what, void *ctx)
std::function< void(TorControlConnection &)> disconnected
Callback when connection lost.
std::function< void(TorControlConnection &)> ConnectionCB
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.
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
#define LogDebug(category,...)
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::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(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.
struct timeval MillisToTimeval(int64_t nTimeout)
Convert milliseconds to a struct timeval for e.g.
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 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.
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 std::thread torControlThread
static const float RECONNECT_TIMEOUT_START
Exponential backoff configuration - initial timeout in seconds.
static const uint16_t DEFAULT_TOR_SOCKS_PORT
CService DefaultOnionServiceTarget(uint16_t port)
void StartTorControl(CService onion_service_target)
constexpr int DEFAULT_TOR_CONTROL_PORT
Functionality for communicating with Tor.
std::string SanitizeString(std::string_view str, int rule)
Remove unsafe chars.