diff options
Diffstat (limited to 'src/sock.rs')
-rw-r--r-- | src/sock.rs | 165 |
1 files changed, 47 insertions, 118 deletions
diff --git a/src/sock.rs b/src/sock.rs index 42a5d90..15311db 100644 --- a/src/sock.rs +++ b/src/sock.rs @@ -137,18 +137,6 @@ impl ReplyLine { } -/// Authentication method and credentials used to authenticate to the Tor control socket. -/// The `COOKIE` and `SAFECOOKIE` authentication methods are not supported. -pub enum Auth { - /// Authenticate without credentials. - Null, - /// Authenticate with a password, this corresponds to the 'HashedControlPassword' configuration - /// option in torrc. See `genpass()` and `hashpass()` for functions to generate a suitable - /// password and `HashedControlPassword` combination. - HashedPassword(String) -} - - /// Status event of Tor's bootstrapping process. #[derive(Debug)] pub struct BootstrapStatus { @@ -294,28 +282,24 @@ impl<'a> QuotedString<'a> { -/// Tor control socket. +/// Tor process and control socket. Use `spawn()` to create an instance of this struct. /// -/// A socket is created either by connecting to an existing Tor process using `Sock::connect()`, or -/// by calling `spawn()` to spawn a new Tor process. -/// -/// A socket can be used from multiple threads, with the following limitations: -/// - It is possible to run commands simultaneously from multiple threads, but the order in which -/// they are executed is, of course, not deterministic. Running a `setconf()` and `getconf()` on -/// the same keys at the same time may not give reliable results. -/// - You should only read asynchronous events from a single thread at a time. The API allows for +/// A Tor struct can be used from multiple threads, with the following limitations: +/// - It is possible to call methods simultaneously from multiple threads, but the order in which +/// they are executed is, of course, not deterministic. +/// - You should only call `read_event()` from a single thread at a time. The API allows for /// multiple threads to read events, but an event will only be received by one thread. -pub struct Sock { +pub struct Tor { sock: TcpStream, - // Should be set when tor is running as a child process and this socket has "ownership" of the process. - child: Option<Child>, + child: Child, + socks: SocketAddr, // Read buffer - only one thread can be reading from the socket at a time. rdbuf: Mutex<Vec<u8>>, // Read queue - if multiple threads are interested in reading from the socket, then this // structure is used to coordinate which thread gets to read from the socket. If that thread // happens to read a reply that it isn't interested in (e.g. it expected a sync reply and got // an async one), then that reply is pushed to the queue. - queue: Mutex<SockQueue>, + queue: Mutex<TorQueue>, queuecv: Condvar, // Write mutex - this ensures only a single command can be sent at a time. This mutex is // currently also held while reading the response of a command, because the read queue itself @@ -325,28 +309,26 @@ pub struct Sock { writer: Mutex<bool>, } -struct SockQueue { +struct TorQueue { queue: Vec<Reply>, reading: bool, } -impl Drop for Sock { +impl Drop for Tor { fn drop(&mut self) { // We have to wait() for the child to shut down, otherwise we get a zombie process. This is // a quick-and-dirty approach which doesn't really give Tor the time to do a clean // shutdown. In fact, we kill the process before even shutting down the socket. // A cleaner "shut down socket and wait a bit" approach should probably be implemented as a // separate method. - if let Some(mut c) = self.child.take() { - c.kill().is_ok(); - c.wait().is_ok(); - } + self.child.kill().is_ok(); + self.child.wait().is_ok(); } } -impl Sock { +impl Tor { fn read_line(buf: &mut Vec<u8>, sock: &TcpStream) -> Result<ReplyLine> { // This buffer handling isn't very efficient, but that's probably fine. loop { @@ -425,60 +407,30 @@ impl Sock { } } - pub(crate) fn connect_child(s: &SocketAddr, auth: &Auth, child: Option<Child>) -> Result<Sock> { - let sock = Sock { + pub(crate) fn connect_child(s: &SocketAddr, pass: &str, child: Child) -> Result<Tor> { + let mut tor = Tor { sock: TcpStream::connect(s)?, child: child, + socks: "0.0.0.0:0".parse().unwrap(), rdbuf: Mutex::new(Vec::new()), - queue: Mutex::new(SockQueue { + queue: Mutex::new(TorQueue { queue: Vec::new(), reading: false, }), queuecv: Condvar::new(), writer: Mutex::new(true), }; + tor.cmd(format!("AUTHENTICATE {}\r\n", QuotedString(pass)))?; + tor.cmd("TAKEOWNERSHIP\r\n")?; + tor.resetconf(&[("__OwningControllerProcess", None)])?; - match auth { - Auth::Null => sock.cmd("AUTHENTICATE\r\n")?, - Auth::HashedPassword(s) => sock.cmd(format!("AUTHENTICATE {}\r\n", QuotedString(s)))? - }; - Ok(sock) - } - - /// Connect to a running Tor process and authenticate using the given authentication method. - pub fn connect(s: &SocketAddr, auth: &Auth) -> Result<Sock> { - Self::connect_child(s, auth, None) - } - - /// Send a TAKEOWNERSHIP command. If this command has been acknowledged, the Tor process - /// will automatically shut down when this control socket is closed. - /// - /// Ownership is already implied if this socket has been created with the `spawn()` function, - /// so in that case you do not have to call this method. - pub fn takeownership(&self) -> Result<()> { - self.cmd("TAKEOWNERSHIP\r\n").map(|_|()) - } - - /// Send a DROPOWNERSHIP command. This reverses any earlier TAKEOWNERSHIP command and tells - /// the Tor process to keep running even after this control socket is closed. - /// - /// Note that, if this socket has been created with the `spawn()` function, then the Tor - /// process will still be killed when the socket is dropped. This method is only useful when - /// connected to an external Tor process. - pub fn dropownership(&self) -> Result<()> { - self.cmd("DROPOWNERSHIP\r\n").map(|_|()) - } + let ret = tor.getinfo(&["net/listeners/socks"])?; + let mut buf = &ret[0].1[..]; + tor.socks = QuotedString::parse(&mut buf)?.parse().map_err(|_| err!(Parse))?; - /// Send a QUIT command. This tells Tor to close this control socket. Any further commands will - /// likely result in an error. - /// - /// The Tor process will exit if this control socket has called `takeownership()` before. - // TODO: Get rid of this function and implement a more thorough shutdown() instead? - pub fn quit(&self) -> Result<()> { - self.cmd("QUIT\r\n").map(|_|()) + Ok(tor) } - // XXX: This IntoIterator works with &[..] and &vec![..]. Haven't tested HashMap/BTreeMap yet, // but I suspect their signature doesn't match. fn setresetconf<'a,T>(&self, mut msg: String, settings: T) -> Result<()> @@ -497,56 +449,27 @@ impl Sock { self.cmd(msg).map(|_|()) } - /// Send a SETCONF command. - pub fn setconf<'a,T>(&self, settings: T) -> Result<()> + /* Send a SETCONF command. + fn setconf<'a,T>(&self, settings: T) -> Result<()> where T: IntoIterator<Item = &'a (&'a str, Option<&'a str>)> { self.setresetconf("SETCONF".to_string(), settings) - } + }*/ - /// Send a RESETCONF command. - pub fn resetconf<'a,T>(&self, settings: T) -> Result<()> + // Send a RESETCONF command. + fn resetconf<'a,T>(&self, settings: T) -> Result<()> where T: IntoIterator<Item = &'a (&'a str, Option<&'a str>)> { self.setresetconf("RESETCONF".to_string(), settings) } - /// Returns the configuration variables for the requested keys. The values are returned in the - /// order they were requested. Some keys may return multiple values, these will be listed - /// multiple times. Some keys may return `None` when are unset or default. An error is - /// returned if any of the requested keys does not exist. - /// - /// This method corresponds to the GETCONF command. - pub fn getconf<'a,T: IntoIterator<Item = &'a &'a str>>(&self, keys: T) -> Result<Vec<(String, Option<String>)>> { - let mut msg = "GETCONF".to_string(); - for k in keys { - is_keyword(k)?; - msg.push(' '); - msg.push_str(k); - } - msg.push_str("\r\n"); - - let mut res = Vec::new(); - for line in self.cmd(msg)? { - if let Some(is) = line.text.find('=') { - let mut val = &line.text[is+1..]; - let val = if val.starts_with('"') { QuotedString::parse(&mut val)? } else { val.to_string() }; - res.push(( (&line.text[..is]).to_string(), Some(val) )); - } else { - res.push((line.text, None)); - } - } - Ok(res) - } - - /// Similar to the `getconf()` method, except this is used to get run-time variables that are - /// not saved in the configuration. An error is returned if any of the requested keys does not - /// exist. - /// - /// Corresponds to the GETINFO command. Refer to the GETINFO documentation in the [tor-control - /// specification](https://gitweb.torproject.org/torspec.git/blob/control-spec.txt) for the - /// list of accepted keys. - pub fn getinfo<'a,T: IntoIterator<Item = &'a &'a str>>(&self, keys: T) -> Result<Vec<(String, String)>> { + // Get run-time Tor variables. An error is returned if any of the requested keys does not + // exist. + // + // Corresponds to the GETINFO command. Refer to the GETINFO documentation in the [tor-control + // specification](https://gitweb.torproject.org/torspec.git/blob/control-spec.txt) for the + // list of accepted keys. + fn getinfo<'a,T: IntoIterator<Item = &'a &'a str>>(&self, keys: T) -> Result<Vec<(String, String)>> { let mut msg = "GETINFO".to_string(); for k in keys { is_keyword(k)?; @@ -574,8 +497,12 @@ impl Sock { Ok(res) } - /// Returns the latest bootstrap status. This is a convenience wrapper around - /// `getinfo(&["status/bootstrap-phase"])`. + /// Returns the address of the Tor SOCKS proxy. + pub fn socks_addr(&self) -> SocketAddr { + self.socks + } + + /// Returns the most recent bootstrap status. pub fn bootstrap_status(&self) -> Result<Box<BootstrapStatus>> { let ret = self.getinfo(&["status/bootstrap-phase"])?; let mut buf = &ret[0].1[..]; @@ -584,12 +511,14 @@ impl Sock { BootstrapStatus::parse(sev, &mut buf) } - pub fn setevents(&self, events: EventList) -> Result<()> { + /// Configure which events to receive with `read_events()`. + pub fn set_events(&self, events: EventList) -> Result<()> { self.cmd(events.cmd()).map(|_|()) } - /// Read an event from the socket. This method blocks until an event has been received. + /// Read the next event from the socket. This method blocks until an event has been received. // TODO: Differentiate between fatal and temporary errors. + // TODO: Some way to wake up a blocked read_event (e.g. to shut down the socket). pub fn read_event(&self) -> Result<Event> { loop { let mut ret = self.get_reply(true)?; |