1use std::collections::HashMap;
2use std::io::prelude::*;
3use std::fmt::{Debug, Write as FmtWrite};
4use std::io::{BufReader, Write};
5use std::net::{TcpStream, ToSocketAddrs};
6use std::str::FromStr;
7
8use nom::IResult;
9
10use crate::parsers;
11
12mod error;
13use error::Error;
14
15const DEFAULT_API: &'static str = "127.0.0.1:9051";
16
17#[derive(Debug, PartialEq, Eq)]
18pub struct ProtocolInfo {
19 pub auth_methods: Vec<AuthMethod>,
20 pub version: String,
21 pub cookiefile: String,
22}
23
24#[derive(Debug, PartialEq, Eq)]
25pub enum AuthMethod {
26 Cookie,
27 SafeCookie,
28 HashedPassword,
29}
30
31impl FromStr for AuthMethod {
32 type Err = Error;
33
34 fn from_str(s: &str) -> Result<Self, Self::Err> {
35 match s {
36 "COOKIE" => Ok(AuthMethod::Cookie),
37 "SAFECOOKIE" => Ok(AuthMethod::SafeCookie),
38 "HASHEDPASSWORD" => Ok(AuthMethod::HashedPassword),
39 _ => Err(Error::UnknownAuthMethod),
40 }
41 }
42}
43
44pub enum Signal {
45 Reload,
46 Shutdown,
47 Dump,
48 Debug,
49 Halt,
50 ClearDNSCache,
51 Newnym,
52 Heartbeat,
53 Dormant,
54 Active,
55}
56
57impl ToString for Signal {
58 fn to_string(&self) -> String {
59 match self {
60 Signal::Reload => "RELOAD".to_string(),
61 Signal::Shutdown => "SHUTDOWN".to_string(),
62 Signal::Dump => "DUMP".to_string(),
63 Signal::Debug => "DEBUG".to_string(),
64 Signal::Halt => "HALT".to_string(),
65 Signal::ClearDNSCache => "CLEARDNSCACHE".to_string(),
66 Signal::Newnym => "NEWNYM".to_string(),
67 Signal::Heartbeat => "HEARTBEAT".to_string(),
68 Signal::Dormant => "DORMANT".to_string(),
69 Signal::Active => "ACTIVE".to_string(),
70 }
71 }
72}
73
74pub enum AddOnionFlag {
75 DiscardPK,
76 Detach,
77 BasicAuth,
78}
79
80#[derive(Debug, PartialEq, Eq)]
81pub enum KeyType {
82 Best,
83 RSA1024,
84 ED25519V3,
85}
86
87impl Default for KeyType {
88 fn default() -> Self {
89 KeyType::Best
90 }
91}
92
93impl ToString for KeyType {
94 fn to_string(&self) -> String {
95 match &self {
96 KeyType::ED25519V3 => String::from("ED25519-V3"),
97 KeyType::RSA1024 => String::from("RSA1024"),
98 KeyType::Best => String::from("BEST"),
99 }
100 }
101}
102
103impl FromStr for KeyType {
104 type Err = Error;
105
106 fn from_str(s: &str) -> Result<Self, Self::Err> {
107 return match s {
108 "ED25519-V3" => Ok(KeyType::ED25519V3),
109 "RSA1024" => Ok(KeyType::RSA1024),
110 _ => Err(Error::UnknownKeyType),
111 };
112 }
113}
114
115#[derive(Debug, PartialEq, Eq)]
116pub struct ServiceID(String);
117
118impl From<&str> for ServiceID {
119 fn from(service_id: &str) -> Self {
120 ServiceID(service_id.to_string())
121 }
122}
123
124impl From<String> for ServiceID {
125 fn from(service_id: String) -> Self {
126 ServiceID(service_id)
127 }
128}
129
130pub struct HiddenService {
131 pub service_id: ServiceID,
132 pub key_type: KeyType,
133 pub private_key: String,
134}
135
136pub struct TorController {
137 conn: TcpStream,
138}
139
140impl TorController {
141 fn send<F, T>(&mut self, msg: String, reply_parser: F) -> Result<T, Error>
142 where
143 T: Debug,
144 F: Fn(&str) -> IResult<&str, T>,
145 {
146 debug!("-> {}", &msg);
147 let bytes = format!("{}\r\n", msg).into_bytes();
148 self.conn.write_all(&bytes)?;
149
150 let mut reader = BufReader::new(&self.conn);
151 let mut buffer = String::new();
152 loop {
153 let mut line = String::new();
154 reader.read_line(&mut line)?;
155 buffer.push_str(&line);
156 if parsers::is_final_line(&line) {
157 break;
158 }
159 }
160
161 debug!("<- {}", &buffer);
162 let comparison = "250-PROTOCOLINFO 1\n250-AUTH METHODS=COOKIE,SAFECOOKIE COOKIEFILE=\"/var/run/tor/control.authcookie\"\n250-VERSION Tor=\"0.1.2.3\"\n250 OK".to_string();
163 debug!("<- {}", &comparison);
164
165 return match reply_parser(&buffer) {
166 Ok((_, response)) => Ok(response),
167 Err(_) => Err(Error::InternalError),
168 };
169 }
170
171 fn authenticate(&mut self, password: String) -> Result<(), Error> {
172 let authentication_string = format!("AUTHENTICATE \"{}\"", password.replace("\"", "\\\""));
173 self.send(authentication_string, parsers::is_ok)?;
174
175 Ok(())
176 }
177
178 pub fn protocol_info(&mut self) -> Result<ProtocolInfo, Error> {
185 let response = self.send(String::from("PROTOCOLINFO"), parsers::protocol_info)?;
186
187 Ok(response)
188 }
189
190 fn connect<A: ToSocketAddrs>(addr: A) -> Result<TorController, Error> {
191 let conn = TcpStream::connect(addr)?;
192 let controller = Self { conn };
193 Ok(controller)
194 }
195
196 pub fn connect_default_with_authcookie() -> Result<TorController, Error> {
197 TorController::connect_with_authcookie(DEFAULT_API)
198 }
199
200 pub fn connect_with_authcookie<A: ToSocketAddrs>(addr: A) -> Result<TorController, Error> {
201 let mut controller = TorController::connect(addr)?;
202 let protocol_info = controller.protocol_info()?;
203
204 let contents = std::fs::read(&protocol_info.cookiefile).unwrap();
205 let mut cookie_string = String::new();
206 contents.into_iter().for_each(|b| write!(cookie_string, "{:02X}", b).unwrap());
207
208 let msg = format!("AUTHENTICATE {}", cookie_string);
209 controller.send(msg, parsers::is_ok)?;
210
211 Ok(controller)
212 }
213
214 pub fn connect_default_with_password(password: String) -> Result<TorController, Error> {
215 TorController::connect_with_password(DEFAULT_API, password)
216 }
217
218 pub fn connect_with_password<A: ToSocketAddrs>(
219 addr: A,
220 password: String,
221 ) -> Result<TorController, Error> {
222 let mut controller = TorController::connect(addr)?;
223
224 let protocol_info = controller.protocol_info()?;
225
226 if !protocol_info
227 .auth_methods
228 .contains(&AuthMethod::HashedPassword)
229 {
230 return Err(Error::AuthMethodDisabled);
231 }
232
233 controller.authenticate(password)?;
234
235 Ok(controller)
236 }
237
238 pub fn add_onion(&mut self, key_type: KeyType, port: u16) -> Result<HiddenService, Error> {
239 let add_onion_command = format!("ADD_ONION NEW:{} port={}", key_type.to_string(), port);
240 let (service_id, key) = self.send(add_onion_command, parsers::add_onion)?;
241 let (key_type, private_key) = key.unwrap();
242 let hidden_service = HiddenService {
243 service_id,
244 key_type,
245 private_key,
246 };
247
248 Ok(hidden_service)
249 }
250
251 pub fn add_onion_default(&mut self, port: u16) -> Result<HiddenService, Error> {
252 self.add_onion(KeyType::default(), port)
253 }
254
255 pub fn add_onion_with_key(
256 &mut self,
257 key_type: KeyType,
258 key: String,
259 port: u16,
260 ) -> Result<HiddenService, Error> {
261 let add_onion_command = format!("ADD_ONION {}:{} port={}", key_type.to_string(), key, port);
262 let (service_id, optional_key) = self.send(add_onion_command, parsers::add_onion)?;
263 let (key_type, private_key) = optional_key.unwrap();
264 let hidden_service = HiddenService {
265 service_id,
266 private_key,
267 key_type,
268 };
269
270 Ok(hidden_service)
271 }
272
273 pub fn delete_onion(&mut self, service_id: ServiceID) -> Result<(), Error> {
275 let del_onion_command = format!("DEL_ONION {}", service_id.0);
276 self.send(del_onion_command, parsers::is_ok)?;
277
278 Ok(())
279 }
280
281 pub fn get_info(&mut self, info_fields: Vec<&str>) -> Result<HashMap<String, String>, Error> {
282 let get_info_command = format!("GETINFO {}", info_fields.join(" "));
283 let response = self.send(get_info_command, parsers::get_info)?;
284
285 Ok(response)
286 }
287
288 pub fn signal(&mut self, signal: Signal) -> Result<(), Error> {
289 let signal_command = format!("SIGNAL {}", signal.to_string());
290 self.send(signal_command, parsers::is_ok)?;
291
292 Ok(())
293 }
294}
295
296#[cfg(test)]
297mod tests {
298 use super::*;
299
300 fn init() {
301 let _ = env_logger::builder().is_test(true).try_init();
302 }
303
304 fn get_controller() -> TorController {
305 TorController::connect_default_with_authcookie().unwrap()
306 }
307
308 #[test]
309 fn establish_connection() {
310 init();
311
312 let password =
313 std::env::var("TOR_CONTROLLER_PASSWORD").expect("TOR_CONTROLLER_PASSWORD is not set");
314 let result = TorController::connect_default_with_password(password);
315 assert!(result.is_ok());
316 }
317
318 #[test]
319 fn create_onion() {
320 let mut controller = get_controller();
321 let hidden_service = controller.add_onion(KeyType::Best, 80);
322 assert!(hidden_service.is_ok());
323 }
324
325 #[test]
326 fn delete_onion() {
327 let mut controller = get_controller();
328 let hidden_service = controller
329 .add_onion_default(80)
330 .expect("Error adding onion");
331 assert!(controller.delete_onion(hidden_service.service_id).is_ok());
332 }
333
334 #[test]
335 fn delete_fake_onion() {
336 let mut controller = get_controller();
337 let service_id = ServiceID::from("does not exist".to_string());
338 assert!(controller.delete_onion(service_id).is_err());
339 }
340
341 #[test]
342 fn get_info() {
343 let mut controller = get_controller();
344 let info = controller.get_info(vec!["version"]).unwrap();
345 assert!(info.contains_key("version"));
346 }
347}