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}