diff --git a/.gitignore b/.gitignore index a8e27a8a..fc2abda4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,9 @@ .idea/ *.iml +# VS Code user-specific stuff +.vscode/ + # Compiled class file *.class diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..7b016a89 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.compile.nullAnalysis.mode": "automatic" +} \ No newline at end of file diff --git a/src/main/java/net/elytrium/limboauth/LimboAuth.java b/src/main/java/net/elytrium/limboauth/LimboAuth.java index a0d66330..8ca54363 100644 --- a/src/main/java/net/elytrium/limboauth/LimboAuth.java +++ b/src/main/java/net/elytrium/limboauth/LimboAuth.java @@ -48,8 +48,6 @@ import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier; import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier; import com.velocitypowered.api.scheduler.ScheduledTask; -import com.velocitypowered.proxy.util.ratelimit.Ratelimiter; -import com.velocitypowered.proxy.util.ratelimit.Ratelimiters; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.whitfin.siphash.SipHasher; import java.io.File; @@ -137,7 +135,7 @@ ) public class LimboAuth { - public static final Ratelimiter RATELIMITER = Ratelimiters.createWithMilliseconds(5000); + public static final InetAddressRateLimiter RATELIMITER = new InetAddressRateLimiter(5000); // Architectury API appends /541f59e4256a337ea252bc482a009d46 to the channel name, that is a UUID.nameUUIDFromBytes from the TokenMessage class name private static final ChannelIdentifier MOD_CHANNEL = MinecraftChannelIdentifier.create("limboauth", "mod/541f59e4256a337ea252bc482a009d46"); @@ -1112,4 +1110,30 @@ public enum PremiumState { RATE_LIMIT, ERROR } + + public static final class InetAddressRateLimiter { + private final long windowMillis; + private final ConcurrentHashMap lastAttempt = new ConcurrentHashMap<>(); + private static final long CLEANUP_INTERVAL = 3600000; // 1 hour in milliseconds + + public InetAddressRateLimiter(long windowMillis) { + this.windowMillis = windowMillis; + } + + public boolean attempt(InetAddress address) { + long now = System.currentTimeMillis(); + Long lastAttemptTime = this.lastAttempt.get(address); + + if (lastAttemptTime == null || now - lastAttemptTime >= this.windowMillis) { + this.lastAttempt.put(address, now); + // Periodically cleanup old entries to prevent memory leaks + if (now % 60000 == 0) { // cleanup every minute + this.lastAttempt.entrySet().removeIf(e -> now - e.getValue() > CLEANUP_INTERVAL); + } + return true; + } + + return false; + } + } } diff --git a/src/main/java/net/elytrium/limboauth/handler/AuthSessionHandler.java b/src/main/java/net/elytrium/limboauth/handler/AuthSessionHandler.java index 0f053b70..9610e03f 100644 --- a/src/main/java/net/elytrium/limboauth/handler/AuthSessionHandler.java +++ b/src/main/java/net/elytrium/limboauth/handler/AuthSessionHandler.java @@ -53,12 +53,15 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.title.Title; import org.checkerframework.checker.nullness.qual.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class AuthSessionHandler implements LimboSessionHandler { public static final CodeVerifier TOTP_CODE_VERIFIER = new DefaultCodeVerifier(new DefaultCodeGenerator(), new SystemTimeProvider()); private static final BCrypt.Verifyer HASH_VERIFIER = BCrypt.verifyer(); private static final BCrypt.Hasher HASHER = BCrypt.withDefaults(); + private static final Logger LOGGER = LoggerFactory.getLogger(AuthSessionHandler.class); private static Component ratelimited; private static BossBar.Color bossbarColor; @@ -442,7 +445,7 @@ private void finishAuth() { } catch (SQLException e) { throw new SQLRuntimeException(e); } catch (Throwable e) { - e.printStackTrace(); + LOGGER.warn("Error occurred during finalization after authentication for player: {}", this.proxyPlayer.getUsername(), e); } this.plugin.cacheAuthUser(this.proxyPlayer);