1use anyhow::Result;
2use clap::{Parser, Subcommand};
3use fast_socks5::client::{Config, Socks5Stream};
4use fast_socks5::server::{AcceptAuthentication, Socks5Server};
5use std::str::FromStr;
6use tokio::io::AsyncWriteExt;
7use tokio::net::{TcpListener, TcpStream};
8use tokio::sync::oneshot;
9use tokio::time::Duration;
10use tokio_stream::StreamExt;
11use tor_chanmgr::transport::proxied::{settings_to_protocol, Protocol};
12use tor_linkspec::PtTransportName;
13use tor_ptmgr::ipc::{
14 PluggableClientTransport, PluggableServerTransport, PluggableTransport, PtClientParameters,
15 PtCommonParameters, PtServerParameters,
16};
17use tor_rtcompat::PreferredRuntime;
18use tor_socksproto::{SocksAuth, SocksVersion};
19
20const SERVER_STATE_LOCATION: &str = "/tmp/arti-pt";
22const CLIENT_STATE_LOCATION: &str = "/tmp/arti-pt-client";
24
25#[derive(Debug, thiserror::Error)]
27#[error("Error while obtaining bridge line data")]
28struct BridgeLineParseError;
29
30#[derive(Subcommand)]
32enum Command {
33 Client {
35 #[arg(short, long, default_value = "9050")]
37 client_port: u16,
38 #[arg(required = true)]
41 remote_obfs4_ip: String,
42 #[arg(required = true)]
45 remote_obfs4_port: u16,
46 #[arg(required = true)]
49 obfs4_auth_info: String,
50 },
51 Server {
53 #[arg(required = true)]
56 listen_address: String,
57 #[arg(default_value = "4000")]
62 final_socks5_port: u16,
63 },
64}
65
66#[derive(Parser)]
68#[command(author, version, about, long_about = None)]
69struct Args {
70 #[command(subcommand)]
71 command: Command,
72 #[arg(required = true)]
74 obfs4_path: String,
75}
76
77#[derive(Clone)]
81struct ForwardingCreds {
82 username: String,
83 password: String,
84 forward_endpoint: String,
85 obfs4_server_ip: String,
86 obfs4_server_port: u16,
87}
88
89fn build_server_config(
91 protocol: &str,
92 bind_addr: &str,
93 forwarding_server_addr: &str,
94) -> Result<(PtCommonParameters, PtServerParameters)> {
95 let bindaddr_formatted = format!("{}-{}", &protocol, bind_addr);
96 let orport = forwarding_server_addr.to_string();
97 Ok((
98 PtCommonParameters::builder()
99 .state_location(SERVER_STATE_LOCATION.into())
100 .timeout(Some(Duration::from_secs(1)))
101 .build()?,
102 PtServerParameters::builder()
103 .transports(vec![protocol.parse()?])
104 .server_bindaddr(bindaddr_formatted)
105 .server_orport(Some(orport))
106 .build()?,
107 ))
108}
109
110fn read_cert_info() -> Result<String> {
112 let file_path = format!("{}/obfs4_bridgeline.txt", SERVER_STATE_LOCATION);
113 match std::fs::read_to_string(file_path) {
114 Ok(contents) => {
115 let line = contents
116 .lines()
117 .find(|line| line.contains("Bridge obfs4"))
118 .ok_or(BridgeLineParseError)?;
119 let cert = line
120 .split_whitespace()
121 .find(|part| part.starts_with("cert="))
122 .ok_or(BridgeLineParseError)?;
123 let iat = line
124 .split_whitespace()
125 .find(|part| part.starts_with("iat-mode="))
126 .ok_or(BridgeLineParseError)?;
127 let complete_config = format!("{};{}", cert, iat);
128 Ok(complete_config)
129 }
130 Err(e) => Err(e.into()),
131 }
132}
133
134fn build_client_config(protocol: &str) -> Result<(PtCommonParameters, PtClientParameters)> {
136 Ok((
137 PtCommonParameters::builder()
138 .state_location(CLIENT_STATE_LOCATION.into())
139 .timeout(Some(Duration::from_secs(1)))
140 .build()?,
141 PtClientParameters::builder()
142 .transports(vec![protocol.parse()?])
143 .build()?,
144 ))
145}
146
147async fn connect_to_obfs4_client(
149 forward_creds: ForwardingCreds,
150) -> Result<Socks5Stream<TcpStream>> {
151 let config = Config::default();
152 Ok(Socks5Stream::connect_with_password(
153 forward_creds.forward_endpoint,
154 forward_creds.obfs4_server_ip,
155 forward_creds.obfs4_server_port,
156 forward_creds.username,
157 forward_creds.password,
158 config,
159 )
160 .await?)
161}
162
163async fn launch_obfs4_client_process(
165 obfs4_path: String,
166) -> anyhow::Result<PluggableClientTransport> {
167 let (common_params, client_params) = build_client_config("obfs4")?;
168 let mut client_pt = PluggableClientTransport::new(
169 obfs4_path.into(),
170 vec![
171 "-enableLogging".to_string(),
172 "-logLevel".to_string(),
173 "DEBUG".to_string(),
174 "-unsafeLogging".to_string(),
175 ],
176 common_params,
177 client_params,
178 );
179 client_pt.launch(PreferredRuntime::current()?).await?;
180 Ok(client_pt)
181}
182
183async fn launch_obfs4_server_process(
185 obfs4_path: String,
186 listen_address: String,
187 final_socks5_endpoint: String,
188) -> anyhow::Result<PluggableServerTransport> {
189 let (common_params, server_params) =
190 build_server_config("obfs4", &listen_address, &final_socks5_endpoint)?;
191
192 let mut server_pt = PluggableServerTransport::new(
193 obfs4_path.into(),
194 vec![
195 "-enableLogging".to_string(),
196 "-logLevel".to_string(),
197 "DEBUG".to_string(),
198 "-unsafeLogging".to_string(),
199 ],
200 common_params,
201 server_params,
202 );
203 server_pt.launch(PreferredRuntime::current()?).await?;
204 Ok(server_pt)
205}
206
207async fn run_forwarding_server(endpoint: &str, forward_creds: ForwardingCreds) -> Result<()> {
211 let listener = TcpListener::bind(endpoint).await?;
212 while let Ok((mut client, _)) = listener.accept().await {
213 let forward_creds_clone = forward_creds.clone();
214 match connect_to_obfs4_client(forward_creds_clone).await {
215 Ok(mut relay_stream) => {
216 if let Err(e) = tokio::io::copy_bidirectional(&mut client, &mut relay_stream).await
217 {
218 eprintln!("{:#?}", e);
219 }
220 }
221 Err(e) => {
222 eprintln!("Couldn't connect to obfs4 client: \"{}\"", e);
223 client.write_all(&[5, 0xFF]).await.unwrap();
226 }
227 }
228 }
229 Ok(())
230}
231
232async fn run_socks5_server(endpoint: &str) -> Result<oneshot::Receiver<bool>> {
235 let listener = Socks5Server::<AcceptAuthentication>::bind(endpoint).await?;
236 let (tx, rx) = oneshot::channel::<bool>();
237 tokio::spawn(async move {
238 while let Some(Ok(socks_socket)) = listener.incoming().next().await {
239 tokio::spawn(async move {
240 if let Err(e) = socks_socket.upgrade_to_socks5().await {
241 eprintln!("{:#?}", e);
242 }
243 });
244 }
245 tx.send(true).unwrap()
246 });
247 Ok(rx)
248}
249
250#[tokio::main]
252async fn main() -> Result<()> {
253 tracing_subscriber::fmt::init();
254 let args = Args::parse();
255 let obfs4_path = args.obfs4_path;
256 match args.command {
257 Command::Client {
258 client_port,
259 remote_obfs4_ip,
260 remote_obfs4_port,
261 obfs4_auth_info: obfs4_server_conf,
262 } => {
263 let entry_addr = format!("127.0.0.1:{}", client_port);
264
265 let client_pt = launch_obfs4_client_process(obfs4_path).await?;
266 let client_endpoint = client_pt
267 .transport_methods()
268 .get(&PtTransportName::from_str("obfs4")?)
269 .unwrap()
270 .endpoint()
271 .to_string();
272
273 let settings = settings_to_protocol(SocksVersion::V5, obfs4_server_conf)?;
274 match settings {
275 Protocol::Socks(_, auth) => match auth {
276 SocksAuth::Username(raw_username, raw_password) => {
277 let username = String::from_utf8(raw_username)?;
278 let password = match raw_password.is_empty() {
279 true => String::from("\0"),
280 false => String::from_utf8(raw_password)?,
281 };
282 let creds = ForwardingCreds {
283 username,
284 password,
285 forward_endpoint: client_endpoint,
286 obfs4_server_ip: remote_obfs4_ip,
287 obfs4_server_port: remote_obfs4_port,
288 };
289 println!();
290 println!("Listening on: {}", entry_addr);
291 run_forwarding_server(&entry_addr, creds).await?;
292 }
293 _ => eprintln!("Unable to get credentials for obfs4 client process!"),
294 },
295 _ => eprintln!("Unexpected protocol"),
296 }
297 }
298 Command::Server {
299 listen_address,
300 final_socks5_port,
301 } => {
302 let final_socks5_endpoint = format!("127.0.0.1:{}", final_socks5_port);
303 let exit_rx = run_socks5_server(&final_socks5_endpoint).await?;
304 println!();
305 println!("Listening on: {}", listen_address);
306 launch_obfs4_server_process(obfs4_path, listen_address, final_socks5_endpoint).await?;
307 let auth_info = read_cert_info().unwrap();
308 println!();
309 println!("Authentication info is: {}", auth_info);
310 exit_rx.await.unwrap();
311 }
312 }
313 Ok(())
314}