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 }