17 #include <boost/function.hpp>
18 #include <boost/bind/bind.hpp>
19 #include <boost/signals2/signal.hpp>
20 #include <boost/foreach.hpp>
21 #include <boost/algorithm/string/predicate.hpp>
22 #include <boost/algorithm/string/split.hpp>
23 #include <boost/algorithm/string/classification.hpp>
24 #include <boost/algorithm/string/replace.hpp>
26 #include <event2/bufferevent.h>
27 #include <event2/buffer.h>
28 #include <event2/util.h>
29 #include <event2/event.h>
30 #include <event2/thread.h>
35 static const int TOR_COOKIE_SIZE = 32;
37 static const int TOR_NONCE_SIZE = 32;
39 static const std::string TOR_SAFE_SERVERKEY =
"Tor safe cookie authentication server-to-controller hash";
41 static const std::string TOR_SAFE_CLIENTKEY =
"Tor safe cookie authentication controller-to-server hash";
43 static const float RECONNECT_TIMEOUT_START = 1.0;
45 static const float RECONNECT_TIMEOUT_EXP = 1.5;
50 static const int MAX_LINE_LENGTH = 100000;
121 static void readcb(
struct bufferevent *bev,
void *ctx);
122 static void eventcb(
struct bufferevent *bev,
short what,
void *ctx);
126 base(_base), b_conn(0)
139 struct evbuffer *input = bufferevent_get_input(bev);
140 size_t n_read_out = 0;
144 while((line = evbuffer_readln(input, &n_read_out, EVBUFFER_EOL_CRLF)) != NULL)
146 std::string s(line, n_read_out);
151 self->message.code =
atoi(s.substr(0,3));
152 self->message.lines.push_back(s.substr(4));
156 if (self->message.code >= 600) {
159 self->async_handler(*
self, self->message);
161 if (!self->reply_handlers.empty()) {
163 self->reply_handlers.front()(*
self,
self->message);
164 self->reply_handlers.pop_front();
166 LogPrint(
"tor",
"tor: Received unexpected sync reply %i\n", self->message.code);
169 self->message.Clear();
175 if (evbuffer_get_length(input) > MAX_LINE_LENGTH) {
176 LogPrintf(
"tor: Disconnecting because MAX_LINE_LENGTH exceeded\n");
184 if (what & BEV_EVENT_CONNECTED) {
185 LogPrint(
"tor",
"tor: Successfully connected!\n");
186 self->connected(*
self);
187 }
else if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
188 if (what & BEV_EVENT_ERROR)
189 LogPrint(
"tor",
"tor: Error connecting to Tor control socket\n");
191 LogPrint(
"tor",
"tor: End of stream\n");
193 self->disconnected(*
self);
202 struct sockaddr_storage connect_to_addr;
203 int connect_to_addrlen =
sizeof(connect_to_addr);
204 if (evutil_parse_sockaddr_port(target.c_str(),
205 (
struct sockaddr*)&connect_to_addr, &connect_to_addrlen)<0) {
206 LogPrintf(
"tor: Error parsing socket address %s\n", target);
211 b_conn = bufferevent_socket_new(
base, -1, BEV_OPT_CLOSE_ON_FREE);
215 bufferevent_enable(
b_conn, EV_READ|EV_WRITE);
220 if (bufferevent_socket_connect(
b_conn, (
struct sockaddr*)&connect_to_addr, connect_to_addrlen) < 0) {
221 LogPrintf(
"tor: Error connecting to address %s\n", target);
239 struct evbuffer *buf = bufferevent_get_output(
b_conn);
242 evbuffer_add(buf, cmd.data(), cmd.size());
243 evbuffer_add(buf,
"\r\n", 2);
253 static std::pair<std::string,std::string> SplitTorReplyLine(
const std::string &s)
257 while (ptr < s.size() && s[ptr] !=
' ') {
258 type.push_back(s[ptr]);
263 return make_pair(type, s.substr(ptr));
268 static std::map<std::string,std::string> ParseTorReplyMapping(
const std::string &s)
270 std::map<std::string,std::string> mapping;
272 while (ptr < s.size()) {
273 std::string key, value;
274 while (ptr < s.size() && s[ptr] !=
'=') {
275 key.push_back(s[ptr]);
279 return std::map<std::string,std::string>();
281 if (ptr < s.size() && s[ptr] ==
'"') {
283 bool escape_next =
false;
284 while (ptr < s.size() && (!escape_next && s[ptr] !=
'"')) {
285 escape_next = (s[ptr] ==
'\\');
286 value.push_back(s[ptr]);
290 return std::map<std::string,std::string>();
297 while (ptr < s.size() && s[ptr] !=
' ') {
298 value.push_back(s[ptr]);
302 if (ptr < s.size() && s[ptr] ==
' ')
304 mapping[key] = value;
316 static std::pair<bool,std::string> ReadBinaryFile(
const std::string &filename,
size_t maxsize=std::numeric_limits<size_t>::max())
318 FILE *f = fopen(filename.c_str(),
"rb");
320 return std::make_pair(
false,
"");
324 while ((n=fread(buffer, 1,
sizeof(buffer), f)) > 0) {
325 retval.append(buffer, buffer+n);
326 if (retval.size() > maxsize)
330 return std::make_pair(
true,retval);
336 static bool WriteBinaryFile(
const std::string &filename,
const std::string &data)
338 FILE *f = fopen(filename.c_str(),
"wb");
341 if (fwrite(data.data(), 1, data.size(), f) != data.size()) {
394 static void reconnect_cb(evutil_socket_t fd,
short what,
void *arg);
399 target(_target), conn(
base), reconnect(true), reconnect_ev(0),
400 reconnect_timeout(RECONNECT_TIMEOUT_START)
404 LogPrintf(
"tor: Failed to create event for reconnection: out of memory?\n");
407 boost::placeholders::_1),
409 LogPrintf(
"tor: Initiating connection to Tor control port %s failed\n", _target);
432 if (reply.
code == 250) {
433 LogPrint(
"tor",
"tor: ADD_ONION successful\n");
434 BOOST_FOREACH(
const std::string &s, reply.
lines) {
435 std::map<std::string,std::string> m = ParseTorReplyMapping(s);
436 std::map<std::string,std::string>::iterator i;
437 if ((i = m.find(
"ServiceID")) != m.end())
439 if ((i = m.find(
"PrivateKey")) != m.end())
451 }
else if (reply.
code == 510) {
452 LogPrintf(
"tor: Add onion failed with unrecognized command (You probably need to upgrade Tor)\n");
454 LogPrintf(
"tor: Add onion failed; error code %d\n", reply.
code);
460 if (reply.
code == 250) {
461 LogPrint(
"tor",
"tor: Authentication successful\n");
465 if (
GetArg(
"-onion",
"") ==
"") {
480 boost::placeholders::_1,
481 boost::placeholders::_2));
483 LogPrintf(
"tor: Authentication failed\n");
503 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)
505 CHMAC_SHA256 computeHash((
const uint8_t*)key.data(), key.size());
507 computeHash.Write(cookie.data(), cookie.size());
508 computeHash.Write(clientNonce.data(), clientNonce.size());
509 computeHash.Write(serverNonce.data(), serverNonce.size());
510 computeHash.Finalize(computedHash.data());
516 if (reply.
code == 250) {
517 LogPrint(
"tor",
"tor: SAFECOOKIE authentication challenge successful\n");
518 std::pair<std::string,std::string> l = SplitTorReplyLine(reply.
lines[0]);
519 if (l.first ==
"AUTHCHALLENGE") {
520 std::map<std::string,std::string> m = ParseTorReplyMapping(l.second);
521 std::vector<uint8_t> serverHash =
ParseHex(m[
"SERVERHASH"]);
522 std::vector<uint8_t> serverNonce =
ParseHex(m[
"SERVERNONCE"]);
523 LogPrint(
"tor",
"tor: AUTHCHALLENGE ServerHash %s ServerNonce %s\n",
HexStr(serverHash),
HexStr(serverNonce));
524 if (serverNonce.size() != 32) {
525 LogPrintf(
"tor: ServerNonce is not 32 bytes, as required by spec\n");
529 std::vector<uint8_t> computedServerHash = ComputeResponse(TOR_SAFE_SERVERKEY,
cookie,
clientNonce, serverNonce);
530 if (computedServerHash != serverHash) {
531 LogPrintf(
"tor: ServerHash %s does not match expected ServerHash %s\n",
HexStr(serverHash),
HexStr(computedServerHash));
535 std::vector<uint8_t> computedClientHash = ComputeResponse(TOR_SAFE_CLIENTKEY,
cookie,
clientNonce, serverNonce);
537 boost::placeholders::_1,
538 boost::placeholders::_2));
540 LogPrintf(
"tor: Invalid reply to AUTHCHALLENGE\n");
543 LogPrintf(
"tor: SAFECOOKIE authentication challenge failed\n");
549 if (reply.
code == 250) {
550 std::set<std::string> methods;
551 std::string cookiefile;
557 BOOST_FOREACH(
const std::string &s, reply.
lines) {
558 std::pair<std::string,std::string> l = SplitTorReplyLine(s);
559 if (l.first ==
"AUTH") {
560 std::map<std::string,std::string> m = ParseTorReplyMapping(l.second);
561 std::map<std::string,std::string>::iterator i;
562 if ((i = m.find(
"METHODS")) != m.end())
563 boost::split(methods, i->second, boost::is_any_of(
","));
564 if ((i = m.find(
"COOKIEFILE")) != m.end())
565 cookiefile = i->second;
566 }
else if (l.first ==
"VERSION") {
567 std::map<std::string,std::string> m = ParseTorReplyMapping(l.second);
568 std::map<std::string,std::string>::iterator i;
569 if ((i = m.find(
"Tor")) != m.end()) {
570 LogPrint(
"tor",
"tor: Connected to Tor version %s\n", i->second);
574 BOOST_FOREACH(
const std::string &s, methods) {
575 LogPrint(
"tor",
"tor: Supported authentication method: %s\n", s);
582 std::string torpassword =
GetArg(
"-torpassword",
"");
583 if (!torpassword.empty()) {
584 if (methods.count(
"HASHEDPASSWORD")) {
585 LogPrint(
"tor",
"tor: Using HASHEDPASSWORD authentication\n");
586 boost::replace_all(torpassword,
"\"",
"\\\"");
588 boost::placeholders::_1,
589 boost::placeholders::_2));
591 LogPrintf(
"tor: Password provided with -torpassword, but HASHEDPASSWORD authentication is not available\n");
593 }
else if (methods.count(
"NULL")) {
594 LogPrint(
"tor",
"tor: Using NULL authentication\n");
596 boost::placeholders::_1,
597 boost::placeholders::_2));
598 }
else if (methods.count(
"SAFECOOKIE")) {
600 LogPrint(
"tor",
"tor: Using SAFECOOKIE authentication, reading cookie authentication from %s\n", cookiefile);
601 std::pair<bool,std::string> status_cookie = ReadBinaryFile(cookiefile, TOR_COOKIE_SIZE);
602 if (status_cookie.first && status_cookie.second.size() == TOR_COOKIE_SIZE) {
604 cookie = std::vector<uint8_t>(status_cookie.second.begin(), status_cookie.second.end());
605 clientNonce = std::vector<uint8_t>(TOR_NONCE_SIZE, 0);
608 boost::placeholders::_1,
609 boost::placeholders::_2));
611 if (status_cookie.first) {
612 LogPrintf(
"tor: Authentication cookie %s is not exactly %i bytes, as is required by the spec\n", cookiefile, TOR_COOKIE_SIZE);
614 LogPrintf(
"tor: Authentication cookie %s could not be opened (check permissions)\n", cookiefile);
617 }
else if (methods.count(
"HASHEDPASSWORD")) {
618 LogPrintf(
"tor: The only supported authentication mechanism left is password, but no password provided with -torpassword\n");
620 LogPrintf(
"tor: No supported authentication method\n");
623 LogPrintf(
"tor: Requesting protocol info failed\n");
632 boost::placeholders::_1,
633 boost::placeholders::_2)))
634 LogPrintf(
"tor: Error sending initial protocolinfo command\n");
646 LogPrint(
"tor",
"tor: Not connected to Tor control port %s, trying to reconnect\n",
target);
661 boost::placeholders::_1),
663 LogPrintf(
"tor: Re-initiating connection to Tor control port %s failed\n",
target);
669 return (
GetDataDir() /
"onion_private_key").string();
682 static void TorControlThread()
686 event_base_dispatch(
base);
693 evthread_use_windows_threads();
695 evthread_use_pthreads();
697 base = event_base_new();
699 LogPrintf(
"tor: Unable to create event_base\n");
710 event_base_loopbreak(
base);
718 event_base_free(
base);
A hasher class for HMAC-SHA-512.
static const size_t OUTPUT_SIZE
A combination of a network address (CNetAddr) and a (TCP) port.
std::string ToString() const
Low-level handling for Tor control connection.
TorControlReply message
Message being received.
std::deque< ReplyHandlerCB > reply_handlers
Response handlers.
boost::signals2::signal< void(TorControlConnection &, const TorControlReply &)> async_handler
Response handlers for async replies.
boost::function< void(TorControlConnection &)> ConnectionCB
bool Connect(const std::string &target, 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.
boost::function< void(TorControlConnection &, const TorControlReply &)> ReplyHandlerCB
static void readcb(struct bufferevent *bev, void *ctx)
Libevent handlers: internal.
static void eventcb(struct bufferevent *bev, short what, void *ctx)
boost::function< void(TorControlConnection &)> connected
Callback when ready for use.
bool Disconnect()
Disconnect from Tor control port.
struct bufferevent * b_conn
Connection to control socket.
struct event_base * base
Libevent event base.
boost::function< void(TorControlConnection &)> disconnected
Callback when connection lost.
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 a ephemeral hi...
TorControlConnection conn
static void reconnect_cb(evutil_socket_t fd, short what, void *arg)
Callback for reconnect timer.
std::string 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.
void disconnected_cb(TorControlConnection &conn)
Callback after connection lost or failed connection attempt.
void authchallenge_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for AUTHCHALLENGE result.
std::vector< uint8_t > cookie
Cookie for SAFECOOKIE auth.
struct event * reconnect_ev
TorController(struct event_base *base, const std::string &target)
void auth_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for AUTHENTICATE result.
void Reconnect()
Reconnect, after getting disconnected.
void protocolinfo_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for PROTOCOLINFO result.
bool RemoveLocal(const CService &addr)
void SetLimited(enum Network net, bool fLimited)
Make a particular network entirely off-limits (no automatic connects to it)
bool AddLocal(const CService &addr, int nScore)
unsigned short GetListenPort()
struct timeval MillisToTimeval(int64_t nTimeout)
Convert milliseconds to a struct timeval for e.g.
CService LookupNumeric(const char *pszName, int portDefault)
bool SetProxy(enum Network net, const proxyType &addrProxy)
void GetRandBytes(unsigned char *buf, int num)
Functions to gather random data via the OpenSSL PRNG.
const std::string DEFAULT_TOR_CONTROL
Default control port.
void InterruptTorControl()
void StartTorControl(boost::thread_group &threadGroup, CScheduler &scheduler)
boost::thread torControlThread
std::string GetArg(const std::string &strArg, const std::string &strDefault)
Return string argument or default value.
const boost::filesystem::path & GetDataDir(bool fNetSpecific)
#define LogPrint(category,...)
void TraceThread(const char *name, Callable func)
int atoi(const std::string &str)
vector< unsigned char > ParseHex(const char *psz)
std::string HexStr(const T itbegin, const T itend, bool fSpaces=false)