mod.rs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2023 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use log::warn;
  19. use std::time::Duration;
  20. use tor_error::ErrorReport;
  21. use async_trait::async_trait;
  22. use smol::io::{AsyncRead, AsyncWrite};
  23. use url::Url;
  24. use crate::{Error, Result};
  25. /// TLS upgrade mechanism
  26. pub(crate) mod tls;
  27. #[cfg(feature = "p2p-tcp")]
  28. /// TCP transport
  29. pub(crate) mod tcp;
  30. #[cfg(feature = "p2p-tor")]
  31. /// Tor transport
  32. pub(crate) mod tor;
  33. #[cfg(feature = "p2p-nym")]
  34. /// Nym transport
  35. pub(crate) mod nym;
  36. #[cfg(feature = "p2p-unix")]
  37. /// Unix socket transport
  38. pub(crate) mod unix;
  39. /// Dialer variants
  40. #[derive(Debug, Clone)]
  41. pub enum DialerVariant {
  42. #[cfg(feature = "p2p-tcp")]
  43. /// Plain TCP
  44. Tcp(tcp::TcpDialer),
  45. #[cfg(feature = "p2p-tcp")]
  46. /// TCP with TLS
  47. TcpTls(tcp::TcpDialer),
  48. #[cfg(feature = "p2p-tor")]
  49. /// Tor
  50. Tor(tor::TorDialer),
  51. #[cfg(feature = "p2p-tor")]
  52. /// Tor with TLS
  53. TorTls(tor::TorDialer),
  54. #[cfg(feature = "p2p-nym")]
  55. /// Nym
  56. Nym(nym::NymDialer),
  57. #[cfg(feature = "p2p-nym")]
  58. /// Nym with TLS
  59. NymTls(nym::NymDialer),
  60. #[cfg(feature = "p2p-unix")]
  61. /// Unix socket
  62. Unix(unix::UnixDialer),
  63. }
  64. /// Listener variants
  65. #[derive(Debug, Clone)]
  66. pub enum ListenerVariant {
  67. #[cfg(feature = "p2p-tcp")]
  68. /// Plain TCP
  69. Tcp(tcp::TcpListener),
  70. #[cfg(feature = "p2p-tcp")]
  71. /// TCP with TLS
  72. TcpTls(tcp::TcpListener),
  73. #[cfg(feature = "p2p-unix")]
  74. /// Unix socket
  75. Unix(unix::UnixListener),
  76. }
  77. /// A dialer that is able to transparently operate over arbitrary transports.
  78. pub struct Dialer {
  79. /// The endpoint to connect to
  80. endpoint: Url,
  81. /// The dialer variant (transport protocol)
  82. variant: DialerVariant,
  83. }
  84. macro_rules! enforce_hostport {
  85. ($endpoint:ident) => {
  86. if $endpoint.host_str().is_none() || $endpoint.port().is_none() {
  87. return Err(Error::InvalidDialerScheme)
  88. }
  89. };
  90. }
  91. macro_rules! enforce_abspath {
  92. ($endpoint:ident) => {
  93. if $endpoint.host_str().is_some() || $endpoint.port().is_some() {
  94. return Err(Error::InvalidDialerScheme)
  95. }
  96. if $endpoint.to_file_path().is_err() {
  97. return Err(Error::InvalidDialerScheme)
  98. }
  99. };
  100. }
  101. impl Dialer {
  102. /// Instantiate a new [`Dialer`] with the given [`Url`].
  103. pub async fn new(endpoint: Url) -> Result<Self> {
  104. match endpoint.scheme().to_lowercase().as_str() {
  105. #[cfg(feature = "p2p-tcp")]
  106. "tcp" => {
  107. // Build a TCP dialer
  108. enforce_hostport!(endpoint);
  109. let variant = tcp::TcpDialer::new(None).await?;
  110. let variant = DialerVariant::Tcp(variant);
  111. Ok(Self { endpoint, variant })
  112. }
  113. #[cfg(feature = "p2p-tcp")]
  114. "tcp+tls" => {
  115. // Build a TCP dialer wrapped with TLS
  116. enforce_hostport!(endpoint);
  117. let variant = tcp::TcpDialer::new(None).await?;
  118. let variant = DialerVariant::TcpTls(variant);
  119. Ok(Self { endpoint, variant })
  120. }
  121. #[cfg(feature = "p2p-tor")]
  122. "tor" => {
  123. // Build a Tor dialer
  124. enforce_hostport!(endpoint);
  125. let variant = tor::TorDialer::new().await?;
  126. let variant = DialerVariant::Tor(variant);
  127. Ok(Self { endpoint, variant })
  128. }
  129. #[cfg(feature = "p2p-tor")]
  130. "tor+tls" => {
  131. // Build a Tor dialer wrapped with TLS
  132. enforce_hostport!(endpoint);
  133. let variant = tor::TorDialer::new().await?;
  134. let variant = DialerVariant::TorTls(variant);
  135. Ok(Self { endpoint, variant })
  136. }
  137. #[cfg(feature = "p2p-nym")]
  138. "nym" => {
  139. // Build a Nym dialer
  140. enforce_hostport!(endpoint);
  141. let variant = nym::NymDialer::new().await?;
  142. let variant = DialerVariant::Nym(variant);
  143. Ok(Self { endpoint, variant })
  144. }
  145. #[cfg(feature = "p2p-nym")]
  146. "nym+tls" => {
  147. // Build a Nym dialer wrapped with TLS
  148. enforce_hostport!(endpoint);
  149. let variant = nym::NymDialer::new().await?;
  150. let variant = DialerVariant::NymTls(variant);
  151. Ok(Self { endpoint, variant })
  152. }
  153. #[cfg(feature = "p2p-unix")]
  154. "unix" => {
  155. enforce_abspath!(endpoint);
  156. // Build a Unix socket dialer
  157. let variant = unix::UnixDialer::new().await?;
  158. let variant = DialerVariant::Unix(variant);
  159. Ok(Self { endpoint, variant })
  160. }
  161. x => Err(Error::UnsupportedTransport(x.to_string())),
  162. }
  163. }
  164. /// Dial an instantiated [`Dialer`]. This creates a connection and returns a stream.
  165. pub async fn dial(&self, timeout: Option<Duration>) -> Result<Box<dyn PtStream>> {
  166. match &self.variant {
  167. #[cfg(feature = "p2p-tcp")]
  168. DialerVariant::Tcp(dialer) => {
  169. // NOTE: sockaddr here is an array, can contain both ipv4 and ipv6
  170. let sockaddr = self.endpoint.socket_addrs(|| None)?;
  171. let stream = dialer.do_dial(sockaddr[0], timeout).await?;
  172. Ok(Box::new(stream))
  173. }
  174. #[cfg(feature = "p2p-tcp")]
  175. DialerVariant::TcpTls(dialer) => {
  176. let sockaddr = self.endpoint.socket_addrs(|| None)?;
  177. let stream = dialer.do_dial(sockaddr[0], timeout).await?;
  178. let tlsupgrade = tls::TlsUpgrade::new();
  179. let stream = tlsupgrade.upgrade_dialer_tls(stream).await?;
  180. Ok(Box::new(stream))
  181. }
  182. #[cfg(feature = "p2p-tor")]
  183. DialerVariant::Tor(dialer) => {
  184. let host = self.endpoint.host_str().unwrap();
  185. let port = self.endpoint.port().unwrap();
  186. // Extract error reports (i.e. very detailed debugging)
  187. // from arti-client in order to help debug Tor connections.
  188. // https://docs.rs/arti-client/latest/arti_client/#reporting-arti-errors
  189. // https://gitlab.torproject.org/tpo/core/arti/-/issues/1086
  190. let result = match dialer.do_dial(host, port, timeout).await {
  191. Ok(stream) => Ok(stream),
  192. Err(err) => {
  193. warn!("{}", err.report());
  194. Err(err)
  195. }
  196. };
  197. let stream = result?;
  198. Ok(Box::new(stream))
  199. }
  200. #[cfg(feature = "p2p-tor")]
  201. DialerVariant::TorTls(dialer) => {
  202. let host = self.endpoint.host_str().unwrap();
  203. let port = self.endpoint.port().unwrap();
  204. // Extract error reports (i.e. very detailed debugging)
  205. // from arti-client in order to help debug Tor connections.
  206. // https://docs.rs/arti-client/latest/arti_client/#reporting-arti-errors
  207. // https://gitlab.torproject.org/tpo/core/arti/-/issues/1086
  208. let result = match dialer.do_dial(host, port, timeout).await {
  209. Ok(stream) => Ok(stream),
  210. Err(err) => {
  211. warn!("{}", err.report());
  212. Err(err)
  213. }
  214. };
  215. let stream = result?;
  216. let tlsupgrade = tls::TlsUpgrade::new();
  217. let stream = tlsupgrade.upgrade_dialer_tls(stream).await?;
  218. Ok(Box::new(stream))
  219. }
  220. #[cfg(feature = "p2p-nym")]
  221. DialerVariant::Nym(_dialer) => {
  222. todo!();
  223. }
  224. #[cfg(feature = "p2p-nym")]
  225. DialerVariant::NymTls(_dialer) => {
  226. todo!();
  227. }
  228. #[cfg(feature = "p2p-unix")]
  229. DialerVariant::Unix(dialer) => {
  230. let path = self.endpoint.to_file_path()?;
  231. let stream = dialer.do_dial(path).await?;
  232. Ok(Box::new(stream))
  233. }
  234. #[cfg(not(any(
  235. feature = "p2p-tcp",
  236. feature = "p2p-tor",
  237. feature = "p2p-nym",
  238. feature = "p2p-unix"
  239. )))]
  240. _ => panic!("No compiled p2p transports!"),
  241. }
  242. }
  243. /// Return a reference to the `Dialer` endpoint
  244. pub fn endpoint(&self) -> &Url {
  245. &self.endpoint
  246. }
  247. }
  248. /// A listener that is able to transparently listen over arbitrary transports.
  249. pub struct Listener {
  250. /// The address to open the listener on
  251. endpoint: Url,
  252. /// The listener variant (transport protocol)
  253. variant: ListenerVariant,
  254. }
  255. impl Listener {
  256. /// Instantiate a new [`Listener`] with the given [`Url`].
  257. /// Must contain a scheme, host string, and a port.
  258. pub async fn new(endpoint: Url) -> Result<Self> {
  259. match endpoint.scheme().to_lowercase().as_str() {
  260. #[cfg(feature = "p2p-tcp")]
  261. "tcp" => {
  262. // Build a TCP listener
  263. enforce_hostport!(endpoint);
  264. let variant = tcp::TcpListener::new(1024).await?;
  265. let variant = ListenerVariant::Tcp(variant);
  266. Ok(Self { endpoint, variant })
  267. }
  268. #[cfg(feature = "p2p-tcp")]
  269. "tcp+tls" => {
  270. // Build a TCP listener wrapped with TLS
  271. enforce_hostport!(endpoint);
  272. let variant = tcp::TcpListener::new(1024).await?;
  273. let variant = ListenerVariant::TcpTls(variant);
  274. Ok(Self { endpoint, variant })
  275. }
  276. #[cfg(feature = "p2p-unix")]
  277. "unix" => {
  278. enforce_abspath!(endpoint);
  279. let variant = unix::UnixListener::new().await?;
  280. let variant = ListenerVariant::Unix(variant);
  281. Ok(Self { endpoint, variant })
  282. }
  283. x => Err(Error::UnsupportedTransport(x.to_string())),
  284. }
  285. }
  286. /// Listen on an instantiated [`Listener`].
  287. /// This will open a socket and return the listener.
  288. pub async fn listen(&self) -> Result<Box<dyn PtListener>> {
  289. match &self.variant {
  290. #[cfg(feature = "p2p-tcp")]
  291. ListenerVariant::Tcp(listener) => {
  292. let sockaddr = self.endpoint.socket_addrs(|| None)?;
  293. let l = listener.do_listen(sockaddr[0]).await?;
  294. Ok(Box::new(l))
  295. }
  296. #[cfg(feature = "p2p-tcp")]
  297. ListenerVariant::TcpTls(listener) => {
  298. let sockaddr = self.endpoint.socket_addrs(|| None)?;
  299. let l = listener.do_listen(sockaddr[0]).await?;
  300. let tlsupgrade = tls::TlsUpgrade::new();
  301. let l = tlsupgrade.upgrade_listener_tcp_tls(l).await?;
  302. Ok(Box::new(l))
  303. }
  304. #[cfg(feature = "p2p-unix")]
  305. ListenerVariant::Unix(listener) => {
  306. let path = self.endpoint.to_file_path()?;
  307. let l = listener.do_listen(&path).await?;
  308. Ok(Box::new(l))
  309. }
  310. #[cfg(not(any(feature = "p2p-tcp", feature = "p2p-unix")))]
  311. _ => panic!("No compiled p2p transports!"),
  312. }
  313. }
  314. pub fn endpoint(&self) -> &Url {
  315. &self.endpoint
  316. }
  317. }
  318. /// Wrapper trait for async streams
  319. pub trait PtStream: AsyncRead + AsyncWrite + Unpin + Send {}
  320. #[cfg(feature = "p2p-tcp")]
  321. impl PtStream for smol::net::TcpStream {}
  322. #[cfg(feature = "p2p-tcp")]
  323. impl PtStream for async_rustls::TlsStream<smol::net::TcpStream> {}
  324. #[cfg(feature = "p2p-tor")]
  325. impl PtStream for arti_client::DataStream {}
  326. #[cfg(feature = "p2p-tor")]
  327. impl PtStream for async_rustls::TlsStream<arti_client::DataStream> {}
  328. #[cfg(feature = "p2p-unix")]
  329. impl PtStream for smol::net::unix::UnixStream {}
  330. /// Wrapper trait for async listeners
  331. #[async_trait]
  332. pub trait PtListener: Send + Sync + Unpin {
  333. async fn next(&self) -> std::io::Result<(Box<dyn PtStream>, Url)>;
  334. }