server.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. #!/usr/bin/env node
  2. var net = require('net'),
  3. mime = require('mime-types'),
  4. fs = require('fs'),
  5. url = require('url'),
  6. http = require('http'),
  7. websocket = require('websocket'),
  8. ssh2 = require('ssh2'),
  9. crypto = require('crypto');
  10. config = require('../config');
  11. libs = require('./libs');
  12. world = require('./world');
  13. try {
  14. var banner = fs.readFileSync(__dirname + "/../banner.txt", "utf-8");
  15. config.welcomeBanner = banner;
  16. world.mud_load();
  17. } catch (error) {
  18. libs.Output.toServerError("MUD cannot be loaded.", error);
  19. process.exit(1);
  20. }
  21. var telnetserver = net.createServer(function(socket) {
  22. var login = new libs.TelnetLogin(socket);
  23. login.attach();
  24. socket.on('data', function(data) {
  25. try {
  26. login.receive(data);
  27. } catch (error) {
  28. libs.Output.toServerError("Error in telnet socket when receiving data", error);
  29. libs.Output.worldToMobiles("You sense a {error}tear{/error} in the fabric of reality.");
  30. }
  31. });
  32. socket.on('end', function() {
  33. login.disconnect();
  34. });
  35. socket.on('error', function(error) {
  36. if(error.code == "EPIPE") {
  37. libs.Output.toServerError("Telnet socket "+socket.nickname+" was written to after being closed.", error);
  38. } else if (error.code == "ECONNRESET") {
  39. libs.Output.toServerError("Telnet socket connection reset.", error);
  40. } else {
  41. libs.Output.toServerError("Unknown telnet socket error", error);
  42. }
  43. });
  44. socket.setTimeout(config.telnettimeout);
  45. socket.on('timeout', function() {
  46. socket.write('\nConnection timed out.\n');
  47. socket.end();
  48. });
  49. });
  50. telnetserver.on('error', function(error) {
  51. if(error.code == "EADDRINUSE") {
  52. libs.Output.toServerError("There is already a server running!", error);
  53. process.exit(1);
  54. } else {
  55. libs.Output.toServerError("Unknown telnet server error", error);
  56. }
  57. });
  58. var webserver = http.createServer(function(request, response) {
  59. if(!config.enableWebserver) {
  60. return;
  61. }
  62. var parsedUrl = url.parse(request.url, true);
  63. var query = parsedUrl.query;
  64. var pathname = parsedUrl.pathname;
  65. var configData = fs.readFileSync(__dirname + "/../config.json", "utf-8");
  66. var webconfig = JSON.parse(configData);
  67. var filename = webconfig.websitePath;
  68. if(config.routes.hasOwnProperty(pathname)) {
  69. filename += webconfig.routes[pathname];
  70. } else {
  71. filename += pathname;
  72. }
  73. fs.stat(filename, function(err, stat) {
  74. if(err != null) {
  75. if(err.code == 'ENOENT') {
  76. response.writeHead(404, {'Content-Type': 'text/html'});
  77. response.write("404 Page Not Found");
  78. } else {
  79. response.writeHead(500, {'Content-Type': 'text/html'});
  80. response.write("500 Server Error");
  81. }
  82. response.end();
  83. return;
  84. }
  85. fs.readFile(filename, function(err, data) {
  86. var mimeType = mime.lookup(filename);
  87. response.writeHead(200, {'Content-Type': mimeType});
  88. response.write(data);
  89. response.end();
  90. });
  91. });
  92. });
  93. // create the server
  94. var websocketserver = new websocket.server({
  95. httpServer: webserver
  96. });
  97. // WebSocket server
  98. websocketserver.on('request', function(request) {
  99. var connection = request.accept(null, request.origin);
  100. var login = new libs.WebsocketLogin(connection);
  101. login.attach();
  102. connection.on('message', function(message) {
  103. if (message.type === 'utf8') {
  104. try {
  105. login.receive(message.utf8Data);
  106. } catch (error) {
  107. libs.Output.toServerError("Error in websocket socket when receiving data", error);
  108. libs.Output.worldToMobiles("You sense a {error}tear{/error} in the fabric of reality.");
  109. }
  110. } else {
  111. libs.Output.toServer(message);
  112. }
  113. });
  114. connection.on('close', function(connection) {
  115. login.disconnect();
  116. });
  117. });
  118. var buffersEqual = function(buffer1, buffer2) {
  119. return 0 == Buffer.compare(buffer1, buffer2);
  120. }
  121. var sshserver = new ssh2.Server({
  122. hostKeys: [fs.readFileSync(config.sshKeyPath)]
  123. }, function(client) {
  124. var userData = null;
  125. client.on('authentication', function(ctx) {
  126. userData = world.mud_getUserData(ctx.username);
  127. switch(ctx.method) {
  128. case "password":
  129. if(world.mud_isValidLogin(userData, ctx.password)) {
  130. ctx.accept();
  131. } else {
  132. ctx.reject();
  133. }
  134. break;
  135. case "publickey":
  136. var pubKeys = [];
  137. var validCheck = false;
  138. for(var index in userData.pubKeys) {
  139. var pubKey = ssh2.utils.genPublicKey(ssh2.utils.parseKey(userData.pubKeys[index]));
  140. if(ctx.key.algo === pubKey.fulltype && buffersEqual(ctx.key.data, pubKey.public)) {
  141. if(ctx.signature) {
  142. var verifier = crypto.createVerify(ctx.sigAlgo);
  143. verifier.update(ctx.blob);
  144. if (verifier.verify(pubKey.publicOrig, ctx.signature)) {
  145. ctx.accept();
  146. validCheck = true;
  147. }
  148. } else {
  149. ctx.accept();
  150. validCheck = true;
  151. }
  152. }
  153. }
  154. if(!validCheck) {
  155. ctx.reject();
  156. }
  157. break;
  158. case "none":
  159. ctx.reject(['publickey','password','keyboard-interactive']);
  160. break;
  161. default:
  162. ctx.reject();
  163. break;
  164. }
  165. });
  166. client.on('ready', function() {
  167. client.on('session', function(accept, reject) {
  168. var session = accept();
  169. session.on("pty", function(accept, reject, info) {
  170. //todo: some kinda screen resolution config
  171. });
  172. session.on("shell", function(accept, reject) {
  173. var channel = accept();
  174. login = new libs.SSHLogin(channel);
  175. try {
  176. login.attach(userData);
  177. } catch (error) {
  178. libs.Output.toServerError("Error when attaching user in SSH", error);
  179. libs.Output.worldToMobiles("You sense a {error}ripple{/error} in the fabric of reality.");
  180. }
  181. channel.on("data", function(data){
  182. try {
  183. login.receive(data);
  184. } catch (error) {
  185. libs.Output.toServerError("Error in ssh socket when receiving data", error);
  186. libs.Output.worldToMobiles("You sense a {error}tear{/error} in the fabric of reality.");
  187. }
  188. });
  189. channel.on("close", function(data){
  190. login.disconnect();
  191. });
  192. });
  193. session.on("close", function() {
  194. //todo: what do?
  195. });
  196. });
  197. });
  198. client.on('end', function() {
  199. //todo: what do?
  200. });
  201. });
  202. telnetserver.listen(config.telnetport, config.telnetlisten, function() {
  203. libs.Output.toServer("Telnet server listening on " + config.telnetlisten + ":" + config.telnetport);
  204. });
  205. webserver.listen(config.websocketport, config.websocketlisten, function() {
  206. libs.Output.toServer("Websocket server listening on " + config.websocketlisten + ":" + config.websocketport);
  207. });
  208. sshserver.listen(config.sshport, config.sshlisten, function() {
  209. libs.Output.toServer("SSH server listening on " + config.sshlisten + ":" + config.sshport);
  210. });
  211. process.stdin.resume();
  212. process.stdin.setEncoding('utf8');
  213. process.stdin.on('data', function (data) {
  214. var input = data.trim().split(" ");
  215. switch(input.shift()) {
  216. case "say":
  217. libs.Output.worldToMobiles(input.join(" "));
  218. break;
  219. case "exit":
  220. case "quit":
  221. case "stop":
  222. libs.Output.worldToMobiles("The server is shutting down!");
  223. for(var playerIndex in session.players) {
  224. var player = session.players[playerIndex];
  225. player.quit();
  226. }
  227. process.exit(0);
  228. break;
  229. case "restart":
  230. for(var playerIndex in session.players) {
  231. var player = session.players[playerIndex];
  232. player.quit();
  233. }
  234. world.mud_load();
  235. break;
  236. }
  237. });