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