Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package net.md_5.bungee.connection;
- import com.google.common.base.Charsets;
- import com.google.common.base.Preconditions;
- import com.google.gson.Gson;
- import java.math.BigInteger;
- import java.net.InetAddress;
- import java.net.InetSocketAddress;
- import java.net.URLEncoder;
- import java.security.MessageDigest;
- import java.util.List;
- import java.util.UUID;
- import java.util.concurrent.TimeUnit;
- import java.util.logging.Level;
- import javax.crypto.SecretKey;
- import lombok.Getter;
- import lombok.RequiredArgsConstructor;
- import lombok.Setter;
- import net.md_5.bungee.BungeeCord;
- import net.md_5.bungee.BungeeServerInfo;
- import net.md_5.bungee.EncryptionUtil;
- import net.md_5.bungee.UserConnection;
- import net.md_5.bungee.Util;
- import net.md_5.bungee.api.AbstractReconnectHandler;
- import net.md_5.bungee.api.Callback;
- import net.md_5.bungee.api.ChatColor;
- import net.md_5.bungee.api.Favicon;
- import net.md_5.bungee.api.ServerPing;
- import net.md_5.bungee.api.chat.BaseComponent;
- import net.md_5.bungee.api.chat.TextComponent;
- import net.md_5.bungee.api.config.ListenerInfo;
- import net.md_5.bungee.api.config.ServerInfo;
- import net.md_5.bungee.api.connection.Connection.Unsafe;
- import net.md_5.bungee.api.connection.PendingConnection;
- import net.md_5.bungee.api.connection.ProxiedPlayer;
- import net.md_5.bungee.api.event.LoginEvent;
- import net.md_5.bungee.api.event.PlayerHandshakeEvent;
- import net.md_5.bungee.api.event.PostLoginEvent;
- import net.md_5.bungee.api.event.PreLoginEvent;
- import net.md_5.bungee.api.event.ProxyPingEvent;
- import net.md_5.bungee.chat.ComponentSerializer;
- import net.md_5.bungee.http.HttpClient;
- import net.md_5.bungee.jni.cipher.BungeeCipher;
- import net.md_5.bungee.netty.ChannelWrapper;
- import net.md_5.bungee.netty.HandlerBoss;
- import net.md_5.bungee.netty.PacketHandler;
- import net.md_5.bungee.netty.PipelineUtils;
- import net.md_5.bungee.netty.cipher.CipherDecoder;
- import net.md_5.bungee.netty.cipher.CipherEncoder;
- import net.md_5.bungee.protocol.DefinedPacket;
- import net.md_5.bungee.protocol.PacketWrapper;
- import net.md_5.bungee.protocol.Protocol;
- import net.md_5.bungee.protocol.ProtocolConstants;
- import net.md_5.bungee.protocol.packet.EncryptionRequest;
- import net.md_5.bungee.protocol.packet.EncryptionResponse;
- import net.md_5.bungee.protocol.packet.Handshake;
- import net.md_5.bungee.protocol.packet.Kick;
- import net.md_5.bungee.protocol.packet.LegacyHandshake;
- import net.md_5.bungee.protocol.packet.LegacyPing;
- import net.md_5.bungee.protocol.packet.LoginRequest;
- import net.md_5.bungee.protocol.packet.LoginSuccess;
- 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.BoundedArrayList;
- @RequiredArgsConstructor
- public class InitialHandler extends PacketHandler implements PendingConnection {
- private final BungeeCord bungee;
- private ChannelWrapper ch;
- @Getter
- private final ListenerInfo listener;
- @Getter
- private Handshake handshake;
- @Getter
- private LoginRequest loginRequest;
- private EncryptionRequest request;
- @Getter
- private final List<PluginMessage> relayMessages = new BoundedArrayList<>(128);
- private State thisState = State.HANDSHAKE;
- private final Unsafe unsafe = new Unsafe() {
- @Override
- public void sendPacket(DefinedPacket packet) {
- ch.write(packet);
- }
- };
- @Getter
- private boolean onlineMode = BungeeCord.getInstance().config.isOnlineMode();
- @Getter
- private InetSocketAddress virtualHost;
- private String name;
- @Getter
- private UUID uniqueId;
- @Getter
- private UUID offlineId;
- @Getter
- @Setter
- private LoginResult loginProfile;
- @Getter
- private boolean legacy;
- @Getter
- private String extraDataInHandshake = "";
- @Override
- public boolean shouldHandle(PacketWrapper packet) throws Exception {
- return !ch.isClosing();
- }
- private enum State {
- HANDSHAKE, STATUS, PING, USERNAME, ENCRYPT, FINISHED;
- }
- @Override
- public void connected(ChannelWrapper channel) throws Exception {
- this.ch = channel;
- }
- @Override
- public void exception(Throwable t) throws Exception {
- disconnect(ChatColor.RED + Util.exception(t));
- }
- @Override
- public void handle(PluginMessage pluginMessage) throws Exception {
- // TODO: Unregister?
- if (PluginMessage.SHOULD_RELAY.apply(pluginMessage)) {
- relayMessages.add(pluginMessage);
- }
- }
- @Override
- public void handle(LegacyHandshake legacyHandshake) throws Exception {
- this.legacy = true;
- ch.close(bungee.getTranslation("outdated_client"));
- }
- @Override
- public void handle(LegacyPing ping) throws Exception {
- this.legacy = true;
- final boolean v1_5 = ping.isV1_5();
- ServerPing legacy = new ServerPing(new ServerPing.Protocol(bungee.getName() + " " + bungee.getGameVersion(), bungee.getProtocolVersion()),
- new ServerPing.Players(listener.getMaxPlayers(), bungee.getOnlineCount(), null),
- new TextComponent(TextComponent.fromLegacyText(listener.getMotd())), (Favicon) null);
- Callback<ProxyPingEvent> callback = new Callback<ProxyPingEvent>() {
- @Override
- public void done(ProxyPingEvent result, Throwable error) {
- if (ch.isClosed()) {
- return;
- }
- ServerPing legacy = result.getResponse();
- String kickMessage;
- if (v1_5) {
- kickMessage = ChatColor.DARK_BLUE
- + "\00" + 127
- + '\00' + legacy.getVersion().getName()
- + '\00' + getFirstLine(legacy.getDescription())
- + '\00' + legacy.getPlayers().getOnline()
- + '\00' + legacy.getPlayers().getMax();
- } else {
- // Clients <= 1.3 don't support colored motds because the color char is used as delimiter
- kickMessage = ChatColor.stripColor(getFirstLine(legacy.getDescription()))
- + '\u00a7' + legacy.getPlayers().getOnline()
- + '\u00a7' + legacy.getPlayers().getMax();
- }
- ch.close( kickMessage );
- }
- };
- bungee.getPluginManager().callEvent(new ProxyPingEvent(this, legacy, callback));
- }
- private static String getFirstLine(String str) {
- int pos = str.indexOf('\n');
- return pos == -1 ? str : str.substring(0, pos);
- }
- @Override
- public void handle(StatusRequest statusRequest) throws Exception {
- Preconditions.checkState(thisState == State.STATUS, "Not expecting STATUS");
- ServerInfo forced = AbstractReconnectHandler.getForcedHost(this);
- final String motd = (forced != null) ? forced.getMotd() : listener.getMotd();
- Callback<ServerPing> pingBack = new Callback<ServerPing>() {
- @Override
- public void done(ServerPing result, Throwable error) {
- if (error != null) {
- result = new ServerPing();
- result.setDescription(bungee.getTranslation("ping_cannot_connect"));
- bungee.getLogger().log(Level.WARNING, "Error pinging remote server", error);
- }
- Callback<ProxyPingEvent> callback = new Callback<ProxyPingEvent>() {
- @Override
- public void done(ProxyPingEvent pingResult, Throwable error) {
- Gson gson = BungeeCord.getInstance().gson;
- unsafe.sendPacket(new StatusResponse(gson.toJson(pingResult.getResponse())));
- }
- };
- bungee.getPluginManager().callEvent(new ProxyPingEvent(InitialHandler.this, result, callback));
- }
- };
- if (forced != null && listener.isPingPassthrough()) {
- ((BungeeServerInfo) forced).ping(pingBack, handshake.getProtocolVersion());
- } else {
- int protocol = (ProtocolConstants.SUPPORTED_VERSION_IDS.contains(handshake.getProtocolVersion())) ? handshake.getProtocolVersion() : bungee.getProtocolVersion();
- pingBack.done(new ServerPing(
- new ServerPing.Protocol(bungee.getName() + " " + bungee.getGameVersion(), protocol),
- new ServerPing.Players(listener.getMaxPlayers(), bungee.getOnlineCount(), null),
- motd, BungeeCord.getInstance().config.getFaviconObject()),
- null);
- }
- thisState = State.PING;
- }
- private static final boolean ACCEPT_INVALID_PACKETS = Boolean.parseBoolean(System.getProperty("waterfall.acceptInvalidPackets", "false"));
- @Override
- public void handle(PingPacket ping) throws Exception {
- if (!ACCEPT_INVALID_PACKETS) {
- Preconditions.checkState(thisState == State.PING, "Not expecting PING");
- }
- unsafe.sendPacket(ping);
- disconnect("");
- }
- @Override
- public void handle(Handshake handshake) throws Exception {
- Preconditions.checkState(thisState == State.HANDSHAKE, "Not expecting HANDSHAKE");
- this.handshake = handshake;
- ch.setVersion(handshake.getProtocolVersion());
- // Starting with FML 1.8, a "\0FML\0" token is appended to the handshake. This interferes
- // with Bungee's IP forwarding, so we detect it, and remove it from the host string, for now.
- // We know FML appends \00FML\00. However, we need to also consider that other systems might
- // add their own data to the end of the string. So, we just take everything from the \0 character
- // and save it for later.
- if (handshake.getHost().contains("\0")) {
- String[] split = handshake.getHost().split("\0", 2);
- handshake.setHost(split[0]);
- extraDataInHandshake = "\0" + split[1];
- }
- // SRV records can end with a . depending on DNS / client.
- if (handshake.getHost().endsWith(".")) {
- handshake.setHost(handshake.getHost().substring(0, handshake.getHost().length() - 1));
- }
- this.virtualHost = InetSocketAddress.createUnresolved(handshake.getHost(), handshake.getPort());
- bungee.getPluginManager().callEvent(new PlayerHandshakeEvent(InitialHandler.this, handshake));
- switch (handshake.getRequestedProtocol()) {
- case 1:
- if (BungeeCord.getInstance().getConfig().isLogServerListPing()) {
- bungee.getLogger().log(Level.INFO, "{0} is pinging", this);
- }
- // Ping
- thisState = State.STATUS;
- ch.setProtocol(Protocol.STATUS);
- break;
- case 2:
- // Login
- bungee.getLogger().log(Level.INFO, "{0} has connected", this);
- thisState = State.USERNAME;
- ch.setProtocol(Protocol.LOGIN);
- if (!ProtocolConstants.SUPPORTED_VERSION_IDS.contains(handshake.getProtocolVersion())) {
- if (handshake.getProtocolVersion() > bungee.getProtocolVersion()) {
- disconnect(bungee.getTranslation("outdated_server"));
- } else {
- disconnect(bungee.getTranslation("outdated_client"));
- }
- return;
- }
- if (bungee.getConnectionThrottle() != null && bungee.getConnectionThrottle().throttle(getAddress().getAddress())) {
- disconnect(bungee.getTranslation("join_throttle_kick", TimeUnit.MILLISECONDS.toSeconds(bungee.getConfig().getThrottle())));
- }
- break;
- default:
- throw new IllegalArgumentException("Cannot request protocol " + handshake.getRequestedProtocol());
- }
- }
- @Override
- public void handle(LoginRequest loginRequest) throws Exception {
- Preconditions.checkState(thisState == State.USERNAME, "Not expecting USERNAME");
- this.loginRequest = loginRequest;
- if (getName().contains(".")) {
- disconnect(bungee.getTranslation("name_invalid"));
- return;
- }
- if (getName().length() > 16) {
- disconnect(bungee.getTranslation("name_too_long"));
- return;
- }
- int limit = BungeeCord.getInstance().config.getPlayerLimit();
- if (limit > 0 && bungee.getOnlineCount() > limit) {
- disconnect(bungee.getTranslation("proxy_full"));
- return;
- }
- // If offline mode and they are already on, don't allow connect
- // We can just check by UUID here as names are based on UUID
- if (!isOnlineMode() && bungee.getPlayer(getUniqueId()) != null) {
- disconnect(bungee.getTranslation("already_connected_proxy"));
- return;
- }
- Callback<PreLoginEvent> callback = new Callback<PreLoginEvent>() {
- @Override
- public void done(PreLoginEvent result, Throwable error) {
- if (result.isCancelled()) {
- disconnect(result.getCancelReasonComponents());
- return;
- }
- if (ch.isClosed()) {
- return;
- }
- if (onlineMode) {
- unsafe().sendPacket(request = EncryptionUtil.encryptRequest());
- } else {
- finish();
- }
- thisState = State.ENCRYPT;
- }
- };
- // fire pre login event
- bungee.getPluginManager().callEvent(new PreLoginEvent(InitialHandler.this, callback));
- }
- @Override
- public void handle(final EncryptionResponse encryptResponse) throws Exception {
- Preconditions.checkState(thisState == State.ENCRYPT, "Not expecting ENCRYPT");
- SecretKey sharedKey = EncryptionUtil.getSecret(encryptResponse, request);
- BungeeCipher decrypt = EncryptionUtil.getCipher(false, sharedKey);
- ch.addBefore(PipelineUtils.FRAME_DECODER, PipelineUtils.DECRYPT_HANDLER, new CipherDecoder(decrypt));
- BungeeCipher encrypt = EncryptionUtil.getCipher(true, sharedKey);
- ch.addBefore(PipelineUtils.FRAME_PREPENDER, PipelineUtils.ENCRYPT_HANDLER, new CipherEncoder(encrypt));
- String encName = URLEncoder.encode(InitialHandler.this.getName(), "UTF-8");
- MessageDigest sha = MessageDigest.getInstance("SHA-1");
- for (byte[] bit : new byte[][]
- {
- request.getServerId().getBytes("ISO_8859_1"), sharedKey.getEncoded(), EncryptionUtil.keys.getPublic().getEncoded()
- }) {
- sha.update(bit);
- }
- String encodedHash = URLEncoder.encode(new BigInteger(sha.digest()).toString(16), "UTF-8");
- String preventProxy = ((BungeeCord.getInstance().config.isPreventProxyConnections()) ? "&ip=" + URLEncoder.encode(getAddress().getAddress().getHostAddress(), "UTF-8") : "");
- String authURL = "https://sessionserver.mojang.com/session/minecraft/hasJoined?username=" + encName + "&serverId=" + encodedHash + preventProxy;
- Callback<String> handler = new Callback<String>() {
- @Override
- public void done(String result, Throwable error) {
- if (error == null) {
- LoginResult obj = BungeeCord.getInstance().gson.fromJson(result, LoginResult.class);
- if (obj != null && obj.getId() != null) {
- loginProfile = obj;
- name = obj.getName();
- uniqueId = Util.getUUID(obj.getId());
- finish();
- return;
- }
- disconnect(bungee.getTranslation("offline_mode_player"));
- } else {
- disconnect(bungee.getTranslation("mojang_fail"));
- bungee.getLogger().log(Level.SEVERE, "Error authenticating " + getName() + " with minecraft.net", error);
- }
- }
- };
- HttpClient.get(authURL, ch.getHandle().eventLoop(), handler);
- }
- private void finish() {
- if (isOnlineMode()) {
- // Check for multiple connections
- // We have to check for the old name first
- ProxiedPlayer oldName = bungee.getPlayer(getName());
- if (oldName != null) {
- // TODO See #1218
- oldName.disconnect(bungee.getTranslation("already_connected_proxy"));
- }
- // And then also for their old UUID
- ProxiedPlayer oldID = bungee.getPlayer(getUniqueId());
- if (oldID != null) {
- // TODO See #1218
- oldID.disconnect(bungee.getTranslation("already_connected_proxy"));
- }
- } else {
- // In offline mode the existing user stays and we kick the new one
- ProxiedPlayer oldName = bungee.getPlayer(getName());
- if (oldName != null) {
- // TODO See #1218
- disconnect(bungee.getTranslation("already_connected_proxy"));
- return;
- }
- }
- offlineId = java.util.UUID.nameUUIDFromBytes(("OfflinePlayer:" + getName()).getBytes(Charsets.UTF_8));
- if (uniqueId == null) {
- uniqueId = offlineId;
- }
- Callback<LoginEvent> complete = new Callback<LoginEvent>() {
- @Override
- public void done(LoginEvent result, Throwable error) {
- if (result.isCancelled()) {
- disconnect(result.getCancelReasonComponents());
- return;
- }
- if (ch.isClosed()) {
- return;
- }
- ch.getHandle().eventLoop().execute(new Runnable() {
- @Override
- public void run() {
- if (!ch.isClosing()) {
- UserConnection userCon = new UserConnection(bungee, ch, getName(), InitialHandler.this);
- userCon.setCompressionThreshold(BungeeCord.getInstance().config.getCompressionThreshold());
- userCon.init();
- unsafe.sendPacket(new LoginSuccess(getUniqueId().toString(), getName())); // With dashes in between
- ch.setProtocol(Protocol.GAME);
- ch.getHandle().pipeline().get(HandlerBoss.class).setHandler(new UpstreamBridge(bungee, userCon));
- bungee.getPluginManager().callEvent(new PostLoginEvent(userCon));
- ServerInfo server;
- if (bungee.getReconnectHandler() != null) {
- server = bungee.getReconnectHandler().getServer(userCon);
- } else {
- server = AbstractReconnectHandler.getForcedHost(InitialHandler.this);
- }
- if (server == null) {
- server = bungee.getServerInfo(listener.getDefaultServer());
- }
- userCon.connect(server, null, true);
- thisState = State.FINISHED;
- }
- }
- });
- }
- };
- // fire login event
- bungee.getPluginManager().callEvent(new LoginEvent(InitialHandler.this, complete));
- }
- @Override
- public void disconnect(String reason) {
- disconnect(TextComponent.fromLegacyText(reason));
- }
- @Override
- public void disconnect(final BaseComponent... reason) {
- if (thisState != State.STATUS && thisState != State.PING && thisState != State.HANDSHAKE) // Waterfall: Don't kick during handshake
- {
- ch.delayedClose(new Kick(ComponentSerializer.toString(reason)));
- } else {
- ch.close();
- }
- }
- @Override
- public void disconnect(BaseComponent reason) {
- disconnect(new BaseComponent[]
- {
- reason
- });
- }
- @Override
- <<<<<<< HEAD
- public String getName()
- {
- return ( name != null ) ? name : ( loginRequest == null ) ? null : loginRequest.getData();
- =======
- public String getName() {
- return (name != null) ? name : (loginRequest == null) ? null : loginRequest.getData();
- >>>>>>> API de definir skin
- }
- @Override
- public int getVersion() {
- return (handshake == null) ? -1 : handshake.getProtocolVersion();
- }
- @Override
- }
- @Override
- public Unsafe unsafe() {
- return unsafe;
- }
- @Override
- public void setSkin(String value, String signature) {
- LoginResult.Property textures = new LoginResult.Property("textures", value, signature);
- if (this.loginProfile == null) {
- this.loginProfile = new LoginResult(this.uniqueId.toString(), this.name, new LoginResult.Property[]{textures});
- } else {
- LoginResult.Property[] present = this.loginProfile.getProperties();
- LoginResult.Property[] newprops = new LoginResult.Property[present.length + 1];
- System.arraycopy(present, 0, newprops, 0, present.length);
- newprops[present.length] = textures;
- this.loginProfile.setProperties(newprops);
- }
- }
- @Override
- public void setOnlineMode(boolean onlineMode) {
- Preconditions.checkState(thisState == State.USERNAME, "Can only set online mode status whilst state is username");
- this.onlineMode = onlineMode;
- }
- @Override
- public void setUniqueId(UUID uuid) {
- Preconditions.checkState(thisState == State.USERNAME, "Can only set uuid while state is username");
- Preconditions.checkState(!onlineMode, "Can only set uuid when online mode is false");
- this.uniqueId = uuid;
- }
- @Override
- public String getUUID() {
- return io.github.waterfallmc.waterfall.utils.UUIDUtils.undash(uniqueId.toString()); // Waterfall
- }
- @Override
- public String toString() {
- return "[" + getAddress() + (getName() != null ? "|" + getName() : "") + "] <-> InitialHandler";
- }
- @Override
- public boolean isConnected() {
- return !ch.isClosed();
- }
- }
- <<<<<<< HEAD
- public InetSocketAddress getAddress()
- {
- return ch.getRemoteAddress();
- =======
- public InetSocketAddress getAddress() {
- return (InetSocketAddress) ch.getHandle().remoteAddress();
- >>>>>>> API de definir skin
Advertisement
Add Comment
Please, Sign In to add comment