Skip to content

chore: publish new iroh-mainline-discovery #30

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion content-discovery/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ members = [
"iroh-mainline-content-discovery",
"iroh-mainline-content-discovery-cli",
"iroh-mainline-tracker",
"tls",
]
resolver = "2"

Expand Down
33 changes: 29 additions & 4 deletions content-discovery/iroh-mainline-content-discovery/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "iroh-mainline-content-discovery"
version = "0.5.0"
version = "0.6.0"
edition = "2021"
description = "Content discovery for iroh, using the bittorrent mainline DHT"
license = "MIT OR Apache-2.0"
Expand All @@ -22,7 +22,7 @@ hex = "0.4.3"

# Optional features for the client functionality
tracing = { version = "0.1", optional = true }
iroh-quinn = { version = "0.13", optional = true }
quinn = { package = "iroh-quinn", version = "0.13", optional = true }
mainline = { version = "2.0.0", optional = true, features = ["async"] }
anyhow = { version = "1", features = ["backtrace"], optional = true }
postcard = { version = "1", default-features = false, features = ["alloc", "use-std"], optional = true }
Expand All @@ -32,8 +32,33 @@ rustls = { version = "0.23", default-features = false, features = ["ring"], opti
genawaiter = { version = "0.99.1", features = ["futures03"], optional = true }
tokio = { workspace = true, optional = true }
flume = "0.11.0"
tls = { path = "../tls", optional = true }

# dependencies for the tls utils
der = { version = "0.7", features = ["alloc", "derive"], optional = true }
webpki = { package = "rustls-webpki", version = "0.102", optional = true }
x509-parser = { version = "0.16", optional = true }
thiserror = { version = "2", optional = true }
ring = { version = "0.17", optional = true }

[features]
client = ["mainline", "iroh-quinn", "tracing", "anyhow", "rcgen", "genawaiter", "rustls", "futures", "postcard", "tokio", "tls"]
client = [
"dep:mainline",
"dep:quinn",
"dep:tracing",
"dep:anyhow",
"dep:rcgen",
"dep:genawaiter",
"dep:rustls",
"dep:futures",
"dep:postcard",
"dep:tokio",
"tls-utils",
]
tls-utils = [
"dep:der",
"dep:webpki",
"dep:x509-parser",
"dep:thiserror",
"dep:ring",
]
default = ["client"]
9 changes: 9 additions & 0 deletions content-discovery/iroh-mainline-content-discovery/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Protocol and client for iroh mainline content discovery

This provides a very minimal protocol for content discovery as well as a
client library for the protocol.

## Features

- client: the client that allows querying content discovery
- tls-utils: utilities to set of quinn connections, used by client
35 changes: 20 additions & 15 deletions content-discovery/iroh-mainline-content-discovery/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@ use iroh::{
};
use iroh_blobs::HashAndFormat;

use crate::protocol::{
AnnounceKind, Query, QueryResponse, Request, Response, SignedAnnounce, ALPN, REQUEST_SIZE_LIMIT,
use crate::{
protocol::{
AnnounceKind, Query, QueryResponse, Request, Response, SignedAnnounce, ALPN,
REQUEST_SIZE_LIMIT,
},
tls_utils,
};

/// Announce to a tracker.
Expand All @@ -33,7 +37,7 @@ use crate::protocol::{
/// `content` is the content to announce.
/// `kind` is the kind of the announcement. We can claim to have the complete data or only some of it.
pub async fn announce_quinn(
connection: iroh_quinn::Connection,
connection: quinn::Connection,
signed_announce: SignedAnnounce,
) -> anyhow::Result<()> {
let (mut send, mut recv) = connection.open_bi().await?;
Expand Down Expand Up @@ -119,14 +123,14 @@ async fn query_iroh_one(

/// A connection provider that can be used to connect to a tracker.
///
/// This can either be a [`iroh_quinn::Endpoint`] where connections are created on demand,
/// This can either be a [`quinn::Endpoint`] where connections are created on demand,
/// or some sort of connection pool.
pub trait QuinnConnectionProvider<Addr>: Clone {
fn connect(&self, addr: Addr) -> BoxFuture<anyhow::Result<iroh_quinn::Connection>>;
fn connect(&self, addr: Addr) -> BoxFuture<anyhow::Result<quinn::Connection>>;
}

impl QuinnConnectionProvider<SocketAddr> for iroh_quinn::Endpoint {
fn connect(&self, addr: SocketAddr) -> BoxFuture<anyhow::Result<iroh_quinn::Connection>> {
impl QuinnConnectionProvider<SocketAddr> for quinn::Endpoint {
fn connect(&self, addr: SocketAddr) -> BoxFuture<anyhow::Result<quinn::Connection>> {
async move { Ok(self.connect(addr, "localhost")?.await?) }.boxed()
}
}
Expand Down Expand Up @@ -229,7 +233,7 @@ pub async fn query_iroh(

/// Assume an existing connection to a tracker and query it for peers for some content.
pub async fn query_quinn(
connection: iroh_quinn::Connection,
connection: quinn::Connection,
args: Query,
) -> anyhow::Result<QueryResponse> {
tracing::info!("connected to {:?}", connection.remote_address());
Expand All @@ -252,12 +256,13 @@ pub fn create_quinn_client(
bind_addr: SocketAddr,
alpn_protocols: Vec<Vec<u8>>,
keylog: bool,
) -> anyhow::Result<iroh_quinn::Endpoint> {
) -> anyhow::Result<quinn::Endpoint> {
let secret_key = iroh::SecretKey::generate(rand::thread_rng());
let tls_client_config = tls::make_client_config(&secret_key, None, alpn_protocols, keylog)?;
let mut client_config = iroh_quinn::ClientConfig::new(Arc::new(tls_client_config));
let mut endpoint = iroh_quinn::Endpoint::client(bind_addr)?;
let mut transport_config = iroh_quinn::TransportConfig::default();
let tls_client_config =
tls_utils::make_client_config(&secret_key, None, alpn_protocols, keylog)?;
let mut client_config = quinn::ClientConfig::new(Arc::new(tls_client_config));
let mut endpoint = quinn::Endpoint::client(bind_addr)?;
let mut transport_config = quinn::TransportConfig::default();
transport_config.keep_alive_interval(Some(Duration::from_secs(1)));
client_config.transport_config(Arc::new(transport_config));
endpoint.set_default_client_config(client_config);
Expand Down Expand Up @@ -340,7 +345,7 @@ pub async fn connect(

pub enum Connection {
Iroh(iroh::endpoint::Connection),
Quinn(iroh_quinn::Connection),
Quinn(quinn::Connection),
}

/// Create a iroh endpoint and connect to a tracker using the [crate::protocol::ALPN] protocol.
Expand All @@ -363,7 +368,7 @@ async fn connect_iroh(
async fn connect_socket(
tracker: SocketAddr,
local_addr: SocketAddr,
) -> anyhow::Result<iroh_quinn::Connection> {
) -> anyhow::Result<quinn::Connection> {
let endpoint = create_quinn_client(local_addr, vec![ALPN.to_vec()], false)?;
tracing::info!("trying t?o )connect to tracker at {:?}", tracker);
let connection = endpoint.connect(tracker, "localhost")?.await?;
Expand Down
2 changes: 2 additions & 0 deletions content-discovery/iroh-mainline-content-discovery/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ mod client;
pub mod protocol;
#[cfg(feature = "client")]
pub use client::*;
#[cfg(feature = "tls-utils")]
pub mod tls_utils;
3 changes: 1 addition & 2 deletions content-discovery/iroh-mainline-tracker/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ iroh-blobs = { workspace = true }
mainline = { version = "2.0.0", features = ["async"] }
pkarr = { version = "2.0.1", features = ["async"] }
postcard = { version = "1", default-features = false, features = ["alloc", "use-std"] }
iroh-quinn = "0.13"
rand = "0.8"
rcgen = "0.12.0"
redb = "1.5.0"
Expand All @@ -42,7 +41,7 @@ url = "2.5.0"
flume = "0.11.0"
genawaiter = { version = "0.99.1", features = ["futures03"] }
iroh-mainline-content-discovery = { path = "../iroh-mainline-content-discovery", features = ["client"] }
tls = { path = "../tls" }
quinn = { package = "iroh-quinn", version = "0.13" }

clap = { version = "4", features = ["derive"], optional = true }
serde-big-array = "0.5.1"
Expand Down
14 changes: 7 additions & 7 deletions content-discovery/iroh-mainline-tracker/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use std::{
use clap::Parser;
use iroh::{discovery::pkarr::dht::DhtDiscovery, Endpoint, NodeId};
use iroh_blobs::util::fs::load_secret_key;
use iroh_mainline_content_discovery::protocol::ALPN;
use iroh_mainline_content_discovery::{protocol::ALPN, tls_utils};
use iroh_mainline_tracker::{
io::{
self, load_from_file, setup_logging, tracker_home, tracker_path, CONFIG_DEBUG_FILE,
Expand Down Expand Up @@ -130,7 +130,7 @@ async fn server(args: Args) -> anyhow::Result<()> {
let udp_socket = tokio::net::UdpSocket::bind(udp_bind_addr).await?;
let quinn_bind_addr =
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, options.quinn_port));
let quinn_endpoint = iroh_quinn::Endpoint::server(server_config, quinn_bind_addr)?;
let quinn_endpoint = quinn::Endpoint::server(server_config, quinn_bind_addr)?;
// set the quinn port to the actual port we bound to so the DHT will announce it correctly
options.quinn_port = quinn_endpoint.local_addr()?.port();
let iroh_endpoint = create_endpoint(key.clone(), options.iroh_ipv4_addr, true).await?;
Expand Down Expand Up @@ -185,7 +185,7 @@ async fn main() -> anyhow::Result<()> {

/// Returns default server configuration along with its certificate.
#[allow(clippy::field_reassign_with_default)] // https://github.com/rust-lang/rust-clippy/issues/6527
fn configure_server(secret_key: &iroh::SecretKey) -> anyhow::Result<iroh_quinn::ServerConfig> {
fn configure_server(secret_key: &iroh::SecretKey) -> anyhow::Result<quinn::ServerConfig> {
make_server_config(secret_key, 8, 1024, vec![ALPN.to_vec()])
}

Expand All @@ -195,10 +195,10 @@ pub fn make_server_config(
max_streams: u64,
max_connections: u32,
alpn_protocols: Vec<Vec<u8>>,
) -> anyhow::Result<iroh_quinn::ServerConfig> {
let tls_server_config = tls::make_server_config(secret_key, alpn_protocols, false)?;
let mut server_config = iroh_quinn::ServerConfig::with_crypto(Arc::new(tls_server_config));
let mut transport_config = iroh_quinn::TransportConfig::default();
) -> anyhow::Result<quinn::ServerConfig> {
let tls_server_config = tls_utils::make_server_config(secret_key, alpn_protocols, false)?;
let mut server_config = quinn::ServerConfig::with_crypto(Arc::new(tls_server_config));
let mut transport_config = quinn::TransportConfig::default();
transport_config
.max_concurrent_bidi_streams(max_streams.try_into()?)
.max_concurrent_uni_streams(0u32.into());
Expand Down
18 changes: 9 additions & 9 deletions content-discovery/iroh-mainline-tracker/src/tracker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use iroh_mainline_content_discovery::{
AbsoluteTime, Announce, AnnounceKind, Query, QueryResponse, Request, Response,
SignedAnnounce, REQUEST_SIZE_LIMIT,
},
to_infohash,
tls_utils, to_infohash,
};
use rand::Rng;
use redb::{ReadableTable, RedbValue};
Expand Down Expand Up @@ -883,7 +883,7 @@ impl Tracker {
Ok(())
}

pub async fn quinn_accept_loop(self, endpoint: iroh_quinn::Endpoint) -> std::io::Result<()> {
pub async fn quinn_accept_loop(self, endpoint: quinn::Endpoint) -> std::io::Result<()> {
let local_addr = endpoint.local_addr()?;
println!("quinn listening on {local_addr:?}");
while let Some(incoming) = endpoint.accept().await {
Expand Down Expand Up @@ -948,7 +948,7 @@ impl Tracker {
/// Handle a single incoming connection on the tracker ALPN.
pub async fn handle_quinn_connection(
&self,
connection: iroh_quinn::Connection,
connection: quinn::Connection,
) -> anyhow::Result<()> {
tracing::debug!("calling accept_bi");
let (mut send, mut recv) = connection.accept_bi().await?;
Expand Down Expand Up @@ -1269,18 +1269,18 @@ impl Tracker {

/// Accept an incoming connection and extract the client-provided [`NodeId`] and ALPN protocol.
async fn accept_conn(
mut conn: iroh_quinn::Connecting,
) -> anyhow::Result<(NodeId, String, iroh_quinn::Connection)> {
mut conn: quinn::Connecting,
) -> anyhow::Result<(NodeId, String, quinn::Connection)> {
let alpn = get_alpn(&mut conn).await?;
let conn = conn.await?;
let node_id = get_remote_node_id(&conn)?;
Ok((node_id, alpn, conn))
}

/// Extract the ALPN protocol from the peer's TLS certificate.
pub async fn get_alpn(connecting: &mut iroh_quinn::Connecting) -> anyhow::Result<String> {
pub async fn get_alpn(connecting: &mut quinn::Connecting) -> anyhow::Result<String> {
let data = connecting.handshake_data().await?;
match data.downcast::<iroh_quinn::crypto::rustls::HandshakeData>() {
match data.downcast::<quinn::crypto::rustls::HandshakeData>() {
Ok(data) => match data.protocol {
Some(protocol) => std::string::String::from_utf8(protocol).map_err(Into::into),
None => anyhow::bail!("no ALPN protocol available"),
Expand All @@ -1289,7 +1289,7 @@ pub async fn get_alpn(connecting: &mut iroh_quinn::Connecting) -> anyhow::Result
}
}

pub fn get_remote_node_id(connection: &iroh_quinn::Connection) -> anyhow::Result<iroh::NodeId> {
pub fn get_remote_node_id(connection: &quinn::Connection) -> anyhow::Result<iroh::NodeId> {
let data = connection.peer_identity();
match data {
None => anyhow::bail!("no peer certificate found"),
Expand All @@ -1301,7 +1301,7 @@ pub fn get_remote_node_id(connection: &iroh_quinn::Connection) -> anyhow::Result
certs.len()
);
}
let cert = tls::certificate::parse(&certs[0])?;
let cert = tls_utils::certificate::parse(&certs[0])?;
Ok(cert.peer_id())
}
Err(_) => anyhow::bail!("invalid peer certificate"),
Expand Down
22 changes: 0 additions & 22 deletions content-discovery/tls/Cargo.toml

This file was deleted.