001    package net.minecraft.network;
002    
003    import java.io.IOException;
004    import java.io.Serializable;
005    import java.net.InetAddress;
006    import java.net.Socket;
007    import java.security.PrivateKey;
008    import java.security.PublicKey;
009    import java.util.Arrays;
010    import java.util.Iterator;
011    import java.util.List;
012    import java.util.Random;
013    import java.util.logging.Logger;
014    import javax.crypto.SecretKey;
015    
016    import cpw.mods.fml.common.network.FMLNetworkHandler;
017    import net.minecraft.entity.player.EntityPlayer;
018    import net.minecraft.entity.player.EntityPlayerMP;
019    import net.minecraft.network.packet.NetHandler;
020    import net.minecraft.network.packet.Packet;
021    import net.minecraft.network.packet.Packet1Login;
022    import net.minecraft.network.packet.Packet205ClientCommand;
023    import net.minecraft.network.packet.Packet250CustomPayload;
024    import net.minecraft.network.packet.Packet252SharedKey;
025    import net.minecraft.network.packet.Packet253ServerAuthData;
026    import net.minecraft.network.packet.Packet254ServerPing;
027    import net.minecraft.network.packet.Packet255KickDisconnect;
028    import net.minecraft.network.packet.Packet2ClientProtocol;
029    import net.minecraft.server.MinecraftServer;
030    import net.minecraft.server.dedicated.DedicatedServerListenThread;
031    import net.minecraft.server.management.ServerConfigurationManager;
032    import net.minecraft.util.StringUtils;
033    
034    public 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_92028_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_92028_k)
154                {
155                    this.raiseErrorAndDisconnect("Duplicate login");
156                    return;
157                }
158    
159                this.field_92028_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    }