iroh_net/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
//! Peer-to-peer QUIC connections.
//!
//! iroh-net is a library to establish direct connectivity between peers.  It exposes an
//! interface to [QUIC] connections and streams to the user, while implementing direct
//! connectivity using [hole punching] complemented by relay servers under the hood.
//!
//! An iroh-net node is created and controlled by the [`Endpoint`], e.g. connecting to
//! another node:
//!
//! ```no_run
//! # use iroh_net::{Endpoint, NodeAddr};
//! # async fn wrapper() -> testresult::TestResult {
//! let addr: NodeAddr = todo!();
//! let ep = Endpoint::builder().bind().await?;
//! let conn = ep.connect(addr, b"my-alpn").await?;
//! let mut send_stream = conn.open_uni().await?;
//! send_stream.write_all(b"msg").await?;
//! # Ok(())
//! # }
//! ```
//!
//! The other node can accept incoming connections using the [`Endpoint`] as well:
//!
//! ```no_run
//! # use iroh_net::{Endpoint, NodeAddr};
//! # async fn wrapper() -> testresult::TestResult {
//! let ep = Endpoint::builder()
//!     .alpns(vec![b"my-alpn".to_vec()])
//!     .bind()
//!     .await?;
//! let conn = ep.accept().await.ok_or("err")?.await?;
//! let mut recv_stream = conn.accept_uni().await?;
//! let mut buf = [0u8; 3];
//! recv_stream.read_exact(&mut buf).await?;
//! # Ok(())
//! # }
//! ```
//!
//! Of course you can also use [bi-directional streams] or any other features from QUIC.
//!
//! For more elaborate examples, see [below](#examples) or the examples directory in
//! the source repository.
//!
//!
//! # Connection Establishment
//!
//! An iroh-net connection between two iroh-net nodes is usually established with the help
//! of a Relay server.  When creating the [`Endpoint`] it connects to the closest Relay
//! server and designates this as the *home relay*.  When other nodes want to connect they
//! first establish connection via this home relay.  As soon as connection between the two
//! nodes is established they will attempt to create a direct connection, using [hole
//! punching] if needed.  Once the direct connection is established the relay server is no
//! longer involved in the connection.
//!
//! If one of the iroh-net nodes can be reached directly, connectivity can also be
//! established without involving a Relay server.  This is done by using the node's
//! listening addresses in the connection establishement instead of the [`RelayUrl`] which
//! is used to identify a Relay server.  Of course it is also possible to use both a
//! [`RelayUrl`] and direct addresses at the same time to connect.
//!
//!
//! # Encryption
//!
//! The connection is encrypted using TLS, like standard QUIC connections.  Unlike standard
//! QUIC there is no client, server or server TLS key and certificate chain.  Instead each iroh-net node has a
//! unique [`SecretKey`] used to authenticate and encrypt the connection.  When an iroh-net
//! node connects, it uses the corresponding [`PublicKey`] to ensure the connection is only
//! established with the intended peer.
//!
//! Since the [`PublicKey`] is also used to identify the iroh-net node it is also known as
//! the [`NodeId`].  As encryption is an integral part of TLS as used in QUIC this
//! [`NodeId`] is always a required parameter to establish a connection.
//!
//! When accepting connections the peer's [`NodeId`] is authenticated.  However it is up to
//! the application to decide if a particular peer is allowed to connect or not.
//!
//!
//! # Relay Servers
//!
//! Relay servers exist to ensure all iroh-net nodes are always reachable.  They accept
//! **encrypted** traffic for iroh-net nodes which are connected to them, forwarding it to
//! the correct destination based on the [`NodeId`] only.  Since nodes only send encrypted
//! traffic, the Relay servers can not decode any traffic for other iroh-net nodes and only
//! forward it.
//!
//! The connections to the Relay server are initiated as normal HTTP 1.1 connections using
//! TLS.  Once connected the transport is upgraded to a plain TCP connection using a custom
//! protocol.  All further data is then sent using this custom relaying protocol.  Usually
//! soon after the connection is established via the Relay it will migrate to a direct
//! connection.  However if this is not possible the connection will keep flowing over the
//! relay server as a fallback.
//!
//! Additionally to providing reliable connectivity between iroh-net nodes, Relay servers
//! provide some functions to assist in [hole punching].  They have various services to help
//! nodes understand their own network situation.  This includes offering a [STUN] server,
//! but also a few HTTP extra endpoints as well as responding to ICMP echo requests.
//!
//! By default the [number 0] relay servers are used, see [`RelayMode::Default`].
//!
//!
//! # Connections and Streams
//!
//! An iroh-net node is managed using the [`Endpoint`] and this is used to create or accept
//! connections to other nodes.  To establish a connection to an iroh-net node you need to
//! know three pieces of information:
//!
//! - The [`NodeId`] of the peer to connect to.
//! - Some addressing information:
//!   - Usually the [`RelayUrl`] identifying the Relay server.
//!   - Sometimes, or usually additionally, any direct addresses which might be known.
//! - The QUIC/TLS Application-Layer Protocol Negotiation, or [ALPN], name to use.
//!
//! The ALPN is used by both sides to agree on which application-specific protocol will be
//! used over the resulting QUIC connection.  These can be protocols like `h3` used for
//! [HTTP/3][HTTP3], but more commonly will be a custom identifier for the application.
//!
//! Once connected the API exposes QUIC streams.  These are very cheap to create so can be
//! created at any time and can be used to create very many short-lived stream as well as
//! long-lived streams.  There are two stream types to choose from:
//!
//! - **Uni-directional** which only allows the peer which initiated the stream to send
//!   data.
//!
//! - **Bi-directional** which allows both peers to send and receive data.  However, the
//!   initiator of this stream has to send data before the peer will be aware of this
//!   stream.
//!
//! Additionally to being extremely light-weight, streams can be interleaved and will not
//! block each other.  Allowing many streams to co-exist, regardless of how long they last.
//!
//! <div class="warning">
//!
//! To keep streams cheap, they are lazily created on the network: only once a sender starts
//! sending data on the stream will the receiver become aware of a stream.  This means only
//! calling [`Connection::open_bi`] is not sufficient for the corresponding call to
//! [`Connection::accept_bi`] to return.  The sender **must** send data on the stream before
//! the receiver's [`Connection::accept_bi`] call will return.
//!
//! </div>
//!
//! ## Node Discovery
//!
//! The need to know the [`RelayUrl`] *or* some direct addresses in addition to the
//! [`NodeId`] to connect to an iroh-net node can be an obstacle.  To address this the
//! [`endpoint::Builder`] allows to configure a [`discovery`] service.
//!
//! The [`DnsDiscovery`] service is a discovery service which will publish the [`RelayUrl`]
//! and direct addresses to a service publishing those as DNS records.  To connect it looks
//! up the [`NodeId`] in the DNS system to find the addressing details.  This enables
//! connecting using only the [`NodeId`] which is often more convenient and resilient.
//!
//! See [the discovery module] for more details.
//!
//!
//! # Examples
//!
//! The central struct is the [`Endpoint`], which allows you to connect to other nodes:
//!
//! ```no_run
//! use anyhow::Result;
//! use iroh_net::{Endpoint, NodeAddr};
//!
//! async fn connect(addr: NodeAddr) -> Result<()> {
//!     // The Endpoint is the central object that manages an iroh-net node.
//!     let ep = Endpoint::builder().bind().await?;
//!
//!     // Establish a QUIC connection, open a bi-directional stream, exchange messages.
//!     let conn = ep.connect(addr, b"hello-world").await?;
//!     let (mut send_stream, mut recv_stream) = conn.open_bi().await?;
//!     send_stream.write_all(b"hello").await?;
//!     send_stream.finish()?;
//!     let _msg = recv_stream.read_to_end(10).await?;
//!
//!     // Gracefully close the connection and endpoint.
//!     conn.close(1u8.into(), b"done");
//!     ep.close(0u8.into(), b"ep closing").await?;
//!     println!("Client closed");
//!     Ok(())
//! }
//! ```
//!
//! Every [`Endpoint`] can also accept connections:
//!
//! ```no_run
//! use anyhow::{Context, Result};
//! use futures_lite::StreamExt;
//! use iroh_net::ticket::NodeTicket;
//! use iroh_net::{Endpoint, NodeAddr};
//!
//! async fn accept() -> Result<()> {
//!     // To accept connections at least one ALPN must be configured.
//!     let ep = Endpoint::builder()
//!         .alpns(vec![b"hello-world".to_vec()])
//!         .bind()
//!         .await?;
//!
//!     // Accept a QUIC connection, accept a bi-directional stream, exchange messages.
//!     let conn = ep.accept().await.context("no incoming connection")?.await?;
//!     let (mut send_stream, mut recv_stream) = conn.accept_bi().await?;
//!     let _msg = recv_stream.read_to_end(10).await?;
//!     send_stream.write_all(b"world").await?;
//!     send_stream.finish()?;
//!
//!     // Wait for the client to close the connection and gracefully close the endpoint.
//!     conn.closed().await;
//!     ep.close(0u8.into(), b"ep closing").await?;
//!     Ok(())
//! }
//! ```
//!
//! Please see the examples directory for more nuanced examples.
//!
//!
//! [QUIC]: https://quickwg.org
//! [bi-directional streams]: crate::endpoint::Connection::open_bi
//! [`NodeTicket`]: crate::ticket::NodeTicket
//! [hole punching]: https://en.wikipedia.org/wiki/Hole_punching_(networking)
//! [socket addresses]: https://doc.rust-lang.org/stable/std/net/enum.SocketAddr.html
//! [STUN]: https://en.wikipedia.org/wiki/STUN
//! [ALPN]: https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation
//! [HTTP3]: https://en.wikipedia.org/wiki/HTTP/3
//! [`SecretKey`]: crate::key::SecretKey
//! [`PublicKey`]: crate::key::PublicKey
//! [`RelayUrl`]: crate::relay::RelayUrl
//! [`discovery`]: crate::endpoint::Builder::discovery
//! [`DnsDiscovery`]: crate::discovery::dns::DnsDiscovery
//! [number 0]: https://n0.computer
//! [`RelayMode::Default`]: crate::relay::RelayMode::Default
//! [the discovery module]: crate::discovery
//! [`Connection::open_bi`]: crate::endpoint::Connection::open_bi
//! [`Connection::accept_bi`]: crate::endpoint::Connection::accept_bi

#![recursion_limit = "256"]
#![deny(missing_docs, rustdoc::broken_intra_doc_links)]
#![cfg_attr(iroh_docsrs, feature(doc_cfg))]

pub mod defaults;
pub mod dialer;
mod disco;
pub mod discovery;
pub mod dns;
pub mod endpoint;
mod magicsock;
pub mod metrics;
pub mod netcheck;
pub mod ping;
pub mod relay;
pub mod stun;
pub mod ticket;
pub mod tls;
pub(crate) mod util;

pub use endpoint::{AddrInfo, Endpoint, NodeAddr};
pub use iroh_base::{key, key::NodeId};

#[cfg(any(test, feature = "test-utils"))]
#[cfg_attr(iroh_docsrs, doc(cfg(any(test, feature = "test-utils"))))]
pub mod test_utils;