Skip to content

Commit

Permalink
fix: Immediately announce onion address to swarm
Browse files Browse the repository at this point in the history
Instead of waiting for the onion service to be fully reachable (registered at intro points and hs_dirs), we announce the address immediately.
This fixes an issue where libp2p would not poll our transport against after we did not announce an address on the first poll.
  • Loading branch information
binarybaron committed Dec 3, 2024
1 parent db1a0c0 commit 3b227a6
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 44 deletions.
4 changes: 0 additions & 4 deletions examples/ping-onion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,6 @@ async fn main() -> Result<(), Box<dyn Error>> {
swarm.dial(remote)?;
println!("Dialed {addr}")
} else {
// TODO: We need to do this because otherwise the status of the onion service is gonna be [`Shutdown`]
// when we first poll it and then the swarm will not pull it again (?). I don't know why this is the case.
tokio::time::sleep(std::time::Duration::from_secs(20)).await;

// If we are not dialing, we need to listen
// Tell the swarm to listen on a specific onion address
swarm.listen_on(onion_listen_address).unwrap();
Expand Down
63 changes: 23 additions & 40 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ use libp2p::{
core::transport::{ListenerId, TransportEvent},
Multiaddr, Transport, TransportError,
};
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use std::{collections::HashSet, pin::Pin};
use std::{collections::VecDeque, sync::Arc};
use thiserror::Error;
use tor_rtcompat::tokio::TokioRustlsRuntime;

Expand Down Expand Up @@ -107,6 +107,8 @@ struct TorListener {
port: u16,
/// The onion address we are listening on
onion_address: Multiaddr,
/// Whether we have already announced this address
announced: bool,
}

/// Mode of address conversion.
Expand Down Expand Up @@ -268,28 +270,6 @@ impl HsIdExt for HsId {
}
}

#[cfg(feature = "listen-onion-service")]
trait StatusExt {
fn is_reachable(&self) -> bool;
fn is_broken(&self) -> bool;
}

#[cfg(feature = "listen-onion-service")]
impl StatusExt for OnionServiceStatus {
/// Returns true if the onion service is probably reachable
fn is_reachable(&self) -> bool {
matches!(
self.state(),
tor_hsservice::status::State::Running | tor_hsservice::status::State::DegradedReachable
)
}

/// Returns true if the onion service is definitely broken
fn is_broken(&self) -> bool {
matches!(self.state(), tor_hsservice::status::State::Broken)
}
}

impl Transport for TorTransport {
type Output = TokioTorStream;
type Error = TorTransportError;
Expand Down Expand Up @@ -320,7 +300,6 @@ impl Transport for TorTransport {

// Find the running onion service that matches the requested address
// If we find it, remove it from [`services`] and insert it into [`listeners`]

let position = self
.services
.iter()
Expand All @@ -343,6 +322,7 @@ impl Transport for TorTransport {
onion_address: onion_address.clone(),
port: address.port(),
status_stream,
announced: false,
},
);

Expand Down Expand Up @@ -421,21 +401,23 @@ impl Transport for TorTransport {
address = listener.onion_address.to_string(),
"Onion service status changed"
);
}

if status.is_reachable() {
// TODO: We might report the address here multiple time to the swarm. Is this a problem?
return Poll::Ready(TransportEvent::NewAddress {
listener_id: *listener_id,
listen_addr: listener.onion_address.clone(),
});
}

if status.is_broken() {
return Poll::Ready(TransportEvent::ListenerError {
listener_id: *listener_id,
error: TorTransportError::Broken,
});
}
// Check if we have already announced this address, if not, do it now
if !listener.announced {
listener.announced = true;

// We announce the address here to the swarm even though we technically cannot guarantee
// that the address is reachable yet from the outside. We might not have registered the
// onion service fully yet (introduction points, hsdir, ...)
//
// However, we need to announce it now because otherwise libp2p might not poll the listener
// again and we will not be able to announce it later.
// TODO: Find out why this is the case, if this is intended behaviour or a bug
return Poll::Ready(TransportEvent::NewAddress {
listener_id: *listener_id,
listen_addr: listener.onion_address.clone(),
});
}

match listener.request_stream.as_mut().poll_next(cx) {
Expand Down Expand Up @@ -467,7 +449,8 @@ impl Transport for TorTransport {
});
}

// The stream has ended. Most likely because the service was shut down
// The stream has ended
// This means that the onion service was shut down, and we will not receive any more connections on it
Poll::Ready(None) => {
return Poll::Ready(TransportEvent::ListenerClosed {
listener_id: *listener_id,
Expand Down

0 comments on commit 3b227a6

Please sign in to comment.