Merge branch 'net'.

This commit is contained in:
Ryley Kimmel
2015-03-01 15:09:20 -05:00
3 changed files with 41 additions and 63 deletions
@@ -4,7 +4,6 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import java.io.IOException;
import java.util.List; import java.util.List;
import net.burtleburtle.bob.rand.IsaacRandom; import net.burtleburtle.bob.rand.IsaacRandom;
@@ -61,7 +60,7 @@ public final class GamePacketDecoder extends StatefulFrameDecoder<GameDecoderSta
} }
@Override @Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out, GameDecoderState state) throws IOException { protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out, GameDecoderState state) {
switch (state) { switch (state) {
case GAME_OPCODE: case GAME_OPCODE:
decodeOpcode(in, out); decodeOpcode(in, out);
@@ -96,9 +95,8 @@ public final class GamePacketDecoder extends StatefulFrameDecoder<GameDecoderSta
* *
* @param buffer The buffer. * @param buffer The buffer.
* @param out The {@link List} of objects to be passed along the pipeline. * @param out The {@link List} of objects to be passed along the pipeline.
* @throws IOException If a received opcode or packet type is illegal.
*/ */
private void decodeOpcode(ByteBuf buffer, List<Object> out) throws IOException { private void decodeOpcode(ByteBuf buffer, List<Object> out) {
if (buffer.isReadable()) { if (buffer.isReadable()) {
int encryptedOpcode = buffer.readUnsignedByte(); int encryptedOpcode = buffer.readUnsignedByte();
opcode = encryptedOpcode - random.nextInt() & 0xFF; opcode = encryptedOpcode - random.nextInt() & 0xFF;
@@ -121,7 +119,7 @@ public final class GamePacketDecoder extends StatefulFrameDecoder<GameDecoderSta
setState(GameDecoderState.GAME_LENGTH); setState(GameDecoderState.GAME_LENGTH);
break; break;
default: default:
throw new IOException("Illegal packet type: " + type + "."); throw new IllegalStateException("Illegal packet type: " + type + ".");
} }
} }
} }
@@ -2,9 +2,9 @@ package org.apollo.net.codec.login;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import java.io.IOException;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.List; import java.util.List;
@@ -28,7 +28,7 @@ public final class LoginDecoder extends StatefulFrameDecoder<LoginDecoderState>
/** /**
* The secure random number generator. * The secure random number generator.
*/ */
private static final SecureRandom random = new SecureRandom(); private static final SecureRandom RANDOM = new SecureRandom();
/** /**
* The login packet length. * The login packet length.
@@ -54,11 +54,11 @@ public final class LoginDecoder extends StatefulFrameDecoder<LoginDecoderState>
* Creates the login decoder with the default initial state. * Creates the login decoder with the default initial state.
*/ */
public LoginDecoder() { public LoginDecoder() {
super(LoginDecoderState.LOGIN_HANDSHAKE, true); super(LoginDecoderState.LOGIN_HANDSHAKE);
} }
@Override @Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out, LoginDecoderState state) throws Exception { protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out, LoginDecoderState state) {
switch (state) { switch (state) {
case LOGIN_HANDSHAKE: case LOGIN_HANDSHAKE:
decodeHandshake(ctx, in, out); decodeHandshake(ctx, in, out);
@@ -70,7 +70,7 @@ public final class LoginDecoder extends StatefulFrameDecoder<LoginDecoderState>
decodePayload(ctx, in, out); decodePayload(ctx, in, out);
break; break;
default: default:
throw new IllegalStateException("Invalid login decoder state."); throw new IllegalStateException("Invalid login decoder state: " + state);
} }
} }
@@ -84,7 +84,7 @@ public final class LoginDecoder extends StatefulFrameDecoder<LoginDecoderState>
private void decodeHandshake(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) { private void decodeHandshake(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) {
if (buffer.isReadable()) { if (buffer.isReadable()) {
usernameHash = buffer.readUnsignedByte(); usernameHash = buffer.readUnsignedByte();
serverSeed = random.nextLong(); serverSeed = RANDOM.nextLong();
ByteBuf response = ctx.alloc().buffer(17); ByteBuf response = ctx.alloc().buffer(17);
response.writeByte(LoginConstants.STATUS_EXCHANGE_DATA); response.writeByte(LoginConstants.STATUS_EXCHANGE_DATA);
@@ -102,14 +102,14 @@ public final class LoginDecoder extends StatefulFrameDecoder<LoginDecoderState>
* @param ctx The channel handler context. * @param ctx The channel handler context.
* @param buffer The buffer. * @param buffer The buffer.
* @param out The {@link List} of objects to pass forward through the pipeline. * @param out The {@link List} of objects to pass forward through the pipeline.
* @throws IOException If the login type sent by the client is invalid.
*/ */
private void decodeHeader(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws IOException { private void decodeHeader(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) {
if (buffer.readableBytes() >= 2) { if (buffer.readableBytes() >= 2) {
int loginType = buffer.readUnsignedByte(); int loginType = buffer.readUnsignedByte();
if (loginType != LoginConstants.TYPE_STANDARD && loginType != LoginConstants.TYPE_RECONNECTION) { if (loginType != LoginConstants.TYPE_STANDARD && loginType != LoginConstants.TYPE_RECONNECTION) {
throw new IOException("Invalid login type."); writeResponseCode(ctx, LoginConstants.STATUS_LOGIN_SERVER_REJECTED_SESSION);
return;
} }
reconnecting = loginType == LoginConstants.TYPE_RECONNECTION; reconnecting = loginType == LoginConstants.TYPE_RECONNECTION;
@@ -125,9 +125,8 @@ public final class LoginDecoder extends StatefulFrameDecoder<LoginDecoderState>
* @param ctx The channel handler context. * @param ctx The channel handler context.
* @param buffer The buffer. * @param buffer The buffer.
* @param out The {@link List} of objects to pass forward through the pipeline. * @param out The {@link List} of objects to pass forward through the pipeline.
* @throws Exception If an error occurs.
*/ */
private void decodePayload(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception { private void decodePayload(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) {
if (buffer.readableBytes() >= loginLength) { if (buffer.readableBytes() >= loginLength) {
ByteBuf payload = buffer.readBytes(loginLength); ByteBuf payload = buffer.readBytes(loginLength);
int clientVersion = 255 - payload.readUnsignedByte(); int clientVersion = 255 - payload.readUnsignedByte();
@@ -136,7 +135,8 @@ public final class LoginDecoder extends StatefulFrameDecoder<LoginDecoderState>
int lowMemoryFlag = payload.readUnsignedByte(); int lowMemoryFlag = payload.readUnsignedByte();
if (lowMemoryFlag != 0 && lowMemoryFlag != 1) { if (lowMemoryFlag != 0 && lowMemoryFlag != 1) {
throw new Exception("Invalid value for low memory flag."); writeResponseCode(ctx, LoginConstants.STATUS_LOGIN_SERVER_REJECTED_SESSION);
return;
} }
boolean lowMemory = lowMemoryFlag == 1; boolean lowMemory = lowMemoryFlag == 1;
@@ -148,7 +148,8 @@ public final class LoginDecoder extends StatefulFrameDecoder<LoginDecoderState>
int securePayloadLength = payload.readUnsignedByte(); int securePayloadLength = payload.readUnsignedByte();
if (securePayloadLength != loginLength - 41) { if (securePayloadLength != loginLength - 41) {
throw new Exception("Secure payload length mismatch."); writeResponseCode(ctx, LoginConstants.STATUS_LOGIN_SERVER_REJECTED_SESSION);
return;
} }
ByteBuf securePayload = payload.readBytes(securePayloadLength); ByteBuf securePayload = payload.readBytes(securePayloadLength);
@@ -160,13 +161,15 @@ public final class LoginDecoder extends StatefulFrameDecoder<LoginDecoderState>
int secureId = securePayload.readUnsignedByte(); int secureId = securePayload.readUnsignedByte();
if (secureId != 10) { if (secureId != 10) {
throw new Exception("Invalid secure payload id."); writeResponseCode(ctx, LoginConstants.STATUS_LOGIN_SERVER_REJECTED_SESSION);
return;
} }
long clientSeed = securePayload.readLong(); long clientSeed = securePayload.readLong();
long reportedServerSeed = securePayload.readLong(); long reportedServerSeed = securePayload.readLong();
if (reportedServerSeed != serverSeed) { if (reportedServerSeed != serverSeed) {
throw new Exception("Server seed mismatch."); writeResponseCode(ctx, LoginConstants.STATUS_LOGIN_SERVER_REJECTED_SESSION);
return;
} }
int uid = securePayload.readInt(); int uid = securePayload.readInt();
@@ -174,10 +177,9 @@ public final class LoginDecoder extends StatefulFrameDecoder<LoginDecoderState>
String username = BufferUtil.readString(securePayload); String username = BufferUtil.readString(securePayload);
String password = BufferUtil.readString(securePayload); String password = BufferUtil.readString(securePayload);
if (password.length() < 6 || password.length() > 20) { if (password.length() < 6 || password.length() > 20 || username.isEmpty() || username.length() > 12) {
throw new Exception("Invalid password."); writeResponseCode(ctx, LoginConstants.STATUS_INVALID_CREDENTIALS);
} else if (username.isEmpty() || username.length() > 12) { return;
throw new Exception("Invalid username.");
} }
int[] seed = new int[4]; int[] seed = new int[4];
@@ -196,14 +198,23 @@ public final class LoginDecoder extends StatefulFrameDecoder<LoginDecoderState>
PlayerCredentials credentials = new PlayerCredentials(username, password, usernameHash, uid); PlayerCredentials credentials = new PlayerCredentials(username, password, usernameHash, uid);
IsaacRandomPair randomPair = new IsaacRandomPair(encodingRandom, decodingRandom); IsaacRandomPair randomPair = new IsaacRandomPair(encodingRandom, decodingRandom);
LoginRequest request = new LoginRequest(credentials, randomPair, reconnecting, lowMemory, releaseNumber, archiveCrcs, LoginRequest request = new LoginRequest(credentials, randomPair, reconnecting, lowMemory, releaseNumber, archiveCrcs, clientVersion);
clientVersion);
out.add(request); out.add(request);
if (buffer.isReadable()) {
out.add(buffer.readBytes(buffer.readableBytes()));
}
} }
} }
/**
* Writes a response code to the client and closes the current channel.
*
* @param ctx The context of the channel handler.
* @param responseCode The response code to write.
*/
private void writeResponseCode(ChannelHandlerContext ctx, int responseCode) {
ByteBuf buffer = ctx.alloc().buffer(1);
buffer.writeByte(responseCode);
ctx.writeAndFlush(buffer).addListener(ChannelFutureListener.CLOSE);
}
} }
+3 -34
View File
@@ -5,8 +5,7 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.ByteToMessageDecoder;
import java.util.List; import java.util.List;
import java.util.Objects;
import com.google.common.base.Preconditions;
/** /**
* A stateful implementation of a {@link ByteToMessageDecoder} which may be extended and used by other classes. The * A stateful implementation of a {@link ByteToMessageDecoder} which may be extended and used by other classes. The
@@ -17,7 +16,7 @@ import com.google.common.base.Preconditions;
* The current state is supplied as a parameter in the {@link StatefulFrameDecoder#decode} and * The current state is supplied as a parameter in the {@link StatefulFrameDecoder#decode} and
* {@link StatefulFrameDecoder#decodeLast} methods. * {@link StatefulFrameDecoder#decodeLast} methods.
* *
* This class is not thread safe: it is recommended that the state is only set in the decode methods overriden. * This class is not thread safe: it is recommended that the state is only set in the decode methods overridden.
* *
* @author Graham * @author Graham
* @param <T> The state enumeration. * @param <T> The state enumeration.
@@ -36,17 +35,6 @@ public abstract class StatefulFrameDecoder<T extends Enum<T>> extends ByteToMess
* @throws NullPointerException If the state is {@code null}. * @throws NullPointerException If the state is {@code null}.
*/ */
public StatefulFrameDecoder(T state) { public StatefulFrameDecoder(T state) {
this(state, false);
}
/**
* Creates the stateful frame decoder with the specified initial state and unwrap flag.
*
* @param state The initial state.
* @param unwrap The unwrap flag.
* @throws NullPointerException If the state is {@code null}.
*/
public StatefulFrameDecoder(T state, boolean unwrap) {
setState(state); setState(state);
} }
@@ -66,24 +54,6 @@ public abstract class StatefulFrameDecoder<T extends Enum<T>> extends ByteToMess
*/ */
protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out, T state) throws Exception; protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out, T state) throws Exception;
@Override
protected final void decodeLast(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
decodeLast(ctx, in, out, state);
}
/**
* Decodes remaining data before the channel is closed into a frame. You may override this method, but it is not
* required. If you do not, remaining data will be discarded!
*
* @param ctx The current context of this handler.
* @param in The cumulative buffer, which may contain zero or more bytes.
* @param out The {@link List} of objects to pass forward through the pipeline.
* @param state The current state. The state may be changed by calling {@link #setState}.
*/
protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, List<Object> out, T state) {
}
/** /**
* Sets a new state. * Sets a new state.
* *
@@ -91,8 +61,7 @@ public abstract class StatefulFrameDecoder<T extends Enum<T>> extends ByteToMess
* @throws NullPointerException If the state is {@code null}. * @throws NullPointerException If the state is {@code null}.
*/ */
public final void setState(T state) { public final void setState(T state) {
Preconditions.checkNotNull(state, "State cannot be null."); this.state = Objects.requireNonNull(state, "State cannot be null.");
this.state = state;
} }
} }