mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-30 21:55:31 +00:00
656 lines
19 KiB
JavaScript
656 lines
19 KiB
JavaScript
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
"use strict";
|
|
|
|
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
|
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
const TOPIC_INTERFACE_STATE_CHANGED = "network-interface-state-changed";
|
|
|
|
const ETHERNET_NETWORK_IFACE_PREFIX = "eth";
|
|
const DEFAULT_ETHERNET_NETWORK_IFACE = "eth0";
|
|
|
|
const INTERFACE_IPADDR_NULL = "0.0.0.0";
|
|
const INTERFACE_GATEWAY_NULL = "0.0.0.0";
|
|
const INTERFACE_PREFIX_NULL = 0;
|
|
const INTERFACE_MACADDR_NULL = "00:00:00:00:00:00";
|
|
|
|
const NETWORK_INTERFACE_UP = "up";
|
|
const NETWORK_INTERFACE_DOWN = "down";
|
|
|
|
const IP_MODE_DHCP = "dhcp";
|
|
const IP_MODE_STATIC = "static";
|
|
|
|
const PREF_NETWORK_DEBUG_ENABLED = "network.debugging.enabled";
|
|
|
|
XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
|
|
"@mozilla.org/network/manager;1",
|
|
"nsINetworkManager");
|
|
|
|
XPCOMUtils.defineLazyServiceGetter(this, "gNetworkService",
|
|
"@mozilla.org/network/service;1",
|
|
"nsINetworkService");
|
|
|
|
let debug;
|
|
function updateDebug() {
|
|
let debugPref = false; // set default value here.
|
|
try {
|
|
debugPref = debugPref || Services.prefs.getBoolPref(PREF_NETWORK_DEBUG_ENABLED);
|
|
} catch (e) {}
|
|
|
|
if (debugPref) {
|
|
debug = function(s) {
|
|
dump("-*- EthernetManager: " + s + "\n");
|
|
};
|
|
} else {
|
|
debug = function(s) {};
|
|
}
|
|
}
|
|
updateDebug();
|
|
|
|
// nsINetworkInterface
|
|
|
|
function EthernetInterface(attr) {
|
|
this.info.state = attr.state;
|
|
this.info.type = attr.type;
|
|
this.info.name = attr.name;
|
|
this.info.ipMode = attr.ipMode;
|
|
this.info.ips = [attr.ip];
|
|
this.info.prefixLengths = [attr.prefixLength];
|
|
this.info.gateways = [attr.gateway];
|
|
this.info.dnses = attr.dnses;
|
|
this.httpProxyHost = "";
|
|
this.httpProxyPort = 0;
|
|
}
|
|
EthernetInterface.prototype = {
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInterface]),
|
|
|
|
updateConfig: function(config) {
|
|
debug("Interface " + this.info.name + " updateConfig " + JSON.stringify(config));
|
|
this.info.state = (config.state != undefined) ?
|
|
config.state : this.info.state;
|
|
this.info.ips = (config.ip != undefined) ? [config.ip] : this.info.ips;
|
|
this.info.prefixLengths = (config.prefixLength != undefined) ?
|
|
[config.prefixLength] : this.info.prefixLengths;
|
|
this.info.gateways = (config.gateway != undefined) ?
|
|
[config.gateway] : this.info.gateways;
|
|
this.info.dnses = (config.dnses != undefined) ? config.dnses : this.info.dnses;
|
|
this.httpProxyHost = (config.httpProxyHost != undefined) ?
|
|
config.httpProxyHost : this.httpProxyHost;
|
|
this.httpProxyPort = (config.httpProxyPort != undefined) ?
|
|
config.httpProxyPort : this.httpProxyPort;
|
|
this.info.ipMode = (config.ipMode != undefined) ?
|
|
config.ipMode : this.info.ipMode;
|
|
},
|
|
|
|
info: {
|
|
getAddresses: function(ips, prefixLengths) {
|
|
ips.value = this.ips.slice();
|
|
prefixLengths.value = this.prefixLengths.slice();
|
|
|
|
return this.ips.length;
|
|
},
|
|
|
|
getGateways: function(count) {
|
|
if (count) {
|
|
count.value = this.gateways.length;
|
|
}
|
|
return this.gateways.slice();
|
|
},
|
|
|
|
getDnses: function(count) {
|
|
if (count) {
|
|
count.value = this.dnses.length;
|
|
}
|
|
return this.dnses.slice();
|
|
}
|
|
}
|
|
};
|
|
|
|
// nsIEthernetManager
|
|
|
|
/*
|
|
* Network state transition diagram
|
|
*
|
|
* ---------- enable --------- connect ----------- disconnect --------------
|
|
* | Disabled | -----> | Enabled | -------> | Connected | <----------> | Disconnected |
|
|
* ---------- --------- ----------- connect --------------
|
|
* ^ | | |
|
|
* | disable | | |
|
|
* -----------------------------------------------------------------------
|
|
*/
|
|
|
|
function EthernetManager() {
|
|
debug("EthernetManager start");
|
|
|
|
// Interface list.
|
|
this.ethernetInterfaces = {};
|
|
|
|
// Used to memorize last connection information.
|
|
this.lastStaticConfig = {};
|
|
|
|
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
|
}
|
|
|
|
EthernetManager.prototype = {
|
|
classID: Components.ID("a96441dd-36b3-4f7f-963b-2c032e28a039"),
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIEthernetManager]),
|
|
|
|
ethernetInterfaces: null,
|
|
lastStaticConfig: null,
|
|
|
|
observer: function(subject, topic, data) {
|
|
switch (topic) {
|
|
case "xpcom-shutdown":
|
|
debug("xpcom-shutdown");
|
|
|
|
this._shutdown();
|
|
|
|
Services.obs.removeObserver(this, "xpcom-shutdown");
|
|
break;
|
|
}
|
|
},
|
|
|
|
_shutdown: function() {
|
|
debug("Shuting down");
|
|
(function onRemove(ifnameList) {
|
|
if (!ifnameList.length) {
|
|
return;
|
|
}
|
|
|
|
let ifname = ifnameList.shift();
|
|
this.removeInterface(ifname, { notify: onRemove.bind(this, ifnameList) });
|
|
}).call(this, Object.keys(this.ethernetInterfaces));
|
|
},
|
|
|
|
get interfaceList() {
|
|
return Object.keys(this.ethernetInterfaces);
|
|
},
|
|
|
|
scan: function(callback) {
|
|
debug("Scan");
|
|
|
|
gNetworkService.getInterfaces(function(success, list) {
|
|
let ethList = [];
|
|
|
|
if (!success) {
|
|
if (callback) {
|
|
callback.notify(ethList);
|
|
}
|
|
return;
|
|
}
|
|
|
|
for (let i = 0; i < list.length; i++) {
|
|
debug("Found interface " + list[i]);
|
|
if (!list[i].startsWith(ETHERNET_NETWORK_IFACE_PREFIX)) {
|
|
continue;
|
|
}
|
|
ethList.push(list[i]);
|
|
}
|
|
|
|
if (callback) {
|
|
callback.notify(ethList);
|
|
}
|
|
});
|
|
},
|
|
|
|
addInterface: function(ifname, callback) {
|
|
debug("Add interface " + ifname);
|
|
|
|
if (!ifname || !ifname.startsWith(ETHERNET_NETWORK_IFACE_PREFIX)) {
|
|
if (callback) {
|
|
callback.notify(false, "Invalid interface.");
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (this.ethernetInterfaces[ifname]) {
|
|
if (callback) {
|
|
callback.notify(true, "Interface already exists.");
|
|
}
|
|
return;
|
|
}
|
|
|
|
gNetworkService.getInterfaceConfig(ifname, function(success, result) {
|
|
if (!success) {
|
|
if (callback) {
|
|
callback.notify(false, "Netd error.");
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Since the operation may still succeed with an invalid interface name,
|
|
// check the mac address as well.
|
|
if (result.macAddr == INTERFACE_MACADDR_NULL) {
|
|
if (callback) {
|
|
callback.notify(false, "Interface not found.");
|
|
}
|
|
return;
|
|
}
|
|
|
|
this.ethernetInterfaces[ifname] = new EthernetInterface({
|
|
state: result.link == NETWORK_INTERFACE_UP ?
|
|
Ci.nsINetworkInfo.NETWORK_STATE_DISABLED :
|
|
Ci.nsINetworkInfo.NETWORK_STATE_ENABLED,
|
|
name: ifname,
|
|
type: Ci.nsINetworkInfo.NETWORK_TYPE_ETHERNET,
|
|
ip: result.ip,
|
|
prefixLength: result.prefix,
|
|
ipMode: IP_MODE_DHCP
|
|
});
|
|
|
|
// Register the interface to NetworkManager.
|
|
gNetworkManager.registerNetworkInterface(this.ethernetInterfaces[ifname]);
|
|
|
|
debug("Add interface " + ifname + " succeeded with " +
|
|
JSON.stringify(this.ethernetInterfaces[ifname]));
|
|
|
|
if (callback) {
|
|
callback.notify(true, "ok");
|
|
}
|
|
}.bind(this));
|
|
},
|
|
|
|
removeInterface: function(ifname, callback) {
|
|
debug("Remove interface " + ifname);
|
|
|
|
if (!ifname || !ifname.startsWith(ETHERNET_NETWORK_IFACE_PREFIX)) {
|
|
if (callback) {
|
|
callback.notify(false, "Invalid interface.");
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!this.ethernetInterfaces[ifname]) {
|
|
if (callback) {
|
|
callback.notify(true, "Interface does not exist.");
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Make sure interface is disable before removing.
|
|
this.disable(ifname, { notify: function(success, message) {
|
|
// Unregister the interface from NetworkManager and also remove it from
|
|
// the interface list.
|
|
gNetworkManager.unregisterNetworkInterface(this.ethernetInterfaces[ifname]);
|
|
delete this.ethernetInterfaces[ifname];
|
|
|
|
debug("Remove interface " + ifname + " succeeded.");
|
|
|
|
if (callback) {
|
|
callback.notify(true, "ok");
|
|
}
|
|
}.bind(this)});
|
|
},
|
|
|
|
updateInterfaceConfig: function(ifname, config, callback) {
|
|
debug("Update interface config with " + ifname);
|
|
|
|
this._ensureIfname(ifname, callback, function(iface) {
|
|
if (!config) {
|
|
if (callback) {
|
|
callback.notify(false, "No config to update.");
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Network state can not be modified externally.
|
|
if (config.state) {
|
|
delete config.state;
|
|
}
|
|
|
|
let currentIpMode = iface.info.ipMode;
|
|
|
|
// Update config.
|
|
this.ethernetInterfaces[iface.info.name].updateConfig(config);
|
|
|
|
// Do not automatically re-connect if the interface is not in connected
|
|
// state.
|
|
if (iface.info.state != Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED) {
|
|
if (callback) {
|
|
callback.notify(true, "ok");
|
|
}
|
|
return;
|
|
}
|
|
|
|
let newIpMode = this.ethernetInterfaces[iface.info.name].info.ipMode;
|
|
|
|
if (newIpMode == IP_MODE_STATIC) {
|
|
this._setStaticIP(iface.info.name, callback);
|
|
return;
|
|
}
|
|
if ((currentIpMode == IP_MODE_STATIC) && (newIpMode == IP_MODE_DHCP)) {
|
|
gNetworkService.stopDhcp(iface.info.name, function(success) {
|
|
if (success) {
|
|
debug("DHCP for " + iface.info.name + " stopped.");
|
|
}
|
|
});
|
|
|
|
// Clear the current network settings before do dhcp request, otherwise
|
|
// dhcp settings could fail.
|
|
this.disconnect(iface.info.name, { notify: function(success, message) {
|
|
if (!success) {
|
|
if (callback) {
|
|
callback.notify("Disconnect failed.");
|
|
}
|
|
return;
|
|
}
|
|
this._runDhcp(iface.info.name, callback);
|
|
}.bind(this) });
|
|
return;
|
|
}
|
|
|
|
if (callback) {
|
|
callback.notify(true, "ok");
|
|
}
|
|
}.bind(this));
|
|
},
|
|
|
|
enable: function(ifname, callback) {
|
|
debug("Enable interface " + ifname);
|
|
|
|
this._ensureIfname(ifname, callback, function(iface) {
|
|
// Interface can be only enabled in the state of disabled.
|
|
if (iface.info.state != Ci.nsINetworkInfo.NETWORK_STATE_DISABLED) {
|
|
if (callback) {
|
|
callback.notify(true, "Interface already enabled.");
|
|
}
|
|
return;
|
|
}
|
|
|
|
let ips = {};
|
|
let prefixLengths = {};
|
|
iface.info.getAddresses(ips, prefixLengths);
|
|
let config = { ifname: iface.info.name,
|
|
ip: ips.value[0],
|
|
prefix: prefixLengths.value[0],
|
|
link: NETWORK_INTERFACE_UP };
|
|
gNetworkService.setInterfaceConfig(config, function(success) {
|
|
if (!success) {
|
|
if (callback) {
|
|
callback.notify(false, "Netd Error.");
|
|
}
|
|
return;
|
|
}
|
|
|
|
this.ethernetInterfaces[iface.info.name].updateConfig({
|
|
state: Ci.nsINetworkInfo.NETWORK_STATE_ENABLED
|
|
});
|
|
|
|
debug("Enable interface " + iface.info.name + " succeeded.");
|
|
|
|
if (callback) {
|
|
callback.notify(true, "ok");
|
|
}
|
|
}.bind(this));
|
|
}.bind(this));
|
|
},
|
|
|
|
disable: function(ifname, callback) {
|
|
debug("Disable interface " + ifname);
|
|
|
|
this._ensureIfname(ifname, callback, function(iface) {
|
|
if (iface.info.state == Ci.nsINetworkInfo.NETWORK_STATE_DISABLED) {
|
|
if (callback) {
|
|
callback.notify(true, "Interface already disabled.");
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (iface.info.state == Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED) {
|
|
gNetworkService.stopDhcp(iface.info.name, function(success) {
|
|
if (success) {
|
|
debug("DHCP for " + iface.info.name + " stopped.");
|
|
}
|
|
});
|
|
}
|
|
|
|
let ips = {};
|
|
let prefixLengths = {};
|
|
iface.info.getAddresses(ips, prefixLengths);
|
|
let config = { ifname: iface.info.name,
|
|
ip: ips.value[0],
|
|
prefix: prefixLengths.value[0],
|
|
link: NETWORK_INTERFACE_DOWN };
|
|
gNetworkService.setInterfaceConfig(config, function(success) {
|
|
if (!success) {
|
|
if (callback) {
|
|
callback.notify(false, "Netd Error.");
|
|
}
|
|
return;
|
|
}
|
|
|
|
this.ethernetInterfaces[iface.info.name].updateConfig({
|
|
state: Ci.nsINetworkInfo.NETWORK_STATE_DISABLED
|
|
});
|
|
|
|
debug("Disable interface " + iface.info.name + " succeeded.");
|
|
|
|
if (callback) {
|
|
callback.notify(true, "ok");
|
|
}
|
|
}.bind(this));
|
|
}.bind(this));
|
|
},
|
|
|
|
connect: function(ifname, callback) {
|
|
debug("Connect interface " + ifname);
|
|
|
|
this._ensureIfname(ifname, callback, function(iface) {
|
|
// Interface can only be connected in the state of enabled or
|
|
// disconnected.
|
|
if (iface.info.state == Ci.nsINetworkInfo.NETWORK_STATE_DISABLED ||
|
|
iface.info.state == Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED) {
|
|
if (callback) {
|
|
callback.notify(true, "Interface " + ifname + " is not available or "
|
|
+ " already connected.");
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (iface.info.ipMode == IP_MODE_DHCP) {
|
|
this._runDhcp(iface.info.name, callback);
|
|
return;
|
|
}
|
|
|
|
if (iface.info.ipMode == IP_MODE_STATIC) {
|
|
if (this._checkConfigNull(iface) && this.lastStaticConfig[iface.info.name]) {
|
|
debug("Connect with lastStaticConfig " +
|
|
JSON.stringify(this.lastStaticConfig[iface.info.name]));
|
|
this.ethernetInterfaces[iface.info.name].updateConfig(
|
|
this.lastStaticConfig[iface.info.name]);
|
|
}
|
|
this._setStaticIP(iface.info.name, callback);
|
|
return;
|
|
}
|
|
|
|
if (callback) {
|
|
callback.notify(false, "IP mode is wrong or not set.");
|
|
}
|
|
}.bind(this));
|
|
},
|
|
|
|
disconnect: function(ifname, callback) {
|
|
debug("Disconnect interface " + ifname);
|
|
|
|
this._ensureIfname(ifname, callback, function(iface) {
|
|
// Interface can be only disconnected in the state of connected.
|
|
if (iface.info.state != Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED) {
|
|
if (callback) {
|
|
callback.notify(true, "Interface is already disconnected");
|
|
}
|
|
return;
|
|
}
|
|
|
|
let config = { ifname: iface.info.name,
|
|
ip: INTERFACE_IPADDR_NULL,
|
|
prefix: INTERFACE_PREFIX_NULL,
|
|
link: NETWORK_INTERFACE_UP };
|
|
gNetworkService.setInterfaceConfig(config, function(success) {
|
|
if (!success) {
|
|
if (callback) {
|
|
callback.notify(false, "Netd error.");
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Stop dhcp daemon.
|
|
gNetworkService.stopDhcp(iface.info.name, function(success) {
|
|
if (success) {
|
|
debug("DHCP for " + iface.info.name + " stopped.");
|
|
}
|
|
});
|
|
|
|
this.ethernetInterfaces[iface.info.name].updateConfig({
|
|
state: Ci.nsINetworkInfo.NETWORK_STATE_DISCONNECTED,
|
|
ip: INTERFACE_IPADDR_NULL,
|
|
prefixLength: INTERFACE_PREFIX_NULL,
|
|
gateway: INTERFACE_GATEWAY_NULL
|
|
});
|
|
|
|
gNetworkManager.updateNetworkInterface(this.ethernetInterfaces[ifname]);
|
|
|
|
debug("Disconnect interface " + iface.info.name + " succeeded.");
|
|
|
|
if (callback) {
|
|
callback.notify(true, "ok");
|
|
}
|
|
}.bind(this));
|
|
}.bind(this));
|
|
},
|
|
|
|
_checkConfigNull: function(iface) {
|
|
let ips = {};
|
|
let prefixLengths = {};
|
|
let gateways = iface.info.getGateways();
|
|
iface.info.getAddresses(ips, prefixLengths);
|
|
|
|
if (ips.value[0] == INTERFACE_IPADDR_NULL &&
|
|
prefixLengths.value[0] == INTERFACE_PREFIX_NULL &&
|
|
gateways[0] == INTERFACE_GATEWAY_NULL) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
},
|
|
|
|
_ensureIfname: function(ifname, callback, func) {
|
|
// If no given ifname, use the default one.
|
|
if (!ifname) {
|
|
ifname = DEFAULT_ETHERNET_NETWORK_IFACE;
|
|
}
|
|
|
|
let iface = this.ethernetInterfaces[ifname];
|
|
if (!iface) {
|
|
if (callback) {
|
|
callback.notify(true, "Interface " + ifname + " is not available.");
|
|
}
|
|
return;
|
|
}
|
|
|
|
func.call(this, iface);
|
|
},
|
|
|
|
_runDhcp: function(ifname, callback) {
|
|
debug("runDhcp with " + ifname);
|
|
|
|
if (!this.ethernetInterfaces[ifname]) {
|
|
if (callback) {
|
|
callback.notify(false, "Invalid interface.");
|
|
}
|
|
return;
|
|
}
|
|
|
|
gNetworkService.dhcpRequest(ifname, function(success, result) {
|
|
if (!success) {
|
|
if (callback) {
|
|
callback.notify(false, "DHCP failed.");
|
|
}
|
|
return;
|
|
}
|
|
|
|
debug("DHCP succeeded with " + JSON.stringify(result));
|
|
|
|
// Clear last static network information when connecting with dhcp mode.
|
|
if (this.lastStaticConfig[ifname]) {
|
|
this.lastStaticConfig[ifname] = null;
|
|
}
|
|
|
|
this.ethernetInterfaces[ifname].updateConfig({
|
|
state: Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED,
|
|
ip: result.ipaddr_str,
|
|
gateway: result.gateway_str,
|
|
prefixLength: result.prefixLength,
|
|
dnses: [result.dns1_str, result.dns2_str]
|
|
});
|
|
|
|
gNetworkManager.updateNetworkInterface(this.ethernetInterfaces[ifname]);
|
|
|
|
debug("Connect interface " + ifname + " with DHCP succeeded.");
|
|
|
|
if (callback) {
|
|
callback.notify(true, "ok");
|
|
}
|
|
}.bind(this));
|
|
},
|
|
|
|
_setStaticIP: function(ifname, callback) {
|
|
let iface = this.ethernetInterfaces[ifname];
|
|
if (!iface) {
|
|
if (callback) {
|
|
callback.notify(false, "Invalid interface.");
|
|
}
|
|
return;
|
|
}
|
|
|
|
let ips = {};
|
|
let prefixLengths = {};
|
|
iface.info.getAddresses(ips, prefixLengths);
|
|
|
|
let config = { ifname: iface.info.name,
|
|
ip: ips.value[0],
|
|
prefix: prefixLengths.value[0],
|
|
link: NETWORK_INTERFACE_UP };
|
|
gNetworkService.setInterfaceConfig(config, function(success) {
|
|
if (!success) {
|
|
if (callback) {
|
|
callback.notify(false, "Netd Error.");
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Keep the lastest static network information.
|
|
let ips = {};
|
|
let prefixLengths = {};
|
|
let gateways = iface.info.getGateways();
|
|
iface.info.getAddresses(ips, prefixLengths);
|
|
|
|
this.lastStaticConfig[iface.info.name] = {
|
|
ip: ips.value[0],
|
|
prefixLength: prefixLengths.value[0],
|
|
gateway: gateways[0]
|
|
};
|
|
|
|
this.ethernetInterfaces[ifname].updateConfig({
|
|
state: Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED,
|
|
});
|
|
|
|
gNetworkManager.updateNetworkInterface(this.ethernetInterfaces[ifname]);
|
|
|
|
debug("Connect interface " + ifname + " with static ip succeeded.");
|
|
|
|
if (callback) {
|
|
callback.notify(true, "ok");
|
|
}
|
|
}.bind(this));
|
|
},
|
|
}
|
|
|
|
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([EthernetManager]);
|