1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
9 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
10 Cu.import("resource://gre/modules/Services.jsm");
11 Cu.import("resource://gre/modules/NetUtil.jsm");
12 Cu.import("resource://gre/modules/FileUtils.jsm");
13 Cu.import("resource://gre/modules/Promise.jsm");
15 const NETWORKSERVICE_CONTRACTID = "@mozilla.org/network/service;1";
16 const NETWORKSERVICE_CID = Components.ID("{baec696c-c78d-42db-8b44-603f8fbfafb4}");
18 XPCOMUtils.defineLazyServiceGetter(this, "gNetworkWorker",
19 "@mozilla.org/network/worker;1",
22 // 1xx - Requested action is proceeding
23 const NETD_COMMAND_PROCEEDING = 100;
24 // 2xx - Requested action has been successfully completed
25 const NETD_COMMAND_OKAY = 200;
26 // 4xx - The command is accepted but the requested action didn't
28 const NETD_COMMAND_FAIL = 400;
29 // 5xx - The command syntax or parameters error
30 const NETD_COMMAND_ERROR = 500;
31 // 6xx - Unsolicited broadcasts
32 const NETD_COMMAND_UNSOLICITED = 600;
34 const WIFI_CTRL_INTERFACE = "wl0.1";
36 const MANUAL_PROXY_CONFIGURATION = 1;
40 // Read debug setting from pref.
42 let debugPref = Services.prefs.getBoolPref("network.debugging.enabled");
43 DEBUG = DEBUG || debugPref;
46 function netdResponseType(code) {
47 return Math.floor(code / 100) * 100;
50 function isError(code) {
51 let type = netdResponseType(code);
52 return (type !== NETD_COMMAND_PROCEEDING && type !== NETD_COMMAND_OKAY);
56 dump("-*- NetworkService: " + msg + "\n");
60 * This component watches for network interfaces changing state and then
61 * adjusts routes etc. accordingly.
63 function NetworkService() {
64 if(DEBUG) debug("Starting net_worker.");
69 let networkListener = {
70 onEvent: function(event) {
71 self.handleWorkerMessage(event);
74 gNetworkWorker.start(networkListener);
76 // Callbacks to invoke when a reply arrives from the net_worker.
77 this.controlCallbacks = Object.create(null);
79 this.shutdown = false;
80 Services.obs.addObserver(this, "xpcom-shutdown", false);
83 NetworkService.prototype = {
84 classID: NETWORKSERVICE_CID,
85 classInfo: XPCOMUtils.generateCI({classID: NETWORKSERVICE_CID,
86 contractID: NETWORKSERVICE_CONTRACTID,
87 classDescription: "Network Service",
88 interfaces: [Ci.nsINetworkService]}),
89 QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkService]),
94 controlMessage: function(params, callback) {
100 let id = this.idgen++;
102 this.controlCallbacks[id] = callback;
104 if (gNetworkWorker) {
105 gNetworkWorker.postMessage(params);
109 handleWorkerMessage: function(response) {
110 if(DEBUG) debug("NetworkManager received message from worker: " + JSON.stringify(response));
111 let id = response.id;
112 if (response.broadcast === true) {
113 Services.obs.notifyObservers(null, response.topic, response.reason);
116 let callback = this.controlCallbacks[id];
118 callback.call(this, response);
119 delete this.controlCallbacks[id];
125 getNetworkInterfaceStats: function(networkName, callback) {
126 if(DEBUG) debug("getNetworkInterfaceStats for " + networkName);
128 let file = new FileUtils.File("/proc/net/dev");
130 callback.networkStatsAvailable(false, 0, 0, Date.now());
134 NetUtil.asyncFetch(file, function(inputStream, status) {
139 if (Components.isSuccessCode(status)) {
140 // Find record for corresponding interface.
141 let statExpr = /(\S+): +(\d+) +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +(\d+) +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+/;
143 NetUtil.readInputStreamToString(inputStream, inputStream.available())
145 for (let i = 2; i < data.length; i++) {
146 let parseResult = statExpr.exec(data[i]);
147 if (parseResult && parseResult[1] === networkName) {
148 rxBytes = parseInt(parseResult[2], 10);
149 txBytes = parseInt(parseResult[3], 10);
155 // netd always return success even interface doesn't exist.
156 callback.networkStatsAvailable(true, rxBytes, txBytes, now);
160 setNetworkInterfaceAlarm: function(networkName, threshold, callback) {
162 callback.networkUsageAlarmResult(-1);
167 this._disableNetworkInterfaceAlarm(networkName, function(result) {
169 if (!isError(result.resultCode)) {
170 callback.networkUsageAlarmResult(null);
173 callback.networkUsageAlarmResult(result.reason);
177 self._setNetworkInterfaceAlarm(networkName, threshold, callback);
181 _setNetworkInterfaceAlarm: function(networkName, threshold, callback) {
182 if(DEBUG) debug("setNetworkInterfaceAlarm for " + networkName + " at " + threshold + "bytes");
185 cmd: "setNetworkInterfaceAlarm",
190 params.report = true;
191 params.isAsync = true;
193 this.controlMessage(params, function(result) {
194 if (!isError(result.resultCode)) {
195 callback.networkUsageAlarmResult(null);
199 this._enableNetworkInterfaceAlarm(networkName, threshold, callback);
203 _enableNetworkInterfaceAlarm: function(networkName, threshold, callback) {
204 if(DEBUG) debug("enableNetworkInterfaceAlarm for " + networkName + " at " + threshold + "bytes");
207 cmd: "enableNetworkInterfaceAlarm",
212 params.report = true;
213 params.isAsync = true;
215 this.controlMessage(params, function(result) {
216 if (!isError(result.resultCode)) {
217 callback.networkUsageAlarmResult(null);
220 callback.networkUsageAlarmResult(result.reason);
224 _disableNetworkInterfaceAlarm: function(networkName, callback) {
225 if(DEBUG) debug("disableNetworkInterfaceAlarm for " + networkName);
228 cmd: "disableNetworkInterfaceAlarm",
232 params.report = true;
233 params.isAsync = true;
235 this.controlMessage(params, function(result) {
240 setWifiOperationMode: function(interfaceName, mode, callback) {
241 if(DEBUG) debug("setWifiOperationMode on " + interfaceName + " to " + mode);
244 cmd: "setWifiOperationMode",
245 ifname: interfaceName,
249 params.report = true;
250 params.isAsync = true;
252 this.controlMessage(params, function(result) {
253 if (isError(result.resultCode)) {
254 callback.wifiOperationModeResult("netd command error");
256 callback.wifiOperationModeResult(null);
261 resetRoutingTable: function(network) {
263 let prefixLengths = {};
264 let length = network.getAddresses(ips, prefixLengths);
266 for (let i = 0; i < length; i++) {
267 let ip = ips.value[i];
268 let prefixLength = prefixLengths.value[i];
271 cmd: "removeNetworkRoute",
272 ifname: network.name,
274 prefixLength: prefixLength
276 this.controlMessage(options);
280 setDNS: function(networkInterface) {
281 if(DEBUG) debug("Going DNS to " + networkInterface.name);
282 let dnses = networkInterface.getDnses();
285 ifname: networkInterface.name,
286 domain: "mozilla." + networkInterface.name + ".doman",
289 this.controlMessage(options);
292 setDefaultRouteAndDNS: function(network, oldInterface) {
293 if(DEBUG) debug("Going to change route and DNS to " + network.name);
294 let gateways = network.getGateways();
295 let dnses = network.getDnses();
297 cmd: "setDefaultRouteAndDNS",
298 ifname: network.name,
299 oldIfname: (oldInterface && oldInterface !== network) ? oldInterface.name : null,
301 domain: "mozilla." + network.name + ".doman",
304 this.controlMessage(options);
305 this.setNetworkProxy(network);
308 removeDefaultRoute: function(network) {
309 if(DEBUG) debug("Remove default route for " + network.name);
310 let gateways = network.getGateways();
312 cmd: "removeDefaultRoute",
313 ifname: network.name,
316 this.controlMessage(options);
319 _setHostRoute: function(doAdd, interfaceName, gateway, host) {
320 let command = doAdd ? "addHostRoute" : "removeHostRoute";
322 if (DEBUG) debug(command + " " + host + " on " + interfaceName);
323 let deferred = Promise.defer();
326 ifname: interfaceName,
330 this.controlMessage(options, function(data) {
332 deferred.reject(data.reason);
337 return deferred.promise;
340 addHostRoute: function(interfaceName, gateway, host) {
341 return this._setHostRoute(true, interfaceName, gateway, host);
344 removeHostRoute: function(interfaceName, gateway, host) {
345 return this._setHostRoute(false, interfaceName, gateway, host);
348 removeHostRoutes: function(ifname) {
349 if(DEBUG) debug("Going to remove all host routes on " + ifname);
351 cmd: "removeHostRoutes",
354 this.controlMessage(options);
357 addSecondaryRoute: function(ifname, route) {
358 if(DEBUG) debug("Going to add route to secondary table on " + ifname);
360 cmd: "addSecondaryRoute",
363 prefix: route.prefix,
364 gateway: route.gateway
366 this.controlMessage(options);
369 removeSecondaryRoute: function(ifname, route) {
370 if(DEBUG) debug("Going to remove route from secondary table on " + ifname);
372 cmd: "removeSecondaryRoute",
375 prefix: route.prefix,
376 gateway: route.gateway
378 this.controlMessage(options);
381 setNetworkProxy: function(network) {
383 if (!network.httpProxyHost || network.httpProxyHost === "") {
384 // Sets direct connection to internet.
385 Services.prefs.clearUserPref("network.proxy.type");
386 Services.prefs.clearUserPref("network.proxy.share_proxy_settings");
387 Services.prefs.clearUserPref("network.proxy.http");
388 Services.prefs.clearUserPref("network.proxy.http_port");
389 Services.prefs.clearUserPref("network.proxy.ssl");
390 Services.prefs.clearUserPref("network.proxy.ssl_port");
391 if(DEBUG) debug("No proxy support for " + network.name + " network interface.");
395 if(DEBUG) debug("Going to set proxy settings for " + network.name + " network interface.");
396 // Sets manual proxy configuration.
397 Services.prefs.setIntPref("network.proxy.type", MANUAL_PROXY_CONFIGURATION);
398 // Do not use this proxy server for all protocols.
399 Services.prefs.setBoolPref("network.proxy.share_proxy_settings", false);
400 Services.prefs.setCharPref("network.proxy.http", network.httpProxyHost);
401 Services.prefs.setCharPref("network.proxy.ssl", network.httpProxyHost);
402 let port = network.httpProxyPort === 0 ? 8080 : network.httpProxyPort;
403 Services.prefs.setIntPref("network.proxy.http_port", port);
404 Services.prefs.setIntPref("network.proxy.ssl_port", port);
406 if(DEBUG) debug("Exception " + ex + ". Unable to set proxy setting for " +
407 network.name + " network interface.");
411 // Enable/Disable DHCP server.
412 setDhcpServer: function(enabled, config, callback) {
413 if (null === config) {
417 config.cmd = "setDhcpServer";
418 config.isAsync = true;
419 config.enabled = enabled;
421 this.controlMessage(config, function setDhcpServerResult(response) {
422 if (!response.success) {
423 callback.dhcpServerResult('Set DHCP server error');
426 callback.dhcpServerResult(null);
430 // Enable/disable WiFi tethering by sending commands to netd.
431 setWifiTethering: function(enable, config, callback) {
432 // config should've already contained:
436 config.wifictrlinterfacename = WIFI_CTRL_INTERFACE;
437 config.cmd = "setWifiTethering";
439 // The callback function in controlMessage may not be fired immediately.
440 config.isAsync = true;
441 this.controlMessage(config, function setWifiTetheringResult(data) {
442 let code = data.resultCode;
443 let reason = data.resultReason;
444 let enable = data.enable;
445 let enableString = enable ? "Enable" : "Disable";
447 if(DEBUG) debug(enableString + " Wifi tethering result: Code " + code + " reason " + reason);
450 callback.wifiTetheringEnabledChange("netd command error");
452 callback.wifiTetheringEnabledChange(null);
457 // Enable/disable USB tethering by sending commands to netd.
458 setUSBTethering: function(enable, config, callback) {
459 config.cmd = "setUSBTethering";
460 // The callback function in controlMessage may not be fired immediately.
461 config.isAsync = true;
462 this.controlMessage(config, function setUsbTetheringResult(data) {
463 let code = data.resultCode;
464 let reason = data.resultReason;
465 let enable = data.enable;
466 let enableString = enable ? "Enable" : "Disable";
468 if(DEBUG) debug(enableString + " USB tethering result: Code " + code + " reason " + reason);
471 callback.usbTetheringEnabledChange("netd command error");
473 callback.usbTetheringEnabledChange(null);
478 // Switch usb function by modifying property of persist.sys.usb.config.
479 enableUsbRndis: function(enable, callback) {
480 if(DEBUG) debug("enableUsbRndis: " + enable);
483 cmd: "enableUsbRndis",
486 // Ask net work to report the result when this value is set to true.
488 params.report = true;
490 params.report = false;
493 // The callback function in controlMessage may not be fired immediately.
494 params.isAsync = true;
495 //this._usbTetheringAction = TETHERING_STATE_ONGOING;
496 this.controlMessage(params, function(data) {
497 callback.enableUsbRndisResult(data.result, data.enable);
501 updateUpStream: function(previous, current, callback) {
503 cmd: "updateUpStream",
505 preInternalIfname: previous.internalIfname,
506 preExternalIfname: previous.externalIfname,
507 curInternalIfname: current.internalIfname,
508 curExternalIfname: current.externalIfname
511 this.controlMessage(params, function(data) {
512 let code = data.resultCode;
513 let reason = data.resultReason;
514 if(DEBUG) debug("updateUpStream result: Code " + code + " reason " + reason);
515 callback.updateUpStreamResult(!isError(code), data.curExternalIfname);
521 observe: function observe(aSubject, aTopic, aData) {
523 case "xpcom-shutdown":
524 debug("NetworkService shutdown");
525 this.shutdown = true;
526 Services.obs.removeObserver(this, "xpcom-shutdown");
527 if (gNetworkWorker) {
528 gNetworkWorker.shutdown();
529 gNetworkWorker = null;
536 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkService]);