Skip to content

Commit c088faf

Browse files
tor-interface: use TcpOrUnixStream and SocketAddrOrUnixSocketAddr, allow using Legacy Tor over unix-domain sockets
Requires: sfackler/rust-socks#22
1 parent d3329ce commit c088faf

File tree

11 files changed

+103
-73
lines changed

11 files changed

+103
-73
lines changed

source/gosling/Cargo.lock

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

source/gosling/crates/cgosling/src/tor_provider.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -584,8 +584,8 @@ pub unsafe extern "C" fn gosling_tor_provider_config_new_system_legacy_client_co
584584
let tor_control_passwd = std::str::from_utf8(tor_control_passwd)?.to_string();
585585

586586
let tor_config = LegacyTorClientConfig::SystemTor {
587-
tor_socks_addr,
588-
tor_control_addr,
587+
tor_socks_addr: tor_socks_addr.into(),
588+
tor_control_addr: tor_control_addr.into(),
589589
tor_control_auth: Some(TorAuth::Password(tor_control_passwd)),
590590
};
591591

source/gosling/crates/tor-interface/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ regex = "1.9"
2323
sha1 = "0.10"
2424
sha3 = "0.10"
2525
signature = "1.5"
26-
socks = "0.3"
26+
# socks = "0.3"
27+
# socks = { path = "../../../../../rust-socks" }
28+
socks = { git = "https://github.com/nabijaczleweli/rust-socks" }
2729
static_assertions = "1.1"
2830
thiserror = "1.0"
2931
tokio = { version = "1", features = ["macros"], optional = true }

source/gosling/crates/tor-interface/src/arti_tor_client.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ impl ArtiTorClient {
125125
}
126126

