mirror of
				https://github.com/Ylianst/MeshCentral.git
				synced 2025-10-29 23:35:02 -04:00 
			
		
		
		
	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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user