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 }