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