summaryrefslogtreecommitdiffstats
path: root/dom/media/webrtc/tests/mochitests/test_peerConnection_gatherWithStun300IPv6.html
blob: 16f8f39978b6e91075939d9ec1c030d03da911d7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
<!DOCTYPE HTML>
<html>
<head>
  <script type="application/javascript" src="pc.js"></script>
  <script type="application/javascript" src="iceTestUtils.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
  createHTML({
    bug: "857668",
    title: "RTCPeerConnection check STUN gathering with STUN/300 responses (IPv6)"
  });

  /* This is pretty hairy, so some background:
   * Spec is here: https://datatracker.ietf.org/doc/html/rfc8489#section-10
   * STUN/300 responses allow a server to redirect STUN requests to one or
     more other servers, as ALTERNATE-SERVER attributes.
   * The server specifies the IP address, IP version, and port for each
     ALTERNATE-SERVER.
   * The spec allows multiple rounds of redirects, and requires the client to
     remember the servers it has already tried to avoid redirect loops.
   * For TURNS, the TURN server can also supply an ALTERNATE-DOMAIN attribute,
     which the client MUST use for the TLS handshake on the new target. The
     client does _not_ use this as an FQDN; it always uses the address in the
     ALTERNATE-SERVER. ALTERNATE-DOMAIN is meaningless in the non-TLS case.
   * STUN/300 with ALTERNATE-SERVER is only defined for the TURN Allocate
     message type (at least in the context of ICE). Clients are supposed to
     treat STUN/300 as an unrecoverable error in all other cases. The TURN spec
     does _not_ spell out how a client should handle multiple ALTERNATE-SERVERs.
     We just take the first one that we have not already tried, and that is the
     same IP version that we started with. This is because switching the IP
     version is problematic for ICE.
   * The test TURN server opens extra ports that will respond with redirects to
     the _real_ ports, but the address remains the same. This is because we
     cannot know ahead of time whether the machine we're running on has more
     than one IP address of each version. This means the test TURN server is not
     useful for testing cases where the address changes. Also, the test TURN
     server does not currently know how to respond with multiple
     ALTERNATE-SERVERs.
   * To test cases where the _address_ changes, we instead use a feature in the
     NAT simulator to respond with fake redirects when the destination address
     matches an address that we configure with a pref. This feature can add
     multiple ALTERNATE-SERVERs.
   * The test TURN server's STUN/300 responses have a proper MESSAGE-INTEGRITY,
     but the NAT simulator's do _not_. For now, we want both cases to work,
     because some servers respond with STUN/300 without including
     MESSAGE-INTEGRITY. This is a spec violation, even though the spec
     contradicts itself in non-normative language elsewhere.
   * Right now, neither the NAT simulator nor the test TURN server support
     ALTERNATE-DOMAIN.
  */

  // These are the ports the test TURN server will respond with redirects on.
  // The test TURN server tells us what these are in the JSON it spits out when
  // we start it.
  let turnRedirectPort;
  let turnsRedirectPort;

  // These are the addresses that we will configure the NAT simulator to
  // redirect to. We do DNS lookups of the host in iceServersArray (provided
  // by the test TURN server), and put the results here. On some platforms this
  // will be 127.0.0.1 and ::1, but on others we may use a real address.
  let redirectTargetV6;

  // Test TURN server tells us these in the JSON it spits out when we start it
  let username;
  let credential;

  // This is the address we will configure the NAT simulator to respond with
  // redirects for. We use an address from TEST-NET since it is really unlikely
  // we'll see that on a real machine, and also because we do not have
  // special-case code in nICEr for TEST-NET (like we do for link-local, for
  // example).
  const redirectAddressV6 = '::ffff:198.51.100.1';

  const tests = [
    async function baselineV6Cases() {
      await checkSrflx([{urls:[`stun:[${redirectTargetV6}]`]}]);
      await checkRelayUdp([{urls:[`turn:[${redirectTargetV6}]`], username, credential}]);
      await checkRelayTcp([{urls:[`turn:[${redirectTargetV6}]?transport=tcp`], username, credential}]);
      await checkRelayUdpTcp([{urls:[`turn:[${redirectTargetV6}]`, `turn:[${redirectTargetV6}]?transport=tcp`], username, credential}]);
    },

    async function stunV6Redirect() {
      // This test uses the test TURN server, because nICEr drops responses
      // without MESSAGE-INTEGRITY on the floor _unless_ they are a STUN/300 to
      // an Allocate request. If we tried to use the NAT simulator for this, we
      // would have to wait for nICEr to time out, since the NAT simulator does
      // not know how to do MESSAGE-INTEGRITY.
      await checkNoSrflx(
          [{urls:[`stun:[${redirectTargetV6}]:${turnRedirectPort}`]}]);
    },

    async function turnV6UdpPortRedirect() {
      await checkRelayUdp([{urls:[`turn:[${redirectTargetV6}]:${turnRedirectPort}`], username, credential}]);
    },

    async function turnV6TcpPortRedirect() {
      await checkRelayTcp([{urls:[`turn:[${redirectTargetV6}]:${turnRedirectPort}?transport=tcp`], username, credential}]);
    },

    async function turnV6UdpTcpPortRedirect() {
      await checkRelayUdpTcp([{urls:[`turn:[${redirectTargetV6}]:${turnRedirectPort}`, `turn:[${redirectTargetV6}]:${turnRedirectPort}?transport=tcp`], username, credential}]);
    },

    async function turnV6UdpAddressRedirect() {
      await pushPrefs(
          ['media.peerconnection.nat_simulator.redirect_address', `${redirectAddressV6}`],
          ['media.peerconnection.nat_simulator.redirect_targets', `${redirectTargetV6}`]);
      await checkRelayUdp([{urls:[`turn:[${redirectAddressV6}]`], username, credential}]);
      await SpecialPowers.popPrefEnv();
    },

    async function turnV6TcpAddressRedirect() {
      await pushPrefs(
          ['media.peerconnection.nat_simulator.redirect_address', `${redirectAddressV6}`],
          ['media.peerconnection.nat_simulator.redirect_targets', `${redirectTargetV6}`]);
      await checkRelayTcp([{urls:[`turn:[${redirectAddressV6}]?transport=tcp`], username, credential}]);
      await SpecialPowers.popPrefEnv();
    },

    async function turnV6UdpTcpAddressRedirect() {
      await pushPrefs(
          ['media.peerconnection.nat_simulator.redirect_address', `${redirectAddressV6}`],
          ['media.peerconnection.nat_simulator.redirect_targets', `${redirectTargetV6}`]);
      await checkRelayUdpTcp([{urls:[`turn:[${redirectAddressV6}]`, `turn:[${redirectAddressV6}]?transport=tcp`], username, credential}]);
      await SpecialPowers.popPrefEnv();
    },

    async function turnV6UdpEmptyRedirect() {
      await pushPrefs(
          ['media.peerconnection.nat_simulator.redirect_address', `${redirectAddressV6}`],
          ['media.peerconnection.nat_simulator.redirect_targets', '']);
      await checkNoRelay([{urls:[`turn:[${redirectAddressV6}]`], username, credential}]);
      await SpecialPowers.popPrefEnv();
    },

    async function turnV6TcpEmptyRedirect() {
      await pushPrefs(
          ['media.peerconnection.nat_simulator.redirect_address', `${redirectAddressV6}`],
          ['media.peerconnection.nat_simulator.redirect_targets', '']);
      await checkNoRelay([{urls:[`turn:[${redirectAddressV6}]?transport=tcp`], username, credential}]);
      await SpecialPowers.popPrefEnv();
    },

    async function turnV6UdpTcpEmptyRedirect() {
      await pushPrefs(
          ['media.peerconnection.nat_simulator.redirect_address', `${redirectAddressV6}`],
          ['media.peerconnection.nat_simulator.redirect_targets', '']);
      await checkNoRelay([{urls:[`turn:[${redirectAddressV6}]`, `turn:[${redirectAddressV6}]?transport=tcp`], username, credential}]);
      await SpecialPowers.popPrefEnv();
    },

    async function turnV6UdpAddressAndPortRedirect() {
      // This should result in two rounds of redirection; the first is by
      // address, the second is by port.
      await pushPrefs(
          ['media.peerconnection.nat_simulator.redirect_address', `${redirectAddressV6}`],
          ['media.peerconnection.nat_simulator.redirect_targets', `${redirectTargetV6}`]);
      await checkRelayUdp([{urls:[`turn:[${redirectAddressV6}]:${turnRedirectPort}`], username, credential}]);
      await SpecialPowers.popPrefEnv();
    },

    async function turnV6TcpAddressAndPortRedirect() {
      // This should result in two rounds of redirection; the first is by
      // address, the second is by port.
      await pushPrefs(
          ['media.peerconnection.nat_simulator.redirect_address', `${redirectAddressV6}`],
          ['media.peerconnection.nat_simulator.redirect_targets', `${redirectTargetV6}`]);
      await checkRelayTcp([{urls:[`turn:[${redirectAddressV6}]:${turnRedirectPort}?transport=tcp`], username, credential}]);
      await SpecialPowers.popPrefEnv();
    },

    async function turnV6UdpTcpAddressAndPortRedirect() {
      // This should result in two rounds of redirection; the first is by
      // address, the second is by port.
      await pushPrefs(
          ['media.peerconnection.nat_simulator.redirect_address', `${redirectAddressV6}`],
          ['media.peerconnection.nat_simulator.redirect_targets', `${redirectTargetV6}`]);
      await checkRelayUdpTcp([{urls:[`turn:[${redirectAddressV6}]:${turnRedirectPort}`, `turn:[${redirectAddressV6}]:${turnRedirectPort}?transport=tcp`], username, credential}]);
      await SpecialPowers.popPrefEnv();
    },

    async function turnV6UdpRedirectLoop() {
      await pushPrefs(
          ['media.peerconnection.nat_simulator.redirect_address', `${redirectAddressV6}`],
          ['media.peerconnection.nat_simulator.redirect_targets', `${redirectAddressV6}`]);
      // If we don't detect the loop, gathering will not finish
      await checkNoRelay([{urls:[`turn:[${redirectAddressV6}]`], username, credential}]);
      await SpecialPowers.popPrefEnv();
    },

    async function turnV6TcpRedirectLoop() {
      await pushPrefs(
          ['media.peerconnection.nat_simulator.redirect_address', `${redirectAddressV6}`],
          ['media.peerconnection.nat_simulator.redirect_targets', `${redirectAddressV6}`]);
      // If we don't detect the loop, gathering will not finish
      await checkNoRelay([{urls:[`turn:[${redirectAddressV6}]?transport=tcp`], username, credential}]);
      await SpecialPowers.popPrefEnv();
    },

    async function turnV6UdpTcpRedirectLoop() {
      await pushPrefs(
          ['media.peerconnection.nat_simulator.redirect_address', `${redirectAddressV6}`],
          ['media.peerconnection.nat_simulator.redirect_targets', `${redirectAddressV6}`]);
      // If we don't detect the loop, gathering will not finish
      await checkNoRelay([{urls:[`turn:[${redirectAddressV6}]`, `turn:[${redirectAddressV6}]?transport=tcp`], username, credential}]);
      await SpecialPowers.popPrefEnv();
    },

    async function turnV6UdpMultipleAddressRedirect() {
      await pushPrefs(
          ['media.peerconnection.nat_simulator.redirect_address', `${redirectAddressV6}`],
          ['media.peerconnection.nat_simulator.redirect_targets', `${redirectAddressV6},${redirectTargetV6}`]);
      await checkRelayUdp([{urls:[`turn:[${redirectAddressV6}]`], username, credential}]);
      await SpecialPowers.popPrefEnv();
    },

    async function turnV6TcpMultipleAddressRedirect() {
      await pushPrefs(
          ['media.peerconnection.nat_simulator.redirect_address', `${redirectAddressV6}`],
          ['media.peerconnection.nat_simulator.redirect_targets', `${redirectAddressV6},${redirectTargetV6}`]);
      await checkRelayTcp([{urls:[`turn:[${redirectAddressV6}]?transport=tcp`], username, credential}]);
      await SpecialPowers.popPrefEnv();
    },

    async function turnV6UdpTcpMultipleAddressRedirect() {
      await pushPrefs(
          ['media.peerconnection.nat_simulator.redirect_address', `${redirectAddressV6}`],
          ['media.peerconnection.nat_simulator.redirect_targets', `${redirectAddressV6},${redirectTargetV6}`]);
      await checkRelayUdpTcp([{urls:[`turn:[${redirectAddressV6}]`, `turn:[${redirectAddressV6}]?transport=tcp`], username, credential}]);
      await SpecialPowers.popPrefEnv();
    },
  ];

  runNetworkTest(async () => {
    const turnServer = iceServersArray.find(server => "username" in server);
    username = turnServer.username;
    credential = turnServer.credential;
    // Special props, non-standard
    turnRedirectPort = turnServer.turn_redirect_port;
    turnsRedirectPort = turnServer.turns_redirect_port;
    // Just use the first url. It might make sense to look for TURNS first,
    // since that will always use a hostname, but on CI we don't have TURNS
    // support anyway (see bug 1323439).
    const turnHostname = getTurnHostname(turnServer.urls[0]);

    await pushPrefs(
        ['media.peerconnection.ice.obfuscate_host_addresses', false],
        ['media.peerconnection.nat_simulator.filtering_type', 'ENDPOINT_INDEPENDENT'],
        ['media.peerconnection.nat_simulator.mapping_type', 'ENDPOINT_INDEPENDENT'],
        ['media.peerconnection.ice.loopback', true],
        ['media.getusermedia.insecure.enabled', true]);

    if (await ipv6Supported()) {
      redirectTargetV6 = await dnsLookupV6(turnHostname);
      if (redirectTargetV6 == '' && turnHostname == 'localhost') {
        // Our testers don't seem to have IPv6 DNS resolution for localhost
        // set up...
        redirectTargetV6 = '::1';
      }

      if (redirectTargetV6 != '') {
        for (const test of tests) {
          info(`Running test: ${test.name}`);
          await test();
          info(`Done running test: ${test.name}`);
        }
      } else {
        ok(false, `This machine has an IPv6 address, but ${turnHostname} does not resolve to an IPv6 address`);
      }
    } else {
      ok(false, 'This machine appears to not have an IPv6 address');
    }

    await SpecialPowers.popPrefEnv();
  }, { useIceServer: true });

</script>
</pre>
</body>
</html>