18 #include <boost/bind.hpp>    19 #include <boost/signals2/signal.hpp>    20 #include <boost/algorithm/string/split.hpp>    21 #include <boost/algorithm/string/classification.hpp>    22 #include <boost/algorithm/string/replace.hpp>    24 #include <event2/bufferevent.h>    25 #include <event2/buffer.h>    26 #include <event2/util.h>    27 #include <event2/event.h>    28 #include <event2/thread.h>    33 static const int TOR_COOKIE_SIZE = 32;
    35 static const int TOR_NONCE_SIZE = 32;
    37 static const std::string TOR_SAFE_SERVERKEY = 
"Tor safe cookie authentication server-to-controller hash";
    39 static const std::string TOR_SAFE_CLIENTKEY = 
"Tor safe cookie authentication controller-to-server hash";
    41 static const float RECONNECT_TIMEOUT_START = 1.0;
    43 static const float RECONNECT_TIMEOUT_EXP = 1.5;
    48 static const int MAX_LINE_LENGTH = 100000;
    75     typedef std::function<void(TorControlConnection &,const TorControlReply &)> 
ReplyHandlerCB;
   103     boost::signals2::signal<void(TorControlConnection &,const TorControlReply &)> 
async_handler;
   119     static void readcb(
struct bufferevent *bev, 
void *ctx);
   120     static void eventcb(
struct bufferevent *bev, 
short what, 
void *ctx);
   124     base(_base), b_conn(nullptr)
   137     struct evbuffer *input = bufferevent_get_input(bev);
   138     size_t n_read_out = 0;
   142     while((line = evbuffer_readln(input, &n_read_out, EVBUFFER_EOL_CRLF)) != 
nullptr)
   144         std::string s(line, n_read_out);
   149         self->message.code = 
