#!/usr/bin/env node var net = require('net'), mime = require('mime-types'), fs = require('fs'), url = require('url'), http = require('http'), websocket = require('websocket'), ssh2 = require('ssh2'), crypto = require('crypto'); config = require('../config'); libs = require('./libs'); world = require('./world'); try { var banner = fs.readFileSync(__dirname + "/../banner.txt", "utf-8"); config.welcomeBanner = banner; world.mud_load(); } catch (error) { libs.Output.toServerError("MUD cannot be loaded.", error); process.exit(1); } var telnetserver = net.createServer(function(socket) { var login = new libs.TelnetLogin(socket); login.attach(); socket.on('data', function(data) { try { login.receive(data); } catch (error) { libs.Output.toServerError("Error in telnet socket when receiving data", error); libs.Output.worldToMobiles("You sense a {error}tear{/error} in the fabric of reality."); } }); socket.on('end', function() { login.disconnect(); }); socket.on('error', function(error) { if(error.code == "EPIPE") { libs.Output.toServerError("Telnet socket "+socket.nickname+" was written to after being closed.", error); } else if (error.code == "ECONNRESET") { libs.Output.toServerError("Telnet socket connection reset.", error); } else { libs.Output.toServerError("Unknown telnet socket error", error); } }); socket.setTimeout(config.telnettimeout); socket.on('timeout', function() { socket.write('\nConnection timed out.\n'); socket.end(); }); }); telnetserver.on('error', function(error) { if(error.code == "EADDRINUSE") { libs.Output.toServerError("There is already a server running!", error); process.exit(1); } else { libs.Output.toServerError("Unknown telnet server error", error); } }); var webserver = http.createServer(function(request, response) { if(!config.enableWebserver) { return; } var parsedUrl = url.parse(request.url, true); var query = parsedUrl.query; var pathname = parsedUrl.pathname; var configData = fs.readFileSync(__dirname + "/../config.json", "utf-8"); var webconfig = JSON.parse(configData); var filename = webconfig.websitePath; if(config.routes.hasOwnProperty(pathname)) { filename += webconfig.routes[pathname]; } else { filename += pathname; } fs.stat(filename, function(err, stat) { if(err != null) { if(err.code == 'ENOENT') { response.writeHead(404, {'Content-Type': 'text/html'}); response.write("404 Page Not Found"); } else { response.writeHead(500, {'Content-Type': 'text/html'}); response.write("500 Server Error"); } response.end(); return; } fs.readFile(filename, function(err, data) { var mimeType = mime.lookup(filename); response.writeHead(200, {'Content-Type': mimeType}); response.write(data); response.end(); }); }); }); // create the server var websocketserver = new websocket.server({ httpServer: webserver }); // WebSocket server websocketserver.on('request', function(request) { var connection = request.accept(null, request.origin); var login = new libs.WebsocketLogin(connection); login.attach(); connection.on('message', function(message) { if (message.type === 'utf8') { try { login.receive(message.utf8Data); } catch (error) { libs.Output.toServerError("Error in websocket socket when receiving data", error); libs.Output.worldToMobiles("You sense a {error}tear{/error} in the fabric of reality."); } } else { libs.Output.toServer(message); } }); connection.on('close', function(connection) { login.disconnect(); }); }); var buffersEqual = function(buffer1, buffer2) { return 0 == Buffer.compare(buffer1, buffer2); } var sshserver = new ssh2.Server({ hostKeys: [fs.readFileSync(config.sshKeyPath)] }, function(client) { var userData = null; client.on('authentication', function(ctx) { userData = world.mud_getUserData(ctx.username); switch(ctx.method) { case "password": if(world.mud_isValidLogin(userData, ctx.password)) { ctx.accept(); } else { ctx.reject(); } break; case "publickey": var pubKeys = []; var validCheck = false; for(var index in userData.pubKeys) { var pubKey = ssh2.utils.genPublicKey(ssh2.utils.parseKey(userData.pubKeys[index])); if(ctx.key.algo === pubKey.fulltype && buffersEqual(ctx.key.data, pubKey.public)) { if(ctx.signature) { var verifier = crypto.createVerify(ctx.sigAlgo); verifier.update(ctx.blob); if (verifier.verify(pubKey.publicOrig, ctx.signature)) { ctx.accept(); validCheck = true; } } else { ctx.accept(); validCheck = true; } } } if(!validCheck) { ctx.reject(); } break; case "none": ctx.reject(['publickey','password','keyboard-interactive']); break; default: ctx.reject(); break; } }); client.on('ready', function() { client.on('session', function(accept, reject) { var session = accept(); session.on("pty", function(accept, reject, info) { //todo: some kinda screen resolution config }); session.on("shell", function(accept, reject) { var channel = accept(); login = new libs.SSHLogin(channel); try { login.attach(userData); } catch (error) { libs.Output.toServerError("Error when attaching user in SSH", error); libs.Output.worldToMobiles("You sense a {error}ripple{/error} in the fabric of reality."); } channel.on("data", function(data){ try { login.receive(data); } catch (error) { libs.Output.toServerError("Error in ssh socket when receiving data", error); libs.Output.worldToMobiles("You sense a {error}tear{/error} in the fabric of reality."); } }); channel.on("close", function(data){ login.disconnect(); }); }); session.on("close", function() { //todo: what do? }); }); }); client.on('end', function() { //todo: what do? }); }); telnetserver.listen(config.telnetport, config.telnetlisten, function() { libs.Output.toServer("Telnet server listening on " + config.telnetlisten + ":" + config.telnetport); }); webserver.listen(config.websocketport, config.websocketlisten, function() { libs.Output.toServer("Websocket server listening on " + config.websocketlisten + ":" + config.websocketport); }); sshserver.listen(config.sshport, config.sshlisten, function() { libs.Output.toServer("SSH server listening on " + config.sshlisten + ":" + config.sshport); }); process.stdin.resume(); process.stdin.setEncoding('utf8'); process.stdin.on('data', function (data) { var input = data.trim().split(" "); switch(input.shift()) { case "say": libs.Output.worldToMobiles(input.join(" ")); break; case "exit": case "quit": case "stop": libs.Output.worldToMobiles("The server is shutting down!"); for(var playerIndex in session.players) { var player = session.players[playerIndex]; player.quit(); } process.exit(0); break; case "restart": for(var playerIndex in session.players) { var player = session.players[playerIndex]; player.quit(); } world.mud_load(); break; } });