Advertisement
Guest User

Untitled

a guest
May 16th, 2017
68
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 9.42 KB | None | 0 0
  1. package org.hyperion.rs2.net;
  2.  
  3. import java.security.SecureRandom;
  4. import java.util.logging.Logger;
  5.  
  6. import org.apache.mina.core.buffer.IoBuffer;
  7. import org.apache.mina.core.session.IoSession;
  8. import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
  9. import org.apache.mina.filter.codec.ProtocolCodecFilter;
  10. import org.apache.mina.filter.codec.ProtocolDecoderOutput;
  11. import org.hyperion.Server;
  12. import org.hyperion.rs2.model.PlayerDetails;
  13. import org.hyperion.rs2.model.World;
  14. import org.hyperion.rs2.net.ondemand.OnDemandPool;
  15. import org.hyperion.rs2.net.ondemand.OnDemandRequest;
  16. import org.hyperion.rs2.util.IoBufferUtils;
  17. import org.hyperion.rs2.util.NameUtils;
  18.  
  19. /**
  20.  * Login protocol decoding class.
  21.  * @author Graham Edgecombe
  22.  *
  23.  */
  24. public class RS2LoginDecoder extends CumulativeProtocolDecoder {
  25.  
  26.     /**
  27.      * Logger instance.
  28.      */
  29.     private static final Logger logger = Logger.getLogger(RS2LoginDecoder.class.getName());
  30.    
  31.     /**
  32.      * Opcode stage.
  33.      */
  34.     public static final int STATE_OPCODE = 0;
  35.    
  36.     /**
  37.      * Login stage.
  38.      */
  39.     public static final int STATE_LOGIN = 1;
  40.    
  41.     /**
  42.      * Precrypted stage.
  43.      */
  44.     public static final int STATE_PRECRYPTED = 2;
  45.    
  46.     /**
  47.      * Crypted stage.
  48.      */
  49.     public static final int STATE_CRYPTED = 3;
  50.    
  51.     /**
  52.      * Update stage.
  53.      */
  54.     public static final int STATE_UPDATE = -1;
  55.    
  56.     /**
  57.      * Game opcode.
  58.      */
  59.     public static final int OPCODE_GAME = 14;
  60.    
  61.     /**
  62.      * Update opcode.
  63.      */
  64.     public static final int OPCODE_UPDATE = 15;
  65.    
  66.     /**
  67.      * Secure random number generator.
  68.      */
  69.     private static final SecureRandom RANDOM = new SecureRandom();
  70.    
  71.     /**
  72.      * Initial login response.
  73.      */
  74.     private static final byte[] INITIAL_RESPONSE = new byte[] {
  75.         0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
  76.     };
  77.        
  78.     @Override
  79.     protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
  80.         int state = (Integer) session.getAttribute("state", STATE_OPCODE);
  81.         switch(state) {
  82.         case STATE_UPDATE:
  83.             if(in.remaining() >= 4) {
  84.                 /*
  85.                  * Here we read the cache id (idx file), file id and priority.
  86.                  */
  87.                 int cacheId = in.get() & 0xFF;
  88.                 int fileId = ((in.get() & 0xFF) << 8) | (in.get() & 0xFF);
  89.                 int priority = in.get() & 0xFF;
  90.  
  91.                 /*
  92.                  * We push the request into the ondemand pool so it can be served.
  93.                  */
  94.                 OnDemandPool.getOnDemandPool().pushRequest(new OnDemandRequest(session, cacheId, fileId, priority));
  95.                 return true;
  96.             } else {
  97.                 in.rewind();
  98.                 return false;
  99.             }
  100.         case STATE_OPCODE:
  101.             if(in.remaining() >= 1) {
  102.                 /*
  103.                  * Here we read the first opcode which indicates the type
  104.                  * of connection.
  105.                  *
  106.                  * 14 = game
  107.                  * 15 = update
  108.                  *
  109.                  * Updating is disabled in the vast majority of 317
  110.                  * clients.
  111.                  */
  112.                 int opcode = in.get() & 0xFF;
  113.                 switch(opcode) {
  114.                 case OPCODE_GAME:
  115.                     session.setAttribute("state", STATE_LOGIN);
  116.                     return true;
  117.                 case OPCODE_UPDATE:
  118.                     session.setAttribute("state", STATE_UPDATE);
  119.                     session.write(new PacketBuilder().put(INITIAL_RESPONSE).toPacket());
  120.                     return true;
  121.                 default:
  122.                     logger.info("Invalid opcode : " + opcode);
  123.                     session.close(false);
  124.                     break;
  125.                 }
  126.             } else {
  127.                 in.rewind();
  128.                 return false;
  129.             }
  130.             break;
  131.         case STATE_LOGIN:
  132.             if(in.remaining() >= 1) {
  133.                 /*
  134.                  * The name hash is a simple hash of the name which is
  135.                  * suspected to be used to select the appropriate login
  136.                  * server.
  137.                  */
  138.                 @SuppressWarnings("unused")
  139.                 int nameHash = in.get() & 0xFF;
  140.                
  141.                 /*
  142.                  * We generated the server session key using a SecureRandom
  143.                  * class for security.
  144.                  */
  145.                 long serverKey = RANDOM.nextLong();
  146.                
  147.                 /*
  148.                  * The initial response is just 0s which the client is set
  149.                  * to ignore (probably some sort of modification).
  150.                  */
  151.                 session.write(new PacketBuilder().put(INITIAL_RESPONSE).put((byte) 0).putLong(serverKey).toPacket());
  152.                 session.setAttribute("state", STATE_PRECRYPTED);
  153.                 session.setAttribute("serverKey", serverKey);
  154.                 return true;
  155.             }
  156.             break;
  157.         case STATE_PRECRYPTED:
  158.             if(in.remaining() >= 2) {
  159.                 /*
  160.                  * We read the type of login.
  161.                  *
  162.                  * 16 = normal
  163.                  * 18 = reconnection
  164.                  */
  165.                 int loginOpcode = in.get() & 0xFF;
  166.                 if(loginOpcode != 16 && loginOpcode != 18) {
  167.                     logger.info("Invalid login opcode : " + loginOpcode);
  168.                     session.close(false);
  169.                     in.rewind();
  170.                     return false;
  171.                 }
  172.                
  173.                 /*
  174.                  * We read the size of the login packet.
  175.                  */
  176.                 int loginSize = in.get() & 0xFF;
  177.                
  178.                 /*
  179.                  * And calculated how long the encrypted block will be.
  180.                  */
  181.                 int loginEncryptSize = loginSize - (36 + 1 + 1 + 2);
  182.                
  183.                 /*
  184.                  * This could be invalid so if it is we ignore it.
  185.                  */
  186.                 if(loginEncryptSize <= 0) {
  187.                     logger.info("Encrypted packet size zero or negative : " + loginEncryptSize);
  188.                     session.close(false);
  189.                     in.rewind();
  190.                     return false;
  191.                 }
  192.                 session.setAttribute("state", STATE_CRYPTED);
  193.                 session.setAttribute("size", loginSize);
  194.                 session.setAttribute("encryptSize", loginEncryptSize);
  195.                 return true;
  196.             }
  197.             break;
  198.         case STATE_CRYPTED:
  199.             int size = (Integer) session.getAttribute("size");
  200.             int encryptSize = (Integer) session.getAttribute("encryptSize");
  201.             if(in.remaining() >= size) {
  202.                 /*
  203.                  * We read the magic ID which is 255 (0xFF) which indicates
  204.                  * this is the real login packet.
  205.                  */
  206.                 int magicId = in.get() & 0xFF;
  207.                 if(magicId != 255) {
  208.                     logger.info("Incorrect magic id : " + magicId);
  209.                     session.close(false);
  210.                     in.rewind();
  211.                     return false;
  212.                 }
  213.                
  214.                 /*
  215.                  * We now read a short which is the client version and
  216.                  * check if it equals 317.
  217.                  */
  218.                 int version = in.getShort() & 0xFFFF;
  219.                 if(version != Server.VERSION) {
  220.                     logger.info("Incorrect version : " + version);
  221.                     session.close(false);
  222.                     in.rewind();
  223.                     return false;
  224.                 }
  225.                
  226.                 /*
  227.                  * The following byte indicates if we are using a low
  228.                  * memory version.
  229.                  */
  230.                 @SuppressWarnings("unused")
  231.                 boolean lowMemoryVersion = (in.get() & 0xFF) == 1;
  232.                
  233.                 /*
  234.                  * We know read the cache indices.
  235.                  */
  236.                 for(int i = 0; i < 9; i++) {
  237.                     in.getInt();
  238.                 }
  239.                
  240.                 /*
  241.                  * The encrypted size includes the size byte which we don't
  242.                  * need.
  243.                  */
  244.                 encryptSize--;
  245.                
  246.                 /*
  247.                  * We check if there is a mismatch in the sizing.
  248.                  */
  249.                 int reportedSize = in.get() & 0xFF;
  250.                 if(reportedSize != encryptSize) {
  251.                     logger.info("Packet size mismatch (expected : " + encryptSize + ", reported : " + reportedSize + ")");
  252.                     session.close(false);
  253.                     in.rewind();
  254.                     return false;
  255.                 }
  256.                
  257.                 /*
  258.                  * We now read the encrypted block opcode (although in most
  259.                  * 317 clients and this server the RSA is disabled) and
  260.                  * check it is equal to 10.
  261.                  */
  262.                 int blockOpcode = in.get() & 0xFF;
  263.                 if(blockOpcode != 10) {
  264.                     logger.info("Invalid login block opcode : " + blockOpcode);
  265.                     session.close(false);
  266.                     in.rewind();
  267.                     return false;
  268.                 }
  269.  
  270.                 /*
  271.                  * We read the client's session key.
  272.                  */
  273.                 long clientKey = in.getLong();
  274.                
  275.                 /*
  276.                  * And verify it has the correct server session key.
  277.                  */
  278.                 long serverKey = (Long) session.getAttribute("serverKey");
  279.                 long reportedServerKey = in.getLong();
  280.                 if(reportedServerKey != serverKey) {
  281.                     logger.info("Server key mismatch (expected : " + serverKey + ", reported : " + reportedServerKey + ")");
  282.                     session.close(false);
  283.                     in.rewind();
  284.                     return false;
  285.                 }
  286.                
  287.                 /*
  288.                  * The UID, found in random.dat in newer clients and
  289.                  * uid.dat in older clients is a way of identifying a
  290.                  * computer.
  291.                  *
  292.                  * However, some clients send a hardcoded or random UID,
  293.                  * making it useless in the private server scene.
  294.                  */
  295.                 int uid = in.getInt();
  296.                
  297.                 /*
  298.                  * We read and format the name and passwords.
  299.                  */
  300.                 if(name.getLength() > 0) {
  301.                 String name = NameUtils.formatName(IoBufferUtils.getRS2String(in));
  302.                 }
  303.                 String pass = IoBufferUtils.getRS2String(in);
  304.                 logger.info("Login request : username=" + name + " password=" + pass);
  305.                
  306.                 /*
  307.                  * And setup the ISAAC cipher which is used to encrypt and
  308.                  * decrypt opcodes.
  309.                  *
  310.                  * However, without RSA, this is rendered useless anyway.
  311.                  */
  312.                 int[] sessionKey = new int[4];
  313.                 sessionKey[0] = (int) (clientKey >> 32);
  314.                 sessionKey[1] = (int) clientKey;
  315.                 sessionKey[2] = (int) (serverKey >> 32);
  316.                 sessionKey[3] = (int) serverKey;
  317.                
  318.                 session.removeAttribute("state");
  319.                 session.removeAttribute("serverKey");
  320.                 session.removeAttribute("size");
  321.                 session.removeAttribute("encryptSize");
  322.                
  323.                 ISAACCipher inCipher = new ISAACCipher(sessionKey);
  324.                 for(int i = 0; i < 4; i++) {
  325.                     sessionKey[i] += 50;
  326.                 }
  327.                 ISAACCipher outCipher = new ISAACCipher(sessionKey);
  328.                
  329.                 /*
  330.                  * Now, the login has completed, and we do the appropriate
  331.                  * things to fire off the chain of events which will load
  332.                  * and check the saved games etc.
  333.                  */
  334.                 session.getFilterChain().remove("protocol");
  335.                 session.getFilterChain().addFirst("protocol", new ProtocolCodecFilter(RS2CodecFactory.GAME));
  336.                
  337.                 PlayerDetails pd = new PlayerDetails(session, name, pass, uid, inCipher, outCipher);
  338.                 World.getWorld().load(pd);
  339.             }
  340.             break;
  341.         }
  342.         in.rewind();
  343.         return false;
  344.     }
  345.  
  346. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement