package javastory.server.handling;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javastory.channel.ChannelClient;
import javastory.client.GameClient;
import javastory.cryptography.AesTransform;
import javastory.cryptography.VersionType;
import javastory.io.GamePacket;
import javastory.io.PacketBuilder;
import javastory.io.PacketFormatException;
import javastory.io.PacketReader;
import javastory.tools.Randomizer;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import com.google.common.collect.Maps;
public abstract class PacketHandler extends IoHandlerAdapter {
private final List<String> blockedIPs = new ArrayList<String>();
private final Map<String, SessionFloodEntry> floodTracker = Maps.newConcurrentMap();
public PacketHandler() {
}
@Override
public final void messageSent(final IoSession session, final Object message) throws Exception {
super.messageSent(session, message);
}
@Override
public final void exceptionCaught(final IoSession session, final Throwable cause) throws Exception {
// Empty statement
}
@Override
public void sessionOpened(final IoSession session) throws Exception {
final InetSocketAddress address = (InetSocketAddress) session.getRemoteAddress();
final String ip = address.getAddress().getHostAddress();
if (this.blockedIPs.contains(ip)) {
session.close(true);
return;
}
final SessionFloodEntry floodEntry = this.floodTracker.get(ip);
int count;
if (floodEntry == null) {
count = 1;
} else {
count = floodEntry.Count;
final long difference = System.currentTimeMillis() - floodEntry.Timestamp;
if (difference < 2000) { // Less than 2 sec
count++;
} else if (difference > 20000) { // Over 20 sec
count = 1;
}
if (count >= 10) {
this.blockedIPs.add(ip);
this.floodTracker.remove(ip);
session.close(true);
return;
}
}
this.floodTracker.put(ip, new SessionFloodEntry(System.currentTimeMillis(), count));
final byte clientIv[] = { 70, 114, 122, (byte) Randomizer.nextInt(255) };
final byte serverIv[] = { 82, 48, 120, (byte) Randomizer.nextInt(255) };
final AesTransform serverCrypto = new AesTransform(serverIv, ServerConstants.GAME_VERSION, VersionType.COMPLEMENT);
final AesTransform clientCrypto = new AesTransform(clientIv, ServerConstants.GAME_VERSION, VersionType.REGULAR);
final GameClient client = this.createClient(clientCrypto, serverCrypto, session);
final PacketDecoder.DecoderState decoderState = new PacketDecoder.DecoderState();
session.setAttribute(PacketDecoder.DECODER_STATE_KEY, decoderState);
final GamePacket helloPacket = getHello(ServerConstants.GAME_VERSION, clientIv, serverIv);
session.write(helloPacket);
session.setAttribute(GameClient.CLIENT_KEY, client);
session.getConfig().setBothIdleTime(30);
System.out.println(":: IoSession opened " + ip + " ::");
}
protected abstract GameClient createClient(final AesTransform clientCrypto, final AesTransform serverCrypto, final IoSession session);
@Override
public void sessionClosed(final IoSession session) throws Exception {
final GameClient client = (GameClient) session.getAttribute(GameClient.CLIENT_KEY);
if (client != null) {
client.disconnect();
session.removeAttribute(GameClient.CLIENT_KEY);
}
super.sessionClosed(session);
}
@Override
public void messageReceived(final IoSession session, final Object message) throws Exception {
final PacketReader reader = new PacketReader((byte[]) message);
final short opCode = reader.readShort();
for (final ClientPacketOpcode code : ClientPacketOpcode.values()) {
if (code.getValue() == opCode) {
final GameClient client = (GameClient) session.getAttribute(GameClient.CLIENT_KEY);
if (code.NeedsChecking()) {
if (!client.isLoggedIn()) {
return;
}
}
try {
this.handlePacket(code, reader, client);
} catch (final PacketFormatException ex) {
client.disconnect();
}
return;
}
}
}
@Override
public void sessionIdle(final IoSession session, final IdleStatus status) throws Exception {
final ChannelClient client = (ChannelClient) session.getAttribute(GameClient.CLIENT_KEY);
if (client != null) {
client.sendPing();
}
super.sessionIdle(session, status);
}
protected abstract void handlePacket(final ClientPacketOpcode header, final PacketReader reader, GameClient client) throws PacketFormatException;
private static GamePacket getHello(final short version, final byte[] clientIv, final byte[] serverIv) {
final PacketBuilder builder = new PacketBuilder(16);
builder.writeAsShort(14);
builder.writeAsShort(version);
builder.writeLengthPrefixedString("1");
builder.writeBytes(clientIv);
builder.writeBytes(serverIv);
builder.writeAsByte(7);
return builder.getPacket();
}
}