server.js 6.7 KB

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