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, '&').replace(/>/g, '>').replace(/</g, '<').replace(/"/g, '"').replace(/'/g, '''); if (typeof x == "boolean") return x; if (typeof x == "number") return x; }
176
//function EscapeHtmlBreaks(x) { if (typeof x == "string") return x.replace(/&/g, '&').replace(/>/g, '>').replace(/</g, '<').replace(/"/g, '"').replace(/'/g, ''').replace(/\r/g, '<br />').replace(/\n/g, '').replace(/\t/g, ' '); 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)