Skip to content

Commit 2e5188a

Browse files
dignifiedquireramfoxflub
authored
feat: allow to bind to a specific address (#2694)
## 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 <[email protected]> Co-authored-by: Floris Bruynooghe <[email protected]>
1 parent b8c0513 commit 2e5188a

File tree

20 files changed

+180
-96
lines changed

20 files changed

+180
-96
lines changed

iroh-cli/src/commands/doctor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -668,7 +668,7 @@ async fn make_endpoint(
668668
Some(relay_map) => endpoint.relay_mode(RelayMode::Custom(relay_map)),
669669
None => endpoint,
670670
};
671-
let endpoint = endpoint.bind(0).await?;
671+
let endpoint = endpoint.bind().await?;
672672

673673
tokio::time::timeout(Duration::from_secs(10), endpoint.direct_addresses().next())
674674
.await

iroh-gossip/examples/chat.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
use std::{collections::HashMap, fmt, str::FromStr};
1+
use std::{
2+
collections::HashMap,
3+
fmt,
4+
net::{Ipv4Addr, SocketAddrV4},
5+
str::FromStr,
6+
};
27

38
use anyhow::{bail, Context, Result};
49
use bytes::Bytes;
@@ -106,7 +111,8 @@ async fn main() -> Result<()> {
106111
.secret_key(secret_key)
107112
.alpns(vec![GOSSIP_ALPN.to_vec()])
108113
.relay_mode(relay_mode)
109-
.bind(args.bind_port)
114+
.bind_addr_v4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, args.bind_port))
115+
.bind()
110116
.await?;
111117
println!("> our node id: {}", endpoint.node_id());
112118

