add webrelay to websocket and meshctrl #6484

Signed-off-by: si458 <simonsmith5521@gmail.com>
This commit is contained in:
si458 2024-11-26 16:46:14 +00:00
parent 545bf58e8d
commit dbb5b4ba11
3 changed files with 99 additions and 2 deletions

View File

@ -16,7 +16,7 @@ var settings = {};
const crypto = require('crypto');
const args = require('minimist')(process.argv.slice(2));
const path = require('path');
const possibleCommands = ['edituser', 'listusers', 'listusersessions', 'listdevicegroups', 'listdevices', 'listusersofdevicegroup', 'listevents', 'logintokens', 'serverinfo', 'userinfo', 'adduser', 'removeuser', 'adddevicegroup', 'removedevicegroup', 'editdevicegroup', 'broadcast', 'showevents', 'addusertodevicegroup', 'removeuserfromdevicegroup', 'addusertodevice', 'removeuserfromdevice', 'sendinviteemail', 'generateinvitelink', 'config', 'movetodevicegroup', 'deviceinfo', 'removedevice', 'editdevice', 'addlocaldevice', 'addamtdevice', 'addusergroup', 'listusergroups', 'removeusergroup', 'runcommand', 'shell', 'upload', 'download', 'deviceopenurl', 'devicemessage', 'devicetoast', 'addtousergroup', 'removefromusergroup', 'removeallusersfromusergroup', 'devicesharing', 'devicepower', 'indexagenterrorlog', 'agentdownload', 'report', 'grouptoast', 'groupmessage'];
const possibleCommands = ['edituser', 'listusers', 'listusersessions', 'listdevicegroups', 'listdevices', 'listusersofdevicegroup', 'listevents', 'logintokens', 'serverinfo', 'userinfo', 'adduser', 'removeuser', 'adddevicegroup', 'removedevicegroup', 'editdevicegroup', 'broadcast', 'showevents', 'addusertodevicegroup', 'removeuserfromdevicegroup', 'addusertodevice', 'removeuserfromdevice', 'sendinviteemail', 'generateinvitelink', 'config', 'movetodevicegroup', 'deviceinfo', 'removedevice', 'editdevice', 'addlocaldevice', 'addamtdevice', 'addusergroup', 'listusergroups', 'removeusergroup', 'runcommand', 'shell', 'upload', 'download', 'deviceopenurl', 'devicemessage', 'devicetoast', 'addtousergroup', 'removefromusergroup', 'removeallusersfromusergroup', 'devicesharing', 'devicepower', 'indexagenterrorlog', 'agentdownload', 'report', 'grouptoast', 'groupmessage', 'webrelay'];
if (args.proxy != null) { try { require('https-proxy-agent'); } catch (ex) { console.log('Missing module "https-proxy-agent", type "npm install https-proxy-agent" to install it.'); return; } }
if (args['_'].length == 0) {
@ -65,6 +65,7 @@ if (args['_'].length == 0) {
console.log(" Shell - Access command shell of a remote device.");
console.log(" Upload - Upload a file to a remote device.");
console.log(" Download - Download a file from a remote device.");
console.log(" WebRelay - Creates a HTTP/HTTPS webrelay link for a remote device.");
console.log(" DeviceOpenUrl - Open a URL on a remote device.");
console.log(" DeviceMessage - Open a message box on a remote device.");
console.log(" DeviceToast - Display a toast notification on a remote device.");
@ -277,6 +278,12 @@ if (args['_'].length == 0) {
else { ok = true; }
break;
}
case 'webrelay': {
if (args.id == null) { console.log(winRemoveSingleQuotes("Missing device id, use --id '[deviceid]'")); }
else if (args.type == null) { console.log(winRemoveSingleQuotes("Missing protocol type, use --type [http,https]")); }
else { ok = true; }
break;
}
case 'deviceopenurl': {
if (args.id == null) { console.log(winRemoveSingleQuotes("Missing device id, use --id '[deviceid]'")); }
else if (args.openurl == null) { console.log("Remote URL, use --openurl [url] specify the link to open."); }
@ -1015,6 +1022,21 @@ if (args['_'].length == 0) {
console.log(" --target [localpath] - The local path to download the file to.");
break;
}
case 'webrelay': {
console.log("Generate a webrelay URL to access a HTTP/HTTPS service on a remote device, Example usages:\r\n");
console.log(winRemoveSingleQuotes(" MeshCtrl WebRelay --id 'deviceid' --type http --port 80"));
console.log(winRemoveSingleQuotes(" MeshCtrl WebRelay --id 'deviceid' --type https --port 443"));
console.log("\r\nRequired arguments:\r\n");
if (process.platform == 'win32') {
console.log(" --id [deviceid] - The device identifier.");
} else {
console.log(" --id '[deviceid]' - The device identifier.");
}
console.log(" --type [http,https] - Type of relay from remote device, http or https.");
console.log("\r\nOptional arguments:\r\n");
console.log(" --port [portnumber] - Set alternative port for http or https, default is 80 for http and 443 for https.");
break;
}
case 'deviceopenurl': {
console.log("Open a web page on a remote device, Example usages:\r\n");
console.log(winRemoveSingleQuotes(" MeshCtrl DeviceOpenUrl --id 'deviceid' --openurl http://meshcentral.com"));
@ -1804,6 +1826,29 @@ function serverConnect() {
req.end()
break;
}
case 'webrelay': {
var protocol = null;
if (args.type != null) {
if (args.type == 'http') {
protocol = 1;
} else if (args.type == 'https') {
protocol = 2;
} else {
console.log("Unknown protocol type: " + args.type); process.exit(1);
}
}
var port = null;
if (typeof args.port == 'number') {
if ((args.port < 1) || (args.port > 65535)) { console.log("Port number must be between 1 and 65535."); process.exit(1); }
port = args.port;
} else if (protocol == 1) {
port = 80;
} else if (protocol == 2) {
port = 443;
}
ws.send(JSON.stringify({ action: 'webrelay', nodeid: args.id, port: port, appid: protocol, responseid: 'meshctrl' }));
break;
}
case 'devicesharing': {
if (args.add) {
if (args.add.length == 0) { console.log("Invalid guest name."); process.exit(1); }
@ -2208,6 +2253,7 @@ function serverConnect() {
break;
}
case 'createDeviceShareLink':
case 'webrelay':
if (data.result == 'OK') {
if (data.publicid) { console.log('ID: ' + data.publicid); }
console.log('URL: ' + data.url);

View File

@ -2948,6 +2948,48 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}
break;
}
case 'webrelay':
{
if (common.validateString(command.nodeid, 8, 128) == false) { err = 'Invalid node id'; } // Check the nodeid
else if (command.nodeid.indexOf('/') == -1) { command.nodeid = 'node/' + domain.id + '/' + command.nodeid; }
else if ((command.nodeid.split('/').length != 3) || (command.nodeid.split('/')[1] != domain.id)) { err = 'Invalid domain'; } // Invalid domain, operation only valid for current domain
else if ((command.port != null) && (common.validateInt(command.port, 1, 65535) == false)) { err = 'Invalid port value'; } // Check the port if present
else {
if (command.nodeid.split('/').length == 1) { command.nodeid = 'node/' + domain.id + '/' + command.nodeid; }
var snode = command.nodeid.split('/');
if ((snode.length != 3) || (snode[0] != 'node') || (snode[1] != domain.id)) { err = 'Invalid node id'; }
}
// Handle any errors
if (err != null) {
if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'webrelay', responseid: command.responseid, result: err })); } catch (ex) { } }
break;
}
// Get the device rights
parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
// If node not found or we don't have remote control, reject.
if (node == null) {
if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'webrelay', responseid: command.responseid, result: 'Invalid node id' })); } catch (ex) { } }
return;
}
var relayid = null;
var addr = null;
if (node.mtype == 3) { // Setup device relay if needed
var mesh = parent.meshes[node.meshid];
if (mesh && mesh.relayid) { relayid = mesh.relayid; addr = node.host; }
}
var webRelayDns = (args.relaydns != null) ? args.relaydns[0] : obj.getWebServerName(domain, req);
var webRelayPort = ((args.relaydns != null) ? ((typeof args.aliasport == 'number') ? args.aliasport : args.port) : ((parent.webrelayserver != null) ? ((typeof args.relayaliasport == 'number') ? args.relayaliasport : parent.webrelayserver.port) : 0));
if (webRelayPort == 0) { try { ws.send(JSON.stringify({ action: 'webrelay', responseid: command.responseid, result: 'WebRelay Disabled' })); return; } catch (ex) { } }
const authRelayCookie = parent.parent.encodeCookie({ ruserid: user._id, x: req.session.x }, parent.parent.loginCookieEncryptionKey);
var url = 'https://' + webRelayDns + ':' + webRelayPort + '/control-redirect.ashx?n=' + command.nodeid + '&p=' + command.port + '&appid=' + command.appid + '&c=' + authRelayCookie;
if (addr != null) { url += '&addr=' + addr; }
if (relayid != null) { url += '&relayid=' + relayid }
command.url = url;
if (command.responseid != null) { command.result = 'OK'; }
try { ws.send(JSON.stringify(command)); } catch (ex) { }
});
break;
}
case 'runcommands':
{
if (common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's

View File

@ -8036,6 +8036,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
parent.debug('web', 'Invalid login, asking for email validation');
try { ws.send(JSON.stringify({ action: 'close', cause: 'emailvalidation', msg: 'emailvalidationrequired', email2fa: email2fa, sms2fa: sms2fa, msg2fa: msg2fa, email2fasent: true })); ws.close(); } catch (e) { }
} else {
req.session.userid = user._id;
req.session.ip = req.clientIp;
setSessionRandom(req);
func(ws, req, domain, user, null, authData);
}
}
@ -8051,6 +8054,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
try { ws.send(JSON.stringify({ action: 'close', cause: 'emailvalidation', msg: 'emailvalidationrequired', email2fa: email2fa, sms2fa: sms2fa, msg2fa: msg2fa, email2fasent: true })); ws.close(); } catch (e) { }
} else {
// We are authenticated
req.session.userid = user._id;
req.session.ip = req.clientIp;
setSessionRandom(req);
func(ws, req, domain, user);
}
}
@ -8179,7 +8185,10 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
if (emailcheck && (user.email != null) && (!(user._id.split('/')[2].startsWith('~'))) && (user.emailVerified !== true)) {
parent.debug('web', 'Invalid login, asking for email validation');
try { ws.send(JSON.stringify({ action: 'close', cause: 'emailvalidation', msg: 'emailvalidationrequired', email2fa: email2fa, email2fasent: true })); ws.close(); } catch (e) { }
} else {
} else {
req.session.userid = user._id;
req.session.ip = req.clientIp;
setSessionRandom(req);
func(ws, req, domain, user);
}
}