Bitcoin ABC 0.26.3
P2P Digital Currency
Loading...
Searching...
No Matches
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:
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
142
148
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.
194 LogPrintf("tor: Disconnecting because MAX_LINE_LENGTH exceeded\n");
195 self->Disconnect();
196 }
197}
198
200 void *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
220 if (b_conn) {
221 Disconnect();
222 }
223 // Parse tor_control_center address:port
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
235 if (!b_conn) {
236 return false;
237 }
241 this->connected = _connected;
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) {
256 }
257 b_conn = nullptr;
258}
259
260bool TorControlConnection::Command(const std::string &cmd,
262 if (!b_conn) {
263 return false;
264 }
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:
419 const std::string &tor_control_center,
420 const CService &target);
422
425
427 void Reconnect();
428
429private:
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
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) {
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"),
526 LogPrintf("tor: Got service ID %s, advertising service %s\n",
529 LogPrint(BCLog::TOR, "tor: Cached service private key to %s\n",
531 } else {
532 LogPrintf("tor: Error writing service private key to %s\n",
534 }
536 // ... onion requested - keep connection open
537 } else if (reply.code == 510) { // 510 Unrecognized command
538 LogPrintf("tor: Add onion failed with unrecognized command (You "
539 "probably need to upgrade Tor)\n");
540 } else {
541 LogPrintf("tor: Add onion failed; error code %d\n", reply.code);
542 }
543}
544
546 const TorControlReply &reply) {
547 if (reply.code == 250) {
548 LogPrint(BCLog::TOR, "tor: Authentication successful\n");
549
550 // Now that we know Tor is running setup the proxy for onion addresses
551 // if -onion isn't set to something else.
552 if (gArgs.GetArg("-onion", "") == "") {
553 CService resolved(LookupNumeric("127.0.0.1", 9050));
556 SetReachable(NET_ONION, true);
557 }
558
559 // Finally - now create the service
560 // No private key, generate one
561 if (private_key.empty()) {
562 // Explicitly request key type - see issue #9214
563 private_key = "NEW:ED25519-V3";
564 }
565 // Request onion service, redirect port.
566 // Note that the 'virtual' port doesn't have to be the same as our
567 // internal port, but this is just a convenient choice. TODO; refactor
568 // the shutdown sequence some day.
569 _conn.Command(strprintf("ADD_ONION %s Port=%i,%s", private_key,
572 std::bind(&TorController::add_onion_cb, this,
573 std::placeholders::_1, std::placeholders::_2));
574 } else {
575 LogPrintf("tor: Authentication failed\n");
576 }
577}
578
595static std::vector<uint8_t>
596ComputeResponse(const std::string &key, const std::vector<uint8_t> &cookie,
597 const std::vector<uint8_t> &clientNonce,
598 const std::vector<uint8_t> &serverNonce) {
599 CHMAC_SHA256 computeHash((const uint8_t *)key.data(), key.size());
600 std::vector<uint8_t> computedHash(CHMAC_SHA256::OUTPUT_SIZE, 0);
601 computeHash.Write(cookie.data(), cookie.size());
602 computeHash.Write(clientNonce.data(), clientNonce.size());
603 computeHash.Write(serverNonce.data(), serverNonce.size());
604 computeHash.Finalize(computedHash.data());
605 return computedHash;
606}
607
609 const TorControlReply &reply) {
610 if (reply.code == 250) {
612 "tor: SAFECOOKIE authentication challenge successful\n");
613 std::pair<std::string, std::string> l =
614 SplitTorReplyLine(reply.lines[0]);
615 if (l.first == "AUTHCHALLENGE") {
616 std::map<std::string, std::string> m =
617 ParseTorReplyMapping(l.second);
618 if (m.empty()) {
619 LogPrintf("tor: Error parsing AUTHCHALLENGE parameters: %s\n",
620 SanitizeString(l.second));
621 return;
622 }
623 std::vector<uint8_t> serverHash = ParseHex(m["SERVERHASH"]);
624 std::vector<uint8_t> serverNonce = ParseHex(m["SERVERNONCE"]);
626 "tor: AUTHCHALLENGE ServerHash %s ServerNonce %s\n",
628 if (serverNonce.size() != 32) {
629 LogPrintf(
630 "tor: ServerNonce is not 32 bytes, as required by spec\n");
631 return;
632 }
633
634 std::vector<uint8_t> computedServerHash = ComputeResponse(
637 LogPrintf("tor: ServerHash %s does not match expected "
638 "ServerHash %s\n",
640 return;
641 }
642
643 std::vector<uint8_t> computedClientHash = ComputeResponse(
645 _conn.Command("AUTHENTICATE " + HexStr(computedClientHash),
646 std::bind(&TorController::auth_cb, this,
647 std::placeholders::_1,
648 std::placeholders::_2));
649 } else {
650 LogPrintf("tor: Invalid reply to AUTHCHALLENGE\n");
651 }
652 } else {
653 LogPrintf("tor: SAFECOOKIE authentication challenge failed\n");
654 }
655}
656
658 const TorControlReply &reply) {
659 if (reply.code == 250) {
660 std::set<std::string> methods;
661 std::string cookiefile;
662 /*
663 * 250-AUTH METHODS=COOKIE,SAFECOOKIE
664 * COOKIEFILE="/home/x/.tor/control_auth_cookie"
665 * 250-AUTH METHODS=NULL
666 * 250-AUTH METHODS=HASHEDPASSWORD
667 */
668 for (const std::string &s : reply.lines) {
669 std::pair<std::string, std::string> l = SplitTorReplyLine(s);
670 if (l.first == "AUTH") {
671 std::map<std::string, std::string> m =
672 ParseTorReplyMapping(l.second);
673 std::map<std::string, std::string>::iterator i;
674 if ((i = m.find("METHODS")) != m.end()) {
675 std::vector<std::string> m_vec =
676 SplitString(i->second, ',');
677 methods = std::set<std::string>(m_vec.begin(), m_vec.end());
678 }
679 if ((i = m.find("COOKIEFILE")) != m.end()) {
680 cookiefile = i->second;
681 }
682 } else if (l.first == "VERSION") {
683 std::map<std::string, std::string> m =
684 ParseTorReplyMapping(l.second);
685 std::map<std::string, std::string>::iterator i;
686 if ((i = m.find("Tor")) != m.end()) {
687 LogPrint(BCLog::TOR, "tor: Connected to Tor version %s\n",
688 i->second);
689 }
690 }
691 }
692 for (const std::string &s : methods) {
693 LogPrint(BCLog::TOR, "tor: Supported authentication method: %s\n",
694 s);
695 }
696 // Prefer NULL, otherwise SAFECOOKIE. If a password is provided, use
697 // HASHEDPASSWORD
698 /* Authentication:
699 * cookie: hex-encoded ~/.tor/control_auth_cookie
700 * password: "password"
701 */
702 std::string torpassword = gArgs.GetArg("-torpassword", "");
703 if (!torpassword.empty()) {
704 if (methods.count("HASHEDPASSWORD")) {
706 "tor: Using HASHEDPASSWORD authentication\n");
707 ReplaceAll(torpassword, "\"", "\\\"");
708 _conn.Command("AUTHENTICATE \"" + torpassword + "\"",
709 std::bind(&TorController::auth_cb, this,
710 std::placeholders::_1,
711 std::placeholders::_2));
712 } else {
713 LogPrintf("tor: Password provided with -torpassword, but "
714 "HASHEDPASSWORD authentication is not available\n");
715 }
716 } else if (methods.count("NULL")) {
717 LogPrint(BCLog::TOR, "tor: Using NULL authentication\n");
718 _conn.Command("AUTHENTICATE", std::bind(&TorController::auth_cb,
719 this, std::placeholders::_1,
720 std::placeholders::_2));
721 } else if (methods.count("SAFECOOKIE")) {
722 // Cookie: hexdump -e '32/1 "%02x""\n"' ~/.tor/control_auth_cookie
724 "tor: Using SAFECOOKIE authentication, "
725 "reading cookie authentication from %s\n",
726 cookiefile);
727 std::pair<bool, std::string> status_cookie =
729 if (status_cookie.first &&
730 status_cookie.second.size() == TOR_COOKIE_SIZE) {
731 // _conn.Command("AUTHENTICATE " + HexStr(status_cookie.second),
732 // std::bind(&TorController::auth_cb, this,
733 // std::placeholders::_1, std::placeholders::_2));
734 cookie = std::vector<uint8_t>(status_cookie.second.begin(),
735 status_cookie.second.end());
736 clientNonce = std::vector<uint8_t>(TOR_NONCE_SIZE, 0);
738 _conn.Command("AUTHCHALLENGE SAFECOOKIE " + HexStr(clientNonce),
739 std::bind(&TorController::authchallenge_cb, this,
740 std::placeholders::_1,
741 std::placeholders::_2));
742 } else {
743 if (status_cookie.first) {
744 LogPrintf("tor: Authentication cookie %s is not exactly %i "
745 "bytes, as is required by the spec\n",
747 } else {
748 LogPrintf("tor: Authentication cookie %s could not be "
749 "opened (check permissions)\n",
750 cookiefile);
751 }
752 }
753 } else if (methods.count("HASHEDPASSWORD")) {
754 LogPrintf("tor: The only supported authentication mechanism left "
755 "is password, but no password provided with "
756 "-torpassword\n");
757 } else {
758 LogPrintf("tor: No supported authentication method\n");
759 }
760 } else {
761 LogPrintf("tor: Requesting protocol info failed\n");
762 }
763}
764
767 // First send a PROTOCOLINFO command to figure out what authentication is
768 // expected
769 if (!_conn.Command("PROTOCOLINFO 1",
770 std::bind(&TorController::protocolinfo_cb, this,
771 std::placeholders::_1,
772 std::placeholders::_2))) {
773 LogPrintf("tor: Error sending initial protocolinfo command\n");
774 }
775}
776
778 // Stop advertising service when disconnected
779 if (service.IsValid()) {
781 }
782 service = CService();
783 if (!reconnect) {
784 return;
785 }
786
788 "tor: Not connected to Tor control port %s, trying to reconnect\n",
790
791 // Single-shot timer for reconnect. Use exponential backoff.
792 struct timeval time = MillisToTimeval(int64_t(reconnect_timeout * 1000.0));
793 if (reconnect_ev) {
794 event_add(reconnect_ev, &time);
795 }
797}
798
800 /* Try to reconnect and reestablish if we get booted - for example, Tor may
801 * be restarting.
802 */
804 std::bind(&TorController::connected_cb, this,
805 std::placeholders::_1),
806 std::bind(&TorController::disconnected_cb, this,
807 std::placeholders::_1))) {
808 LogPrintf(
809 "tor: Re-initiating connection to Tor control port %s failed\n",
811 }
812}
813
815 return gArgs.GetDataDirNet() / "onion_v3_private_key";
816}
817
819 TorController *self = static_cast<TorController *>(arg);
820 self->Reconnect();
821}
822
823/****** Thread ********/
824static struct event_base *gBase;
825static std::thread torControlThread;
826
833
835 assert(!gBase);
836#ifdef WIN32
838#else
840#endif
842 if (!gBase) {
843 LogPrintf("tor: Unable to create event_base\n");
844 return;
845 }
846
848 std::thread(&util::TraceThread, "torcontrol", [onion_service_target] {
850 });
851}
852
854 if (gBase) {
855 LogPrintf("tor: Thread interrupt\n");
857 gBase, -1, EV_TIMEOUT,
858 [](evutil_socket_t, short, void *) { event_base_loopbreak(gBase); },
859 nullptr, nullptr);
860 }
861}
862
864 if (gBase) {
865 torControlThread.join();
867 gBase = nullptr;
868 }
869}
870
ArgsManager gArgs
Definition args.cpp:38
secp256k1_context * ctx
const CChainParams & Params()
Return the currently selected parameters.
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
bool IsValid() const
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.
TorControlReply message
Message being received.
std::deque< ReplyHandlerCB > reply_handlers
Response handlers.
bool Connect(const std::string &tor_control_center, const ConnectionCB &connected, const ConnectionCB &disconnected)
Connect to a Tor control port.
TorControlConnection(struct event_base *base)
Create a new TorControlConnection.
bool Command(const std::string &cmd, const ReplyHandlerCB &reply_handler)
Send a command, register a handler for the reply.
std::function< void(TorControlConnection &)> connected
Callback when ready for use.
boost::signals2::signal< void(TorControlConnection &, const TorControlReply &)> async_handler
Response handlers for async replies.
static void readcb(struct bufferevent *bev, void *ctx)
Libevent handlers: internal.
std::function< void(TorControlConnection &)> disconnected
Callback when connection lost.
static void eventcb(struct bufferevent *bev, short what, void *ctx)
std::function< void(TorControlConnection &, const TorControlReply &)> ReplyHandlerCB
void Disconnect()
Disconnect from Tor control port.
struct bufferevent * b_conn
Connection to control socket.
struct event_base * base
Libevent event base.
std::function< void(TorControlConnection &)> ConnectionCB
Reply from Tor, can be single or multi-line.
std::vector< std::string > lines
Controller that connects to Tor control socket, authenticate, then create and maintain an ephemeral o...
TorControlConnection conn
static void reconnect_cb(evutil_socket_t fd, short what, void *arg)
Callback for reconnect timer.
std::string service_id
struct event_base * base
fs::path GetPrivateKeyFile()
Get name fo file to store private key in.
std::vector< uint8_t > clientNonce
ClientNonce for SAFECOOKIE auth.
void connected_cb(TorControlConnection &conn)
Callback after successful connection.
void add_onion_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for ADD_ONION result.
const std::string m_tor_control_center
void disconnected_cb(TorControlConnection &conn)
Callback after connection lost or failed connection attempt.
const CService m_target
void authchallenge_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for AUTHCHALLENGE result.
CService service
std::vector< uint8_t > cookie
Cookie for SAFECOOKIE auth.
struct event * reconnect_ev
TorController(struct event_base *base, const std::string &tor_control_center, const CService &target)
float reconnect_timeout
void auth_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for AUTHENTICATE result.
void Reconnect()
Reconnect, after getting disconnected.
std::string private_key
void protocolinfo_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for PROTOCOLINFO result.
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition fs.h:30
#define LogPrint(category,...)
Definition logging.h:211
#define LogPrintf(...)
Definition logging.h:207
@ 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
T GetRand(T nMax=std::numeric_limits< T >::max()) noexcept
Generate a uniform random integer of type T in the range [0..nMax) nMax defaults to std::numeric_limi...
Definition random.h:85
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...
static const std::string TOR_SAFE_CLIENTKEY
For computing clientHash in SAFECOOKIE.
CService DefaultOnionServiceTarget()
static std::vector< uint8_t > ComputeResponse(const std::string &key, const std::vector< uint8_t > &cookie, const std::vector< uint8_t > &clientNonce, const std::vector< uint8_t > &serverNonce)
Compute Tor SAFECOOKIE response.
static const int MAX_LINE_LENGTH
Maximum length for lines received on TorControlConnection.
static const float RECONNECT_TIMEOUT_EXP
Exponential backoff configuration - growth factor.
const std::string DEFAULT_TOR_CONTROL
Default control port.
static void TorControlThread(CService onion_service_target)
std::pair< std::string, std::string > SplitTorReplyLine(const std::string &s)
static const std::string TOR_SAFE_SERVERKEY
For computing serverHash in SAFECOOKIE.
static const int TOR_COOKIE_SIZE
Tor cookie size (from control-spec.txt)
static struct event_base * gBase
static const int TOR_NONCE_SIZE
Size of client/server nonce for SAFECOOKIE.
void InterruptTorControl()
std::map< std::string, std::string > ParseTorReplyMapping(const std::string &s)
Parse reply arguments in the form 'METHODS=COOKIE,SAFECOOKIE COOKIEFILE=".../control_auth_cookie"'.
static std::thread torControlThread
static const float RECONNECT_TIMEOUT_START
Exponential backoff configuration - initial timeout in seconds.
void StartTorControl(CService onion_service_target)
void StopTorControl()
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())