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.func_98033_al(), 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.func_98033_al().func_98233_a("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.func_98033_al().func_98233_a(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.field_82559_a == 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}