Added support for MeshCmd routing.

This commit is contained in:
Ylian Saint-Hilaire 2017-10-31 16:19:58 -07:00
parent 951e6236f9
commit e75adafb05
21 changed files with 343 additions and 70 deletions

BIN
agents/MeshConsole.exe Normal file

Binary file not shown.

BIN
agents/MeshConsole64.exe Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

170
agents/meshcmd.js Normal file
View File

@ -0,0 +1,170 @@
var fs = require('fs');
var os = require('os');
var net = require('net');
var http = require('http');
var dgram = require('dgram');
var httpHeaders = require('http-headers');
var tcpserver = null;
var broadcastSockets = {};
var multicastSockets = {};
var discoveryInterval = null;
var membershipIPv4 = '239.255.255.235';
var membershipIPv6 = 'FF02:0:0:0:0:0:0:FE';
/*
// Route Settings
var settings = {
action: 'route',
localPort: 1234,
remoteName: 'AmtMachine7',
remoteNodeId: 'node//nmiPnDhT3vHKu$zg296YC5RjK53Trgh3Cimx3K8GVrFh$xch0UAAett2rbJpeddc',
remotePort: 3389,
username: 'a',
password: 'a',
serverUrl: 'wss://devbox.mesh.meshcentral.com:443/meshrelay.ashx',
serverId: 'D99362D5ED8BAEA8BF9E743B34B242256370C460FD66CB62373C6CFCB204D6D707403E396CF0EF6DC2B3A42F735135FD', // SHA384 of server HTTPS public key
serverHttpsHash: 'D9DE9E27A229B5355708A3672FB23237CC994A680B3570D242A91E36B4AE5BC9', // SHA256 of server HTTPS certificate
debugLevel: 0
}
*/
// Check the server certificate fingerprint
function onVerifyServer(clientName, certs) {
try { for (var i in certs) { if (certs[i].fingerprint.replace(/:/g, '') == settings.serverHttpsHash) { return; } } } catch (e) { }
if (serverhash != null) { console.log('Error: Failed to verify server certificate.'); return false; }
}
// Print a debug message
function debug(level, message) { if ((settings.debugLevel != null) && (settings.debugLevel >= level)) { console.log(message); } }
// Start the router, start by listening to the local port
function run(argv) {
console.log('MeshCentral Command v1.0');
var actionpath = 'meshaction.txt';
if (argv.length >= 2) { actionpath = argv[1]; }
// Load the action file
var actionfile = null;
try { actionfile = fs.readFileSync(actionpath); } catch (e) { }
if (actionfile == null) { console.log('Unable to load \"' + actionpath + '\". Create this file or specify the location as the first argument.'); process.exit(1); }
try { settings = JSON.parse(actionfile); } catch (e) { console.log(actionpath, e); process.exit(1); }
// Validate meshaction.txt
if (settings.action == null) { console.log('No \"action\" specified.'); process.exit(1); }
settings.action = settings.action.toLowerCase();
if (settings.action == 'route') {
if ((settings.localPort == null) || (typeof settings.localPort != 'number') || (settings.localPort < 0) || (settings.localPort > 65535)) { console.log('No or invalid \"localPort\" specified.'); process.exit(1); }
if ((settings.remoteNodeId == null) || (typeof settings.remoteNodeId != 'string')) { console.log('No or invalid \"remoteNodeId\" specified.'); process.exit(1); }
if ((settings.username == null) || (typeof settings.username != 'string')) { console.log('No or invalid \"username\" specified.'); process.exit(1); }
if ((settings.password == null) || (typeof settings.password != 'string') || (settings.password == '')) { console.log('No or invalid \"password\" specified.'); process.exit(1); }
if ((settings.serverId == null) || (typeof settings.serverId != 'string') || (settings.serverId.length != 96)) { console.log('No or invalid \"serverId\" specified.'); process.exit(1); }
if ((settings.serverHttpsHash == null) || (typeof settings.serverHttpsHash != 'string') || (settings.serverHttpsHash.length != 96)) { console.log('No or invalid \"serverHttpsHash\" specified.'); process.exit(1); }
if ((settings.remotePort == null) || (typeof settings.remotePort != 'number') || (settings.remotePort < 0) || (settings.remotePort > 65535)) { console.log('No or invalid \"remotePort\" specified.'); process.exit(1); }
} else {
console.log('Invalid \"action\" specified.'); process.exit(1);
}
debug(1, "Settings: " + JSON.stringify(settings));
if (settings.serverUrl != null) { startRouter(); } else { discoverMeshServer(); }
}
// Starts the router
function startRouter() {
tcpserver = net.createServer(OnTcpClientConnected);
tcpserver.on('error', function (err) { console.log(err); process.exit(0); });
tcpserver.listen(settings.localPort, function () {
// We started listening.
if (settings.remoteName == null) {
console.log('Redirecting local port ' + settings.localPort + ' to remote port ' + settings.remotePort + '.');
} else {
console.log('Redirecting local port ' + settings.localPort + ' to ' + settings.remoteName + ':' + settings.remotePort + '.');
}
console.log('Press ctrl-c to terminal.');
// If settings has a "cmd", run it now.
//process.exec("notepad.exe");
});
}
// Called when a TCP connect is received on the local port. Launch a tunnel.
function OnTcpClientConnected(c) {
try {
// 'connection' listener
debug(1, 'Client connected');
c.on('end', function () { disconnectTunnel(this, this.websocket, 'Client closed'); });
c.pause();
try {
options = http.parseUri(settings.serverUrl + '?user=' + settings.username + '&pass=' + settings.password + '&nodeid=' + settings.remoteNodeId + '&tcpport=' + settings.remotePort);
} catch (e) { console.log('Unable to parse \"serverUrl\".'); process.exit(1); }
options.checkServerIdentity = onVerifyServer;
c.websocket = http.request(options);
c.websocket.tcp = c;
c.websocket.tunneling = false;
c.websocket.upgrade = OnWebSocket;
c.websocket.on('error', function (msg) { console.log(msg); });
c.websocket.end();
} catch (e) { debug(2, e); }
}
// Disconnect both TCP & WebSocket connections and display a message.
function disconnectTunnel(tcp, ws, msg) {
if (ws != null) { try { ws.end(); } catch (e) { debug(2, e); } }
if (tcp != null) { try { tcp.end(); } catch (e) { debug(2, e); } }
debug(1, 'Tunnel disconnected: ' + msg);
}
// Called when the web socket gets connected
function OnWebSocket(msg, s, head) {
debug(1, 'Websocket connected');
s.on('data', function (msg) {
if (this.parent.tunneling == false) {
msg = msg.toString();
if (msg == 'c') {
this.parent.tunneling = true; this.pipe(this.parent.tcp); this.parent.tcp.pipe(this); debug(1, 'Tunnel active');
} else if ((msg.length > 6) && (msg.substring(0, 6) == 'error:')) {
console.log(msg.substring(6));
disconnectTunnel(this.tcp, this, msg.substring(6));
}
}
});
s.on('error', function (msg) { disconnectTunnel(this.tcp, this, 'Websocket error'); });
s.on('close', function (msg) { disconnectTunnel(this.tcp, this, 'Websocket closed'); });
s.parent = this;
}
// Try to discover the location of the mesh server
function discoverMeshServer() { console.log('Looking for server...'); discoveryInterval = setInterval(discoverMeshServerOnce, 5000); discoverMeshServerOnce(); }
// Try to discover the location of the mesh server only once
function discoverMeshServerOnce() {
var interfaces = os.networkInterfaces();
for (var adapter in interfaces) {
if (interfaces.hasOwnProperty(adapter)) {
for (var i = 0 ; i < interfaces[adapter].length; ++i) {
var addr = interfaces[adapter][i];
multicastSockets[i] = dgram.createSocket({ type: (addr.family == "IPv4" ? "udp4" : "udp6") });
multicastSockets[i].bind({ address: addr.address, exclusive: false });
if (addr.family == "IPv4") {
multicastSockets[i].addMembership(membershipIPv4);
//multicastSockets[i].setMulticastLoopback(true);
multicastSockets[i].once('message', OnMulticastMessage);
multicastSockets[i].send(settings.serverId, 16989, membershipIPv4);
}
}
}
}
}
// Called when a multicast packet is received
function OnMulticastMessage(msg, rinfo) {
var m = msg.toString().split('|');
if ((m.length == 3) && (m[0] == 'MeshCentral2') && (m[1] == settings.serverId)) {
settings.serverUrl = m[2].replace('%s', rinfo.address).replace('/agent.ashx', '/meshrelay.ashx');
console.log('Found server at ' + settings.serverUrl + '.');
if (discoveryInterval != null) { clearInterval(discoveryInterval); discoveryInterval = null; }
startRouter();
}
}
try { run(process.argv); } catch (e) { /*console.log(e);*/ }

