Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
michael@0 | 2 | /* vim: set ts=4 sts=4 et sw=4 tw=80: */ |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | const CC = Components.Constructor; |
michael@0 | 8 | const ServerSocket = CC("@mozilla.org/network/server-socket;1", |
michael@0 | 9 | "nsIServerSocket", |
michael@0 | 10 | "init"); |
michael@0 | 11 | var serv; |
michael@0 | 12 | var ios; |
michael@0 | 13 | |
michael@0 | 14 | /** Example local IP addresses (literal IP address hostname). |
michael@0 | 15 | * |
michael@0 | 16 | * Note: for IPv6 Unique Local and Link Local, a wider range of addresses is |
michael@0 | 17 | * set aside than those most commonly used. Technically, link local addresses |
michael@0 | 18 | * include those beginning with fe80:: through febf::, although in practise |
michael@0 | 19 | * only fe80:: is used. Necko code blocks speculative connections for the wider |
michael@0 | 20 | * range; hence, this test considers that range too. |
michael@0 | 21 | */ |
michael@0 | 22 | var localIPv4Literals = |
michael@0 | 23 | [ // IPv4 RFC1918 \ |
michael@0 | 24 | "10.0.0.1", "10.10.10.10", "10.255.255.255", // 10/8 |
michael@0 | 25 | "172.16.0.1", "172.23.172.12", "172.31.255.255", // 172.16/20 |
michael@0 | 26 | "192.168.0.1", "192.168.192.168", "192.168.255.255", // 192.168/16 |
michael@0 | 27 | // IPv4 Link Local |
michael@0 | 28 | "169.254.0.1", "169.254.192.154", "169.254.255.255" // 169.254/16 |
michael@0 | 29 | ]; |
michael@0 | 30 | var localIPv6Literals = |
michael@0 | 31 | [ // IPv6 Unique Local fc00::/7 |
michael@0 | 32 | "fc00::1", "fdfe:dcba:9876:abcd:ef01:2345:6789:abcd", |
michael@0 | 33 | "fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", |
michael@0 | 34 | // IPv6 Link Local fe80::/10 |
michael@0 | 35 | "fe80::1", "fe80::abcd:ef01:2345:6789", |
michael@0 | 36 | "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff" |
michael@0 | 37 | ]; |
michael@0 | 38 | var localIPLiterals = localIPv4Literals.concat(localIPv6Literals); |
michael@0 | 39 | |
michael@0 | 40 | /** Test function list and descriptions. |
michael@0 | 41 | */ |
michael@0 | 42 | var testList = |
michael@0 | 43 | [ test_speculative_connect, |
michael@0 | 44 | test_hostnames_resolving_to_local_addresses, |
michael@0 | 45 | test_proxies_with_local_addresses |
michael@0 | 46 | ]; |
michael@0 | 47 | |
michael@0 | 48 | var testDescription = |
michael@0 | 49 | [ "Expect pass with localhost", |
michael@0 | 50 | "Expect failure with resolved local IPs", |
michael@0 | 51 | "Expect failure for proxies with local IPs" |
michael@0 | 52 | ]; |
michael@0 | 53 | |
michael@0 | 54 | var testIdx = 0; |
michael@0 | 55 | var hostIdx = 0; |
michael@0 | 56 | |
michael@0 | 57 | |
michael@0 | 58 | /** TestServer |
michael@0 | 59 | * |
michael@0 | 60 | * Implements nsIServerSocket for test_speculative_connect. |
michael@0 | 61 | */ |
michael@0 | 62 | function TestServer() { |
michael@0 | 63 | this.listener = ServerSocket(-1, true, -1); |
michael@0 | 64 | this.listener.asyncListen(this); |
michael@0 | 65 | } |
michael@0 | 66 | |
michael@0 | 67 | TestServer.prototype = { |
michael@0 | 68 | QueryInterface: function(iid) { |
michael@0 | 69 | if (iid.equals(Ci.nsIServerSocket) || |
michael@0 | 70 | iid.equals(Ci.nsISupports)) |
michael@0 | 71 | return this; |
michael@0 | 72 | throw Cr.NS_ERROR_NO_INTERFACE; |
michael@0 | 73 | }, |
michael@0 | 74 | onSocketAccepted: function(socket, trans) { |
michael@0 | 75 | try { this.listener.close(); } catch(e) {} |
michael@0 | 76 | do_check_true(true); |
michael@0 | 77 | next_test(); |
michael@0 | 78 | }, |
michael@0 | 79 | |
michael@0 | 80 | onStopListening: function(socket) {} |
michael@0 | 81 | }; |
michael@0 | 82 | |
michael@0 | 83 | /** TestOutputStreamCallback |
michael@0 | 84 | * |
michael@0 | 85 | * Implements nsIOutputStreamCallback for socket layer tests. |
michael@0 | 86 | */ |
michael@0 | 87 | function TestOutputStreamCallback(transport, hostname, proxied, expectSuccess, next) { |
michael@0 | 88 | this.transport = transport; |
michael@0 | 89 | this.hostname = hostname; |
michael@0 | 90 | this.proxied = proxied; |
michael@0 | 91 | this.expectSuccess = expectSuccess; |
michael@0 | 92 | this.next = next; |
michael@0 | 93 | this.dummyContent = "Dummy content"; |
michael@0 | 94 | } |
michael@0 | 95 | |
michael@0 | 96 | TestOutputStreamCallback.prototype = { |
michael@0 | 97 | QueryInterface: function(iid) { |
michael@0 | 98 | if (iid.equals(Ci.nsIOutputStreamCallback) || |
michael@0 | 99 | iid.equals(Ci.nsISupports)) |
michael@0 | 100 | return this; |
michael@0 | 101 | throw Cr.NS_ERROR_NO_INTERFACE; |
michael@0 | 102 | }, |
michael@0 | 103 | onOutputStreamReady: function(stream) { |
michael@0 | 104 | do_check_neq(typeof(stream), undefined); |
michael@0 | 105 | try { |
michael@0 | 106 | stream.write(this.dummyContent, this.dummyContent.length); |
michael@0 | 107 | } catch (e) { |
michael@0 | 108 | // Spec Connect FAILED. |
michael@0 | 109 | do_check_instanceof(e, Ci.nsIException); |
michael@0 | 110 | if (this.expectSuccess) { |
michael@0 | 111 | // We may expect success, but the address could be unreachable |
michael@0 | 112 | // in the test environment, so expect errors. |
michael@0 | 113 | if (this.proxied) { |
michael@0 | 114 | do_check_true(e.result == Cr.NS_ERROR_NET_TIMEOUT || |
michael@0 | 115 | e.result == Cr.NS_ERROR_PROXY_CONNECTION_REFUSED); |
michael@0 | 116 | } else { |
michael@0 | 117 | do_check_true(e.result == Cr.NS_ERROR_NET_TIMEOUT || |
michael@0 | 118 | e.result == Cr.NS_ERROR_CONNECTION_REFUSED); |
michael@0 | 119 | } |
michael@0 | 120 | } else { |
michael@0 | 121 | // A refusal to connect speculatively should throw an error. |
michael@0 | 122 | do_check_eq(e.result, Cr.NS_ERROR_CONNECTION_REFUSED); |
michael@0 | 123 | } |
michael@0 | 124 | this.transport.close(Cr.NS_BINDING_ABORTED); |
michael@0 | 125 | this.next(); |
michael@0 | 126 | return; |
michael@0 | 127 | } |
michael@0 | 128 | // Spec Connect SUCCEEDED. |
michael@0 | 129 | if (this.expectSuccess) { |
michael@0 | 130 | do_check_true(true, "Success for " + this.hostname); |
michael@0 | 131 | } else { |
michael@0 | 132 | do_throw("Speculative Connect should have failed for " + |
michael@0 | 133 | this.hostname); |
michael@0 | 134 | } |
michael@0 | 135 | this.transport.close(Cr.NS_BINDING_ABORTED); |
michael@0 | 136 | this.next(); |
michael@0 | 137 | } |
michael@0 | 138 | }; |
michael@0 | 139 | |
michael@0 | 140 | /** test_speculative_connect |
michael@0 | 141 | * |
michael@0 | 142 | * Tests a basic positive case using nsIOService.SpeculativeConnect: |
michael@0 | 143 | * connecting to localhost. |
michael@0 | 144 | */ |
michael@0 | 145 | function test_speculative_connect() { |
michael@0 | 146 | serv = new TestServer(); |
michael@0 | 147 | var URI = ios.newURI("http://localhost:" + serv.listener.port + "/just/a/test", null, null); |
michael@0 | 148 | ios.QueryInterface(Ci.nsISpeculativeConnect) |
michael@0 | 149 | .speculativeConnect(URI, null); |
michael@0 | 150 | } |
michael@0 | 151 | |
michael@0 | 152 | /* Speculative connections should not be allowed for hosts with local IP |
michael@0 | 153 | * addresses (Bug 853423). That list includes: |
michael@0 | 154 | * -- IPv4 RFC1918 and Link Local Addresses. |
michael@0 | 155 | * -- IPv6 Unique and Link Local Addresses. |
michael@0 | 156 | * |
michael@0 | 157 | * Two tests are required: |
michael@0 | 158 | * 1. Verify IP Literals passed to the SpeculativeConnect API. |
michael@0 | 159 | * 2. Verify hostnames that need to be resolved at the socket layer. |
michael@0 | 160 | */ |
michael@0 | 161 | |
michael@0 | 162 | /** test_hostnames_resolving_to_addresses |
michael@0 | 163 | * |
michael@0 | 164 | * Common test function for resolved hostnames. Takes a list of hosts, a |
michael@0 | 165 | * boolean to determine if the test is expected to succeed or fail, and a |
michael@0 | 166 | * function to call the next test case. |
michael@0 | 167 | */ |
michael@0 | 168 | function test_hostnames_resolving_to_addresses(host, expectSuccess, next) { |
michael@0 | 169 | do_print(host); |
michael@0 | 170 | var sts = Cc["@mozilla.org/network/socket-transport-service;1"] |
michael@0 | 171 | .getService(Ci.nsISocketTransportService); |
michael@0 | 172 | do_check_neq(typeof(sts), undefined); |
michael@0 | 173 | var transport = sts.createTransport(null, 0, host, 80, null); |
michael@0 | 174 | do_check_neq(typeof(transport), undefined); |
michael@0 | 175 | |
michael@0 | 176 | transport.connectionFlags = Ci.nsISocketTransport.DISABLE_RFC1918; |
michael@0 | 177 | transport.setTimeout(Ci.nsISocketTransport.TIMEOUT_CONNECT, 1); |
michael@0 | 178 | transport.setTimeout(Ci.nsISocketTransport.TIMEOUT_READ_WRITE, 1); |
michael@0 | 179 | do_check_eq(1, transport.getTimeout(Ci.nsISocketTransport.TIMEOUT_CONNECT)); |
michael@0 | 180 | |
michael@0 | 181 | var outStream = transport.openOutputStream(Ci.nsITransport.OPEN_UNBUFFERED,0,0); |
michael@0 | 182 | do_check_neq(typeof(outStream), undefined); |
michael@0 | 183 | |
michael@0 | 184 | var callback = new TestOutputStreamCallback(transport, host, false, |
michael@0 | 185 | expectSuccess, |
michael@0 | 186 | next); |
michael@0 | 187 | do_check_neq(typeof(callback), undefined); |
michael@0 | 188 | |
michael@0 | 189 | // Need to get main thread pointer to ensure nsSocketTransport::AsyncWait |
michael@0 | 190 | // adds callback to nsOutputStreamReadyEvent on main thread, and doesn't |
michael@0 | 191 | // addref off the main thread. |
michael@0 | 192 | var gThreadManager = Cc["@mozilla.org/thread-manager;1"] |
michael@0 | 193 | .getService(Ci.nsIThreadManager); |
michael@0 | 194 | var mainThread = gThreadManager.currentThread; |
michael@0 | 195 | |
michael@0 | 196 | try { |
michael@0 | 197 | outStream.QueryInterface(Ci.nsIAsyncOutputStream) |
michael@0 | 198 | .asyncWait(callback, 0, 0, mainThread); |
michael@0 | 199 | } catch (e) { |
michael@0 | 200 | do_throw("asyncWait should not fail!"); |
michael@0 | 201 | } |
michael@0 | 202 | } |
michael@0 | 203 | |
michael@0 | 204 | /** |
michael@0 | 205 | * test_hostnames_resolving_to_local_addresses |
michael@0 | 206 | * |
michael@0 | 207 | * Creates an nsISocketTransport and simulates a speculative connect request |
michael@0 | 208 | * for a hostname that resolves to a local IP address. |
michael@0 | 209 | * Runs asynchronously; on test success (i.e. failure to connect), the callback |
michael@0 | 210 | * will call this function again until all hostnames in the test list are done. |
michael@0 | 211 | * |
michael@0 | 212 | * Note: This test also uses an IP literal for the hostname. This should be ok, |
michael@0 | 213 | * as the socket layer will ask for the hostname to be resolved anyway, and DNS |
michael@0 | 214 | * code should return a numerical version of the address internally. |
michael@0 | 215 | */ |
michael@0 | 216 | function test_hostnames_resolving_to_local_addresses() { |
michael@0 | 217 | if (hostIdx >= localIPLiterals.length) { |
michael@0 | 218 | // No more local IP addresses; move on. |
michael@0 | 219 | next_test(); |
michael@0 | 220 | return; |
michael@0 | 221 | } |
michael@0 | 222 | var host = localIPLiterals[hostIdx++]; |
michael@0 | 223 | // Test another local IP address when the current one is done. |
michael@0 | 224 | var next = test_hostnames_resolving_to_local_addresses; |
michael@0 | 225 | test_hostnames_resolving_to_addresses(host, false, next); |
michael@0 | 226 | } |
michael@0 | 227 | |
michael@0 | 228 | /** test_speculative_connect_with_host_list |
michael@0 | 229 | * |
michael@0 | 230 | * Common test function for resolved proxy hosts. Takes a list of hosts, a |
michael@0 | 231 | * boolean to determine if the test is expected to succeed or fail, and a |
michael@0 | 232 | * function to call the next test case. |
michael@0 | 233 | */ |
michael@0 | 234 | function test_proxies(proxyHost, expectSuccess, next) { |
michael@0 | 235 | do_print("Proxy: " + proxyHost); |
michael@0 | 236 | var sts = Cc["@mozilla.org/network/socket-transport-service;1"] |
michael@0 | 237 | .getService(Ci.nsISocketTransportService); |
michael@0 | 238 | do_check_neq(typeof(sts), undefined); |
michael@0 | 239 | var pps = Cc["@mozilla.org/network/protocol-proxy-service;1"] |
michael@0 | 240 | .getService(); |
michael@0 | 241 | do_check_neq(typeof(pps), undefined); |
michael@0 | 242 | |
michael@0 | 243 | var proxyInfo = pps.newProxyInfo("http", proxyHost, 8080, 0, 1, null); |
michael@0 | 244 | do_check_neq(typeof(proxyInfo), undefined); |
michael@0 | 245 | |
michael@0 | 246 | var transport = sts.createTransport(null, 0, "dummyHost", 80, proxyInfo); |
michael@0 | 247 | do_check_neq(typeof(transport), undefined); |
michael@0 | 248 | |
michael@0 | 249 | transport.connectionFlags = Ci.nsISocketTransport.DISABLE_RFC1918; |
michael@0 | 250 | |
michael@0 | 251 | transport.setTimeout(Ci.nsISocketTransport.TIMEOUT_CONNECT, 1); |
michael@0 | 252 | do_check_eq(1, transport.getTimeout(Ci.nsISocketTransport.TIMEOUT_CONNECT)); |
michael@0 | 253 | transport.setTimeout(Ci.nsISocketTransport.TIMEOUT_READ_WRITE, 1); |
michael@0 | 254 | |
michael@0 | 255 | var outStream = transport.openOutputStream(Ci.nsITransport.OPEN_UNBUFFERED,0,0); |
michael@0 | 256 | do_check_neq(typeof(outStream), undefined); |
michael@0 | 257 | |
michael@0 | 258 | var callback = new TestOutputStreamCallback(transport, proxyHost, true, |
michael@0 | 259 | expectSuccess, |
michael@0 | 260 | next); |
michael@0 | 261 | do_check_neq(typeof(callback), undefined); |
michael@0 | 262 | |
michael@0 | 263 | // Need to get main thread pointer to ensure nsSocketTransport::AsyncWait |
michael@0 | 264 | // adds callback to nsOutputStreamReadyEvent on main thread, and doesn't |
michael@0 | 265 | // addref off the main thread. |
michael@0 | 266 | var gThreadManager = Cc["@mozilla.org/thread-manager;1"] |
michael@0 | 267 | .getService(Ci.nsIThreadManager); |
michael@0 | 268 | var mainThread = gThreadManager.currentThread; |
michael@0 | 269 | |
michael@0 | 270 | try { |
michael@0 | 271 | outStream.QueryInterface(Ci.nsIAsyncOutputStream) |
michael@0 | 272 | .asyncWait(callback, 0, 0, mainThread); |
michael@0 | 273 | } catch (e) { |
michael@0 | 274 | do_throw("asyncWait should not fail!"); |
michael@0 | 275 | } |
michael@0 | 276 | } |
michael@0 | 277 | |
michael@0 | 278 | /** |
michael@0 | 279 | * test_proxies_with_local_addresses |
michael@0 | 280 | * |
michael@0 | 281 | * Creates an nsISocketTransport and simulates a speculative connect request |
michael@0 | 282 | * for a proxy that resolves to a local IP address. |
michael@0 | 283 | * Runs asynchronously; on test success (i.e. failure to connect), the callback |
michael@0 | 284 | * will call this function again until all proxies in the test list are done. |
michael@0 | 285 | * |
michael@0 | 286 | * Note: This test also uses an IP literal for the proxy. This should be ok, |
michael@0 | 287 | * as the socket layer will ask for the proxy to be resolved anyway, and DNS |
michael@0 | 288 | * code should return a numerical version of the address internally. |
michael@0 | 289 | */ |
michael@0 | 290 | function test_proxies_with_local_addresses() { |
michael@0 | 291 | if (hostIdx >= localIPLiterals.length) { |
michael@0 | 292 | // No more local IP addresses; move on. |
michael@0 | 293 | next_test(); |
michael@0 | 294 | return; |
michael@0 | 295 | } |
michael@0 | 296 | var host = localIPLiterals[hostIdx++]; |
michael@0 | 297 | // Test another local IP address when the current one is done. |
michael@0 | 298 | var next = test_proxies_with_local_addresses; |
michael@0 | 299 | test_proxies(host, false, next); |
michael@0 | 300 | } |
michael@0 | 301 | |
michael@0 | 302 | /** next_test |
michael@0 | 303 | * |
michael@0 | 304 | * Calls the next test in testList. Each test is responsible for calling this |
michael@0 | 305 | * function when its test cases are complete. |
michael@0 | 306 | */ |
michael@0 | 307 | function next_test() { |
michael@0 | 308 | if (testIdx >= testList.length) { |
michael@0 | 309 | // No more tests; we're done. |
michael@0 | 310 | do_test_finished(); |
michael@0 | 311 | return; |
michael@0 | 312 | } |
michael@0 | 313 | do_print("SpeculativeConnect: " + testDescription[testIdx]); |
michael@0 | 314 | hostIdx = 0; |
michael@0 | 315 | // Start next test in list. |
michael@0 | 316 | testList[testIdx++](); |
michael@0 | 317 | } |
michael@0 | 318 | |
michael@0 | 319 | /** run_test |
michael@0 | 320 | * |
michael@0 | 321 | * Main entry function for test execution. |
michael@0 | 322 | */ |
michael@0 | 323 | function run_test() { |
michael@0 | 324 | ios = Cc["@mozilla.org/network/io-service;1"] |
michael@0 | 325 | .getService(Ci.nsIIOService); |
michael@0 | 326 | |
michael@0 | 327 | do_test_pending(); |
michael@0 | 328 | next_test(); |
michael@0 | 329 | } |
michael@0 | 330 |