Prechádzať zdrojové kódy

net: add i2p transport

oars 1 rok pred
rodič
commit
c73e8607bc

+ 2 - 0
bin/lilith/src/main.rs

@@ -363,6 +363,8 @@ async fn spawn_net(name: String, info: &NetInfo, ex: Arc<Executor<'static>>) ->
             "tor+tls".to_string(),
             "nym".to_string(),
             "nym+tls".to_string(),
+            "i2p".to_string(),
+            "i2p+tls".to_string(),
         ],
         ban_policy: BanPolicy::Relaxed,
         ..Default::default()

+ 2 - 1
src/net/connector.rs

@@ -67,6 +67,7 @@ impl Connector {
         let transport_mixing = settings.transport_mixing;
         let datastore = settings.p2p_datastore.clone();
         let outbound_connect_timeout = settings.outbound_connect_timeout;
+        let i2p_socks5_proxy = settings.i2p_socks5_proxy.clone();
         let socks5_proxy = settings.socks5_proxy.clone();
         drop(settings);
 
@@ -111,7 +112,7 @@ impl Connector {
             }
         }
 
-        let dialer = Dialer::new(endpoint.clone(), datastore).await?;
+        let dialer = Dialer::new(endpoint.clone(), datastore, Some(i2p_socks5_proxy)).await?;
         let timeout = Duration::from_secs(outbound_connect_timeout);
 
         let stop_fut = async {

+ 40 - 1
src/net/hosts.rs

@@ -40,6 +40,7 @@ use super::{
 use crate::{
     system::{Publisher, PublisherPtr, Subscription},
     util::{
+        encoding::base32,
         file::{load_file, save_file},
         most_frequent_or_any,
         path::expand_path,
@@ -1357,6 +1358,16 @@ impl Hosts {
                     );
                 }
 
+                "i2p" | "i2p+tls" => {
+                    if !Self::is_i2p_host(host_str) {
+                        continue
+                    }
+                    trace!(
+                        target: "net::hosts::filter_addresses",
+                        "[I2p] Valid: {}", host_str,
+                    );
+                }
+
                 _ => continue,
             }
 
@@ -1501,7 +1512,7 @@ impl Hosts {
                 // with stuff that has host_str().
                 if addr.host_str().is_some() {
                     // Localhost connections should never enter the blacklist
-                    // This however allows any Tor and Nym connections.
+                    // This however allows any Tor, Nym and I2p connections.
                     if self.is_local_host(addr) {
                         return Ok(());
                     }
@@ -1590,6 +1601,26 @@ impl Hosts {
 
         None
     }
+
+    fn is_i2p_host(host: &str) -> bool {
+        if !host.ends_with(".i2p") {
+            return false
+        }
+
+        // Two kinds of address
+        // 1. wvbtv6i6njxdtxwsgsr3d4xejdtsy6n7s3d2paqgigjkv3fv5imq.b32.i2p
+        // 2. node.darkfi.i2p
+        let name = host.trim_end_matches(".i2p");
+
+        if name.ends_with(".b32") {
+            let b32 = name.trim_end_matches(".b32");
+            let decoded = base32::decode(b32);
+            // decoded should be a SHA256 hash
+            return decoded.is_some() && decoded.unwrap().len() == 32
+        }
+
+        name.chars().all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '.')
+    }
 }
 
 /// We need a convenience method from Rust's unstable feature "ip".
@@ -1866,4 +1897,12 @@ mod tests {
             println!("last entry: {} {}", entry.0, entry.1);
         });
     }
+
+    #[test]
+    fn test_is_p2p_host() {
+        assert!(Hosts::is_i2p_host("tm7bz5qfh73id33yjpshxmesrqedoz2ckghd3levktqywcrramwq.b32.i2p"));
+        assert!(!Hosts::is_i2p_host("randomstring.b32.i2p"));
+        assert!(Hosts::is_i2p_host("node.dark.fi.i2p"));
+        assert!(!Hosts::is_i2p_host("node.dark.fi"));
+    }
 }

+ 2 - 1
src/net/protocol/protocol_address.rs

@@ -73,7 +73,8 @@ const PROTO_NAME: &str = "ProtocolAddress";
 /// combinations.  Should be updated if and when new transports are
 /// added. Creates a upper bound on the number of transports a given peer
 /// can request.
