442 lines
11 KiB
JavaScript
442 lines
11 KiB
JavaScript
/** @module nxapp/protocols/Tcp */
|
|
define([
|
|
'dcl/dcl',
|
|
"nxapp/utils",
|
|
'nxapp/protocols/ProtocolBase',
|
|
"dojo/node!net",
|
|
"dojo/node!child_process",
|
|
"dojo/node!util",
|
|
"dojo/node!path",
|
|
"xide/mixins/EventedMixin",
|
|
"xide/mixins/ReloadMixin",
|
|
"xide/types",
|
|
"dojo/Deferred",
|
|
"dojo/node!evilscan",
|
|
"dojo/node!ipaddr.js",
|
|
"dojo/node!os"
|
|
], function (dcl, utils, ProtocolBase, net, child, util, path, EventedMixin, ReloadMixin, types, Deferred, evilscan, ipaddr, os) {
|
|
|
|
var debug = false;
|
|
|
|
/**
|
|
* Tcp protocol client
|
|
* @class module:nxapp/protocols/Tcp
|
|
* @extends module:nxapp/protocols/ProtocolBase
|
|
* @implements module:nxapp/protocols/ProtocolBase
|
|
*/
|
|
var Module = dcl(ProtocolBase, {
|
|
declaredClass: "nxapp.protocols.Tcp",
|
|
/**
|
|
* @member _socket {module:net/Socket | module:net/Server}
|
|
*/
|
|
_socket: null,
|
|
protocolName: 'tcp',
|
|
/**
|
|
* @member delegate {module:nxapp/manager/ConnectionManager}
|
|
*/
|
|
delegate: null,
|
|
driverInstance: null,
|
|
_handleSocketEmits: function (socket) {
|
|
var self = this;
|
|
self.clients = [];
|
|
var connection = self.connection;
|
|
var scope = this.blockScope;
|
|
var responseVariable = scope.getVariable('value');
|
|
var responseVariables = scope.getVariables({
|
|
group: types.BLOCK_GROUPS.CF_DRIVER_RESPONSE_VARIABLES
|
|
});
|
|
var responseBlocks = scope.getBlocks({
|
|
group: types.BLOCK_GROUPS.CF_DRIVER_RESPONSE_BLOCKS
|
|
});
|
|
socket.on('connection', function (conn) {
|
|
self.socket = conn;
|
|
self.clients.push(conn);
|
|
conn.on('data', function (data) {
|
|
var messages = [{
|
|
string: data.toString(),
|
|
bytes: data.join(",")
|
|
}];
|
|
|
|
for (var i = 0; i < messages.length; i++) {
|
|
if (messages[i].length === 0) {
|
|
continue;
|
|
}
|
|
responseVariable.value = new String(messages[i].string);
|
|
responseVariable.value.setBytes(messages[i].bytes);
|
|
|
|
//now run each top variable block in 'conditional process'
|
|
for (var j = 0; j < responseVariables.length; j++) {
|
|
var _var = responseVariables[j];
|
|
if (responseVariables[j].title == 'value') {
|
|
continue;
|
|
}
|
|
|
|
var _varResult = null;
|
|
var _cValue = responseVariable.value;
|
|
|
|
|
|
if (!(typeof _cValue == "number")) {
|
|
_cValue = '' + _cValue;
|
|
_cValue = "'" + _cValue + "'";
|
|
}
|
|
var prefix = "var value = " + _cValue + ";";
|
|
|
|
_varResult = _cValue;
|
|
if (_var.target && _var.target != 'None' && _varResult !== null && _varResult != 'null' && _varResult != "'null'") {
|
|
var targetVariable = scope.getVariable(_var.target);
|
|
if (targetVariable) {
|
|
targetVariable.value = _varResult;
|
|
this.publish(types.EVENTS.ON_DRIVER_VARIABLE_CHANGED, {
|
|
item: targetVariable,
|
|
scope: scope,
|
|
owner: this,
|
|
save: false,
|
|
source: types.MESSAGE_SOURCE.BLOX //for prioritizing
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
for (var k = 0; k < messages.length; k++) {
|
|
var __message = messages[k];
|
|
if (_.isObject(__message)) {
|
|
if (__message.src) {
|
|
var block = scope.getBlockById(__message.src);
|
|
if (block && block.onData) {
|
|
block.onData(__message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
for (var l = 0; l < responseBlocks.length; l++) {
|
|
var block = responseBlocks[l];
|
|
if (block.enabled === false) {
|
|
continue;
|
|
}
|
|
block.override = {
|
|
args: [responseVariable.value]
|
|
};
|
|
try {
|
|
scope.solveBlock(responseBlocks[l], {
|
|
highlight: false
|
|
});
|
|
} catch (e) {
|
|
console.log('----solving response block crashed ', e);
|
|
console.trace();
|
|
}
|
|
}
|
|
}
|
|
});
|
|
conn.on('close', function () {
|
|
debug && console.info('close client connection ');
|
|
});
|
|
});
|
|
},
|
|
_creatingServer: false,
|
|
makeServer: function () {
|
|
if (this._creatingServer) {
|
|
return;
|
|
}
|
|
this._creatingServer = true;
|
|
try {
|
|
|
|
debug && console.log('create server');
|
|
var options = this.options;
|
|
var port = options.port;
|
|
var host = options.host;
|
|
|
|
var connectionManager = this.owner;
|
|
var context = connectionManager.ctx;
|
|
|
|
var driverManager = context.getDriverManager();
|
|
var dfd = driverManager.createDriverInstance(options);
|
|
var self = this;
|
|
|
|
dfd.then(function (data) {
|
|
self.blockScope = data.blockScope;
|
|
self.driverInstance = data.driverInstance;
|
|
});
|
|
|
|
var server = new net.Server();
|
|
server.listen(port, host);
|
|
self._socket = server;
|
|
server.writable = true;
|
|
self._handleSocketEmits(server);
|
|
self.connection.connected = true;
|
|
self.delegate.onConnect2(self.connection);
|
|
this.subscribe(types.EVENTS.ON_DEVICE_CONNECTED, this.onDeviceConnected);
|
|
this.subscribe(types.EVENTS.ON_DEVICE_DISCONNECTED, this.onDeviceDisconnected);
|
|
} catch (e) {
|
|
console.error('error creating server', e);
|
|
}
|
|
return this;
|
|
},
|
|
/**
|
|
* @member {module:nxapp/model/Connection []}
|
|
*/
|
|
connections: null,
|
|
/**
|
|
* @param evt {object}
|
|
* @param evt.connection {module:nxapp/model/Connection}
|
|
*/
|
|
onDeviceConnected: function (evt) {
|
|
var connection = evt.connection;
|
|
if (!connection) {
|
|
return;
|
|
}
|
|
var cOptions = connection.options;
|
|
var options = this.options;
|
|
if (!this.connections) {
|
|
this.connections = [];
|
|
}
|
|
if (connection.id === this.connection.id) {
|
|
return;
|
|
}
|
|
var exists = this.delegate.getConnectionById(connection.id);
|
|
|
|
if (exists && options.host === cOptions.host && options.port === cOptions.port && options.protocol === cOptions.protocol && !connection.isServer()) {
|
|
if (this.connections.indexOf(connection) === -1) {
|
|
this.connections.push(connection);
|
|
}
|
|
}
|
|
},
|
|
/**
|
|
* @param evt {object}
|
|
* @param evt.connection {module:nxapp/model/Connection}
|
|
*/
|
|
onDeviceDisconnected: function (evt) {
|
|
var connection = evt.connection;
|
|
|
|
if (!connection) {
|
|
return;
|
|
}
|
|
if (!this.connections) {
|
|
this.connections = [];
|
|
}
|
|
if (this.connections.indexOf(connection) !== -1) {
|
|
this.connections.remove(connection);
|
|
}
|
|
|
|
},
|
|
/**
|
|
* Implement connect
|
|
* @returns {module:nxapp/protocols/Tcp}
|
|
*/
|
|
connect: function () {
|
|
var isServer = this.isServer();
|
|
// console.log('is server', isServer, this.options);
|
|
if (isServer) {
|
|
return this.makeServer();
|
|
}
|
|
var options = this.options;
|
|
var port = this.options.port;
|
|
var host = this.options.host;
|
|
this.clients = [];
|
|
|
|
|
|
this._socket = new net.Socket(utils.mixin({
|
|
allowHalfOpen: true,
|
|
writable: true,
|
|
readable: true
|
|
}, options.options));
|
|
|
|
|
|
var self = this;
|
|
this.isDebug() && console.log('TCP-Client->Connect to ' + this.options.host + ' : ' + this.options.port + ' @ ' + this.protocolName);
|
|
this._socket.connect(port, host, function () {
|
|
self.connection.connected = true;
|
|
self.delegate.onConnect2(self.connection);
|
|
});
|
|
this._socket.setNoDelay(true);
|
|
this._socket.setKeepAlive(true, 0);
|
|
this._socket.owner = this;
|
|
this._setupEventListeners(this._socket, this.delegate);
|
|
return this;
|
|
},
|
|
broadCastMessage: function (eventName, _data) {
|
|
var self = this;
|
|
var data = _data;
|
|
if (this.connections) {
|
|
_.each(this.connections, function (connection) {
|
|
if (connection && connection._destroyed) {
|
|
self.connections.remove(connection);
|
|
}
|
|
if (connection) {
|
|
if (connection.client) {
|
|
connection.client.delegate.onData(connection, data.toString(), data);
|
|
} else {
|
|
console.error('not at a real connection', connection);
|
|
}
|
|
} else {
|
|
console.error('invalid connection', connection);
|
|
}
|
|
});
|
|
}
|
|
},
|
|
/**
|
|
* Implement send
|
|
* @implements module:nxapp/protocols/ProtocolBase~send
|
|
* @inheritDoc
|
|
* @param cmd {string} the raw string to send
|
|
*/
|
|
send: function (cmd) {
|
|
if (cmd == null) {
|
|
console.error('TCP : invalid command');
|
|
return;
|
|
}
|
|
var intArray = utils.bufferFromDecString(cmd);
|
|
var buffer = new Buffer(intArray);
|
|
if (this.isServer()) {
|
|
this.broadCastMessage(types.EVENTS.ON_DEVICE_MESSAGE, buffer);
|
|
} else {
|
|
this._socket.write(buffer);
|
|
}
|
|
},
|
|
destroy: function () {
|
|
console.log('destroy');
|
|
if (!this.isServer()) {
|
|
this._socket.end();
|
|
} else {
|
|
this._socket.close();
|
|
}
|
|
this.blockScope && this.blockScope.destroy();
|
|
this.driverInstance && this.driverInstance.destroy();
|
|
delete this.blockScope;
|
|
delete this.driverInstance;
|
|
delete this.connections;
|
|
delete this.clients;
|
|
delete this._socket;
|
|
this._destroyed = true;
|
|
},
|
|
/**
|
|
* Implement close
|
|
* @inheritDoc
|
|
*/
|
|
close: function () {
|
|
if (!this.isServer()) {
|
|
this._socket.end();
|
|
} else {
|
|
this._socket.close();
|
|
this.destroy();
|
|
}
|
|
}
|
|
});
|
|
Module.net = net;
|
|
Module.ls = function (query) {
|
|
try {
|
|
var dfd = new Deferred();
|
|
var ifaces = os.networkInterfaces();
|
|
var ips = [];
|
|
Object.keys(ifaces).forEach(function (ifname) {
|
|
var alias = 0;
|
|
ifaces[ifname].forEach(function (iface) {
|
|
if ('IPv4' !== iface.family || iface.internal !== false) {
|
|
// skip over internal (i.e. 127.0.0.1) and non-ipv4 addresses
|
|
return;
|
|
}
|
|
if (alias >= 1) {
|
|
// this single interface has multiple ipv4 addresses
|
|
ips.push({
|
|
face: ifname + alias,
|
|
ip: iface.address
|
|
})
|
|
} else {
|
|
ips.push({
|
|
face: ifname,
|
|
ip: iface.address
|
|
})
|
|
}
|
|
++alias;
|
|
});
|
|
});
|
|
var results = {}
|
|
|
|
function checkResults() {
|
|
var done = true;
|
|
_.each(results, function (item) {
|
|
if (!item.done) {
|
|
done = false;
|
|
}
|
|
})
|
|
if (done) {
|
|
var result = [];
|
|
results = _.filter(results, function (result) {
|
|
return result.list.length > 0
|
|
});
|
|
_.each(results, function (item) {
|
|
result = result.concat(item.list);
|
|
});
|
|
dfd.resolve(result);
|
|
}
|
|
}
|
|
_.each(ips, function (ip) {
|
|
var range = ipaddr.parse(ip.ip);
|
|
var octets = range.octets;
|
|
octets[octets.length - 1] = 0;
|
|
var target = octets.join('.') + '-254';
|
|
var testPorts = [query.ports || '80'];
|
|
var options = {
|
|
target: target,
|
|
port: testPorts.join(','),
|
|
status: 'Open', // Timeout, Refused, Open, Unreachable
|
|
timeout: 500,
|
|
banner: false
|
|
}
|
|
var scanner = new evilscan(options);
|
|
if (!results[target]) {
|
|
results[target] = {
|
|
done: false
|
|
};
|
|
results[target].list = [];
|
|
}
|
|
scanner.on('result', function (data) {
|
|
// fired when item is matching options
|
|
if (data.status === 'open') {
|
|
results[target].list.push({
|
|
host: data.ip,
|
|
port: data.port,
|
|
description: ip.face + ' ' + ip.ip,
|
|
"interface": ip.face
|
|
});
|
|
}
|
|
});
|
|
scanner.on('error', function (err) {
|
|
dfd.reject("error scanning tcp");
|
|
});
|
|
scanner.on('done', function () {
|
|
//console.log('done');
|
|
results[target].done = true;
|
|
checkResults();
|
|
});
|
|
scanner.run();
|
|
})
|
|
} catch (e) {
|
|
console.error('error', e);
|
|
}
|
|
return dfd;
|
|
}
|
|
Module.options = function (query) {
|
|
var dfd = new Deferred();
|
|
var ECIType = types.ECIType;
|
|
var NetworkGroup = 'Network';
|
|
var cis = [
|
|
utils.createCI('allowHalfOpen', ECIType.BOOL, true, {
|
|
group: NetworkGroup
|
|
}),
|
|
utils.createCI('readable', ECIType.BOOL, true, {
|
|
group: NetworkGroup
|
|
}),
|
|
utils.createCI('writable', ECIType.BOOL, true, {
|
|
group: NetworkGroup
|
|
})
|
|
]
|
|
dfd.resolve(cis);
|
|
return dfd;
|
|
|
|
}
|
|
|
|
return Module;
|
|
});
|