127127
impl TorProvider for ArtiTorClient {
128-
type Stream = TcpOnionStream;
128+
type Stream = TcpOrUnixOnionStream;
129129
type Listener = TcpOnionListener;
130130

131131
fn update(&mut self) -> Result<Vec<TorEvent>, tor_provider::Error> {
@@ -226,8 +226,8 @@ impl TorProvider for ArtiTorClient {
226226
let stream = self.rpc_conn.open_stream(None, (host.as_str(), port), isolation)
227227
.map_err(Error::ArtiOpenStreamFailed)?;
228228

229-
Ok(TcpOnionStream {
230-
stream,
229+
Ok(TcpOrUnixOnionStream {
230+
stream: stream.into(),
231231
local_addr: None,
232232
peer_addr: Some(target),
233233
})

source/gosling/crates/tor-interface/src/legacy_tor_client.rs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::sync::{atomic, Arc};
1010
use std::time::Duration;
1111

1212
// extern crates
13-
use socks::Socks5Stream;
13+
use socks::{SocketAddrOrUnixSocketAddr, Socks5Stream};
1414

1515
// internal crates
1616
use crate::censorship_circumvention::*;
@@ -166,8 +166,8 @@ pub enum LegacyTorClientConfig {
166166
bridge_lines: Option<Vec<BridgeLine>>,
167167
},
168168
SystemTor {
169-
tor_socks_addr: SocketAddr,
170-
tor_control_addr: SocketAddr,
169+
tor_socks_addr: SocketAddrOrUnixSocketAddr,
170+
tor_control_addr: SocketAddrOrUnixSocketAddr,
171171
tor_control_auth: Option<TorAuth>,
172172
},
173173
}
@@ -193,7 +193,7 @@ pub struct LegacyTorClient {
193193
version: LegacyTorVersion,
194194
controller: LegacyTorController,
195195
bootstrapped: bool,
196-
socks_listener: Option<SocketAddr>,
196+
socks_listener: Option<SocketAddrOrUnixSocketAddr>,
197197
// list of open onion services and their is_active flag
198198
onion_services: Vec<(V3OnionServiceId, Arc<atomic::AtomicBool>)>,
199199
// our list of circuit tokens for the tor daemon
@@ -232,9 +232,8 @@ impl LegacyTorClient {
232232
tor_control_auth,
233233
} => {
234234
// open a control stream
235-
let control_stream =
236-
LegacyControlStream::new(&tor_control_addr, Duration::from_millis(16))
237-
.map_err(Error::LegacyControlStreamCreationFailed)?;
235+
let control_stream = LegacyControlStream::new(tor_control_addr, Duration::from_millis(16))
236+
.map_err(Error::LegacyControlStreamCreationFailed)?;
238237

239238
// create a controler
240239
let controller = LegacyTorController::new(control_stream)
@@ -244,7 +243,7 @@ impl LegacyTorClient {
244243
None,
245244
controller,
246245
tor_control_auth.take(),
247-
Some(tor_socks_addr.clone()),
246+
Some(tor_socks_addr.clone().into()),
248247
)
249248
}
250249
};
@@ -469,7 +468,7 @@ impl LegacyTorClient {
469468
}
470469

471470
impl TorProvider for LegacyTorClient {
472-
type Stream = TcpOnionStream;
471+
type Stream = TcpOrUnixOnionStream;
473472
type Listener = TcpOnionListener;
474473

475474
fn update(&mut self) -> Result<Vec<TorEvent>, tor_provider::Error> {
@@ -603,10 +602,10 @@ impl TorProvider for LegacyTorClient {
603602
if listeners.is_empty() {
604603
return Err(Error::NoSocksListenersFound())?;
605604
}
606-
self.socks_listener = Some(listeners.swap_remove(0));
605+
self.socks_listener = Some(listeners.swap_remove(0).into());
607606
}
608607

609-
let socks_listener = match self.socks_listener {
608+
let socks_listener = match self.socks_listener.as_ref() {
610609
Some(socks_listener) => socks_listener,
611610
None => unreachable!(),
612611
};
@@ -625,10 +624,10 @@ impl TorProvider for LegacyTorClient {
625624

626625
// readwrite stream
627626
let stream = match &circuit {
628-
None => Socks5Stream::connect(socks_listener, socks_target),
627+
None => Socks5Stream::connect_either(socks_listener, socks_target),
629628
Some(circuit) => {
630629
if let Some(circuit) = self.circuit_tokens.get(circuit) {
631-
Socks5Stream::connect_with_password(
630+
Socks5Stream::connect_either_with_password(
632631
socks_listener,
633632
socks_target,
634633
&circuit.username,
@@ -641,7 +640,7 @@ impl TorProvider for LegacyTorClient {
641640
}
642641
.map_err(Error::Socks5ConnectionFailed)?;
643642

644-
Ok(TcpOnionStream {
643+
Ok(TcpOrUnixOnionStream {
645644
stream: stream.into_inner(),
646645
local_addr: None,
647646
peer_addr: Some(target),

source/gosling/crates/tor-interface/src/legacy_tor_control_stream.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
// standard
22
use std::collections::VecDeque;
33
use std::default::Default;
4-
use std::io::{ErrorKind, IoSlice, Read, Write};
5-
use std::net::{SocketAddr, TcpStream};
4+
use std::io::{ErrorKind, Read, Write, IoSlice};
65
use std::option::Option;
76
use std::string::ToString;
87
use std::time::Duration;
98

109
// extern crates
1110
use regex::Regex;
11+
use socks::{SocketAddrOrUnixSocketAddr, TcpOrUnixStream};
1212

1313
#[derive(thiserror::Error, Debug)]
1414
pub enum Error {
@@ -41,7 +41,7 @@ pub enum Error {
4141
}
4242

4343
pub(crate) struct LegacyControlStream {
44-
stream: TcpStream,
44+
stream: TcpOrUnixStream,
4545
closed_by_remote: bool,
4646
pending_data: Vec<u8>,
4747
pending_lines: VecDeque<String>,
@@ -60,12 +60,12 @@ pub(crate) struct Reply {
6060
}
6161

6262
impl LegacyControlStream {
63-
pub fn new(addr: &SocketAddr, read_timeout: Duration) -> Result<LegacyControlStream, Error> {
63+
pub fn new<T: Into<SocketAddrOrUnixSocketAddr>>(addr: T, read_timeout: Duration) -> Result<LegacyControlStream, Error> {
6464
if read_timeout.is_zero() {
6565
return Err(Error::ReadTimeoutZero());
6666
}
6767

68-
let stream = TcpStream::connect(addr).map_err(Error::CreationFailed)?;
68+
let stream = TcpOrUnixStream::connect(addr).map_err(Error::CreationFailed)?;
6969
stream
7070
.set_read_timeout(Some(read_timeout))
7171
.map_err(Error::ConfigurationFailed)?;

source/gosling/crates/tor-interface/src/legacy_tor_controller.rs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -943,10 +943,22 @@ impl LegacyTorController {
943943
#[test]
944944
#[serial]
945945
fn test_tor_controller() -> anyhow::Result<()> {
946+
test_tor_controller_impl(false)
947+
}
948+
#[test]
949+
#[serial]
950+
#[cfg(unix)]
951+
fn test_tor_controller_unix() -> anyhow::Result<()> {
952+
test_tor_controller_impl(true)
953+
}
954+
#[cfg(test)]
955+
fn test_tor_controller_impl(unix: bool) -> anyhow::Result<()> {
956+
use std::borrow::Cow;
957+
946958
let tor_path = which::which(format!("tor{}", std::env::consts::EXE_SUFFIX))?;
947959
let mut data_path = std::env::temp_dir();
948960
data_path.push("test_tor_controller");
949-
let tor_process = LegacyTorProcess::new(&tor_path, &data_path)?;
961+
let tor_process = LegacyTorProcess::new_unix(&tor_path, &data_path, unix)?;
950962

951963
// create a scope to ensure tor_controller is dropped
952964
{
@@ -956,11 +968,11 @@ fn test_tor_controller() -> anyhow::Result<()> {
956968
// create a tor controller and send authentication command
957969
let mut tor_controller = LegacyTorController::new(control_stream)?;
958970
tor_controller.authenticate_cmd(tor_process.get_password())?;
959-
assert!(
971+
assert_eq!(
960972
tor_controller
961973
.authenticate_cmd("invalid password")?
962-
.status_code
963-
== 515u32
974+
.status_code,
975+
515u32
964976
);
965977

966978
// tor controller should have shutdown the connection after failed authentication
@@ -991,7 +1003,7 @@ fn test_tor_controller() -> anyhow::Result<()> {
9911003
"DisableNetwork" => "1",
9921004
_ => panic!("unexpected returned key: {}", key),
9931005
};
994-
assert!(value == expected);
1006+
assert_eq!(value, expected);
9951007
}
9961008

9971009
let vals = tor_controller.getinfo(&["version", "config-file", "config-text"])?;
@@ -1002,14 +1014,15 @@ fn test_tor_controller() -> anyhow::Result<()> {
10021014
for (key, value) in vals.iter() {
10031015
match key.as_str() {
10041016
"version" => assert!(Regex::new(r"\d+\.\d+\.\d+\.\d+")?.is_match(&value)),
1005-
"config-file" => assert!(Path::new(&value) == expected_torrc_path),
1006-
"config-text" => assert!(
1007-
value.to_string()
1008-
== format!(
1009-
"\nControlPort auto\nControlPortWriteToFile {}\nDataDirectory {}",
1010-
expected_control_port_path.display(),
1011-
data_path.display()
1012-
)
1017+
"config-file" => assert_eq!(Path::new(&value), expected_torrc_path),
1018+
"config-text" => assert_eq!(
1019+
value.to_string(),
1020+
format!(
1021+
"\nControlPort {}\nControlPortWriteToFile {}\nDataDirectory {}",
1022+
if unix { Cow::Owned(format!("unix:{}", expected_control_port_path.with_file_name("control.sock").display())) } else { Cow::Borrowed("auto") },
1023+
expected_control_port_path.display(),
1024+
data_path.display()
1025+
)
10131026
),
10141027
_ => panic!("unexpected returned key: {}", key),
10151028
}

source/gosling/crates/tor-interface/src/legacy_tor_process.rs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// standard
2+
use std::borrow::Cow;
23
use std::default::Default;
34
use std::fs;
45
use std::fs::File;
@@ -12,11 +13,14 @@ use std::str::FromStr;
1213
use std::string::ToString;
1314
use std::sync::{Arc, Mutex};
1415
use std::time::{Duration, Instant};
16+
#[cfg(unix)]
17+
use std::os::unix::net::SocketAddr as UnixSocketAddr;
1518

1619
// extern crates
1720
use data_encoding::HEXUPPER;
1821
use rand::RngCore;
1922
use sha1::{Digest, Sha1};
23+
use socks::SocketAddrOrUnixSocketAddr;
2024

2125
// internal crates
2226
use crate::tor_crypto::generate_password;
@@ -69,7 +73,7 @@ pub enum Error {
6973
StdoutReadThreadSpawnFailed(#[source] std::io::Error),
7074
}
7175

72-
fn read_control_port_file(control_port_file: &Path) -> Result<SocketAddr, Error> {
76+
fn read_control_port_file(control_port_file: &Path) -> Result<SocketAddrOrUnixSocketAddr, Error> {
7377
// open file
7478
let mut file = File::open(control_port_file).map_err(Error::ControlPortFileReadFailed)?;
7579

@@ -90,7 +94,14 @@ fn read_control_port_file(control_port_file: &Path) -> Result<SocketAddr, Error>
9094
if contents.starts_with("PORT=") {
9195
let addr_string = &contents.trim_end()["PORT=".len()..];
9296
if let Ok(addr) = SocketAddr::from_str(addr_string) {
93-
return Ok(addr);
97+
return Ok(addr.into());
98+
}
99+
}
100+
#[cfg(unix)]
101+
if contents.starts_with("UNIX_PORT=") {
102+
let addr_string = &contents.trim_end()["UNIX_PORT=".len()..];
103+
if let Ok(addr) = UnixSocketAddr::from_pathname(addr_string) {
104+
return Ok(addr.into());
94105
}
95106
}
96107
Err(Error::ControlPortFileContentsInvalid(format!(
@@ -101,7 +112,7 @@ fn read_control_port_file(control_port_file: &Path) -> Result<SocketAddr, Error>
101112

102113
// Encapsulates the tor daemon process
103114
pub(crate) struct LegacyTorProcess {
104-
control_addr: SocketAddr,
115+
control_addr: SocketAddrOrUnixSocketAddr,
105116
process: Child,
106117
password: String,
107118
// stdout data
@@ -162,7 +173,7 @@ impl LegacyTorProcess {
162173
Self::hash_tor_password_with_salt(&salt, password)
163174
}
164175

165-
pub fn get_control_addr(&self) -> &SocketAddr {
176+
pub fn get_control_addr(&self) -> &SocketAddrOrUnixSocketAddr {
166177
&self.control_addr
167178
}
168179

@@ -171,6 +182,11 @@ impl LegacyTorProcess {
171182
}
172183

173184
pub fn new(tor_bin_path: &Path, data_directory: &Path) -> Result<LegacyTorProcess, Error> {
185+
Self::new_unix(tor_bin_path, data_directory, false)
186+
}
187+
188+
/// Unix mode is test/debug only
189+
pub(crate) fn new_unix(tor_bin_path: &Path, data_directory: &Path, unix: bool) -> Result<LegacyTorProcess, Error> {
174190
if tor_bin_path.is_relative() {
175191
return Err(Error::TorBinPathNotAbsolute(format!(
176192
"{}",
@@ -249,10 +265,10 @@ impl LegacyTorProcess {
249265
// daemon will assign us a port, and we will
250266
// read it from the control port file
251267
.arg("ControlPort")
252-
.arg("auto")
268+
.arg(&*if unix { Cow::Owned(format!("unix:{}", data_directory.join("control.sock").display())) } else { Cow::Borrowed("auto") })
253269
// control port file destination
254270
.arg("ControlPortWriteToFile")
255-
.arg(control_port_file.clone())
271+
.arg(&control_port_file)
256272
// use password authentication to prevent other apps
257273
// from modifying our daemon's settings
258274
.arg("HashedControlPassword")

source/gosling/crates/tor-interface/src/mock_tor_client.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ impl MockTorNetwork {
8383
}
8484

8585
if let Ok(stream) = TcpStream::connect(socket_addr) {
86-
Ok(TcpOnionStream {
87-
stream,
86+
Ok(TcpOrUnixOnionStream {
87+
stream: stream.into(),
8888
local_addr: None,
8989
peer_addr: Some(TargetAddr::OnionService(onion_addr)),
9090
})
@@ -168,7 +168,7 @@ impl Default for MockTorClient {
168168
}
169169

170170
impl TorProvider for MockTorClient {
171-
type Stream = TcpOnionStream;
171+
type Stream = TcpOrUnixOnionStream;
172172
type Listener = TcpOnionListener;
173173

174174
fn update(&mut self) -> Result<Vec<TorEvent>, tor_provider::Error> {
@@ -256,8 +256,8 @@ impl TorProvider for MockTorClient {
256256
.local_addr()
257257
.expect("loopback local_addr failed"),
258258
) {
259-
return Ok(TcpOnionStream {
260-
stream,
259+
return Ok(TcpOrUnixOnionStream {
260+
stream: stream.into(),
261261
local_addr: None,
262262
peer_addr: Some(target_address),
263263
});

0 commit comments

Comments
 (0)