PageRenderTime 79ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/marauroa/src/marauroa/server/game/messagehandler/SecuredLoginHandler.java

http://wastelanders.googlecode.com/
Java | 296 lines | 178 code | 44 blank | 74 comment | 19 complexity | e1f6ae2c4edb0ff5a273cd153e30b4f9 MD5 | raw file
Possible License(s): Apache-2.0, GPL-2.0
  1. /* $Id: SecuredLoginHandler.java,v 1.10 2010/10/28 22:41:42 nhnb Exp $ */
  2. /***************************************************************************
  3. * (C) Copyright 2003-2010 - Marauroa *
  4. ***************************************************************************
  5. ***************************************************************************
  6. * *
  7. * This program is free software; you can redistribute it and/or modify *
  8. * it under the terms of the GNU General Public License as published by *
  9. * the Free Software Foundation; either version 2 of the License, or *
  10. * (at your option) any later version. *
  11. * *
  12. ***************************************************************************/
  13. package marauroa.server.game.messagehandler;
  14. import marauroa.common.Configuration;
  15. import marauroa.common.Log4J;
  16. import marauroa.common.TimeoutConf;
  17. import marauroa.common.crypto.Hash;
  18. import marauroa.common.net.message.*;
  19. import marauroa.server.db.command.DBCommand;
  20. import marauroa.server.db.command.DBCommandQueue;
  21. import marauroa.server.game.GameServerManager;
  22. import marauroa.server.game.container.ClientState;
  23. import marauroa.server.game.container.PlayerEntry;
  24. import marauroa.server.game.container.PlayerEntry.SecuredLoginInfo;
  25. import marauroa.server.game.container.PlayerEntryContainer;
  26. import marauroa.server.game.dbcommand.LoadAllActiveCharactersCommand;
  27. import marauroa.server.game.dbcommand.LoginCommand;
  28. import marauroa.server.game.rp.RPServerManager;
  29. import org.apache.log4j.Logger;
  30. import java.io.IOException;
  31. import java.io.UnsupportedEncodingException;
  32. import java.nio.channels.SocketChannel;
  33. import java.util.ArrayList;
  34. import java.util.Enumeration;
  35. import java.util.List;
  36. /**
  37. * Complete the login stage. It will either success and
  38. * game continue or fail and resources for this player
  39. * are freed.
  40. */
  41. class SecuredLoginHandler extends MessageHandler implements DelayedEventHandler {
  42. /**
  43. * the logger instance.
  44. */
  45. private static final marauroa.common.Logger logger = Log4J.getLogger(GameServerManager.class);
  46. /**
  47. * This last method completes the login process.
  48. *
  49. * @param msg the final message the contains the encrypted password.
  50. */
  51. @Override
  52. public void process(Message msg) {
  53. int clientid = msg.getClientID();
  54. PlayerEntry entry = playerContainer.get(clientid);
  55. // verify event
  56. if (!isValidEvent(msg, entry, ClientState.CONNECTION_ACCEPTED)) {
  57. return;
  58. }
  59. SecuredLoginInfo info = fillLoginInfo(msg, entry);
  60. DBCommand command = new LoginCommand(info,
  61. this, entry.clientid,
  62. msg.getSocketChannel(), msg.getProtocolVersion());
  63. DBCommandQueue.get().enqueue(command);
  64. }
  65. private void completeLogin(SocketChannel channel, int clientid, int protocolVersion, SecuredLoginInfo info, List<String> previousLogins) {
  66. PlayerEntry entry = PlayerEntryContainer.getContainer().get(clientid);
  67. logger.debug("Correct username/password");
  68. if (entry == null) {
  69. logger.warn("Did not find PlayerEntry in completeLogin, timeout? " + info);
  70. return;
  71. }
  72. /* Correct: The login is correct */
  73. entry.username = info.username;
  74. /* We clean the login information as it is not longer useful. */
  75. entry.loginInformations = null;
  76. stats.add("Players login", 1);
  77. /* Send player the Login ACK message */
  78. MessageS2CLoginACK msgLoginACK = new MessageS2CLoginACK(channel, previousLogins);
  79. msgLoginACK.setClientID(clientid);
  80. msgLoginACK.setProtocolVersion(protocolVersion);
  81. netMan.sendMessage(msgLoginACK);
  82. /* Send player the ServerInfo */
  83. MessageS2CServerInfo msgServerInfo = new MessageS2CServerInfo(channel, ServerInfo.get());
  84. msgServerInfo.setClientID(clientid);
  85. msgServerInfo.setProtocolVersion(protocolVersion);
  86. netMan.sendMessage(msgServerInfo);
  87. /* Build player character list and send it to client */
  88. DBCommand command = new LoadAllActiveCharactersCommand(entry.username,
  89. new SendCharacterListHandler(netMan, protocolVersion),
  90. clientid, channel, protocolVersion);
  91. DBCommandQueue.get().enqueue(command);
  92. entry.state = ClientState.LOGIN_COMPLETE;
  93. }
  94. private SecuredLoginInfo fillLoginInfo(Message msg, PlayerEntry entry) {
  95. SecuredLoginInfo info = entry.loginInformations;
  96. if (msg instanceof MessageC2SLoginSendNonceNameAndPassword) {
  97. MessageC2SLoginSendNonceNameAndPassword msgLogin = (MessageC2SLoginSendNonceNameAndPassword) msg;
  98. info.clientNonce = msgLogin.getHash();
  99. info.username = msgLogin.getUsername();
  100. info.password = msgLogin.getPassword();
  101. } else {
  102. MessageC2SLoginSendNonceNamePasswordAndSeed msgLogin = (MessageC2SLoginSendNonceNamePasswordAndSeed) msg;
  103. info.clientNonce = msgLogin.getHash();
  104. info.username = msgLogin.getUsername();
  105. info.password = msgLogin.getPassword();
  106. info.seed = decode(info, msgLogin.getSeed());
  107. }
  108. return info;
  109. }
  110. private String decode(SecuredLoginInfo info, byte[] data) {
  111. byte[] b1 = info.key.decodeByteArray(data);
  112. byte[] b2 = Hash.xor(info.clientNonce, info.serverNonce);
  113. if (b2 == null) {
  114. logger.debug("B2 is null");
  115. return null;
  116. }
  117. byte[] result = Hash.xor(b1, b2);
  118. try {
  119. return new String(result, "UTF-8");
  120. } catch (UnsupportedEncodingException e) {
  121. logger.error(e, e);
  122. return null;
  123. }
  124. }
  125. /**
  126. * This class stores Server information like
  127. * <ul>
  128. * <li>Type of game
  129. * <li>Server name
  130. * <li>Server version number
  131. * <li>Server contact information.
  132. * </ul>
  133. */
  134. private static class ServerInfo {
  135. private static Logger infoLogger = Logger.getLogger(ServerInfo.class);
  136. private static Configuration config;
  137. static {
  138. try {
  139. config = Configuration.getConfiguration();
  140. // just check if mandatory properties are set
  141. config.get("server_typeGame");
  142. config.get("server_name");
  143. config.get("server_version");
  144. config.get("server_contact");
  145. } catch (Exception e) {
  146. infoLogger.error("ERROR: Unable to load Server info", e);
  147. }
  148. }
  149. /**
  150. * This method builds a String[] from the properties used in Server Info
  151. *
  152. * @return Server Info
  153. */
  154. public static String[] get() {
  155. List<String> l_result = new ArrayList<String>();
  156. Enumeration<?> props = config.propertyNames();
  157. while (props.hasMoreElements()) {
  158. String prop_name = String.valueOf(props.nextElement());
  159. if (prop_name.startsWith("server_")) {
  160. l_result.add(config.get(prop_name));
  161. }
  162. }
  163. String[] result = new String[l_result.size()];
  164. return l_result.toArray(result);
  165. }
  166. }
  167. public void handleDelayedEvent(RPServerManager rpMan, Object data) {
  168. try {
  169. LoginCommand command = (LoginCommand) data;
  170. SecuredLoginInfo info = command.getInfo();
  171. /*
  172. * We check that player didn't failed too many time the login, if it
  173. * did, we reject the login request until the block pass.
  174. */
  175. if (command.getFailReason() == MessageS2CLoginNACK.Reasons.TOO_MANY_TRIES) {
  176. logger.debug("Blocked account for player " + info.username + " and/or address " + info.address);
  177. /* Send player the Login NACK message */
  178. MessageS2CLoginNACK msgLoginNACK = new MessageS2CLoginNACK(command.getChannel(),
  179. MessageS2CLoginNACK.Reasons.TOO_MANY_TRIES);
  180. msgLoginNACK.setProtocolVersion(command.getProtocolVersion());
  181. netMan.sendMessage(msgLoginNACK);
  182. /*
  183. * Disconnect player of server.
  184. */
  185. netMan.disconnectClient(command.getChannel());
  186. return;
  187. }
  188. /*
  189. * We verify the username and the password to make sure player is
  190. * who he/she says he/she is.
  191. */
  192. if (command.getFailReason() != null) {
  193. /*
  194. * If the verification fails we send player a NACK and record
  195. * the event
  196. */
  197. logger.debug("Incorrect username/password for player " + info.username);
  198. stats.add("Players invalid login", 1);
  199. /* Send player the Login NACK message */
  200. if (info.reason == null) {
  201. info.reason = MessageS2CLoginNACK.Reasons.USERNAME_WRONG;
  202. }
  203. MessageS2CLoginNACK msgLoginNACK = new MessageS2CLoginNACK(command.getChannel(),
  204. info.reason);
  205. msgLoginNACK.setProtocolVersion(command.getProtocolVersion());
  206. netMan.sendMessage(msgLoginNACK);
  207. playerContainer.remove(command.getClientid());
  208. return;
  209. }
  210. /*
  211. * We check now the account is not banned or inactive.
  212. */
  213. if (command.getFailMessage() != null) {
  214. logger.info("Banned/Inactive account for player " + info.username + ": " + command.getFailMessage());
  215. /* Send player the Login NACK message */
  216. MessageS2CLoginMessageNACK msgLoginMessageNACK = new MessageS2CLoginMessageNACK(command.getChannel(), command.getFailMessage());
  217. msgLoginMessageNACK.setProtocolVersion(command.getProtocolVersion());
  218. netMan.sendMessage(msgLoginMessageNACK);
  219. /*
  220. * Disconnect player of server.
  221. */
  222. netMan.disconnectClient(command.getChannel());
  223. return;
  224. }
  225. /* Now we count the number of connections from this ip-address */
  226. int count = info.countConnectionsFromSameIPAddress(playerContainer);
  227. Configuration conf = Configuration.getConfiguration();
  228. int limit = conf.getInt("parallel_connection_limit", TimeoutConf.PARALLEL_CONNECTION_LIMIT);
  229. if (count > limit) {
  230. String whiteList = "," + conf.get("ip_whitelist", "127.0.0.1") + ",";
  231. if (whiteList.indexOf("," + info.address + ",") < 0) {
  232. logger.info("to many parallel connections from " + info.address + " rejecting login of " + info.username);
  233. /* Send player the Login NACK message */
  234. MessageS2CLoginMessageNACK msgLoginMessageNACK = new MessageS2CLoginMessageNACK(command.getChannel(),
  235. "There are too many connections from your ip-address.\nPlease contact /support, if you are at a conference or something similar.");
  236. msgLoginMessageNACK.setProtocolVersion(command.getProtocolVersion());
  237. netMan.sendMessage(msgLoginMessageNACK);
  238. // Disconnect player of server.
  239. netMan.disconnectClient(command.getChannel());
  240. return;
  241. }
  242. }
  243. /* Obtain previous logins attemps */
  244. List<String> previousLogins = command.getPreviousLogins();
  245. completeLogin(command.getChannel(), command.getClientid(), command.getProtocolVersion(), info, previousLogins);
  246. } catch (IOException e) {
  247. logger.error("error while processing SecuredLoginEvent: " + data, e);
  248. } catch (RuntimeException e) {
  249. logger.error("error while processing SecuredLoginEvent: " + data, e);
  250. }
  251. }
  252. }