diff --git a/api/src/main/java/net/md_5/bungee/Util.java b/api/src/main/java/net/md_5/bungee/Util.java index 31101f6b2..e565c836b 100644 --- a/api/src/main/java/net/md_5/bungee/Util.java +++ b/api/src/main/java/net/md_5/bungee/Util.java @@ -7,6 +7,7 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.URI; import java.net.URISyntaxException; +import java.util.Locale; import java.util.UUID; /** @@ -68,6 +69,17 @@ public class Util return String.format( "0x%02X", i ); } + /** + * Formats an char as a unicode value. + * + * @param c the character to format + * @return the unicode representation of the character + */ + public static String unicode(char c) + { + return "\\u" + String.format( "%04x", (int) c ).toUpperCase( Locale.ROOT ); + } + /** * Constructs a pretty one line version of a {@link Throwable}. Useful for * debugging. diff --git a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java index f5238abcd..cc40fe1b9 100644 --- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java +++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java @@ -62,6 +62,7 @@ import net.md_5.bungee.protocol.packet.PingPacket; import net.md_5.bungee.protocol.packet.PluginMessage; import net.md_5.bungee.protocol.packet.StatusRequest; import net.md_5.bungee.protocol.packet.StatusResponse; +import net.md_5.bungee.util.AllowedCharacters; import net.md_5.bungee.util.BoundedArrayList; import net.md_5.bungee.util.BufUtil; import net.md_5.bungee.util.QuietException; @@ -352,13 +353,13 @@ public class InitialHandler extends PacketHandler implements PendingConnection public void handle(LoginRequest loginRequest) throws Exception { Preconditions.checkState( thisState == State.USERNAME, "Not expecting USERNAME" ); - this.loginRequest = loginRequest; - if ( getName().contains( " " ) ) + if ( !AllowedCharacters.isValidName( loginRequest.getData(), onlineMode ) ) { disconnect( bungee.getTranslation( "name_invalid" ) ); return; } + this.loginRequest = loginRequest; int limit = BungeeCord.getInstance().config.getPlayerLimit(); if ( limit > 0 && bungee.getOnlineCount() >= limit ) diff --git a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java index c21bdefa9..41101a6e7 100644 --- a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java +++ b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java @@ -32,6 +32,7 @@ import net.md_5.bungee.protocol.packet.PlayerListItem; import net.md_5.bungee.protocol.packet.PluginMessage; import net.md_5.bungee.protocol.packet.TabCompleteRequest; import net.md_5.bungee.protocol.packet.TabCompleteResponse; +import net.md_5.bungee.util.AllowedCharacters; public class UpstreamBridge extends PacketHandler { @@ -147,10 +148,9 @@ public class UpstreamBridge extends PacketHandler for ( int index = 0, length = chat.getMessage().length(); index < length; index++ ) { char c = chat.getMessage().charAt( index ); - // Section symbol, control sequences, and delete - if ( c == '\u00A7' || c < ' ' || c == 127 ) + if ( !AllowedCharacters.isChatAllowedCharacter( c ) ) { - con.disconnect( bungee.getTranslation( "illegal_chat_characters", String.format( "\\u%04x", (int) c ) ) ); + con.disconnect( bungee.getTranslation( "illegal_chat_characters", Util.unicode( c ) ) ); throw CancelSendSignal.INSTANCE; } } diff --git a/proxy/src/main/java/net/md_5/bungee/util/AllowedCharacters.java b/proxy/src/main/java/net/md_5/bungee/util/AllowedCharacters.java new file mode 100644 index 000000000..d1cd10905 --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/util/AllowedCharacters.java @@ -0,0 +1,39 @@ +package net.md_5.bungee.util; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class AllowedCharacters +{ + + public static boolean isChatAllowedCharacter(char character) + { + // Section symbols, control sequences, and deletes are not allowed + return character != '\u00A7' && character >= ' ' && character != 127; + } + + private static boolean isNameAllowedCharacter(char c, boolean onlineMode) + { + if ( onlineMode ) + { + return ( c >= 'a' && c <= 'z' ) || ( c >= '0' && c <= '9' ) || ( c >= 'A' && c <= 'Z' ) || c == '_'; + } else + { + // Don't allow spaces, Yaml config doesn't support them + return isChatAllowedCharacter( c ) && c != ' '; + } + } + + public static boolean isValidName(String name, boolean onlineMode) + { + for ( int index = 0, len = name.length(); index < len; index++ ) + { + if ( !isNameAllowedCharacter( name.charAt( index ), onlineMode ) ) + { + return false; + } + } + return true; + } +}