Guest User

Untitled

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