iroh-gossip/src/net.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -845,7 +845,7 @@ mod test {
845845
.alpns(vec![GOSSIP_ALPN.to_vec()])
846846
.relay_mode(RelayMode::Custom(relay_map))
847847
.insecure_skip_relay_cert_verify(true)
848-
.bind(0)
848+
.bind()
849849
.await?;
850850

851851
ep.watch_home_relay().next().await;

iroh-net/bench/src/iroh.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ pub fn server_endpoint(
4141
.alpns(vec![ALPN.to_vec()])
4242
.relay_mode(relay_mode)
4343
.transport_config(transport_config(opt.max_streams, opt.initial_mtu))
44-
.bind(0)
44+
.bind()
4545
.await
4646
.unwrap();
4747

@@ -97,7 +97,7 @@ pub async fn connect_client(
9797
.alpns(vec![ALPN.to_vec()])
9898
.relay_mode(relay_mode)
9999
.transport_config(transport_config(opt.max_streams, opt.initial_mtu))
100-
.bind(0)
100+
.bind()
101101
.await
102102
.unwrap();
103103

iroh-net/examples/connect-unreliable.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ async fn main() -> anyhow::Result<()> {
5252
// Use `RelayMode::Disable` to disable holepunching and relaying over HTTPS
5353
// 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
5454
.relay_mode(RelayMode::Default)
55-
// You can choose a port to bind to, but passing in `0` will bind the socket to a random available port
56-
.bind(0)
55+
// You can choose an address to bind to, but passing in `None` will bind the socket to a random available port
56+
.bind()
5757
.await?;
5858

5959
let me = endpoint.node_id();

iroh-net/examples/connect.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ async fn main() -> anyhow::Result<()> {
4949
// Use `RelayMode::Disable` to disable holepunching and relaying over HTTPS
5050
// 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
5151
.relay_mode(RelayMode::Default)
52-
// You can choose a port to bind to, but passing in `0` will bind the socket to a random available port
53-
.bind(0)
52+
// You can choose an address to bind to, but passing in `None` will bind the socket to a random available port
53+
.bind()
5454
.await?;
5555

5656
let me = endpoint.node_id();

iroh-net/examples/dht_discovery.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ async fn chat_server(args: Args) -> anyhow::Result<()> {
7070
.alpns(vec![CHAT_ALPN.to_vec()])
7171
.secret_key(secret_key)
7272
.discovery(Box::new(discovery))
73-
.bind(0)
73+
.bind()
7474
.await?;
7575
let zid = pkarr::PublicKey::try_from(node_id.as_bytes())?.to_z32();
7676
println!("Listening on {}", node_id);
@@ -115,7 +115,7 @@ async fn chat_client(args: Args) -> anyhow::Result<()> {
115115
let endpoint = Endpoint::builder()
116116
.secret_key(secret_key)
117117
.discovery(Box::new(discovery))
118-
.bind(0)
118+
.bind()
119119
.await?;
120120
println!("We are {} and connecting to {}", node_id, remote_node_id);
121121
let connection = endpoint

iroh-net/examples/listen-unreliable.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ async fn main() -> anyhow::Result<()> {
3030
// 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
3131
.relay_mode(RelayMode::Default)
3232
// you can choose a port to bind to, but passing in `0` will bind the socket to a random available port
33-
.bind(0)
33+
.bind()
3434
.await?;
3535

3636
let me = endpoint.node_id();

iroh-net/examples/listen.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ async fn main() -> anyhow::Result<()> {
3535
// 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
3636
.relay_mode(RelayMode::Default)
3737
// you can choose a port to bind to, but passing in `0` will bind the socket to a random available port
38-
.bind(0)
38+
.bind()
3939
.await?;
4040

4141
let me = endpoint.node_id();

iroh-net/src/discovery.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,7 @@ mod tests {
594594
.discovery(Box::new(disco))
595595
.relay_mode(RelayMode::Disabled)
596596
.alpns(vec![TEST_ALPN.to_vec()])
597-
.bind(0)
597+
.bind()
598598
.await
599599
.unwrap();
600600

@@ -762,7 +762,7 @@ mod test_dns_pkarr {
762762
.alpns(vec![TEST_ALPN.to_vec()])
763763
.dns_resolver(dns_pkarr_server.dns_resolver())
764764
.discovery(dns_pkarr_server.discovery(secret_key))
765-
.bind(0)
765+
.bind()
766766
.await?;
767767

768768
let handle = tokio::spawn({

iroh-net/src/discovery/local_swarm_discovery.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ mod tests {
308308
};
309309

310310
// pass in endpoint, this is never used
311-
let ep = crate::endpoint::Builder::default().bind(0).await?;
311+
let ep = crate::endpoint::Builder::default().bind().await?;
312312
// resolve twice to ensure we can create separate streams for the same node_id
313313
let mut s1 = discovery_a.resolve(ep.clone(), node_id_b).unwrap();
314314
let mut s2 = discovery_a.resolve(ep, node_id_b).unwrap();

iroh-net/src/discovery/pkarr/dht.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ mod tests {
399399
#[ignore = "flaky"]
400400
async fn dht_discovery_smoke() -> TestResult {
401401
let _ = tracing_subscriber::fmt::try_init();
402-
let ep = crate::Endpoint::builder().bind(0).await?;
402+
let ep = crate::Endpoint::builder().bind().await?;
403403
let secret = ep.secret_key().clone();
404404
let testnet = mainline::dht::Testnet::new(2);
405405
let settings = pkarr::Settings {

iroh-net/src/endpoint.rs

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
1414
use std::any::Any;
1515
use std::future::{Future, IntoFuture};
16-
use std::net::{IpAddr, SocketAddr};
16+
use std::net::{IpAddr, SocketAddr, SocketAddrV4, SocketAddrV6};
1717
use std::pin::Pin;
1818
use std::sync::Arc;
1919
use std::task::Poll;
@@ -81,6 +81,8 @@ pub struct Builder {
8181
dns_resolver: Option<DnsResolver>,
8282
#[cfg(any(test, feature = "test-utils"))]
8383
insecure_skip_relay_cert_verify: bool,
84+
addr_v4: Option<SocketAddrV4>,
85+
addr_v6: Option<SocketAddrV6>,
8486
}
8587

8688
impl Default for Builder {
@@ -97,6 +99,8 @@ impl Default for Builder {
9799
dns_resolver: None,
98100
#[cfg(any(test, feature = "test-utils"))]
99101
insecure_skip_relay_cert_verify: false,
102+
addr_v4: None,
103+
addr_v6: None,
100104
}
101105
}
102106
}
@@ -107,14 +111,8 @@ impl Builder {
107111

108112
// # The final constructor that everyone needs.
109113

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

129127
let msock_opts = magicsock::Options {
130-
port: bind_port,
128+
addr_v4: self.addr_v4,
129+
addr_v6: self.addr_v6,
131130
secret_key,
132131
relay_map,
133132
node_map: self.node_map,
@@ -142,6 +141,28 @@ impl Builder {
142141

143142
// # The very common methods everyone basically needs.
144143

144+
/// Sets the IPv4 bind address.
145+
///
146+
/// Setting the port to `0` will use a random port.
147+
/// If the port specified is already in use, it will fallback to choosing a random port.
148+
///
149+
/// By default will use `0.0.0.0:0` to bind to.
150+
pub fn bind_addr_v4(mut self, addr: SocketAddrV4) -> Self {
151+
self.addr_v4.replace(addr);
152+
self
153+
}
154+
155+
/// Sets the IPv6 bind address.
156+
///
157+
/// Setting the port to `0` will use a random port.
158+
/// If the port specified is already in use, it will fallback to choosing a random port.
159+
///
160+
/// By default will use `[::]:0` to bind to.
161+
pub fn bind_addr_v6(mut self, addr: SocketAddrV6) -> Self {
162+
self.addr_v6.replace(addr);
163+
self
164+
}
165+
145166
/// Sets a secret key to authenticate with other peers.
146167
///
147168
/// This secret key's public key will be the [`PublicKey`] of this endpoint and thus
@@ -683,7 +704,7 @@ impl Endpoint {
683704
///
684705
/// # let rt = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();
685706
/// # rt.block_on(async move {
686-
/// let mep = Endpoint::builder().bind(0).await.unwrap();
707+
/// let mep = Endpoint::builder().bind().await.unwrap();
687708
/// let _addrs = mep.direct_addresses().next().await;
688709
/// # });
689710
/// ```
@@ -1258,7 +1279,7 @@ mod tests {
12581279
let _guard = iroh_test::logging::setup();
12591280
let ep = Endpoint::builder()
12601281
.alpns(vec![TEST_ALPN.to_vec()])
1261-
.bind(0)
1282+
.bind()
12621283
.await
12631284
.unwrap();
12641285
let my_addr = ep.node_addr().await.unwrap();
@@ -1289,7 +1310,7 @@ mod tests {
12891310
.alpns(vec![TEST_ALPN.to_vec()])
12901311
.relay_mode(RelayMode::Custom(relay_map))
12911312
.insecure_skip_relay_cert_verify(true)
1292-
.bind(0)
1313+
.bind()
12931314
.await
12941315
.unwrap();
12951316
info!("accepting connection");
@@ -1324,7 +1345,7 @@ mod tests {
13241345
.alpns(vec![TEST_ALPN.to_vec()])
13251346
.relay_mode(RelayMode::Custom(relay_map))
13261347
.insecure_skip_relay_cert_verify(true)
1327-
.bind(0)
1348+
.bind()
13281349
.await
13291350
.unwrap();
13301351
info!("client connecting");
@@ -1386,7 +1407,7 @@ mod tests {
13861407
}
13871408
builder
13881409
.alpns(vec![TEST_ALPN.to_vec()])
1389-
.bind(0)
1410+
.bind()
13901411
.await
13911412
.unwrap()
13921413
}
@@ -1443,7 +1464,7 @@ mod tests {
14431464
.secret_key(server_secret_key)
14441465
.alpns(vec![TEST_ALPN.to_vec()])
14451466
.relay_mode(RelayMode::Custom(relay_map))
1446-
.bind(0)
1467+
.bind()
14471468
.await
14481469
.unwrap();
14491470
let eps = ep.bound_sockets();
@@ -1489,7 +1510,7 @@ mod tests {
14891510
.insecure_skip_relay_cert_verify(true)
14901511
.relay_mode(RelayMode::Custom(relay_map))
14911512
.secret_key(client_secret_key)
1492-
.bind(0)
1513+
.bind()
14931514
.await
14941515
.unwrap();
14951516
let eps = ep.bound_sockets();
@@ -1534,13 +1555,13 @@ mod tests {
15341555
let ep1 = Endpoint::builder()
15351556
.alpns(vec![TEST_ALPN.to_vec()])
15361557
.relay_mode(RelayMode::Disabled)
1537-
.bind(0)
1558+
.bind()
15381559
.await
15391560
.unwrap();
15401561
let ep2 = Endpoint::builder()
15411562
.alpns(vec![TEST_ALPN.to_vec()])
15421563
.relay_mode(RelayMode::Disabled)
1543-
.bind(0)
1564+
.bind()
15441565
.await
15451566
.unwrap();
15461567
let ep1_nodeaddr = ep1.node_addr().await.unwrap();
@@ -1631,15 +1652,15 @@ mod tests {
16311652
.insecure_skip_relay_cert_verify(true)
16321653
.alpns(vec![TEST_ALPN.to_vec()])
16331654
.relay_mode(RelayMode::Custom(relay_map.clone()))
1634-
.bind(0)
1655+
.bind()
16351656
.await
16361657
.unwrap();
16371658
let ep2 = Endpoint::builder()
16381659
.secret_key(ep2_secret_key)
16391660
.insecure_skip_relay_cert_verify(true)
16401661
.alpns(vec![TEST_ALPN.to_vec()])
16411662
.relay_mode(RelayMode::Custom(relay_map))
1642-
.bind(0)
1663+
.bind()
16431664
.await
16441665
.unwrap();
16451666

0 commit comments

Comments
 (0)