Advertisement
Guest User

Untitled

a guest
Aug 1st, 2017
82
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 9.72 KB | None | 0 0
  1. // Copyright (C) 2010 Mike Linden
  2. // This program is free software; you can redistribute it and/or
  3. // modify it under the terms of the GNU General Public License
  4. // as published by the Free Software Foundation; either version 2
  5. // of the License, or (at your option) any later version.
  6. //
  7. // This program is distributed in the hope that it will be useful,
  8. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  10. // GNU General Public License for more details.
  11. //
  12. // You should have received a copy of the GNU General Public License
  13. // along with this program; if not, write to the Free Software
  14. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  15.  
  16. package org.ex.ws.net.codec;
  17.  
  18. import java.security.SecureRandom;
  19. import java.util.logging.Logger;
  20.  
  21. import org.ex.constants.GlobalConstants;
  22. import org.ex.core.util.ChannelBufferUtils;
  23. import org.ex.ws.model.World;
  24. import org.ex.ws.model.player.PlayerDetails;
  25. import org.ex.ws.net.ISAACCipher;
  26. import org.ex.ws.net.codec.game.GamePacketBuilder;
  27. import org.ex.ws.net.ondemand.OnDemandPool;
  28. import org.ex.ws.net.ondemand.OnDemandRequest;
  29. import org.ex.ws.util.NameUtils;
  30. import org.jboss.netty.buffer.ChannelBuffer;
  31. import org.jboss.netty.channel.Channel;
  32. import org.jboss.netty.channel.ChannelHandlerContext;
  33. import org.jboss.netty.handler.codec.replay.ReplayingDecoder;
  34.  
  35. /**
  36.  * Login protocol decoding class.
  37.  *
  38.  * @author Mike
  39.  *
  40.  */
  41. public final class RS2LoginDecoder extends ReplayingDecoder<RS2LoginStage> {
  42.  
  43.     /**
  44.      * Logger instance.
  45.      */
  46.     private static final Logger logger = Logger.getLogger(RS2LoginDecoder.class
  47.             .getName());
  48.  
  49.     /**
  50.      * Packet opcode for game requests.
  51.      */
  52.     public static final int OPCODE_GAME = 14;
  53.  
  54.     /**
  55.      * Packet opcode for update requests.
  56.      */
  57.     public static final int OPCODE_UPDATE = 15;
  58.  
  59.     /**
  60.      * The secured random number generator.
  61.      */
  62.     private static final SecureRandom random = new SecureRandom();
  63.  
  64.     /**
  65.      * The initial login response data.
  66.      */
  67.     private static final byte[] INITIAL_RESPONSE = new byte[] { 0x0, 0x0, 0x0,
  68.             0x0, 0x0, 0x0, 0x0, 0x0 };
  69.  
  70.     @Override
  71.     protected Object decode(ChannelHandlerContext ctx, Channel channel,
  72.             ChannelBuffer buffer, RS2LoginStage stage) throws Exception {
  73.         switch (stage) {
  74.         case UPDATE:
  75.             /*
  76.              * Here we read the cache id (idx file), file id and priority.
  77.              */
  78.             int cacheId = buffer.readUnsignedByte();
  79.             int fileId = (buffer.readUnsignedByte() << 8)
  80.                     | buffer.readUnsignedByte();
  81.             int priority = buffer.readUnsignedByte();
  82.  
  83.             /*
  84.              * We push the request into the on-demand pool so it can be served.
  85.              */
  86.             OnDemandPool.getOnDemandPool().pushRequest(
  87.                     new OnDemandRequest(channel, cacheId, fileId, priority));
  88.  
  89.             break;
  90.         case OPCODE:
  91.             /*
  92.              * Here we read the first opcode which indicates the type of
  93.              * connection.
  94.              *
  95.              * 14 = game, 15 = update.
  96.              *
  97.              * Updating is disabled in the vast majority of 317 clients.
  98.              */
  99.             int opcode = buffer.readUnsignedByte();
  100.             switch (opcode) {
  101.             case OPCODE_GAME:
  102.                 checkpoint(RS2LoginStage.LOGIN);
  103.                 return null;
  104.             case OPCODE_UPDATE:
  105.                 checkpoint(RS2LoginStage.UPDATE);
  106.                 channel.write(new GamePacketBuilder().put(INITIAL_RESPONSE)
  107.                         .toGamePacket());
  108.                 return null;
  109.             }
  110.  
  111.             break;
  112.         case LOGIN:
  113.             /*
  114.              * The name hash is a simple hash of the name which is suspected to
  115.              * be used to select the appropiate login server.
  116.              */
  117.             @SuppressWarnings("unused")
  118.             int nameHash = buffer.readUnsignedByte();
  119.  
  120.             /*
  121.              * We generated the server session key using a SecureRandom class
  122.              * for security.
  123.              */
  124.             long serverKey = random.nextLong();
  125.  
  126.             /*
  127.              * The initial response is just 0s which the client is set to ignore
  128.              * (probably some sort of modification).
  129.              */
  130.             channel.write(new GamePacketBuilder().put(INITIAL_RESPONSE)
  131.                     .put((byte) 0).putLong(serverKey).toGamePacket());
  132.             checkpoint(RS2LoginStage.PRECRYPTED);
  133.             RS2ChannelAttributes.SERVER_KEY.set(channel, serverKey);
  134.             return null;
  135.  
  136.         case PRECRYPTED:
  137.             /*
  138.              * Read the type of login.
  139.              *
  140.              * 16 = normal, 18 = reconnection.
  141.              */
  142.             int loginOpcode = buffer.readUnsignedByte();
  143.             if (loginOpcode != 16 && loginOpcode != 18) {
  144.                 logger.info("Invalid login opcode : " + loginOpcode);
  145.                 channel.close();
  146.                 return null;
  147.             }
  148.  
  149.             /*
  150.              * Read the size of the login packet.
  151.              */
  152.             int loginSize = buffer.readUnsignedByte();
  153.  
  154.             /*
  155.              * And calculate how long the encrypted block will be.
  156.              */
  157.             int loginEncryptSize = loginSize - (36 + 1 + 1 + 2);
  158.  
  159.             /*
  160.              * This could be invalid, if so, ignore it.
  161.              */
  162.             if (loginEncryptSize <= 0) {
  163.                 logger.info("Encrypted packet size zero or negative : "
  164.                         + loginEncryptSize);
  165.                 channel.close();
  166.                 return null;
  167.             }
  168.             checkpoint(RS2LoginStage.CRYPTED);
  169.             RS2ChannelAttributes.LOGIN_SIZE.set(channel, loginSize);
  170.             RS2ChannelAttributes.LOGIN_SIZE_ENCRYPTED.set(channel,
  171.                     loginEncryptSize);
  172.             return null;
  173.         case CRYPTED:
  174.             int size = RS2ChannelAttributes.LOGIN_SIZE.get(channel);
  175.             int encryptSize = RS2ChannelAttributes.LOGIN_SIZE_ENCRYPTED
  176.                     .get(channel);
  177.             if (super.actualReadableBytes() >= size) {
  178.                 /*
  179.                  * Read the magic ID which is 255 (0xFF) which indicates this is
  180.                  * the real login packet.
  181.                  */
  182.                 final int magicId = buffer.readUnsignedByte();
  183.                 if (magicId != 255) {
  184.                     logger.info("Incorrect magic id : " + magicId);
  185.                     channel.close();
  186.                     return null;
  187.                 }
  188.  
  189.                 /*
  190.                  * Now read a short which is the client version and check if it
  191.                  * equals the server version.
  192.                  */
  193.                 final int version = buffer.readUnsignedShort();
  194.                 if (version != GlobalConstants.VERSION) {
  195.                     logger.info("Incorrect version : " + version);
  196.                     channel.close();
  197.                     return null;
  198.                 }
  199.  
  200.                 /*
  201.                  * The following byte indicates if the client is on low or high
  202.                  * memory.
  203.                  */
  204.                 @SuppressWarnings("unused")
  205.                 final boolean lowMemoryVersion = buffer.readUnsignedByte() == 1;
  206.  
  207.                 /*
  208.                  * Read the cache indices.
  209.                  */
  210.                 for (int i = 0; i < 9; i++) {
  211.                     buffer.readInt();
  212.                 }
  213.  
  214.                 /*
  215.                  * The encrypted size includes the size byte which is not
  216.                  * needed.
  217.                  */
  218.                 encryptSize--;
  219.  
  220.                 /*
  221.                  * Check if there is a mismatch in the sizing.
  222.                  */
  223.                 final int reportedSize = buffer.readUnsignedByte();
  224.                 if (reportedSize != encryptSize) {
  225.                     logger.info("Packet size mismatch (expected : "
  226.                             + encryptSize + ", reported : " + reportedSize
  227.                             + ")");
  228.                     channel.close();
  229.                     return null;
  230.                 }
  231.  
  232.                 /*
  233.                  * Now read the encrypted block opcode (although in most 317
  234.                  * clients and this server the RSA is disabled) and check if it
  235.                  * is equal to 10.
  236.                  */
  237.                 int blockOpcode = buffer.readUnsignedByte();
  238.                 if (blockOpcode != 10) {
  239.                     logger.info("Invalid login block opcode : " + blockOpcode);
  240.                     channel.close();
  241.                     return null;
  242.                 }
  243.  
  244.                 /*
  245.                  * Read the client's session key.
  246.                  */
  247.                 long clientKey = buffer.readLong();
  248.  
  249.                 /*
  250.                  * And verifiy it has the correct server session key.
  251.                  */
  252.                 long servKey = RS2ChannelAttributes.SERVER_KEY.get(channel);
  253.                 long reportedServerKey = buffer.readLong();
  254.                 if (reportedServerKey != servKey) {
  255.                     logger.info("Server key mismatch (expected : " + servKey
  256.                             + ", reported : " + reportedServerKey + ").");
  257.                     channel.close();
  258.                     return null;
  259.                 }
  260.  
  261.                 /*
  262.                  * The UID, found in random.dat in newer clients and uid.dat in
  263.                  * older clients, is a way of identifying a computer.
  264.                  *
  265.                  * However, some clients send a hardcoded or random UID, making
  266.                  * it useless in the private server scene.
  267.                  *
  268.                  * Excellia no longer uses any form of UID identification.
  269.                  * Instead, a specific long is used which must match the server
  270.                  * sided copy or the channel will close.
  271.                  */
  272.                 // int uid = buffer.readInt();
  273.                 final long verification = buffer.readLong();
  274.                 if (verification != 839527L) {
  275.                     logger.info("Invalid security verification (expected : 839527L, reported : "
  276.                             + verification + ").");
  277.                     channel.close();
  278.                     return null;
  279.                 }
  280.  
  281.                 /*
  282.                  * Read and format the name and password.
  283.                  */
  284.                 String name = NameUtils.formatName(ChannelBufferUtils
  285.                         .getRS2String(buffer));
  286.                 String pass = ChannelBufferUtils.getRS2String(buffer);
  287.                 logger.info("Login request : username=" + name + " password="
  288.                         + pass);
  289.  
  290.                 /*
  291.                  * Setup the ISAAC cipher which is used to encrypt and decrypt
  292.                  * opcodes.
  293.                  *
  294.                  * However, without RSA, this is rendered useless anyway.
  295.                  */
  296.                 int[] sessionKey = new int[4];
  297.                 sessionKey[0] = (int) (clientKey >> 32);
  298.                 sessionKey[1] = (int) clientKey;
  299.                 sessionKey[2] = (int) (servKey >> 32);
  300.                 sessionKey[3] = (int) servKey;
  301.  
  302.                 this.setState(null);
  303.                 RS2ChannelAttributes.SERVER_KEY.remove(channel);
  304.                 RS2ChannelAttributes.LOGIN_SIZE.remove(channel);
  305.                 RS2ChannelAttributes.LOGIN_SIZE_ENCRYPTED.remove(channel);
  306.  
  307.                 ISAACCipher inCipher = new ISAACCipher(sessionKey);
  308.                 for (int i = 0; i < 4; i++) {
  309.                     sessionKey[i] += 50;
  310.                 }
  311.                 ISAACCipher outCipher = new ISAACCipher(sessionKey);
  312.  
  313.                 /*
  314.                  * The login has successfully completed, do the appropiate
  315.                  * things to fire orr the chain of events which will load and
  316.                  * check saved games, etc.
  317.                  */
  318.                 channel.getPipeline().remove("protocol");
  319.                 channel.getPipeline()
  320.                         .addFirst("protocol", RS2CodecFactory.GAME);
  321.  
  322.                 PlayerDetails pd = new PlayerDetails(channel, name, pass,
  323.                         inCipher, outCipher);
  324.                 World.getWorld().load(pd);
  325.             }
  326.         }
  327.         return null;
  328.     }
  329.  
  330. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement