cuprate_p2p_transport/
socks.rs1use std::{
9 io::{self, ErrorKind},
10 net::SocketAddr,
11};
12
13use tokio::{
14 io::{AsyncReadExt, AsyncWriteExt},
15 net::{
16 tcp::{OwnedReadHalf, OwnedWriteHalf},
17 TcpStream,
18 },
19 time::{timeout, Duration},
20};
21use tokio_socks::tcp::Socks5Stream;
22use tokio_util::codec::{FramedRead, FramedWrite};
23
24use cuprate_p2p_core::{ClearNet, NetworkZone, Transport};
25use cuprate_wire::MoneroWireCodec;
26
27use crate::DisabledListener;
28
29pub async fn is_socks5_proxy(addr: SocketAddr) -> bool {
31 let Ok(Ok(mut stream)) = timeout(Duration::from_secs(3), TcpStream::connect(addr)).await else {
32 return false;
33 };
34
35 if stream.write_all(&[0x05, 0x01, 0x00]).await.is_err() {
37 return false;
38 }
39
40 let mut buf = [0_u8; 2];
41 stream.read_exact(&mut buf).await.is_ok() && buf[0] == 0x05
42}
43
44#[derive(Debug, Clone, Copy, Default)]
48pub struct Socks;
49
50#[derive(Debug, Clone, PartialEq, Eq)]
51pub struct SocksClientConfig {
52 pub proxy: SocketAddr,
54
55 pub authentication: Option<(String, String)>,
57}
58
59#[async_trait::async_trait]
62impl Transport<ClearNet> for Socks {
63 type ClientConfig = SocksClientConfig;
64 type ServerConfig = ();
65
66 type Stream = FramedRead<OwnedReadHalf, MoneroWireCodec>;
67 type Sink = FramedWrite<OwnedWriteHalf, MoneroWireCodec>;
68 type Listener = DisabledListener<ClearNet, OwnedReadHalf, OwnedWriteHalf>;
69
70 async fn connect_to_peer(
71 addr: <ClearNet as NetworkZone>::Addr,
72 config: &Self::ClientConfig,
73 ) -> Result<(Self::Stream, Self::Sink), io::Error> {
74 let proxy = if let Some((username, password)) = config.authentication.as_ref() {
76 Socks5Stream::connect_with_password(config.proxy, addr, username, password).await
77 } else {
78 Socks5Stream::connect(config.proxy, addr.to_string()).await
79 };
80
81 proxy
82 .map_err(|e| io::Error::new(ErrorKind::ConnectionAborted, e.to_string()))
83 .map(|stream| {
84 let (stream, sink) = stream.into_inner().into_split();
85 (
86 FramedRead::new(stream, MoneroWireCodec::default()),
87 FramedWrite::new(sink, MoneroWireCodec::default()),
88 )
89 })
90 }
91
92 async fn incoming_connection_listener(
93 _config: Self::ServerConfig,
94 ) -> Result<Self::Listener, io::Error> {
95 panic!("In proxy mode, inbound is disabled!");
96 }
97}