13 #include <boost/function.hpp> 14 #include <boost/bind.hpp> 15 #include <boost/signals2/signal.hpp> 16 #include <boost/foreach.hpp> 17 #include <boost/algorithm/string/predicate.hpp> 18 #include <boost/algorithm/string/split.hpp> 19 #include <boost/algorithm/string/classification.hpp> 20 #include <boost/algorithm/string/replace.hpp> 22 #include <event2/bufferevent.h> 23 #include <event2/buffer.h> 24 #include <event2/util.h> 25 #include <event2/event.h> 26 #include <event2/thread.h> 35 static const std::string
TOR_SAFE_SERVERKEY =
"Tor safe cookie authentication server-to-controller hash";
37 static const std::string
TOR_SAFE_CLIENTKEY =
"Tor safe cookie authentication controller-to-server hash";
73 typedef boost::function<void(TorControlConnection &,const TorControlReply &)>
ReplyHandlerCB;
101 boost::signals2::signal<void(TorControlConnection &,const TorControlReply &)>
async_handler;
117 static void readcb(
struct bufferevent *bev,
void *
ctx);
118 static void eventcb(
struct bufferevent *bev,
short what,
void *
ctx);
135 struct evbuffer *input = bufferevent_get_input(bev);
136 size_t n_read_out = 0;
140 while((line = evbuffer_readln(input, &n_read_out, EVBUFFER_EOL_CRLF)) != NULL)
142 std::string s(line, n_read_out);
147 self->message.code =
atoi(s.substr(0,3));
148 self->message.lines.push_back(s.substr(4));
152 if (self->message.code >= 600) {
155 self->async_handler(*
self, self->message);
157 if (!self->reply_handlers.empty()) {
159 self->reply_handlers.front()(*
self,
self->message);
160 self->reply_handlers.pop_front();
162 LogPrint(
"tor",
"tor: Received unexpected sync reply %i\n", self->message.code);
165 self->message.Clear();
172 LogPrintf(
"tor: Disconnecting because MAX_LINE_LENGTH exceeded\n");
180 if (what & BEV_EVENT_CONNECTED) {
181 LogPrint(
"tor",
"tor: Succesfully connected!\n");
182 self->connected(*
self);
183 }
else if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
184 if (what & BEV_EVENT_ERROR)
185 LogPrint(
"tor",
"tor: Error connecting to Tor control socket\n");
187 LogPrint(
"tor",
"tor: End of stream\n");
189 self->disconnected(*
self);
198 struct sockaddr_storage connect_to_addr;
199 int connect_to_addrlen =
sizeof(connect_to_addr);
200 if (evutil_parse_sockaddr_port(target.c_str(),
201 (
struct sockaddr*)&connect_to_addr, &connect_to_addrlen)<0) {
202 LogPrintf(
"tor: Error parsing socket address %s\n", target);
207 b_conn = bufferevent_socket_new(
base, -1, BEV_OPT_CLOSE_ON_FREE);
211 bufferevent_enable(
b_conn, EV_READ|EV_WRITE);
216 if (bufferevent_socket_connect(
b_conn, (
struct sockaddr*)&connect_to_addr, connect_to_addrlen) < 0) {
217 LogPrintf(
"tor: Error connecting to address %s\n", target);
235 struct evbuffer *buf = bufferevent_get_output(
b_conn);
238 evbuffer_add(buf, cmd.data(), cmd.size());
239 evbuffer_add(buf,
"\r\n", 2);
253 while (ptr < s.size() && s[ptr] !=
' ') {
254 type.push_back(s[ptr]);
259 return make_pair(type, s.substr(ptr));
266 std::map<std::string,std::string> mapping;
268 while (ptr < s.size()) {
269 std::string
key, value;
270 while (ptr < s.size() && s[ptr] !=
'=') {
271 key.push_back(s[ptr]);
275 return std::map<std::string,std::string>();
277 if (ptr < s.size() && s[ptr] ==
'"') {
279 bool escape_next =
false;
280 while (ptr < s.size() && (!escape_next && s[ptr] !=
'"')) {
281 escape_next = (s[ptr] ==
'\\');
282 value.push_back(s[ptr]);
286 return std::map<std::string,std::string>();
293 while (ptr < s.size() && s[ptr] !=
' ') {
294 value.push_back(s[ptr]);
298 if (ptr < s.size() && s[ptr] ==
' ')
300 mapping[
key] = value;
312 static std::pair<bool,std::string>
ReadBinaryFile(
const std::string &filename,
size_t maxsize=std::numeric_limits<size_t>::max())
314 FILE *
f = fopen(filename.c_str(),
"rb");
316 return std::make_pair(
false,
"");
320 while ((
n=fread(buffer, 1,
sizeof(buffer),
f)) > 0) {
321 retval.append(buffer, buffer+
n);
322 if (
retval.size() > maxsize)
326 return std::make_pair(
true,
retval);
334 FILE *
f = fopen(filename.c_str(),
"wb");
390 static void reconnect_cb(evutil_socket_t fd,
short what,
void *arg);
395 target(target), conn(
base), reconnect(true), reconnect_ev(0),
400 LogPrintf(
"tor: Failed to create event for reconnection: out of memory?\n");
404 LogPrintf(
"tor: Initiating connection to Tor control port %s failed\n",
target);
427 if (reply.
code == 250) {
428 LogPrint(
"tor",
"tor: ADD_ONION succesful\n");
429 BOOST_FOREACH(
const std::string &s, reply.
lines) {
431 std::map<std::string,std::string>::iterator i;
432 if ((i =
m.find(
"ServiceID")) !=
m.end())
434 if ((i =
m.find(
"PrivateKey")) !=
m.end())
446 }
else if (reply.
code == 510) {
447 LogPrintf(
"tor: Add onion failed with unrecognized command (You probably need to upgrade Tor)\n");
449 LogPrintf(
"tor: Add onion failed; error code %d\n", reply.
code);
455 if (reply.
code == 250) {
456 LogPrint(
"tor",
"tor: Authentication succesful\n");
460 if (
GetArg(
"-onion",
"") ==
"") {
476 LogPrintf(
"tor: Authentication failed\n");
496 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)
500 computeHash.Write(
begin_ptr(cookie), cookie.size());
501 computeHash.Write(
begin_ptr(clientNonce), clientNonce.size());
502 computeHash.Write(
begin_ptr(serverNonce), serverNonce.size());
503 computeHash.Finalize(
begin_ptr(computedHash));
509 if (reply.
code == 250) {
510 LogPrint(
"tor",
"tor: SAFECOOKIE authentication challenge succesful\n");
512 if (l.first ==
"AUTHCHALLENGE") {
514 std::vector<uint8_t> serverHash =
ParseHex(
m[
"SERVERHASH"]);
515 std::vector<uint8_t> serverNonce =
ParseHex(
m[
"SERVERNONCE"]);
516 LogPrint(
"tor",
"tor: AUTHCHALLENGE ServerHash %s ServerNonce %s\n",
HexStr(serverHash),
HexStr(serverNonce));
517 if (serverNonce.size() != 32) {
518 LogPrintf(
"tor: ServerNonce is not 32 bytes, as required by spec\n");
523 if (computedServerHash != serverHash) {
524 LogPrintf(
"tor: ServerHash %s does not match expected ServerHash %s\n",
HexStr(serverHash),
HexStr(computedServerHash));
531 LogPrintf(
"tor: Invalid reply to AUTHCHALLENGE\n");
534 LogPrintf(
"tor: SAFECOOKIE authentication challenge failed\n");
540 if (reply.
code == 250) {
541 std::set<std::string> methods;
542 std::string cookiefile;
548 BOOST_FOREACH(
const std::string &s, reply.
lines) {
550 if (l.first ==
"AUTH") {
552 std::map<std::string,std::string>::iterator i;
553 if ((i =
m.find(
"METHODS")) !=
m.end())
554 boost::split(methods, i->second, boost::is_any_of(
","));
555 if ((i =
m.find(
"COOKIEFILE")) !=
m.end())
556 cookiefile = i->second;
557 }
else if (l.first ==
"VERSION") {
559 std::map<std::string,std::string>::iterator i;
560 if ((i =
m.find(
"Tor")) !=
m.end()) {
561 LogPrint(
"tor",
"tor: Connected to Tor version %s\n", i->second);
565 BOOST_FOREACH(
const std::string &s, methods) {
566 LogPrint(
"tor",
"tor: Supported authentication method: %s\n", s);
573 std::string torpassword =
GetArg(
"-torpassword",
"");
574 if (methods.count(
"NULL")) {
575 LogPrint(
"tor",
"tor: Using NULL authentication\n");
577 }
else if (methods.count(
"SAFECOOKIE")) {
579 LogPrint(
"tor",
"tor: Using SAFECOOKIE authentication, reading cookie authentication from %s\n", cookiefile);
581 if (status_cookie.first && status_cookie.second.size() ==
TOR_COOKIE_SIZE) {
583 cookie = std::vector<uint8_t>(status_cookie.second.begin(), status_cookie.second.end());
588 if (status_cookie.first) {
589 LogPrintf(
"tor: Authentication cookie %s is not exactly %i bytes, as is required by the spec\n", cookiefile,
TOR_COOKIE_SIZE);
591 LogPrintf(
"tor: Authentication cookie %s could not be opened (check permissions)\n", cookiefile);
594 }
else if (methods.count(
"HASHEDPASSWORD")) {
595 if (!torpassword.empty()) {
596 LogPrint(
"tor",
"tor: Using HASHEDPASSWORD authentication\n");
597 boost::replace_all(torpassword,
"\"",
"\\\"");
600 LogPrintf(
"tor: Password authentication required, but no password provided with -torpassword\n");
603 LogPrintf(
"tor: No supported authentication method\n");
606 LogPrintf(
"tor: Requesting protocol info failed\n");
615 LogPrintf(
"tor: Error sending initial protocolinfo command\n");
627 LogPrint(
"tor",
"tor: Not connected to Tor control port %s, trying to reconnect\n",
target);
643 LogPrintf(
"tor: Re-initiating connection to Tor control port %s failed\n",
target);
649 return (
GetDataDir() /
"onion_private_key").string();
666 event_base_dispatch(
base);
673 evthread_use_windows_threads();
675 evthread_use_pthreads();
677 base = event_base_new();
679 LogPrintf(
"tor: Unable to create event_base\n");
690 event_base_loopbreak(
base);
699 #if BOOST_VERSION >= 105000 704 event_base_free(
base);
const boost::filesystem::path & GetDataDir(bool fNetSpecific)
static void TorControlThread()
boost::thread torControlThread
static const std::string TOR_SAFE_CLIENTKEY
static std::map< std::string, std::string > ParseTorReplyMapping(const std::string &s)
void add_onion_cb(TorControlConnection &conn, const TorControlReply &reply)
void auth_cb(TorControlConnection &conn, const TorControlReply &reply)
bool AddLocal(const CService &addr, int nScore)
boost::signals2::signal< void(TorControlConnection &, const TorControlReply &)> async_handler
static const int TOR_COOKIE_SIZE
boost::function< void(TorControlConnection &)> connected
static const size_t OUTPUT_SIZE
CService LookupNumeric(const char *pszName, int portDefault)
static void eventcb(struct bufferevent *bev, short what, void *ctx)
std::string HexStr(const T itbegin, const T itend, bool fSpaces=false)
void connected_cb(TorControlConnection &conn)
void SetLimited(enum Network net, bool fLimited)
static const int TOR_NONCE_SIZE
unsigned short GetListenPort()
TorControlConnection conn
std::string ToString(bool fUseGetnameinfo=true) const
std::deque< ReplyHandlerCB > reply_handlers
static std::pair< std::string, std::string > SplitTorReplyLine(const std::string &s)
struct bufferevent * b_conn
static void readcb(struct bufferevent *bev, void *ctx)
std::string GetPrivateKeyFile()
const std::string DEFAULT_TOR_CONTROL
static const float RECONNECT_TIMEOUT_START
static int LogPrint(const char *category, const char *format)
boost::function< void(TorControlConnection &)> disconnected
struct event * reconnect_ev
static const float RECONNECT_TIMEOUT_EXP
static secp256k1_context * ctx
void TraceThread(const char *name, Callable func)
static void reconnect_cb(evutil_socket_t fd, short what, void *arg)
vector< unsigned char > ParseHex(const char *psz)
bool Connect(const std::string &target, const ConnectionCB &connected, const ConnectionCB &disconnected)
TorControlConnection(struct event_base *base)
V::value_type * begin_ptr(V &v)
bool SetProxy(enum Network net, const proxyType &addrProxy)
struct timeval MillisToTimeval(int64_t nTimeout)
void disconnected_cb(TorControlConnection &conn)
std::vector< std::string > lines
std::vector< uint8_t > cookie
bool RemoveLocal(const CService &addr)
static std::pair< bool, std::string > ReadBinaryFile(const std::string &filename, size_t maxsize=std::numeric_limits< size_t >::max())
void StartTorControl(boost::thread_group &threadGroup, CScheduler &scheduler)
boost::function< void(TorControlConnection &, const TorControlReply &)> ReplyHandlerCB
void GetRandBytes(unsigned char *buf, int num)
std::string GetArg(const std::string &strArg, const std::string &strDefault)
void InterruptTorControl()
bool Command(const std::string &cmd, const ReplyHandlerCB &reply_handler)
static const int MAX_LINE_LENGTH
void authchallenge_cb(TorControlConnection &conn, const TorControlReply &reply)
static const std::string TOR_SAFE_SERVERKEY
static bool WriteBinaryFile(const std::string &filename, const std::string &data)
TorController(struct event_base *base, const std::string &target)
std::vector< uint8_t > clientNonce
void protocolinfo_cb(TorControlConnection &conn, const TorControlReply &reply)
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)
boost::function< void(TorControlConnection &)> ConnectionCB
int atoi(const std::string &str)