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