From 363211c086eb555e4ca6899118eb7953204b34b8 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 7 Mar 2025 12:07:10 +0100 Subject: [PATCH] use modern api to retrieve ice servers --- .../conversations/xmpp/jingle/IceServers.java | 83 ++----------------- .../xmpp/jingle/JingleRtpConnection.java | 3 +- .../WebRTCDataChannelTransport.java | 4 +- .../xmpp/model/disco/external/Services.java | 80 ++++++++++++++++++ 4 files changed, 89 insertions(+), 81 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/IceServers.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/IceServers.java index 82026096d0581694e6272ed4e04d71160305c987..d852bd5f96017f6e3c65e14a1fa309a8ff4341b8 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/IceServers.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/IceServers.java @@ -1,17 +1,8 @@ package eu.siacs.conversations.xmpp.jingle; -import android.util.Log; -import com.google.common.base.Strings; -import com.google.common.collect.ImmutableSet; -import com.google.common.primitives.Ints; -import eu.siacs.conversations.Config; -import eu.siacs.conversations.utils.IP; -import eu.siacs.conversations.xml.Element; -import eu.siacs.conversations.xml.Namespace; +import im.conversations.android.xmpp.model.disco.external.Services; import im.conversations.android.xmpp.model.stanza.Iq; -import java.util.Arrays; import java.util.Collections; -import java.util.List; import java.util.Set; import org.webrtc.PeerConnection; @@ -21,74 +12,10 @@ public final class IceServers { if (response.getType() != Iq.Type.RESULT) { return Collections.emptySet(); } - final var builder = new ImmutableSet.Builder(); - final Element services = - response.findChild("services", Namespace.EXTERNAL_SERVICE_DISCOVERY); - final List children = - services == null ? Collections.emptyList() : services.getChildren(); - for (final Element child : children) { - if ("service".equals(child.getName())) { - final String type = child.getAttribute("type"); - final String host = child.getAttribute("host"); - final String sport = child.getAttribute("port"); - final Integer port = sport == null ? null : Ints.tryParse(sport); - final String transport = child.getAttribute("transport"); - final String username = child.getAttribute("username"); - final String password = child.getAttribute("password"); - if (Strings.isNullOrEmpty(host) || port == null) { - continue; - } - if (port < 0 || port > 65535) { - continue; - } - - if (Arrays.asList("stun", "stuns", "turn", "turns").contains(type) - && Arrays.asList("udp", "tcp").contains(transport)) { - if (Arrays.asList("stuns", "turns").contains(type) && "udp".equals(transport)) { - Log.w( - Config.LOGTAG, - "skipping invalid combination of udp/tls in external services"); - continue; - } - - // STUN URLs do not support a query section since M110 - final String uri; - if (Arrays.asList("stun", "stuns").contains(type)) { - uri = String.format("%s:%s:%s", type, IP.wrapIPv6(host), port); - } else { - uri = - String.format( - "%s:%s:%s?transport=%s", - type, IP.wrapIPv6(host), port, transport); - } - - final PeerConnection.IceServer.Builder iceServerBuilder = - PeerConnection.IceServer.builder(uri); - iceServerBuilder.setTlsCertPolicy( - PeerConnection.TlsCertPolicy.TLS_CERT_POLICY_INSECURE_NO_CHECK); - if (username != null && password != null) { - iceServerBuilder.setUsername(username); - iceServerBuilder.setPassword(password); - } else if (Arrays.asList("turn", "turns").contains(type)) { - // The WebRTC spec requires throwing an - // InvalidAccessError when username (from libwebrtc - // source coder) - // https://chromium.googlesource.com/external/webrtc/+/master/pc/ice_server_parsing.cc - Log.w( - Config.LOGTAG, - "skipping " - + type - + "/" - + transport - + " without username and password"); - continue; - } - final PeerConnection.IceServer iceServer = iceServerBuilder.createIceServer(); - Log.w(Config.LOGTAG, "discovered ICE Server: " + iceServer); - builder.add(iceServer); - } - } + final var services = response.getExtension(Services.class); + if (services == null) { + return Collections.emptySet(); } - return builder.build(); + return services.getIceServers(); } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java index cd35d163b8bafcaf1a8644209c5a0a04bc05d0e9..895cadcf8087d3b08a209a698720efcf49009314 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -44,6 +44,7 @@ import eu.siacs.conversations.xmpp.jingle.stanzas.Proceed; import eu.siacs.conversations.xmpp.jingle.stanzas.Propose; import eu.siacs.conversations.xmpp.jingle.stanzas.Reason; import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription; +import im.conversations.android.xmpp.model.disco.external.Services; import im.conversations.android.xmpp.model.jingle.Jingle; import im.conversations.android.xmpp.model.stanza.Iq; import java.util.Arrays; @@ -2824,7 +2825,7 @@ public class JingleRtpConnection extends AbstractJingleConnection if (id.account.getXmppConnection().getFeatures().externalServiceDiscovery()) { final Iq request = new Iq(Iq.Type.GET); request.setTo(id.account.getDomain()); - request.addChild("services", Namespace.EXTERNAL_SERVICE_DISCOVERY); + request.addExtension(new Services()); xmppConnectionService.sendIqPacket( id.account, request, diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/transports/WebRTCDataChannelTransport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/transports/WebRTCDataChannelTransport.java index 134c0be69bf6cf7b8a22f9ede6dafe2060c06eee..62d06f796510d53fb5c691d7af012a1f1d944105 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/transports/WebRTCDataChannelTransport.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/transports/WebRTCDataChannelTransport.java @@ -14,12 +14,12 @@ import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.SettableFuture; import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.Account; -import eu.siacs.conversations.xml.Namespace; import eu.siacs.conversations.xmpp.XmppConnection; import eu.siacs.conversations.xmpp.jingle.IceServers; import eu.siacs.conversations.xmpp.jingle.WebRTCWrapper; import eu.siacs.conversations.xmpp.jingle.stanzas.IceUdpTransportInfo; import eu.siacs.conversations.xmpp.jingle.stanzas.WebRTCDataChannelTransportInfo; +import im.conversations.android.xmpp.model.disco.external.Services; import im.conversations.android.xmpp.model.stanza.Iq; import java.io.IOException; import java.io.InputStream; @@ -233,7 +233,7 @@ public class WebRTCDataChannelTransport implements Transport { SettableFuture.create(); final Iq request = new Iq(Iq.Type.GET); request.setTo(this.account.getDomain()); - request.addChild("services", Namespace.EXTERNAL_SERVICE_DISCOVERY); + request.addExtension(new Services()); xmppConnection.sendIqPacket( request, (response) -> { diff --git a/src/main/java/im/conversations/android/xmpp/model/disco/external/Services.java b/src/main/java/im/conversations/android/xmpp/model/disco/external/Services.java index 36338083da5d03530ee42027f618225e8c248632..61045a2b8c8dd939d288499c053f3cd5e5f45a37 100644 --- a/src/main/java/im/conversations/android/xmpp/model/disco/external/Services.java +++ b/src/main/java/im/conversations/android/xmpp/model/disco/external/Services.java @@ -1,7 +1,17 @@ package im.conversations.android.xmpp.model.disco.external; +import android.util.Log; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableSet; +import com.google.common.primitives.Ints; +import eu.siacs.conversations.Config; +import eu.siacs.conversations.utils.IP; import im.conversations.android.annotation.XmlElement; import im.conversations.android.xmpp.model.Extension; +import java.util.Arrays; +import java.util.Collection; +import java.util.Set; +import org.webrtc.PeerConnection; @XmlElement public class Services extends Extension { @@ -9,4 +19,74 @@ public class Services extends Extension { public Services() { super(Services.class); } + + public Collection getServices() { + return this.getExtensions(Service.class); + } + + public Set getIceServers() { + final var builder = new ImmutableSet.Builder(); + for (final var service : this.getServices()) { + final String type = service.getAttribute("type"); + final String host = service.getAttribute("host"); + final String sport = service.getAttribute("port"); + final Integer port = sport == null ? null : Ints.tryParse(sport); + final String transport = service.getAttribute("transport"); + final String username = service.getAttribute("username"); + final String password = service.getAttribute("password"); + if (Strings.isNullOrEmpty(host) || port == null) { + continue; + } + if (port < 0 || port > 65535) { + continue; + } + + if (Arrays.asList("stun", "stuns", "turn", "turns").contains(type) + && Arrays.asList("udp", "tcp").contains(transport)) { + if (Arrays.asList("stuns", "turns").contains(type) && "udp".equals(transport)) { + Log.w( + Config.LOGTAG, + "skipping invalid combination of udp/tls in external services"); + continue; + } + + // STUN URLs do not support a query section since M110 + final String uri; + if (Arrays.asList("stun", "stuns").contains(type)) { + uri = String.format("%s:%s:%s", type, IP.wrapIPv6(host), port); + } else { + uri = + String.format( + "%s:%s:%s?transport=%s", + type, IP.wrapIPv6(host), port, transport); + } + + final PeerConnection.IceServer.Builder iceServerBuilder = + PeerConnection.IceServer.builder(uri); + iceServerBuilder.setTlsCertPolicy( + PeerConnection.TlsCertPolicy.TLS_CERT_POLICY_INSECURE_NO_CHECK); + if (username != null && password != null) { + iceServerBuilder.setUsername(username); + iceServerBuilder.setPassword(password); + } else if (Arrays.asList("turn", "turns").contains(type)) { + // The WebRTC spec requires throwing an + // InvalidAccessError when username (from libwebrtc + // source coder) + // https://chromium.googlesource.com/external/webrtc/+/master/pc/ice_server_parsing.cc + Log.w( + Config.LOGTAG, + "skipping " + + type + + "/" + + transport + + " without username and password"); + continue; + } + final PeerConnection.IceServer iceServer = iceServerBuilder.createIceServer(); + Log.w(Config.LOGTAG, "discovered ICE Server: " + iceServer); + builder.add(iceServer); + } + } + return builder.build(); + } }