mono/packages/vfs/ref-server/protocols/Tcp.ts

451 lines
16 KiB
TypeScript

import { Protocol } from './Protocol';
import * as utils from '../shared/utils';
import { mixin } from '@xblox/core/objects';
import { remove } from '@xblox/core/arrays';
import * as _ from 'lodash';
import * as net from 'net';
import { DeviceInfo, EVENTS } from './../shared/types';
import { Connection } from '../model/Connection'
import * as debug from '../log';
import { IProtocolHandler, ConnectionManager } from '../manager/ConnectionManager';
import * as os from 'os';
import { IDeviceCommand, ERRORS } from '../types';
const ipaddr = require('ipaddr.js');
const evilscan = require('evilscan');
const _debug = false;
export class Tcp extends Protocol {
_name = 'tcp';
_socket: net.Socket;
_server: net.Server;
_clients: Connection[] = [];
_creatingServer: boolean;
_handler: ConnectionManager;
connections: any[] = [];
_handleSocketEmits(socket: any) {
/*
var self = this;
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', (conn) =>{
this.socket = conn;
this.clients.push(conn);
conn.on('data', function (data) {
const 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 ');
});
});
*/
}
makeServer() {
if (this._creatingServer) {
return;
}
this._creatingServer = true;
/*
try {
var options = this.info();
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;
*/
}
onConnect(evt: any) {
var connection = evt.connection;
if (!connection) {
return;
}
var cOptions = connection.options;
var options = this.device;
if (!this.connections) {
this.connections = [];
}
if (connection.id === this.connection.id) {
return;
}
var exists = false;//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);
}
}
}
onDisconnect(evt: any) {
var connection = evt.connection;
if (!connection) {
return;
}
remove(this.connections, connection);
}
connect() {
const isServer = this.isServer();
if (isServer) {
return this.makeServer();
}
const options = this.device;
const port = options.port;
const host = options.host;
this._clients = [];
this._socket = new net.Socket(mixin({
allowHalfOpen: true,
writable: true,
readable: true
}, {
}));
const self = this;
this.isDebug() && console.log('TCP-Client->Connect to ' + options.host + ' : ' + options.port + ' @ ' + this.name());
this._socket.connect(parseInt(port), host, () => {
this.connected = true;
this.handler().onConnected(this, new Buffer(this.device.name));
});
this._socket.setNoDelay(true);
this._socket.setKeepAlive(true, 0);
this.listen(this._socket, this.handler());
// (this._socket as any).owner = this;
// this._setupEventListeners(this._socket, this.delegate);
return this;
}
listen(socket: net.Socket, handler:ConnectionManager) {
var self = this;
//var connection = this.connection;
/*
socket.handle = function (data) {
handler.onHandle(connection, data);
};*/
const toArrayBuffer = (buffer) => {
var ab = new ArrayBuffer(buffer.length);
var view = new Uint8Array(ab);
for (var i = 0; i < buffer.length; ++i) {
view[i] = buffer[i];
}
return ab;
}
socket.on('data', (data) => {
handler.onData(this, data);
});
if (!handler) {
debug.stack('have no handler');
}
/*
if (!connection) {
console.error('have no connection');
debug.stack('have no connect');
}
*/
socket.on('error', (exception) => {
this.isDebug() && debug.error('socket error : ', exception);
handler.onError(this, exception);
});
socket.on('timeout', () => {
handler.onTimeout(this);
});
// Add a 'close' event handler for the client client
socket.on('close', (data) => {
handler.onClose(this);
});
}
broadCastMessage(eventName, _data) {
const data = _data;
const connections = this.connections;
this.connections.forEach((connection) => {
if (!connection) {
return;
}
if (connection._destroyed) {
remove(connections, Connection);
}
if (connection.client) {
connection.client.delegate.onData(connection, data.toString(), data);
} else {
debug.error('not at a real connection', connection);
}
});
}
send(data: IDeviceCommand) {
return new Promise<any>((resolve, reject) => {
const cmd = data.command;
if (cmd == null) {
debug.error('TCP : invalid command');
return;
}
const intArray = utils.bufferFromDecString(cmd);
const buffer = new Buffer(intArray);
if (this.isServer()) {
this.broadCastMessage(EVENTS.ON_DEVICE_MESSAGE, buffer);
} else {
debug.log('send device command ' + buffer.toString());
if (this._socket.writable) {
try {
if (this._socket.write(buffer)) {
resolve();
} else {
reject('send failed');
};
} catch (e) {
debug.error('error send TCP command', e);
}
} else {
debug.error('Socket not writeable ' + this.debug());
const e = {
code: ERRORS.EHOSTUNREACH,
errno: 16,
message: 'Socket not writeable'
} as NodeJS.ErrnoException
this.handler().onError(this, e);
}
}
});
}
debug = () => `Protocol ${this.name()} @ ${this.device.host} : ${this.device.port} | Id : ${this.id}`;
destroy() {
if (this.isDestroyed()) {
debug.warn('already destroyed: ' + this.debug());
return;
}
if (!this.isServer()) {
this._socket.end();
} else {
this._server.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;
*/
super.destroy();
}
close() {
if (!this.isServer()) {
this._socket.end();
} else {
this._server.close();
this.destroy();
}
}
static ls(query: any = {}) {
return new Promise((resolve, reject) => {
try {
const ifaces = os.networkInterfaces();
const ips = [];
Object.keys(ifaces).forEach(function (ifname) {
let 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;
});
});
let 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);
});
resolve(result);
}
}
_.each(ips, function (ip) {
const range = ipaddr.parse(ip.ip);
const octets = range.octets;
octets[octets.length - 1] = 0;
const target = octets.join('.') + '-254';
const testPorts = [query.ports || '80'];
const options = {
target: target,
port: testPorts.join(','),
status: 'Open', // Timeout, Refused, Open, Unreachable
timeout: 500,
banner: false
}
const 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) {
reject("error scanning tcp");
});
scanner.on('done', function () {
//console.log('done');
results[target].done = true;
checkResults();
});
scanner.run();
});
} catch (e) {
reject(e);
}
});
}
}