RuneHive-Game
Loading...
Searching...
No Matches
LoginDecoder.java
Go to the documentation of this file.
1package com.runehive.net.codec.login;
2
3import com.runehive.Config;
4import com.runehive.net.codec.IsaacCipher;
5import com.runehive.net.session.LoginSession;
6import io.netty.buffer.ByteBuf;
7import io.netty.channel.Channel;
8import io.netty.channel.ChannelFutureListener;
9import io.netty.channel.ChannelHandlerContext;
10import io.netty.handler.codec.ByteToMessageDecoder;
11import org.apache.logging.log4j.LogManager;
12import org.apache.logging.log4j.Logger;
13import org.jire.runehiveps.ByteBufUtil;
14
15import java.math.BigInteger;
16import java.net.InetSocketAddress;
17import java.security.SecureRandom;
18import java.util.List;
19import java.util.Random;
20
21/**
22 * The class that handles a connection through the login protocol.
23 *
24 * @author nshusa
25 */
26public final class LoginDecoder extends ByteToMessageDecoder {
27
28 private static final Logger logger = LogManager.getLogger(LoginDecoder.class);
29
30 private static final int LOGIN_HANDSHAKE = 14;
31 private static final int NEW_CONNECTION_OPCODE = 16;
32 private static final int RECONNECTION_OPCODE = 18;
33 private static final int MAGIC_NUMBER = 255;
34 private static final int LOGIN_BLOCK_HEADER_SIZE = 38;
35
36 private static final ThreadLocal<Random> RANDOM = ThreadLocal.withInitial(SecureRandom::new);
37
39
40 @Override
41 protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
42 switch (state) {
43
44 case HANDSHAKE:
45 decodeHandshake(ctx, in);
47 break;
48
49 case CONNECTION_TYPE:
50 decodeConnectionType(ctx, in);
52 break;
53
54 case PAYLOAD:
55 decodePayload(ctx, in, out);
56 break;
57
58 }
59 }
60
61 private void decodeHandshake(ChannelHandlerContext ctx, ByteBuf in) {
62 final Channel channel = ctx.channel();
63 channel.attr(Config.SESSION_KEY).set(new LoginSession(channel));
64
65 if (in.readableBytes() >= 2) {
66 final int handshake = in.readUnsignedByte();
67
68 if (handshake != LOGIN_HANDSHAKE) {
70 return;
71 }
72
73 @SuppressWarnings("unused") final int nameHash = in.readUnsignedByte();
74 final long serverSeed = RANDOM.get().nextLong();
75
76 ByteBuf buf = ctx.alloc().buffer(17);
77 buf.writeLong(0);
78 buf.writeByte(0);
79 buf.writeLong(serverSeed);
80 ctx.writeAndFlush(buf, ctx.voidPromise());
81 } else {
83 }
84 }
85
86 private void decodeConnectionType(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
87 if (in.isReadable()) {
88 final int connectionType = in.readUnsignedByte();
89
90 if (connectionType != NEW_CONNECTION_OPCODE && connectionType != RECONNECTION_OPCODE) {
92 }
93 } else {
95 }
96 }
97
98 private void decodePayload(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
99 final String host = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress();
100
101 final int loginBlockSize = in.readUnsignedByte();
102 if (in.isReadable(loginBlockSize)) {
103 final int magicId = in.readUnsignedByte();
104
105 if (magicId != MAGIC_NUMBER) {
106 logger.warn(String.format("[%s] wrong magic id: %d", host, magicId));
108 return;
109 }
110
111 final int clientVersion = in.readUnsignedByte();
112 if (Config.CLIENT_VERSION != clientVersion) {
113 logger.warn("[{}] outdated client(client version): {} should be: {}", host, clientVersion, Config.CLIENT_VERSION);
115 return;
116 }
117
118 final int memoryVersion = in.readUnsignedByte();
119 if (memoryVersion != 0 && memoryVersion != 1) {
120 logger.warn(String.format("[%s] wrong memory version: %d", host, memoryVersion));
122 return;
123 }
124
125 final int[] crcs = new int[9];
126
127 for (int index = 0; index < crcs.length; index++) {
128 crcs[index] = in.readInt();
129 }
130
131 final int expectedSize = in.readUnsignedByte(); // rsa header
132
133 if (expectedSize != loginBlockSize - LOGIN_BLOCK_HEADER_SIZE) {
134 logger.warn(String.format("[%s] wrong rsa block size: %d expecting: %d", host, (loginBlockSize - LOGIN_BLOCK_HEADER_SIZE), expectedSize));
136 return;
137 }
138
139 final byte[] rsaBytes = new byte[loginBlockSize - LOGIN_BLOCK_HEADER_SIZE];
140 in.readBytes(rsaBytes);
141
142 final byte[] rsaBufBytes = new BigInteger(rsaBytes).modPow(Config.RSA_EXPONENT, Config.RSA_MODULUS).toByteArray();
143 final int rsaBufferSize = rsaBufBytes.length;
144 final ByteBuf rsaBuffer = in.alloc().buffer(rsaBufferSize, rsaBufferSize);
145 try {
146 rsaBuffer.writeBytes(rsaBufBytes);
147
148 final int rsa = rsaBuffer.readUnsignedByte();
149
150 if (rsa != 10) {
151 logger.warn(String.format("[%s] failed decrypt rsa %d", host, rsa));
153 return;
154 }
155
156 final long clientHalf = rsaBuffer.readLong();
157 final long serverHalf = rsaBuffer.readLong();
158
159 int[] isaacSeed = {
160 (int) (clientHalf >> 32),
161 (int) clientHalf,
162 (int) (serverHalf >> 32),
163 (int) serverHalf
164 };
165
166 final IsaacCipher decryptor = new IsaacCipher(isaacSeed);
167
168 for (int index = 0; index < isaacSeed.length; index++) {
169 isaacSeed[index] += 50;
170 }
171
172 final IsaacCipher encryptor = new IsaacCipher(isaacSeed);
173
174 @SuppressWarnings("unused") final int uid = rsaBuffer.readInt();
175
176 final String UUID = ByteBufUtil.readString(rsaBuffer);
177 final String macAddress = ByteBufUtil.readString(rsaBuffer);
178 final String username = ByteBufUtil.readString(rsaBuffer);
179 final String password = ByteBufUtil.readString(rsaBuffer);
180
181 out.add(new LoginDetailsPacket(UUID, macAddress, username, password, encryptor, decryptor));
182 } finally {
183 rsaBuffer.release();
184 }
185 } else {
187 }
188 }
189
190 private void sendResponseCode(ChannelHandlerContext ctx, LoginResponse response) {
192 final String host = ((InetSocketAddress) ctx.channel().remoteAddress()).getHostString();
193 logger.warn(String.format("[%s] session was rejected", host));
194 }
195 ByteBuf buffer = ctx.alloc().buffer(Byte.BYTES);
196 buffer.writeByte(response.getOpcode());
197 ctx.writeAndFlush(buffer)
198 .addListener(ChannelFutureListener.CLOSE);
200 }
201
208
209}
The class that contains setting-related constants for the server.
Definition Config.java:24
static final int CLIENT_VERSION
Definition Config.java:135
static final BigInteger RSA_MODULUS
The RSA modulus.
Definition Config.java:95
static final BigInteger RSA_EXPONENT
The RSA exponent.
Definition Config.java:98
static final AttributeKey< Session > SESSION_KEY
The session key.
Definition Config.java:90
An implementation of an ISAAC cipher.
The class that handles a connection through the login protocol.
void sendResponseCode(ChannelHandlerContext ctx, LoginResponse response)
void decodeConnectionType(ChannelHandlerContext ctx, ByteBuf in)
static final ThreadLocal< Random > RANDOM
void decodePayload(ChannelHandlerContext ctx, ByteBuf in, List< Object > out)
void decode(ChannelHandlerContext ctx, ByteBuf in, List< Object > out)
void decodeHandshake(ChannelHandlerContext ctx, ByteBuf in)
Represents a Session for authenticating users logging in.
Represents the enumerated login response codes.
final int getOpcode()
Gets the opcode for this response.