atoi(s.substr(0,3));
   150         self->message.lines.push_back(s.substr(4));
   154             if (self->message.code >= 600) {
   157                 self->async_handler(*
self, self->message);
   159                 if (!self->reply_handlers.empty()) {
   161                     self->reply_handlers.front()(*
self, 
self->message);
   162                     self->reply_handlers.pop_front();
   164                     LogPrint(
BCLog::TOR, 
"tor: Received unexpected sync reply %i\n", self->message.code);
   167             self->message.Clear();
   173     if (evbuffer_get_length(input) > MAX_LINE_LENGTH) {
   174         LogPrintf(
"tor: Disconnecting because MAX_LINE_LENGTH exceeded\n");
   182     if (what & BEV_EVENT_CONNECTED) {
   183         LogPrint(
BCLog::TOR, 
"tor: Successfully connected!\n");
   184         self->connected(*
self);
   185     } 
else if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
   186         if (what & BEV_EVENT_ERROR) {
   187             LogPrint(
BCLog::TOR, 
"tor: Error connecting to Tor control socket\n");
   192         self->disconnected(*
self);
   201     struct sockaddr_storage connect_to_addr;
   202     int connect_to_addrlen = 
sizeof(connect_to_addr);
   203     if (evutil_parse_sockaddr_port(target.c_str(),
   204         (
struct sockaddr*)&connect_to_addr, &connect_to_addrlen)<0) {
   205         LogPrintf(
"tor: Error parsing socket address %s\n", target);
   210     b_conn = bufferevent_socket_new(
base, -1, BEV_OPT_CLOSE_ON_FREE);
   214     bufferevent_enable(
b_conn, EV_READ|EV_WRITE);
   219     if (bufferevent_socket_connect(
b_conn, (
struct sockaddr*)&connect_to_addr, connect_to_addrlen) < 0) {
   220         LogPrintf(
"tor: Error connecting to address %s\n", target);
   237     struct evbuffer *buf = bufferevent_get_output(
b_conn);
   240     evbuffer_add(buf, cmd.data(), cmd.size());
   241     evbuffer_add(buf, 
"\r\n", 2);
   257     while (ptr < s.size() && s[ptr] != 
' ') {
   258         type.push_back(s[ptr]);
   263     return make_pair(type, s.substr(ptr));
   274     std::map<std::string,std::string> mapping;
   276     while (ptr < s.size()) {
   277         std::string key, value;
   278         while (ptr < s.size() && s[ptr] != 
'=' && s[ptr] != 
' ') {
   279             key.push_back(s[ptr]);
   283             return std::map<std::string,std::string>();
   287         if (ptr < s.size() && s[ptr] == 
'"') { 
   289             bool escape_next = 
false;
   290             while (ptr < s.size() && (escape_next || s[ptr] != 
'"')) {
   292                 escape_next = (s[ptr] == 
'\\' && !escape_next);
   293                 value.push_back(s[ptr]);
   297                 return std::map<std::string,std::string>();
   309             std::string escaped_value;
   310             for (
size_t i = 0; i < value.size(); ++i) {
   311                 if (value[i] == 
'\\') {
   317                     if (value[i] == 
'n') {
   318                         escaped_value.push_back(
'\n');
   319                     } 
else if (value[i] == 
't') {
   320                         escaped_value.push_back(
'\t');
   321                     } 
else if (value[i] == 
'r') {
   322                         escaped_value.push_back(
'\r');
   323                     } 
else if (
'0' <= value[i] && value[i] <= 
'7') {
   328                         for (j = 1; j < 3 && (i+j) < value.size() && 
'0' <= value[i+j] && value[i+j] <= 
'7'; ++j) {}
   332                         if (j == 3 && value[i] > 
'3') {
   335                         escaped_value.push_back(strtol(value.substr(i, j).c_str(), 
nullptr, 8));
   339                         escaped_value.push_back(value[i]);
   342                     escaped_value.push_back(value[i]);
   345             value = escaped_value;
   347             while (ptr < s.size() && s[ptr] != 
' ') {
   348                 value.push_back(s[ptr]);
   352         if (ptr < s.size() && s[ptr] == 
' ')
   354         mapping[key] = value;
   366 static std::pair<bool,std::string> ReadBinaryFile(
const fs::path &filename, 
size_t maxsize=std::numeric_limits<size_t>::max())
   370         return std::make_pair(
false,
"");
   374     while ((n=fread(buffer, 1, 
sizeof(buffer), f)) > 0) {
   379             return std::make_pair(
false,
"");
   381         retval.append(buffer, buffer+n);
   382         if (retval.size() > maxsize)
   386     return std::make_pair(
true,retval);
   392 static bool WriteBinaryFile(
const fs::path &filename, 
const std::string &data)
   397     if (fwrite(data.data(), 1, data.size(), f) != data.size()) {
   450     static void reconnect_cb(evutil_socket_t fd, 
short what, 
void *arg);
   455     target(_target), conn(base), reconnect(true), reconnect_ev(0),
   456     reconnect_timeout(RECONNECT_TIMEOUT_START)
   460         LogPrintf(
"tor: Failed to create event for reconnection: out of memory?\n");
   464         LogPrintf(
"tor: Initiating connection to Tor control port %s failed\n", _target);
   487     if (reply.
code == 250) {
   488         LogPrint(
BCLog::TOR, 
"tor: ADD_ONION successful\n");
   489         for (
const std::string &s : reply.
lines) {
   491             std::map<std::string,std::string>::iterator i;
   492             if ((i = m.find(
"ServiceID")) != m.end())
   494             if ((i = m.find(
"PrivateKey")) != m.end())
   498             LogPrintf(
"tor: Error parsing ADD_ONION parameters:\n");
   499             for (
const std::string &s : reply.
lines) {
   509             LogPrintf(
"tor: Error writing service private key to %s\n", 
GetPrivateKeyFile().
string());
   513     } 
else if (reply.
code == 510) { 
   514         LogPrintf(
"tor: Add onion failed with unrecognized command (You probably need to upgrade Tor)\n");
   516         LogPrintf(
"tor: Add onion failed; error code %d\n", reply.
code);
   522     if (reply.
code == 250) {
   523         LogPrint(
BCLog::TOR, 
"tor: Authentication successful\n");
   543         LogPrintf(
"tor: Authentication failed\n");
   563 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)
   565     CHMAC_SHA256 computeHash((
const uint8_t*)key.data(), key.size());
   567     computeHash.Write(cookie.data(), cookie.size());
   568     computeHash.Write(clientNonce.data(), clientNonce.size());
   569     computeHash.Write(serverNonce.data(), serverNonce.size());
   570     computeHash.Finalize(computedHash.data());
   576     if (reply.
code == 250) {
   577         LogPrint(
BCLog::TOR, 
"tor: SAFECOOKIE authentication challenge successful\n");
   579         if (l.first == 
"AUTHCHALLENGE") {
   582                 LogPrintf(
"tor: Error parsing AUTHCHALLENGE parameters: %s\n", 
SanitizeString(l.second));
   585             std::vector<uint8_t> serverHash = 
ParseHex(m[
"SERVERHASH"]);
   586             std::vector<uint8_t> serverNonce = 
ParseHex(m[
"SERVERNONCE"]);
   587             LogPrint(
BCLog::TOR, 
"tor: AUTHCHALLENGE ServerHash %s ServerNonce %s\n", 
HexStr(serverHash), 
HexStr(serverNonce));
   588             if (serverNonce.size() != 32) {
   589                 LogPrintf(
"tor: ServerNonce is not 32 bytes, as required by spec\n");
   593             std::vector<uint8_t> computedServerHash = ComputeResponse(TOR_SAFE_SERVERKEY, 
cookie, 
clientNonce, serverNonce);
   594             if (computedServerHash != serverHash) {
   595                 LogPrintf(
"tor: ServerHash %s does not match expected ServerHash %s\n", 
HexStr(serverHash), 
HexStr(computedServerHash));
   599             std::vector<uint8_t> computedClientHash = ComputeResponse(TOR_SAFE_CLIENTKEY, 
cookie, 
clientNonce, serverNonce);
   602             LogPrintf(
"tor: Invalid reply to AUTHCHALLENGE\n");
   605         LogPrintf(
"tor: SAFECOOKIE authentication challenge failed\n");
   611     if (reply.
code == 250) {
   612         std::set<std::string> methods;
   613         std::string cookiefile;
   619         for (
const std::string &s : reply.
lines) {
   621             if (l.first == 
"AUTH") {
   623                 std::map<std::string,std::string>::iterator i;
   624                 if ((i = m.find(
"METHODS")) != m.end())
   625                     boost::split(methods, i->second, boost::is_any_of(
","));
   626                 if ((i = m.find(
"COOKIEFILE")) != m.end())
   627                     cookiefile = i->second;
   628             } 
else if (l.first == 
"VERSION") {
   630                 std::map<std::string,std::string>::iterator i;
   631                 if ((i = m.find(
"Tor")) != m.end()) {
   632                     LogPrint(
BCLog::TOR, 
"tor: Connected to Tor version %s\n", i->second);
   636         for (
const std::string &s : methods) {
   637             LogPrint(
BCLog::TOR, 
"tor: Supported authentication method: %s\n", s);
   644         std::string torpassword = 
gArgs.
GetArg(
"-torpassword", 
"");
   645         if (!torpassword.empty()) {
   646             if (methods.count(
"HASHEDPASSWORD")) {
   647                 LogPrint(
BCLog::TOR, 
"tor: Using HASHEDPASSWORD authentication\n");
   648                 boost::replace_all(torpassword, 
"\"", 
"\\\"");
   651                 LogPrintf(
"tor: Password provided with -torpassword, but HASHEDPASSWORD authentication is not available\n");
   653         } 
else if (methods.count(
"NULL")) {
   654             LogPrint(
BCLog::TOR, 
"tor: Using NULL authentication\n");
   656         } 
else if (methods.count(
"SAFECOOKIE")) {
   658             LogPrint(
BCLog::TOR, 
"tor: Using SAFECOOKIE authentication, reading cookie authentication from %s\n", cookiefile);
   659             std::pair<bool,std::string> status_cookie = ReadBinaryFile(cookiefile, TOR_COOKIE_SIZE);
   660             if (status_cookie.first && status_cookie.second.size() == TOR_COOKIE_SIZE) {
   662                 cookie = std::vector<uint8_t>(status_cookie.second.begin(), status_cookie.second.end());
   663                 clientNonce = std::vector<uint8_t>(TOR_NONCE_SIZE, 0);
   667                 if (status_cookie.first) {
   668                     LogPrintf(
"tor: Authentication cookie %s is not exactly %i bytes, as is required by the spec\n", cookiefile, TOR_COOKIE_SIZE);
   670                     LogPrintf(
"tor: Authentication cookie %s could not be opened (check permissions)\n", cookiefile);
   673         } 
else if (methods.count(
"HASHEDPASSWORD")) {
   674             LogPrintf(
"tor: The only supported authentication mechanism left is password, but no password provided with -torpassword\n");
   676             LogPrintf(
"tor: No supported authentication method\n");
   679         LogPrintf(
"tor: Requesting protocol info failed\n");
   688         LogPrintf(
"tor: Error sending initial protocolinfo command\n");
   700     LogPrint(
BCLog::TOR, 
"tor: Not connected to Tor control port %s, trying to reconnect\n", 
target);
   716         LogPrintf(
"tor: Re-initiating connection to Tor control port %s failed\n", 
target);
   732 static struct event_base *gBase;
   733 static std::thread torControlThread;
   735 static void TorControlThread()
   739     event_base_dispatch(gBase);
   746     evthread_use_windows_threads();
   748     evthread_use_pthreads();
   750     gBase = event_base_new();
   752         LogPrintf(
"tor: Unable to create event_base\n");
   756     torControlThread = std::thread(std::bind(&
TraceThread<
void (*)()>, 
"torcontrol", &TorControlThread));
   762         LogPrintf(
"tor: Thread interrupt\n");
   763         event_base_loopbreak(gBase);
   770         torControlThread.join();
   771         event_base_free(gBase);
 void authchallenge_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for AUTHCHALLENGE result. 
bool AddLocal(const CService &addr, int nScore)
FILE * fopen(const fs::path &p, const char *mode)
std::function< void(TorControlConnection &)> disconnected
Callback when connection lost. 
std::function< void(TorControlConnection &)> ConnectionCB
struct bufferevent * b_conn
Connection to control socket. 
std::vector< uint8_t > clientNonce
ClientNonce for SAFECOOKIE auth. 
CService LookupNumeric(const char *pszName, int portDefault)
Reply from Tor, can be single or multi-line. 
bool Connect(const std::string &target, const ConnectionCB &connected, const ConnectionCB &disconnected)
Connect to a Tor control port. 
std::string HexStr(const T itbegin, const T itend, bool fSpaces=false)
void SetLimited(enum Network net, bool fLimited)
Make a particular network entirely off-limits (no automatic connects to it) 
A hasher class for HMAC-SHA-256. 
void protocolinfo_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for PROTOCOLINFO result. 
void Reconnect()
Reconnect, after getting disconnected. 
std::vector< std::string > lines
unsigned short GetListenPort()
std::deque< ReplyHandlerCB > reply_handlers
Response handlers. 
std::function< void(TorControlConnection &, const TorControlReply &)> ReplyHandlerCB
void disconnected_cb(TorControlConnection &conn)
Callback after connection lost or failed connection attempt. 
static void readcb(struct bufferevent *bev, void *ctx)
Libevent handlers: internal. 
fs::path GetPrivateKeyFile()
Get name fo file to store private key in. 
struct event * reconnect_ev
TorControlConnection(struct event_base *base)
Create a new TorControlConnection. 
if(!params[0].isNull()) nMinDepth
std::map< std::string, std::string > ParseTorReplyMapping(const std::string &s)
Parse reply arguments in the form 'METHODS=COOKIE,SAFECOOKIE COOKIEFILE=".../control_auth_cookie"'. 
const std::string DEFAULT_TOR_CONTROL
Default control port. 
std::vector< uint8_t > cookie
Cookie for SAFECOOKIE auth. 
TorControlConnection conn
A combination of a network address (CNetAddr) and a (TCP) port. 
TorController(struct event_base *base, const std::string &target)
void TraceThread(const char *name, Callable func)
static void reconnect_cb(evutil_socket_t fd, short what, void *arg)
Callback for reconnect timer. 
void add_onion_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for ADD_ONION result. 
std::pair< std::string, std::string > SplitTorReplyLine(const std::string &s)
bool SetProxy(enum Network net, const proxyType &addrProxy)
struct timeval MillisToTimeval(int64_t nTimeout)
Convert milliseconds to a struct timeval for e.g. 
void auth_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for AUTHENTICATE result. 
void Disconnect()
Disconnect from Tor control port. 
static void eventcb(struct bufferevent *bev, short what, void *ctx)
static const size_t OUTPUT_SIZE
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value. 
bool Command(const std::string &cmd, const ReplyHandlerCB &reply_handler)
Send a command, register a handler for the reply. 
void GetRandBytes(unsigned char *buf, int num)
Functions to gather random data via the OpenSSL PRNG. 
void connected_cb(TorControlConnection &conn)
Callback after successful connection. 
std::string ToString() const
const fs::path & GetDataDir(bool fNetSpecific)
Controller that connects to Tor control socket, authenticate, then create and maintain an ephemeral h...
void InterruptTorControl()
Low-level handling for Tor control connection. 
boost::signals2::signal< void(TorControlConnection &, const TorControlReply &)> async_handler
Response handlers for async replies. 
std::string SanitizeString(const std::string &str, int rule)
Remove unsafe chars. 
TorControlReply message
Message being received. 
int atoi(const std::string &str)
void RemoveLocal(const CService &addr)
struct event_base * base
Libevent event base. 
std::function< void(TorControlConnection &)> connected
Callback when ready for use. 
std::vector< unsigned char > ParseHex(const char *psz)