    001package net.minecraft.network;
    002
    003import java.io.IOException;
    004import java.io.Serializable;
    005import java.net.InetAddress;
    006import java.net.Socket;
    007import java.security.PrivateKey;
    008import java.security.PublicKey;
    009import java.util.Arrays;
    010import java.util.Iterator;
    011import java.util.List;
    012import java.util.Random;
    013import java.util.logging.Logger;
    014import javax.crypto.SecretKey;
    015
    016import cpw.mods.fml.common.network.FMLNetworkHandler;
    017import net.minecraft.entity.player.EntityPlayer;
    018import net.minecraft.entity.player.EntityPlayerMP;
    019import net.minecraft.network.packet.NetHandler;
    020import net.minecraft.network.packet.Packet;
    021import net.minecraft.network.packet.Packet1Login;
    022import net.minecraft.network.packet.Packet205ClientCommand;
    023import net.minecraft.network.packet.Packet250CustomPayload;
    024import net.minecraft.network.packet.Packet252SharedKey;
    025import net.minecraft.network.packet.Packet253ServerAuthData;
    026import net.minecraft.network.packet.Packet254ServerPing;
    027import net.minecraft.network.packet.Packet255KickDisconnect;
    028import net.minecraft.network.packet.Packet2ClientProtocol;
    029import net.minecraft.server.MinecraftServer;
    030import net.minecraft.server.dedicated.DedicatedServerListenThread;
    031import net.minecraft.server.management.ServerConfigurationManager;
    032import net.minecraft.util.StringUtils;
    033
    034public class NetLoginHandler extends NetHandler
    035{
    036    /** The 4 byte verify token read from a Packet252SharedKey */
    037    private byte[] verifyToken;
    038
    039    /** The Minecraft logger. */
    040    public static Logger logger = Logger.getLogger("Minecraft");
    041
    042    /** The Random object used to generate serverId hex strings. */
    043    private static Random rand = new Random();
    044    public TcpConnection myTCPConnection;
    045    public boolean connectionComplete = false;
    046
    047    /** Reference to the MinecraftServer object. */
    048    private MinecraftServer mcServer;
    049    private int connectionTimer = 0;
    050    public String clientUsername = null;
    051    private volatile boolean field_72544_i = false;
    052
    053    /** server ID that is randomly generated by this login handler. */
    054    private String loginServerId = "";
    055    private boolean field_92079_k = false;
    056
    057    /** Secret AES key obtained from the client's Packet252SharedKey */
    058    private SecretKey sharedKey = null;
    059
    060    public NetLoginHandler(MinecraftServer par1MinecraftServer, Socket par2Socket, String par3Str) throws IOException
    061    {
    062        this.mcServer = par1MinecraftServer;
    063        this.myTCPConnection = new TcpConnection(par2Socket, par3Str, this, par1MinecraftServer.getKeyPair().getPrivate());
    064        this.myTCPConnection.field_74468_e = 0;
    065    }
    066
    067    /**
    068     * Logs the user in if a login packet is found, otherwise keeps processing network packets unless the timeout has
    069     * occurred.
    070     */
    071    public void tryLogin()
    072    {
    073        if (this.field_72544_i)
    074        {
    075            this.initializePlayerConnection();
    076        }
    077
    078        if (this.connectionTimer++ == 6000)
    079        {
    080            this.raiseErrorAndDisconnect("Took too long to log in");
    081        }
    082        else
    083        {
    084            this.myTCPConnection.processReadPackets();
    085        }
    086    }
    087
    088    public void raiseErrorAndDisconnect(String par1Str)
    089    {
    090        try
    091        {
    092            logger.info("Disconnecting " + this.getUsernameAndAddress() + ": " + par1Str);
    093            this.myTCPConnection.addToSendQueue(new Packet255KickDisconnect(par1Str));
    094            this.myTCPConnection.serverShutdown();
    095            this.connectionComplete = true;
    096        }
    097        catch (Exception var3)
    098        {
    099            var3.printStackTrace();
    100        }
    101    }
    102
    103    public void handleClientProtocol(Packet2ClientProtocol par1Packet2ClientProtocol)
    104    {
    105        this.clientUsername = par1Packet2ClientProtocol.getUsername();
    106
    107        if (!this.clientUsername.equals(StringUtils.stripControlCodes(this.clientUsername)))
    108        {
    109            this.raiseErrorAndDisconnect("Invalid username!");
    110        }
    111        else
    112        {
    113            PublicKey var2 = this.mcServer.getKeyPair().getPublic();
    114
    115            if (par1Packet2ClientProtocol.getProtocolVersion() != 51)
    116            {
    117                if (par1Packet2ClientProtocol.getProtocolVersion() > 51)
    118                {
    119                    this.raiseErrorAndDisconnect("Outdated server!");
    120                }
    121                else
    122                {
    123                    this.raiseErrorAndDisconnect("Outdated client!");
    124                }
    125            }
    126            else
    127            {
    128                this.loginServerId = this.mcServer.isServerInOnlineMode() ? Long.toString(rand.nextLong(), 16) : "-";
    129                this.verifyToken = new byte[4];
    130                rand.nextBytes(this.verifyToken);
    131                this.myTCPConnection.addToSendQueue(new Packet253ServerAuthData(this.loginServerId, var2, this.verifyToken));
    132            }
    133        }
    134    }
    135
    136    public void handleSharedKey(Packet252SharedKey par1Packet252SharedKey)
    137    {
    138        PrivateKey var2 = this.mcServer.getKeyPair().getPrivate();
    139        this.sharedKey = par1Packet252SharedKey.getSharedKey(var2);
    140
    141        if (!Arrays.equals(this.verifyToken, par1Packet252SharedKey.getVerifyToken(var2)))
    142        {
    143            this.raiseErrorAndDisconnect("Invalid client reply");
    144        }
    145
    146        this.myTCPConnection.addToSendQueue(new Packet252SharedKey());
    147    }
    148
    149    public void handleClientCommand(Packet205ClientCommand par1Packet205ClientCommand)
    150    {
    151        if (par1Packet205ClientCommand.forceRespawn == 0)
    152        {
    153            if (this.field_92079_k)
    154            {
    155                this.raiseErrorAndDisconnect("Duplicate login");
    156                return;
    157            }
    158
    159            this.field_92079_k = true;
    160
    161            if (this.mcServer.isServerInOnlineMode())
    162            {
    163                (new ThreadLoginVerifier(this)).start();
    164            }
    165            else
    166            {
    167                this.field_72544_i = true;
    168            }
    169        }
    170    }
    171
    172    public void handleLogin(Packet1Login par1Packet1Login)
    173    {
    174        FMLNetworkHandler.handleLoginPacketOnServer(this, par1Packet1Login);
    175    }
    176
    177    /**
    178     * on success the specified username is connected to the minecraftInstance, otherwise they are packet255'd
    179     */
    180    public void initializePlayerConnection()
    181    {
    182        FMLNetworkHandler.onConnectionReceivedFromClient(this, this.mcServer, this.myTCPConnection.getSocketAddress(), this.clientUsername);
    183    }
    184
    185    public void completeConnection(String var1)
    186    {
    187
    188        if (var1 != null)
    189        {
    190            this.raiseErrorAndDisconnect(var1);
    191        }
    192        else
    193        {
    194            EntityPlayerMP var2 = this.mcServer.getConfigurationManager().createPlayerForUser(this.clientUsername);
    195
    196            if (var2 != null)
    197            {
    198                this.mcServer.getConfigurationManager().initializeConnectionToPlayer(this.myTCPConnection, var2);
    199            }
    200        }
    201
    202        this.connectionComplete = true;
    203    }
    204
    205    public void handleErrorMessage(String par1Str, Object[] par2ArrayOfObj)
    206    {
    207        logger.info(this.getUsernameAndAddress() + " lost connection");
    208        this.connectionComplete = true;
    209    }
    210
    211    /**
    212     * Handle a server ping packet.
    213     */
    214    public void handleServerPing(Packet254ServerPing par1Packet254ServerPing)
    215    {
    216        try
    217        {
    218            ServerConfigurationManager var2 = this.mcServer.getConfigurationManager();
    219            String var3 = null;
    220
    221            if (par1Packet254ServerPing.field_82559_a == 1)
    222            {
    223                List var4 = Arrays.asList(new Serializable[] {Integer.valueOf(1), Integer.valueOf(51), this.mcServer.getMinecraftVersion(), this.mcServer.getMOTD(), Integer.valueOf(var2.getCurrentPlayerCount()), Integer.valueOf(var2.getMaxPlayers())});
    224                Object var6;
    225
    226                for (Iterator var5 = var4.iterator(); var5.hasNext(); var3 = var3 + var6.toString().replaceAll("\u0000", ""))
    227                {
    228                    var6 = var5.next();
    229
    230                    if (var3 == null)
    231                    {
    232                        var3 = "\u00a7";
    233                    }
    234                    else
    235                    {
    236                        var3 = var3 + "\u0000";
    237                    }
    238                }
    239            }
    240            else
    241            {
    242                var3 = this.mcServer.getMOTD() + "\u00a7" + var2.getCurrentPlayerCount() + "\u00a7" + var2.getMaxPlayers();
    243            }
    244
    245            InetAddress var8 = null;
    246
    247            if (this.myTCPConnection.getSocket() != null)
    248            {
    249                var8 = this.myTCPConnection.getSocket().getInetAddress();
    250            }
    251
    252            this.myTCPConnection.addToSendQueue(new Packet255KickDisconnect(var3));
    253            this.myTCPConnection.serverShutdown();
    254
    255            if (var8 != null && this.mcServer.getNetworkThread() instanceof DedicatedServerListenThread)
    256            {
    257                ((DedicatedServerListenThread)this.mcServer.getNetworkThread()).func_71761_a(var8);
    258            }
    259
    260            this.connectionComplete = true;
    261        }
    262        catch (Exception var7)
    263        {
    264            var7.printStackTrace();
    265        }
    266    }
    267
    268    /**
    269     * Default handler called for packets that don't have their own handlers in NetClientHandler; currentlly does
    270     * nothing.
    271     */
    272    public void unexpectedPacket(Packet par1Packet)
    273    {
    274        this.raiseErrorAndDisconnect("Protocol error");
    275    }
    276
    277    public String getUsernameAndAddress()
    278    {
    279        return this.clientUsername != null ? this.clientUsername + " [" + this.myTCPConnection.getSocketAddress().toString() + "]" : this.myTCPConnection.getSocketAddress().toString();
    280    }
    281
    282    /**
    283     * determine if it is a server handler
    284     */
    285    public boolean isServerHandler()
    286    {
    287        return true;
    288    }
    289
    290    /**
    291     * Returns the server Id randomly generated by this login handler.
    292     */
    293    static String getServerId(NetLoginHandler par0NetLoginHandler)
    294    {
    295        return par0NetLoginHandler.loginServerId;
    296    }
    297
    298    /**
    299     * Returns the reference to Minecraft Server.
    300     */
    301    static MinecraftServer getLoginMinecraftServer(NetLoginHandler par0NetLoginHandler)
    302    {
    303        return par0NetLoginHandler.mcServer;
    304    }
    305
    306    /**
    307     * Return the secret AES sharedKey
    308     */
    309    static SecretKey getSharedKey(NetLoginHandler par0NetLoginHandler)
    310    {
    311        return par0NetLoginHandler.sharedKey;
    312    }
    313
    314    /**
    315     * Returns the connecting client username.
    316     */
    317    static String getClientUsername(NetLoginHandler par0NetLoginHandler)
    318    {
    319        return par0NetLoginHandler.clientUsername;
    320    }
    321
    322    public static boolean func_72531_a(NetLoginHandler par0NetLoginHandler, boolean par1)
    323    {
    324        return par0NetLoginHandler.field_72544_i = par1;
    325    }
    326
    327
    328    public void handleCustomPayload(Packet250CustomPayload par1Packet250CustomPayload)
    329    {
    330        FMLNetworkHandler.handlePacket250Packet(par1Packet250CustomPayload, myTCPConnection, this);
    331    }
    332
    333    @Override
    334    public void handleVanilla250Packet(Packet250CustomPayload payload)
    335    {
    336        // NOOP for login
    337    }
    338
    339    public EntityPlayer getPlayer()
    340    {
    341        return null;
    342    };
    343}