Services.java

 1package im.conversations.android.xmpp.model.disco.external;
 2
 3import android.util.Log;
 4import com.google.common.base.Strings;
 5import com.google.common.collect.ImmutableSet;
 6import com.google.common.primitives.Ints;
 7import eu.siacs.conversations.Config;
 8import eu.siacs.conversations.utils.IP;
 9import im.conversations.android.annotation.XmlElement;
10import im.conversations.android.xmpp.model.Extension;
11import java.util.Arrays;
12import java.util.Collection;
13import java.util.Set;
14import org.webrtc.PeerConnection;
15
16@XmlElement
17public class Services extends Extension {
18
19    public Services() {
20        super(Services.class);
21    }
22
23    public Collection<Service> getServices() {
24        return this.getExtensions(Service.class);
25    }
26
27    public Set<PeerConnection.IceServer> getIceServers() {
28        final var builder = new ImmutableSet.Builder<PeerConnection.IceServer>();
29        for (final var service : this.getServices()) {
30            final String type = service.getAttribute("type");
31            final String host = service.getAttribute("host");
32            final String sport = service.getAttribute("port");
33            final Integer port = sport == null ? null : Ints.tryParse(sport);
34            final String transport = service.getAttribute("transport");
35            final String username = service.getAttribute("username");
36            final String password = service.getAttribute("password");
37            if (Strings.isNullOrEmpty(host) || port == null) {
38                continue;
39            }
40            if (port < 0 || port > 65535) {
41                continue;
42            }
43
44            if (Arrays.asList("stun", "stuns", "turn", "turns").contains(type)
45                    && Arrays.asList("udp", "tcp").contains(transport)) {
46                if (Arrays.asList("stuns", "turns").contains(type) && "udp".equals(transport)) {
47                    Log.w(
48                            Config.LOGTAG,
49                            "skipping invalid combination of udp/tls in external services");
50                    continue;
51                }
52
53                // STUN URLs do not support a query section since M110
54                final String uri;
55                if (Arrays.asList("stun", "stuns").contains(type)) {
56                    uri = String.format("%s:%s:%s", type, IP.wrapIPv6(host), port);
57                } else {
58                    uri =
59                            String.format(
60                                    "%s:%s:%s?transport=%s",
61                                    type, IP.wrapIPv6(host), port, transport);
62                }
63
64                final PeerConnection.IceServer.Builder iceServerBuilder =
65                        PeerConnection.IceServer.builder(uri);
66                iceServerBuilder.setTlsCertPolicy(
67                        PeerConnection.TlsCertPolicy.TLS_CERT_POLICY_INSECURE_NO_CHECK);
68                if (username != null && password != null) {
69                    iceServerBuilder.setUsername(username);
70                    iceServerBuilder.setPassword(password);
71                } else if (Arrays.asList("turn", "turns").contains(type)) {
72                    // The WebRTC spec requires throwing an
73                    // InvalidAccessError when username (from libwebrtc
74                    // source coder)
75                    // https://chromium.googlesource.com/external/webrtc/+/master/pc/ice_server_parsing.cc
76                    Log.w(
77                            Config.LOGTAG,
78                            "skipping "
79                                    + type
80                                    + "/"
81                                    + transport
82                                    + " without username and password");
83                    continue;
84                }
85                final PeerConnection.IceServer iceServer = iceServerBuilder.createIceServer();
86                Log.w(Config.LOGTAG, "discovered ICE Server: " + iceServer);
87                builder.add(iceServer);
88            }
89        }
90        return builder.build();
91    }
92}