torcontrol.h raw
1 // Copyright (c) 2015-2021 The Limenka developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5 /**
6 * Functionality for communicating with Tor.
7 */
8 #ifndef LIMENKA_TORCONTROL_H
9 #define LIMENKA_TORCONTROL_H
10
11 #include <limenka-build-config.h> // IWYU pragma: keep
12
13 #include <netaddress.h>
14 #include <util/fs.h>
15
16 #include <event2/util.h>
17
18 #include <cstdint>
19 #include <deque>
20 #include <functional>
21 #include <string>
22 #include <vector>
23
24 namespace subprocess {
25 class Popen;
26 }
27
28 constexpr int DEFAULT_TOR_CONTROL_PORT = 9051;
29 extern const std::string DEFAULT_TOR_CONTROL;
30 extern const std::string DEFAULT_TOR_EXECUTE;
31 static const bool DEFAULT_LISTEN_ONION = true;
32
33 /** Tor control reply code. Ref: https://spec.torproject.org/control-spec/replies.html */
34 constexpr int TOR_REPLY_SYNTAX_ERROR{512}; //!< Syntax error in command argument
35
36 void StartTorControl(CService onion_service_target);
37 void InterruptTorControl();
38 void StopTorControl();
39
40 CService DefaultOnionServiceTarget(uint16_t port);
41
42 /** Reply from Tor, can be single or multi-line */
43 class TorControlReply
44 {
45 public:
46 TorControlReply() { Clear(); }
47
48 int code;
49 std::vector<std::string> lines;
50
51 void Clear()
52 {
53 code = 0;
54 lines.clear();
55 }
56 };
57
58 /** Low-level handling for Tor control connection.
59 * Speaks the SMTP-like protocol as defined in torspec/control-spec.txt
60 */
61 class TorControlConnection
62 {
63 public:
64 typedef std::function<void(TorControlConnection&)> ConnectionCB;
65 typedef std::function<void(TorControlConnection &,const TorControlReply &)> ReplyHandlerCB;
66 static void IgnoreReplyHandler(TorControlConnection &, const TorControlReply &);
67
68 /** Create a new TorControlConnection.
69 */
70 explicit TorControlConnection(struct event_base *base);
71 ~TorControlConnection();
72
73 /**
74 * Connect to a Tor control port.
75 * tor_control_center is address of the form host:port.
76 * connected is the handler that is called when connection is successfully established.
77 * disconnected is a handler that is called when the connection is broken.
78 * Return true on success.
79 */
80 bool Connect(const std::string& tor_control_center, const ConnectionCB& connected, const ConnectionCB& disconnected);
81
82 /**
83 * Disconnect from Tor control port.
84 */
85 void Disconnect();
86
87 /** Send a command, register a handler for the reply.
88 * A trailing CRLF is automatically added.
89 * Return true on success.
90 */
91 bool Command(const std::string &cmd, const ReplyHandlerCB& reply_handler = IgnoreReplyHandler);
92
93 private:
94 /** Callback when ready for use */
95 std::function<void(TorControlConnection&)> connected;
96 /** Callback when connection lost */
97 std::function<void(TorControlConnection&)> disconnected;
98 /** Libevent event base */
99 struct event_base *base;
100 /** Connection to control socket */
101 struct bufferevent* b_conn{nullptr};
102 /** Message being received */
103 TorControlReply message;
104 /** Response handlers */
105 std::deque<ReplyHandlerCB> reply_handlers;
106
107 /** Libevent handlers: internal */
108 static void readcb(struct bufferevent *bev, void *ctx);
109 static void eventcb(struct bufferevent *bev, short what, void *ctx);
110 };
111
112 /****** Limenka specific TorController implementation ********/
113
114 /** Controller that connects to Tor control socket, authenticate, then create
115 * and maintain an ephemeral onion service.
116 */
117 class TorController
118 {
119 public:
120 TorController(struct event_base* base, const std::string& tor_control_center, const CService& target, const std::string& execute);
121 TorController() : conn{nullptr} {
122 // Used for testing only.
123 }
124 ~TorController();
125
126 /** Get name of file to store private key in */
127 fs::path GetPrivateKeyFile();
128
129 /** Reconnect, after getting disconnected */
130 void Reconnect();
131 private:
132 struct event_base* base;
133 const std::string m_connect_tor_control_center;
134 std::string m_current_tor_control_center;
135 TorControlConnection conn;
136 std::string private_key;
137 std::string service_id;
138 bool m_try_exec{true};
139 bool reconnect;
140 struct event *reconnect_ev = nullptr;
141 float reconnect_timeout;
142 std::string m_execute{DEFAULT_TOR_EXECUTE};
143 #ifdef ENABLE_TOR_SUBPROCESS
144 subprocess::Popen *m_process{nullptr};
145 #endif
146 CService service;
147 const CService m_target;
148 /** Cookie for SAFECOOKIE auth */
149 std::vector<uint8_t> cookie;
150 /** ClientNonce for SAFECOOKIE auth */
151 std::vector<uint8_t> clientNonce;
152
153 public:
154 /** Callback for GETINFO net/listeners/socks result */
155 void get_socks_cb(TorControlConnection& conn, const TorControlReply& reply);
156 /** Callback for ADD_ONION result */
157 void add_onion_cb(TorControlConnection& conn, const TorControlReply& reply, bool pow_was_enabled);
158 /** Callback for AUTHENTICATE result */
159 void auth_cb(TorControlConnection& conn, const TorControlReply& reply);
160 /** Callback for AUTHCHALLENGE result */
161 void authchallenge_cb(TorControlConnection& conn, const TorControlReply& reply);
162 /** Callback for PROTOCOLINFO result */
163 void protocolinfo_cb(TorControlConnection& conn, const TorControlReply& reply);
164 /** Callback after successful connection */
165 void connected_cb(TorControlConnection& conn);
166 /** Callback after connection lost or failed connection attempt */
167 void disconnected_cb(TorControlConnection& conn);
168
169 /** Callback for reconnect timer */
170 static void reconnect_cb(evutil_socket_t fd, short what, void *arg);
171
172 std::string LaunchTor();
173 };
174
175 #endif // LIMENKA_TORCONTROL_H
176