-const TRANSPORT_COMBOS: [&str; 7] = ["tor", "tls", "tcp", "nym", "tor+tls", "nym+tls", "tcp+tls"];
+const TRANSPORT_COMBOS: [&str; 9] =
+    ["tor", "tls", "tcp", "nym", "i2p", "tor+tls", "nym+tls", "tcp+tls", "i2p+tls"];
 
 impl ProtocolAddress {
     /// Creates a new address protocol. Makes an address, an external address

+ 8 - 0
src/net/settings.rs

@@ -67,6 +67,8 @@ pub struct Settings {
     /// Socks5 proxy to connect to when socks5 or socks5+tls are added to allowed transports
     /// and transport mixing is enabled
     pub socks5_proxy: Url,
+    /// I2p Socks5 proxy to connect to i2p eepsite (hidden services)
+    pub i2p_socks5_proxy: Url,
     /// Outbound connection slots number, this many connections will be
     /// attempted. (This does not include manual connections)
     pub outbound_connections: usize,
@@ -128,6 +130,7 @@ impl Default for Settings {
             allowed_transports: vec!["tcp+tls".to_string()],
             transport_mixing: true,
             socks5_proxy: Url::parse("socks5://127.0.0.1:9050").unwrap(),
+            i2p_socks5_proxy: Url::parse("socks5://127.0.0.1:4447").unwrap(),
             outbound_connections: 8,
             inbound_connections: 8,
             outbound_connect_timeout: 15,
@@ -234,6 +237,10 @@ pub struct SettingsOpt {
     #[structopt(long)]
     pub socks5_proxy: Option<Url>,
 
+    /// I2p Socks5 proxy to connect to i2p eepsite (hidden services)
+    #[structopt(long)]
+    pub i2p_socks5_proxy: Option<Url>,
+
     /// If this is true, strictly follow the gold_connect_count and
     /// white_connect_percent settings. Otherwise, connect to greylist
     /// entries if we have no white or gold connections.
@@ -311,6 +318,7 @@ impl From<SettingsOpt> for Settings {
             allowed_transports: opt.allowed_transports.unwrap_or(def.allowed_transports),
             transport_mixing: opt.transport_mixing.unwrap_or(def.transport_mixing),
             socks5_proxy: opt.socks5_proxy.unwrap_or(def.socks5_proxy),
+            i2p_socks5_proxy: opt.i2p_socks5_proxy.unwrap_or(def.i2p_socks5_proxy),
             outbound_connections: opt.outbound_connections.unwrap_or(def.outbound_connections),
             inbound_connections: opt.inbound_connections.unwrap_or(def.inbound_connections),
             outbound_connect_timeout: opt

+ 26 - 1
src/net/transport/mod.rs

@@ -132,7 +132,11 @@ macro_rules! enforce_abspath {
 
 impl Dialer {
     /// Instantiate a new [`Dialer`] with the given [`Url`] and datastore path.
-    pub async fn new(endpoint: Url, datastore: Option<String>) -> io::Result<Self> {
+    pub async fn new(
+        endpoint: Url,
+        datastore: Option<String>,
+        i2p_socks5_proxy: Option<Url>,
+    ) -> io::Result<Self> {
         match endpoint.scheme().to_lowercase().as_str() {
             "tcp" => {
                 // Build a TCP dialer
@@ -211,6 +215,27 @@ impl Dialer {
                 Ok(Self { endpoint, variant })
             }
 
+            "i2p" => {
+                // Build a Socks5 Dialer for I2p
+                enforce_hostport!(endpoint);
+                let mut url = i2p_socks5_proxy.unwrap();
+                url.set_path(&format!("{}:{}", endpoint.host().unwrap(), endpoint.port().unwrap()));
+                let variant = socks5::Socks5Dialer::new(&url).await?;
+                let variant = DialerVariant::Socks5(variant);
+                Ok(Self { endpoint, variant })
+            }
+
+            "i2p+tls" => {
+                // Build a SOCKS5 dialer with TLS encapsulation for I2p
+                enforce_hostport!(endpoint);
+                let mut url = i2p_socks5_proxy.unwrap();
+                url.set_path(&format!("{}:{}", endpoint.host().unwrap(), endpoint.port().unwrap()));
+                url.set_scheme("socks5+tls").unwrap();
+                let variant = socks5::Socks5Dialer::new(&url).await?;
+                let variant = DialerVariant::Socks5Tls(variant);
+                Ok(Self { endpoint, variant })
+            }
+
             x => {
                 error!("[P2P] Requested unsupported transport: {}", x);
                 Err(io::Error::from_raw_os_error(libc::ENETUNREACH))

+ 2 - 2
src/rpc/client.rs

@@ -70,7 +70,7 @@ impl RpcClient {
 
         // Instantiate Dialer and dial the server
         // TODO: Could add a timeout here
-        let dialer = Dialer::new(dialer_url, None).await?;
+        let dialer = Dialer::new(dialer_url, None, None).await?;
         let stream = dialer.dial(None).await?;
 
         // Create the StoppableTask running the request-reply loop.
@@ -322,7 +322,7 @@ impl RpcChadClient {
 
         // Instantiate Dialer and dial the server
         // TODO: Could add a timeout here
-        let dialer = Dialer::new(dialer_url, None).await?;
+        let dialer = Dialer::new(dialer_url, None, None).await?;
         let stream = dialer.dial(None).await?;
 
         // Create the StoppableTask running the request-reply loop.

+ 3 - 3
tests/network_transports.rs

@@ -39,7 +39,7 @@ fn tcp_transport() {
 
         let payload = "ohai tcp";
 
-        let dialer = Dialer::new(url, None).await.unwrap();
+        let dialer = Dialer::new(url, None, None).await.unwrap();
         let mut client = dialer.dial(None).await.unwrap();
         payload.encode_async(&mut client).await.unwrap();
 
@@ -70,7 +70,7 @@ fn tcp_tls_transport() {
 
         let payload = "ohai tls";
 
-        let dialer = Dialer::new(url, None).await.unwrap();
+        let dialer = Dialer::new(url, None, None).await.unwrap();
         let mut client = dialer.dial(None).await.unwrap();
         payload.encode_async(&mut client).await.unwrap();
 
@@ -103,7 +103,7 @@ fn unix_transport() {
 
         let payload = "ohai unix";
 
-        let dialer = Dialer::new(url, None).await.unwrap();
+        let dialer = Dialer::new(url, None, None).await.unwrap();
         let mut client = dialer.dial(None).await.unwrap();
         payload.encode_async(&mut client).await.unwrap();