Skip to content

Commit

Permalink
feat: allow to bind to a specific address (#2694)
Browse files Browse the repository at this point in the history
## Description

Allows to specify the IPv4 and IPv6 addresses where iroh should bind to.

Closes #2565

## Breaking Changes

- changed 
  - `iroh_net::endpoint::Endpoint::bind` now takes  no arguments
- removed
  -  `iroh::node::Builder::bind_port`
- added
  -  `iroh_net::endpoint::Builder::bind_addr_v4`
  -  `iroh_net::endpoint::Builder::bind_addr_v6`
  -  `iroh::node::Builder::bind_addr_v4`
  -  `iroh::node::Builder::bind_addr_v6`
  -  `iroh::node::Builder::bind_random_port`

## Change checklist

- [x] Self-review.
- [x] Documentation updates following the [style
guide](https://rust-lang.github.io/rfcs/1574-more-api-documentation-conventions.html#appendix-a-full-conventions-text),
if relevant.
- [x] Tests if relevant.
- [x] All breaking changes documented.

---------

Co-authored-by: Kasey <kasey@n0.computer>
Co-authored-by: Floris Bruynooghe <flub@n0.computer>
  • Loading branch information
3 people authored Sep 5, 2024
1 parent b8c0513 commit 2e5188a
Show file tree
Hide file tree
Showing 20 changed files with 180 additions and 96 deletions.
2 changes: 1 addition & 1 deletion iroh-cli/src/commands/doctor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -668,7 +668,7 @@ async fn make_endpoint(
Some(relay_map) => endpoint.relay_mode(RelayMode::Custom(relay_map)),
None => endpoint,
};
let endpoint = endpoint.bind(0).await?;
let endpoint = endpoint.bind().await?;

tokio::time::timeout(Duration::from_secs(10), endpoint.direct_addresses().next())
.await
Expand Down
10 changes: 8 additions & 2 deletions iroh-gossip/examples/chat.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
use std::{collections::HashMap, fmt, str::FromStr};
use std::{
collections::HashMap,
fmt,
net::{Ipv4Addr, SocketAddrV4},
str::FromStr,
};

use anyhow::{bail, Context, Result};
use bytes::Bytes;
Expand Down Expand Up @@ -106,7 +111,8 @@ async fn main() -> Result<()> {
.secret_key(secret_key)
.alpns(vec![GOSSIP_ALPN.to_vec()])
.relay_mode(relay_mode)
.bind(args.bind_port)
.bind_addr_v4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, args.bind_port))
.bind()
.await?;
println!("> our node id: {}", endpoint.node_id());

Expand Down
2 changes: 1 addition & 1 deletion iroh-gossip/src/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -845,7 +845,7 @@ mod test {
.alpns(vec![GOSSIP_ALPN.to_vec()])
.relay_mode(RelayMode::Custom(relay_map))
.insecure_skip_relay_cert_verify(true)
.bind(0)
.bind()
.await?;

ep.watch_home_relay().next().await;
Expand Down
4 changes: 2 additions & 2 deletions iroh-net/bench/src/iroh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub fn server_endpoint(
.alpns(vec![ALPN.to_vec()])
.relay_mode(relay_mode)
.transport_config(transport_config(opt.max_streams, opt.initial_mtu))
.bind(0)
.bind()
.await
.unwrap();

Expand Down Expand Up @@ -97,7 +97,7 @@ pub async fn connect_client(
.alpns(vec![ALPN.to_vec()])
.relay_mode(relay_mode)
.transport_config(transport_config(opt.max_streams, opt.initial_mtu))
.bind(0)
.bind()
.await
.unwrap();

Expand Down
4 changes: 2 additions & 2 deletions iroh-net/examples/connect-unreliable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ async fn main() -> anyhow::Result<()> {
// Use `RelayMode::Disable` to disable holepunching and relaying over HTTPS
// If you want to experiment with relaying using your own relay server, you must pass in the same custom relay url to both the `listen` code AND the `connect` code
.relay_mode(RelayMode::Default)
// You can choose a port to bind to, but passing in `0` will bind the socket to a random available port
.bind(0)
// You can choose an address to bind to, but passing in `None` will bind the socket to a random available port
.bind()
.await?;

let me = endpoint.node_id();
Expand Down
4 changes: 2 additions & 2 deletions iroh-net/examples/connect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ async fn main() -> anyhow::Result<()> {
// Use `RelayMode::Disable` to disable holepunching and relaying over HTTPS
// If you want to experiment with relaying using your own relay server, you must pass in the same custom relay url to both the `listen` code AND the `connect` code
.relay_mode(RelayMode::Default)
// You can choose a port to bind to, but passing in `0` will bind the socket to a random available port
.bind(0)
// You can choose an address to bind to, but passing in `None` will bind the socket to a random available port
.bind()
.await?;

let me = endpoint.node_id();
Expand Down
4 changes: 2 additions & 2 deletions iroh-net/examples/dht_discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ async fn chat_server(args: Args) -> anyhow::Result<()> {
.alpns(vec![CHAT_ALPN.to_vec()])
.secret_key(secret_key)
.discovery(Box::new(discovery))
.bind(0)
.bind()
.await?;
let zid = pkarr::PublicKey::try_from(node_id.as_bytes())?.to_z32();
println!("Listening on {}", node_id);
Expand Down Expand Up @@ -115,7 +115,7 @@ async fn chat_client(args: Args) -> anyhow::Result<()> {
let endpoint = Endpoint::builder()
.secret_key(secret_key)
.discovery(Box::new(discovery))
.bind(0)
.bind()
.await?;
println!("We are {} and connecting to {}", node_id, remote_node_id);
let connection = endpoint
Expand Down
2 changes: 1 addition & 1 deletion iroh-net/examples/listen-unreliable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ async fn main() -> anyhow::Result<()> {
// If you want to experiment with relaying using your own relay server, you must pass in the same custom relay url to both the `listen` code AND the `connect` code
.relay_mode(RelayMode::Default)
// you can choose a port to bind to, but passing in `0` will bind the socket to a random available port
.bind(0)
.bind()
.await?;

let me = endpoint.node_id();
Expand Down
2 changes: 1 addition & 1 deletion iroh-net/examples/listen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ async fn main() -> anyhow::Result<()> {
// If you want to experiment with relaying using your own relay server, you must pass in the same custom relay url to both the `listen` code AND the `connect` code
.relay_mode(RelayMode::Default)
// you can choose a port to bind to, but passing in `0` will bind the socket to a random available port
.bind(0)
.bind()
.await?;

let me = endpoint.node_id();
Expand Down
4 changes: 2 additions & 2 deletions iroh-net/src/discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ mod tests {
.discovery(Box::new(disco))
.relay_mode(RelayMode::Disabled)
.alpns(vec![TEST_ALPN.to_vec()])
.bind(0)
.bind()
.await
.unwrap();

Expand Down Expand Up @@ -762,7 +762,7 @@ mod test_dns_pkarr {
.alpns(vec![TEST_ALPN.to_vec()])
.dns_resolver(dns_pkarr_server.dns_resolver())
.discovery(dns_pkarr_server.discovery(secret_key))
.bind(0)
.bind()
.await?;

let handle = tokio::spawn({
Expand Down
2 changes: 1 addition & 1 deletion iroh-net/src/discovery/local_swarm_discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ mod tests {
};

// pass in endpoint, this is never used
let ep = crate::endpoint::Builder::default().bind(0).await?;
let ep = crate::endpoint::Builder::default().bind().await?;
// resolve twice to ensure we can create separate streams for the same node_id
let mut s1 = discovery_a.resolve(ep.clone(), node_id_b).unwrap();
let mut s2 = discovery_a.resolve(ep, node_id_b).unwrap();
Expand Down
2 changes: 1 addition & 1 deletion iroh-net/src/discovery/pkarr/dht.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ mod tests {
#[ignore = "flaky"]
async fn dht_discovery_smoke() -> TestResult {
let _ = tracing_subscriber::fmt::try_init();
let ep = crate::Endpoint::builder().bind(0).await?;
let ep = crate::Endpoint::builder().bind().await?;
let secret = ep.secret_key().clone();
let testnet = mainline::dht::Testnet::new(2);
let settings = pkarr::Settings {
Expand Down
63 changes: 42 additions & 21 deletions iroh-net/src/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
use std::any::Any;
use std::future::{Future, IntoFuture};
use std::net::{IpAddr, SocketAddr};
use std::net::{IpAddr, SocketAddr, SocketAddrV4, SocketAddrV6};
use std::pin::Pin;
use std::sync::Arc;
use std::task::Poll;
Expand Down Expand Up @@ -81,6 +81,8 @@ pub struct Builder {
dns_resolver: Option<DnsResolver>,
#[cfg(any(test, feature = "test-utils"))]
insecure_skip_relay_cert_verify: bool,
addr_v4: Option<SocketAddrV4>,
addr_v6: Option<SocketAddrV6>,
}

impl Default for Builder {
Expand All @@ -97,6 +99,8 @@ impl Default for Builder {
dns_resolver: None,
#[cfg(any(test, feature = "test-utils"))]
insecure_skip_relay_cert_verify: false,
addr_v4: None,
addr_v6: None,
}
}
}
Expand All @@ -107,14 +111,8 @@ impl Builder {

// # The final constructor that everyone needs.

/// Binds the magic endpoint on the specified socket address.
///
/// The *bind_port* is the port that should be bound locally.
/// The port will be used to bind an IPv4 and, if supported, and IPv6 socket.
/// You can pass `0` to let the operating system choose a free port for you.
///
/// NOTE: This will be improved soon to add support for binding on specific addresses.
pub async fn bind(self, bind_port: u16) -> Result<Endpoint> {
/// Binds the magic endpoint.
pub async fn bind(self) -> Result<Endpoint> {
let relay_map = self.relay_mode.relay_map();
let secret_key = self.secret_key.unwrap_or_else(SecretKey::generate);
let static_config = StaticConfig {
Expand All @@ -127,7 +125,8 @@ impl Builder {
.unwrap_or_else(|| default_resolver().clone());

let msock_opts = magicsock::Options {
port: bind_port,
addr_v4: self.addr_v4,
addr_v6: self.addr_v6,
secret_key,
relay_map,
node_map: self.node_map,
Expand All @@ -142,6 +141,28 @@ impl Builder {

// # The very common methods everyone basically needs.

/// Sets the IPv4 bind address.
///
/// Setting the port to `0` will use a random port.
/// If the port specified is already in use, it will fallback to choosing a random port.
///
/// By default will use `0.0.0.0:0` to bind to.
pub fn bind_addr_v4(mut self, addr: SocketAddrV4) -> Self {
self.addr_v4.replace(addr);
self
}

/// Sets the IPv6 bind address.
///
/// Setting the port to `0` will use a random port.
/// If the port specified is already in use, it will fallback to choosing a random port.
///
/// By default will use `[::]:0` to bind to.
pub fn bind_addr_v6(mut self, addr: SocketAddrV6) -> Self {
self.addr_v6.replace(addr);
self
}

/// Sets a secret key to authenticate with other peers.
///
/// This secret key's public key will be the [`PublicKey`] of this endpoint and thus
Expand Down Expand Up @@ -683,7 +704,7 @@ impl Endpoint {
///
/// # let rt = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();
/// # rt.block_on(async move {
/// let mep = Endpoint::builder().bind(0).await.unwrap();
/// let mep = Endpoint::builder().bind().await.unwrap();
/// let _addrs = mep.direct_addresses().next().await;
/// # });
/// ```
Expand Down Expand Up @@ -1258,7 +1279,7 @@ mod tests {
let _guard = iroh_test::logging::setup();
let ep = Endpoint::builder()
.alpns(vec![TEST_ALPN.to_vec()])
.bind(0)
.bind()
.await
.unwrap();
let my_addr = ep.node_addr().await.unwrap();
Expand Down Expand Up @@ -1289,7 +1310,7 @@ mod tests {
.alpns(vec![TEST_ALPN.to_vec()])
.relay_mode(RelayMode::Custom(relay_map))
.insecure_skip_relay_cert_verify(true)
.bind(0)
.bind()
.await
.unwrap();
info!("accepting connection");
Expand Down Expand Up @@ -1324,7 +1345,7 @@ mod tests {
.alpns(vec![TEST_ALPN.to_vec()])
.relay_mode(RelayMode::Custom(relay_map))
.insecure_skip_relay_cert_verify(true)
.bind(0)
.bind()
.await
.unwrap();
info!("client connecting");
Expand Down Expand Up @@ -1386,7 +1407,7 @@ mod tests {
}
builder
.alpns(vec![TEST_ALPN.to_vec()])
.bind(0)
.bind()
.await
.unwrap()
}
Expand Down Expand Up @@ -1443,7 +1464,7 @@ mod tests {
.secret_key(server_secret_key)
.alpns(vec![TEST_ALPN.to_vec()])
.relay_mode(RelayMode::Custom(relay_map))
.bind(0)
.bind()
.await
.unwrap();
let eps = ep.bound_sockets();
Expand Down Expand Up @@ -1489,7 +1510,7 @@ mod tests {
.insecure_skip_relay_cert_verify(true)
.relay_mode(RelayMode::Custom(relay_map))
.secret_key(client_secret_key)
.bind(0)
.bind()
.await
.unwrap();
let eps = ep.bound_sockets();
Expand Down Expand Up @@ -1534,13 +1555,13 @@ mod tests {
let ep1 = Endpoint::builder()
.alpns(vec![TEST_ALPN.to_vec()])
.relay_mode(RelayMode::Disabled)
.bind(0)
.bind()
.await
.unwrap();
let ep2 = Endpoint::builder()
.alpns(vec![TEST_ALPN.to_vec()])
.relay_mode(RelayMode::Disabled)
.bind(0)
.bind()
.await
.unwrap();
let ep1_nodeaddr = ep1.node_addr().await.unwrap();
Expand Down Expand Up @@ -1631,15 +1652,15 @@ mod tests {
.insecure_skip_relay_cert_verify(true)
.alpns(vec![TEST_ALPN.to_vec()])
.relay_mode(RelayMode::Custom(relay_map.clone()))
.bind(0)
.bind()
.await
.unwrap();
let ep2 = Endpoint::builder()
.secret_key(ep2_secret_key)
.insecure_skip_relay_cert_verify(true)
.alpns(vec![TEST_ALPN.to_vec()])
.relay_mode(RelayMode::Custom(relay_map))
.bind(0)
.bind()
.await
.unwrap();

Expand Down
Loading

0 comments on commit 2e5188a

Please sign in to comment.