Fixed database and webserver race condition.

Ylian Saint-Hilaire committed Jan 22, 2019 at 15:40 UTC 3e6a5bb501475269bfd828a8a5b4a4668ac3d335
5 files changed +174 -155
meshagent.js
+2 -2
@@ -156,7 +156,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
156 else if (cmdid == 12) { // MeshCommand_AgentHash
157 if ((msg.length == 52) && (obj.agentExeInfo != null) && (obj.agentExeInfo.update == true)) {
158 var agenthash = obj.common.rstr2hex(msg.substring(4)).toLowerCase();
159 - if (agenthash != obj.agentExeInfo.hash) {
159 + if ((agenthash != obj.agentExeInfo.hash) && (agenthash != '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')) {
160 // Mesh agent update required
161 if (obj.nodeid != null) { obj.parent.parent.debug(1, 'Agent update required, NodeID=0x' + obj.nodeid.substring(0, 16) + ', ' + obj.agentExeInfo.desc); }
162 obj.fs.open(obj.agentExeInfo.path, 'r', function (err, fd) {
@@ -193,7 +193,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
193 // Check the mesh core, if the agent is capable of running one
194 if (((obj.agentInfo.capabilities & 16) != 0) && (obj.parent.parent.meshAgentsArchitectureNumbers[obj.agentInfo.agentId].core != null)) {
195 obj.send(obj.common.ShortToStr(11) + obj.common.ShortToStr(0)); // Command 11, ask for mesh core hash.
196 - }
196 + }
197 }
198 }
199 }
meshcentral.js
+11 -1
@@ -90,7 +90,7 @@ function CreateMeshCentralServer(config, args) {
90 try { require('./pass').hash('test', function () { }); } catch (e) { console.log('Old version of node, must upgrade.'); return; } // TODO: Not sure if this test works or not.
91
92 // Check for invalid arguments
93 - var validArguments = ['_', 'notls', 'user', 'port', 'aliasport', 'mpsport', 'mpsaliasport', 'redirport', 'cert', 'mpscert', 'deletedomain', 'deletedefaultdomain', 'showall', 'showusers', 'shownodes', 'showmeshes', 'showevents', 'showpower', 'clearpower', 'showiplocations', 'help', 'exactports', 'install', 'uninstall', 'start', 'stop', 'restart', 'debug', 'filespath', 'datapath', 'noagentupdate', 'launch', 'noserverbackup', 'mongodb', 'mongodbcol', 'wanonly', 'lanonly', 'nousers', 'mpsdebug', 'mpspass', 'ciralocalfqdn', 'dbexport', 'dbimport', 'selfupdate', 'tlsoffload', 'userallowedip', 'swarmallowedip', 'fastcert', 'swarmport', 'swarmdebug', 'logintoken', 'logintokenkey', 'logintokengen', 'logintokengen', 'mailtokengen', 'admin', 'unadmin', 'sessionkey', 'sessiontime', 'minify', 'minifycore'];
93 + var validArguments = ['_', 'notls', 'user', 'port', 'aliasport', 'mpsport', 'mpsaliasport', 'redirport', 'cert', 'mpscert', 'deletedomain', 'deletedefaultdomain', 'showall', 'showusers', 'shownodes', 'showmeshes', 'showevents', 'showpower', 'clearpower', 'showiplocations', 'help', 'exactports', 'install', 'uninstall', 'start', 'stop', 'restart', 'debug', 'filespath', 'datapath', 'noagentupdate', 'launch', 'noserverbackup', 'mongodb', 'mongodbcol', 'wanonly', 'lanonly', 'nousers', 'mpsdebug', 'mpspass', 'ciralocalfqdn', 'dbexport', 'dbexportmin', 'dbimport', 'selfupdate', 'tlsoffload', 'userallowedip', 'swarmallowedip', 'fastcert', 'swarmport', 'swarmdebug', 'logintoken', 'logintokenkey', 'logintokengen', 'logintokengen', 'mailtokengen', 'admin', 'unadmin', 'sessionkey', 'sessiontime', 'minify', 'minifycore'];
94 for (var arg in obj.args) { obj.args[arg.toLocaleLowerCase()] = obj.args[arg]; if (validArguments.indexOf(arg.toLocaleLowerCase()) == -1) { console.log('Invalid argument "' + arg + '", use --help.'); return; } }
95 if (obj.args.mongodb == true) { console.log('Must specify: --mongodb [connectionstring] \r\nSee https://docs.mongodb.com/manual/reference/connection-string/ for MongoDB connection string.'); return; }
96 for (i in obj.config.settings) { obj.args[i] = obj.config.settings[i]; } // Place all settings into arguments, arguments have already been placed into settings so arguments take precedence.
@@ -279,6 +279,16 @@ function CreateMeshCentralServer(config, args) {
279 });
280 return;
281 }
282 + if (obj.args.dbexportmin) {
283 + // Export a minimal database to a JSON file. Export only users, meshes and nodes.
284 + // This is a useful command to look at the database.
285 + if (obj.args.dbexportmin == true) { obj.args.dbexportmin = obj.getConfigFilePath('meshcentral.db.json'); }
286 + obj.db.GetAllType({ $in: ['user', 'node', 'mesh'] }, function (err, docs) {
287 + obj.fs.writeFileSync(obj.args.dbexportmin, JSON.stringify(docs));
288 + console.log('Exported ' + docs.length + ' objects(s) to ' + obj.args.dbexportmin + '.'); process.exit();
289 + });
290 + return;
291 + }
292 if (obj.args.dbimport) {
293 // Import the entire database from a JSON file
294 if (obj.args.dbimport == true) { obj.args.dbimport = obj.getConfigFilePath('meshcentral.db.json'); }
package.json
+1 -1
@@ -1,6 +1,6 @@
1 {
2 "name": "meshcentral",
3 - "version": "0.2.6-q",
3 + "version": "0.2.6-t",
4 "keywords": [
5 "Remote Management",
6 "Intel AMT",
views/default.handlebars
-1
@@ -1755,7 +1755,6 @@
1755 var deviceHeaders = {};
1756 var oldviewmode = 0;
1757 function updateDevices() {
1758 - if (xxcurrentView != 1) return;
1758 var r = '', c = 0, current = null, count = 0, displayedMeshes = {}, view = Q('viewselect').value, groups = {}, groupCount = {};
1759 QV('xdevices', view < 4);
1760 QV('xdevicesmap', view == 4);
webserver.js
+160 -150
@@ -175,35 +175,6 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
175 function EscapeHtml(x) { if (typeof x == "string") return x.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g, '&lt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;'); if (typeof x == "boolean") return x; if (typeof x == "number") return x; }
176 //function EscapeHtmlBreaks(x) { if (typeof x == "string") return x.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g, '&lt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;').replace(/\r/g, '<br />').replace(/\n/g, '').replace(/\t/g, '&nbsp;&nbsp;'); if (typeof x == "boolean") return x; if (typeof x == "number") return x; }
177
178 - if (obj.args.notls || obj.args.tlsoffload) {
179 - // Setup the HTTP server without TLS
180 - obj.expressWs = require('express-ws')(obj.app);
181 - } else {
182 - // Setup the HTTP server with TLS, use only TLS 1.2 and higher with perfect forward secrecy (PFS).
183 - const tlsOptions = { cert: obj.certificates.web.cert, key: obj.certificates.web.key, ca: obj.certificates.web.ca, rejectUnauthorized: true, ciphers: "HIGH:!aNULL:!eNULL:!EXPORT:!RSA:!DES:!RC4:!MD5:!PSK:!SRP:!CAMELLIA", secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE | constants.SSL_OP_NO_TLSv1 | constants.SSL_OP_NO_TLSv1_1 };
184 - if (obj.tlsSniCredentials != null) { tlsOptions.SNICallback = TlsSniCallback; } // We have multiple web server certificate used depending on the domain name
185 - obj.tlsServer = require('https').createServer(tlsOptions, obj.app);
186 - obj.tlsServer.on('secureConnection', function () { /*console.log('tlsServer secureConnection');*/ });
187 - obj.tlsServer.on('error', function () { console.log('tlsServer error'); });
188 - obj.tlsServer.on('newSession', function (id, data, cb) { if (tlsSessionStoreCount > 1000) { tlsSessionStoreCount = 0; tlsSessionStore = {}; } tlsSessionStore[id.toString('hex')] = data; tlsSessionStoreCount++; cb(); });
189 - obj.tlsServer.on('resumeSession', function (id, cb) { cb(null, tlsSessionStore[id.toString('hex')] || null); });
190 - obj.expressWs = require('express-ws')(obj.app, obj.tlsServer);
191 - }
192 -
193 - // Setup middleware
194 - obj.app.engine('handlebars', obj.exphbs({})); // defaultLayout: 'main'
195 - obj.app.set('view engine', 'handlebars');
196 - if (obj.args.tlsoffload) { obj.app.set('trust proxy', obj.args.tlsoffload); } // Reverse proxy should add the "X-Forwarded-*" headers
197 - obj.app.use(obj.bodyParser.urlencoded({ extended: false }));
198 - var sessionOptions = {
199 - name: 'xid', // Recommended security practice to not use the default cookie name
200 - httpOnly: true,
201 - keys: [obj.args.sessionkey], // If multiple instances of this server are behind a load-balancer, this secret must be the same for all instances
202 - secure: (obj.args.notls != true) // Use this cookie only over TLS (Check this: https://expressjs.com/en/guide/behind-proxies.html)
203 - }
204 - if (obj.args.sessiontime != null) { sessionOptions.maxAge = (obj.args.sessiontime * 60 * 1000); }
205 - obj.app.use(obj.session(sessionOptions));
206 -
178 // Session-persisted message middleware
179 obj.app.use(function (req, res, next) {
180 var err = null, msg = null, passhint = null;
@@ -234,10 +205,16 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
205 console.log('Server ' + ((i == '') ? '' : (i + ' ')) + 'has no users, next new account will be site administrator.');
206 }
207 }
237 - });
208
239 - // Fetch all meshes from the database, keep this in memory
240 - obj.db.GetAllType('mesh', function (err, docs) { obj.common.unEscapeAllLinksFieldName(docs); for (var i in docs) { obj.meshes[docs[i]._id] = docs[i]; } });
209 + // Fetch all meshes from the database, keep this in memory
210 + obj.db.GetAllType('mesh', function (err, docs) {
211 + obj.common.unEscapeAllLinksFieldName(docs);
212 + for (var i in docs) { obj.meshes[docs[i]._id] = docs[i]; }
213 +
214 + // We loaded the users and mesh state, start the server
215 + serverStart();
216 + });
217 + });
218
219 // Authenticate the user
220 obj.authenticate = function (name, pass, domain, fn) {
@@ -764,7 +741,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
741 user = obj.users[req.session.userid];
742 if ((user == null) || (user.sid != req.session.usersid)) {
743 // Create the domain user
767 - var usercount = 0, user2 = { type: 'user', _id: req.session.userid, name: req.connection.user, domain: domain.id, sid: req.session.usersid };
744 + var usercount = 0, user2 = { type: 'user', _id: req.session.userid, name: req.connection.user, domain: domain.id, sid: req.session.usersid, creation: Date.now() };
745 for (var i in obj.users) { if (obj.users[i].domain == domain.id) { usercount++; } }
746 if (usercount == 0) { user2.siteadmin = 0xFFFFFFFF; } // If this is the first user, give the account site admin.
747 obj.users[req.session.userid] = user2;
@@ -1873,128 +1850,164 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1850 res.send(meshsettings);
1851 };
1852
1876 - // Add HTTP security headers to all responses
1877 - obj.app.use(function (req, res, next) {
1878 - res.removeHeader("X-Powered-By");
1879 - var domain = req.xdomain = getDomain(req);
1880 -
1881 - // Detect if this is a file sharing domain, if so, just share files.
1882 - if ((domain != null) && (domain.share != null)) {
1883 - var rpath;
1884 - if (domain.dns == null) { rpath = req.url.split('/'); rpath.splice(1, 1); rpath = rpath.join('/'); } else { rpath = req.url; }
1885 - if ((res.headers != null) && (res.headers.upgrade)) {
1886 - // If this is a websocket, stop here.
1887 - res.sendStatus(404);
1888 - } else {
1889 - // Check if the file exists, if so, serve it.
1890 - obj.fs.exists(obj.path.join(domain.share, rpath), function (exists) { if (exists == true) { res.sendfile(rpath, { root: domain.share }); } else { res.sendStatus(404); } });
1891 - }
1853 + // Starts the HTTPS server, this should be called after the user/mesh tables are loaded
1854 + function serverStart() {
1855 + // Start the server, only after users and meshes are loaded from the database.
1856 + if (obj.args.notls || obj.args.tlsoffload) {
1857 + // Setup the HTTP server without TLS
1858 + obj.expressWs = require('express-ws')(obj.app);
1859 } else {
1893 - // Two more headers to take a look at:
1894 - // 'Public-Key-Pins': 'pin-sha256="X3pGTSOuJeEVw989IJ/cEtXUEmy52zs1TZQrU06KUKg="; max-age=10'
1895 - // 'strict-transport-security': 'max-age=31536000; includeSubDomains'
1896 - /*
1897 - var headers = null;
1898 - if (obj.args.notls) {
1899 - // Default headers if no TLS is used
1900 - headers = { 'Referrer-Policy': 'no-referrer', 'x-frame-options': 'SAMEORIGIN', 'X-XSS-Protection': '1; mode=block', 'X-Content-Type-Options': 'nosniff', 'Content-Security-Policy': "default-src http: ws: data: 'self';script-src http: 'unsafe-inline';style-src http: 'unsafe-inline'" };
1901 - } else {
1902 - // Default headers if TLS is used
1903 - headers = { 'Referrer-Policy': 'no-referrer', 'x-frame-options': 'SAMEORIGIN', 'X-XSS-Protection': '1; mode=block', 'X-Content-Type-Options': 'nosniff', 'Content-Security-Policy': "default-src https: wss: data: 'self';script-src https: 'unsafe-inline';style-src https: 'unsafe-inline'" };
1904 - }
1905 - if (parent.config.settings.accesscontrolalloworigin != null) { headers['Access-Control-Allow-Origin'] = parent.config.settings.accesscontrolalloworigin; }
1906 - res.set(headers);
1907 - */
1908 - return next();
1860 + // Setup the HTTP server with TLS, use only TLS 1.2 and higher with perfect forward secrecy (PFS).
1861 + const tlsOptions = { cert: obj.certificates.web.cert, key: obj.certificates.web.key, ca: obj.certificates.web.ca, rejectUnauthorized: true, ciphers: "HIGH:!aNULL:!eNULL:!EXPORT:!RSA:!DES:!RC4:!MD5:!PSK:!SRP:!CAMELLIA", secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE | constants.SSL_OP_NO_TLSv1 | constants.SSL_OP_NO_TLSv1_1 };
1862 + if (obj.tlsSniCredentials != null) { tlsOptions.SNICallback = TlsSniCallback; } // We have multiple web server certificate used depending on the domain name
1863 + obj.tlsServer = require('https').createServer(tlsOptions, obj.app);
1864 + obj.tlsServer.on('secureConnection', function () { /*console.log('tlsServer secureConnection');*/ });
1865 + obj.tlsServer.on('error', function () { console.log('tlsServer error'); });
1866 + obj.tlsServer.on('newSession', function (id, data, cb) { if (tlsSessionStoreCount > 1000) { tlsSessionStoreCount = 0; tlsSessionStore = {}; } tlsSessionStore[id.toString('hex')] = data; tlsSessionStoreCount++; cb(); });
1867 + obj.tlsServer.on('resumeSession', function (id, cb) { cb(null, tlsSessionStore[id.toString('hex')] || null); });
1868 + obj.expressWs = require('express-ws')(obj.app, obj.tlsServer);
1869 }
1910 - });
1870
1912 - // Setup all HTTP handlers
1913 - obj.app.get('/backup.zip', handleBackupRequest);
1914 - obj.app.post('/restoreserver.ashx', handleRestoreRequest);
1915 - if (parent.multiServer != null) { obj.app.ws('/meshserver.ashx', function (ws, req) { parent.multiServer.CreatePeerInServer(parent.multiServer, ws, req); }); }
1916 - for (var i in parent.config.domains) {
1917 - if (parent.config.domains[i].dns != null) { continue; } // This is a subdomain with a DNS name, no added HTTP bindings needed.
1918 - var url = parent.config.domains[i].url;
1919 - obj.app.get(url, handleRootRequest);
1920 - obj.app.get(url + 'terms', handleTermsRequest);
1921 - obj.app.post(url + 'login', handleLoginRequest);
1922 - obj.app.post(url + 'tokenlogin', handleLoginRequest);
1923 - obj.app.get(url + 'logout', handleLogoutRequest);
1924 - obj.app.get(url + 'MeshServerRootCert.cer', handleRootCertRequest);
1925 - obj.app.get(url + 'mescript.ashx', handleMeScriptRequest);
1926 - obj.app.post(url + 'changepassword', handlePasswordChangeRequest);
1927 - obj.app.post(url + 'deleteaccount', handleDeleteAccountRequest);
1928 - obj.app.post(url + 'createaccount', handleCreateAccountRequest);
1929 - obj.app.post(url + 'resetaccount', handleResetAccountRequest);
1930 - obj.app.get(url + 'checkmail', handleCheckMailRequest);
1931 - obj.app.post(url + 'amtevents.ashx', obj.handleAmtEventRequest);
1932 - obj.app.get(url + 'meshagents', obj.handleMeshAgentRequest);
1933 - obj.app.get(url + 'messenger', handleMessengerRequest);
1934 - obj.app.get(url + 'meshosxagent', obj.handleMeshOsxAgentRequest);
1935 - obj.app.get(url + 'meshsettings', obj.handleMeshSettingsRequest);
1936 - obj.app.get(url + 'downloadfile.ashx', handleDownloadFile);
1937 - obj.app.post(url + 'uploadfile.ashx', handleUploadFile);
1938 - obj.app.post(url + 'uploadmeshcorefile.ashx', handleUploadMeshCoreFile);
1939 - obj.app.get(url + 'userfiles/*', handleDownloadUserFiles);
1940 - obj.app.ws(url + 'echo.ashx', handleEchoWebSocket);
1941 - obj.app.ws(url + 'meshrelay.ashx', function (ws, req) { PerformWSSessionAuth(ws, req, true, function (ws1, req1, domain, user, cookie) { obj.meshRelayHandler.CreateMeshRelay(obj, ws1, req1, domain, user, cookie); }); });
1942 - obj.app.get(url + 'webrelay.ashx', function (req, res) { res.send('Websocket connection expected'); });
1943 - obj.app.ws(url + 'webrelay.ashx', function (ws, req) { PerformWSSessionAuth(ws, req, false, handleRelayWebSocket); });
1944 - obj.app.ws(url + 'control.ashx', function (ws, req) { PerformWSSessionAuth(ws, req, false, function (ws1, req1, domain, user, cookie) { obj.meshUserHandler.CreateMeshUser(obj, obj.db, ws1, req1, obj.args, domain, user); }); });
1945 - obj.app.get(url + 'logo.png', handleLogoRequest);
1946 -
1947 - // Server picture
1948 - obj.app.get(url + 'serverpic.ashx', function (req, res) {
1949 - // Check if we have "server.png" in the data folder, if so, use that.
1950 - var p = obj.path.join(obj.parent.datapath, 'server.jpg');
1951 - if (obj.fs.existsSync(p)) {
1952 - // Use the data folder server picture
1953 - try { res.sendFile(p); } catch (e) { res.sendStatus(404); }
1871 + // Setup middleware
1872 + obj.app.engine('handlebars', obj.exphbs({})); // defaultLayout: 'main'
1873 + obj.app.set('view engine', 'handlebars');
1874 + if (obj.args.tlsoffload) { obj.app.set('trust proxy', obj.args.tlsoffload); } // Reverse proxy should add the "X-Forwarded-*" headers
1875 + obj.app.use(obj.bodyParser.urlencoded({ extended: false }));
1876 + var sessionOptions = {
1877 + name: 'xid', // Recommended security practice to not use the default cookie name
1878 + httpOnly: true,
1879 + keys: [obj.args.sessionkey], // If multiple instances of this server are behind a load-balancer, this secret must be the same for all instances
1880 + secure: (obj.args.notls != true) // Use this cookie only over TLS (Check this: https://expressjs.com/en/guide/behind-proxies.html)
1881 + }
1882 + if (obj.args.sessiontime != null) { sessionOptions.maxAge = (obj.args.sessiontime * 60 * 1000); }
1883 + obj.app.use(obj.session(sessionOptions));
1884 +
1885 + // Add HTTP security headers to all responses
1886 + obj.app.use(function (req, res, next) {
1887 + res.removeHeader("X-Powered-By");
1888 + var domain = req.xdomain = getDomain(req);
1889 +
1890 + // Detect if this is a file sharing domain, if so, just share files.
1891 + if ((domain != null) && (domain.share != null)) {
1892 + var rpath;
1893 + if (domain.dns == null) { rpath = req.url.split('/'); rpath.splice(1, 1); rpath = rpath.join('/'); } else { rpath = req.url; }
1894 + if ((res.headers != null) && (res.headers.upgrade)) {
1895 + // If this is a websocket, stop here.
1896 + res.sendStatus(404);
1897 + } else {
1898 + // Check if the file exists, if so, serve it.
1899 + obj.fs.exists(obj.path.join(domain.share, rpath), function (exists) { if (exists == true) { res.sendfile(rpath, { root: domain.share }); } else { res.sendStatus(404); } });
1900 + }
1901 } else {
1955 - // Use the default server picture
1956 - try { res.sendFile(obj.path.join(__dirname, 'public/images/server-200.jpg')); } catch (e) { res.sendStatus(404); }
1902 + // Two more headers to take a look at:
1903 + // 'Public-Key-Pins': 'pin-sha256="X3pGTSOuJeEVw989IJ/cEtXUEmy52zs1TZQrU06KUKg="; max-age=10'
1904 + // 'strict-transport-security': 'max-age=31536000; includeSubDomains'
1905 + /*
1906 + var headers = null;
1907 + if (obj.args.notls) {
1908 + // Default headers if no TLS is used
1909 + headers = { 'Referrer-Policy': 'no-referrer', 'x-frame-options': 'SAMEORIGIN', 'X-XSS-Protection': '1; mode=block', 'X-Content-Type-Options': 'nosniff', 'Content-Security-Policy': "default-src http: ws: data: 'self';script-src http: 'unsafe-inline';style-src http: 'unsafe-inline'" };
1910 + } else {
1911 + // Default headers if TLS is used
1912 + headers = { 'Referrer-Policy': 'no-referrer', 'x-frame-options': 'SAMEORIGIN', 'X-XSS-Protection': '1; mode=block', 'X-Content-Type-Options': 'nosniff', 'Content-Security-Policy': "default-src https: wss: data: 'self';script-src https: 'unsafe-inline';style-src https: 'unsafe-inline'" };
1913 + }
1914 + if (parent.config.settings.accesscontrolalloworigin != null) { headers['Access-Control-Allow-Origin'] = parent.config.settings.accesscontrolalloworigin; }
1915 + res.set(headers);
1916 + */
1917 + return next();
1918 }
1919 });
1920
1960 - // Receive mesh agent connections
1961 - obj.app.ws(url + 'agent.ashx', function (ws, req) {
1962 - //console.log(++obj.agentConnCount);
1963 - /*
1964 - var ip, port, type;
1965 - if (req.connection) { ip = req.connection.remoteAddress; port = req.connection.remotePort; type = 1; } // HTTP(S) request
1966 - else if (req._socket) { ip = req._socket.remoteAddress; port = req._socket.remotePort; type = 2; } // WebSocket request
1967 - console.log('AgentConnect', ip, port, type);
1968 - */
1969 - try { obj.meshAgentHandler.CreateMeshAgent(obj, obj.db, ws, req, obj.args, getDomain(req)); } catch (e) { console.log(e); }
1970 - });
1921 + // Setup all HTTP handlers
1922 + obj.app.get('/backup.zip', handleBackupRequest);
1923 + obj.app.post('/restoreserver.ashx', handleRestoreRequest);
1924 + if (parent.multiServer != null) { obj.app.ws('/meshserver.ashx', function (ws, req) { parent.multiServer.CreatePeerInServer(parent.multiServer, ws, req); }); }
1925 + for (var i in parent.config.domains) {
1926 + if (parent.config.domains[i].dns != null) { continue; } // This is a subdomain with a DNS name, no added HTTP bindings needed.
1927 + var url = parent.config.domains[i].url;
1928 + obj.app.get(url, handleRootRequest);
1929 + obj.app.get(url + 'terms', handleTermsRequest);
1930 + obj.app.post(url + 'login', handleLoginRequest);
1931 + obj.app.post(url + 'tokenlogin', handleLoginRequest);
1932 + obj.app.get(url + 'logout', handleLogoutRequest);
1933 + obj.app.get(url + 'MeshServerRootCert.cer', handleRootCertRequest);
1934 + obj.app.get(url + 'mescript.ashx', handleMeScriptRequest);
1935 + obj.app.post(url + 'changepassword', handlePasswordChangeRequest);
1936 + obj.app.post(url + 'deleteaccount', handleDeleteAccountRequest);
1937 + obj.app.post(url + 'createaccount', handleCreateAccountRequest);
1938 + obj.app.post(url + 'resetaccount', handleResetAccountRequest);
1939 + obj.app.get(url + 'checkmail', handleCheckMailRequest);
1940 + obj.app.post(url + 'amtevents.ashx', obj.handleAmtEventRequest);
1941 + obj.app.get(url + 'meshagents', obj.handleMeshAgentRequest);
1942 + obj.app.get(url + 'messenger', handleMessengerRequest);
1943 + obj.app.get(url + 'meshosxagent', obj.handleMeshOsxAgentRequest);
1944 + obj.app.get(url + 'meshsettings', obj.handleMeshSettingsRequest);
1945 + obj.app.get(url + 'downloadfile.ashx', handleDownloadFile);
1946 + obj.app.post(url + 'uploadfile.ashx', handleUploadFile);
1947 + obj.app.post(url + 'uploadmeshcorefile.ashx', handleUploadMeshCoreFile);
1948 + obj.app.get(url + 'userfiles/*', handleDownloadUserFiles);
1949 + obj.app.ws(url + 'echo.ashx', handleEchoWebSocket);
1950 + obj.app.ws(url + 'meshrelay.ashx', function (ws, req) { PerformWSSessionAuth(ws, req, true, function (ws1, req1, domain, user, cookie) { obj.meshRelayHandler.CreateMeshRelay(obj, ws1, req1, domain, user, cookie); }); });
1951 + obj.app.get(url + 'webrelay.ashx', function (req, res) { res.send('Websocket connection expected'); });
1952 + obj.app.ws(url + 'webrelay.ashx', function (ws, req) { PerformWSSessionAuth(ws, req, false, handleRelayWebSocket); });
1953 + obj.app.ws(url + 'control.ashx', function (ws, req) { PerformWSSessionAuth(ws, req, false, function (ws1, req1, domain, user, cookie) { obj.meshUserHandler.CreateMeshUser(obj, obj.db, ws1, req1, obj.args, domain, user); }); });
1954 + obj.app.get(url + 'logo.png', handleLogoRequest);
1955 +
1956 + // Server picture
1957 + obj.app.get(url + 'serverpic.ashx', function (req, res) {
1958 + // Check if we have "server.png" in the data folder, if so, use that.
1959 + var p = obj.path.join(obj.parent.datapath, 'server.jpg');
1960 + if (obj.fs.existsSync(p)) {
1961 + // Use the data folder server picture
1962 + try { res.sendFile(p); } catch (e) { res.sendStatus(404); }
1963 + } else {
1964 + // Use the default server picture
1965 + try { res.sendFile(obj.path.join(__dirname, 'public/images/server-200.jpg')); } catch (e) { res.sendStatus(404); }
1966 + }
1967 + });
1968
1972 - // Creates a login token using the user/pass that is passed in as URL arguments.
1973 - // For example: https://localhost/createLoginToken.ashx?user=admin&pass=admin&a=3
1974 - // It's not advised to use this to create login tokens since the URL is often logged and you got credentials in the URL.
1975 - // However, people want it so here it is.
1976 - obj.app.get(url + 'createLoginToken.ashx', function (req, res) {
1977 - // A web socket session can be authenticated in many ways (Default user, session, user/pass and cookie). Check authentication here.
1978 - if ((req.query.user != null) && (req.query.pass != null)) {
1979 - // A user/pass is provided in URL arguments
1980 - obj.authenticate(req.query.user, req.query.pass, getDomain(req), function (err, userid) {
1981 - if ((err == null) && (obj.users[userid])) {
1982 - // User is authenticated, create a token
1983 - var x = { a: 3 }; for (var i in req.query) { if ((i != 'user') && (i != 'pass')) { x[i] = obj.common.toNumber(req.query[i]); } } x.u = userid;
1984 - res.send(obj.parent.encodeCookie(x, obj.parent.loginCookieEncryptionKey));
1985 - } else {
1986 - res.sendStatus(404);
1987 - }
1988 - });
1989 - } else {
1990 - res.sendStatus(404);
1991 - }
1992 - });
1969 + // Receive mesh agent connections
1970 + obj.app.ws(url + 'agent.ashx', function (ws, req) {
1971 + //console.log(++obj.agentConnCount);
1972 + /*
1973 + var ip, port, type;
1974 + if (req.connection) { ip = req.connection.remoteAddress; port = req.connection.remotePort; type = 1; } // HTTP(S) request
1975 + else if (req._socket) { ip = req._socket.remoteAddress; port = req._socket.remotePort; type = 2; } // WebSocket request
1976 + console.log('AgentConnect', ip, port, type);
1977 + */
1978 + try { obj.meshAgentHandler.CreateMeshAgent(obj, obj.db, ws, req, obj.args, getDomain(req)); } catch (e) { console.log(e); }
1979 + });
1980 +
1981 + // Creates a login token using the user/pass that is passed in as URL arguments.
1982 + // For example: https://localhost/createLoginToken.ashx?user=admin&pass=admin&a=3
1983 + // It's not advised to use this to create login tokens since the URL is often logged and you got credentials in the URL.
1984 + // However, people want it so here it is.
1985 + obj.app.get(url + 'createLoginToken.ashx', function (req, res) {
1986 + // A web socket session can be authenticated in many ways (Default user, session, user/pass and cookie). Check authentication here.
1987 + if ((req.query.user != null) && (req.query.pass != null)) {
1988 + // A user/pass is provided in URL arguments
1989 + obj.authenticate(req.query.user, req.query.pass, getDomain(req), function (err, userid) {
1990 + if ((err == null) && (obj.users[userid])) {
1991 + // User is authenticated, create a token
1992 + var x = { a: 3 }; for (var i in req.query) { if ((i != 'user') && (i != 'pass')) { x[i] = obj.common.toNumber(req.query[i]); } } x.u = userid;
1993 + res.send(obj.parent.encodeCookie(x, obj.parent.loginCookieEncryptionKey));
1994 + } else {
1995 + res.sendStatus(404);
1996 + }
1997 + });
1998 + } else {
1999 + res.sendStatus(404);
2000 + }
2001 + });
2002
1994 - obj.app.get(url + 'stop', function (req, res) { res.send('Stopping Server, <a href="' + url + '">click here to login</a>.'); setTimeout(function () { parent.Stop(); }, 500); });
2003 + obj.app.get(url + 'stop', function (req, res) { res.send('Stopping Server, <a href="' + url + '">click here to login</a>.'); setTimeout(function () { parent.Stop(); }, 500); });
2004
1996 - // Indicates to ExpressJS that the public folder should be used to serve static files.
1997 - obj.app.use(url, obj.express.static(obj.path.join(__dirname, 'public')));
2005 + // Indicates to ExpressJS that the public folder should be used to serve static files.
2006 + obj.app.use(url, obj.express.static(obj.path.join(__dirname, 'public')));
2007 + }
2008 +
2009 + // Start server on a free port
2010 + CheckListenPort(obj.args.port, StartWebServer);
2011 }
2012
2013 // Authenticates a session and forwards
@@ -2131,8 +2144,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
2144 agent.send(obj.common.ShortToStr(11) + obj.common.ShortToStr(0)); // Command 11, ask for mesh core hash.
2145 } else if (coretype == 'custom') {
2146 agent.agentCoreCheck = 1000; // Tell the agent object we are using a custom core.
2134 - const hash = obj.crypto.createHash('sha384').update(Buffer.from(core, 'binary')).digest().toString('binary'); // Perform a SHA384 hash on the core module
2135 - agent.send(obj.common.ShortToStr(10) + obj.common.ShortToStr(0) + hash + core); // Send the code module to the agent
2147 + const hash = obj.crypto.createHash('sha384').update(Buffer.from(coredata, 'binary')).digest().toString('binary'); // Perform a SHA384 hash on the core module
2148 + agent.send(obj.common.ShortToStr(10) + obj.common.ShortToStr(0) + hash + coredata); // Send the code module to the agent
2149 }
2150 }
2151 };
@@ -2165,9 +2178,6 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
2178 else if (arguments.length == 7) { console.log(arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6]); }
2179 }
2180
2168 - // Start server on a free port
2169 - CheckListenPort(obj.args.port, StartWebServer);
2170 -
2181 /*
2182 obj.wssessions = {}; // UserId --> Array Of Sessions
2183 obj.wssessions2 = {}; // "UserId + SessionRnd" --> Session (Note that the SessionId is the UserId + / + SessionRnd)