Advertisement
Guest User

Untitled

a guest
Jun 20th, 2017
78
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.38 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. String name = NameUtils.formatName(IoBufferUtils.getRS2String(in));
  301. String pass = IoBufferUtils.getRS2String(in);
  302. logger.info("Login request : username=" + name + " password=" + pass);
  303.  
  304. /*
  305. * And setup the ISAAC cipher which is used to encrypt and
  306. * decrypt opcodes.
  307. *
  308. * However, without RSA, this is rendered useless anyway.
  309. */
  310. int[] sessionKey = new int[4];
  311. sessionKey[0] = (int) (clientKey >> 32);
  312. sessionKey[1] = (int) clientKey;
  313. sessionKey[2] = (int) (serverKey >> 32);
  314. sessionKey[3] = (int) serverKey;
  315.  
  316. session.removeAttribute("state");
  317. session.removeAttribute("serverKey");
  318. session.removeAttribute("size");
  319. session.removeAttribute("encryptSize");
  320.  
  321. ISAACCipher inCipher = new ISAACCipher(sessionKey);
  322. for(int i = 0; i < 4; i++) {
  323. sessionKey[i] += 50;
  324. }
  325. ISAACCipher outCipher = new ISAACCipher(sessionKey);
  326.  
  327. /*
  328. * Now, the login has completed, and we do the appropriate
  329. * things to fire off the chain of events which will load
  330. * and check the saved games etc.
  331. */
  332. session.getFilterChain().remove("protocol");
  333. session.getFilterChain().addFirst("protocol", new ProtocolCodecFilter(RS2CodecFactory.GAME));
  334.  
  335. PlayerDetails pd = new PlayerDetails(session, name, pass, uid, inCipher, outCipher);
  336. World.getWorld().load(pd);
  337. }
  338. break;
  339. }
  340. in.rewind();
  341. return false;
  342. }
  343.  
  344. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement