27 #include <boost/signals2/signal.hpp> 28 #include <boost/algorithm/string/split.hpp> 29 #include <boost/algorithm/string/classification.hpp> 30 #include <boost/algorithm/string/replace.hpp> 32 #include <event2/bufferevent.h> 33 #include <event2/buffer.h> 34 #include <event2/util.h> 35 #include <event2/event.h> 36 #include <event2/thread.h> 45 static const std::string
TOR_SAFE_SERVERKEY =
"Tor safe cookie authentication server-to-controller hash";
47 static const std::string
TOR_SAFE_CLIENTKEY =
"Tor safe cookie authentication controller-to-server hash";
61 base(_base), b_conn(nullptr)
74 struct evbuffer *input = bufferevent_get_input(bev);
75 size_t n_read_out = 0;
79 while((line = evbuffer_readln(input, &n_read_out, EVBUFFER_EOL_CRLF)) !=
nullptr)
81 std::string s(line, n_read_out);
86 self->message.code =
atoi(s.substr(0,3));
87 self->message.lines.push_back(s.substr(4));
91 if (self->message.code >= 600) {
94 self->async_handler(*
self, self->message);
96 if (!self->reply_handlers.empty()) {
98 self->reply_handlers.front()(*
self,
self->message);
99 self->reply_handlers.pop_front();
104 self->message.Clear();
111 LogPrintf(
"tor: Disconnecting because MAX_LINE_LENGTH exceeded\n");
119 if (what & BEV_EVENT_CONNECTED) {
121 self->connected(*
self);
122 }
else if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
123 if (what & BEV_EVENT_ERROR) {
129 self->disconnected(*
self);
138 struct sockaddr_storage connect_to_addr;
139 int connect_to_addrlen =
sizeof(connect_to_addr);
140 if (evutil_parse_sockaddr_port(tor_control_center.c_str(),
141 (
struct sockaddr*)&connect_to_addr, &connect_to_addrlen)<0) {
142 LogPrintf(
"tor: Error parsing socket address %s\n", tor_control_center);
147 b_conn = bufferevent_socket_new(
base, -1, BEV_OPT_CLOSE_ON_FREE);
151 bufferevent_enable(
b_conn, EV_READ|EV_WRITE);
156 if (bufferevent_socket_connect(
b_conn, (
struct sockaddr*)&connect_to_addr, connect_to_addrlen) < 0) {
157 LogPrintf(
"tor: Error connecting to address %s\n", tor_control_center);
174 struct evbuffer *buf = bufferevent_get_output(
b_conn);
177 evbuffer_add(buf, cmd.data(), cmd.size());
178 evbuffer_add(buf,
"\r\n", 2);
194 while (ptr < s.size() && s[ptr] !=
' ') {
195 type.push_back(s[ptr]);
200 return make_pair(type, s.substr(ptr));
211 std::map<std::string,std::string> mapping;
213 while (ptr < s.size()) {
214 std::string key, value;
215 while (ptr < s.size() && s[ptr] !=
'=' && s[ptr] !=
' ') {
216 key.push_back(s[ptr]);
220 return std::map<std::string,std::string>();
224 if (ptr < s.size() && s[ptr] ==
'"') {
226 bool escape_next =
false;
227 while (ptr < s.size() && (escape_next || s[ptr] !=
'"')) {
229 escape_next = (s[ptr] ==
'\\' && !escape_next);
230 value.push_back(s[ptr]);
234 return std::map<std::string,std::string>();
246 std::string escaped_value;
247 for (
size_t i = 0; i < value.size(); ++i) {
248 if (value[i] ==
'\\') {
254 if (value[i] ==
'n') {
255 escaped_value.push_back(
'\n');
256 }
else if (value[i] ==
't') {
257 escaped_value.push_back(
'\t');
258 }
else if (value[i] ==
'r') {
259 escaped_value.push_back(
'\r');
260 }
else if (
'0' <= value[i] && value[i] <=
'7') {
265 for (j = 1; j < 3 && (i+j) < value.size() &&
'0' <= value[i+j] && value[i+j] <=
'7'; ++j) {}
269 if (j == 3 && value[i] >
'3') {
272 escaped_value.push_back(strtol(value.substr(i, j).c_str(),
nullptr, 8));
276 escaped_value.push_back(value[i]);
279 escaped_value.push_back(value[i]);
282 value = escaped_value;
284 while (ptr < s.size() && s[ptr] !=
' ') {
285 value.push_back(s[ptr]);
289 if (ptr < s.size() && s[ptr] ==
' ')
291 mapping[key] = value;
298 m_tor_control_center(tor_control_center), conn(base), reconnect(true), reconnect_ev(0),
304 LogPrintf(
"tor: Failed to create event for reconnection: out of memory?\n");
331 if (reply.
code == 250) {
333 for (
const std::string &s : reply.
lines) {
335 std::map<std::string,std::string>::iterator i;
336 if ((i = m.find(
"ServiceID")) != m.end())
338 if ((i = m.find(
"PrivateKey")) != m.end())
342 LogPrintf(
"tor: Error parsing ADD_ONION parameters:\n");
343 for (
const std::string &s : reply.
lines) {
357 }
else if (reply.
code == 510) {
358 LogPrintf(
"tor: Add onion failed with unrecognized command (You probably need to upgrade Tor)\n");
360 LogPrintf(
"tor: Add onion failed; error code %d\n", reply.
code);
366 if (reply.
code == 250) {
387 LogPrintf(
"tor: Authentication failed\n");
407 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)
409 CHMAC_SHA256 computeHash((
const uint8_t*)key.data(), key.size());
411 computeHash.Write(cookie.data(), cookie.size());
412 computeHash.Write(clientNonce.data(), clientNonce.size());
413 computeHash.Write(serverNonce.data(), serverNonce.size());
414 computeHash.Finalize(computedHash.data());
420 if (reply.
code == 250) {
423 if (l.first ==
"AUTHCHALLENGE") {
429 std::vector<uint8_t> serverHash =
ParseHex(m[
"SERVERHASH"]);
430 std::vector<uint8_t> serverNonce =
ParseHex(m[
"SERVERNONCE"]);
432 if (serverNonce.size() != 32) {
433 LogPrintf(
"tor: ServerNonce is not 32 bytes, as required by spec\n");
438 if (computedServerHash != serverHash) {
439 LogPrintf(
"tor: ServerHash %s does not match expected ServerHash %s\n",
HexStr(serverHash),
HexStr(computedServerHash));
446 LogPrintf(
"tor: Invalid reply to AUTHCHALLENGE\n");
449 LogPrintf(
"tor: SAFECOOKIE authentication challenge failed\n");
455 if (reply.
code == 250) {
456 std::set<std::string> methods;
457 std::string cookiefile;
463 for (
const std::string &s : reply.
lines) {
465 if (l.first ==
"AUTH") {
467 std::map<std::string,std::string>::iterator i;
468 if ((i = m.find(
"METHODS")) != m.end())
469 boost::split(methods, i->second, boost::is_any_of(
","));
470 if ((i = m.find(
"COOKIEFILE")) != m.end())
471 cookiefile = i->second;
472 }
else if (l.first ==
"VERSION") {
474 std::map<std::string,std::string>::iterator i;
475 if ((i = m.find(
"Tor")) != m.end()) {
480 for (
const std::string &s : methods) {
488 std::string torpassword =
gArgs.
GetArg(
"-torpassword",
"");
489 if (!torpassword.empty()) {
490 if (methods.count(
"HASHEDPASSWORD")) {
492 boost::replace_all(torpassword,
"\"",
"\\\"");
493 _conn.
Command(
"AUTHENTICATE \"" + torpassword +
"\"", std::bind(&
TorController::auth_cb,
this, std::placeholders::_1, std::placeholders::_2));
495 LogPrintf(
"tor: Password provided with -torpassword, but HASHEDPASSWORD authentication is not available\n");
497 }
else if (methods.count(
"NULL")) {
500 }
else if (methods.count(
"SAFECOOKIE")) {
502 LogPrint(
BCLog::TOR,
"tor: Using SAFECOOKIE authentication, reading cookie authentication from %s\n", cookiefile);
504 if (status_cookie.first && status_cookie.second.size() ==
TOR_COOKIE_SIZE) {
506 cookie = std::vector<uint8_t>(status_cookie.second.begin(), status_cookie.second.end());
511 if (status_cookie.first) {
512 LogPrintf(
"tor: Authentication cookie %s is not exactly %i bytes, as is required by the spec\n", cookiefile,
TOR_COOKIE_SIZE);
514 LogPrintf(
"tor: Authentication cookie %s could not be opened (check permissions)\n", cookiefile);
517 }
else if (methods.count(
"HASHEDPASSWORD")) {
518 LogPrintf(
"tor: The only supported authentication mechanism left is password, but no password provided with -torpassword\n");
520 LogPrintf(
"tor: No supported authentication method\n");
523 LogPrintf(
"tor: Requesting protocol info failed\n");
532 LogPrintf(
"tor: Error sending initial protocolinfo command\n");
583 event_base_dispatch(
gBase);
590 evthread_use_windows_threads();
592 evthread_use_pthreads();
594 gBase = event_base_new();
596 LogPrintf(
"tor: Unable to create event_base\n");
609 event_base_once(
gBase, -1, EV_TIMEOUT, [](evutil_socket_t,
short,
void*) {
610 event_base_loopbreak(
gBase);
611 },
nullptr,
nullptr);
619 event_base_free(
gBase);
626 struct in_addr onion_service_target;
627 onion_service_target.s_addr = htonl(INADDR_LOOPBACK);
void authchallenge_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for AUTHCHALLENGE result.
std::string SanitizeString(const std::string &str, int rule)
Remove unsafe chars.
static void TorControlThread(CService onion_service_target)
static std::thread torControlThread
static const std::string TOR_SAFE_CLIENTKEY
For computing clientHash in SAFECOOKIE.
#define LogPrint(category,...)
bool AddLocal(const CService &addr, int nScore)
std::function< void(TorControlConnection &)> disconnected
Callback when connection lost.
std::function< void(TorControlConnection &)> ConnectionCB
bool Connect(const std::string &tor_control_center, const ConnectionCB &connected, const ConnectionCB &disconnected)
Connect to a Tor control port.
struct bufferevent * b_conn
Connection to control socket.
static const int TOR_COOKIE_SIZE
Tor cookie size (from control-spec.txt)
std::vector< uint8_t > clientNonce
ClientNonce for SAFECOOKIE auth.
static struct event_base * gBase
Reply from Tor, can be single or multi-line.
std::vector< unsigned char > ParseHex(const char *psz)
A hasher class for HMAC-SHA-256.
void protocolinfo_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for PROTOCOLINFO result.
void Reconnect()
Reconnect, after getting disconnected.
std::vector< std::string > lines
static const int TOR_NONCE_SIZE
Size of client/server nonce for SAFECOOKIE.
const CBaseChainParams & BaseParams()
Return the currently selected parameters.
uint16_t OnionServiceTargetPort() const
void GetRandBytes(unsigned char *buf, int num) noexcept
Overall design of the RNG and entropy sources.
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 StartTorControl(CService onion_service_target)
std::deque< ReplyHandlerCB > reply_handlers
Response handlers.
void SetReachable(enum Network net, bool reachable)
Mark a network as reachable or unreachable (no automatic connects to it)
bool WriteBinaryFile(const fs::path &filename, const std::string &data)
Write contents of std::string to a file.
std::function< void(TorControlConnection &, const TorControlReply &)> ReplyHandlerCB
void disconnected_cb(TorControlConnection &conn)
Callback after connection lost or failed connection attempt.
const std::string m_tor_control_center
static void readcb(struct bufferevent *bev, void *ctx)
Libevent handlers: internal.
fs::path GetPrivateKeyFile()
Get name of file to store private key in.
struct event * reconnect_ev
TorControlConnection(struct event_base *base)
Create a new TorControlConnection.
void TraceThread(const char *thread_name, std::function< void()> thread_func)
A wrapper for do-something-once thread functions.
std::map< std::string, std::string > ParseTorReplyMapping(const std::string &s)
Parse reply arguments in the form 'METHODS=COOKIE,SAFECOOKIE COOKIEFILE=".../control_auth_cookie"'.
const std::string DEFAULT_TOR_CONTROL
Default control port.
std::vector< uint8_t > cookie
Cookie for SAFECOOKIE auth.
static const float RECONNECT_TIMEOUT_START
Exponential backoff configuration - initial timeout in seconds.
TorControlConnection conn
static const float RECONNECT_TIMEOUT_EXP
Exponential backoff configuration - growth factor.
static secp256k1_context * ctx
int atoi(const std::string &str)
A combination of a network address (CNetAddr) and a (TCP) port.
static void reconnect_cb(evutil_socket_t fd, short what, void *arg)
Callback for reconnect timer.
void add_onion_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for ADD_ONION result.
std::pair< bool, std::string > ReadBinaryFile(const fs::path &filename, size_t maxsize=std::numeric_limits< size_t >::max())
Read full contents of a file and return them in a std::string.
std::pair< std::string, std::string > SplitTorReplyLine(const std::string &s)
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
bool SetProxy(enum Network net, const proxyType &addrProxy)
void auth_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for AUTHENTICATE result.
void Disconnect()
Disconnect from Tor control port.
const CChainParams & Params()
Return the currently selected parameters.
static void eventcb(struct bufferevent *bev, short what, void *ctx)
static const size_t OUTPUT_SIZE
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
std::string ToStringIPPort() const
bool Command(const std::string &cmd, const ReplyHandlerCB &reply_handler)
Send a command, register a handler for the reply.
CService DefaultOnionServiceTarget()
struct timeval MillisToTimeval(int64_t nTimeout)
Convert milliseconds to a struct timeval for e.g.
void connected_cb(TorControlConnection &conn)
Callback after successful connection.
std::string ToString() const
Controller that connects to Tor control socket, authenticate, then create and maintain an ephemeral o...
void InterruptTorControl()
Low-level handling for Tor control connection.
static const int MAX_LINE_LENGTH
Maximum length for lines received on TorControlConnection.
static const std::string TOR_SAFE_SERVERKEY
For computing serverHash 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.
void RemoveLocal(const CService &addr)
const fs::path & GetDataDirNet() const
Get data directory path with appended network identifier.
struct event_base * base
Libevent event base.
std::function< void(TorControlConnection &)> connected
Callback when ready for use.