View File

@ -59,7 +59,7 @@ CheckInstallAgent() {
DownloadAgent $url $meshid $machineid
fi
else
echo "MeshID is not correct, must be 64 HEX characters long."
echo "MeshID is not correct, must be 64 characters long."
fi
else
echo "URI and/or MeshID have not been specified, must be passed in as arguments."
@ -78,7 +78,7 @@ DownloadAgent() {
wget $url/meshagents?id=$machineid -q --no-check-certificate -O /usr/local/mesh/meshagent
if [ $? -eq 0 ]
then
echo "Mesh agent download."
echo "Mesh agent downloaded."
# TODO: We could check the meshagent sha256 hash, but best to authenticate the server.
chmod 755 /usr/local/mesh/meshagent
wget $url/meshsettings?id=$meshid -q --no-check-certificate -O /usr/local/mesh/meshagent.msh
@ -97,6 +97,7 @@ DownloadAgent() {
ln -s /usr/local/mesh/meshagent /etc/rc3.d/S20mesh
ln -s /usr/local/mesh/meshagent /etc/rc5.d/S20mesh
fi
echo "Mesh agent started."
else
echo "Unable to download mesh settings at: $url/meshsettings?id=$meshid."
fi

View File

@ -131,7 +131,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
if (len == agentUpdateBlockSize) { obj.ws.send(obj.agentUpdate.buf); } else { obj.ws.send(obj.agentUpdate.buf.slice(0, len + 4)); } // Command 14, mesh agent next data block
if (len < agentUpdateBlockSize) {
console.log("Agent update sent");
//console.log("Agent update sent");
obj.send(obj.common.ShortToStr(13) + obj.common.ShortToStr(0) + obj.common.hex2rstr(obj.agentExeInfo.hash)); // Command 13, end mesh agent download, send agent SHA384 hash
obj.fs.close(obj.agentUpdate.fd);
obj.agentUpdate = null;

View File

@ -4,7 +4,7 @@
* @version v0.0.1
*/
module.exports.CreateMeshRelay = function (parent, ws, req) {
module.exports.CreateMeshRelay = function (parent, ws, req, domain) {
var obj = {};
obj.ws = ws;
obj.req = req;
@ -12,6 +12,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req) {
obj.parent = parent;
obj.id = req.query.id;
obj.remoteaddr = obj.ws._socket.remoteAddress;
obj.domain = domain;
if (obj.remoteaddr.startsWith('::ffff:')) { obj.remoteaddr = obj.remoteaddr.substring(7); }
// Disconnect this agent
@ -60,7 +61,27 @@ module.exports.CreateMeshRelay = function (parent, ws, req) {
if (req.query.auth == null) {
// Use ExpressJS session, check if this session is a logged in user, at least one of the two connections will need to be authenticated.
try { if ((req.session) && (req.session.userid) || (req.session.domainid == getDomain(req).id)) { obj.authenticated = true; } } catch (e) { }
try { if ((req.session) && (req.session.userid) || (req.session.domainid == obj.domain.id)) { obj.authenticated = true; } } catch (e) { }
if ((obj.authenticated != true) && (req.query.user != null) && (req.query.pass != null)) {
// Check user authentication
obj.parent.authenticate(req.query.user, req.query.pass, obj.domain, function (err, userid, passhint) {
if (userid != null) {
obj.authenticated = true;
// Check is we have agent routing instructions, process this here.
if ((req.query.nodeid != null) && (req.query.tcpport != null)) {
if (obj.id == undefined) { obj.id = ('' + Math.random()).substring(2); } // If there is no connection id, generate one.
var command = { nodeid: req.query.nodeid, action: 'msg', type: 'tunnel', value: '*/meshrelay.ashx?id=' + obj.id, tcpport: req.query.tcpport, tcpaddr: ((req.query.tcpaddr == null) ? '127.0.0.1' : req.query.tcpaddr) };
if (obj.sendAgentMessage(command, userid, obj.domain.id) == false) { obj.id = null; obj.parent.parent.debug(1, 'Relay: Unable to contact this agent (' + obj.remoteaddr + ')'); }
}
} else {
obj.parent.parent.debug(1, 'Relay: User authentication failed (' + obj.remoteaddr + ')');
obj.ws.send('error:Authentication failed');
}
performRelay();
});
} else {
performRelay();
}
} else {
// Get the session from the cookie
var cookie = obj.parent.parent.webserver.decodeCookie(req.query.auth);
@ -76,74 +97,78 @@ module.exports.CreateMeshRelay = function (parent, ws, req) {
} else {
obj.id = null;
obj.parent.parent.debug(1, 'Relay: invalid cookie (' + obj.remoteaddr + ')');
obj.ws.send('error:Invalid cookie');
}
performRelay();
}
if (obj.id == null) { try { obj.close(); } catch (e) { } return null; } // Attempt to connect without id, drop this.
ws._socket.setKeepAlive(true, 240000); // Set TCP keep alive
function performRelay() {
if (obj.id == null) { try { obj.close(); } catch (e) { } return null; } // Attempt to connect without id, drop this.
ws._socket.setKeepAlive(true, 240000); // Set TCP keep alive
// Validate that the id is valid, we only need to do this on non-authenticated sessions.
// TODO: Figure out when this needs to be done.
/*
if (!parent.args.notls) {
// Check the identifier, if running without TLS, skip this.
var ids = obj.id.split(':');
if (ids.length != 3) { obj.ws.close(); obj.id = null; return null; } // Invalid ID, drop this.
if (parent.crypto.createHmac('SHA384', parent.relayRandom).update(ids[0] + ':' + ids[1]).digest('hex') != ids[2]) { obj.ws.close(); obj.id = null; return null; } // Invalid HMAC, drop this.
if ((Date.now() - parseInt(ids[1])) > 120000) { obj.ws.close(); obj.id = null; return null; } // Expired time, drop this.
obj.id = ids[0];
}
*/
// Validate that the id is valid, we only need to do this on non-authenticated sessions.
// TODO: Figure out when this needs to be done.
/*
if (!parent.args.notls) {
// Check the identifier, if running without TLS, skip this.
var ids = obj.id.split(':');
if (ids.length != 3) { obj.ws.close(); obj.id = null; return null; } // Invalid ID, drop this.
if (parent.crypto.createHmac('SHA384', parent.relayRandom).update(ids[0] + ':' + ids[1]).digest('hex') != ids[2]) { obj.ws.close(); obj.id = null; return null; } // Invalid HMAC, drop this.
if ((Date.now() - parseInt(ids[1])) > 120000) { obj.ws.close(); obj.id = null; return null; } // Expired time, drop this.
obj.id = ids[0];
}
*/
// Check the peer connection status
{
var relayinfo = parent.wsrelays[obj.id];
if (relayinfo) {
if (relayinfo.state == 1) {
// Check that at least one connection is authenticated
if ((obj.authenticated != true) && (relayinfo.peer1.authenticated != true)) {
// Check the peer connection status
{
var relayinfo = parent.wsrelays[obj.id];
if (relayinfo) {
if (relayinfo.state == 1) {
// Check that at least one connection is authenticated
if ((obj.authenticated != true) && (relayinfo.peer1.authenticated != true)) {
obj.id = null;
obj.ws.close();
obj.parent.parent.debug(1, 'Relay without-auth: ' + obj.id + ' (' + obj.remoteaddr + ')');
return null;
}
// Connect to peer
obj.peer = relayinfo.peer1;
obj.peer.peer = obj;
relayinfo.peer2 = obj;
relayinfo.state = 2;
obj.ws.send('c'); // Send connect to both peers
relayinfo.peer1.ws.send('c');
relayinfo.peer1.ws.resume(); // Release the traffic
relayinfo.peer1.ws.peer = relayinfo.peer2.ws;
relayinfo.peer2.ws.peer = relayinfo.peer1.ws;
obj.parent.parent.debug(1, 'Relay connected: ' + obj.id + ' (' + obj.remoteaddr + ' --> ' + obj.peer.remoteaddr + ')');
} else {
// Connected already, drop (TODO: maybe we should re-connect?)
obj.id = null;
obj.ws.close();
obj.parent.parent.debug(1, 'Relay without-auth: ' + obj.id + ' (' + obj.remoteaddr + ')');
obj.parent.parent.debug(1, 'Relay duplicate: ' + obj.id + ' (' + obj.remoteaddr + ')');
return null;
}
// Connect to peer
obj.peer = relayinfo.peer1;
obj.peer.peer = obj;
relayinfo.peer2 = obj;
relayinfo.state = 2;
obj.ws.send('c'); // Send connect to both peers
relayinfo.peer1.ws.send('c');
relayinfo.peer1.ws.resume(); // Release the traffic
relayinfo.peer1.ws.peer = relayinfo.peer2.ws;
relayinfo.peer2.ws.peer = relayinfo.peer1.ws;
obj.parent.parent.debug(1, 'Relay connected: ' + obj.id + ' (' + obj.remoteaddr + ' --> ' + obj.peer.remoteaddr + ')');
} else {
// Connected already, drop (TODO: maybe we should re-connect?)
obj.id = null;
obj.ws.close();
obj.parent.parent.debug(1, 'Relay duplicate: ' + obj.id + ' (' + obj.remoteaddr + ')');
return null;
}
} else {
// Wait for other relay connection
ws.pause(); // Hold traffic until the other connection
parent.wsrelays[obj.id] = { peer1: obj, state: 1 };
obj.parent.parent.debug(1, 'Relay holding: ' + obj.id + ' (' + obj.remoteaddr + ')');
// Wait for other relay connection
ws.pause(); // Hold traffic until the other connection
parent.wsrelays[obj.id] = { peer1: obj, state: 1 };
obj.parent.parent.debug(1, 'Relay holding: ' + obj.id + ' (' + obj.remoteaddr + ')');
// Check if a peer server has this connection
if (parent.parent.multiServer != null) {
var rsession = obj.parent.wsPeerRelays[obj.id];
if ((rsession != null) && (rsession.serverId > obj.parent.parent.serverId)) {
// We must initiate the connection to the peer
parent.parent.multiServer.createPeerRelay(ws, req, rsession.serverId, req.session.userid);
delete parent.wsrelays[obj.id];
} else {
// Send message to other peers that we have this connection
parent.parent.multiServer.DispatchMessage(JSON.stringify({ action: 'relay', id: obj.id }));
// Check if a peer server has this connection
if (parent.parent.multiServer != null) {
var rsession = obj.parent.wsPeerRelays[obj.id];
if ((rsession != null) && (rsession.serverId > obj.parent.parent.serverId)) {
// We must initiate the connection to the peer
parent.parent.multiServer.createPeerRelay(ws, req, rsession.serverId, req.session.userid);
delete parent.wsrelays[obj.id];
} else {
// Send message to other peers that we have this connection
parent.parent.multiServer.DispatchMessage(JSON.stringify({ action: 'relay', id: obj.id }));
}
}
}
}

View File

@ -338,7 +338,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
// TODO: Right now, we only create type 1 Agent-less Intel AMT mesh, or type 2 Agent mesh
if ((command.meshtype == 1) || (command.meshtype == 2)) {
// Create a type 1 agent-less Intel AMT mesh.
obj.crypto.randomBytes(48, function (err, buf) {
obj.parent.crypto.randomBytes(48, function (err, buf) {
var meshid = 'mesh/' + domain.id + '/' + buf.toString('base64').replace(/\+/g, '@').replace(/\//g, '$');;
var links = {}
links[user._id] = { name: user.name, rights: 0xFFFFFFFF };
@ -492,7 +492,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 4) == 0)) return;
// Create a new nodeid
obj.crypto.randomBytes(48, function (err, buf) {
obj.parent.crypto.randomBytes(48, function (err, buf) {
// create the new node
var nodeid = 'node/' + domain.id + '/' + buf.toString('base64').replace(/\+/g, '@').replace(/\//g, '$');;
var device = { type: 'node', mtype: 1, _id: nodeid, meshid: command.meshid, name: command.devicename, host: command.hostname, domain: domain.id, intelamt: { user: command.amtusername, pass: command.amtpassword, tls: parseInt(command.amttls) } };

View File

@ -1,6 +1,6 @@
{
"name": "meshcentral",
"version": "0.0.8-w",
"version": "0.1.0-d",
"keywords": [
"Remote Management",
"Intel AMT",

View File

@ -25,7 +25,6 @@
<script type="text/javascript" src="scripts/ol3-contextmenu.js"></script>
<title>MeshCentral</title>
</head>
<body onload="javascript:if (typeof(startup) !== 'undefined') startup();" oncontextmenu="handleContextMenu(event)">
<!-- right click menu -->
<div id="contextMenu" class="contextMenu" style="display: none">
@ -1494,10 +1493,10 @@
setDialogMode(2, "Add Mesh Agent", 1, null, x);
if (serverinfo.https == true) {
Q('agins_linux_area').value = "wget -q https://" + serverinfo.name + ":" + serverinfo.port + "/meshagents?script=1 --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh https://" + serverinfo.name + ":" + serverinfo.port + " " + meshid.split('/')[2] + "\r\n";
Q('agins_linux_area').value = "wget -q https://" + serverinfo.name + ":" + serverinfo.port + "/meshagents?script=1 --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh https://" + serverinfo.name + ":" + serverinfo.port + " " + meshid.split('/')[2].replace(/\$/g, '\\$') + "\r\n";
Q('agins_linux_area_un').value = "wget -q https://" + serverinfo.name + ":" + serverinfo.port + "/meshagents?script=1 --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh uninstall\r\n";
} else {
Q('agins_linux_area').value = "wget -q http://" + serverinfo.name + ":" + serverinfo.port + "/meshagents?script=1 -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh http://" + serverinfo.name + ":" + serverinfo.port + " " + meshid.split('/')[2] + "\r\n";
Q('agins_linux_area').value = "wget -q http://" + serverinfo.name + ":" + serverinfo.port + "/meshagents?script=1 -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh http://" + serverinfo.name + ":" + serverinfo.port + " " + meshid.split('/')[2].replace(/\$/g, '\\$') + "\r\n";
Q('agins_linux_area_un').value = "wget -q http://" + serverinfo.name + ":" + serverinfo.port + "/meshagents?script=1 -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh uninstall\r\n";
}
}
@ -2415,6 +2414,8 @@
if (mesh.mtype == 2) x += '<a style=cursor:pointer onclick=p10showNodeNetInfoDialog("' + node._id + '") title="Show device network interface information">Interfaces</a>&nbsp;';
if (xxmap != null) x += '<a style=cursor:pointer onclick=p10showNodeLocationDialog("' + node._id + '") title="Show device locations information">Location</a>&nbsp;';
if (mesh.mtype == 2) x += '<a style=cursor:pointer onclick=p10showRouterDialog("' + node._id + '") title="Traffic router used to connect to a device thru this server.">Router</a>&nbsp;';
// RDP link, show this link only of the remote machine is Windows.
if (((connectivity & 1) != 0) && (clickOnce == true) && (mesh.mtype == 2) && ((meshrights & 8) != 0)) {
if ((node.agent.id > 0) && (node.agent.id < 5)) { x += '<a style=cursor:pointer onclick=p10clickOnce("' + node._id + '","RDP2",3389) title="Requires Microsoft ClickOnce support in your browser.">RDP</a>&nbsp;'; }
@ -2689,6 +2690,39 @@
meshserver.Send({ action: 'getnetworkinfo', nodeid: currentNode._id });
}
// Show router dialog
function p10showRouterDialog() {
if (xxdialogMode) return;
var y = "<select id=aginsSelect onclick=meshCmdOsClick() style=width:236px>";
y += "<option value=1>Windows (32bit)</option>";
y += "<option value=2>Windows (64bit)</option>";
y += "<option value=5>Linux x86 (32bit)</option>";
y += "<option value=6>Linux x86 (64bit)</option>";
y += "<option value=25>Linux ARM, Raspberry Pi (32bit)</option>";
y += "</select>";
var x = "";
x += "<div>Download \"meshcmd\" with an action file to route traffic thru this server to this device. Make sure to edit meshaction.txt and add your account password or make any changes needed.<br /><br />";
x += addHtmlValue('Operating System', y);
x += addHtmlValue('Mesh Command', '<a id="meshcmddownloadid" href="meshagents?meshcmd=1" target="_blank"></a>');
x += addHtmlValue('Action File', '<a href="meshagents?meshaction=route&nodeid=' + currentNode._id + '" target="_blank">MeshAction (.txt)</a>');
x += "</div>";
setDialogMode(2, "Network Router", 1, null, x, currentNode._id);
meshCmdOsClick();
}
function meshCmdOsClick() {
var os = Q('aginsSelect').value, osn = '';
Q('meshcmddownloadid').href = "meshagents?meshcmd=" + os;
if (os == 1) { osn = 'MeshCmd (Win32 executable)'; }
if (os == 2) { osn = 'MeshCmd (Win64 executable)'; }
if (os == 5) { osn = 'MeshCmd (Linux x86, 32bit)'; }
if (os == 6) { osn = 'MeshCmd (Linux x86, 64bit)'; }
if (os == 25) { osn = 'MeshCmd (Linux ARM, 32bit)'; }
QH('meshcmddownloadid', osn);
}
function p10showiconselector() {
if (xxdialogMode) return;
var mesh = meshes[currentNode.meshid];

View File

@ -88,6 +88,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
// Perform hash on web certificate and agent certificate
obj.webCertificateHash = parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.web.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'binary' });
obj.webCertificateHashBase64 = new Buffer(parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.web.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'binary' }), 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
obj.agentCertificateHashHex = parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.agent.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'hex' });
obj.agentCertificateHashBase64 = new Buffer(parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.agent.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'binary' }), 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
obj.agentCertificateAsn1 = parent.certificateOperations.forge.asn1.toDer(parent.certificateOperations.forge.pki.certificateToAsn1(parent.certificateOperations.forge.pki.certificateFromPem(parent.certificates.agent.cert))).getBytes();
@ -1119,6 +1120,48 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
if (scriptInfo == null) { res.sendStatus(404); return; }
res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'text/plain', 'Content-Disposition': 'attachment; filename=' + scriptInfo.rname });
res.sendFile(scriptInfo.path);
} else if (req.query.meshcmd != null) {
// Send meshcmd for a specific platform back
var argentInfo = obj.parent.meshAgentBinaries[req.query.meshcmd];
if (argentInfo == null) { res.sendStatus(404); return; }
// Load the agent
obj.fs.readFile(argentInfo.path, function (err, agentexe) {
if (err != null) { res.sendStatus(404); return; }
// Load meshcmd.js
obj.fs.readFile(obj.path.join(__dirname, 'agents', 'meshcmd.js'), function (err, meshcmdjs) {
if (err != null) { res.sendStatus(404); return; }
res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=meshcmd' + ((req.query.meshcmd <= 4) ? '.exe' : '') });
var tail = new Buffer(8);
tail.writeInt32BE(meshcmdjs.length, 0);
tail.writeInt32BE(agentexe.length + meshcmdjs.length + 8, 4);
res.send(Buffer.concat([agentexe, meshcmdjs, tail]));
});
});
} else if (req.query.meshaction != null) {
var domain = checkUserIpAddress(req, res);
var user = obj.users[req.session.userid];
if (domain == null || req.query.nodeid == null) { res.sendStatus(404); return; }
obj.db.Get(req.query.nodeid, function (err, nodes) {
if (nodes.length != 1) { res.sendStatus(401); return; }
var node = nodes[0];
// Create the meshaction.txt file for meshcmd.exe
var meshaction = {
action: req.query.meshaction,
localPort: 1234,
remoteName: node.name,
remoteNodeId: node._id,
remotePort: 3389,
username: '',
password: '',
serverId: obj.agentCertificateHashHex.toUpperCase(), // SHA384 of server HTTPS public key
serverHttpsHash: new Buffer(obj.webCertificateHash, 'binary').toString('hex').toUpperCase(), // SHA384 of server HTTPS certificate
debugLevel: 0
}
if (user != null) { meshaction.username = user.name; }
if (obj.args.lanonly != true) { meshaction.serverUrl = ((obj.args.notls == true) ? 'ws://' : 'wss://') + obj.certificates.CommonName + ':' + obj.args.port + '/' + ((domain.id == '') ? '' : ('/' + domain.id)) + 'meshrelay.ashx'; }
res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'text/plain', 'Content-Disposition': 'attachment; filename=meshaction.txt' });
res.send(JSON.stringify(meshaction, null, ' '));
});
} else {
// Send a list of available mesh agents
var response = '<html><head><title>Mesh Agents</title><style>table,th,td { border:1px solid black;border-collapse:collapse;padding:3px; }</style></head><body><table>';
@ -1140,7 +1183,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
if (domain == null) return;
//if ((domain.id !== '') || (!req.session) || (req.session == null) || (!req.session.userid)) { res.sendStatus(401); return; }
// Delete a mesh and all computers within it
// Query the meshid
obj.db.Get('mesh/' + domain.id + '/' + req.query.id, function (err, meshes) {
if (meshes.length != 1) { res.sendStatus(401); return; }
var mesh = meshes[0];
@ -1205,7 +1248,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
obj.app.post(url + 'uploadmeshcorefile.ashx', handleUploadMeshCoreFile);
obj.app.get(url + 'userfiles/*', handleDownloadUserFiles);
obj.app.ws(url + 'echo.ashx', handleEchoWebSocket);
obj.app.ws(url + 'meshrelay.ashx', function (ws, req) { try { obj.meshRelayHandler.CreateMeshRelay(obj, ws, req); } catch (e) { console.log(e); } });
obj.app.ws(url + 'meshrelay.ashx', function (ws, req) { try { obj.meshRelayHandler.CreateMeshRelay(obj, ws, req, getDomain(req)); } catch (e) { console.log(e); } });
// Receive mesh agent connections
obj.app.ws(url + 'agent.ashx', function (ws, req) { try { var domain = getDomain(req); obj.meshAgentHandler.CreateMeshAgent(obj, obj.db, ws, req, obj.args, domain); } catch (e) { console.log(e); } });