added TURN support

This commit is contained in:
Jasper Hugo 2021-08-13 23:09:17 +07:00
parent bed8d156dc
commit 7190086673
8 changed files with 82 additions and 51 deletions

View File

@ -115,4 +115,8 @@ The dependency `gstreamer` is licensed under the GNU Lesser General Public Licen
Any kinds of contributions are welcome as a pull request.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in these crates by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in these crates by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
## Acknowledgements
`gst-meet` development is sponsored by [AVStack](https://www.avstack.io/). We provide globally-distributed, scalable, managed Jitsi Meet backends.

View File

@ -21,12 +21,7 @@ use xmpp_parsers::{
BareJid, Element, FullJid, Jid,
};
use crate::{
jingle::JingleSession,
source::MediaType,
stanza_filter::StanzaFilter,
xmpp,
};
use crate::{jingle::JingleSession, source::MediaType, stanza_filter::StanzaFilter, xmpp};
static DISCO_INFO: Lazy<DiscoInfoResult> = Lazy::new(|| DiscoInfoResult {
node: None,
@ -36,7 +31,6 @@ static DISCO_INFO: Lazy<DiscoInfoResult> = Lazy::new(|| DiscoInfoResult {
Feature::new(ns::JINGLE_RTP_VIDEO),
Feature::new(ns::JINGLE_ICE_UDP),
Feature::new(ns::JINGLE_DTLS),
// not supported yet: rtx
// Feature::new("urn:ietf:rfc:4588"),

View File

@ -280,7 +280,9 @@ impl JitsiConnection {
}
let iq = Iq::from_get(generate_id(), xmpp::extdisco::ServicesQuery {})
.with_from(Jid::Full(locked_inner.jid.as_ref().context("missing jid")?.clone()))
.with_from(Jid::Full(
locked_inner.jid.as_ref().context("missing jid")?.clone(),
))
.with_to(Jid::Bare(locked_inner.xmpp_domain.clone()));
tx.send(iq.into()).await?;
locked_inner.state = DiscoveringExternalServices;

View File

@ -34,6 +34,7 @@ use crate::{
};
const DEFAULT_STUN_PORT: u16 = 3478;
const DEFAULT_TURNS_PORT: u16 = 5349;
pub(crate) struct JingleSession {
pipeline: gstreamer::Pipeline,
@ -198,7 +199,10 @@ impl JingleSession {
}
if let Some(remote_fingerprint) = dtls_fingerprint {
warn!("Remote DTLS fingerprint (verification not implemented yet): {:?}", remote_fingerprint);
warn!(
"Remote DTLS fingerprint (verification not implemented yet): {:?}",
remote_fingerprint
);
}
let mut dtls_cert_params = CertificateParams::new(vec!["gst-meet".to_owned()]);
@ -225,36 +229,63 @@ impl JingleSession {
debug!("audio SSRC: {}", audio_ssrc);
debug!("video SSRC: {}", video_ssrc);
let ice_agent = nice::Agent::new(&conference.glib_main_context, nice::Compatibility::Rfc5245);
ice_agent.set_ice_tcp(false);
ice_agent.set_upnp(false);
let ice_stream_id = ice_agent.add_stream(1);
let ice_component_id = 1;
let maybe_stun = conference
.external_services
.iter()
.find(|svc| svc.r#type == "stun");
let stun_addr = if let Some(stun) = maybe_stun {
lookup_host(format!("{}:{}", stun.host, stun.port.unwrap_or(DEFAULT_STUN_PORT)))
.await?
.next()
lookup_host(format!(
"{}:{}",
stun.host,
stun.port.unwrap_or(DEFAULT_STUN_PORT)
))
.await?
.next()
}
else {
None
};
debug!("STUN address: {:?}", stun_addr);
let ice_agent = nice::Agent::new(&conference.glib_main_context, nice::Compatibility::Rfc5245);
ice_agent.set_ice_tcp(false);
if let Some((stun_addr, stun_port)) = stun_addr.map(|sa| (sa.ip().to_string(), sa.port())) {
ice_agent.set_stun_server(Some(&stun_addr));
ice_agent.set_stun_server_port(stun_port as u32);
}
ice_agent.set_upnp(false);
ice_agent.connect_component_state_changed(|_, a, b, c| {
debug!("ICE component-state-changed {} {} {}", a, b, c);
});
ice_agent.connect_new_selected_pair(|_, a, b, c, d| {
debug!("ICE new-selected-pair {} {} {} {}", a, b, c, d);
});
let ice_stream_id = ice_agent.add_stream(1);
let ice_component_id = 1;
let maybe_turn = conference
.external_services
.iter()
.find(|svc| svc.r#type == "turns");
if let Some(turn_server) = maybe_turn {
let maybe_addr = lookup_host(format!(
"{}:{}",
turn_server.host,
turn_server.port.unwrap_or(DEFAULT_TURNS_PORT)
))
.await?
.next();
if let Some(addr) = maybe_addr {
debug!("TURN address: {:?}", addr);
ice_agent.set_relay_info(
ice_stream_id,
ice_component_id,
&addr.ip().to_string(),
addr.port() as u32,
turn_server.username.as_deref().unwrap_or_default(),
turn_server.password.as_deref().unwrap_or_default(),
nice::RelayType::Tls,
);
}
}
if !ice_agent.attach_recv(
ice_stream_id,
@ -716,13 +747,12 @@ impl JingleSession {
});
description.ssrcs = vec![ssrc];
let mut transport = IceUdpTransport::new()
.with_fingerprint(Fingerprint {
hash: Algo::Sha_256,
setup: Some(Setup::Active),
value: fingerprint.clone(),
required: Some(true.to_string()),
});
let mut transport = IceUdpTransport::new().with_fingerprint(Fingerprint {
hash: Algo::Sha_256,
setup: Some(Setup::Active),
value: fingerprint.clone(),
required: Some(true.to_string()),
});
transport.ufrag = Some(ice_local_ufrag.clone());
transport.pwd = Some(ice_local_pwd.clone());
transport.candidates = vec![];

View File

@ -1,11 +1,11 @@
pub mod conference;
pub mod connection;
mod jingle;
mod xmpp;
mod pinger;
pub mod source;
mod stanza_filter;
mod util;
mod xmpp;
pub use crate::{
conference::{JitsiConference, JitsiConferenceConfig, Participant},

View File

@ -1,10 +1,7 @@
use std::convert::TryFrom;
use anyhow::{bail, Context, Result};
use xmpp_parsers::{
Element,
iq::IqGetPayload,
};
use xmpp_parsers::{iq::IqGetPayload, Element};
use crate::xmpp::ns;
@ -56,18 +53,22 @@ impl TryFrom<Element> for ServicesResult {
Ok(ServicesResult {
services: elem
.children()
.map(|child| Ok(Service {
r#type: child.attr("type").context("missing type attr")?.to_owned(),
name: child.attr("name").map(ToOwned::to_owned),
host: child.attr("host").context("missing host attr")?.to_owned(),
port: child.attr("port").map(|p| p.parse()).transpose()?,
transport: child.attr("transport").map(ToOwned::to_owned),
restricted: child.attr("restricted").map(|b| b.to_lowercase() == "parse" || b == "1"),
username: child.attr("username").map(ToOwned::to_owned),
password: child.attr("password").map(ToOwned::to_owned),
expires: child.attr("expires").map(ToOwned::to_owned),
}))
.map(|child| {
Ok(Service {
r#type: child.attr("type").context("missing type attr")?.to_owned(),
name: child.attr("name").map(ToOwned::to_owned),
host: child.attr("host").context("missing host attr")?.to_owned(),
port: child.attr("port").map(|p| p.parse()).transpose()?,
transport: child.attr("transport").map(ToOwned::to_owned),
restricted: child
.attr("restricted")
.map(|b| b.to_lowercase() == "parse" || b == "1"),
username: child.attr("username").map(ToOwned::to_owned),
password: child.attr("password").map(ToOwned::to_owned),
expires: child.attr("expires").map(ToOwned::to_owned),
})
})
.collect::<Result<_>>()?,
})
}
}
}

View File

@ -1,3 +1,3 @@
pub(crate) mod extdisco;
pub(crate) mod jitsi;
mod ns;
mod ns;

View File

@ -1,4 +1,4 @@
/// XEP-0215: External Service Discovery
pub(crate) const EXTDISCO: &str = "urn:xmpp:extdisco:2";
pub(crate) const JITSI_FOCUS: &str = "http://jitsi.org/protocol/focus";
pub(crate) const JITSI_FOCUS: &str = "http://jitsi.org/protocol/focus";