/marauroa/src/marauroa/server/game/messagehandler/SecuredLoginHandler.java
Java | 296 lines | 178 code | 44 blank | 74 comment | 19 complexity | e1f6ae2c4edb0ff5a273cd153e30b4f9 MD5 | raw file
Possible License(s): Apache-2.0, GPL-2.0
- /* $Id: SecuredLoginHandler.java,v 1.10 2010/10/28 22:41:42 nhnb Exp $ */
- /***************************************************************************
- * (C) Copyright 2003-2010 - Marauroa *
- ***************************************************************************
- ***************************************************************************
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- ***************************************************************************/
- package marauroa.server.game.messagehandler;
- import marauroa.common.Configuration;
- import marauroa.common.Log4J;
- import marauroa.common.TimeoutConf;
- import marauroa.common.crypto.Hash;
- import marauroa.common.net.message.*;
- import marauroa.server.db.command.DBCommand;
- import marauroa.server.db.command.DBCommandQueue;
- import marauroa.server.game.GameServerManager;
- import marauroa.server.game.container.ClientState;
- import marauroa.server.game.container.PlayerEntry;
- import marauroa.server.game.container.PlayerEntry.SecuredLoginInfo;
- import marauroa.server.game.container.PlayerEntryContainer;
- import marauroa.server.game.dbcommand.LoadAllActiveCharactersCommand;
- import marauroa.server.game.dbcommand.LoginCommand;
- import marauroa.server.game.rp.RPServerManager;
- import org.apache.log4j.Logger;
- import java.io.IOException;
- import java.io.UnsupportedEncodingException;
- import java.nio.channels.SocketChannel;
- import java.util.ArrayList;
- import java.util.Enumeration;
- import java.util.List;
- /**
- * Complete the login stage. It will either success and
- * game continue or fail and resources for this player
- * are freed.
- */
- class SecuredLoginHandler extends MessageHandler implements DelayedEventHandler {
- /**
- * the logger instance.
- */
- private static final marauroa.common.Logger logger = Log4J.getLogger(GameServerManager.class);
- /**
- * This last method completes the login process.
- *
- * @param msg the final message the contains the encrypted password.
- */
- @Override
- public void process(Message msg) {
- int clientid = msg.getClientID();
- PlayerEntry entry = playerContainer.get(clientid);
- // verify event
- if (!isValidEvent(msg, entry, ClientState.CONNECTION_ACCEPTED)) {
- return;
- }
- SecuredLoginInfo info = fillLoginInfo(msg, entry);
- DBCommand command = new LoginCommand(info,
- this, entry.clientid,
- msg.getSocketChannel(), msg.getProtocolVersion());
- DBCommandQueue.get().enqueue(command);
- }
- private void completeLogin(SocketChannel channel, int clientid, int protocolVersion, SecuredLoginInfo info, List<String> previousLogins) {
- PlayerEntry entry = PlayerEntryContainer.getContainer().get(clientid);
- logger.debug("Correct username/password");
- if (entry == null) {
- logger.warn("Did not find PlayerEntry in completeLogin, timeout? " + info);
- return;
- }
- /* Correct: The login is correct */
- entry.username = info.username;
- /* We clean the login information as it is not longer useful. */
- entry.loginInformations = null;
- stats.add("Players login", 1);
- /* Send player the Login ACK message */
- MessageS2CLoginACK msgLoginACK = new MessageS2CLoginACK(channel, previousLogins);
- msgLoginACK.setClientID(clientid);
- msgLoginACK.setProtocolVersion(protocolVersion);
- netMan.sendMessage(msgLoginACK);
- /* Send player the ServerInfo */
- MessageS2CServerInfo msgServerInfo = new MessageS2CServerInfo(channel, ServerInfo.get());
- msgServerInfo.setClientID(clientid);
- msgServerInfo.setProtocolVersion(protocolVersion);
- netMan.sendMessage(msgServerInfo);
- /* Build player character list and send it to client */
- DBCommand command = new LoadAllActiveCharactersCommand(entry.username,
- new SendCharacterListHandler(netMan, protocolVersion),
- clientid, channel, protocolVersion);
- DBCommandQueue.get().enqueue(command);
- entry.state = ClientState.LOGIN_COMPLETE;
- }
- private SecuredLoginInfo fillLoginInfo(Message msg, PlayerEntry entry) {
- SecuredLoginInfo info = entry.loginInformations;
- if (msg instanceof MessageC2SLoginSendNonceNameAndPassword) {
- MessageC2SLoginSendNonceNameAndPassword msgLogin = (MessageC2SLoginSendNonceNameAndPassword) msg;
- info.clientNonce = msgLogin.getHash();
- info.username = msgLogin.getUsername();
- info.password = msgLogin.getPassword();
- } else {
- MessageC2SLoginSendNonceNamePasswordAndSeed msgLogin = (MessageC2SLoginSendNonceNamePasswordAndSeed) msg;
- info.clientNonce = msgLogin.getHash();
- info.username = msgLogin.getUsername();
- info.password = msgLogin.getPassword();
- info.seed = decode(info, msgLogin.getSeed());
- }
- return info;
- }
- private String decode(SecuredLoginInfo info, byte[] data) {
- byte[] b1 = info.key.decodeByteArray(data);
- byte[] b2 = Hash.xor(info.clientNonce, info.serverNonce);
- if (b2 == null) {
- logger.debug("B2 is null");
- return null;
- }
- byte[] result = Hash.xor(b1, b2);
- try {
- return new String(result, "UTF-8");
- } catch (UnsupportedEncodingException e) {
- logger.error(e, e);
- return null;
- }
- }
- /**
- * This class stores Server information like
- * <ul>
- * <li>Type of game
- * <li>Server name
- * <li>Server version number
- * <li>Server contact information.
- * </ul>
- */
- private static class ServerInfo {
- private static Logger infoLogger = Logger.getLogger(ServerInfo.class);
- private static Configuration config;
- static {
- try {
- config = Configuration.getConfiguration();
- // just check if mandatory properties are set
- config.get("server_typeGame");
- config.get("server_name");
- config.get("server_version");
- config.get("server_contact");
- } catch (Exception e) {
- infoLogger.error("ERROR: Unable to load Server info", e);
- }
- }
- /**
- * This method builds a String[] from the properties used in Server Info
- *
- * @return Server Info
- */
- public static String[] get() {
- List<String> l_result = new ArrayList<String>();
- Enumeration<?> props = config.propertyNames();
- while (props.hasMoreElements()) {
- String prop_name = String.valueOf(props.nextElement());
- if (prop_name.startsWith("server_")) {
- l_result.add(config.get(prop_name));
- }
- }
- String[] result = new String[l_result.size()];
- return l_result.toArray(result);
- }
- }
- public void handleDelayedEvent(RPServerManager rpMan, Object data) {
- try {
- LoginCommand command = (LoginCommand) data;
- SecuredLoginInfo info = command.getInfo();
- /*
- * We check that player didn't failed too many time the login, if it
- * did, we reject the login request until the block pass.
- */
- if (command.getFailReason() == MessageS2CLoginNACK.Reasons.TOO_MANY_TRIES) {
- logger.debug("Blocked account for player " + info.username + " and/or address " + info.address);
- /* Send player the Login NACK message */
- MessageS2CLoginNACK msgLoginNACK = new MessageS2CLoginNACK(command.getChannel(),
- MessageS2CLoginNACK.Reasons.TOO_MANY_TRIES);
- msgLoginNACK.setProtocolVersion(command.getProtocolVersion());
- netMan.sendMessage(msgLoginNACK);
- /*
- * Disconnect player of server.
- */
- netMan.disconnectClient(command.getChannel());
- return;
- }
- /*
- * We verify the username and the password to make sure player is
- * who he/she says he/she is.
- */
- if (command.getFailReason() != null) {
- /*
- * If the verification fails we send player a NACK and record
- * the event
- */
- logger.debug("Incorrect username/password for player " + info.username);
- stats.add("Players invalid login", 1);
- /* Send player the Login NACK message */
- if (info.reason == null) {
- info.reason = MessageS2CLoginNACK.Reasons.USERNAME_WRONG;
- }
- MessageS2CLoginNACK msgLoginNACK = new MessageS2CLoginNACK(command.getChannel(),
- info.reason);
- msgLoginNACK.setProtocolVersion(command.getProtocolVersion());
- netMan.sendMessage(msgLoginNACK);
- playerContainer.remove(command.getClientid());
- return;
- }
- /*
- * We check now the account is not banned or inactive.
- */
- if (command.getFailMessage() != null) {
- logger.info("Banned/Inactive account for player " + info.username + ": " + command.getFailMessage());
- /* Send player the Login NACK message */
- MessageS2CLoginMessageNACK msgLoginMessageNACK = new MessageS2CLoginMessageNACK(command.getChannel(), command.getFailMessage());
- msgLoginMessageNACK.setProtocolVersion(command.getProtocolVersion());
- netMan.sendMessage(msgLoginMessageNACK);
- /*
- * Disconnect player of server.
- */
- netMan.disconnectClient(command.getChannel());
- return;
- }
- /* Now we count the number of connections from this ip-address */
- int count = info.countConnectionsFromSameIPAddress(playerContainer);
- Configuration conf = Configuration.getConfiguration();
- int limit = conf.getInt("parallel_connection_limit", TimeoutConf.PARALLEL_CONNECTION_LIMIT);
- if (count > limit) {
- String whiteList = "," + conf.get("ip_whitelist", "127.0.0.1") + ",";
- if (whiteList.indexOf("," + info.address + ",") < 0) {
- logger.info("to many parallel connections from " + info.address + " rejecting login of " + info.username);
- /* Send player the Login NACK message */
- MessageS2CLoginMessageNACK msgLoginMessageNACK = new MessageS2CLoginMessageNACK(command.getChannel(),
- "There are too many connections from your ip-address.\nPlease contact /support, if you are at a conference or something similar.");
- msgLoginMessageNACK.setProtocolVersion(command.getProtocolVersion());
- netMan.sendMessage(msgLoginMessageNACK);
- // Disconnect player of server.
- netMan.disconnectClient(command.getChannel());
- return;
- }
- }
- /* Obtain previous logins attemps */
- List<String> previousLogins = command.getPreviousLogins();
- completeLogin(command.getChannel(), command.getClientid(), command.getProtocolVersion(), info, previousLogins);
- } catch (IOException e) {
- logger.error("error while processing SecuredLoginEvent: " + data, e);
- } catch (RuntimeException e) {
- logger.error("error while processing SecuredLoginEvent: " + data, e);
- }
- }
- }