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