Bitcoin ABC 0.30.7
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;
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), b_conn(nullptr) {}
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 = atoi(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;
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(true), reconnect_ev(0),
469 reconnect_timeout(RECONNECT_TIMEOUT_START), m_target(target) {
470 reconnect_ev = event_new(base, -1, 0, reconnect_cb, this);
471 if (!reconnect_ev) {
472 LogPrintf(
473 "tor: Failed to create event for reconnection: out of memory?\n");
474 }
475 // Start connection attempts immediately
477 std::bind(&TorController::connected_cb, this,
478 std::placeholders::_1),
479 std::bind(&TorController::disconnected_cb, this,
480 std::placeholders::_1))) {
481 LogPrintf("tor: Initiating connection to Tor control port %s failed\n",
483 }
484 // Read service private key if cached
485 std::pair<bool, std::string> pkf = ReadBinaryFile(GetPrivateKeyFile());
486 if (pkf.first) {
487 LogPrint(BCLog::TOR, "tor: Reading cached private key from %s\n",
489 private_key = pkf.second;
490 }
491}
492
494 if (reconnect_ev) {
495 event_free(reconnect_ev);
496 reconnect_ev = nullptr;
497 }
498 if (service.IsValid()) {
500 }
501}
502
504 const TorControlReply &reply) {
505 if (reply.code == 250) {
506 LogPrint(BCLog::TOR, "tor: ADD_ONION successful\n");
507 for (const std::string &s : reply.lines) {
508 std::map<std::string, std::string> m = ParseTorReplyMapping(s);
509 std::map<std::string, std::string>::iterator i;
510 if ((i = m.find("ServiceID")) != m.end()) {
511 service_id = i->second;
512 }
513 if ((i = m.find("PrivateKey")) != m.end()) {
514 private_key = i->second;
515 }
516 }
517 if (service_id.empty()) {
518 LogPrintf("tor: Error parsing ADD_ONION parameters:\n");
519 for (const std::string &s : reply.lines) {
520 LogPrintf(" %s\n", SanitizeString(s));
521 }
522 return;
523 }
524 service = LookupNumeric(std::string(service_id + ".onion"),
527 "Got service ID %s, advertising service %s\n",
530 LogPrint(BCLog::TOR, "tor: Cached service private key to %s\n",
532 } else {
533 LogPrintf("tor: Error writing service private key to %s\n",
535 }
537 // ... onion requested - keep connection open
538 } else if (reply.code == 510) { // 510 Unrecognized command
539 LogPrintf("tor: Add onion failed with unrecognized command (You "
540 "probably need to upgrade Tor)\n");
541 } else {
542 LogPrintf("tor: Add onion failed; error code %d\n", reply.code);
543 }
544}
545
547 const TorControlReply &reply) {
548 if (reply.code == 250) {
549 LogPrint(BCLog::TOR, "tor: Authentication successful\n");
550
551 // Now that we know Tor is running setup the proxy for onion addresses
552 // if -onion isn't set to something else.
553 if (gArgs.GetArg("-onion", "") == "") {
554 CService resolved(LookupNumeric("127.0.0.1", 9050));
555 proxyType addrOnion = proxyType(resolved, true);
556 SetProxy(NET_ONION, addrOnion);
557 SetReachable(NET_ONION, true);
558 }
559
560 // Finally - now create the service
561 // No private key, generate one
562 if (private_key.empty()) {
563 // Explicitly request key type - see issue #9214
564 private_key = "NEW:ED25519-V3";
565 }
566 // Request onion service, redirect port.
567 // Note that the 'virtual' port doesn't have to be the same as our
568 // internal port, but this is just a convenient choice. TODO; refactor
569 // the shutdown sequence some day.
570 _conn.Command(strprintf("ADD_ONION %s Port=%i,%s", private_key,
573 std::bind(&TorController::add_onion_cb, this,
574 std::placeholders::_1, std::placeholders::_2));
575 } else {
576 LogPrintf("tor: Authentication failed\n");
577 }
578}
579
596static std::vector<uint8_t>
597ComputeResponse(const std::string &key, const std::vector<uint8_t> &cookie,
598 const std::vector<uint8_t> &clientNonce,
599 const std::vector<uint8_t> &serverNonce) {
600 CHMAC_SHA256 computeHash((const uint8_t *)key.data(), key.size());
601 std::vector<uint8_t> computedHash(CHMAC_SHA256::OUTPUT_SIZE, 0);
602 computeHash.Write(cookie.data(), cookie.size());
603 computeHash.Write(clientNonce.data(), clientNonce.size());
604 computeHash.Write(serverNonce.data(), serverNonce.size());
605 computeHash.Finalize(computedHash.data());
606 return computedHash;
607}
608
610 const TorControlReply &reply) {
611 if (reply.code == 250) {
613 "tor: SAFECOOKIE authentication challenge successful\n");
614 std::pair<std::string, std::string> l =
615 SplitTorReplyLine(reply.lines[0]);
616 if (l.first == "AUTHCHALLENGE") {
617 std::map<std::string, std::string> m =
618 ParseTorReplyMapping(l.second);
619 if (m.empty()) {
620 LogPrintf("tor: Error parsing AUTHCHALLENGE parameters: %s\n",
621 SanitizeString(l.second));
622 return;
623 }
624 std::vector<uint8_t> serverHash = ParseHex(m["SERVERHASH"]);
625 std::vector<uint8_t> serverNonce = ParseHex(m["SERVERNONCE"]);
627 "tor: AUTHCHALLENGE ServerHash %s ServerNonce %s\n",
628 HexStr(serverHash), HexStr(serverNonce));
629 if (serverNonce.size() != 32) {
630 LogPrintf(
631 "tor: ServerNonce is not 32 bytes, as required by spec\n");
632 return;
633 }
634
635 std::vector<uint8_t> computedServerHash = ComputeResponse(
636 TOR_SAFE_SERVERKEY, cookie, clientNonce, serverNonce);
637 if (computedServerHash != serverHash) {
638 LogPrintf("tor: ServerHash %s does not match expected "
639 "ServerHash %s\n",
640 HexStr(serverHash), HexStr(computedServerHash));
641 return;
642 }
643
644 std::vector<uint8_t> computedClientHash = ComputeResponse(
645 TOR_SAFE_CLIENTKEY, cookie, clientNonce, serverNonce);
646 _conn.Command("AUTHENTICATE " + HexStr(computedClientHash),
647 std::bind(&TorController::auth_cb, this,
648 std::placeholders::_1,
649 std::placeholders::_2));
650 } else {
651 LogPrintf("tor: Invalid reply to AUTHCHALLENGE\n");
652 }
653 } else {
654 LogPrintf("tor: SAFECOOKIE authentication challenge failed\n");
655 }
656}
657
659 const TorControlReply &reply) {
660 if (reply.code == 250) {
661 std::set<std::string> methods;
662 std::string cookiefile;
663 /*
664 * 250-AUTH METHODS=COOKIE,SAFECOOKIE
665 * COOKIEFILE="/home/x/.tor/control_auth_cookie"
666 * 250-AUTH METHODS=NULL
667 * 250-AUTH METHODS=HASHEDPASSWORD
668 */
669 for (const std::string &s : reply.lines) {
670 std::pair<std::string, std::string> l = SplitTorReplyLine(s);
671 if (l.first == "AUTH") {
672 std::map<std::string, std::string> m =
673 ParseTorReplyMapping(l.second);
674 std::map<std::string, std::string>::iterator i;
675 if ((i = m.find("METHODS")) != m.end()) {
676 std::vector<std::string> m_vec =
677 SplitString(i->second, ',');
678 methods = std::set<std::string>(m_vec.begin(), m_vec.end());
679 }
680 if ((i = m.find("COOKIEFILE")) != m.end()) {
681 cookiefile = i->second;
682 }
683 } else if (l.first == "VERSION") {
684 std::map<std::string, std::string> m =
685 ParseTorReplyMapping(l.second);
686 std::map<std::string, std::string>::iterator i;
687 if ((i = m.find("Tor")) != m.end()) {
688 LogPrint(BCLog::TOR, "tor: Connected to Tor version %s\n",
689 i->second);
690 }
691 }
692 }
693 for (const std::string &s : methods) {
694 LogPrint(BCLog::TOR, "tor: Supported authentication method: %s\n",
695 s);
696 }
697 // Prefer NULL, otherwise SAFECOOKIE. If a password is provided, use
698 // HASHEDPASSWORD
699 /* Authentication:
700 * cookie: hex-encoded ~/.tor/control_auth_cookie
701 * password: "password"
702 */
703 std::string torpassword = gArgs.GetArg("-torpassword", "");
704 if (!torpassword.empty()) {
705 if (methods.count("HASHEDPASSWORD")) {
707 "tor: Using HASHEDPASSWORD authentication\n");
708 ReplaceAll(torpassword, "\"", "\\\"");
709 _conn.Command("AUTHENTICATE \"" + torpassword + "\"",
710 std::bind(&TorController::auth_cb, this,
711 std::placeholders::_1,
712 std::placeholders::_2));
713 } else {
714 LogPrintf("tor: Password provided with -torpassword, but "
715 "HASHEDPASSWORD authentication is not available\n");
716 }
717 } else if (methods.count("NULL")) {
718 LogPrint(BCLog::TOR, "tor: Using NULL authentication\n");
719 _conn.Command("AUTHENTICATE", std::bind(&TorController::auth_cb,
720 this, std::placeholders::_1,
721 std::placeholders::_2));
722 } else if (methods.count("SAFECOOKIE")) {
723 // Cookie: hexdump -e '32/1 "%02x""\n"' ~/.tor/control_auth_cookie
725 "tor: Using SAFECOOKIE authentication, "
726 "reading cookie authentication from %s\n",
727 cookiefile);
728 std::pair<bool, std::string> status_cookie =
730 if (status_cookie.first &&
731 status_cookie.second.size() == TOR_COOKIE_SIZE) {
732 // _conn.Command("AUTHENTICATE " + HexStr(status_cookie.second),
733 // std::bind(&TorController::auth_cb, this,
734 // std::placeholders::_1, std::placeholders::_2));
735 cookie = std::vector<uint8_t>(status_cookie.second.begin(),
736 status_cookie.second.end());
737 clientNonce = std::vector<uint8_t>(TOR_NONCE_SIZE, 0);
739 _conn.Command("AUTHCHALLENGE SAFECOOKIE " + HexStr(clientNonce),
740 std::bind(&TorController::authchallenge_cb, this,
741 std::placeholders::_1,
742 std::placeholders::_2));
743 } else {
744 if (status_cookie.first) {
745 LogPrintf("tor: Authentication cookie %s is not exactly %i "
746 "bytes, as is required by the spec\n",
747 cookiefile, TOR_COOKIE_SIZE);
748 } else {
749 LogPrintf("tor: Authentication cookie %s could not be "
750 "opened (check permissions)\n",
751 cookiefile);
752 }
753 }
754 } else if (methods.count("HASHEDPASSWORD")) {
755 LogPrintf("tor: The only supported authentication mechanism left "
756 "is password, but no password provided with "
757 "-torpassword\n");
758 } else {
759 LogPrintf("tor: No supported authentication method\n");
760 }
761 } else {
762 LogPrintf("tor: Requesting protocol info failed\n");
763 }
764}
765
768 // First send a PROTOCOLINFO command to figure out what authentication is
769 // expected
770 if (!_conn.Command("PROTOCOLINFO 1",
771 std::bind(&TorController::protocolinfo_cb, this,
772 std::placeholders::_1,
773 std::placeholders::_2))) {
774 LogPrintf("tor: Error sending initial protocolinfo command\n");
775 }
776}
777
779 // Stop advertising service when disconnected
780 if (service.IsValid()) {
782 }
783 service = CService();
784 if (!reconnect) {
785 return;
786 }
787
789 "tor: Not connected to Tor control port %s, trying to reconnect\n",
791
792 // Single-shot timer for reconnect. Use exponential backoff.
793 struct timeval time = MillisToTimeval(int64_t(reconnect_timeout * 1000.0));
794 if (reconnect_ev) {
795 event_add(reconnect_ev, &time);
796 }
798}
799
801 /* Try to reconnect and reestablish if we get booted - for example, Tor may
802 * be restarting.
803 */
805 std::bind(&TorController::connected_cb, this,
806 std::placeholders::_1),
807 std::bind(&TorController::disconnected_cb, this,
808 std::placeholders::_1))) {
809 LogPrintf(
810 "tor: Re-initiating connection to Tor control port %s failed\n",
812 }
813}
814
816 return gArgs.GetDataDirNet() / "onion_v3_private_key";
817}
818
819void TorController::reconnect_cb(evutil_socket_t fd, short what, void *arg) {
820 TorController *self = static_cast<TorController *>(arg);
821 self->Reconnect();
822}
823
824/****** Thread ********/
825static struct event_base *gBase;
826static std::thread torControlThread;
827
828static void TorControlThread(CService onion_service_target) {
829 TorController ctrl(gBase, gArgs.GetArg("-torcontrol", DEFAULT_TOR_CONTROL),
830 onion_service_target);
831
832 event_base_dispatch(gBase);
833}
834
835void StartTorControl(CService onion_service_target) {
836 assert(!gBase);
837#ifdef WIN32
838 evthread_use_windows_threads();
839#else
840 evthread_use_pthreads();
841#endif
842 gBase = event_base_new();
843 if (!gBase) {
844 LogPrintf("tor: Unable to create event_base\n");
845 return;
846 }
847
849 std::thread(&util::TraceThread, "torcontrol", [onion_service_target] {
850 TorControlThread(onion_service_target);
851 });
852}
853
855 if (gBase) {
856 LogPrintf("tor: Thread interrupt\n");
857 event_base_once(
858 gBase, -1, EV_TIMEOUT,
859 [](evutil_socket_t, short, void *) { event_base_loopbreak(gBase); },
860 nullptr, nullptr);
861 }
862}
863
865 if (gBase) {
866 torControlThread.join();
867 event_base_free(gBase);
868 gBase = nullptr;
869 }
870}
871
873 struct in_addr onion_service_target;
874 onion_service_target.s_addr = htonl(INADDR_LOOPBACK);
875 return {onion_service_target, BaseParams().OnionServiceTargetPort()};
876}
ArgsManager gArgs
Definition: args.cpp:38
secp256k1_context * ctx
const CChainParams & Params()
Return the currently selected parameters.
Definition: chainparams.cpp:19
const CBaseChainParams & BaseParams()
Return the currently selected parameters.
fs::path GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: args.h:215
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: args.cpp:494
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:477
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:819
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:815
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:766
void add_onion_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for ADD_ONION result.
Definition: torcontrol.cpp:503
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:778
const CService m_target
Definition: torcontrol.cpp:439
void authchallenge_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for AUTHCHALLENGE result.
Definition: torcontrol.cpp:609
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:546
void Reconnect()
Reconnect, after getting disconnected.
Definition: torcontrol.cpp:800
std::string private_key
Definition: torcontrol.cpp:433
void protocolinfo_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for PROTOCOLINFO result.
Definition: torcontrol.cpp:658
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:238
#define LogPrintfCategory(category,...)
Definition: logging.h:231
#define LogPrintf(...)
Definition: logging.h:227
@ TOR
Definition: logging.h:41
static std::string PathToString(const path &path)
Convert path object to byte string.
Definition: fs.h:142
static path PathFromString(const std::string &string)
Convert byte string to path object.
Definition: fs.h:165
void TraceThread(const char *thread_name, std::function< void()> thread_func)
A wrapper for do-something-once thread functions.
Definition: thread.cpp:13
void RemoveLocal(const CService &addr)
Definition: net.cpp:311
void SetReachable(enum Network net, bool reachable)
Mark a network as reachable or unreachable (no automatic connects to it)
Definition: net.cpp:317
bool AddLocal(const CService &addr, int nScore)
Definition: net.cpp:278
@ LOCAL_MANUAL
Definition: net.h:239
@ 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:261
bool SetProxy(enum Network net, const proxyType &addrProxy)
Definition: netbase.cpp:705
void GetRandBytes(Span< uint8_t > bytes) noexcept
Overall design of the RNG and entropy sources.
Definition: random.cpp:639
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:158
#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:872
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:597
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:828
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:825
static const int TOR_NONCE_SIZE
Size of client/server nonce for SAFECOOKIE.
Definition: torcontrol.cpp:42
void InterruptTorControl()
Definition: torcontrol.cpp:854
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:826
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:835
void StopTorControl()
Definition: torcontrol.cpp:864
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)
int atoi(const std::string &str)
std::string SanitizeString(std::string_view str, int rule)
Remove unsafe chars.
assert(!tx.IsCoinBase())