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.getLogAgent(), 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.getLogAgent().logInfo("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.getLogAgent().logInfo(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.readSuccessfully == 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}