diff --git a/eaglercraftbungee/Java-WebSocket-1.5.1-with-dependencies.jar b/eaglercraftbungee/Java-WebSocket-1.5.1-with-dependencies.jar index 8fb33ad..8f103a7 100644 Binary files a/eaglercraftbungee/Java-WebSocket-1.5.1-with-dependencies.jar and b/eaglercraftbungee/Java-WebSocket-1.5.1-with-dependencies.jar differ diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/BungeeCord.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/BungeeCord.java index ad67b50..6cc8cfb 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/BungeeCord.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/BungeeCord.java @@ -19,7 +19,6 @@ import net.md_5.bungee.api.connection.ProxiedPlayer; import java.util.MissingResourceException; import net.md_5.bungee.protocol.packet.DefinedPacket; import io.netty.channel.ChannelException; -import java.util.Iterator; import io.netty.util.concurrent.GenericFutureListener; import java.net.SocketAddress; import io.netty.channel.EventLoopGroup; @@ -28,7 +27,6 @@ import io.netty.util.AttributeKey; import net.md_5.bungee.netty.PipelineUtils; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.bootstrap.ServerBootstrap; -import io.netty.util.concurrent.Future; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import net.md_5.bungee.api.config.ListenerInfo; @@ -38,7 +36,7 @@ import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.command.ConsoleCommandSender; import java.util.concurrent.TimeUnit; -import java.util.Calendar; +import java.util.ArrayList; import java.io.IOException; import jline.UnsupportedTerminal; import java.io.OutputStream; @@ -50,6 +48,14 @@ import jline.internal.Log; import java.io.PrintStream; import com.google.common.io.ByteStreams; import net.md_5.bungee.command.CommandFind; +import net.md_5.bungee.command.CommandGlobalBan; +import net.md_5.bungee.command.CommandGlobalBanIP; +import net.md_5.bungee.command.CommandGlobalBanRegex; +import net.md_5.bungee.command.CommandGlobalBanReload; +import net.md_5.bungee.command.CommandGlobalBanWildcard; +import net.md_5.bungee.command.CommandGlobalCheckBan; +import net.md_5.bungee.command.CommandGlobalListBan; +import net.md_5.bungee.command.CommandGlobalUnban; import net.md_5.bungee.command.CommandSend; import net.md_5.bungee.command.CommandPerms; import net.md_5.bungee.command.CommandBungee; @@ -61,10 +67,9 @@ import net.md_5.bungee.command.CommandEnd; import net.md_5.bungee.api.plugin.Command; import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.command.CommandReload; -import java.util.concurrent.ExecutorService; import net.md_5.bungee.scheduler.BungeeScheduler; import net.md_5.bungee.config.YamlConfig; -import net.md_5.bungee.eaglercraft.PluginEaglerAuth; +import net.md_5.bungee.eaglercraft.BanList; import net.md_5.bungee.eaglercraft.PluginEaglerSkins; import net.md_5.bungee.eaglercraft.WebSocketListener; @@ -99,6 +104,8 @@ public class BungeeCord extends ProxyServer { public final ScheduledThreadPoolExecutor executors; public final MultithreadEventLoopGroup eventLoops; private final Timer saveThread; + private final Timer reloadBanThread; + private final Timer closeInactiveSockets; private Collection listeners; private Collection wsListeners; private final Map connections; @@ -111,6 +118,7 @@ public class BungeeCord extends ProxyServer { private final TaskScheduler scheduler; private ConsoleReader consoleReader; private final Logger logger; + private Collection banCommands; public static BungeeCord getInstance() { return (BungeeCord) ProxyServer.getInstance(); @@ -122,6 +130,8 @@ public class BungeeCord extends ProxyServer { this.executors = new BungeeThreadPool(new ThreadFactoryBuilder().setNameFormat("Bungee Pool Thread #%1$d").build()); this.eventLoops = (MultithreadEventLoopGroup) new NioEventLoopGroup(Runtime.getRuntime().availableProcessors(), new ThreadFactoryBuilder().setNameFormat("Netty IO Thread #%1$d").build()); this.saveThread = new Timer("Reconnect Saver"); + this.reloadBanThread = new Timer("Ban List Reload"); + this.closeInactiveSockets = new Timer("close Inactive WebSockets"); this.listeners = new HashSet(); this.wsListeners = new HashSet(); this.connections = (Map) new CaseInsensitiveMap(); @@ -131,6 +141,7 @@ public class BungeeCord extends ProxyServer { this.pluginChannels = new HashSet(); this.pluginsFolder = new File("plugins"); this.scheduler = new BungeeScheduler(); + this.banCommands = new ArrayList(); this.getPluginManager().registerCommand(null, new CommandReload()); this.getPluginManager().registerCommand(null, new CommandEnd()); this.getPluginManager().registerCommand(null, new CommandList()); @@ -153,6 +164,42 @@ public class BungeeCord extends ProxyServer { this.logger.info("NOTE: This error is non crucial, and BungeeCord will still function correctly! Do not bug the author about it unless you are still unable to get it working"); } } + + public void reconfigureBanCommands(boolean replaceBukkit) { + if(banCommands.size() > 0) { + for(Command c : banCommands) { + this.getPluginManager().unregisterCommand(c); + } + banCommands.clear(); + } + + Command cBan = new CommandGlobalBan(replaceBukkit); + Command cUnban = new CommandGlobalUnban(replaceBukkit); + Command cBanReload = new CommandGlobalBanReload(replaceBukkit); + Command cBanIP = new CommandGlobalBanIP(replaceBukkit); + Command cBanWildcard = new CommandGlobalBanWildcard(replaceBukkit); + Command cBanRegex = new CommandGlobalBanRegex(replaceBukkit); + Command cBanCheck = new CommandGlobalCheckBan(replaceBukkit); + Command cBanList = new CommandGlobalListBan(replaceBukkit); + + banCommands.add(cBan); + banCommands.add(cUnban); + banCommands.add(cBanReload); + banCommands.add(cBanIP); + banCommands.add(cBanWildcard); + banCommands.add(cBanRegex); + banCommands.add(cBanCheck); + banCommands.add(cBanList); + + this.getPluginManager().registerCommand(null, cBan); + this.getPluginManager().registerCommand(null, cUnban); + this.getPluginManager().registerCommand(null, cBanReload); + this.getPluginManager().registerCommand(null, cBanIP); + this.getPluginManager().registerCommand(null, cBanWildcard); + this.getPluginManager().registerCommand(null, cBanRegex); + this.getPluginManager().registerCommand(null, cBanCheck); + this.getPluginManager().registerCommand(null, cBanList); + } public static void main(final String[] args) throws Exception { final BungeeCord bungee = new BungeeCord(); @@ -186,6 +233,20 @@ public class BungeeCord extends ProxyServer { BungeeCord.this.getReconnectHandler().save(); } }, 0L, TimeUnit.MINUTES.toMillis(5L)); + this.reloadBanThread.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + BanList.maybeReloadBans(null); + } + }, 0L, TimeUnit.SECONDS.toMillis(3L)); + this.closeInactiveSockets.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + for(WebSocketListener lst : BungeeCord.this.wsListeners) { + lst.closeInactiveSockets(); + } + } + }, 0L, TimeUnit.SECONDS.toMillis(10L)); } public void startListeners() { diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/UserConnection.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/UserConnection.java index cb3d9da..25651d9 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/UserConnection.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/UserConnection.java @@ -4,13 +4,10 @@ package net.md_5.bungee; -import net.md_5.bungee.api.connection.Server; -import net.md_5.bungee.api.connection.PendingConnection; import java.beans.ConstructorProperties; import net.md_5.bungee.util.CaseInsensitiveSet; import java.util.HashSet; import net.md_5.bungee.api.config.TexturePackInfo; -import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.event.PermissionCheckEvent; import java.util.Collections; import java.net.InetSocketAddress; @@ -36,10 +33,14 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; import net.md_5.bungee.api.ChatColor; import java.util.Objects; +import java.util.WeakHashMap; + import net.md_5.bungee.api.event.ServerConnectEvent; import net.md_5.bungee.protocol.packet.DefinedPacket; import com.google.common.base.Preconditions; import java.util.Iterator; +import java.util.Map; + import net.md_5.bungee.api.connection.Connection; import net.md_5.bungee.api.score.Scoreboard; import net.md_5.bungee.protocol.packet.PacketCCSettings; @@ -71,6 +72,7 @@ public final class UserConnection implements ProxiedPlayer { private final Scoreboard serverSentScoreboard; private String displayName; private final Connection.Unsafe unsafe; + private final Map attachment = new WeakHashMap(); public void init() { this.displayName = this.name; @@ -383,4 +385,9 @@ public final class UserConnection implements ProxiedPlayer { public String getDisplayName() { return this.displayName; } + + @Override + public Map getAttachment() { + return attachment; + } } diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/api/CommandSender.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/api/CommandSender.java index 7f93805..fe7f9d7 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/api/CommandSender.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/api/CommandSender.java @@ -5,6 +5,7 @@ package net.md_5.bungee.api; import java.util.Collection; +import java.util.Map; public interface CommandSender { String getName(); @@ -22,4 +23,6 @@ public interface CommandSender { boolean hasPermission(final String p0); void setPermission(final String p0, final boolean p1); + + Map getAttachment(); } diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/api/config/ListenerInfo.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/api/config/ListenerInfo.java index 801c1be..9efb56c 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/api/config/ListenerInfo.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/api/config/ListenerInfo.java @@ -5,6 +5,9 @@ package net.md_5.bungee.api.config; import java.beans.ConstructorProperties; +import java.io.File; + +import net.md_5.bungee.api.ServerIcon; import net.md_5.bungee.api.tab.TabListHandler; import java.util.Map; import java.net.InetSocketAddress; @@ -18,13 +21,18 @@ public class ListenerInfo { private final String fallbackServer; private final boolean forceDefault; private final boolean websocket; + private final boolean forwardIp; private final Map forcedHosts; private final TexturePackInfo texturePack; private final Class tabList; + private final String serverIcon; + private final int[] serverIconCache; + private boolean serverIconLoaded; + private boolean serverIconSet; - @ConstructorProperties({ "host", "motd", "maxPlayers", "tabListSize", "defaultServer", "fallbackServer", "forceDefault", "websocket", "forcedHosts", "texturePack", "tabList" }) + @ConstructorProperties({ "host", "motd", "maxPlayers", "tabListSize", "defaultServer", "fallbackServer", "forceDefault", "websocket", "forwardIp", "forcedHosts", "texturePack", "tabList", "serverIcon" }) public ListenerInfo(final InetSocketAddress host, final String motd, final int maxPlayers, final int tabListSize, final String defaultServer, final String fallbackServer, final boolean forceDefault, final boolean websocket, - final Map forcedHosts, final TexturePackInfo texturePack, final Class tabList) { + final boolean forwardIp, final Map forcedHosts, final TexturePackInfo texturePack, final Class tabList, final String serverIcon) { this.host = host; this.motd = motd; this.maxPlayers = maxPlayers; @@ -33,9 +41,14 @@ public class ListenerInfo { this.fallbackServer = fallbackServer; this.forceDefault = forceDefault; this.websocket = websocket; + this.forwardIp = forwardIp; this.forcedHosts = forcedHosts; this.texturePack = texturePack; this.tabList = tabList; + this.serverIcon = serverIcon; + this.serverIconCache = new int[4096]; + this.serverIconLoaded = false; + this.serverIconSet = false; } public InetSocketAddress getHost() { @@ -120,6 +133,9 @@ public class ListenerInfo { if (this.getTabListSize() != other.getTabListSize()) { return false; } + if (this.isWebsocket() != other.isWebsocket()) { + return false; + } final Object this$defaultServer = this.getDefaultServer(); final Object other$defaultServer = other.getDefaultServer(); Label_0165: { @@ -180,6 +196,15 @@ public class ListenerInfo { } else if (this$tabList.equals(other$tabList)) { return true; } + final Object this$getServerIcon = this.getServerIcon(); + final Object other$getServerIcon = other.getServerIcon(); + if (this$getServerIcon == null) { + if (other$getServerIcon == null) { + return true; + } + } else if (this$getServerIcon.equals(other$getServerIcon)) { + return true; + } return false; } @@ -208,6 +233,8 @@ public class ListenerInfo { result = result * 31 + (($texturePack == null) ? 0 : $texturePack.hashCode()); final Object $tabList = this.getTabList(); result = result * 31 + (($tabList == null) ? 0 : $tabList.hashCode()); + final Object $serverIconCache = this.getTabList(); + result = result * 31 + (($serverIconCache == null) ? 0 : $serverIconCache.hashCode()); return result; } @@ -220,4 +247,9 @@ public class ListenerInfo { public boolean isWebsocket() { return websocket; } + + public boolean hasForwardedHeaders() { + return forwardIp; + } + } diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBan.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBan.java new file mode 100644 index 0000000..2ee3dab --- /dev/null +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBan.java @@ -0,0 +1,61 @@ +package net.md_5.bungee.command; + +import java.util.Collection; + +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.plugin.Command; +import net.md_5.bungee.eaglercraft.BanList; + +public class CommandGlobalBan extends Command { + + private final boolean replaceBukkit; + + public CommandGlobalBan(boolean replaceBukkit) { + super(replaceBukkit ? "ban" : "eag-ban", "bungeecord.command.eag.ban", replaceBukkit ? new String[] { "kickban", "eag-ban", "e-ban", "gban" } : new String[] { "e-ban", "gban" }); + this.replaceBukkit = replaceBukkit; + } + + @Override + public void execute(CommandSender p0, String[] p1) { + if(p1.length >= 1) { + String p = p1[0].trim().toLowerCase(); + if(p0.getName().equalsIgnoreCase(p)) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "You cannot ban yourself"); + return; + } + String reason = "The ban hammer has spoken!"; + if(p1.length >= 2) { + reason = ""; + for(int i = 1; i < p1.length; ++i) { + if(reason.length() > 0) { + reason += " "; + } + reason += p1[i]; + } + } + String wasTheKick = null; + Collection playerz = BungeeCord.getInstance().getPlayers(); + for(ProxiedPlayer pp : playerz) { + if(pp.getName().equalsIgnoreCase(p)) { + wasTheKick = pp.getName(); + pp.disconnect("" + ChatColor.RED + "You are banned.\n" + ChatColor.DARK_GRAY + "Reason: " + reason); + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.WHITE + "Kicked: " + pp.getName()); + } + } + if(BanList.ban(p, reason)) { + if(wasTheKick == null) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.YELLOW + "Warning! '" + ChatColor.WHITE + p + ChatColor.YELLOW + "' is not currently on this server"); + } + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Username '" + ChatColor.WHITE + (wasTheKick == null ? p : wasTheKick) + ChatColor.GREEN + "' was added to the ban list"); + }else { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Username '" + ChatColor.WHITE + p + ChatColor.RED + "' is already banned"); + } + }else { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "To ban a player, use: " + ChatColor.WHITE + "/" + (replaceBukkit?"":"eag-") + "ban [reason]"); + } + } + +} diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBanIP.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBanIP.java new file mode 100644 index 0000000..0fcbc14 --- /dev/null +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBanIP.java @@ -0,0 +1,148 @@ +package net.md_5.bungee.command; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.List; + +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.plugin.Command; +import net.md_5.bungee.eaglercraft.BanList; +import net.md_5.bungee.eaglercraft.BanList.IPBan; + +public class CommandGlobalBanIP extends Command { + + private final boolean replaceBukkit; + + public CommandGlobalBanIP(boolean replaceBukkit) { + super(replaceBukkit ? "ban-ip" : "eag-ban-ip", "bungeecord.command.eag.banip", (replaceBukkit ? new String[] {"eag-ban-ip", "banip", "e-ban-ip", "gban-ip"} : + new String[] {"gban-ip", "e-ban-ip", "gbanip", "e-banip"}) ); + this.replaceBukkit = replaceBukkit; + } + + @Override + public void execute(CommandSender p0, String[] p1) { + String w = (String) p0.getAttachment().get("banIPWaitingToAdd"); + if(w != null) { + List lst = (List)p0.getAttachment().get("banIPWaitingToKick"); + if(p1.length != 1 || (!p1[0].equalsIgnoreCase("confirm") && !p1[0].equalsIgnoreCase("cancel"))) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Type " + ChatColor.WHITE + (replaceBukkit ? "/ban-ip" : "/eag-ban-ip") + " confirm" + ChatColor.RED + " to add IP " + ChatColor.WHITE + w + + ChatColor.RED + " and ban " + ChatColor.WHITE + lst.size() + ChatColor.RED + " players"); + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Type " + ChatColor.WHITE + (replaceBukkit ? "/ban-ip" : "/eag-ban-ip") + " cancel" + ChatColor.RED + " to cancel this operation"); + }else { + if(p1[0].equalsIgnoreCase("confirm")) { + try { + if(BanList.banIP(w)) { + for(ProxiedPlayer pp : lst) { + pp.disconnect("" + ChatColor.RED + "You are banned.\n" + ChatColor.DARK_GRAY + "Reason: banned by IP"); + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Kicked: " + ChatColor.WHITE + pp.getName()); + } + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Added IP '" + ChatColor.WHITE + w + ChatColor.GREEN + "' to the ban list"); + }else { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "IP '" + ChatColor.WHITE + w + ChatColor.RED + "' is already on the ban list"); + } + } catch (UnknownHostException e) { + e.printStackTrace(); + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "ERROR: address '" + ChatColor.WHITE + w + ChatColor.RED + "' is suddenly invalid for some reason"); + } + }else { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Canceled ban"); + } + p0.getAttachment().remove("banIPWaitingToAdd"); + p0.getAttachment().remove("banIPWaitingToKick"); + } + return; + } + if(p1.length != 1) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "How to use: " + ChatColor.WHITE + (replaceBukkit ? "/ban-ip" : "/eag-ban-ip") + " "); + return; + } + boolean isPlayer = false; + IPBan p = null; + try { + p = BanList.constructIpBan(p1[0]); + }catch(Throwable t) { + for(ProxiedPlayer pp : BungeeCord.getInstance().getPlayers()) { + if(pp.getName().equalsIgnoreCase(p1[0])) { + Object addr = pp.getAttachment().get("remoteAddr"); + if(addr != null) { + String newAddr = ((InetAddress)addr).getHostAddress(); + isPlayer = true; + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Player '" + ChatColor.WHITE + p1[0] + ChatColor.GREEN + "' has IP " + ChatColor.WHITE + newAddr); + p1[0] = newAddr; + try { + p = BanList.constructIpBan(p1[0]); + }catch(UnknownHostException ex) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Address '" + ChatColor.WHITE + p1[0] + "' is suddenly invalid: " + ChatColor.WHITE + p1[0]); + return; + } + } + break; + } + } + if(!isPlayer) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Player '" + ChatColor.WHITE + p1[0] + "' is not on this server"); + return; + } + } + boolean blocked = false; + for(IPBan b : BanList.blockedBans) { + if(b.checkBan(p.getBaseAddress()) || p.checkBan(b.getBaseAddress())) { + blocked = true; + } + } + if(blocked) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Cannot ban '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "', it will ban local addresses that may break your game"); + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "To force, add to the " + ChatColor.WHITE + "[IPs]" + ChatColor.RED + " section of " + ChatColor.WHITE + "bans.txt" + ChatColor.RED + " in your bungee directory"); + return; + } + boolean isSenderGonnaGetKicked = false; + List usersThatAreGonnaBeKicked = new ArrayList(); + for(ProxiedPlayer pp : BungeeCord.getInstance().getPlayers()) { + Object addr = pp.getAttachment().get("remoteAddr"); + if(addr != null) { + InetAddress addrr = (InetAddress)addr; + if(p.checkBan(addrr)) { + usersThatAreGonnaBeKicked.add(pp); + if(pp.getName().equalsIgnoreCase(p0.getName())) { + isSenderGonnaGetKicked = true; + break; + } + } + } + } + if(isSenderGonnaGetKicked) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "banning address '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' will ban you off of your own server"); + return; + } + if(usersThatAreGonnaBeKicked.size() > 1) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "WARNING: banning address '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is gonna ban " + + ChatColor.WHITE + usersThatAreGonnaBeKicked.size() + ChatColor.RED + " players off of your server"); + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Type " + ChatColor.WHITE + (replaceBukkit ? "/ban-ip" : "/eag-ban-ip") + " confirm" + ChatColor.RED + " to continue, or type " + + ChatColor.WHITE + (replaceBukkit ? "/ban-ip" : "/eag-ban-ip") + " cancel" + ChatColor.RED + " to cancel"); + p0.getAttachment().put("banIPWaitingToKick", usersThatAreGonnaBeKicked); + p0.getAttachment().put("banIPWaitingToAdd", p1[0]); + }else { + try { + if(BanList.banIP(p1[0])) { + if(usersThatAreGonnaBeKicked.size() > 0) { + usersThatAreGonnaBeKicked.get(0).disconnect("" + ChatColor.RED + "You are banned.\n" + ChatColor.DARK_GRAY + "Reason: banned by IP"); + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Kicked: " + ChatColor.WHITE + usersThatAreGonnaBeKicked.get(0).getName()); + } + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Added IP '" + ChatColor.WHITE + p1[0] + ChatColor.GREEN + "' to the ban list"); + }else { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "IP '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is already on the ban list"); + } + } catch (UnknownHostException e) { + e.printStackTrace(); + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "ERROR: address '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is suddenly invalid for some reason"); + return; + } + } + } + +} diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBanRegex.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBanRegex.java new file mode 100644 index 0000000..076711f --- /dev/null +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBanRegex.java @@ -0,0 +1,106 @@ +package net.md_5.bungee.command; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.plugin.Command; +import net.md_5.bungee.eaglercraft.BanList; + +public class CommandGlobalBanRegex extends Command { + + private final boolean replaceBukkit; + + public CommandGlobalBanRegex(boolean replaceBukkit) { + super(replaceBukkit ? "ban-regex" : "eag-ban-regex", "bungeecord.command.eag.banregex", replaceBukkit ? new String[] { "eag-ban-regex", "e-ban-regex", + "gban-regex", "eag-banregex", "e-banregex", "gbanregex", "banregex" } : new String[] { "e-ban-regex", "gban-regex", + "eag-banregex", "e-banregex", "gbanregex" }); + this.replaceBukkit = replaceBukkit; + } + + @Override + public void execute(CommandSender p0, String[] p1) { + String w = (String) p0.getAttachment().get("banRegexWaitingToAdd"); + if(w != null) { + List lst = (List)p0.getAttachment().get("banRegexWaitingToKick"); + if(p1.length != 1 || (!p1[0].equalsIgnoreCase("confirm") && !p1[0].equalsIgnoreCase("cancel"))) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Type " + ChatColor.WHITE + (replaceBukkit ? "/ban-regex" : "/eag-ban-regex") + " confirm" + ChatColor.RED + " to add regex " + ChatColor.WHITE + w + + ChatColor.RED + " and ban " + ChatColor.WHITE + lst.size() + ChatColor.RED + " players"); + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Type " + ChatColor.WHITE + (replaceBukkit ? "/ban-regex" : "/eag-ban-regex") + " cancel" + ChatColor.RED + " to cancel this operation"); + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.YELLOW + "Note: all usernames are converted to lowercase before being matched"); + }else { + if(p1[0].equalsIgnoreCase("confirm")) { + if(BanList.banRegex(w)) { + for(ProxiedPlayer pp : lst) { + pp.disconnect("" + ChatColor.RED + "You are banned.\n" + ChatColor.DARK_GRAY + "Reason: banned by regex"); + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Kicked: " + ChatColor.WHITE + pp.getName()); + } + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Added regex '" + ChatColor.WHITE + w + ChatColor.GREEN + "' to the ban list"); + }else { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Regex '" + ChatColor.WHITE + w + ChatColor.RED + "' is already banned"); + } + }else { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Canceled ban"); + } + p0.getAttachment().remove("banRegexWaitingToAdd"); + p0.getAttachment().remove("banRegexWaitingToKick"); + } + return; + } + if(p1.length != 1) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "How to use: " + ChatColor.WHITE + (replaceBukkit ? "/ban-regex" : "/eag-ban-regex") + " "); + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.YELLOW + "Note: all usernames are converted to lowercase before being matched"); + return; + } + Pattern p; + try { + p = Pattern.compile(p1[0]); + }catch(Throwable t) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Regex syntax error: " + t.getMessage()); + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.YELLOW + "Note: all usernames are converted to lowercase before being matched"); + return; + } + boolean isSenderGonnaGetKicked = false; + List usersThatAreGonnaBeKicked = new ArrayList(); + for(ProxiedPlayer pp : BungeeCord.getInstance().getPlayers()) { + String n = pp.getName().toLowerCase(); + if(p.matcher(n).matches()) { + usersThatAreGonnaBeKicked.add(pp); + if(n.equalsIgnoreCase(p0.getName())) { + isSenderGonnaGetKicked = true; + break; + } + } + } + if(isSenderGonnaGetKicked) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "banning regex '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is gonna ban your own username"); + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.YELLOW + "Note: all usernames are converted to lowercase before being matched"); + return; + } + if(usersThatAreGonnaBeKicked.size() > 1) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "WARNING: banning regex '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is gonna ban " + + ChatColor.WHITE + usersThatAreGonnaBeKicked.size() + ChatColor.RED + " players off of your server"); + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Type " + ChatColor.WHITE + (replaceBukkit ? "/ban-regex" : "/eag-ban-regex") + " confirm" + ChatColor.RED + " to continue, or type " + + ChatColor.WHITE + "/eag-ban-regex cancel" + ChatColor.RED + " to cancel"); + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.YELLOW + "Note: all usernames are converted to lowercase before being matched"); + p0.getAttachment().put("banRegexWaitingToKick", usersThatAreGonnaBeKicked); + p0.getAttachment().put("banRegexWaitingToAdd", p1[0]); + }else { + if(BanList.banRegex(p1[0])) { + if(usersThatAreGonnaBeKicked.size() > 0) { + usersThatAreGonnaBeKicked.get(0).disconnect("" + ChatColor.RED + "You are banned.\n" + ChatColor.DARK_GRAY + "Reason: banned by regex"); + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Kicked: " + ChatColor.WHITE + usersThatAreGonnaBeKicked.get(0).getName()); + } + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Added regex '" + ChatColor.WHITE + p1[0] + ChatColor.GREEN + "' to the ban list"); + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.YELLOW + "Note: all usernames are converted to lowercase before being matched"); + }else { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Regex '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is already banned"); + } + } + } + +} diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBanReload.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBanReload.java new file mode 100644 index 0000000..3063b31 --- /dev/null +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBanReload.java @@ -0,0 +1,21 @@ +package net.md_5.bungee.command; + +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.plugin.Command; +import net.md_5.bungee.eaglercraft.BanList; + +public class CommandGlobalBanReload extends Command { + + public CommandGlobalBanReload(boolean replaceBukkit) { + super(replaceBukkit ? "reloadban" : "eag-reloadban", "bungeecord.command.eag.reloadban", replaceBukkit ? new String[] { "eag-reloadban", "banreload", "eag-banreload", "e-reloadban", + "e-banreload", "gbanreload", "greloadban"} : new String[] { "eag-banreload", "e-reloadban", "e-banreload", "gbanreload", "greloadban"}); + } + + @Override + public void execute(CommandSender p0, String[] p1) { + BanList.maybeReloadBans(p0); + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.WHITE + "Ban list reloaded"); + } + +} diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBanWildcard.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBanWildcard.java new file mode 100644 index 0000000..1d4783a --- /dev/null +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBanWildcard.java @@ -0,0 +1,125 @@ +package net.md_5.bungee.command; + +import java.util.ArrayList; +import java.util.List; + +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.plugin.Command; +import net.md_5.bungee.eaglercraft.BanList; + +public class CommandGlobalBanWildcard extends Command { + + private final boolean replaceBukkit; + + public CommandGlobalBanWildcard(boolean replaceBukkit) { + super(replaceBukkit ? "ban-wildcard" : "eag-ban-wildcard", "bungeecord.command.eag.banwildcard", replaceBukkit ? new String[] { "eag-ban-wildcard", "e-ban-wildcard", "gban-wildcard", + "banwildcard", "eag-banwildcard", "banwildcard"} : new String[] { "e-ban-wildcard", "gban-wildcard", "eag-banwildcard"}); + this.replaceBukkit = replaceBukkit; + } + + @Override + public void execute(CommandSender p0, String[] p1) { + String w = (String) p0.getAttachment().get("banWildcardWaitingToAdd"); + if(w != null) { + List lst = (List)p0.getAttachment().get("banWildcardWaitingToKick"); + if(p1.length != 1 || (!p1[0].equalsIgnoreCase("confirm") && !p1[0].equalsIgnoreCase("cancel"))) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Type " + ChatColor.WHITE + (replaceBukkit ? "/ban-wildcard" : "/eag-ban-wildcard") + " confirm" + ChatColor.RED + " to add wildcard " + ChatColor.WHITE + w + + ChatColor.RED + " and ban " + ChatColor.WHITE + lst.size() + ChatColor.RED + " players"); + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Type " + ChatColor.WHITE + (replaceBukkit ? "/ban-wildcard" : "/eag-ban-wildcard") + " cancel" + ChatColor.RED + " to cancel this operation"); + }else { + if(p1[0].equalsIgnoreCase("confirm")) { + if(BanList.banWildcard(w)) { + for(ProxiedPlayer pp : lst) { + pp.disconnect("" + ChatColor.RED + "You are banned.\n" + ChatColor.DARK_GRAY + "Reason: banned by wildcard"); + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Kicked: " + ChatColor.WHITE + pp.getName()); + } + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Added wildcard '" + ChatColor.WHITE + w + ChatColor.GREEN + "' to the ban list"); + }else { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Wildcard '" + ChatColor.WHITE + w + ChatColor.RED + "' is already banned"); + } + }else { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Canceled ban"); + } + p0.getAttachment().remove("banWildcardWaitingToAdd"); + p0.getAttachment().remove("banWildcardWaitingToKick"); + } + return; + } + if(p1.length != 1) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "How to use: " + ChatColor.WHITE + (replaceBukkit ? "/ban-wildcard" : "/eag-ban-wildcard") + " "); + return; + } + p1[0] = p1[0].toLowerCase(); + String s = p1[0]; + boolean startStar = s.startsWith("*"); + if(startStar) { + s = s.substring(1); + } + boolean endStar = s.endsWith("*"); + if(endStar) { + s = s.substring(0, s.length() - 1); + } + if(!startStar && !endStar) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "'" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is not a wildcard, try '" + + ChatColor.WHITE + "*" + p1[0] + ChatColor.RED + "' or '" + ChatColor.WHITE + p1[0] + "*" + ChatColor.RED + "' or '" + ChatColor.WHITE + + "*" + p1[0] + "*" + ChatColor.RED + "' instead"); + return; + } + boolean isSenderGonnaGetKicked = false; + List usersThatAreGonnaBeKicked = new ArrayList(); + for(ProxiedPlayer pp : BungeeCord.getInstance().getPlayers()) { + String n = pp.getName().toLowerCase(); + if(startStar && endStar) { + if(n.contains(s)) { + usersThatAreGonnaBeKicked.add(pp); + if(pp.getName().equalsIgnoreCase(p0.getName())) { + isSenderGonnaGetKicked = true; + break; + } + } + }else if(startStar) { + if(n.endsWith(s)) { + usersThatAreGonnaBeKicked.add(pp); + if(pp.getName().equalsIgnoreCase(p0.getName())) { + isSenderGonnaGetKicked = true; + break; + } + } + }else if(endStar) { + if(n.startsWith(s)) { + usersThatAreGonnaBeKicked.add(pp); + if(pp.getName().equalsIgnoreCase(p0.getName())) { + isSenderGonnaGetKicked = true; + break; + } + } + } + } + if(isSenderGonnaGetKicked) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "banning wildcard '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is gonna ban your own username"); + return; + } + if(usersThatAreGonnaBeKicked.size() > 1) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "WARNING: banning wildcard '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is gonna ban " + + ChatColor.WHITE + usersThatAreGonnaBeKicked.size() + ChatColor.RED + " players off of your server"); + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Type " + ChatColor.WHITE + (replaceBukkit ? "/ban-wildcard" : "/eag-ban-wildcard") + " confirm" + ChatColor.RED + " to continue, or type " + + ChatColor.WHITE + (replaceBukkit ? "/ban-wildcard" : "/eag-ban-wildcard") + " cancel" + ChatColor.RED + " to cancel"); + p0.getAttachment().put("banWildcardWaitingToKick", usersThatAreGonnaBeKicked); + p0.getAttachment().put("banWildcardWaitingToAdd", p1[0]); + }else { + if(BanList.banWildcard(p1[0])) { + if(usersThatAreGonnaBeKicked.size() > 0) { + usersThatAreGonnaBeKicked.get(0).disconnect("" + ChatColor.RED + "You are banned.\n" + ChatColor.DARK_GRAY + "Reason: banned by wildcard"); + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Kicked: " + ChatColor.WHITE + usersThatAreGonnaBeKicked.get(0).getName()); + } + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Added wildcard '" + ChatColor.WHITE + p1[0] + ChatColor.GREEN + "' to the ban list"); + }else { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Wildcard '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is already banned"); + } + } + } + +} diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandGlobalCheckBan.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandGlobalCheckBan.java new file mode 100644 index 0000000..be2bd85 --- /dev/null +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandGlobalCheckBan.java @@ -0,0 +1,56 @@ +package net.md_5.bungee.command; + +import java.net.InetAddress; + +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.plugin.Command; +import net.md_5.bungee.eaglercraft.BanList; +import net.md_5.bungee.eaglercraft.BanList.BanCheck; +import net.md_5.bungee.eaglercraft.BanList.BanState; + +public class CommandGlobalCheckBan extends Command { + + private final boolean replaceBukkit; + + public CommandGlobalCheckBan(boolean replaceBukkit) { + super(replaceBukkit ? "banned" : "eag-bannned", "bungeecord.command.eag.banned", replaceBukkit ? new String[] { "eag-banned", "isbanned", "e-banned", "gbanned", "eag-isbanned", "e-isbanned", "gisbanned" } : + new String[] { "e-banned", "gbanned", "eag-isbanned", "e-isbanned", "gisbanned" }); + this.replaceBukkit = replaceBukkit; + } + + @Override + public void execute(CommandSender p0, String[] p1) { + if(p1.length != 1) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "To check if a player or IP is banned, use: " + ChatColor.WHITE + (replaceBukkit ? "/banned" : "/eag-banned") + " "); + }else { + BanCheck bc = BanList.checkBanned(p1[0]); + if(!bc.isBanned()) { + try { + InetAddress addr = InetAddress.getByName(p1[0]); + bc = BanList.checkIpBanned(addr); + if(bc.isBanned()) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "IP address '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is banned by: " + + "'" + ChatColor.WHITE + bc.match + ChatColor.RED + "' " + ChatColor.YELLOW + "(" + bc.string + ")"); + return; + } + }catch(Throwable t) { + // no + } + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Player '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' has not been banned"); + }else { + if(bc.reason == BanState.USER_BANNED) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Player '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is banned by username, reason: " + + ChatColor.YELLOW + "\"" + bc.string + "\""); + }else if(bc.reason == BanState.WILDCARD_BANNED) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Player '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is banned by wildcard: " + + ChatColor.WHITE + "\"" + bc.match + "\""); + }else if(bc.reason == BanState.REGEX_BANNED) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Player '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is banned by regex: " + + ChatColor.WHITE + "\"" + bc.match + "\""); + } + } + } + } + +} diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandGlobalListBan.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandGlobalListBan.java new file mode 100644 index 0000000..52c36c7 --- /dev/null +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandGlobalListBan.java @@ -0,0 +1,66 @@ +package net.md_5.bungee.command; + +import java.util.List; + +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.plugin.Command; +import net.md_5.bungee.eaglercraft.BanList; + +public class CommandGlobalListBan extends Command { + + private final boolean replaceBukkit; + + public CommandGlobalListBan(boolean replaceBukkit) { + super(replaceBukkit ? "banlist" : "eag-banlist", "bungeecord.command.eag.banlist", replaceBukkit ? new String[] { "eag-banlist", "gbanlist", "e-banlist", + "gbanlist" } : new String[] { "gbanlist", "e-banlist" }); + this.replaceBukkit = replaceBukkit; + } + + @Override + public void execute(CommandSender p0, String[] p1) { + if(p1.length == 0 || (p1.length == 1 && (p1[0].equalsIgnoreCase("user") || p1[0].equalsIgnoreCase("username") + || p1[0].equalsIgnoreCase("users") || p1[0].equalsIgnoreCase("usernames")))) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Players banned by username: " + ChatColor.WHITE + BanList.listAllBans()); + return; + }else if(p1.length == 1) { + if(p1[0].equalsIgnoreCase("regex") || p1[0].equalsIgnoreCase("regexes")) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Regex ban list: " + ChatColor.WHITE + BanList.listAllRegexBans()); + return; + }else if(p1[0].equalsIgnoreCase("wildcard") || p1[0].equalsIgnoreCase("wildcards")) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Wildcard ban list: " + ChatColor.WHITE + BanList.listAllWildcardBans()); + return; + }else if(p1[0].equalsIgnoreCase("ip") || p1[0].equalsIgnoreCase("ips")) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "To list IP bans, use: " + ChatColor.WHITE + (replaceBukkit ? "/banlist" : "/eag-banlist") + " ip [v4|v6]"); + return; + } + }else if(p1.length > 1 && p1.length <= 3 && (p1[0].equalsIgnoreCase("ip") || p1[0].equalsIgnoreCase("ips"))) { + int addrOrNetmask = 0; + if(p1[1].equalsIgnoreCase("addr") || p1[1].equalsIgnoreCase("addrs")) { + addrOrNetmask = 1; + }else if(p1[1].equalsIgnoreCase("netmask") || p1[1].equalsIgnoreCase("netmasks")) { + addrOrNetmask = 2; + } + if(addrOrNetmask > 0) { + boolean yes = false; + if(p1.length == 2 || (p1.length == 3 && (p1[2].equalsIgnoreCase("v4") || p1[2].equals("4")))) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "IPv4 " + (addrOrNetmask == 2 ? "netmask" : "address") + " ban list: " + ChatColor.WHITE + BanList.listAllIPBans(false, addrOrNetmask == 2)); + yes = true; + } + if(p1.length == 2 || (p1.length == 3 && (p1[2].equalsIgnoreCase("v6") || p1[2].equals("6")))) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "IPv6 " + (addrOrNetmask == 2 ? "netmask" : "address") + " ban list: " + ChatColor.WHITE + BanList.listAllIPBans(true, addrOrNetmask == 2)); + yes = true; + } + if(yes) { + return; + } + } + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "To list IP bans, use: " + ChatColor.WHITE + (replaceBukkit ? "/banlist" : "/eag-banlist") + " ip [v4|v6]"); + return; + } + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "To list all user bans, use: " + ChatColor.WHITE + (replaceBukkit ? "/banlist" : "/eag-banlist")); + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "To list ips, regexes, and wildcards, use: " + ChatColor.WHITE + (replaceBukkit ? "/banlist" : "/eag-banlist") + " "); + return; + } + +} diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandGlobalUnban.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandGlobalUnban.java new file mode 100644 index 0000000..aac2039 --- /dev/null +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandGlobalUnban.java @@ -0,0 +1,56 @@ +package net.md_5.bungee.command; + +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.plugin.Command; +import net.md_5.bungee.eaglercraft.BanList; + +public class CommandGlobalUnban extends Command { + + private final boolean replaceBukkit; + + public CommandGlobalUnban(boolean replaceBukkit) { + super(replaceBukkit ? "unban" : "eag-unban", "bungeecord.command.eag.unban", replaceBukkit ? new String[] {"eag-unban", "e-unban", "gunban"} :new String[] {"e-unban", "gunban"}); + this.replaceBukkit = replaceBukkit; + } + + @Override + public void execute(CommandSender p0, String[] p1) { + if(p1.length != 2 || (!p1[0].equalsIgnoreCase("user") && !p1[0].equalsIgnoreCase("username") && !p1[0].equalsIgnoreCase("player") + && !p1[0].equalsIgnoreCase("wildcard") && !p1[0].equalsIgnoreCase("regex") && !p1[0].equalsIgnoreCase("ip"))) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "To unban a player, use: " + ChatColor.WHITE + "/" + (replaceBukkit?"":"eag-") + "unban user "); + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "To unban an ip/wildcard/regex, use: " + ChatColor.WHITE + "/" + (replaceBukkit?"":"eag-") + "unban "); + return; + } + if(p1[0].equalsIgnoreCase("user") || p1[0].equalsIgnoreCase("username") || p1[0].equalsIgnoreCase("player")) { + if(BanList.unban(p1[1])) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "User '" + ChatColor.WHITE + p1[1] + ChatColor.GREEN + "' was unbanned"); + }else { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "User '" + ChatColor.WHITE + p1[1] + ChatColor.RED + "' is not banned"); + } + }else if(p1[0].equalsIgnoreCase("ip")) { + try { + if(BanList.unbanIP(p1[1])) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "IP '" + ChatColor.WHITE + p1[1] + ChatColor.GREEN + "' was unbanned"); + }else { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "IP '" + ChatColor.WHITE + p1[1] + ChatColor.RED + "' is not banned"); + } + }catch(Throwable t) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "IP address '" + ChatColor.WHITE + p1[1] + ChatColor.RED + "' is invalid: " + t.getMessage()); + } + }else if(p1[0].equalsIgnoreCase("wildcard")) { + if(BanList.unbanWildcard(p1[1])) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Wildcard '" + ChatColor.WHITE + p1[1] + ChatColor.GREEN + "' was unbanned"); + }else { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Wildcard '" + ChatColor.WHITE + p1[1] + ChatColor.RED + "' is not banned"); + } + }else if(p1[0].equalsIgnoreCase("regex")) { + if(BanList.unbanRegex(p1[1])) { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Regex '" + ChatColor.WHITE + p1[1] + ChatColor.GREEN + "' was unbanned"); + }else { + p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Regex '" + ChatColor.WHITE + p1[1] + ChatColor.RED + "' is not banned"); + } + } + } + +} diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandIP.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandIP.java index 376fbfb..608d5e6 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandIP.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandIP.java @@ -6,6 +6,9 @@ package net.md_5.bungee.command; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.ProxyServer; + +import java.net.InetAddress; + import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.plugin.Command; @@ -25,7 +28,12 @@ public class CommandIP extends Command { if (user == null) { sender.sendMessage(ChatColor.RED + "That user is not online"); } else { - sender.sendMessage(ChatColor.BLUE + "IP of " + args[0] + " is " + user.getAddress()); + Object o = user.getAttachment().get("remoteAddr"); + if(o != null) { + sender.sendMessage(ChatColor.BLUE + "IP of " + args[0] + " is " + (InetAddress)o); + }else { + sender.sendMessage(ChatColor.BLUE + "IP of " + args[0] + " is " + user.getAddress()); + } } } } diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java index c9f4cd4..d89e7fd 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java @@ -4,14 +4,16 @@ package net.md_5.bungee.command; -import java.util.Collections; import java.util.HashSet; +import java.util.Map; +import java.util.WeakHashMap; import java.util.Collection; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.CommandSender; public class ConsoleCommandSender implements CommandSender { private static final ConsoleCommandSender instance; + private static final Map attachment = new WeakHashMap(); private ConsoleCommandSender() { } @@ -65,4 +67,9 @@ public class ConsoleCommandSender implements CommandSender { static { instance = new ConsoleCommandSender(); } + + @Override + public Map getAttachment() { + return attachment; + } } diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/config/Configuration.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/config/Configuration.java index dc17dcf..09e06af 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/config/Configuration.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/config/Configuration.java @@ -12,6 +12,7 @@ import com.google.common.base.Preconditions; import net.md_5.bungee.api.ProxyServer; import java.util.UUID; import net.md_5.bungee.api.config.ServerInfo; +import net.md_5.bungee.eaglercraft.EaglercraftBungee; import gnu.trove.map.TMap; import net.md_5.bungee.api.config.ListenerInfo; import java.util.Collection; @@ -24,6 +25,7 @@ public class Configuration { private AuthServiceInfo authInfo; private boolean onlineMode; private int playerLimit; + private String name; public Configuration() { this.timeout = 30000; @@ -41,6 +43,7 @@ public class Configuration { this.authInfo = adapter.getAuthSettings(); this.onlineMode = false; this.playerLimit = adapter.getInt("player_limit", this.playerLimit); + this.name = adapter.getString("server_name", EaglercraftBungee.brand + " Server"); Preconditions.checkArgument(this.listeners != null && !this.listeners.isEmpty(), (Object) "No listeners defined."); final Map newServers = adapter.getServers(); Preconditions.checkArgument(newServers != null && !newServers.isEmpty(), (Object) "No servers defined"); @@ -88,4 +91,8 @@ public class Configuration { public AuthServiceInfo getAuthInfo() { return authInfo; } + + public String getServerName() { + return name; + } } diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/config/YamlConfig.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/config/YamlConfig.java index 20b90d4..e465ac9 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/config/YamlConfig.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/config/YamlConfig.java @@ -67,18 +67,11 @@ public class YamlConfig implements ConfigurationAdapter { final Map permissions = this.get("permissions", new HashMap()); if (permissions.isEmpty()) { permissions.put("default", Arrays.asList("bungeecord.command.server", "bungeecord.command.list")); - permissions.put("admin", Arrays.asList("bungeecord.command.alert", "bungeecord.command.end", "bungeecord.command.ip", "bungeecord.command.reload")); + permissions.put("admin", Arrays.asList("bungeecord.command.alert", "bungeecord.command.end", "bungeecord.command.ip", "bungeecord.command.reload", + "bungeecord.command.eag.ban", "bungeecord.command.eag.banwildcard", "bungeecord.command.eag.banip", "bungeecord.command.eag.banregex", + "bungeecord.command.eag.reloadban", "bungeecord.command.eag.banned", "bungeecord.command.eag.banlist", "bungeecord.command.eag.unban")); } this.get("groups", new HashMap()); - /* - final Map auth = this.get("authservice", new HashMap()); - if(auth.isEmpty()) { - auth.put("enabled", false); - auth.put("limbo", "lobby"); - auth.put("authfile", "passwords.yml"); - auth.put("timeout", 30); - } - */ } private T get(final String path, final T def) { @@ -159,6 +152,7 @@ public class YamlConfig implements ConfigurationAdapter { final String fallbackServer = this.get("fallback_server", defaultServer, val); final boolean forceDefault = this.get("force_default_server", true, val); final boolean websocket = this.get("websocket", true, val); + final boolean forwardIp = this.get("forward_ip", false, val); final String host = this.get("host", "0.0.0.0:25565", val); final int tabListSize = this.get("tab_size", 60, val); final InetSocketAddress address = Util.getAddr(host); @@ -167,11 +161,12 @@ public class YamlConfig implements ConfigurationAdapter { final int textureSize = this.get("texture_size", 16, val); final TexturePackInfo texture = (textureURL == null) ? null : new TexturePackInfo(textureURL, textureSize); final String tabListName = this.get("tab_list", "GLOBAL_PING", val); + final String serverIcon = this.get("server_icon", "server-icon.png", val); DefaultTabList value = DefaultTabList.valueOf(tabListName.toUpperCase()); if (value == null) { value = DefaultTabList.GLOBAL_PING; } - final ListenerInfo info = new ListenerInfo(address, motd, maxPlayers, tabListSize, defaultServer, fallbackServer, forceDefault, websocket, forced, texture, value.clazz); + final ListenerInfo info = new ListenerInfo(address, motd, maxPlayers, tabListSize, defaultServer, fallbackServer, forceDefault, websocket, forwardIp, forced, texture, value.clazz, serverIcon); ret.add(info); } return ret; diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/connection/InitialHandler.java index 3e7e62b..10e4530 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/connection/InitialHandler.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/connection/InitialHandler.java @@ -6,6 +6,7 @@ package net.md_5.bungee.connection; import java.beans.ConstructorProperties; import java.util.ArrayList; +import java.net.InetAddress; import java.net.InetSocketAddress; import net.md_5.bungee.protocol.packet.PacketFFKick; import net.md_5.bungee.api.config.ServerInfo; @@ -37,6 +38,9 @@ import net.md_5.bungee.protocol.Forge; import net.md_5.bungee.netty.PacketDecoder; import com.google.common.base.Preconditions; import net.md_5.bungee.api.event.ProxyPingEvent; +import net.md_5.bungee.eaglercraft.BanList; +import net.md_5.bungee.eaglercraft.WebSocketProxy; +import net.md_5.bungee.eaglercraft.BanList.BanCheck; import net.md_5.bungee.api.ServerPing; import net.md_5.bungee.protocol.packet.PacketFEPing; import net.md_5.bungee.Util; @@ -112,8 +116,12 @@ public class InitialHandler extends PacketHandler implements PendingConnection { if (handshake.getProcolVersion() == 69) { skipEncryption = true; this.handshake.swapProtocol((byte) 61); + }else if(handshake.getProcolVersion() == 71) { + this.disconnect("this server does not support microsoft accounts"); + return; }else if(handshake.getProcolVersion() != 61) { this.disconnect("minecraft 1.5.2 required for eaglercraft backdoor access"); + return; } String un = handshake.getUsername(); if (un.length() < 3) { @@ -128,6 +136,37 @@ public class InitialHandler extends PacketHandler implements PendingConnection { this.disconnect("Go fuck yourself"); return; } + InetAddress sc = WebSocketProxy.localToRemote.get(this.ch.getHandle().remoteAddress()); + if(sc == null) { + System.out.println("WARNING: player '" + un + "' doesn't have a websocket IP, remote address: " + this.ch.getHandle().remoteAddress().toString()); + }else { + BanCheck bc = BanList.checkIpBanned(sc); + if(bc.isBanned()) { + System.err.println("Player '" + un + "' [" + sc.toString() + "] is banned by IP: " + bc.match + " (" + bc.string + ")"); + this.disconnect("" + ChatColor.RED + "You are banned.\n" + ChatColor.DARK_GRAY + "Reason: " + bc.string); + return; + }else { + System.out.println("Player '" + un + "' [" + sc.toString() + "] has remote websocket IP: " + sc.getHostAddress()); + } + } + BanCheck bc = BanList.checkBanned(un); + if(bc.isBanned()) { + switch(bc.reason) { + case USER_BANNED: + System.err.println("Player '" + un + "' is banned by username, because '" + bc.string + "'"); + break; + case WILDCARD_BANNED: + System.err.println("Player '" + un + "' is banned by wildcard: " + bc.match); + break; + case REGEX_BANNED: + System.err.println("Player '" + un + "' is banned by regex: " + bc.match); + break; + default: + System.err.println("Player '" + un + "' is banned: " + bc.string); + } + this.disconnect("" + ChatColor.RED + "You are banned.\n" + ChatColor.DARK_GRAY + "Reason: " + bc.string); + return; + } final int limit = BungeeCord.getInstance().config.getPlayerLimit(); if (limit > 0 && this.bungee.getOnlineCount() > limit) { this.disconnect(this.bungee.getTranslation("proxy_full")); @@ -194,6 +233,10 @@ public class InitialHandler extends PacketHandler implements PendingConnection { public void handle(final PacketCDClientStatus clientStatus) throws Exception { Preconditions.checkState(this.thisState == State.LOGIN, (Object) "Not expecting LOGIN"); final UserConnection userCon = new UserConnection(this.bungee, this.ch, this.getName(), this); + InetAddress ins = WebSocketProxy.localToRemote.get(this.ch.getHandle().remoteAddress()); + if(ins != null) { + userCon.getAttachment().put("remoteAddr", ins); + } userCon.init(); this.bungee.getPluginManager().callEvent(new PostLoginEvent(userCon)); ((HandlerBoss) this.ch.getHandle().pipeline().get((Class) HandlerBoss.class)).setHandler(new UpstreamBridge(this.bungee, userCon)); diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/BanList.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/BanList.java new file mode 100644 index 0000000..49ae738 --- /dev/null +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/BanList.java @@ -0,0 +1,863 @@ +package net.md_5.bungee.eaglercraft; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.PrintWriter; +import java.math.BigInteger; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.eaglercraft.sun.net.util.IPAddressUtil; + +public class BanList { + + private static final Object banListMutex = new Object(); + + public static enum BanState { + NOT_BANNED, USER_BANNED, IP_BANNED, WILDCARD_BANNED, REGEX_BANNED; + } + + public static class BanCheck { + public final BanState reason; + public final String match; + public final String string; + private BanCheck(BanState reason, String match, String string) { + this.reason = reason; + this.match = match; + this.string = string; + } + public boolean isBanned() { + return reason != BanState.NOT_BANNED; + } + } + + private static class RegexBan { + public final String string; + public final Pattern compiled; + private RegexBan(String string, Pattern compiled) { + this.string = string; + this.compiled = compiled; + } + public String toString() { + return string; + } + public int hashCode() { + return string.hashCode(); + } + } + + public static interface IPBan { + + boolean checkBan(InetAddress addr); + InetAddress getBaseAddress(); + boolean hasNetMask(); + + } + + private static class IPBan4 implements IPBan { + + private final int addr; + private final InetAddress addrI; + private final int mask; + private final String string; + + protected IPBan4(Inet4Address addr, String s, int mask) { + if(mask >= 32) { + this.mask = 0xFFFFFFFF; + }else { + this.mask = ~((1 << (32 - mask)) - 1); + } + this.string = s; + byte[] bits = addr.getAddress(); + this.addr = this.mask & ((bits[0] << 24) | (bits[1] << 16) | (bits[2] << 8) | (bits[3] & 0xFF)); + this.addrI = addr; + } + + @Override + public boolean checkBan(InetAddress addr4) { + if(addr4 instanceof Inet4Address) { + Inet4Address a = (Inet4Address)addr4; + byte[] bits = a.getAddress(); + int addrBits = ((bits[0] << 24) | (bits[1] << 16) | (bits[2] << 8) | (bits[3] & 0xFF)); + return (mask & addrBits) == addr; + }else { + return false; + } + } + + @Override + public InetAddress getBaseAddress() { + return addrI; + } + + @Override + public String toString() { + return string; + } + + @Override + public int hashCode() { + return string.hashCode(); + } + + @Override + public boolean equals(Object o) { + return o != null && o instanceof IPBan4 && ((IPBan4)o).addr == addr && ((IPBan4)o).mask == mask; + } + + @Override + public boolean hasNetMask() { + return mask != 0xFFFFFFFF; + } + + } + + private static class IPBan6 implements IPBan { + + private static final BigInteger mask128 = new BigInteger(1, new byte[] { + (byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF, + (byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF, + (byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF, + (byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF + }); + + private final BigInteger addr; + private final InetAddress addrI; + private final BigInteger mask; + private final String string; + + protected IPBan6(Inet6Address addr, String s, int mask) { + this.mask = BigInteger.valueOf(1l).shiftLeft(128 - mask).subtract(BigInteger.valueOf(1l)).xor(mask128); + this.string = s.toLowerCase(); + this.addr = new BigInteger(1, addr.getAddress()).and(this.mask); + this.addrI = addr; + } + + @Override + public boolean checkBan(InetAddress addr6) { + if(addr6 instanceof Inet6Address) { + Inet6Address a = (Inet6Address)addr6; + BigInteger addrBits = new BigInteger(1, a.getAddress()).and(this.mask); + return addr.equals(addrBits); + }else { + return false; + } + } + + @Override + public InetAddress getBaseAddress() { + return addrI; + } + + @Override + public String toString() { + return string; + } + + @Override + public int hashCode() { + return string.hashCode(); + } + + @Override + public boolean equals(Object o) { + return o != null && o instanceof IPBan6 && ((IPBan6)o).addr.equals(addr) && ((IPBan6)o).mask.equals(mask); + } + + @Override + public boolean hasNetMask() { + return !mask.equals(mask128); + } + + } + + public static final File bansFile = new File("bans.txt"); + + public static final String banChatMessagePrefix = ChatColor.GOLD + "[BanList] "; + + public static final Map userBans = new HashMap(); + public static final Set ipBans = new HashSet(); + public static final Set wildcardBans = new HashSet(); + public static final Set regexBans = new HashSet(); + private static List currentBanList = null; + + public static final List blockedBans = new ArrayList(); + + static { + try { + blockedBans.add(constructIpBan("127.0.0.0/8")); + }catch(UnknownHostException e) { + System.err.println("Error: could not whitelist '127.0.0.0/8'"); + e.printStackTrace(); + } + try { + blockedBans.add(constructIpBan("10.0.0.0/8")); + }catch(UnknownHostException e) { + System.err.println("Error: could not whitelist '10.0.0.0/8'"); + e.printStackTrace(); + } + try { + blockedBans.add(constructIpBan("172.24.0.0/14")); + }catch(UnknownHostException e) { + System.err.println("Error: could not whitelist '172.24.0.0/14'"); + e.printStackTrace(); + } + try { + blockedBans.add(constructIpBan("192.168.0.0/16")); + }catch(UnknownHostException e) { + System.err.println("Error: could not whitelist '192.168.0.0/16'"); + e.printStackTrace(); + } + try { + blockedBans.add(constructIpBan("::1/128")); + }catch(UnknownHostException e) { + System.err.println("Error: could not whitelist '::1/128'"); + e.printStackTrace(); + } + } + + private static long lastListTest = 0l; + private static long lastListLoad = 0l; + private static boolean fileIsBroken = false; + + public static BanCheck checkIpBanned(InetAddress addr) { + synchronized(banListMutex) { + for(IPBan b : ipBans) { + if(b.checkBan(addr)) { + return new BanCheck(BanState.IP_BANNED, b.toString(), b.hasNetMask() ? "Banned by Netmask" : "Banned by IP"); + } + } + } + return new BanCheck(BanState.NOT_BANNED, "none", "not banned"); + } + + public static BanCheck checkBanned(String player) { + synchronized(banListMutex) { + player = player.trim().toLowerCase(); + String r = userBans.get(player); + if(r != null) { + if(r.length() <= 0) { + r = "The ban hammer has spoken"; + } + return new BanCheck(BanState.USER_BANNED, player, r); + } + for(String ss : wildcardBans) { + String s = ss; + boolean startStar = s.startsWith("*"); + if(startStar) { + s = s.substring(1); + } + boolean endStar = s.endsWith("*"); + if(endStar) { + s = s.substring(0, s.length() - 1); + } + if(startStar && endStar) { + if(player.contains(s)) { + return new BanCheck(BanState.WILDCARD_BANNED, ss, "You've been banned via wildcard"); + } + }else if(endStar) { + if(player.startsWith(s)) { + return new BanCheck(BanState.WILDCARD_BANNED, ss, "You've been banned via wildcard"); + } + }else if(startStar) { + if(player.endsWith(s)) { + return new BanCheck(BanState.WILDCARD_BANNED, ss, "You've been banned via wildcard"); + } + }else { + if(player.equals(s)) { + return new BanCheck(BanState.WILDCARD_BANNED, ss, "You've been banned via wildcard"); + } + } + } + for(RegexBan p : regexBans) { + if(p.compiled.matcher(player).matches()) { + return new BanCheck(BanState.REGEX_BANNED, p.string, "You've been banned via regex"); + } + } + } + return new BanCheck(BanState.NOT_BANNED, "none", "not banned"); + } + + private static void saveCurrentBanListLines() { + try { + PrintWriter pf = new PrintWriter(new FileWriter(bansFile)); + for(String s : currentBanList) { + pf.println(s); + } + pf.close(); + lastListLoad = lastListTest = System.currentTimeMillis(); + }catch(Throwable t) { + System.err.println("ERROR: the ban list could not be saved to file '" + bansFile.getName() + "', please fix this or you will lose all your bans next time this server restarts"); + t.printStackTrace(); + } + } + + private static boolean addEntryToFile(BanState b, String s) { + if(b == null || b == BanState.NOT_BANNED) { + return false; + } + String wantedHeader = b == BanState.USER_BANNED ? "[Usernames]" : (b == BanState.WILDCARD_BANNED ? "[Wildcards]" : (b == BanState.REGEX_BANNED ? "[Regex]" : (b == BanState.IP_BANNED ? "[IPs]" : "shit"))); + int lastFullPart = -1; + boolean isFilePart = false; + boolean isPartStart = false; + for(int i = 0, l = currentBanList.size(); i < l; ++i) { + String ss = currentBanList.get(i).trim(); + if(ss.length() <= 0) { + continue; + } + if(ss.startsWith("#")) { + continue; + } + if(ss.equalsIgnoreCase(wantedHeader)) { + isFilePart = true; + isPartStart = true; + lastFullPart = i; + }else if(ss.indexOf('[') != -1) { + if(isFilePart) { + break; + } + }else { + if(isFilePart) { + lastFullPart = i; + isPartStart = false; + } + } + } + if(lastFullPart != -1) { + if(isPartStart) { + lastFullPart += 1; + currentBanList.add(lastFullPart, ""); + } + lastFullPart += 1; + currentBanList.add(lastFullPart, s); + lastFullPart += 1; + if(currentBanList.size() > lastFullPart && currentBanList.get(lastFullPart).trim().length() > 0) { + currentBanList.add(lastFullPart, ""); + } + }else { + if(currentBanList.size() > 0 && currentBanList.get(currentBanList.size() - 1).trim().length() > 0) { + currentBanList.add(""); + } + currentBanList.add(wantedHeader); + currentBanList.add(""); + currentBanList.add(s); + currentBanList.add(""); + } + saveCurrentBanListLines(); + return true; + } + + private static boolean removeEntryFromFile(BanState b, String s, boolean ignoreCase) { + if(b == null || b == BanState.NOT_BANNED) { + return false; + } + String wantedHeader = b == BanState.USER_BANNED ? "[Usernames]" : (b == BanState.WILDCARD_BANNED ? "[Wildcards]" : (b == BanState.REGEX_BANNED ? "[Regex]" : (b == BanState.IP_BANNED ? "[IPs]" : "shit"))); + Iterator lns = currentBanList.iterator(); + boolean isFilePart = false; + boolean wasRemoved = false; + while(lns.hasNext()) { + String ss = lns.next().trim(); + if(ss.length() <= 0) { + continue; + } + if(ss.startsWith("#")) { + continue; + } + if(ss.equalsIgnoreCase(wantedHeader)) { + isFilePart = true; + }else if(ss.indexOf('[') != -1) { + isFilePart = false; + }else { + if(b == BanState.USER_BANNED && ss.contains(":")) { + ss = ss.substring(0, ss.indexOf(':')).trim(); + } + if(isFilePart && (ignoreCase ? ss.equalsIgnoreCase(s) : ss.equals(s))) { + lns.remove(); + wasRemoved = true; + } + } + } + if(wasRemoved) { + saveCurrentBanListLines(); + } + return wasRemoved; + } + + public static boolean unban(String player) { + synchronized(banListMutex) { + String s = player.trim().toLowerCase(); + if(userBans.remove(s) != null) { + removeEntryFromFile(BanState.USER_BANNED, player, true); + return true; + }else { + return false; + } + } + } + + public static boolean ban(String player, String reason) { + synchronized(banListMutex) { + player = player.trim().toLowerCase(); + if(userBans.put(player, reason) == null) { + addEntryToFile(BanState.USER_BANNED, player + (reason == null || reason.length() <= 0 ? "" : ": " + reason)); + return true; + }else { + return false; + } + } + } + + public static boolean banWildcard(String wc) throws PatternSyntaxException { + synchronized(banListMutex) { + wc = wc.trim().toLowerCase(); + boolean b = wc.contains("*"); + if(!b || (b && !wc.startsWith("*") && !wc.endsWith("*"))) { + throw new PatternSyntaxException("Wildcard can only begin and/or end with *", wc, 0); + } + if(wildcardBans.add(wc)) { + addEntryToFile(BanState.WILDCARD_BANNED, wc); + return true; + }else { + return false; + } + } + } + + public static boolean unbanWildcard(String wc) { + synchronized(banListMutex) { + wc = wc.trim().toLowerCase(); + if(wildcardBans.remove(wc)) { + removeEntryFromFile(BanState.WILDCARD_BANNED, wc, true); + return true; + }else { + return false; + } + } + } + + public static boolean banRegex(String regex) throws PatternSyntaxException { + synchronized(banListMutex) { + regex = regex.trim(); + Pattern p = Pattern.compile(regex); + if(regexBans.add(new RegexBan(regex, p))) { + addEntryToFile(BanState.REGEX_BANNED, regex); + return true; + }else { + return false; + } + } + } + + public static boolean unbanRegex(String regex) { + synchronized(banListMutex) { + regex = regex.trim(); + Iterator banz = regexBans.iterator(); + while(banz.hasNext()) { + if(banz.next().string.equals(regex)) { + banz.remove(); + removeEntryFromFile(BanState.REGEX_BANNED, regex, false); + return true; + } + } + return false; + } + } + + public static IPBan constructIpBan(String ip) throws UnknownHostException { + synchronized(banListMutex) { + ip = ip.trim(); + String s = ip; + int subnet = -1; + int i = s.indexOf('/'); + if(i != -1) { + String s2 = s.substring(i + 1); + s = s.substring(0, i); + try { + subnet = Integer.parseInt(s2); + }catch(Throwable t) { + throw new UnknownHostException("Invalid netmask: '" + s + "'"); + } + } + + if(!IPAddressUtil.isIPv4LiteralAddress(s) && !IPAddressUtil.isIPv6LiteralAddress(s)) { + throw new UnknownHostException("Invalid address: '" + s + "'"); + } + + InetAddress aa = InetAddress.getByName(s); + if(aa instanceof Inet4Address) { + if(subnet > 32 || subnet < -1) { + throw new UnknownHostException("IPv4 netmask '" + subnet + "' is invalid"); + } + if(subnet == -1) { + subnet = 32; + } + return new IPBan4((Inet4Address)aa, ip, subnet); + }else if(aa instanceof Inet6Address) { + if(subnet > 128 || subnet < -1) { + throw new UnknownHostException("IPv6 netmask '" + subnet + "' is invalid"); + } + if(subnet == -1) { + subnet = 128; + } + return new IPBan6((Inet6Address)aa, ip, subnet); + }else { + throw new UnknownHostException("Only ipv4 and ipv6 addresses allowed in Eaglercraft"); + } + } + } + + public static boolean banIP(String ip) throws UnknownHostException { + synchronized(banListMutex) { + ip = ip.trim(); + IPBan b = constructIpBan(ip); + if(b != null) { + if(ipBans.add(b)) { + addEntryToFile(BanState.IP_BANNED, ip); + return true; + } + } + return false; + } + } + + public static boolean unbanIP(String ip) throws UnknownHostException { + synchronized(banListMutex) { + ip = ip.trim(); + IPBan b = constructIpBan(ip); + if(b != null) { + Iterator banz = ipBans.iterator(); + while(banz.hasNext()) { + IPBan bb = banz.next(); + if(bb.equals(b)) { + banz.remove(); + removeEntryFromFile(BanState.IP_BANNED, bb.toString(), true); + return true; + } + } + } + return false; + } + } + + private static final int MAX_CHAT_LENGTH = 118; + + public static String listAllBans() { + synchronized(banListMutex) { + String ret = ""; + for(String s : userBans.keySet()) { + if(ret.length() > 0) { + ret += ", "; + } + ret += s; + } + return ret.length() > 0 ? ret : "(none)"; + } + } + + public static String listAllWildcardBans() { + synchronized(banListMutex) { + String ret = ""; + for(String s : wildcardBans) { + if(ret.length() > 0) { + ret += ", "; + } + ret += s; + } + return ret.length() > 0 ? ret : "(none)"; + } + } + + public static String listAllRegexBans() { + synchronized(banListMutex) { + String ret = ""; + for(RegexBan s : regexBans) { + if(ret.length() > 0) { + ret += " | "; + } + ret += s.string; + } + return ret.length() > 0 ? ret : "(none)"; + } + } + + public static String listAllIPBans(boolean v6, boolean netmask) { + synchronized(banListMutex) { + String ret = ""; + for(IPBan b : ipBans) { + if(v6) { + if(b instanceof IPBan6) { + IPBan6 b2 = (IPBan6)b; + if(netmask == b2.hasNetMask()) { + if(ret.length() > 0) { + ret += ", "; + } + ret += b2.string; + } + } + }else { + if(b instanceof IPBan4) { + IPBan4 b2 = (IPBan4)b; + if(netmask == b2.hasNetMask()) { + if(ret.length() > 0) { + ret += ", "; + } + ret += b2.string; + } + } + } + } + if(ret.length() <= 0) { + ret = "(none)"; + } + return ret; + } + } + + public static void maybeReloadBans(CommandSender cs) { + synchronized(banListMutex) { + long st = System.currentTimeMillis(); + if(cs == null && st - lastListTest < 1000l) { + return; + } + lastListTest = st; + boolean ex = bansFile.exists(); + if(!fileIsBroken && !ex) { + try { + PrintWriter p = new PrintWriter(new FileWriter(bansFile)); + p.println(); + p.println("#"); + p.println("# This file allows you to configure bans for eaglercraftbungee"); + p.println("# When it is saved, eaglercraft should reload it automatically"); + p.println("# (check the console though to be safe)"); + p.println("#"); + p.println("# For a [Usernames] ban, just add the player's name. Use a colon ':' to put in a ban reason"); + p.println("# For a [IPs] ban, just add the player's IP, or a subnet like 69.69.0.0/16 to ban all IPs beginning with 69.69.*"); + p.println("# For a [Wildcards] ban, type a string and prefix and/or suffix it with * to define the wildcard"); + p.println("# For a [Regex] ban, type a valid regular expression in the java.util.regex format"); + p.println("#"); + p.println("# All bans are case-insensitive, USERNAMES ARE CONVERTED TO LOWERCASE BEFORE BEING MATCHED VIA REGEX"); + p.println("# Java regex syntax: https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html"); + p.println("#"); + p.println(); + p.println("# set this to false to use \"/eag-ban\" to ban on bungee instead of just \"/ban\""); + p.println("# (most likely needs a restart to take effect)"); + p.println("replace-bukkit=true"); + p.println(); + p.println(); + p.println("[Usernames]"); + p.println(); + p.println("# ban_test1: The ban hammer has spoken!"); + p.println("# ban_test2: custom ban message here"); + p.println("# ban_test3"); + p.println(); + p.println("# (remove the '#' before each line to enable)"); + p.println(); + p.println("[IPs]"); + p.println(); + p.println("# WARNING: if you're using nginx, banning any player's IP is gonna ban ALL PLAYERS on your server"); + p.println("# For this reason, the ban IP command doesn't ban 127.0.0.1 or any other 'private' range IPs"); + p.println(); + p.println("# 101.202.69.11"); + p.println("# 123.21.43.0/24"); + p.println("# 2601:1062:69:418:BEEF::10"); + p.println("# 2601:6090:420::/48"); + p.println(); + p.println(); + p.println("[Wildcards]"); + p.println(); + p.println("# *fuck*"); + p.println("# nigg*"); + p.println(); + p.println(); + p.println("[Regex]"); + p.println(); + p.println("# you.+are.(a|the).+bitch"); + p.println(); + p.println(); + p.println("# end of file"); + p.println(); + p.close(); + System.out.println("Wrote a new bans.txt to: " + bansFile.getAbsolutePath()); + lastListLoad = 0l; + }catch(Throwable t) { + fileIsBroken = true; + if(cs != null) { + cs.sendMessage(banChatMessagePrefix + ChatColor.RED + "Could not create blank 'bans.txt' list file"); + cs.sendMessage(banChatMessagePrefix + ChatColor.RED + "(Reason: " + t.toString() + ")"); + } + System.err.println("Could not create blank 'bans.txt' list file"); + System.err.println("(Reason: " + t.toString() + ")"); + t.printStackTrace(); + } + return; + } + if(fileIsBroken && ex) { + fileIsBroken = false; + lastListLoad = 0l; + } + if(fileIsBroken) { + return; + } + long lastEdit = bansFile.lastModified(); + if(cs != null || lastEdit - lastListLoad > 400l) { + try { + BufferedReader r = new BufferedReader(new FileReader(bansFile)); + currentBanList = new LinkedList(); + String s; + while((s = r.readLine()) != null) { + currentBanList.add(s); + } + r.close(); + lastListLoad = lastEdit; + System.out.println("Server bans.txt changed, it will be reloaded automatically"); + if(cs == null) { + for(ProxiedPlayer pp : BungeeCord.getInstance().getPlayers()) { + if(pp.hasPermission("bungeecord.command.eag.reloadban")) { + pp.sendMessage(BanList.banChatMessagePrefix + ChatColor.WHITE + "Your Eaglercraftbungee bans.txt just got modified, it will be reloaded asap"); + pp.sendMessage(BanList.banChatMessagePrefix + ChatColor.YELLOW + "Stop your server and check your config immediately if you don't know how this happened!!"); + } + } + } + parseListFrom(); + System.out.println("Reload complete"); + }catch(Throwable t) { + if(cs != null) { + cs.sendMessage(banChatMessagePrefix + ChatColor.RED + "Could not reload 'bans.txt' list file"); + cs.sendMessage(banChatMessagePrefix + ChatColor.RED + "(Reason: " + t.toString() + ")"); + } + System.err.println("Could not reload 'bans.txt' list file"); + System.err.println("(Reason: " + t.toString() + ")"); + } + } + } + } + + private static void parseListFrom() { + userBans.clear(); + ipBans.clear(); + wildcardBans.clear(); + regexBans.clear(); + + int filePart = 0; + boolean replaceBukkit = false; + + for(String s : currentBanList) { + s = s.trim(); + if(s.length() <= 0) { + continue; + } + if(s.startsWith("#")) { + continue; + } + if(s.equals("[Usernames]")) { + filePart = 1; + }else if(s.equals("[Wildcards]")) { + filePart = 2; + }else if(s.equals("[Regex]")) { + filePart = 3; + }else if(s.equals("[IPs]")) { + filePart = 4; + }else if(s.equals("replace-bukkit=true")) { + replaceBukkit = true; + }else if(s.equals("replace-bukkit=false")) { + continue; + }else { + if(filePart == 1) { + int i = s.indexOf(':'); + if(i == -1) { + userBans.put(s.toLowerCase(), ""); + }else { + userBans.put(s.substring(0, i).trim().toLowerCase(), s.substring(i + 1).trim()); + } + }else if(filePart == 2) { + boolean ws = s.startsWith("*"); + boolean we = s.endsWith("*"); + if(!ws && !we) { + if(s.contains("*")) { + System.err.println("Error: wildcard '" + s + "' contains a '*' not at the start/end of the string"); + }else { + System.err.println("Error: wildcard '" + s + "' does not contain a '*' wildcard character"); + } + }else { + int total = (ws ? 1 : 0) + (we ? 1 : 0); + int t2 = 0; + for(char c : s.toCharArray()) { + if(c == '*') ++t2; + } + if(total != t2) { + System.err.println("Error: wildcard '" + s + "' contains a '*' not at the start/end of the string"); + } + } + wildcardBans.add(s.toLowerCase()); + }else if(filePart == 3) { + Pattern p = null; + try { + p = Pattern.compile(s); + }catch(Throwable t) { + System.err.println("Error: the regex " + s.toLowerCase() + " is invalid"); + System.err.println("Reason: " + t.getClass().getSimpleName() + ": " + t.getLocalizedMessage()); + } + if(p != null) { + regexBans.add(new RegexBan(s, p)); + } + }else if(filePart == 4) { + String ss = s; + int subnet = -1; + int i = s.indexOf('/'); + if(i != -1) { + String s2 = s.substring(i + 1); + s = s.substring(0, i); + try { + subnet = Integer.parseInt(s2); + }catch(Throwable t) { + System.err.println("Error: the subnet '"+ s2 +"' for IP ban address " + s + " was invalid"); + subnet = -2; + } + } + if(subnet >= -1) { + try { + InetAddress aa = InetAddress.getByName(s); + if(aa instanceof Inet4Address) { + if(subnet == -1) { + subnet = 32; + } + ipBans.add(new IPBan4((Inet4Address)aa, ss, subnet)); + }else if(aa instanceof Inet6Address) { + if(subnet == -1) { + subnet = 128; + } + ipBans.add(new IPBan6((Inet6Address)aa, ss, subnet)); + }else { + throw new UnknownHostException("Only ipv4 and ipv6 addresses allowed in Eaglercraft"); + } + }catch(Throwable t) { + System.err.println("Error: the IP ban address " + s + " could not be parsed"); + t.printStackTrace(); + } + } + } + } + } + + BungeeCord.getInstance().reconfigureBanCommands(replaceBukkit); + } + +} diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/EaglercraftBungee.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/EaglercraftBungee.java new file mode 100644 index 0000000..8668b11 --- /dev/null +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/EaglercraftBungee.java @@ -0,0 +1,9 @@ +package net.md_5.bungee.eaglercraft; + +public class EaglercraftBungee { + + public static final String brand = "Eagtek"; + public static final String version = "0.1.0"; + public static final boolean cracked = true; + +} diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/WebSocketListener.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/WebSocketListener.java index ff87ca0..c23ce41 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/WebSocketListener.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/WebSocketListener.java @@ -1,33 +1,50 @@ package net.md_5.bungee.eaglercraft; +import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.UnknownHostException; import java.nio.ByteBuffer; import org.java_websocket.WebSocket; import org.java_websocket.handshake.ClientHandshake; import org.java_websocket.server.WebSocketServer; +import net.md_5.bungee.BungeeCord; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.config.ListenerInfo; public class WebSocketListener extends WebSocketServer { + + public static class PendingSocket { + public long openTime; + public InetAddress realAddress; + protected PendingSocket(long openTime, InetAddress realAddress) { + this.openTime = openTime; + this.realAddress = realAddress; + } + } private InetSocketAddress bungeeProxy; private ProxyServer bungeeCord; + private ListenerInfo info; public WebSocketListener(ListenerInfo info, InetSocketAddress sock, ProxyServer bungeeCord) { super(info.getHost()); this.setTcpNoDelay(true); this.setConnectionLostTimeout(5); this.start(); + this.info = info; this.bungeeProxy = sock; this.bungeeCord = bungeeCord; } @Override public void onClose(WebSocket arg0, int arg1, String arg2, boolean arg3) { - if(arg0.getAttachment() != null) { - ((WebSocketProxy)arg0.getAttachment()).killConnection(); + Object o = arg0.getAttachment(); + if(o != null) { + if(o instanceof WebSocketProxy) { + ((WebSocketProxy)arg0.getAttachment()).killConnection(); + } } System.out.println("websocket closed - " + arg0.getRemoteSocketAddress()); } @@ -43,24 +60,73 @@ public class WebSocketListener extends WebSocketServer { @Override public void onMessage(WebSocket arg0, ByteBuffer arg1) { - if(arg0.getAttachment() != null) { - ((WebSocketProxy)arg0.getAttachment()).sendPacket(arg1); + Object o = arg0.getAttachment(); + if(o == null || (o instanceof PendingSocket)) { + InetAddress realAddr; + if(o == null) { + realAddr = arg0.getRemoteSocketAddress().getAddress(); + }else { + realAddr = ((PendingSocket)o).realAddress; + } + System.out.println("connection is binary - " + realAddr); + WebSocketProxy proxyObj = new WebSocketProxy(arg0, realAddr, bungeeProxy); + arg0.setAttachment(proxyObj); + if(!proxyObj.connect()) { + System.err.println("loopback to '" + bungeeProxy.toString() + "' failed - " + realAddr); + arg0.close(); + return; + } + o = proxyObj; + } + if(o != null) { + if(o instanceof WebSocketProxy) { + ((WebSocketProxy)o).sendPacket(arg1); + }else { + System.out.println("error: recieved binary data on text websocket - " + arg0.getRemoteSocketAddress()); + arg0.close(); + } } } @Override public void onOpen(WebSocket arg0, ClientHandshake arg1) { System.out.println("websocket opened - " + arg0.getRemoteSocketAddress()); - WebSocketProxy proxyObj = new WebSocketProxy(arg0, bungeeProxy); - arg0.setAttachment(proxyObj); - if(!proxyObj.connect()) { - arg0.close(); + if(info.hasForwardedHeaders()) { + String s = arg1.getFieldValue("X-Real-IP"); + if(s != null) { + try { + InetAddress addr = InetAddress.getByName(s); + arg0.setAttachment(new PendingSocket(System.currentTimeMillis(), addr)); + System.out.println("real IP of '" + arg0.getRemoteSocketAddress().toString() + "' is '" + addr.getHostAddress() + "'"); + }catch(UnknownHostException e) { + System.out.println("invalid 'X-Real-IP' header - " + e.toString()); + arg0.close(); + } + }else { + arg0.setAttachment(new PendingSocket(System.currentTimeMillis(), arg0.getRemoteSocketAddress().getAddress())); + } + }else { + arg0.setAttachment(new PendingSocket(System.currentTimeMillis(), arg0.getRemoteSocketAddress().getAddress())); } } @Override public void onStart() { - + } + + public void closeInactiveSockets() { + for(WebSocket w : this.getConnections()) { + Object o = w.getAttachment(); + if(o == null) { + System.out.println("close inactive websocket - " + w.getRemoteSocketAddress()); + w.close(); + }else if(o instanceof PendingSocket) { + if(System.currentTimeMillis() - ((PendingSocket)o).openTime > 5000l) { + System.out.println("close inactive websocket - " + ((PendingSocket)o).realAddress); + w.close(); + } + } + } } } diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/WebSocketProxy.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/WebSocketProxy.java index 69738c0..e0d0d6d 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/WebSocketProxy.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/WebSocketProxy.java @@ -1,7 +1,9 @@ package net.md_5.bungee.eaglercraft; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.ByteBuffer; +import java.util.HashMap; import org.java_websocket.WebSocket; @@ -16,6 +18,8 @@ import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.GenericFutureListener; /** * Not the ideal solution but what are we supposed to do @@ -25,20 +29,22 @@ public class WebSocketProxy extends SimpleChannelInboundHandler { private WebSocket client; private InetSocketAddress tcpListener; + private InetSocketAddress localAddress; + private InetAddress realRemoteAddr; private NioSocketChannel tcpChannel; private static final EventLoopGroup group = new NioEventLoopGroup(4); + public static final HashMap localToRemote = new HashMap(); - public WebSocketProxy(WebSocket w, InetSocketAddress addr) { + public WebSocketProxy(WebSocket w, InetAddress remoteAddr, InetSocketAddress addr) { client = w; + realRemoteAddr = remoteAddr; tcpListener = addr; tcpChannel = null; } public void killConnection() { - if(client.isOpen()) { - client.close(); - } + localToRemote.remove(localAddress); if(tcpChannel != null && tcpChannel.isOpen()) { try { tcpChannel.disconnect().sync(); @@ -59,9 +65,16 @@ public class WebSocketProxy extends SimpleChannelInboundHandler { clientBootstrap.handler(new ChannelInitializer() { protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(WebSocketProxy.this); + socketChannel.closeFuture().addListener(new GenericFutureListener>() { + @Override + public void operationComplete(Future paramF) throws Exception { + localToRemote.remove(localAddress); + } + }); } }); tcpChannel = (NioSocketChannel) clientBootstrap.connect().sync().channel(); + localToRemote.put(localAddress = tcpChannel.localAddress(), realRemoteAddr); return true; } }catch(Throwable t) { @@ -89,4 +102,8 @@ public class WebSocketProxy extends SimpleChannelInboundHandler { } } + public void finalize() { + localToRemote.remove(localAddress); + } + } diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/sun/net/util/IPAddressUtil.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/sun/net/util/IPAddressUtil.java new file mode 100644 index 0000000..4cef80d --- /dev/null +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/sun/net/util/IPAddressUtil.java @@ -0,0 +1,323 @@ +package net.md_5.bungee.eaglercraft.sun.net.util; + +import java.net.URL; +import java.util.Arrays; + +public class IPAddressUtil { + private static final int INADDR4SZ = 4; + + private static final int INADDR16SZ = 16; + + private static final int INT16SZ = 2; + + private static final long L_IPV6_DELIMS = 0L; + + private static final long H_IPV6_DELIMS = 671088640L; + + private static final long L_GEN_DELIMS = -8935000888854970368L; + + private static final long H_GEN_DELIMS = 671088641L; + + private static final long L_AUTH_DELIMS = 288230376151711744L; + + private static final long H_AUTH_DELIMS = 671088641L; + + private static final long L_COLON = 288230376151711744L; + + private static final long H_COLON = 0L; + + private static final long L_SLASH = 140737488355328L; + + private static final long H_SLASH = 0L; + + private static final long L_BACKSLASH = 0L; + + private static final long H_BACKSLASH = 268435456L; + + private static final long L_NON_PRINTABLE = 4294967295L; + + private static final long H_NON_PRINTABLE = -9223372036854775808L; + + private static final long L_EXCLUDE = -8935000884560003073L; + + private static final long H_EXCLUDE = -9223372035915251711L; + + public static byte[] textToNumericFormatV4(String paramString) { + byte[] arrayOfByte = new byte[4]; + long l = 0L; + byte b1 = 0; + boolean bool = true; + int i = paramString.length(); + if (i == 0 || i > 15) + return null; + for (byte b2 = 0; b2 < i; b2++) { + char c = paramString.charAt(b2); + if (c == '.') { + if (bool || l < 0L || l > 255L || b1 == 3) + return null; + arrayOfByte[b1++] = (byte) (int) (l & 0xFFL); + l = 0L; + bool = true; + } else { + int j = Character.digit(c, 10); + if (j < 0) + return null; + l *= 10L; + l += j; + bool = false; + } + } + if (bool || l < 0L || l >= 1L << (4 - b1) * 8) + return null; + switch (b1) { + case 0 : + arrayOfByte[0] = (byte) (int) (l >> 24L & 0xFFL); + case 1 : + arrayOfByte[1] = (byte) (int) (l >> 16L & 0xFFL); + case 2 : + arrayOfByte[2] = (byte) (int) (l >> 8L & 0xFFL); + case 3 : + arrayOfByte[3] = (byte) (int) (l >> 0L & 0xFFL); + break; + } + return arrayOfByte; + } + + public static byte[] textToNumericFormatV6(String paramString) { + if (paramString.length() < 2) + return null; + char[] arrayOfChar = paramString.toCharArray(); + byte[] arrayOfByte1 = new byte[16]; + int j = arrayOfChar.length; + int k = paramString.indexOf("%"); + if (k == j - 1) + return null; + if (k != -1) + j = k; + byte b = -1; + byte b1 = 0, b2 = 0; + if (arrayOfChar[b1] == ':' && arrayOfChar[++b1] != ':') + return null; + byte b3 = b1; + boolean bool = false; + int i = 0; + while (b1 < j) { + char c = arrayOfChar[b1++]; + int m = Character.digit(c, 16); + if (m != -1) { + i <<= 4; + i |= m; + if (i > 65535) + return null; + bool = true; + continue; + } + if (c == ':') { + b3 = b1; + if (!bool) { + if (b != -1) + return null; + b = b2; + continue; + } + if (b1 == j) + return null; + if (b2 + 2 > 16) + return null; + arrayOfByte1[b2++] = (byte) (i >> 8 & 0xFF); + arrayOfByte1[b2++] = (byte) (i & 0xFF); + bool = false; + i = 0; + continue; + } + if (c == '.' && b2 + 4 <= 16) { + String str = paramString.substring(b3, j); + byte b4 = 0; + int n = 0; + while ((n = str.indexOf('.', n)) != -1) { + b4++; + n++; + } + if (b4 != 3) + return null; + byte[] arrayOfByte = textToNumericFormatV4(str); + if (arrayOfByte == null) + return null; + for (byte b5 = 0; b5 < 4; b5++) + arrayOfByte1[b2++] = arrayOfByte[b5]; + bool = false; + break; + } + return null; + } + if (bool) { + if (b2 + 2 > 16) + return null; + arrayOfByte1[b2++] = (byte) (i >> 8 & 0xFF); + arrayOfByte1[b2++] = (byte) (i & 0xFF); + } + if (b != -1) { + int m = b2 - b; + if (b2 == 16) + return null; + for (b1 = 1; b1 <= m; b1++) { + arrayOfByte1[16 - b1] = arrayOfByte1[b + m - b1]; + arrayOfByte1[b + m - b1] = 0; + } + b2 = 16; + } + if (b2 != 16) + return null; + byte[] arrayOfByte2 = convertFromIPv4MappedAddress(arrayOfByte1); + if (arrayOfByte2 != null) + return arrayOfByte2; + return arrayOfByte1; + } + + public static boolean isIPv4LiteralAddress(String paramString) { + return (textToNumericFormatV4(paramString) != null); + } + + public static boolean isIPv6LiteralAddress(String paramString) { + return (textToNumericFormatV6(paramString) != null); + } + + public static byte[] convertFromIPv4MappedAddress(byte[] paramArrayOfbyte) { + if (isIPv4MappedAddress(paramArrayOfbyte)) { + byte[] arrayOfByte = new byte[4]; + System.arraycopy(paramArrayOfbyte, 12, arrayOfByte, 0, 4); + return arrayOfByte; + } + return null; + } + + private static boolean isIPv4MappedAddress(byte[] paramArrayOfbyte) { + if (paramArrayOfbyte.length < 16) + return false; + if (paramArrayOfbyte[0] == 0 && paramArrayOfbyte[1] == 0 && paramArrayOfbyte[2] == 0 && paramArrayOfbyte[3] == 0 + && paramArrayOfbyte[4] == 0 && paramArrayOfbyte[5] == 0 && paramArrayOfbyte[6] == 0 + && paramArrayOfbyte[7] == 0 && paramArrayOfbyte[8] == 0 && paramArrayOfbyte[9] == 0 + && paramArrayOfbyte[10] == -1 && paramArrayOfbyte[11] == -1) + return true; + return false; + } + + public static boolean match(char paramChar, long paramLong1, long paramLong2) { + if (paramChar < '@') + return ((1L << paramChar & paramLong1) != 0L); + if (paramChar < '€') + return ((1L << paramChar - 64 & paramLong2) != 0L); + return false; + } + + public static int scan(String paramString, long paramLong1, long paramLong2) { + byte b = -1; + int i; + if (paramString == null || (i = paramString.length()) == 0) + return -1; + boolean bool = false; + while (++b < i && !(bool = match(paramString.charAt(b), paramLong1, paramLong2))); + if (bool) + return b; + return -1; + } + + public static int scan(String paramString, long paramLong1, long paramLong2, char[] paramArrayOfchar) { + byte b = -1; + int i; + if (paramString == null || (i = paramString.length()) == 0) + return -1; + boolean bool = false; + char c2 = paramArrayOfchar[0]; + char c1; + while (++b < i && !(bool = match(c1 = paramString.charAt(b), paramLong1, paramLong2))) { + if (c1 >= c2 && Arrays.binarySearch(paramArrayOfchar, c1) > -1) { + bool = true; + break; + } + } + if (bool) + return b; + return -1; + } + + private static String describeChar(char paramChar) { + if (paramChar < ' ' || paramChar == '') { + if (paramChar == '\n') + return "LF"; + if (paramChar == '\r') + return "CR"; + return "control char (code=" + paramChar + ")"; + } + if (paramChar == '\\') + return "'\\'"; + return "'" + paramChar + "'"; + } + + private static String checkUserInfo(String paramString) { + int i = scan(paramString, -9223231260711714817L, -9223372035915251711L); + if (i >= 0) + return "Illegal character found in user-info: " + describeChar(paramString.charAt(i)); + return null; + } + + private static String checkHost(String paramString) { + if (paramString.startsWith("[") && paramString.endsWith("]")) { + paramString = paramString.substring(1, paramString.length() - 1); + if (isIPv6LiteralAddress(paramString)) { + int j = paramString.indexOf('%'); + if (j >= 0) { + j = scan(paramString = paramString.substring(j), 4294967295L, -9223372036183687168L); + if (j >= 0) + return "Illegal character found in IPv6 scoped address: " + describeChar(paramString.charAt(j)); + } + return null; + } + return "Unrecognized IPv6 address format"; + } + int i = scan(paramString, -8935000884560003073L, -9223372035915251711L); + if (i >= 0) + return "Illegal character found in host: " + describeChar(paramString.charAt(i)); + return null; + } + + private static String checkAuth(String paramString) { + int i = scan(paramString, -9223231260711714817L, -9223372036586340352L); + if (i >= 0) + return "Illegal character found in authority: " + describeChar(paramString.charAt(i)); + return null; + } + + public static String checkAuthority(URL paramURL) { + if (paramURL == null) + return null; + String str1; + String str2; + if ((str1 = checkUserInfo(str2 = paramURL.getUserInfo())) != null) + return str1; + String str3; + if ((str1 = checkHost(str3 = paramURL.getHost())) != null) + return str1; + if (str3 == null && str2 == null) + return checkAuth(paramURL.getAuthority()); + return null; + } + + public static String checkExternalForm(URL paramURL) { + if (paramURL == null) + return null; + String str; + int i = scan(str = paramURL.getUserInfo(), 140741783322623L, Long.MIN_VALUE); + if (i >= 0) + return "Illegal character found in authority: " + describeChar(str.charAt(i)); + if ((str = checkHostString(paramURL.getHost())) != null) + return str; + return null; + } + + public static String checkHostString(String paramString) { + if (paramString == null) + return null; + return null; + } +} \ No newline at end of file