70 private static final Logger logger = LogManager.getLogger(
LoginDecoder.class);
72 private static final int LOGIN_HANDSHAKE = 14;
73 private static final int NEW_CONNECTION_OPCODE = 16;
74 private static final int RECONNECTION_OPCODE = 18;
75 private static final int MAGIC_NUMBER = 255;
76 private static final int LOGIN_BLOCK_HEADER_SIZE = 38;
78 private static final ThreadLocal<Random> RANDOM = ThreadLocal.withInitial(SecureRandom::new);
80 private State state = State.HANDSHAKE;
83 protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
throws Exception {
87 decodeHandshake(ctx, in);
88 state = State.CONNECTION_TYPE;
92 decodeConnectionType(ctx, in);
93 state = State.PAYLOAD;
97 decodePayload(ctx, in, out);
103 private void decodeHandshake(ChannelHandlerContext ctx, ByteBuf in) {
104 final Channel channel = ctx.channel();
107 if (in.readableBytes() >= 2) {
108 final int handshake = in.readUnsignedByte();
110 if (handshake != LOGIN_HANDSHAKE) {
111 sendResponseCode(ctx,
LoginResponse.LOGIN_SERVER_REJECTED_SESSION);
115 @SuppressWarnings(
"unused")
final int nameHash = in.readUnsignedByte();
116 final long serverSeed = RANDOM.get().nextLong();
118 ByteBuf buf = ctx.alloc().buffer(17);
121 buf.writeLong(serverSeed);
122 ctx.writeAndFlush(buf, ctx.voidPromise());
124 sendResponseCode(ctx,
LoginResponse.LOGIN_SERVER_REJECTED_SESSION);
128 private void decodeConnectionType(ChannelHandlerContext ctx, ByteBuf in)
throws Exception {
129 if (in.isReadable()) {
130 final int connectionType = in.readUnsignedByte();
132 if (connectionType != NEW_CONNECTION_OPCODE && connectionType != RECONNECTION_OPCODE) {
133 sendResponseCode(ctx,
LoginResponse.LOGIN_SERVER_REJECTED_SESSION);
136 sendResponseCode(ctx,
LoginResponse.LOGIN_SERVER_REJECTED_SESSION);
140 private void decodePayload(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
141 final String host = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress();
143 final int loginBlockSize = in.readUnsignedByte();
144 if (in.isReadable(loginBlockSize)) {
145 final int magicId = in.readUnsignedByte();
147 if (magicId != MAGIC_NUMBER) {
148 logger.warn(String.format(
"[%s] wrong magic id: %d", host, magicId));
149 sendResponseCode(ctx,
LoginResponse.LOGIN_SERVER_REJECTED_SESSION);
153 final int clientVersion = in.readUnsignedByte();
154 if (
Config.CLIENT_VERSION != clientVersion) {
155 logger.warn(
"[{}] outdated client(client version): {} should be: {}", host, clientVersion,
Config.CLIENT_VERSION);
160 final int memoryVersion = in.readUnsignedByte();
161 if (memoryVersion != 0 && memoryVersion != 1) {
162 logger.warn(String.format(
"[%s] wrong memory version: %d", host, memoryVersion));
163 sendResponseCode(ctx,
LoginResponse.LOGIN_SERVER_REJECTED_SESSION);
167 final int[] crcs =
new int[9];
169 for (
int index = 0; index < crcs.length; index++) {
170 crcs[index] = in.readInt();
173 final int expectedSize = in.readUnsignedByte();
175 if (expectedSize != loginBlockSize - LOGIN_BLOCK_HEADER_SIZE) {
176 logger.warn(String.format(
"[%s] wrong rsa block size: %d expecting: %d", host, (loginBlockSize - LOGIN_BLOCK_HEADER_SIZE), expectedSize));
177 sendResponseCode(ctx,
LoginResponse.LOGIN_SERVER_REJECTED_SESSION);
181 final byte[] rsaBytes =
new byte[loginBlockSize - LOGIN_BLOCK_HEADER_SIZE];
182 in.readBytes(rsaBytes);
185 final int rsaBufferSize = rsaBufBytes.length;
186 final ByteBuf rsaBuffer = in.alloc().buffer(rsaBufferSize, rsaBufferSize);
188 rsaBuffer.writeBytes(rsaBufBytes);
190 final int rsa = rsaBuffer.readUnsignedByte();
193 logger.warn(String.format(
"[%s] failed decrypt rsa %d", host, rsa));
194 sendResponseCode(ctx,
LoginResponse.LOGIN_SERVER_REJECTED_SESSION);
198 final long clientHalf = rsaBuffer.readLong();
199 final long serverHalf = rsaBuffer.readLong();
202 (int) (clientHalf >> 32),
204 (
int) (serverHalf >> 32),
210 for (
int index = 0; index < isaacSeed.length; index++) {
211 isaacSeed[index] += 50;
216 @SuppressWarnings(
"unused")
final int uid = rsaBuffer.readInt();
218 final String UUID = ByteBufUtil.readString(rsaBuffer);
219 final String macAddress = ByteBufUtil.readString(rsaBuffer);
220 final String username = ByteBufUtil.readString(rsaBuffer);
221 final String password = ByteBufUtil.readString(rsaBuffer);
223 out.add(
new LoginDetailsPacket(UUID, macAddress, username, password, encryptor, decryptor));
228 sendResponseCode(ctx,
LoginResponse.LOGIN_SERVER_REJECTED_SESSION);
232 private void sendResponseCode(ChannelHandlerContext ctx,
LoginResponse response) {
233 if (response ==
LoginResponse.LOGIN_SERVER_REJECTED_SESSION) {
234 final String host = ((InetSocketAddress) ctx.channel().remoteAddress()).getHostString();
235 logger.warn(String.format(
"[%s] session was rejected", host));
237 ByteBuf buffer = ctx.alloc().buffer(Byte.BYTES);
239 ctx.writeAndFlush(buffer)
240 .addListener(ChannelFutureListener.CLOSE);
241 state = State.IGNORE;