mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-11-27 11:57:45 +03:00
Merge pull request #491 from jsastriawan/master
Adding console apf command
This commit is contained in:
commit
2ffea1ffef
@ -306,6 +306,7 @@ function createMeshCore(agent)
|
|||||||
var amtscanner = null;
|
var amtscanner = null;
|
||||||
var nextTunnelIndex = 1;
|
var nextTunnelIndex = 1;
|
||||||
var amtPolicy = null;
|
var amtPolicy = null;
|
||||||
|
var apftunnel = null;
|
||||||
|
|
||||||
// Add to the server event log
|
// Add to the server event log
|
||||||
function MeshServerLog(msg, state) {
|
function MeshServerLog(msg, state) {
|
||||||
@ -1667,7 +1668,7 @@ function createMeshCore(agent)
|
|||||||
var response = null;
|
var response = null;
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case 'help': { // Displays available commands
|
case 'help': { // Displays available commands
|
||||||
response = 'Available commands: help, info, osinfo, args, print, type, dbget, dbset, dbcompact, eval, parseuri, httpget,\r\nwslist, wsconnect, wssend, wsclose, notify, ls, ps, kill, amt, netinfo, location, power, wakeonlan, scanwifi,\r\nscanamt, setdebug, smbios, rawsmbios, toast, lock, users, sendcaps, openurl, amtreset, amtccm, amtacm,\r\namtdeactivate, amtpolicy, getscript, getclip, setclip, log, av, cpuinfo, sysinfo.';
|
response = 'Available commands: help, info, osinfo, args, print, type, dbget, dbset, dbcompact, eval, parseuri, httpget,\r\nwslist, wsconnect, wssend, wsclose, notify, ls, ps, kill, amt, netinfo, location, power, wakeonlan, scanwifi,\r\nscanamt, setdebug, smbios, rawsmbios, toast, lock, users, sendcaps, openurl, amtreset, amtccm, amtacm,\r\namtdeactivate, amtpolicy, getscript, getclip, setclip, log, av, cpuinfo, sysinfo, apf.';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
@ -2290,6 +2291,49 @@ function createMeshCore(agent)
|
|||||||
if (diag) { diag.close(); diag = null; }
|
if (diag) { diag.close(); diag = null; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'apf': {
|
||||||
|
if (meshCoreObj.intelamt!==null) {
|
||||||
|
if (args['_'].length == 1) {
|
||||||
|
if (args['_'][0] == 'on') {
|
||||||
|
response = 'Starting APF tunnel'
|
||||||
|
var apfarg = {
|
||||||
|
mpsurl: mesh.ServerUrl.replace('agent.ashx','apf.ashx'),
|
||||||
|
mpsuser: Buffer.from(mesh.ServerInfo.MeshID,'hex').toString('base64').substring(0,16),
|
||||||
|
mpspass: Buffer.from(mesh.ServerInfo.MeshID,'hex').toString('base64').substring(0,16),
|
||||||
|
mpskeepalive: 60000,
|
||||||
|
clientname: require('os').hostname(),
|
||||||
|
clientaddress: '127.0.0.1',
|
||||||
|
clientuuid: meshCoreObj.intelamt.uuid
|
||||||
|
};
|
||||||
|
var tobj = { debug: false }; //
|
||||||
|
apftunnel= require('apfclient')(tobj,apfarg);
|
||||||
|
try {
|
||||||
|
apftunnel.connect();
|
||||||
|
response += "..success";
|
||||||
|
} catch (e) {
|
||||||
|
response += JSON.stringify(e);
|
||||||
|
}
|
||||||
|
} else if (args['_'][0] == 'off') {
|
||||||
|
response = 'Stopping APF tunnel';
|
||||||
|
try {
|
||||||
|
apftunnel.disconnect();
|
||||||
|
response += "..success";
|
||||||
|
} catch (e) {
|
||||||
|
response += JSON.stringify(e);
|
||||||
|
}
|
||||||
|
apftunnel=null;
|
||||||
|
} else {
|
||||||
|
response = 'Invalid command.\r\nCmd syntax: apf on|off';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
response = 'APF tunnel is '+ (apftunnel == null ? 'off': 'on' );
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
response = 'APF tunnel requires Intel AMT';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default: { // This is an unknown command, return an error message
|
default: { // This is an unknown command, return an error message
|
||||||
response = 'Unknown command \"' + cmd + '\", type \"help\" for list of avaialble commands.';
|
response = 'Unknown command \"' + cmd + '\", type \"help\" for list of avaialble commands.';
|
||||||
break;
|
break;
|
||||||
|
496
agents/modules_meshcore/apfclient.js
Normal file
496
agents/modules_meshcore/apfclient.js
Normal file
@ -0,0 +1,496 @@
|
|||||||
|
/**
|
||||||
|
* @description APF/CIRA Client for duktape
|
||||||
|
* @author Joko Sastriawan
|
||||||
|
* @copyright Intel Corporation 2019
|
||||||
|
* @license Apache-2.0
|
||||||
|
* @version v0.0.1
|
||||||
|
*/
|
||||||
|
|
||||||
|
function CreateAPFClient(parent, args) {
|
||||||
|
var obj = {};
|
||||||
|
obj.parent = parent;
|
||||||
|
obj.args = args;
|
||||||
|
obj.http = require('http');
|
||||||
|
//obj.common = require('common');
|
||||||
|
obj.net = require('net');
|
||||||
|
obj.forwardClient = null;
|
||||||
|
obj.downlinks = {};
|
||||||
|
obj.pfwd_idx = 0;
|
||||||
|
// keep alive timer
|
||||||
|
obj.timer = null;
|
||||||
|
|
||||||
|
// some function copied from common.js
|
||||||
|
function ReadInt(v, p) {
|
||||||
|
return (v.charCodeAt(p) * 0x1000000) + (v.charCodeAt(p + 1) << 16) + (v.charCodeAt(p + 2) << 8) + v.charCodeAt(p + 3);
|
||||||
|
}; // We use "*0x1000000" instead of "<<24" because the shift converts the number to signed int32.
|
||||||
|
|
||||||
|
function IntToStr(v) {
|
||||||
|
return String.fromCharCode((v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF);
|
||||||
|
};
|
||||||
|
|
||||||
|
function hex2rstr(d) {
|
||||||
|
var r = '', m = ('' + d).match(/../g), t;
|
||||||
|
while (t = m.shift()) { r += String.fromCharCode('0x' + t); }
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convert decimal to hex
|
||||||
|
function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); };
|
||||||
|
|
||||||
|
// Convert a raw string to a hex string
|
||||||
|
function rstr2hex(input) {
|
||||||
|
var r = '', i;
|
||||||
|
for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); }
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
|
||||||
|
function d2h(d) {
|
||||||
|
return (d / 256 + 1 / 512).toString(16).substring(2, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buf2hex(input) {
|
||||||
|
var r = '', i;
|
||||||
|
for (i = 0; i < input.length; i++) { r += d2h(input[i]); }
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function Debug(str) {
|
||||||
|
if (obj.parent.debug) {
|
||||||
|
console.log(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// CIRA state
|
||||||
|
var CIRASTATE = {
|
||||||
|
INITIAL: 0,
|
||||||
|
PROTOCOL_VERSION_SENT: 1,
|
||||||
|
AUTH_SERVICE_REQUEST_SENT: 2,
|
||||||
|
AUTH_REQUEST_SENT: 3,
|
||||||
|
PFWD_SERVICE_REQUEST_SENT: 4,
|
||||||
|
GLOBAL_REQUEST_SENT: 5,
|
||||||
|
FAILED: -1
|
||||||
|
}
|
||||||
|
obj.cirastate = CIRASTATE.INITIAL;
|
||||||
|
|
||||||
|
// REDIR state
|
||||||
|
var REDIR_TYPE = {
|
||||||
|
REDIR_UNKNOWN: 0,
|
||||||
|
REDIR_SOL: 1,
|
||||||
|
REDIR_KVM: 2,
|
||||||
|
REDIR_IDER: 3
|
||||||
|
}
|
||||||
|
|
||||||
|
// redirection start command
|
||||||
|
obj.RedirectStartSol = String.fromCharCode(0x10, 0x00, 0x00, 0x00, 0x53, 0x4F, 0x4C, 0x20);
|
||||||
|
obj.RedirectStartKvm = String.fromCharCode(0x10, 0x01, 0x00, 0x00, 0x4b, 0x56, 0x4d, 0x52);
|
||||||
|
obj.RedirectStartIder = String.fromCharCode(0x10, 0x00, 0x00, 0x00, 0x49, 0x44, 0x45, 0x52);
|
||||||
|
|
||||||
|
|
||||||
|
// AMT forwarded port list for non-TLS mode
|
||||||
|
var pfwd_ports = [16992, 623, 16994, 5900];
|
||||||
|
// protocol definitions
|
||||||
|
var APFProtocol = {
|
||||||
|
UNKNOWN: 0,
|
||||||
|
DISCONNECT: 1,
|
||||||
|
SERVICE_REQUEST: 5,
|
||||||
|
SERVICE_ACCEPT: 6,
|
||||||
|
USERAUTH_REQUEST: 50,
|
||||||
|
USERAUTH_FAILURE: 51,
|
||||||
|
USERAUTH_SUCCESS: 52,
|
||||||
|
GLOBAL_REQUEST: 80,
|
||||||
|
REQUEST_SUCCESS: 81,
|
||||||
|
REQUEST_FAILURE: 82,
|
||||||
|
CHANNEL_OPEN: 90,
|
||||||
|
CHANNEL_OPEN_CONFIRMATION: 91,
|
||||||
|
CHANNEL_OPEN_FAILURE: 92,
|
||||||
|
CHANNEL_WINDOW_ADJUST: 93,
|
||||||
|
CHANNEL_DATA: 94,
|
||||||
|
CHANNEL_CLOSE: 97,
|
||||||
|
PROTOCOLVERSION: 192,
|
||||||
|
KEEPALIVE_REQUEST: 208,
|
||||||
|
KEEPALIVE_REPLY: 209,
|
||||||
|
KEEPALIVE_OPTIONS_REQUEST: 210,
|
||||||
|
KEEPALIVE_OPTIONS_REPLY: 211
|
||||||
|
}
|
||||||
|
|
||||||
|
var APFDisconnectCode = {
|
||||||
|
HOST_NOT_ALLOWED_TO_CONNECT: 1,
|
||||||
|
PROTOCOL_ERROR: 2,
|
||||||
|
KEY_EXCHANGE_FAILED: 3,
|
||||||
|
RESERVED: 4,
|
||||||
|
MAC_ERROR: 5,
|
||||||
|
COMPRESSION_ERROR: 6,
|
||||||
|
SERVICE_NOT_AVAILABLE: 7,
|
||||||
|
PROTOCOL_VERSION_NOT_SUPPORTED: 8,
|
||||||
|
HOST_KEY_NOT_VERIFIABLE: 9,
|
||||||
|
CONNECTION_LOST: 10,
|
||||||
|
BY_APPLICATION: 11,
|
||||||
|
TOO_MANY_CONNECTIONS: 12,
|
||||||
|
AUTH_CANCELLED_BY_USER: 13,
|
||||||
|
NO_MORE_AUTH_METHODS_AVAILABLE: 14,
|
||||||
|
INVALID_CREDENTIALS: 15,
|
||||||
|
CONNECTION_TIMED_OUT: 16,
|
||||||
|
BY_POLICY: 17,
|
||||||
|
TEMPORARILY_UNAVAILABLE: 18
|
||||||
|
}
|
||||||
|
|
||||||
|
var APFChannelOpenFailCodes = {
|
||||||
|
ADMINISTRATIVELY_PROHIBITED: 1,
|
||||||
|
CONNECT_FAILED: 2,
|
||||||
|
UNKNOWN_CHANNEL_TYPE: 3,
|
||||||
|
RESOURCE_SHORTAGE: 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
var APFChannelOpenFailureReasonCode = {
|
||||||
|
AdministrativelyProhibited: 1,
|
||||||
|
ConnectFailed: 2,
|
||||||
|
UnknownChannelType: 3,
|
||||||
|
ResourceShortage: 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.onSecureConnect = function onSecureConnect(resp, ws, head) {
|
||||||
|
Debug("APF Secure WebSocket connected.");
|
||||||
|
//console.log(JSON.stringify(resp));
|
||||||
|
obj.forwardClient.tag = { accumulator: [] };
|
||||||
|
obj.forwardClient.ws = ws;
|
||||||
|
obj.forwardClient.ws.on('end', function () {
|
||||||
|
Debug("APF: Connection is closing.");
|
||||||
|
if (obj.timer != null) {
|
||||||
|
clearInterval(obj.timer);
|
||||||
|
obj.timer = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
obj.forwardClient.ws.on('data', function (data) {
|
||||||
|
obj.forwardClient.tag.accumulator += hex2rstr(buf2hex(data));
|
||||||
|
try {
|
||||||
|
var len = 0;
|
||||||
|
do {
|
||||||
|
len = ProcessData(obj.forwardClient);
|
||||||
|
if (len > 0) {
|
||||||
|
obj.forwardClient.tag.accumulator = obj.forwardClient.tag.accumulator.slice(len);
|
||||||
|
}
|
||||||
|
if (obj.cirastate == CIRASTATE.FAILED) {
|
||||||
|
Debug("APF: in a failed state, destroying socket.")
|
||||||
|
obj.forwardClient.ws.end();
|
||||||
|
}
|
||||||
|
} while (len > 0);
|
||||||
|
} catch (e) {
|
||||||
|
Debug(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
obj.forwardClient.ws.on('error', function (e) {
|
||||||
|
Debug("APF: Connection error, ending connecting.");
|
||||||
|
if (obj.timer != null) {
|
||||||
|
clearInterval(obj.timer);
|
||||||
|
obj.timer = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
obj.state = CIRASTATE.INITIAL;
|
||||||
|
SendProtocolVersion(obj.forwardClient.ws, obj.args.clientuuid);
|
||||||
|
SendServiceRequest(obj.forwardClient.ws, 'auth@amt.intel.com');
|
||||||
|
}
|
||||||
|
|
||||||
|
function guidToStr(g) { return g.substring(6, 8) + g.substring(4, 6) + g.substring(2, 4) + g.substring(0, 2) + "-" + g.substring(10, 12) + g.substring(8, 10) + "-" + g.substring(14, 16) + g.substring(12, 14) + "-" + g.substring(16, 20) + "-" + g.substring(20); }
|
||||||
|
function strToGuid(s) {
|
||||||
|
s = s.replace(/-/g, '');
|
||||||
|
var ret = s.substring(6, 8) + s.substring(4, 6) + s.substring(2, 4) + s.substring(0, 2);
|
||||||
|
ret += s.substring(10, 12) + s.substring(8, 10) + s.substring(14, 16) + s.substring(12, 14) + s.substring(16, 20) + s.substring(20);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function binzerostring(len) {
|
||||||
|
var res='';
|
||||||
|
for (var l=0; l< len ; l++) {
|
||||||
|
res+=String.fromCharCode(0 & 0xFF);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function SendProtocolVersion(socket, uuid) {
|
||||||
|
var buuid = strToGuid(uuid);
|
||||||
|
var data = String.fromCharCode(APFProtocol.PROTOCOLVERSION) + '' + IntToStr(1) + IntToStr(0) + IntToStr(0) + hex2rstr(buuid) + binzerostring(64);
|
||||||
|
socket.write(data);
|
||||||
|
Debug("APF: Send protocol version 1 0 " + uuid);
|
||||||
|
obj.cirastate = CIRASTATE.PROTOCOL_VERSION_SENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SendServiceRequest(socket, service) {
|
||||||
|
var data = String.fromCharCode(APFProtocol.SERVICE_REQUEST) + IntToStr(service.length) + service;
|
||||||
|
socket.write(data);
|
||||||
|
Debug("APF: Send service request " + service);
|
||||||
|
if (service == 'auth@amt.intel.com') {
|
||||||
|
obj.cirastate = CIRASTATE.AUTH_SERVICE_REQUEST_SENT;
|
||||||
|
} else if (service == 'pfwd@amt.intel.com') {
|
||||||
|
obj.cirastate = CIRASTATE.PFWD_SERVICE_REQUEST_SENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function SendUserAuthRequest(socket, user, pass) {
|
||||||
|
var service = "pfwd@amt.intel.com";
|
||||||
|
var data = String.fromCharCode(APFProtocol.USERAUTH_REQUEST) + IntToStr(user.length) + user + IntToStr(service.length) + service;
|
||||||
|
//password auth
|
||||||
|
data += IntToStr(8) + 'password';
|
||||||
|
data += binzerostring(1) + IntToStr(pass.length) + pass;
|
||||||
|
socket.write(data);
|
||||||
|
Debug("APF: Send username password authentication to MPS");
|
||||||
|
obj.cirastate = CIRASTATE.AUTH_REQUEST_SENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SendGlobalRequestPfwd(socket, amthostname, amtport) {
|
||||||
|
var tcpipfwd = 'tcpip-forward';
|
||||||
|
var data = String.fromCharCode(APFProtocol.GLOBAL_REQUEST) + IntToStr(tcpipfwd.length) + tcpipfwd + binzerostring(1, 1);
|
||||||
|
data += IntToStr(amthostname.length) + amthostname + IntToStr(amtport);
|
||||||
|
socket.write(data);
|
||||||
|
Debug("APF: Send tcpip-forward " + amthostname + ":" + amtport);
|
||||||
|
obj.cirastate = CIRASTATE.GLOBAL_REQUEST_SENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SendKeepAliveRequest(socket) {
|
||||||
|
var data = String.fromCharCode(APFProtocol.KEEPALIVE_REQUEST) + IntToStr(255);
|
||||||
|
socket.write(data);
|
||||||
|
Debug("APF: Send keepalive request");
|
||||||
|
}
|
||||||
|
|
||||||
|
function SendKeepAliveReply(socket, cookie) {
|
||||||
|
var data = String.fromCharCode(APFProtocol.KEEPALIVE_REPLY) + IntToStr(cookie);
|
||||||
|
socket.write(data);
|
||||||
|
Debug("APF: Send keepalive reply");
|
||||||
|
}
|
||||||
|
|
||||||
|
function ProcessData(socket) {
|
||||||
|
var cmd = socket.tag.accumulator.charCodeAt(0);
|
||||||
|
var len = socket.tag.accumulator.length;
|
||||||
|
var data = socket.tag.accumulator;
|
||||||
|
if (len == 0) { return 0; }
|
||||||
|
// respond to MPS according to obj.cirastate
|
||||||
|
switch (cmd) {
|
||||||
|
case APFProtocol.SERVICE_ACCEPT: {
|
||||||
|
var slen = ReadInt(data, 1);
|
||||||
|
var service = data.substring(5, 6 + slen);
|
||||||
|
Debug("APF: Service request to " + service + " accepted.");
|
||||||
|
if (service == 'auth@amt.intel.com') {
|
||||||
|
if (obj.cirastate >= CIRASTATE.AUTH_SERVICE_REQUEST_SENT) {
|
||||||
|
SendUserAuthRequest(socket.ws, obj.args.mpsuser, obj.args.mpspass);
|
||||||
|
}
|
||||||
|
} else if (service == 'pfwd@amt.intel.com') {
|
||||||
|
if (obj.cirastate >= CIRASTATE.PFWD_SERVICE_REQUEST_SENT) {
|
||||||
|
SendGlobalRequestPfwd(socket.ws, obj.args.clientname, pfwd_ports[obj.pfwd_idx++]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 5 + slen;
|
||||||
|
}
|
||||||
|
case APFProtocol.REQUEST_SUCCESS: {
|
||||||
|
if (len >= 5) {
|
||||||
|
var port = ReadInt(data, 1);
|
||||||
|
Debug("APF: Request to port forward " + port + " successful.");
|
||||||
|
// iterate to pending port forward request
|
||||||
|
if (obj.pfwd_idx < pfwd_ports.length) {
|
||||||
|
SendGlobalRequestPfwd(socket.ws, obj.args.clientname, pfwd_ports[obj.pfwd_idx++]);
|
||||||
|
} else {
|
||||||
|
// no more port forward, now setup timer to send keep alive
|
||||||
|
Debug("APF: Start keep alive for every " + obj.args.mpskeepalive + " ms.");
|
||||||
|
obj.timer = setInterval(function () {
|
||||||
|
SendKeepAliveRequest(obj.forwardClient.ws);
|
||||||
|
}, obj.args.mpskeepalive);//
|
||||||
|
}
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
Debug("APF: Request successful.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
case APFProtocol.USERAUTH_SUCCESS: {
|
||||||
|
Debug("APF: User Authentication successful");
|
||||||
|
// Send Pfwd service request
|
||||||
|
SendServiceRequest(socket.ws, 'pfwd@amt.intel.com');
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
case APFProtocol.USERAUTH_FAILURE: {
|
||||||
|
Debug("APF: User Authentication failed");
|
||||||
|
obj.cirastate = CIRASTATE.FAILED;
|
||||||
|
return 14;
|
||||||
|
}
|
||||||
|
case APFProtocol.KEEPALIVE_REQUEST: {
|
||||||
|
Debug("APF: Keep Alive Request with cookie: " + ReadInt(data, 1));
|
||||||
|
SendKeepAliveReply(socket.ws, ReadInt(data, 1));
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
case APFProtocol.KEEPALIVE_REPLY: {
|
||||||
|
Debug("APF: Keep Alive Reply with cookie: " + ReadInt(data, 1));
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
// Channel management
|
||||||
|
case APFProtocol.CHANNEL_OPEN: {
|
||||||
|
//parse CHANNEL OPEN request
|
||||||
|
var p_res = parseChannelOpen(data);
|
||||||
|
Debug("APF: CHANNEL_OPEN request: " + JSON.stringify(p_res));
|
||||||
|
// Check if target port is in pfwd_ports
|
||||||
|
if (pfwd_ports.indexOf(p_res.target_port) >= 0) {
|
||||||
|
// connect socket to that port
|
||||||
|
obj.downlinks[p_res.sender_chan] = obj.net.createConnection({ host: obj.args.clientaddress, port: p_res.target_port }, function () {
|
||||||
|
obj.downlinks[p_res.sender_chan].setEncoding('binary');//assume everything is binary, not interpreting
|
||||||
|
SendChannelOpenConfirm(socket.ws, p_res);
|
||||||
|
});
|
||||||
|
|
||||||
|
obj.downlinks[p_res.sender_chan].on('data', function (ddata) {
|
||||||
|
//Relay data to fordwardclient
|
||||||
|
SendChannelData(socket.ws, p_res.sender_chan, ddata.length, ddata);
|
||||||
|
});
|
||||||
|
|
||||||
|
obj.downlinks[p_res.sender_chan].on('error', function (e) {
|
||||||
|
Debug("Downlink connection error: " + e);
|
||||||
|
});
|
||||||
|
|
||||||
|
obj.downlinks[p_res.sender_chan].on('end', function () {
|
||||||
|
if (obj.downlinks[p_res.sender_chan]) {
|
||||||
|
try {
|
||||||
|
SendChannelClose(socket.ws, p_res.sender_chan);
|
||||||
|
delete obj.downlinks[p_res.sender_chan];
|
||||||
|
} catch (e) {
|
||||||
|
Debug("Downlink connection exception: " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
SendChannelOpenFailure(socket.ws, p_res);
|
||||||
|
}
|
||||||
|
return p_res.len;
|
||||||
|
}
|
||||||
|
case APFProtocol.CHANNEL_OPEN_CONFIRMATION: {
|
||||||
|
Debug("APF: CHANNEL_OPEN_CONFIRMATION");
|
||||||
|
return 17;
|
||||||
|
}
|
||||||
|
case APFProtocol.CHANNEL_CLOSE: {
|
||||||
|
var rcpt_chan = ReadInt(data, 1);
|
||||||
|
Debug("APF: CHANNEL_CLOSE: " + rcpt_chan);
|
||||||
|
SendChannelClose(socket.ws, rcpt_chan);
|
||||||
|
try {
|
||||||
|
obj.downlinks[rcpt_chan].end();
|
||||||
|
delete obj.downlinks[rcpt_chan];
|
||||||
|
} catch (e) { }
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
case APFProtocol.CHANNEL_DATA: {
|
||||||
|
Debug("APF: CHANNEL_DATA: " + JSON.stringify(rstr2hex(data)));
|
||||||
|
var rcpt_chan = ReadInt(data, 1);
|
||||||
|
var chan_data_len = ReadInt(data, 5);
|
||||||
|
var chan_data = data.substring(9, 9 + chan_data_len);
|
||||||
|
if (obj.downlinks[rcpt_chan]) {
|
||||||
|
try {
|
||||||
|
obj.downlinks[rcpt_chan].write(chan_data, 'binary', function () {
|
||||||
|
Debug("Write completed.");
|
||||||
|
SendChannelWindowAdjust(socket.ws, rcpt_chan, chan_data_len);//I have full window capacity now
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
Debug("Cannot forward data to downlink socket.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 9 + chan_data_len;
|
||||||
|
}
|
||||||
|
case APFProtocol.CHANNEL_WINDOW_ADJUST: {
|
||||||
|
Debug("APF: CHANNEL_WINDOW_ADJUST ");
|
||||||
|
return 9;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
Debug("CMD: " + cmd + " is not implemented.");
|
||||||
|
obj.cirastate = CIRASTATE.FAILED;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseChannelOpen(data) {
|
||||||
|
var result = {
|
||||||
|
len: 0, //to be filled later
|
||||||
|
cmd: APFProtocol.CHANNEL_OPEN,
|
||||||
|
chan_type: "", //to be filled later
|
||||||
|
sender_chan: 0, //to be filled later
|
||||||
|
window_size: 0, //to be filled later
|
||||||
|
target_address: "", //to be filled later
|
||||||
|
target_port: 0, //to be filled later
|
||||||
|
origin_address: "", //to be filled later
|
||||||
|
origin_port: 0, //to be filled later
|
||||||
|
};
|
||||||
|
var chan_type_slen = ReadInt(data, 1);
|
||||||
|
result.chan_type = data.substring(5, 5 + chan_type_slen);
|
||||||
|
result.sender_chan = ReadInt(data, 5 + chan_type_slen);
|
||||||
|
result.window_size = ReadInt(data, 9 + chan_type_slen);
|
||||||
|
var c_len = ReadInt(data, 17 + chan_type_slen);
|
||||||
|
result.target_address = data.substring(21 + chan_type_slen, 21 + chan_type_slen + c_len);
|
||||||
|
result.target_port = ReadInt(data, 21 + chan_type_slen + c_len);
|
||||||
|
var o_len = ReadInt(data, 25 + chan_type_slen + c_len);
|
||||||
|
result.origin_address = data.substring(29 + chan_type_slen + c_len, 29 + chan_type_slen + c_len + o_len);
|
||||||
|
result.origin_port = ReadInt(data, 29 + chan_type_slen + c_len + o_len);
|
||||||
|
result.len = 33 + chan_type_slen + c_len + o_len;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
function SendChannelOpenFailure(socket, chan_data) {
|
||||||
|
var data = String.fromCharCode(APFProtocol.CHANNEL_OPEN_FAILURE) + IntToStr(chan_data.sender_chan)
|
||||||
|
+ IntToStr(2) + IntToStr(0) + IntToStr(0);
|
||||||
|
socket.write(data);
|
||||||
|
Debug("APF: Send ChannelOpenFailure");
|
||||||
|
}
|
||||||
|
function SendChannelOpenConfirm(socket, chan_data) {
|
||||||
|
var data = String.fromCharCode(APFProtocol.CHANNEL_OPEN_CONFIRMATION) + IntToStr(chan_data.sender_chan)
|
||||||
|
+ IntToStr(chan_data.sender_chan) + IntToStr(chan_data.window_size) + IntToStr(0xFFFFFFFF);
|
||||||
|
socket.write(data);
|
||||||
|
Debug("APF: Send ChannelOpenConfirmation");
|
||||||
|
}
|
||||||
|
|
||||||
|
function SendChannelWindowAdjust(socket, chan, size) {
|
||||||
|
var data = String.fromCharCode(APFProtocol.CHANNEL_WINDOW_ADJUST) + IntToStr(chan) + IntToStr(size);
|
||||||
|
socket.write(data);
|
||||||
|
Debug("APF: Send ChannelWindowAdjust: " + rstr2hex(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
function SendChannelData(socket, chan, len, data) {
|
||||||
|
var buf = String.fromCharCode(APFProtocol.CHANNEL_DATA) + IntToStr(chan) + IntToStr(len) + data;
|
||||||
|
socket.write(Buffer.from(buf, 'binary'));
|
||||||
|
Debug("APF: Send ChannelData: " + rstr2hex(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
function SendChannelClose(socket, chan) {
|
||||||
|
var buf = String.fromCharCode(APFProtocol.CHANNEL_CLOSE) + IntToStr(chan);
|
||||||
|
socket.write(Buffer.from(buf, 'binary'));
|
||||||
|
Debug("APF: Send ChannelClose: " + rstr2hex(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.connect = function () {
|
||||||
|
if (obj.forwardClient != null) {
|
||||||
|
try {
|
||||||
|
obj.forwardClient.ws.end();
|
||||||
|
} catch (e) {
|
||||||
|
Debug(e);
|
||||||
|
}
|
||||||
|
//obj.forwardClient = null;
|
||||||
|
}
|
||||||
|
obj.cirastate = CIRASTATE.INITIAL;
|
||||||
|
obj.pfwd_idx = 0;
|
||||||
|
|
||||||
|
//obj.forwardClient = new obj.ws(obj.args.mpsurl, obj.tlsoptions);
|
||||||
|
//obj.forwardClient.on("open", obj.onSecureConnect);
|
||||||
|
|
||||||
|
|
||||||
|
var wsoptions = obj.http.parseUri(obj.args.mpsurl);
|
||||||
|
wsoptions.rejectUnauthorized = 0;
|
||||||
|
obj.forwardClient = obj.http.request(wsoptions);
|
||||||
|
obj.forwardClient.upgrade = obj.onSecureConnect;
|
||||||
|
obj.forwardClient.end(); // end request, trigger completion of HTTP request
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.disconnect = function () {
|
||||||
|
try {
|
||||||
|
obj.forwardClient.ws.end();
|
||||||
|
} catch (e) {
|
||||||
|
Debug(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = CreateAPFClient;
|
19
apfserver.js
19
apfserver.js
@ -133,16 +133,19 @@ module.exports.CreateApfServer = function (parent, db, args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
obj.onConnection = function(socket) {
|
obj.onConnection = function(socket) {
|
||||||
console.log("Here");
|
|
||||||
connectionCount++;
|
connectionCount++;
|
||||||
// treat APS over WS like tlsoffload APF
|
// treat APS over WS like tlsoffload APF
|
||||||
socket.tag = { first: true, clientCert: null, accumulator: "", activetunnels: 0, boundPorts: [], socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 };
|
socket.tag = { first: true, clientCert: null, accumulator: "", activetunnels: 0, boundPorts: [], socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 };
|
||||||
parent.debug('apf', "New APF connection");
|
parent.debug('apf', "New APF connection");
|
||||||
|
parent.debug('apf',"WS Extensions:"+socket.extensions);
|
||||||
|
parent.debug('apf',"WS Binary type:"+socket.binaryType);
|
||||||
|
|
||||||
|
socket._socket.on('data', function(chunk) { console.log(chunk.toString('hex'))});
|
||||||
|
|
||||||
// Setup the APF keep alive timer
|
// Setup the APF keep alive timer
|
||||||
// Websocket does not have timout
|
// Websocket does not have timout
|
||||||
// socket.setTimeout(MAX_IDLE);
|
// socket.setTimeout(MAX_IDLE);
|
||||||
//socket.on("timeout", () => { ciraTimeoutCount++; parent.debug('mps', "APF timeout, disconnecting."); try { socket.terminate(); } catch (e) { } });
|
//socket.on("timeout", () => { ciraTimeoutCount++; parent.debug('apf', "APF timeout, disconnecting."); try { socket.terminate(); } catch (e) { } });
|
||||||
//use on message instead because of websocket
|
//use on message instead because of websocket
|
||||||
socket.on("message", function (data) {
|
socket.on("message", function (data) {
|
||||||
// use the same debug flag like APF
|
// use the same debug flag like APF
|
||||||
@ -206,13 +209,13 @@ module.exports.CreateApfServer = function (parent, db, args) {
|
|||||||
parent.debug('apfcmd', 'USERAUTH_REQUEST user=' + username + ', service=' + serviceName + ', method=' + methodName + ', password=' + password);
|
parent.debug('apfcmd', 'USERAUTH_REQUEST user=' + username + ', service=' + serviceName + ', method=' + methodName + ', password=' + password);
|
||||||
|
|
||||||
// Check the APF password
|
// Check the APF password
|
||||||
if ((args.mpspass != null) && (password != args.mpspass)) { incorrectPasswordCount++; parent.debug('mps', 'Incorrect password', username, password); SendUserAuthFail(socket); return -1; }
|
if ((args.mpspass != null) && (password != args.mpspass)) { incorrectPasswordCount++; parent.debug('apf', 'Incorrect password', username, password); SendUserAuthFail(socket); return -1; }
|
||||||
|
|
||||||
// Check the APF username, which should be the start of the MeshID.
|
// Check the APF username, which should be the start of the MeshID.
|
||||||
if (usernameLen != 16) { badUserNameLengthCount++; parent.debug('mps', 'Username length not 16', username, password); SendUserAuthFail(socket); return -1; }
|
if (usernameLen != 16) { badUserNameLengthCount++; parent.debug('apf', 'Username length not 16', username, password); SendUserAuthFail(socket); return -1; }
|
||||||
var meshIdStart = '/' + username, mesh = null;
|
var meshIdStart = '/' + username, mesh = null;
|
||||||
if (obj.parent.webserver.meshes) { for (var i in obj.parent.webserver.meshes) { if (obj.parent.webserver.meshes[i]._id.replace(/\@/g, 'X').replace(/\$/g, 'X').indexOf(meshIdStart) > 0) { mesh = obj.parent.webserver.meshes[i]; break; } } }
|
if (obj.parent.webserver.meshes) { for (var i in obj.parent.webserver.meshes) { if (obj.parent.webserver.meshes[i]._id.replace(/\@/g, 'X').replace(/\$/g, 'X').indexOf(meshIdStart) > 0) { mesh = obj.parent.webserver.meshes[i]; break; } } }
|
||||||
if (mesh == null) { meshNotFoundCount++; parent.debug('mps', 'Mesh not found', username, password); SendUserAuthFail(socket); return -1; }
|
if (mesh == null) { meshNotFoundCount++; parent.debug('apf', 'Mesh not found', username, password); SendUserAuthFail(socket); return -1; }
|
||||||
|
|
||||||
// If this is a agent-less mesh, use the device guid 3 times as ID.
|
// If this is a agent-less mesh, use the device guid 3 times as ID.
|
||||||
if (mesh.mtype == 1) {
|
if (mesh.mtype == 1) {
|
||||||
@ -547,14 +550,14 @@ module.exports.CreateApfServer = function (parent, db, args) {
|
|||||||
|
|
||||||
socket.addListener("close", function () {
|
socket.addListener("close", function () {
|
||||||
socketClosedCount++;
|
socketClosedCount++;
|
||||||
parent.debug('mps', 'APF connection closed');
|
parent.debug('apf', 'APF connection closed');
|
||||||
try { delete obj.apfConnections[socket.tag.nodeid]; } catch (e) { }
|
try { delete obj.apfConnections[socket.tag.nodeid]; } catch (e) { }
|
||||||
obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 8);
|
obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 8);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.addListener("error", function () {
|
socket.addListener("error", function (e) {
|
||||||
socketErrorCount++;
|
socketErrorCount++;
|
||||||
//console.log("APF Error: " + socket.remoteAddress);
|
console.log("APF Error: " + e);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user