Fixed desktop multiplexor race condition, web application improvements.
This commit is contained in:
parent
ea9edefad1
commit
b09efc7df5
|
@ -203,6 +203,7 @@ function CreateDesktopMultiplexor(parent, domain, nodeid, func) {
|
|||
|
||||
// Clean up ourselves
|
||||
function dispose() {
|
||||
if (obj.viewers == null) return;
|
||||
//console.log('dispose', obj.nodeid);
|
||||
delete obj.viewers;
|
||||
delete obj.imagesCounters;
|
||||
|
@ -226,6 +227,8 @@ function CreateDesktopMultiplexor(parent, domain, nodeid, func) {
|
|||
parent.parent.DispatchEvent(['*', obj.nodeid], obj, event); // TODO: Add Node MeshID to targets
|
||||
obj.startTime = null;
|
||||
}
|
||||
|
||||
parent.parent.debug('relay', 'DesktopRelay: Disposing desktop multiplexor');
|
||||
}
|
||||
|
||||
// Send data to the agent or queue it up for sending
|
||||
|
@ -715,7 +718,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
|||
}
|
||||
|
||||
// If there is no authentication, drop this connection
|
||||
if ((obj.id != null) && (obj.user == null) && (obj.ruserid == null)) { try { ws.close(); parent.parent.debug('relay', 'Relay: Connection with no authentication (' + cleanRemoteAddr(obj.req.ip) + ')'); } catch (e) { console.log(e); } return; }
|
||||
if ((obj.id != null) && (obj.user == null) && (obj.ruserid == null)) { try { ws.close(); parent.parent.debug('relay', 'DesktopRelay: Connection with no authentication (' + cleanRemoteAddr(obj.req.ip) + ')'); } catch (e) { console.log(e); } return; }
|
||||
|
||||
// Relay session count (we may remove this in the future)
|
||||
obj.relaySessionCounted = true;
|
||||
|
@ -748,8 +751,8 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
|||
if (obj.ws == null) return; // Already closed.
|
||||
|
||||
// Close the connection
|
||||
if ((arg == 1) || (arg == null)) { try { ws.close(); parent.parent.debug('relay', 'Relay: Soft disconnect (' + cleanRemoteAddr(obj.req.ip) + ')'); } catch (e) { console.log(e); } } // Soft close, close the websocket
|
||||
if (arg == 2) { try { ws._socket._parent.end(); parent.parent.debug('relay', 'Relay: Hard disconnect (' + cleanRemoteAddr(obj.req.ip) + ')'); } catch (e) { console.log(e); } } // Hard close, close the TCP socket
|
||||
if ((arg == 1) || (arg == null)) { try { ws.close(); parent.parent.debug('relay', 'DesktopRelay: Soft disconnect (' + cleanRemoteAddr(obj.req.ip) + ')'); } catch (e) { console.log(e); } } // Soft close, close the websocket
|
||||
if (arg == 2) { try { ws._socket._parent.end(); parent.parent.debug('relay', 'DesktopRelay: Hard disconnect (' + cleanRemoteAddr(obj.req.ip) + ')'); } catch (e) { console.log(e); } } // Hard close, close the TCP socket
|
||||
if (obj.relaySessionCounted) { parent.relaySessionCount--; delete obj.relaySessionCounted; }
|
||||
if (obj.deskDecoder != null) { if (obj.deskDecoder.removePeer(obj) == true) { delete parent.desktoprelays[obj.nodeid]; } }
|
||||
|
||||
|
@ -826,9 +829,9 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
|||
try { if (obj.peer != null) { obj.peer.ws.send('{"ctrlChannel":"102938","type":"pong"}'); } } catch (ex) { }
|
||||
}
|
||||
|
||||
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
|
||||
function performRelay(retryCount) {
|
||||
if ((obj.id == null) || (retryCount > 20)) { try { obj.close(); } catch (e) { } return null; } // Attempt to connect without id, drop this.
|
||||
if (retryCount == 0) { 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.
|
||||
|
@ -843,13 +846,19 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
|||
}
|
||||
*/
|
||||
|
||||
// Setup the agent PING/PONG timers
|
||||
if ((typeof parent.parent.args.agentping == 'number') && (obj.pingtimer == null)) { obj.pingtimer = setInterval(sendPing, parent.parent.args.agentping * 1000); }
|
||||
else if ((typeof parent.parent.args.agentpong == 'number') && (obj.pongtimer == null)) { obj.pongtimer = setInterval(sendPong, parent.parent.args.agentpong * 1000); }
|
||||
if (retryCount == 0) {
|
||||
// Setup the agent PING/PONG timers
|
||||
if ((typeof parent.parent.args.agentping == 'number') && (obj.pingtimer == null)) { obj.pingtimer = setInterval(sendPing, parent.parent.args.agentping * 1000); }
|
||||
else if ((typeof parent.parent.args.agentpong == 'number') && (obj.pongtimer == null)) { obj.pongtimer = setInterval(sendPong, parent.parent.args.agentpong * 1000); }
|
||||
|
||||
parent.parent.debug('relay', 'DesktopRelay: Connection (' + cleanRemoteAddr(obj.req.ip) + ')');
|
||||
}
|
||||
|
||||
// Create if needed and add this peer to the desktop multiplexor
|
||||
obj.deskDecoder = parent.desktoprelays[obj.nodeid];
|
||||
if (obj.deskDecoder == null) {
|
||||
parent.desktoprelays[obj.nodeid] = 1; // Indicate that the creating of the desktop multiplexor is pending.
|
||||
parent.parent.debug('relay', 'DesktopRelay: Creating new desktop multiplexor');
|
||||
CreateDesktopMultiplexor(parent, domain, obj.nodeid, function (deskDecoder) {
|
||||
obj.deskDecoder = deskDecoder;
|
||||
parent.desktoprelays[obj.nodeid] = obj.deskDecoder;
|
||||
|
@ -857,8 +866,14 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
|||
ws._socket.resume(); // Release the traffic
|
||||
});
|
||||
} else {
|
||||
obj.deskDecoder.addPeer(obj);
|
||||
ws._socket.resume(); // Release the traffic
|
||||
if (obj.deskDecoder == 1) {
|
||||
// The multiplexor is being created, hold a little and try again. This is to prevent a possible race condition.
|
||||
setTimeout(function () { performRelay(++retryCount); }, 50);
|
||||
} else {
|
||||
// Hook up this peer to the multiplexor and release the traffic
|
||||
obj.deskDecoder.addPeer(obj);
|
||||
ws._socket.resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -902,7 +917,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
|||
const command = { nodeid: cookie.nodeid, action: 'msg', type: 'tunnel', value: '*/meshrelay.ashx?id=' + obj.id + '&rauth=' + rcookie, tcpport: cookie.tcpport, tcpaddr: cookie.tcpaddr };
|
||||
parent.parent.debug('relay', 'Relay: Sending agent tunnel command: ' + JSON.stringify(command));
|
||||
if (obj.sendAgentMessage(command, user._id, cookie.domainid) == false) { delete obj.id; parent.parent.debug('relay', 'Relay: Unable to contact this agent (' + cleanRemoteAddr(obj.req.ip) + ')'); }
|
||||
performRelay();
|
||||
performRelay(0);
|
||||
});
|
||||
return obj;
|
||||
} else if ((obj.req.query.nodeid != null) && ((obj.req.query.tcpport != null) || (obj.req.query.udpport != null))) {
|
||||
|
@ -927,14 +942,14 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
|||
parent.parent.debug('relay', 'Relay: Sending agent UDP tunnel command: ' + JSON.stringify(command));
|
||||
if (obj.sendAgentMessage(command, user._id, domain.id) == false) { delete obj.id; parent.parent.debug('relay', 'Relay: Unable to contact this agent (' + cleanRemoteAddr(obj.req.ip) + ')'); }
|
||||
}
|
||||
performRelay();
|
||||
performRelay(0);
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
// If this is not an authenticated session, or the session does not have routing instructions, just go ahead an connect to existing session.
|
||||
performRelay();
|
||||
performRelay(0);
|
||||
return obj;
|
||||
};
|
||||
|
||||
|
|
|
@ -1666,6 +1666,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||
if ((command.emailVerified === true || command.emailVerified === false) && (chguser.emailVerified != command.emailVerified)) { chguser.emailVerified = command.emailVerified; change = 1; }
|
||||
if ((common.validateInt(command.quota, 0) || command.quota == null) && (command.quota != chguser.quota)) { chguser.quota = command.quota; if (chguser.quota == null) { delete chguser.quota; } change = 1; }
|
||||
if ((command.consent != null) && (typeof command.consent == 'number')) { if (command.consent == 0) { delete chguser.consent; } else { chguser.consent = command.consent; } change = 1; }
|
||||
if ((command.phone != null) && (typeof command.phone == 'string') && ((command.phone == '') || isPhoneNumber(command.phone))) { if (command.phone == '') { delete chguser.phone; } else { chguser.phone = command.phone; } change = 1; }
|
||||
|
||||
// Site admins can change any server rights, user managers can only change AccountLock, NoMeshCmd and NoNewGroups
|
||||
if (chguser._id !== user._id) { // We can't change our own siteadmin permissions.
|
||||
|
@ -4202,6 +4203,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||
// Split a string taking into account the quoats. Used for command line parsing
|
||||
function splitArgs(str) { var myArray = [], myRegexp = /[^\s"]+|"([^"]*)"/gi; do { var match = myRegexp.exec(str); if (match != null) { myArray.push(match[1] ? match[1] : match[0]); } } while (match != null); return myArray; }
|
||||
function toNumberIfNumber(x) { if ((typeof x == 'string') && (+parseInt(x) === x)) { x = parseInt(x); } return x; }
|
||||
function isPhoneNumber(x) { return x.match(/^\(?([0-9]{3,4})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/) }
|
||||
|
||||
function removeAllUnderScore(obj) {
|
||||
if (typeof obj != 'object') return obj;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "meshcentral",
|
||||
"version": "0.5.18",
|
||||
"version": "0.5.19",
|
||||
"keywords": [
|
||||
"Remote Management",
|
||||
"Intel AMT",
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2213,7 +2213,7 @@
|
|||
case 'verifyPhone': {
|
||||
if (xxdialogMode && (xxdialogTag != 'verifyPhone')) return;
|
||||
var x = '<table><tr><td><img src="images/phone80.png" style=padding:8px>';
|
||||
x += '<td>Check your phone and enter the verification code.';
|
||||
x += '<td>' + "Check your phone and enter the verification code.";
|
||||
x += '<br /><br /><div style=width:100%;text-align:center>' + "Verification code:" + ' <input type=tel pattern="[0-9]" inputmode="number" maxlength=6 id=d2phoneCodeInput onKeyUp=account_managePhoneCodeValidate() onkeypress="if (event.key==\'Enter\') account_managePhoneCodeValidate(1)"></div></table>';
|
||||
setDialogMode(2, "Phone Notifications", 3, account_managePhoneConfirm, x, message.cookie);
|
||||
Q('d2phoneCodeInput').focus();
|
||||
|
@ -7943,7 +7943,7 @@
|
|||
account_managePhoneRemoveValidate();
|
||||
} else {
|
||||
x = '<table style=width:100%><tr><td style=width:56px><img src="images/phone80.png" style=padding:8px>';
|
||||
x += '<td>Enter your SMS capable phone number. Once verified, the number may be used for login verification and other notifications.';
|
||||
x += '<td>' + "Enter your SMS capable phone number. Once verified, the number may be used for login verification and other notifications.";
|
||||
x += '<br /><br /><div style=width:100%;text-align:center>' + "Phone number:" + ' <input type=tel pattern="[0-9]" autocomplete="tel" inputmode="tel" maxlength=18 id=d2phoneinput onKeyUp=account_managePhoneValidate() onkeypress="if (event.key==\'Enter\') account_managePhoneValidate(1)"></div></table>';
|
||||
setDialogMode(2, "Phone Notifications", 3, account_managePhoneAdd, x, 'verifyPhone');
|
||||
Q('d2phoneinput').focus();
|
||||
|
@ -9839,10 +9839,10 @@
|
|||
if (user.email != null) {
|
||||
if (((features & 0x200000) == 0) || (user.email.toLowerCase() != user.name.toLowerCase())) {
|
||||
// Username & email are different
|
||||
username += ', <a href=# onclick=\'return doemail(event,\"' + user.email + '\")\'>' + user.email + '</a>' + emailVerified;
|
||||
username += ', <a href="mailto:' + user.email + '" \'>' + user.email + '</a>' + emailVerified;
|
||||
} else {
|
||||
// Username & email are the same
|
||||
username += ' <a href=# onclick=\'return doemail(event,\"' + user.email + '\")\'><img src="images/mail12.png" height=9 width=12 title="' + "Send email to user" + '" style="margin-top:2px" /></a>' + emailVerified;
|
||||
username += ' <a href="mailto:' + user.email + '" \'><img src="images/mail12.png" height=9 width=12 title="' + "Send email to user" + '" style="margin-top:2px" /></a>' + emailVerified;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9851,7 +9851,7 @@
|
|||
if ((user.siteadmin != null) && ((user.siteadmin & 32) != 0) && (user.siteadmin != 0xFFFFFFFF)) { username += ' <img src="images/padlock12.png" height=12 width=8 title="' + "Account is locked" + '" style="margin-top:2px" />'; }
|
||||
x += '<tr tabindex=0 onmouseover=userMouseHover(this,1) onmouseout=userMouseHover(this,0) onkeypress="if (event.key==\'Enter\') gotoUser(\'' + encodeURIComponent(user._id) + '\')"><td>';
|
||||
x += '<div class=bar>';
|
||||
x += '<div class=baricon><input class=UserCheckbox value=' + encodeURIComponent(user._id) + ' onclick=p3updateInfo() type=checkbox' + ((user._id == userinfo._id)?' disabled':'') + '></div><div style=cursor:pointer onclick=gotoUser(\"' + encodeURIComponent(user._id) + '\")>';
|
||||
x += '<div class=baricon><input class=UserCheckbox value=' + encodeURIComponent(user._id) + ' onclick=p3updateInfo() type=checkbox' + ((user._id == userinfo._id)?' disabled':'') + '></div><div style=cursor:pointer onclick=gotoUser(\"' + encodeURIComponent(user._id) + '\",false,event)>';
|
||||
x += '<div class=baricon><div class="' + icon + gray + '"></div></div>';
|
||||
x += '<div class=g1></div><div class=g2></div><div>';
|
||||
x += '<div><span>' + username + '</span>' + msg + '</div></div><td style=text-align:center>' + groups + '<td style=text-align:center>' + lastAccess + '<td style=text-align:center>' + permissions;
|
||||
|
@ -9975,13 +9975,6 @@
|
|||
|
||||
function showUserAlertDialogEx(button, userid) { meshserver.send({ action: 'notifyuser', userid: decodeURIComponent(userid), msg: Q('d2notifyText').value }); }
|
||||
|
||||
function doemail(e, addr) {
|
||||
if (xxdialogMode) return false;
|
||||
haltEvent(e);
|
||||
window.open('mailto:' + addr);
|
||||
return false;
|
||||
}
|
||||
|
||||
function p4batchAccountCreate() {
|
||||
if (xxdialogMode) return;
|
||||
var x = "Create many accounts at once by importing a JSON file with the following format:" + '<br /><pre>[\r\n {"user":"x1","pass":"x","email":"x1@x"},\r\n {"user":"x2","pass":"x","resetNextLogin":true}\r\n]</pre><input style=width:370px type=file id=d4importFile accept=".json" onchange=p4batchAccountCreateValidate() />';
|
||||
|
@ -10633,8 +10626,9 @@
|
|||
//
|
||||
|
||||
var currentUser = null;
|
||||
function gotoUser(userid, force) {
|
||||
function gotoUser(userid, force, event) {
|
||||
if (xxdialogMode && !force) return;
|
||||
if ((event != null) && (event.originalTarget != null) && (event.originalTarget.href != null)) return;
|
||||
var user = currentUser = users[decodeURIComponent(userid)];
|
||||
if (user == null) { setDialogMode(0); go(4); return; }
|
||||
QH('p30userName', user.name);
|
||||
|
@ -10657,13 +10651,17 @@
|
|||
var email = user.email?EscapeHtml(user.email):'<i>' + "Not set" + '</i>', everify = '';
|
||||
if (serverinfo.emailcheck) { everify = ((user.emailVerified == true) ? '<b style=color:green;cursor:pointer title=\"' + "Email is verified" + '\">✓</b> ' : '<b style=color:red;cursor:pointer title=\"' + "Email not verified" + '\">✗</b> '); }
|
||||
if (user.name.toLowerCase() != user._id.split('/')[2]) { x += addDeviceAttribute("User Identifier", user._id.split('/')[2]); }
|
||||
if (((features & 0x200000) == 0) && ((user.siteadmin != 0xFFFFFFFF) || (userinfo.siteadmin == 0xFFFFFFFF))) { // If we are not site admin, we can't change a admin email.
|
||||
x += addDeviceAttribute("Email", everify + '<a href=# style=cursor:pointer onclick=p30showUserEmailChangeDialog(event,\"' + userid + '\")>' + email + '</a> <a href=# style=cursor:pointer onclick=\'return doemail(event,\"' + user.email + '\")\'><img class=hoverButton src="images/link1.png" /></a>');
|
||||
if (((user.siteadmin != 0xFFFFFFFF) || (userinfo.siteadmin == 0xFFFFFFFF))) { // If we are not site admin, we can't change a admin email.
|
||||
x += addDeviceAttribute("Email", everify + email + ' <a href="mailto:' + user.email + '" \'><img class=hoverButton src="images/link1.png" /></a>' + ' <img class=hoverButton style=cursor:pointer src="images/link5.png" onclick=p30showUserEmailChangeDialog(event,\"' + userid + '\") />');
|
||||
} else {
|
||||
x += addDeviceAttribute("Email", everify + email + ' <a href=# style=cursor:pointer onclick=\'return doemail(event,\"' + user.email + '\")\'><img class=hoverButton src="images/link1.png" /></a>');
|
||||
x += addDeviceAttribute("Email", everify + email + ' <a href="mailto:' + user.email + '" \'><img class=hoverButton src="images/link1.png" /></a>');
|
||||
}
|
||||
if (user.phone != null) { x += addDeviceAttribute("Phone Number", user.phone); }
|
||||
x += addDeviceAttribute("Server Rights", premsg + '<a href=# style=cursor:pointer onclick=\'return showUserAdminDialog(event,\"' + userid + '\")\'>' + msg.join(', ') + '</a>');
|
||||
|
||||
if ((features & 0x02000000) || (user.phone != null)) { // If SMS is enabled on the server or user has a phone number
|
||||
x += addDeviceAttribute("Phone Number", (user.phone?user.phone:('<i>' + "None" + '</i>')) + ' <img class=hoverButton style=cursor:pointer src="images/link5.png" onclick=p30editPhone() />');
|
||||
}
|
||||
|
||||
x += addDeviceAttribute("Server Rights", premsg + msg.join(', ') + ' <img style=cursor:pointer class=hoverButton onclick=\'return showUserAdminDialog(event,\"' + userid + '\")\' src="images/link5.png" />');
|
||||
if (user.quota) x += addDeviceAttribute("Server Quota", EscapeHtml(parseInt(user.quota) / 1024) + ' k');
|
||||
x += addDeviceAttribute("Creation", printDateTime(new Date(user.creation * 1000)));
|
||||
if (user.login) x += addDeviceAttribute("Last Login", printDateTime(new Date(user.login * 1000)));
|
||||
|
@ -10756,6 +10754,25 @@
|
|||
}
|
||||
}
|
||||
|
||||
function p30editPhone() {
|
||||
if (xxdialogMode) return;
|
||||
var x = '<table style=width:100%><tr><td style=width:56px><img src="images/phone80.png" style=padding:8px>';
|
||||
x += '<td style=width:100%;text-align:center>' + "SMS capable phone number for this user." + '<br />' + "Leave blank for none.";
|
||||
x += '<br /><br /><div style=width:100%;text-align:center>' + "Phone number:" + ' <input type=tel pattern="[0-9]" autocomplete="tel" value="' + (currentUser.phone?currentUser.phone:'') + '" inputmode="tel" maxlength=18 id=d2phoneinput onKeyUp=p30editPhoneValidate() onkeypress="if (event.key==\'Enter\') p30editPhoneValidate(1)"></div></table>';
|
||||
setDialogMode(2, "Phone Notifications", 3, p30editPhoneEx, x, 'verifyPhone');
|
||||
Q('d2phoneinput').focus();
|
||||
p30editPhoneValidate();
|
||||
}
|
||||
|
||||
function p30editPhoneValidate(x) {
|
||||
var ok = (Q('d2phoneinput').value == '') || (isPhoneNumber(Q('d2phoneinput').value));
|
||||
QE('idx_dlgOkButton', ok);
|
||||
if ((x == 1) && ok) { dialogclose(1); }
|
||||
}
|
||||
|
||||
// Send to the server the user's new phone number
|
||||
function p30editPhoneEx() { meshserver.send({ action: 'edituser', id: currentUser._id, phone: Q('d2phoneinput').value }); }
|
||||
|
||||
// Display the user's email change dialog box
|
||||
function p30showUserEmailChangeDialog(event) {
|
||||
if (xxdialogMode) return false;
|
||||
|
|
Loading…
Reference in New Issue