Package marauroa.server.game.messagehandler

Source Code of marauroa.server.game.messagehandler.SecuredLoginHandler$ServerInfo

/* $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 java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import marauroa.common.Configuration;
import marauroa.common.Log4J;
import marauroa.common.TimeoutConf;
import marauroa.common.crypto.Hash;
import marauroa.common.net.message.Message;
import marauroa.common.net.message.MessageC2SLoginSendNonceNameAndPassword;
import marauroa.common.net.message.MessageC2SLoginSendNonceNamePasswordAndSeed;
import marauroa.common.net.message.MessageS2CLoginACK;
import marauroa.common.net.message.MessageS2CLoginMessageNACK;
import marauroa.common.net.message.MessageS2CLoginNACK;
import marauroa.common.net.message.MessageS2CServerInfo;
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;

/**
* 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);
    }
  }
}
TOP

Related Classes of marauroa.server.game.messagehandler.SecuredLoginHandler$ServerInfo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.