summaryrefslogtreecommitdiff
path: root/src/sock.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/sock.rs')
-rw-r--r--src/sock.rs165
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)?;