Selaa lähdekoodia

net: when tcp://[::]:0 is set as the inbound, TcpListener will store the port which can be fetched using listener.endpoint() method. However currently this address is not auto-advertised.

darkfi 1 vuosi sitten
vanhempi
sitoutus
234bf1260a
3 muutettua tiedostoa jossa 42 lisäystä ja 11 poistoa
  1. 18 1
      src/net/transport/mod.rs
  2. 16 2
      src/net/transport/tcp.rs
  3. 8 8
      src/net/transport/tor.rs

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

@@ -372,8 +372,25 @@ impl Listener {
 
     pub async fn endpoint(&self) -> Url {
         match &self.variant {
+            ListenerVariant::Tcp(listener) | ListenerVariant::TcpTls(listener) => {
+                let mut endpoint = self.endpoint.clone();
+
+                // Endpoint *must* always have a port set.
+                // This is enforced by the enforce_hostport!() macro in Listener::new().
+                let port = self.endpoint.port().unwrap();
+
+                // `port == 0` means we got the OS to assign a random listen port to us.
+                // Get the port from the listener and modify the endpoint.
+                if port == 0 {
+                    let actual_port = *listener.port.get().unwrap();
+                    endpoint.set_port(Some(actual_port)).unwrap();
+                }
+
+                endpoint
+            }
             #[cfg(feature = "p2p-tor")]
-            ListenerVariant::Tor(listener) => listener.endpoint.lock().await.clone().unwrap(),
+            ListenerVariant::Tor(listener) => listener.endpoint.get().unwrap().clone(),
+            #[allow(unreachable_patterns)]
             _ => self.endpoint.clone(),
         }
     }

+ 16 - 2
src/net/transport/tcp.rs

@@ -16,7 +16,7 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use std::{io, time::Duration};
+use std::{io, sync::Arc, time::Duration};
 
 use async_trait::async_trait;
 use futures::{
@@ -26,6 +26,7 @@ use futures::{
 use futures_rustls::{TlsAcceptor, TlsStream};
 use log::debug;
 use smol::{
+    lock::OnceCell,
     net::{SocketAddr, TcpListener as SmolTcpListener, TcpStream},
     Async, Timer,
 };
@@ -144,12 +145,15 @@ impl TcpDialer {
 pub struct TcpListener {
     /// Size of the listen backlog for listen sockets
     backlog: i32,
+    /// When the user puts a port of 0, the OS will assign a random port.
+    /// We get it from the listener so we know what the true endpoint is.
+    pub port: Arc<OnceCell<u16>>,
 }
 
 impl TcpListener {
     /// Instantiate a new [`TcpListener`] with given backlog size.
     pub async fn new(backlog: i32) -> io::Result<Self> {
-        Ok(Self { backlog })
+        Ok(Self { backlog, port: Arc::new(OnceCell::new()) })
     }
 
     /// Internal helper function to create a TCP socket.
@@ -177,8 +181,18 @@ impl TcpListener {
         socket.set_nonblocking(true)?;
 
         let listener = std::net::TcpListener::from(socket);
+        let local_addr = listener.local_addr()?;
         let listener = smol::Async::<std::net::TcpListener>::try_from(listener)?;
 
+        match local_addr {
+            // Ignore ipv4 addrs since they're behind NAT
+            SocketAddr::V4(_) => {}
+            SocketAddr::V6(addr) => {
+                let port = addr.port();
+                self.port.set(port).await.expect("fatal port already set for TcpListener");
+            }
+        }
+
         Ok(SmolTcpListener::from(listener))
     }
 }

+ 8 - 8
src/net/transport/tor.rs

@@ -36,7 +36,7 @@ use futures::{
 };
 use log::{debug, error, info, warn};
 use smol::{
-    lock::{Mutex, OnceCell},
+    lock::{Mutex as AsyncMutex, OnceCell},
     Timer,
 };
 use tor_cell::relaycell::msg::Connected;
@@ -154,13 +154,13 @@ impl TorDialer {
 #[derive(Clone, Debug)]
 pub struct TorListener {
     datastore: Option<String>,
-    pub endpoint: Arc<Mutex<Option<Url>>>,
+    pub endpoint: Arc<OnceCell<Url>>,
 }
 
 impl TorListener {
     /// Instantiate a new [`TorListener`]
     pub async fn new(datastore: Option<String>) -> io::Result<Self> {
-        Ok(Self { datastore, endpoint: Arc::new(Mutex::new(None)) })
+        Ok(Self { datastore, endpoint: Arc::new(OnceCell::new()) })
     }
 
     /// Internal listen function
@@ -224,14 +224,14 @@ impl TorListener {
             onion_service.onion_name().unwrap(), port,
         );
 
-        *self.endpoint.lock().await = Some(
-            Url::parse(&format!("tor://{}:{}", onion_service.onion_name().unwrap(), port)).unwrap(),
-        );
+        let endpoint =
+            Url::parse(&format!("tor://{}:{}", onion_service.onion_name().unwrap(), port)).unwrap();
+        self.endpoint.set(endpoint).await.expect("fatal endpoint already set for TorListener");
 
         Ok(TorListenerIntern {
             port,
             _onion_service: onion_service,
-            rendreq_stream: Mutex::new(Box::pin(rendreq_stream)),
+            rendreq_stream: AsyncMutex::new(Box::pin(rendreq_stream)),
         })
     }
 }
@@ -241,7 +241,7 @@ pub struct TorListenerIntern {
     port: u16,
     _onion_service: Arc<RunningOnionService>,
     //rendreq_stream: Mutex<BoxStream<'a, RendRequest>>,
-    rendreq_stream: Mutex<Pin<Box<dyn Stream<Item = RendRequest> + Send>>>,
+    rendreq_stream: AsyncMutex<Pin<Box<dyn Stream<Item = RendRequest> + Send>>>,
 }
 
 unsafe impl Sync for TorListenerIntern {}