You've already forked Arcturus-Morningstar-Extended
mirror of
https://github.com/duckietm/Arcturus-Morningstar-Extended.git
synced 2026-06-20 07:26:18 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 03d37650a0 | |||
| f4e5449443 | |||
| 1ebc8314a8 | |||
| 85a60cf591 | |||
| 41d7420251 | |||
| 5dd602ebab |
+1
-1
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>com.eu.habbo</groupId>
|
<groupId>com.eu.habbo</groupId>
|
||||||
<artifactId>Habbo</artifactId>
|
<artifactId>Habbo</artifactId>
|
||||||
<version>4.1.6</version>
|
<version>4.1.8</version>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
|||||||
+90
-2
@@ -364,8 +364,18 @@ public class AuthHttpHandler extends ChannelInboundHandlerAdapter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try (Connection conn = Emulator.getDatabase().getDataSource().getConnection();
|
try (Connection conn = Emulator.getDatabase().getDataSource().getConnection()) {
|
||||||
PreparedStatement stmt = conn.prepareStatement(
|
if (ip != null && !ip.isEmpty()) {
|
||||||
|
BanInfo ipBan = lookupIpBan(conn, ip);
|
||||||
|
if (ipBan != null) {
|
||||||
|
LOGGER.info("[auth/login] ip ban hit ip={} type={} expires={}",
|
||||||
|
ip, ipBan.type, ipBan.expiresAt);
|
||||||
|
sendJson(ctx, req, HttpResponseStatus.FORBIDDEN, bannedPayload(ipBan));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try (PreparedStatement stmt = conn.prepareStatement(
|
||||||
"SELECT id, username, password FROM users WHERE username = ? LIMIT 1")) {
|
"SELECT id, username, password FROM users WHERE username = ? LIMIT 1")) {
|
||||||
stmt.setString(1, username);
|
stmt.setString(1, username);
|
||||||
try (ResultSet rs = stmt.executeQuery()) {
|
try (ResultSet rs = stmt.executeQuery()) {
|
||||||
@@ -392,6 +402,15 @@ public class AuthHttpHandler extends ChannelInboundHandlerAdapter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BanInfo accountBan = lookupAccountBan(conn, userId);
|
||||||
|
if (accountBan != null) {
|
||||||
|
LOGGER.info("[auth/login] account ban hit userId={} type={} expires={}",
|
||||||
|
userId, accountBan.type, accountBan.expiresAt);
|
||||||
|
AuthRateLimiter.recordSuccess(ip);
|
||||||
|
sendJson(ctx, req, HttpResponseStatus.FORBIDDEN, bannedPayload(accountBan));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
String ssoTicket = mintSsoTicket();
|
String ssoTicket = mintSsoTicket();
|
||||||
|
|
||||||
try (PreparedStatement upd = conn.prepareStatement(
|
try (PreparedStatement upd = conn.prepareStatement(
|
||||||
@@ -421,6 +440,7 @@ public class AuthHttpHandler extends ChannelInboundHandlerAdapter {
|
|||||||
if (rememberToken != null) ok.addProperty("rememberToken", rememberToken);
|
if (rememberToken != null) ok.addProperty("rememberToken", rememberToken);
|
||||||
sendJson(ctx, req, HttpResponseStatus.OK, ok);
|
sendJson(ctx, req, HttpResponseStatus.OK, ok);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOGGER.error("Login query failed for username=" + username, e);
|
LOGGER.error("Login query failed for username=" + username, e);
|
||||||
sendJson(ctx, req, HttpResponseStatus.INTERNAL_SERVER_ERROR, errorPayload("Server error."));
|
sendJson(ctx, req, HttpResponseStatus.INTERNAL_SERVER_ERROR, errorPayload("Server error."));
|
||||||
@@ -804,6 +824,74 @@ public class AuthHttpHandler extends ChannelInboundHandlerAdapter {
|
|||||||
sendJson(ctx, req, HttpResponseStatus.OK, ok);
|
sendJson(ctx, req, HttpResponseStatus.OK, ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final long PERMANENT_BAN_THRESHOLD_SECONDS = 30L * 365L * 24L * 60L * 60L;
|
||||||
|
|
||||||
|
private static final class BanInfo {
|
||||||
|
final String type;
|
||||||
|
final String reason;
|
||||||
|
final int expiresAt;
|
||||||
|
|
||||||
|
BanInfo(String type, String reason, int expiresAt) {
|
||||||
|
this.type = type == null ? "account" : type;
|
||||||
|
this.reason = reason == null ? "" : reason;
|
||||||
|
this.expiresAt = expiresAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isPermanent() {
|
||||||
|
return (long) expiresAt - Emulator.getIntUnixTimestamp() > PERMANENT_BAN_THRESHOLD_SECONDS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BanInfo lookupAccountBan(Connection conn, int userId) throws SQLException {
|
||||||
|
try (PreparedStatement stmt = conn.prepareStatement(
|
||||||
|
"SELECT ban_expire, ban_reason, type FROM bans " +
|
||||||
|
"WHERE user_id = ? AND ban_expire >= ? AND (type = 'account' OR type = 'super') " +
|
||||||
|
"ORDER BY ban_expire DESC LIMIT 1")) {
|
||||||
|
stmt.setInt(1, userId);
|
||||||
|
stmt.setInt(2, Emulator.getIntUnixTimestamp());
|
||||||
|
try (ResultSet rs = stmt.executeQuery()) {
|
||||||
|
if (rs.next()) {
|
||||||
|
return new BanInfo(rs.getString("type"), rs.getString("ban_reason"), rs.getInt("ban_expire"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BanInfo lookupIpBan(Connection conn, String ip) throws SQLException {
|
||||||
|
try (PreparedStatement stmt = conn.prepareStatement(
|
||||||
|
"SELECT ban_expire, ban_reason, type FROM bans " +
|
||||||
|
"WHERE ip = ? AND ban_expire >= ? AND (type = 'ip' OR type = 'super') " +
|
||||||
|
"ORDER BY ban_expire DESC LIMIT 1")) {
|
||||||
|
stmt.setString(1, ip);
|
||||||
|
stmt.setInt(2, Emulator.getIntUnixTimestamp());
|
||||||
|
try (ResultSet rs = stmt.executeQuery()) {
|
||||||
|
if (rs.next()) {
|
||||||
|
return new BanInfo(rs.getString("type"), rs.getString("ban_reason"), rs.getInt("ban_expire"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JsonObject bannedPayload(BanInfo ban) {
|
||||||
|
boolean permanent = ban.isPermanent();
|
||||||
|
String message = permanent
|
||||||
|
? "Your account has been permanently banned."
|
||||||
|
: "Your account is temporarily banned.";
|
||||||
|
|
||||||
|
JsonObject details = new JsonObject();
|
||||||
|
details.addProperty("type", ban.type);
|
||||||
|
details.addProperty("reason", ban.reason);
|
||||||
|
details.addProperty("permanent", permanent);
|
||||||
|
if (!permanent) details.addProperty("expiresAt", ban.expiresAt);
|
||||||
|
|
||||||
|
JsonObject obj = new JsonObject();
|
||||||
|
obj.addProperty("error", message);
|
||||||
|
obj.add("ban", details);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean checkPassword(String plain, String stored) {
|
private static boolean checkPassword(String plain, String stored) {
|
||||||
String compatible = stored.startsWith("$2y$") ? "$2a$" + stored.substring(4) : stored;
|
String compatible = stored.startsWith("$2y$") ? "$2a$" + stored.substring(4) : stored;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public class WsAesDecoder extends MessageToMessageDecoder<ByteBuf> {
|
|||||||
byte[] plain = WsSessionCrypto.aesGcmDecrypt(key, nonce, ct);
|
byte[] plain = WsSessionCrypto.aesGcmDecrypt(key, nonce, ct);
|
||||||
out.add(Unpooled.wrappedBuffer(plain));
|
out.add(Unpooled.wrappedBuffer(plain));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOGGER.warn("[ws-crypto] AES-GCM decrypt failed", e);
|
LOGGER.warn("[ws-crypto] AES-GCM decrypt failed ({}), closing channel", e.getClass().getSimpleName());
|
||||||
ctx.close();
|
ctx.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-2
@@ -3,6 +3,7 @@ package com.eu.habbo.networking.gameserver.crypto;
|
|||||||
import com.eu.habbo.Emulator;
|
import com.eu.habbo.Emulator;
|
||||||
import com.eu.habbo.networking.gameserver.GameServerAttributes;
|
import com.eu.habbo.networking.gameserver.GameServerAttributes;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
@@ -118,7 +119,7 @@ public class WsHandshakeHandler extends ChannelInboundHandlerAdapter {
|
|||||||
|
|
||||||
LOGGER.debug("[ws-crypto] handshake complete for {}", clientAddress(ctx));
|
LOGGER.debug("[ws-crypto] handshake complete for {}", clientAddress(ctx));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOGGER.error("[ws-crypto] handshake failed from " + clientAddress(ctx), e);
|
LOGGER.warn("[ws-crypto] handshake failed from {} : {}", clientAddress(ctx), friendlyReason(e));
|
||||||
ctx.close();
|
ctx.close();
|
||||||
} finally {
|
} finally {
|
||||||
in.release();
|
in.release();
|
||||||
@@ -131,9 +132,21 @@ public class WsHandshakeHandler extends ChannelInboundHandlerAdapter {
|
|||||||
return String.valueOf(ctx.channel().remoteAddress());
|
return String.valueOf(ctx.channel().remoteAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String friendlyReason(Throwable t) {
|
||||||
|
if (t == null) return "unknown";
|
||||||
|
String name = t.getClass().getSimpleName();
|
||||||
|
String msg = t.getMessage();
|
||||||
|
return (msg == null || msg.isEmpty()) ? name : name + ": " + msg;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||||
LOGGER.error("[ws-crypto] handshake handler error", cause);
|
if (cause instanceof java.io.IOException) {
|
||||||
|
LOGGER.debug("[ws-crypto] client disconnected during handshake ({}): {}",
|
||||||
|
clientAddress(ctx), friendlyReason(cause));
|
||||||
|
} else {
|
||||||
|
LOGGER.error("[ws-crypto] handshake handler error from " + clientAddress(ctx), cause);
|
||||||
|
}
|
||||||
ctx.close();
|
ctx.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -2,7 +2,6 @@ package com.eu.habbo.networking.gameserver.decoders;
|
|||||||
|
|
||||||
import com.eu.habbo.messages.ClientMessage;
|
import com.eu.habbo.messages.ClientMessage;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||||
|
|
||||||
@@ -12,8 +11,7 @@ public class GameByteDecoder extends ByteToMessageDecoder {
|
|||||||
@Override
|
@Override
|
||||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
|
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
|
||||||
short header = in.readShort();
|
short header = in.readShort();
|
||||||
ByteBuf body = Unpooled.copiedBuffer(in.readBytes(in.readableBytes()));
|
ByteBuf body = in.readBytes(in.readableBytes());
|
||||||
|
|
||||||
out.add(new ClientMessage(header, body));
|
out.add(new ClientMessage(header, body));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Binary file not shown.
Reference in New Issue
Block a user