Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions misc/webrtc-utils/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## 0.5.0

- Add `sdp::offer`, mirroring the existing `sdp::answer`. The offer template moved here
from `libp2p-webrtc`, where it was private.
See [PR 6572](https://github.com/libp2p/rust-libp2p/pull/6572).

- Revert migration to `quick-protobuf`, migrate back to `prost`.
See [PR 6363](https://github.com/libp2p/rust-libp2p/pull/6363).

Expand Down
107 changes: 107 additions & 0 deletions misc/webrtc-utils/src/sdp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,24 @@ use tinytemplate::TinyTemplate;

use crate::fingerprint::Fingerprint;

/// Renders the SDP offer, describing the client (the dialer).
///
/// The server does not verify the client's certificate -- the client's identity is established by
/// the Noise handshake that runs afterwards -- so a server rendering this offer on the client's
/// behalf passes [`Fingerprint::FF`] here.
pub fn offer(addr: SocketAddr, client_fingerprint: Fingerprint, client_ufrag: &str) -> String {
let offer = render_description(
CLIENT_SESSION_DESCRIPTION,
addr,
client_fingerprint,
client_ufrag,
);

tracing::trace!(%offer, "Created SDP offer");

offer
}

pub fn answer(addr: SocketAddr, server_fingerprint: Fingerprint, client_ufrag: &str) -> String {
let answer = render_description(
SERVER_SESSION_DESCRIPTION,
Expand All @@ -39,6 +57,95 @@ pub fn answer(addr: SocketAddr, server_fingerprint: Fingerprint, client_ufrag: &
answer
}

// An SDP message that constitutes the offer.
//
// Main RFC: <https://datatracker.ietf.org/doc/html/rfc8866>
// `sctp-port` and `max-message-size` attrs RFC: <https://datatracker.ietf.org/doc/html/rfc8841>
// `group` and `mid` attrs RFC: <https://datatracker.ietf.org/doc/html/rfc9143>
// `ice-ufrag`, `ice-pwd` and `ice-options` attrs RFC: <https://datatracker.ietf.org/doc/html/rfc8839>
// `setup` attr RFC: <https://datatracker.ietf.org/doc/html/rfc8122>
//
// Short description:
//
// v=<protocol-version> -> always 0
// o=<username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address>
//
// <username> identifies the creator of the SDP document. We are allowed to use dummy values
// (`-` and `0.0.0.0` as <addrtype>) to remain anonymous, which we do. Note that "IN" means
// "Internet".
//
// s=<session name>
//
// We are allowed to pass a dummy `-`.
//
// c=<nettype> <addrtype> <connection-address>
//
// Indicates the IP address of the remote.
// Note that "IN" means "Internet".
//
// t=<start-time> <stop-time>
//
// Start and end of the validity of the session. `0 0` means that the session never expires.
//
// m=<media> <port> <proto> <fmt> ...
//
// A `m=` line describes a request to establish a certain protocol. The protocol in this line
// (i.e. `TCP/DTLS/SCTP` or `UDP/DTLS/SCTP`) must always be the same as the one in the offer.
// We know that this is true because we tweak the offer to match the protocol. The `<fmt>`
// component must always be `webrtc-datachannel` for WebRTC.
// RFCs: 8839, 8866, 8841
//
// a=mid:<MID>
//
// Media ID - uniquely identifies this media stream (RFC9143).
//
// a=ice-options:ice2
//
// Indicates that we are complying with RFC8839 (as opposed to the legacy RFC5245).
//
// a=ice-ufrag:<ICE user>
// a=ice-pwd:<ICE password>
//
// ICE username and password, which are used for establishing and
// maintaining the ICE connection. (RFC8839)
// MUST match ones used by the answerer (server).
//
// a=fingerprint:sha-256 <fingerprint>
//
// Fingerprint of the certificate that the remote will use during the TLS
// handshake. (RFC8122)
//
// a=setup:actpass
//
// The endpoint that is the offerer MUST use the setup attribute value of setup:actpass and be
// prepared to receive a client_hello before it receives the answer.
//
// a=sctp-port:<value>
//
// The SCTP port (RFC8841)
// Note it's different from the "m=" line port value, which indicates the port of the
// underlying transport-layer protocol (UDP or TCP).
//
// a=max-message-size:<value>
//
// The maximum SCTP user message size (in bytes). (RFC8841)
const CLIENT_SESSION_DESCRIPTION: &str = "v=0
o=- 0 0 IN {ip_version} {target_ip}
s=-
c=IN {ip_version} {target_ip}
t=0 0

m=application {target_port} UDP/DTLS/SCTP webrtc-datachannel
a=mid:0
a=ice-options:ice2
a=ice-ufrag:{ufrag}
a=ice-pwd:{pwd}
a=fingerprint:{fingerprint_algorithm} {fingerprint_value}
a=setup:actpass
a=sctp-port:5000
a=max-message-size:16384
";

// See [`CLIENT_SESSION_DESCRIPTION`].
//
// a=ice-lite
Expand Down
4 changes: 4 additions & 0 deletions transports/webrtc/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## 0.10.0-alpha

- Move the offer SDP template into `libp2p-webrtc-utils` and render it through the new
`sdp::offer` there. No behaviour change.
See [PR 6572](https://github.com/libp2p/rust-libp2p/pull/6572).

- Update webrtc-rs to `v0.17` and fix libp2p noise data channel negotiation.
See [PR 6429](https://github.com/libp2p/rust-libp2p/pull/6429)

Expand Down
101 changes: 4 additions & 97 deletions transports/webrtc/src/tokio/sdp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@

use std::net::SocketAddr;

use libp2p_webrtc_utils::Fingerprint;
pub(crate) use libp2p_webrtc_utils::sdp::random_ufrag;
use libp2p_webrtc_utils::{Fingerprint, sdp::render_description};
use webrtc::peer_connection::sdp::session_description::RTCSessionDescription;

/// Creates the SDP answer used by the client.
Expand All @@ -42,103 +42,10 @@ pub(crate) fn answer(
///
/// Certificate verification is disabled which is why we hardcode a dummy fingerprint here.
pub(crate) fn offer(addr: SocketAddr, client_ufrag: &str) -> RTCSessionDescription {
let offer = render_description(
CLIENT_SESSION_DESCRIPTION,
RTCSessionDescription::offer(libp2p_webrtc_utils::sdp::offer(
addr,
Fingerprint::FF,
client_ufrag,
);

tracing::trace!(offer=%offer, "Created SDP offer");

RTCSessionDescription::offer(offer).unwrap()
))
.unwrap()
}

// An SDP message that constitutes the offer.
//
// Main RFC: <https://datatracker.ietf.org/doc/html/rfc8866>
// `sctp-port` and `max-message-size` attrs RFC: <https://datatracker.ietf.org/doc/html/rfc8841>
// `group` and `mid` attrs RFC: <https://datatracker.ietf.org/doc/html/rfc9143>
// `ice-ufrag`, `ice-pwd` and `ice-options` attrs RFC: <https://datatracker.ietf.org/doc/html/rfc8839>
// `setup` attr RFC: <https://datatracker.ietf.org/doc/html/rfc8122>
//
// Short description:
//
// v=<protocol-version> -> always 0
// o=<username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address>
//
// <username> identifies the creator of the SDP document. We are allowed to use dummy values
// (`-` and `0.0.0.0` as <addrtype>) to remain anonymous, which we do. Note that "IN" means
// "Internet".
//
// s=<session name>
//
// We are allowed to pass a dummy `-`.
//
// c=<nettype> <addrtype> <connection-address>
//
// Indicates the IP address of the remote.
// Note that "IN" means "Internet".
//
// t=<start-time> <stop-time>
//
// Start and end of the validity of the session. `0 0` means that the session never expires.
//
// m=<media> <port> <proto> <fmt> ...
//
// A `m=` line describes a request to establish a certain protocol. The protocol in this line
// (i.e. `TCP/DTLS/SCTP` or `UDP/DTLS/SCTP`) must always be the same as the one in the offer.
// We know that this is true because we tweak the offer to match the protocol. The `<fmt>`
// component must always be `webrtc-datachannel` for WebRTC.
// RFCs: 8839, 8866, 8841
//
// a=mid:<MID>
//
// Media ID - uniquely identifies this media stream (RFC9143).
//
// a=ice-options:ice2
//
// Indicates that we are complying with RFC8839 (as opposed to the legacy RFC5245).
//
// a=ice-ufrag:<ICE user>
// a=ice-pwd:<ICE password>
//
// ICE username and password, which are used for establishing and
// maintaining the ICE connection. (RFC8839)
// MUST match ones used by the answerer (server).
//
// a=fingerprint:sha-256 <fingerprint>
//
// Fingerprint of the certificate that the remote will use during the TLS
// handshake. (RFC8122)
//
// a=setup:actpass
//
// The endpoint that is the offerer MUST use the setup attribute value of setup:actpass and be
// prepared to receive a client_hello before it receives the answer.
//
// a=sctp-port:<value>
//
// The SCTP port (RFC8841)
// Note it's different from the "m=" line port value, which indicates the port of the
// underlying transport-layer protocol (UDP or TCP).
//
// a=max-message-size:<value>
//
// The maximum SCTP user message size (in bytes). (RFC8841)
const CLIENT_SESSION_DESCRIPTION: &str = "v=0
o=- 0 0 IN {ip_version} {target_ip}
s=-
c=IN {ip_version} {target_ip}
t=0 0

m=application {target_port} UDP/DTLS/SCTP webrtc-datachannel
a=mid:0
a=ice-options:ice2
a=ice-ufrag:{ufrag}
a=ice-pwd:{pwd}
a=fingerprint:{fingerprint_algorithm} {fingerprint_value}
a=setup:actpass
a=sctp-port:5000
a=max-message-size:16384
";