Bitcoin ABC 0.32.4
P2P Digital Currency
torcontrol.cpp
Go to the documentation of this file.
1// Copyright (c) 2015-2016 The Bitcoin Core developers
2// Copyright (c) 2017 The Zcash developers
3// Distributed under the MIT software license, see the accompanying
4// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6#include <torcontrol.h>
7
8#include <chainparams.h>
9#include <chainparamsbase.h>
10#include <common/args.h>
11#include <compat.h>
12#include <crypto/hmac_sha256.h>
13#include <logging.h>
14#include <net.h>
15#include <netaddress.h>
16#include <netbase.h>
17#include <util/readwritefile.h>
18#include <util/strencodings.h>
19#include <util/string.h>
20#include <util/thread.h>
21#include <util/time.h>
22
23#include <boost/signals2/signal.hpp>
24
25#include <event2/buffer.h>
26#include <event2/bufferevent.h>
27#include <event2/event.h>
28#include <event2/thread.h>
29#include <event2/util.h>
30
31#include <cstdlib>
32#include <deque>
33#include <functional>
34#include <set>
35#include <vector>
36
38const std::string DEFAULT_TOR_CONTROL = "127.0.0.1:9051";
40static const int TOR_COOKIE_SIZE = 32;
42static const int TOR_NONCE_SIZE = 32;
44static const std::string TOR_SAFE_SERVERKEY =
45 "Tor safe cookie authentication server-to-controller hash";
47static const std::string TOR_SAFE_CLIENTKEY =
48 "Tor safe cookie authentication controller-to-server hash";
50static const float RECONNECT_TIMEOUT_START = 1.0;
52static const float RECONNECT_TIMEOUT_EXP = 1.5;
59static const int MAX_LINE_LENGTH = 100000;
60
61/****** Low-level TorControlConnection ********/
62
65public:
67
68 int code;
69 std::vector<std::string> lines;
70
71 void Clear() {
72 code = 0;
73 lines.clear();
74 }
75};
76
82public:
83 typedef std::function<void(TorControlConnection &)> ConnectionCB;
84 typedef std::function<void(TorControlConnection &, const TorControlReply &)>
86
89 explicit TorControlConnection(struct event_base *base);
91
100 bool Connect(const std::string &tor_control_center,
101 const ConnectionCB &connected,
103
107 void Disconnect();
108
114 bool Command(const std::string &cmd, const ReplyHandlerCB &reply_handler);
115
117 boost::signals2::signal<void(TorControlConnection &,
118 const TorControlReply &)>
120
121private:
123 std::function<void(TorControlConnection &)> connected;
125 std::function<void(TorControlConnection &)> disconnected;
127 struct event_base *base;
129 struct bufferevent *b_conn{nullptr};
133 std::deque<ReplyHandlerCB> reply_handlers;
134
136 static void readcb(struct bufferevent *bev, void *ctx);
137 static void eventcb(struct bufferevent *bev, short what, void *ctx);
138};
139
141 : base(_base) {}
142
144 if (b_conn) {
145 bufferevent_free(b_conn);
146 }
147}
148
149void TorControlConnection::readcb(struct bufferevent *bev, void *ctx) {
150 TorControlConnection *self = static_cast<TorControlConnection *>(ctx);
151 struct evbuffer *input = bufferevent_get_input(bev);
152 size_t n_read_out = 0;
153 char *line;
154 assert(input);
155 // If there is not a whole line to read, evbuffer_readln returns nullptr
156 while ((line = evbuffer_readln(input, &n_read_out, EVBUFFER_EOL_CRLF)) !=
157 nullptr) {
158 std::string s(line, n_read_out);
159 free(line);
160 // Short line
161 if (s.size() < 4) {
162 continue;
163 }
164 // <status>(-|+| )<data><CRLF>
165 self->message.code = LocaleIndependentAtoi<int>(s.substr(0, 3));
166 self->message.lines.push_back(s.substr(4));
167 // '-','+' or ' '
168 char ch = s[3];
169 if (ch == ' ') {
170 // Final line, dispatch reply and clean up
171 if (self->message.code >= 600) {
172 // Dispatch async notifications to async handler.
173 // Synchronous and asynchronous messages are never interleaved
174 self->async_handler(*self, self->message);
175 } else {
176 if (!self->reply_handlers.empty()) {
177 // Invoke reply handler with message
178 self->reply_handlers.front()(*self, self->message);
179 self->reply_handlers.pop_front();
180 } else {
182 "tor: Received unexpected sync reply %i\n",
183 self->message.code);
184 }
185 }
186 self->message.Clear();
187 }
188 }
189
190 // Check for size of buffer - protect against memory exhaustion with very
191 // long lines. Do this after evbuffer_readln to make sure all full lines
192 // have been removed from the buffer. Everything left is an incomplete line.
193 if (evbuffer_get_length(input) > MAX_LINE_LENGTH) {
194 LogPrintf("tor: Disconnecting because MAX_LINE_LENGTH exceeded\n");
195 self->Disconnect();
196 }
197}
198
199void TorControlConnection::eventcb(struct bufferevent *bev, short what,
200 void *ctx) {
201 TorControlConnection *self = static_cast<TorControlConnection *>(ctx);
202 if (what & BEV_EVENT_CONNECTED) {
203 LogPrint(BCLog::TOR, "tor: Successfully connected!\n");
204 self->connected(*self);
205 } else if (what & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) {
206 if (what & BEV_EVENT_ERROR) {
208 "tor: Error connecting to Tor control socket\n");
209 } else {
210 LogPrint(BCLog::TOR, "tor: End of stream\n");
211 }
212 self->Disconnect();
213 self->disconnected(*self);
214 }
215}
216
217bool TorControlConnection::Connect(const std::string &tor_control_center,
218 const ConnectionCB &_connected,
219 const ConnectionCB &_disconnected) {
220 if (b_conn) {
221 Disconnect();
222 }
223 // Parse tor_control_center address:port
224 struct sockaddr_storage connect_to_addr;
225 int connect_to_addrlen = sizeof(connect_to_addr);
226 if (evutil_parse_sockaddr_port(tor_control_center.c_str(),
227 (struct sockaddr *)&connect_to_addr,
228 &connect_to_addrlen) < 0) {
229 LogPrintf("tor: Error parsing socket address %s\n", tor_control_center);
230 return false;
231 }
232
233 // Create a new socket, set up callbacks and enable notification bits
234 b_conn = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
235 if (!b_conn) {
236 return false;
237 }
238 bufferevent_setcb(b_conn, TorControlConnection::readcb, nullptr,
240 bufferevent_enable(b_conn, EV_READ | EV_WRITE);
241 this->connected = _connected;
242 this->disconnected = _disconnected;
243
244 // Finally, connect to tor_control_center
245 if (bufferevent_socket_connect(b_conn, (struct sockaddr *)&connect_to_addr,
246 connect_to_addrlen) < 0) {
247 LogPrintf("tor: Error connecting to address %s\n", tor_control_center);
248 return false;
249 }
250 return true;
251}
252
254 if (b_conn) {
255 bufferevent_free(b_conn);
256 }
257 b_conn = nullptr;
258}
259
260bool TorControlConnection::Command(const std::string &cmd,
261 const ReplyHandlerCB &reply_handler) {
262 if (!b_conn) {
263 return false;
264 }
265 struct evbuffer *buf = bufferevent_get_output(b_conn);
266 if (!buf) {
267 return false;
268 }
269 evbuffer_add(buf, cmd.data(), cmd.size());
270 evbuffer_add(buf, "\r\n", 2);
271 reply_handlers.push_back(reply_handler);
272 return true;
273}
274
275/****** General parsing utilities ********/
276
277/* Split reply line in the form 'AUTH METHODS=...' into a type
278 * 'AUTH' and arguments 'METHODS=...'.
279 * Grammar is implicitly defined in https://spec.torproject.org/control-spec by
280 * the server reply formats for PROTOCOLINFO (S3.21) and AUTHCHALLENGE (S3.24).
281 */
282std::pair<std::string, std::string> SplitTorReplyLine(const std::string &s) {
283 size_t ptr = 0;
284 std::string type;
285 while (ptr < s.size() && s[ptr] != ' ') {
286 type.push_back(s[ptr]);
287 ++ptr;
288 }
289 if (ptr < s.size()) {
290 // skip ' '
291 ++ptr;
292 }
293 return make_pair(type, s.substr(ptr));
294}
295
304std::map<std::string, std::string> ParseTorReplyMapping(const std::string &s) {
305 std::map<std::string, std::string> mapping;
306 size_t ptr = 0;
307 while (ptr < s.size()) {
308 std::string key, value;
309 while (ptr < s.size() && s[ptr] != '=' && s[ptr] != ' ') {
310 key.push_back(s[ptr]);
311 ++ptr;
312 }
313 // unexpected end of line
314 if (ptr == s.size()) {
315 return std::map<std::string, std::string>();
316 }
317 // The remaining string is an OptArguments
318 if (s[ptr] == ' ') {
319 break;
320 }
321 // skip '='
322 ++ptr;
323 // Quoted string
324 if (ptr < s.size() && s[ptr] == '"') {
325 // skip opening '"'
326 ++ptr;
327 bool escape_next = false;
328 while (ptr < s.size() && (escape_next || s[ptr] != '"')) {
329 // Repeated backslashes must be interpreted as pairs
330 escape_next = (s[ptr] == '\\' && !escape_next);
331 value.push_back(s[ptr]);
332 ++ptr;
333 }
334 // unexpected end of line
335 if (ptr == s.size()) {
336 return std::map<std::string, std::string>();
337 }
338 // skip closing '"'
339 ++ptr;
352 std::string escaped_value;
353 for (size_t i = 0; i < value.size(); ++i) {
354 if (value[i] == '\\') {
355 // This will always be valid, because if the QuotedString
356 // ended in an odd number of backslashes, then the parser
357 // would already have returned above, due to a missing
358 // terminating double-quote.
359 ++i;
360 if (value[i] == 'n') {
361 escaped_value.push_back('\n');
362 } else if (value[i] == 't') {
363 escaped_value.push_back('\t');
364 } else if (value[i] == 'r') {
365 escaped_value.push_back('\r');
366 } else if ('0' <= value[i] && value[i] <= '7') {
367 size_t j;
368 // Octal escape sequences have a limit of three octal
369 // digits, but terminate at the first character that is
370 // not a valid octal digit if encountered sooner.
371 for (j = 1; j < 3 && (i + j) < value.size() &&
372 '0' <= value[i + j] && value[i + j] <= '7';
373 ++j) {
374 }
375 // Tor restricts first digit to 0-3 for three-digit
376 // octals. A leading digit of 4-7 would therefore be
377 // interpreted as a two-digit octal.
378 if (j == 3 && value[i] > '3') {
379 j--;
380 }
381 escaped_value.push_back(
382 strtol(value.substr(i, j).c_str(), NULL, 8));
383 // Account for automatic incrementing at loop end
384 i += j - 1;
385 } else {
386 escaped_value.push_back(value[i]);
387 }
388 } else {
389 escaped_value.push_back(value[i]);
390 }
391 }
392 value = escaped_value;
393 } else {
394 // Unquoted value. Note that values can contain '=' at will, just no
395 // spaces
396 while (ptr < s.size() && s[ptr] != ' ') {
397 value.push_back(s[ptr]);
398 ++ptr;
399 }
400 }
401 if (ptr < s.size() && s[ptr] == ' ') {
402 // skip ' ' after key=value
403 ++ptr;
404 }
405 mapping[key] = value;
406 }
407 return mapping;
408}
409
410/****** Bitcoin specific TorController implementation ********/
411
417public:
418 TorController(struct event_base *base,
419 const std::string &tor_control_center,
420 const CService &target);
422
425
427 void Reconnect();
428
429private:
430 struct event_base *base;
431 const std::string m_tor_control_center;
433 std::string private_key;
434 std::string service_id;
435 bool reconnect{true};
436 struct event *reconnect_ev;
441 std::vector<uint8_t> cookie;
443 std::vector<uint8_t> clientNonce;
444
451 const TorControlReply &reply);
454 const TorControlReply &reply);
459
461 static void reconnect_cb(evutil_socket_t fd, short what, void *arg);
462};
463
464TorController::TorController(struct event_base *_base,
465 const std::string &tor_control_center,
466 const CService &target)
467 : base(_base), m_tor_control_center(tor_control_center), conn(base),
468 reconnect_timeout(RECONNECT_TIMEOUT_START), m_target(target) {
469 reconnect_ev = event_new(base, -1, 0, reconnect_cb, this);
470 if (!reconnect_ev) {
471 LogPrintf(
472 "tor: Failed to create event for reconnection: out of memory?\n");
473 }
474 // Start connection attempts immediately
476 std::bind(&TorController::connected_cb, this,
477 std::placeholders::_1),
478 std::bind(&TorController::disconnected_cb, this,
479 std::placeholders::_1))) {
480 LogPrintf("tor: Initiating connection to Tor control port %s failed\n",
482 }
483 // Read service private key if cached
484 std::pair<bool, std::string> pkf = ReadBinaryFile(GetPrivateKeyFile());
485 if (pkf.first) {
486 LogPrint(BCLog::TOR, "tor: Reading cached private key from %s\n",
488 private_key = pkf.second;
489 }
490}
491
493 if (reconnect_ev) {
494 event_free(reconnect_ev);
495 reconnect_ev = nullptr;
496 }
497 if (service.IsValid()) {
499 }
500}
501
503 const TorControlReply &reply) {
504 if (reply.code == 250) {
505 LogPrint(BCLog::TOR, "tor: ADD_ONION successful\n");
506 for (const std::string &s : reply.lines) {
507 std::map<std::string, std::string> m = ParseTorReplyMapping(s);
508 std::map<std::string, std::string>::iterator i;
509 if ((i = m.find("ServiceID")) != m.end()) {
510 service_id = i->second;
511 }
512 if ((i = m.find("PrivateKey")) != m.end()) {
513 private_key = i->second;
514 }
515 }
516 if (service_id.empty()) {
517 LogPrintf("tor: Error parsing ADD_ONION parameters:\n");
518 for (const std::string &s : reply.lines) {
519 LogPrintf(" %s\n", SanitizeString(s));
520 }
521 return;
522 }
523 service = LookupNumeric(std::string(service_id + ".onion"),
525 LogInfo("Got tor service ID %s, advertising service %s\n", service_id,
526 service.ToString());
528 LogPrint(BCLog::TOR, "tor: Cached service private key to %s\n",
530 } else {
531 LogPrintf("tor: Error writing service private key to %s\n",
533 }
535 // ... onion requested - keep connection open
536 } else if (reply.code == 510) { // 510 Unrecognized command
537 LogPrintf("tor: Add onion failed with unrecognized command (You "
538 "probably need to upgrade Tor)\n");
539 } else {
540 LogPrintf("tor: Add onion failed; error code %d\n", reply.code);
541 }
542}
543
545 const TorControlReply &reply) {
546 if (reply.code == 250) {
547 LogPrint(BCLog::TOR, "tor: Authentication successful\n");
548
549 // Now that we know Tor is running setup the proxy for onion addresses
550 // if -onion isn't set to something else.
551 if (gArgs.GetArg("-onion", "") == "") {
552 CService resolved(LookupNumeric("127.0.0.1", 9050));
553 proxyType addrOnion = proxyType(resolved, true);
554 SetProxy(NET_ONION, addrOnion);
555 SetReachable(NET_ONION, true);
556 }
557
558 // Finally - now create the service
559 // No private key, generate one
560 if (private_key.empty()) {
561 // Explicitly request key type - see issue #9214
562 private_key = "NEW:ED25519-V3";
563 }
564 // Request onion service, redirect port.
565 // Note that the 'virtual' port doesn't have to be the same as our
566 // internal port, but this is just a convenient choice. TODO; refactor
567 // the shutdown sequence some day.
568 _conn.Command(strprintf("ADD_ONION %s Port=%i,%s", private_key,
571 std::bind(&TorController::add_onion_cb, this,
572 std::placeholders::_1, std::placeholders::_2));
573 } else {
574 LogPrintf("tor: Authentication failed\n");
575 }
576}
577
594static std::vector<uint8_t>
595ComputeResponse(const std::string &key, const std::vector<uint8_t> &cookie,
596 const std::vector<uint8_t> &clientNonce,
597 const std::vector<uint8_t> &serverNonce) {
598 CHMAC_SHA256 computeHash((const uint8_t *)key.data(), key.size());
599 std::vector<uint8_t> computedHash(CHMAC_SHA256::OUTPUT_SIZE, 0);
600 computeHash.Write(cookie.data(), cookie.size());
601 computeHash.Write(clientNonce.data(), clientNonce.size());
602 computeHash.Write(serverNonce.data(), serverNonce.size());
603 computeHash.Finalize(computedHash.data());
604 return computedHash;
605}
606
608 const TorControlReply &reply) {
609 if (reply.code == 250) {
611 "tor: SAFECOOKIE authentication challenge successful\n");
612 std::pair<std::string, std::string> l =
613 SplitTorReplyLine(reply.lines[0]);
614 if (l.first == "AUTHCHALLENGE") {
615 std::map<std::string, std::string> m =
616 ParseTorReplyMapping(l.second);
617 if (m.empty()) {
618 LogPrintf("tor: Error parsing AUTHCHALLENGE parameters: %s\n",
619 SanitizeString(l.second));
620 return;
621 }
622 std::vector<uint8_t> serverHash = ParseHex(m["SERVERHASH"]);
623 std::vector<uint8_t> serverNonce = ParseHex(m["SERVERNONCE"]);
625 "tor: AUTHCHALLENGE ServerHash %s ServerNonce %s\n",
626 HexStr(serverHash), HexStr(serverNonce));
627 if (serverNonce.size() != 32) {
628 LogPrintf(
629 "tor: ServerNonce is not 32 bytes, as required by spec\n");
630 return;
631 }
632
633 std::vector<uint8_t> computedServerHash = ComputeResponse(
634 TOR_SAFE_SERVERKEY, cookie, clientNonce, serverNonce);
635 if (computedServerHash != serverHash) {
636 LogPrintf("tor: ServerHash %s does not match expected "
637 "ServerHash %s\n",
638 HexStr(serverHash), HexStr(computedServerHash));
639 return;
640 }
641
642 std::vector<uint8_t> computedClientHash = ComputeResponse(
643 TOR_SAFE_CLIENTKEY, cookie, clientNonce, serverNonce);
644 _conn.Command("AUTHENTICATE " + HexStr(computedClientHash),
645 std::bind(&TorController::auth_cb, this,
646 std::placeholders::_1,
647 std::placeholders::_2));
648 } else {
649 LogPrintf("tor: Invalid reply to AUTHCHALLENGE\n");
650 }
651 } else {
652 LogPrintf("tor: SAFECOOKIE authentication challenge failed\n");
653 }
654}
655
657 const TorControlReply &reply) {
658 if (reply.code == 250) {
659 std::set<std::string> methods;
660 std::string cookiefile;
661 /*
662 * 250-AUTH METHODS=COOKIE,SAFECOOKIE
663 * COOKIEFILE="/home/x/.tor/control_auth_cookie"
664 * 250-AUTH METHODS=NULL
665 * 250-AUTH METHODS=HASHEDPASSWORD
666 */
667 for (const std::string &s : reply.lines) {
668 std::pair<std::string, std::string> l = SplitTorReplyLine(s);
669 if (l.first == "AUTH") {
670 std::map<std::string, std::string> m =
671 ParseTorReplyMapping(l.second);
672 std::map<std::string, std::string>::iterator i;
673 if ((i = m.find("METHODS")) != m.end()) {
674 std::vector<std::string> m_vec =
675 SplitString(i->second, ',');
676 methods = std::set<std::string>(m_vec.begin(), m_vec.end());
677 }
678 if ((i = m.find("COOKIEFILE")) != m.end()) {
679 cookiefile = i->second;
680 }
681 } else if (l.first == "VERSION") {
682 std::map<std::string, std::string> m =
683 ParseTorReplyMapping(l.second);
684 std::map<std::string, std::string>::iterator i;
685 if ((i = m.find("Tor")) != m.end()) {
686 LogPrint(BCLog::TOR, "tor: Connected to Tor version %s\n",
687 i->second);
688 }
689 }
690 }
691 for (const std::string &s : methods) {
692 LogPrint(BCLog::TOR, "tor: Supported authentication method: %s\n",
693 s);
694 }
695 // Prefer NULL, otherwise SAFECOOKIE. If a password is provided, use
696 // HASHEDPASSWORD
697 /* Authentication:
698 * cookie: hex-encoded ~/.tor/control_auth_cookie
699 * password: "password"
700 */
701 std::string torpassword = gArgs.GetArg("-torpassword", "");
702 if (!torpassword.empty()) {
703 if (methods.count("HASHEDPASSWORD")) {
705 "tor: Using HASHEDPASSWORD authentication\n");
706 ReplaceAll(torpassword, "\"", "\\\"");
707 _conn.Command("AUTHENTICATE \"" + torpassword + "\"",
708 std::bind(&TorController::auth_cb, this,
709 std::placeholders::_1,
710 std::placeholders::_2));
711 } else {
712 LogPrintf("tor: Password provided with -torpassword, but "
713 "HASHEDPASSWORD authentication is not available\n");
714 }
715 } else if (methods.count("NULL")) {
716 LogPrint(BCLog::TOR, "tor: Using NULL authentication\n");
717 _conn.Command("AUTHENTICATE", std::bind(&TorController::auth_cb,
718 this, std::placeholders::_1,
719 std::placeholders::_2));
720 } else if (methods.count("SAFECOOKIE")) {
721 // Cookie: hexdump -e '32/1 "%02x""\n"' ~/.tor/control_auth_cookie
723 "tor: Using SAFECOOKIE authentication, "
724 "reading cookie authentication from %s\n",
725 cookiefile);
726 std::pair<bool, std::string> status_cookie =
728 if (status_cookie.first &&
729 status_cookie.second.size() == TOR_COOKIE_SIZE) {
730 // _conn.Command("AUTHENTICATE " + HexStr(status_cookie.second),
731 // std::bind(&TorController::auth_cb, this,
732 // std::placeholders::_1, std::placeholders::_2));
733 cookie = std::vector<uint8_t>(status_cookie.second.begin(),
734 status_cookie.second.end());
735 clientNonce = std::vector<uint8_t>(TOR_NONCE_SIZE, 0);
737 _conn.Command("AUTHCHALLENGE SAFECOOKIE " + HexStr(clientNonce),
738 std::bind(&TorController::authchallenge_cb, this,
739 std::placeholders::_1,
740 std::placeholders::_2));
741 } else {
742 if (status_cookie.first) {
743 LogPrintf("tor: Authentication cookie %s is not exactly %i "
744 "bytes, as is required by the spec\n",
745 cookiefile, TOR_COOKIE_SIZE);
746 } else {
747 LogPrintf("tor: Authentication cookie %s could not be "
748 "opened (check permissions)\n",
749 cookiefile);
750 }
751 }
752 } else if (methods.count("HASHEDPASSWORD")) {
753 LogPrintf("tor: The only supported authentication mechanism left "
754 "is password, but no password provided with "
755 "-torpassword\n");
756 } else {
757 LogPrintf("tor: No supported authentication method\n");
758 }
759 } else {
760 LogPrintf("tor: Requesting protocol info failed\n");
761 }
762}
763
766 // First send a PROTOCOLINFO command to figure out what authentication is
767 // expected
768 if (!_conn.Command("PROTOCOLINFO 1",
769 std::bind(&TorController::protocolinfo_cb, this,
770 std::placeholders::_1,
771 std::placeholders::_2))) {
772 LogPrintf("tor: Error sending initial protocolinfo command\n");
773 }
774}
775
777 // Stop advertising service when disconnected
778 if (service.IsValid()) {
780 }
781 service = CService();
782 if (!reconnect) {
783 return;
784 }
785
787 "tor: Not connected to Tor control port %s, trying to reconnect\n",
789
790 // Single-shot timer for reconnect. Use exponential backoff.
791 struct timeval time = MillisToTimeval(int64_t(reconnect_timeout * 1000.0));
792 if (reconnect_ev) {
793 event_add(reconnect_ev, &time);
794 }
796}
797
799 /* Try to reconnect and reestablish if we get booted - for example, Tor may
800 * be restarting.
801 */
803 std::bind(&TorController::connected_cb, this,
804 std::placeholders::_1),
805 std::bind(&TorController::disconnected_cb, this,
806 std::placeholders::_1))) {
807 LogPrintf(
808 "tor: Re-initiating connection to Tor control port %s failed\n",
810 }
811}
812
814 return gArgs.GetDataDirNet() / "onion_v3_private_key";
815}
816
817void TorController::reconnect_cb(evutil_socket_t fd, short what, void *arg) {
818 TorController *self = static_cast<TorController *>(arg);
819 self->Reconnect();
820}
821
822/****** Thread ********/
823static struct event_base *gBase;
824static std::thread torControlThread;
825
826static void TorControlThread(CService onion_service_target) {
827 TorController ctrl(gBase, gArgs.GetArg("-torcontrol", DEFAULT_TOR_CONTROL),
828 onion_service_target);
829
830 event_base_dispatch(gBase);
831}
832
833void StartTorControl(CService onion_service_target) {
834 assert(!gBase);
835#ifdef WIN32
836 evthread_use_windows_threads();
837#else
838 evthread_use_pthreads();
839#endif
840 gBase = event_base_new();
841 if (!gBase) {
842 LogPrintf("tor: Unable to create event_base\n");
843 return;
844 }
845
847 std::thread(&util::TraceThread, "torcontrol", [onion_service_target] {
848 TorControlThread(onion_service_target);
849 });
850}
851
853 if (gBase) {
854 LogPrintf("tor: Thread interrupt\n");
855 event_base_once(
856 gBase, -1, EV_TIMEOUT,
857 [](evutil_socket_t, short, void *) { event_base_loopbreak(gBase); },
858 nullptr, nullptr);
859 }
860}
861
863 if (gBase) {
864 torControlThread.join();
865 event_base_free(gBase);
866 gBase = nullptr;
867 }
868}
869
871 struct in_addr onion_service_target;
872 onion_service_target.s_addr = htonl(INADDR_LOOPBACK);
873 return {onion_service_target, BaseParams().OnionServiceTargetPort()};
874}
ArgsManager gArgs
Definition: args.cpp:40
secp256k1_context * ctx
const CChainParams & Params()
Return the currently selected parameters.
Definition: chainparams.cpp:21
const CBaseChainParams & BaseParams()
Return the currently selected parameters.
fs::path GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: args.h:239
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: args.cpp:463
uint16_t OnionServiceTargetPort() const
A hasher class for HMAC-SHA-256.
Definition: hmac_sha256.h:14
static const size_t OUTPUT_SIZE
Definition: hmac_sha256.h:20
CHMAC_SHA256 & Write(const uint8_t *data, size_t len)
Definition: hmac_sha256.h:23
void Finalize(uint8_t hash[OUTPUT_SIZE])
Definition: hmac_sha256.cpp:30
bool IsValid() const
Definition: netaddress.cpp:474
A combination of a network address (CNetAddr) and a (TCP) port.
Definition: netaddress.h:545
std::string ToStringIPPort() const
std::string ToString() const
Low-level handling for Tor control connection.
Definition: torcontrol.cpp:81
TorControlReply message
Message being received.
Definition: torcontrol.cpp:131
std::deque< ReplyHandlerCB > reply_handlers
Response handlers.
Definition: torcontrol.cpp:133
bool Connect(const std::string &tor_control_center, const ConnectionCB &connected, const ConnectionCB &disconnected)
Connect to a Tor control port.
Definition: torcontrol.cpp:217
TorControlConnection(struct event_base *base)
Create a new TorControlConnection.
Definition: torcontrol.cpp:140
bool Command(const std::string &cmd, const ReplyHandlerCB &reply_handler)
Send a command, register a handler for the reply.
Definition: torcontrol.cpp:260
std::function< void(TorControlConnection &)> connected
Callback when ready for use.
Definition: torcontrol.cpp:123
boost::signals2::signal< void(TorControlConnection &, const TorControlReply &)> async_handler
Response handlers for async replies.
Definition: torcontrol.cpp:119
static void readcb(struct bufferevent *bev, void *ctx)
Libevent handlers: internal.
Definition: torcontrol.cpp:149
std::function< void(TorControlConnection &)> disconnected
Callback when connection lost.
Definition: torcontrol.cpp:125
static void eventcb(struct bufferevent *bev, short what, void *ctx)
Definition: torcontrol.cpp:199
std::function< void(TorControlConnection &, const TorControlReply &)> ReplyHandlerCB
Definition: torcontrol.cpp:85
void Disconnect()
Disconnect from Tor control port.
Definition: torcontrol.cpp:253
struct bufferevent * b_conn
Connection to control socket.
Definition: torcontrol.cpp:129
struct event_base * base
Libevent event base.
Definition: torcontrol.cpp:127
std::function< void(TorControlConnection &)> ConnectionCB
Definition: torcontrol.cpp:83
Reply from Tor, can be single or multi-line.
Definition: torcontrol.cpp:64
std::vector< std::string > lines
Definition: torcontrol.cpp:69
Controller that connects to Tor control socket, authenticate, then create and maintain an ephemeral o...
Definition: torcontrol.cpp:416
TorControlConnection conn
Definition: torcontrol.cpp:432
static void reconnect_cb(evutil_socket_t fd, short what, void *arg)
Callback for reconnect timer.
Definition: torcontrol.cpp:817
std::string service_id
Definition: torcontrol.cpp:434
struct event_base * base
Definition: torcontrol.cpp:430
fs::path GetPrivateKeyFile()
Get name fo file to store private key in.
Definition: torcontrol.cpp:813
std::vector< uint8_t > clientNonce
ClientNonce for SAFECOOKIE auth.
Definition: torcontrol.cpp:443
void connected_cb(TorControlConnection &conn)
Callback after successful connection.
Definition: torcontrol.cpp:764
void add_onion_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for ADD_ONION result.
Definition: torcontrol.cpp:502
const std::string m_tor_control_center
Definition: torcontrol.cpp:431
void disconnected_cb(TorControlConnection &conn)
Callback after connection lost or failed connection attempt.
Definition: torcontrol.cpp:776
const CService m_target
Definition: torcontrol.cpp:439
void authchallenge_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for AUTHCHALLENGE result.
Definition: torcontrol.cpp:607
CService service
Definition: torcontrol.cpp:438
std::vector< uint8_t > cookie
Cookie for SAFECOOKIE auth.
Definition: torcontrol.cpp:441
struct event * reconnect_ev
Definition: torcontrol.cpp:436
TorController(struct event_base *base, const std::string &tor_control_center, const CService &target)
Definition: torcontrol.cpp:464
float reconnect_timeout
Definition: torcontrol.cpp:437
void auth_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for AUTHENTICATE result.
Definition: torcontrol.cpp:544
void Reconnect()
Reconnect, after getting disconnected.
Definition: torcontrol.cpp:798
std::string private_key
Definition: torcontrol.cpp:433
void protocolinfo_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for PROTOCOLINFO result.
Definition: torcontrol.cpp:656
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:30
#define LogPrint(category,...)
Definition: logging.h:452
#define LogInfo(...)
Definition: logging.h:413
#define LogPrintf(...)
Definition: logging.h:424
@ TOR
Definition: logging.h:70
static std::string PathToString(const path &path)
Convert path object to byte string.
Definition: fs.h:147
static path PathFromString(const std::string &string)
Convert byte string to path object.
Definition: fs.h:170
void TraceThread(std::string_view thread_name, std::function< void()> thread_func)
A wrapper for do-something-once thread functions.
Definition: thread.cpp:14
void RemoveLocal(const CService &addr)
Definition: net.cpp:313
void SetReachable(enum Network net, bool reachable)
Mark a network as reachable or unreachable (no automatic connects to it)
Definition: net.cpp:319
bool AddLocal(const CService &addr, int nScore)
Definition: net.cpp:280
@ LOCAL_MANUAL
Definition: net.h:165
@ NET_ONION
TOR (v2 or v3)
Definition: netaddress.h:56
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.
Definition: netbase.cpp:259
bool SetProxy(enum Network net, const proxyType &addrProxy)
Definition: netbase.cpp:720
void GetRandBytes(Span< uint8_t > bytes) noexcept
Overall design of the RNG and entropy sources.
Definition: random.cpp:642
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=std::numeric_limits< size_t >::max())
Read full contents of a file and return them in a std::string.
static uint16_t GetDefaultPort()
Definition: bitcoin.h:18
void ReplaceAll(std::string &in_out, const std::string &search, const std::string &substitute)
Definition: string.cpp:10
std::vector< std::string > SplitString(std::string_view str, char sep)
Definition: string.h:22
struct timeval MillisToTimeval(int64_t nTimeout)
Convert milliseconds to a struct timeval for e.g.
Definition: time.cpp:154
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1202
static const std::string TOR_SAFE_CLIENTKEY
For computing clientHash in SAFECOOKIE.
Definition: torcontrol.cpp:47
CService DefaultOnionServiceTarget()
Definition: torcontrol.cpp:870
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.
Definition: torcontrol.cpp:595
static const int MAX_LINE_LENGTH
Maximum length for lines received on TorControlConnection.
Definition: torcontrol.cpp:59
static const float RECONNECT_TIMEOUT_EXP
Exponential backoff configuration - growth factor.
Definition: torcontrol.cpp:52
const std::string DEFAULT_TOR_CONTROL
Default control port.
Definition: torcontrol.cpp:38
static void TorControlThread(CService onion_service_target)
Definition: torcontrol.cpp:826
std::pair< std::string, std::string > SplitTorReplyLine(const std::string &s)
Definition: torcontrol.cpp:282
static const std::string TOR_SAFE_SERVERKEY
For computing serverHash in SAFECOOKIE.
Definition: torcontrol.cpp:44
static const int TOR_COOKIE_SIZE
Tor cookie size (from control-spec.txt)
Definition: torcontrol.cpp:40
static struct event_base * gBase
Definition: torcontrol.cpp:823
static const int TOR_NONCE_SIZE
Size of client/server nonce for SAFECOOKIE.
Definition: torcontrol.cpp:42
void InterruptTorControl()
Definition: torcontrol.cpp:852
std::map< std::string, std::string > ParseTorReplyMapping(const std::string &s)
Parse reply arguments in the form 'METHODS=COOKIE,SAFECOOKIE COOKIEFILE=".../control_auth_cookie"'.
Definition: torcontrol.cpp:304
static std::thread torControlThread
Definition: torcontrol.cpp:824
static const float RECONNECT_TIMEOUT_START
Exponential backoff configuration - initial timeout in seconds.
Definition: torcontrol.cpp:50
void StartTorControl(CService onion_service_target)
Definition: torcontrol.cpp:833
void StopTorControl()
Definition: torcontrol.cpp:862
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
template std::vector< std::byte > ParseHex(std::string_view)
std::string SanitizeString(std::string_view str, int rule)
Remove unsafe chars.
assert(!tx.IsCoinBase())