skysurvival

Untitled

Nov 13th, 2017
191
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 22.97 KB | None | 0 0
  1. package net.md_5.bungee.connection;
  2.  
  3. import com.google.common.base.Charsets;
  4. import com.google.common.base.Preconditions;
  5. import com.google.gson.Gson;
  6.  
  7. import java.math.BigInteger;
  8. import java.net.InetAddress;
  9. import java.net.InetSocketAddress;
  10. import java.net.URLEncoder;
  11. import java.security.MessageDigest;
  12. import java.util.List;
  13. import java.util.UUID;
  14. import java.util.concurrent.TimeUnit;
  15. import java.util.logging.Level;
  16. import javax.crypto.SecretKey;
  17.  
  18. import lombok.Getter;
  19. import lombok.RequiredArgsConstructor;
  20. import lombok.Setter;
  21. import net.md_5.bungee.BungeeCord;
  22. import net.md_5.bungee.BungeeServerInfo;
  23. import net.md_5.bungee.EncryptionUtil;
  24. import net.md_5.bungee.UserConnection;
  25. import net.md_5.bungee.Util;
  26. import net.md_5.bungee.api.AbstractReconnectHandler;
  27. import net.md_5.bungee.api.Callback;
  28. import net.md_5.bungee.api.ChatColor;
  29. import net.md_5.bungee.api.Favicon;
  30. import net.md_5.bungee.api.ServerPing;
  31. import net.md_5.bungee.api.chat.BaseComponent;
  32. import net.md_5.bungee.api.chat.TextComponent;
  33. import net.md_5.bungee.api.config.ListenerInfo;
  34. import net.md_5.bungee.api.config.ServerInfo;
  35. import net.md_5.bungee.api.connection.Connection.Unsafe;
  36. import net.md_5.bungee.api.connection.PendingConnection;
  37. import net.md_5.bungee.api.connection.ProxiedPlayer;
  38. import net.md_5.bungee.api.event.LoginEvent;
  39. import net.md_5.bungee.api.event.PlayerHandshakeEvent;
  40. import net.md_5.bungee.api.event.PostLoginEvent;
  41. import net.md_5.bungee.api.event.PreLoginEvent;
  42. import net.md_5.bungee.api.event.ProxyPingEvent;
  43. import net.md_5.bungee.chat.ComponentSerializer;
  44. import net.md_5.bungee.http.HttpClient;
  45. import net.md_5.bungee.jni.cipher.BungeeCipher;
  46. import net.md_5.bungee.netty.ChannelWrapper;
  47. import net.md_5.bungee.netty.HandlerBoss;
  48. import net.md_5.bungee.netty.PacketHandler;
  49. import net.md_5.bungee.netty.PipelineUtils;
  50. import net.md_5.bungee.netty.cipher.CipherDecoder;
  51. import net.md_5.bungee.netty.cipher.CipherEncoder;
  52. import net.md_5.bungee.protocol.DefinedPacket;
  53. import net.md_5.bungee.protocol.PacketWrapper;
  54. import net.md_5.bungee.protocol.Protocol;
  55. import net.md_5.bungee.protocol.ProtocolConstants;
  56. import net.md_5.bungee.protocol.packet.EncryptionRequest;
  57. import net.md_5.bungee.protocol.packet.EncryptionResponse;
  58. import net.md_5.bungee.protocol.packet.Handshake;
  59. import net.md_5.bungee.protocol.packet.Kick;
  60. import net.md_5.bungee.protocol.packet.LegacyHandshake;
  61. import net.md_5.bungee.protocol.packet.LegacyPing;
  62. import net.md_5.bungee.protocol.packet.LoginRequest;
  63. import net.md_5.bungee.protocol.packet.LoginSuccess;
  64. import net.md_5.bungee.protocol.packet.PingPacket;
  65. import net.md_5.bungee.protocol.packet.PluginMessage;
  66. import net.md_5.bungee.protocol.packet.StatusRequest;
  67. import net.md_5.bungee.protocol.packet.StatusResponse;
  68. import net.md_5.bungee.util.BoundedArrayList;
  69.  
  70. @RequiredArgsConstructor
  71. public class InitialHandler extends PacketHandler implements PendingConnection {
  72.  
  73. private final BungeeCord bungee;
  74. private ChannelWrapper ch;
  75. @Getter
  76. private final ListenerInfo listener;
  77. @Getter
  78. private Handshake handshake;
  79. @Getter
  80. private LoginRequest loginRequest;
  81. private EncryptionRequest request;
  82. @Getter
  83. private final List<PluginMessage> relayMessages = new BoundedArrayList<>(128);
  84. private State thisState = State.HANDSHAKE;
  85. private final Unsafe unsafe = new Unsafe() {
  86. @Override
  87. public void sendPacket(DefinedPacket packet) {
  88. ch.write(packet);
  89. }
  90. };
  91. @Getter
  92. private boolean onlineMode = BungeeCord.getInstance().config.isOnlineMode();
  93. @Getter
  94. private InetSocketAddress virtualHost;
  95. private String name;
  96. @Getter
  97. private UUID uniqueId;
  98. @Getter
  99. private UUID offlineId;
  100. @Getter
  101. @Setter
  102. private LoginResult loginProfile;
  103. @Getter
  104. private boolean legacy;
  105. @Getter
  106. private String extraDataInHandshake = "";
  107.  
  108. @Override
  109. public boolean shouldHandle(PacketWrapper packet) throws Exception {
  110. return !ch.isClosing();
  111. }
  112.  
  113. private enum State {
  114.  
  115. HANDSHAKE, STATUS, PING, USERNAME, ENCRYPT, FINISHED;
  116. }
  117.  
  118. @Override
  119. public void connected(ChannelWrapper channel) throws Exception {
  120. this.ch = channel;
  121. }
  122.  
  123. @Override
  124. public void exception(Throwable t) throws Exception {
  125. disconnect(ChatColor.RED + Util.exception(t));
  126. }
  127.  
  128. @Override
  129. public void handle(PluginMessage pluginMessage) throws Exception {
  130. // TODO: Unregister?
  131. if (PluginMessage.SHOULD_RELAY.apply(pluginMessage)) {
  132. relayMessages.add(pluginMessage);
  133. }
  134. }
  135.  
  136. @Override
  137. public void handle(LegacyHandshake legacyHandshake) throws Exception {
  138. this.legacy = true;
  139. ch.close(bungee.getTranslation("outdated_client"));
  140. }
  141.  
  142. @Override
  143. public void handle(LegacyPing ping) throws Exception {
  144. this.legacy = true;
  145. final boolean v1_5 = ping.isV1_5();
  146.  
  147. ServerPing legacy = new ServerPing(new ServerPing.Protocol(bungee.getName() + " " + bungee.getGameVersion(), bungee.getProtocolVersion()),
  148. new ServerPing.Players(listener.getMaxPlayers(), bungee.getOnlineCount(), null),
  149. new TextComponent(TextComponent.fromLegacyText(listener.getMotd())), (Favicon) null);
  150.  
  151. Callback<ProxyPingEvent> callback = new Callback<ProxyPingEvent>() {
  152. @Override
  153. public void done(ProxyPingEvent result, Throwable error) {
  154. if (ch.isClosed()) {
  155. return;
  156. }
  157.  
  158. ServerPing legacy = result.getResponse();
  159. String kickMessage;
  160.  
  161. if (v1_5) {
  162. kickMessage = ChatColor.DARK_BLUE
  163. + "\00" + 127
  164. + '\00' + legacy.getVersion().getName()
  165. + '\00' + getFirstLine(legacy.getDescription())
  166. + '\00' + legacy.getPlayers().getOnline()
  167. + '\00' + legacy.getPlayers().getMax();
  168. } else {
  169. // Clients <= 1.3 don't support colored motds because the color char is used as delimiter
  170. kickMessage = ChatColor.stripColor(getFirstLine(legacy.getDescription()))
  171. + '\u00a7' + legacy.getPlayers().getOnline()
  172. + '\u00a7' + legacy.getPlayers().getMax();
  173. }
  174.  
  175. ch.close( kickMessage );
  176. }
  177. };
  178.  
  179. bungee.getPluginManager().callEvent(new ProxyPingEvent(this, legacy, callback));
  180. }
  181.  
  182. private static String getFirstLine(String str) {
  183. int pos = str.indexOf('\n');
  184. return pos == -1 ? str : str.substring(0, pos);
  185. }
  186.  
  187. @Override
  188. public void handle(StatusRequest statusRequest) throws Exception {
  189. Preconditions.checkState(thisState == State.STATUS, "Not expecting STATUS");
  190.  
  191. ServerInfo forced = AbstractReconnectHandler.getForcedHost(this);
  192. final String motd = (forced != null) ? forced.getMotd() : listener.getMotd();
  193.  
  194. Callback<ServerPing> pingBack = new Callback<ServerPing>() {
  195. @Override
  196. public void done(ServerPing result, Throwable error) {
  197. if (error != null) {
  198. result = new ServerPing();
  199. result.setDescription(bungee.getTranslation("ping_cannot_connect"));
  200. bungee.getLogger().log(Level.WARNING, "Error pinging remote server", error);
  201. }
  202.  
  203. Callback<ProxyPingEvent> callback = new Callback<ProxyPingEvent>() {
  204. @Override
  205. public void done(ProxyPingEvent pingResult, Throwable error) {
  206. Gson gson = BungeeCord.getInstance().gson;
  207. unsafe.sendPacket(new StatusResponse(gson.toJson(pingResult.getResponse())));
  208. }
  209. };
  210.  
  211. bungee.getPluginManager().callEvent(new ProxyPingEvent(InitialHandler.this, result, callback));
  212. }
  213. };
  214.  
  215. if (forced != null && listener.isPingPassthrough()) {
  216. ((BungeeServerInfo) forced).ping(pingBack, handshake.getProtocolVersion());
  217. } else {
  218. int protocol = (ProtocolConstants.SUPPORTED_VERSION_IDS.contains(handshake.getProtocolVersion())) ? handshake.getProtocolVersion() : bungee.getProtocolVersion();
  219. pingBack.done(new ServerPing(
  220. new ServerPing.Protocol(bungee.getName() + " " + bungee.getGameVersion(), protocol),
  221. new ServerPing.Players(listener.getMaxPlayers(), bungee.getOnlineCount(), null),
  222. motd, BungeeCord.getInstance().config.getFaviconObject()),
  223. null);
  224. }
  225.  
  226. thisState = State.PING;
  227. }
  228.  
  229. private static final boolean ACCEPT_INVALID_PACKETS = Boolean.parseBoolean(System.getProperty("waterfall.acceptInvalidPackets", "false"));
  230.  
  231. @Override
  232. public void handle(PingPacket ping) throws Exception {
  233. if (!ACCEPT_INVALID_PACKETS) {
  234. Preconditions.checkState(thisState == State.PING, "Not expecting PING");
  235. }
  236. unsafe.sendPacket(ping);
  237. disconnect("");
  238. }
  239.  
  240. @Override
  241. public void handle(Handshake handshake) throws Exception {
  242. Preconditions.checkState(thisState == State.HANDSHAKE, "Not expecting HANDSHAKE");
  243. this.handshake = handshake;
  244. ch.setVersion(handshake.getProtocolVersion());
  245.  
  246. // Starting with FML 1.8, a "\0FML\0" token is appended to the handshake. This interferes
  247. // with Bungee's IP forwarding, so we detect it, and remove it from the host string, for now.
  248. // We know FML appends \00FML\00. However, we need to also consider that other systems might
  249. // add their own data to the end of the string. So, we just take everything from the \0 character
  250. // and save it for later.
  251. if (handshake.getHost().contains("\0")) {
  252. String[] split = handshake.getHost().split("\0", 2);
  253. handshake.setHost(split[0]);
  254. extraDataInHandshake = "\0" + split[1];
  255. }
  256.  
  257. // SRV records can end with a . depending on DNS / client.
  258. if (handshake.getHost().endsWith(".")) {
  259. handshake.setHost(handshake.getHost().substring(0, handshake.getHost().length() - 1));
  260. }
  261.  
  262. this.virtualHost = InetSocketAddress.createUnresolved(handshake.getHost(), handshake.getPort());
  263.  
  264. bungee.getPluginManager().callEvent(new PlayerHandshakeEvent(InitialHandler.this, handshake));
  265.  
  266. switch (handshake.getRequestedProtocol()) {
  267. case 1:
  268. if (BungeeCord.getInstance().getConfig().isLogServerListPing()) {
  269. bungee.getLogger().log(Level.INFO, "{0} is pinging", this);
  270. }
  271. // Ping
  272. thisState = State.STATUS;
  273. ch.setProtocol(Protocol.STATUS);
  274. break;
  275. case 2:
  276. // Login
  277. bungee.getLogger().log(Level.INFO, "{0} has connected", this);
  278. thisState = State.USERNAME;
  279. ch.setProtocol(Protocol.LOGIN);
  280.  
  281. if (!ProtocolConstants.SUPPORTED_VERSION_IDS.contains(handshake.getProtocolVersion())) {
  282. if (handshake.getProtocolVersion() > bungee.getProtocolVersion()) {
  283. disconnect(bungee.getTranslation("outdated_server"));
  284. } else {
  285. disconnect(bungee.getTranslation("outdated_client"));
  286. }
  287. return;
  288. }
  289.  
  290. if (bungee.getConnectionThrottle() != null && bungee.getConnectionThrottle().throttle(getAddress().getAddress())) {
  291. disconnect(bungee.getTranslation("join_throttle_kick", TimeUnit.MILLISECONDS.toSeconds(bungee.getConfig().getThrottle())));
  292. }
  293. break;
  294. default:
  295. throw new IllegalArgumentException("Cannot request protocol " + handshake.getRequestedProtocol());
  296. }
  297. }
  298.  
  299. @Override
  300. public void handle(LoginRequest loginRequest) throws Exception {
  301. Preconditions.checkState(thisState == State.USERNAME, "Not expecting USERNAME");
  302. this.loginRequest = loginRequest;
  303.  
  304. if (getName().contains(".")) {
  305. disconnect(bungee.getTranslation("name_invalid"));
  306. return;
  307. }
  308.  
  309. if (getName().length() > 16) {
  310. disconnect(bungee.getTranslation("name_too_long"));
  311. return;
  312. }
  313.  
  314. int limit = BungeeCord.getInstance().config.getPlayerLimit();
  315. if (limit > 0 && bungee.getOnlineCount() > limit) {
  316. disconnect(bungee.getTranslation("proxy_full"));
  317. return;
  318. }
  319.  
  320. // If offline mode and they are already on, don't allow connect
  321. // We can just check by UUID here as names are based on UUID
  322. if (!isOnlineMode() && bungee.getPlayer(getUniqueId()) != null) {
  323. disconnect(bungee.getTranslation("already_connected_proxy"));
  324. return;
  325. }
  326.  
  327. Callback<PreLoginEvent> callback = new Callback<PreLoginEvent>() {
  328.  
  329. @Override
  330. public void done(PreLoginEvent result, Throwable error) {
  331. if (result.isCancelled()) {
  332. disconnect(result.getCancelReasonComponents());
  333. return;
  334. }
  335. if (ch.isClosed()) {
  336. return;
  337. }
  338. if (onlineMode) {
  339. unsafe().sendPacket(request = EncryptionUtil.encryptRequest());
  340. } else {
  341. finish();
  342. }
  343. thisState = State.ENCRYPT;
  344. }
  345. };
  346.  
  347. // fire pre login event
  348. bungee.getPluginManager().callEvent(new PreLoginEvent(InitialHandler.this, callback));
  349. }
  350.  
  351. @Override
  352. public void handle(final EncryptionResponse encryptResponse) throws Exception {
  353. Preconditions.checkState(thisState == State.ENCRYPT, "Not expecting ENCRYPT");
  354.  
  355. SecretKey sharedKey = EncryptionUtil.getSecret(encryptResponse, request);
  356. BungeeCipher decrypt = EncryptionUtil.getCipher(false, sharedKey);
  357. ch.addBefore(PipelineUtils.FRAME_DECODER, PipelineUtils.DECRYPT_HANDLER, new CipherDecoder(decrypt));
  358. BungeeCipher encrypt = EncryptionUtil.getCipher(true, sharedKey);
  359. ch.addBefore(PipelineUtils.FRAME_PREPENDER, PipelineUtils.ENCRYPT_HANDLER, new CipherEncoder(encrypt));
  360.  
  361. String encName = URLEncoder.encode(InitialHandler.this.getName(), "UTF-8");
  362.  
  363. MessageDigest sha = MessageDigest.getInstance("SHA-1");
  364. for (byte[] bit : new byte[][]
  365. {
  366. request.getServerId().getBytes("ISO_8859_1"), sharedKey.getEncoded(), EncryptionUtil.keys.getPublic().getEncoded()
  367. }) {
  368. sha.update(bit);
  369. }
  370. String encodedHash = URLEncoder.encode(new BigInteger(sha.digest()).toString(16), "UTF-8");
  371.  
  372. String preventProxy = ((BungeeCord.getInstance().config.isPreventProxyConnections()) ? "&ip=" + URLEncoder.encode(getAddress().getAddress().getHostAddress(), "UTF-8") : "");
  373. String authURL = "https://sessionserver.mojang.com/session/minecraft/hasJoined?username=" + encName + "&serverId=" + encodedHash + preventProxy;
  374.  
  375. Callback<String> handler = new Callback<String>() {
  376. @Override
  377. public void done(String result, Throwable error) {
  378. if (error == null) {
  379. LoginResult obj = BungeeCord.getInstance().gson.fromJson(result, LoginResult.class);
  380. if (obj != null && obj.getId() != null) {
  381. loginProfile = obj;
  382. name = obj.getName();
  383. uniqueId = Util.getUUID(obj.getId());
  384. finish();
  385. return;
  386. }
  387. disconnect(bungee.getTranslation("offline_mode_player"));
  388. } else {
  389. disconnect(bungee.getTranslation("mojang_fail"));
  390. bungee.getLogger().log(Level.SEVERE, "Error authenticating " + getName() + " with minecraft.net", error);
  391. }
  392. }
  393. };
  394.  
  395. HttpClient.get(authURL, ch.getHandle().eventLoop(), handler);
  396. }
  397.  
  398. private void finish() {
  399. if (isOnlineMode()) {
  400. // Check for multiple connections
  401. // We have to check for the old name first
  402. ProxiedPlayer oldName = bungee.getPlayer(getName());
  403. if (oldName != null) {
  404. // TODO See #1218
  405. oldName.disconnect(bungee.getTranslation("already_connected_proxy"));
  406. }
  407. // And then also for their old UUID
  408. ProxiedPlayer oldID = bungee.getPlayer(getUniqueId());
  409. if (oldID != null) {
  410. // TODO See #1218
  411. oldID.disconnect(bungee.getTranslation("already_connected_proxy"));
  412. }
  413. } else {
  414. // In offline mode the existing user stays and we kick the new one
  415. ProxiedPlayer oldName = bungee.getPlayer(getName());
  416. if (oldName != null) {
  417. // TODO See #1218
  418. disconnect(bungee.getTranslation("already_connected_proxy"));
  419. return;
  420. }
  421.  
  422. }
  423.  
  424. offlineId = java.util.UUID.nameUUIDFromBytes(("OfflinePlayer:" + getName()).getBytes(Charsets.UTF_8));
  425. if (uniqueId == null) {
  426. uniqueId = offlineId;
  427. }
  428.  
  429. Callback<LoginEvent> complete = new Callback<LoginEvent>() {
  430. @Override
  431. public void done(LoginEvent result, Throwable error) {
  432. if (result.isCancelled()) {
  433. disconnect(result.getCancelReasonComponents());
  434. return;
  435. }
  436. if (ch.isClosed()) {
  437. return;
  438. }
  439.  
  440. ch.getHandle().eventLoop().execute(new Runnable() {
  441. @Override
  442. public void run() {
  443. if (!ch.isClosing()) {
  444. UserConnection userCon = new UserConnection(bungee, ch, getName(), InitialHandler.this);
  445. userCon.setCompressionThreshold(BungeeCord.getInstance().config.getCompressionThreshold());
  446. userCon.init();
  447.  
  448. unsafe.sendPacket(new LoginSuccess(getUniqueId().toString(), getName())); // With dashes in between
  449. ch.setProtocol(Protocol.GAME);
  450.  
  451. ch.getHandle().pipeline().get(HandlerBoss.class).setHandler(new UpstreamBridge(bungee, userCon));
  452. bungee.getPluginManager().callEvent(new PostLoginEvent(userCon));
  453. ServerInfo server;
  454. if (bungee.getReconnectHandler() != null) {
  455. server = bungee.getReconnectHandler().getServer(userCon);
  456. } else {
  457. server = AbstractReconnectHandler.getForcedHost(InitialHandler.this);
  458. }
  459. if (server == null) {
  460. server = bungee.getServerInfo(listener.getDefaultServer());
  461. }
  462.  
  463. userCon.connect(server, null, true);
  464.  
  465. thisState = State.FINISHED;
  466. }
  467. }
  468. });
  469. }
  470. };
  471.  
  472. // fire login event
  473. bungee.getPluginManager().callEvent(new LoginEvent(InitialHandler.this, complete));
  474. }
  475.  
  476. @Override
  477. public void disconnect(String reason) {
  478. disconnect(TextComponent.fromLegacyText(reason));
  479. }
  480.  
  481. @Override
  482. public void disconnect(final BaseComponent... reason) {
  483. if (thisState != State.STATUS && thisState != State.PING && thisState != State.HANDSHAKE) // Waterfall: Don't kick during handshake
  484. {
  485. ch.delayedClose(new Kick(ComponentSerializer.toString(reason)));
  486. } else {
  487. ch.close();
  488. }
  489. }
  490.  
  491. @Override
  492. public void disconnect(BaseComponent reason) {
  493. disconnect(new BaseComponent[]
  494. {
  495. reason
  496. });
  497. }
  498.  
  499. @Override
  500. <<<<<<< HEAD
  501. public String getName()
  502. {
  503. return ( name != null ) ? name : ( loginRequest == null ) ? null : loginRequest.getData();
  504. =======
  505. public String getName() {
  506. return (name != null) ? name : (loginRequest == null) ? null : loginRequest.getData();
  507. >>>>>>> API de definir skin
  508. }
  509.  
  510. @Override
  511. public int getVersion() {
  512. return (handshake == null) ? -1 : handshake.getProtocolVersion();
  513. }
  514.  
  515. @Override
  516. }
  517.  
  518. @Override
  519. public Unsafe unsafe() {
  520. return unsafe;
  521. }
  522.  
  523. @Override
  524. public void setSkin(String value, String signature) {
  525. LoginResult.Property textures = new LoginResult.Property("textures", value, signature);
  526.  
  527. if (this.loginProfile == null) {
  528. this.loginProfile = new LoginResult(this.uniqueId.toString(), this.name, new LoginResult.Property[]{textures});
  529. } else {
  530. LoginResult.Property[] present = this.loginProfile.getProperties();
  531. LoginResult.Property[] newprops = new LoginResult.Property[present.length + 1];
  532. System.arraycopy(present, 0, newprops, 0, present.length);
  533. newprops[present.length] = textures;
  534. this.loginProfile.setProperties(newprops);
  535. }
  536. }
  537.  
  538. @Override
  539. public void setOnlineMode(boolean onlineMode) {
  540. Preconditions.checkState(thisState == State.USERNAME, "Can only set online mode status whilst state is username");
  541. this.onlineMode = onlineMode;
  542. }
  543.  
  544. @Override
  545. public void setUniqueId(UUID uuid) {
  546. Preconditions.checkState(thisState == State.USERNAME, "Can only set uuid while state is username");
  547. Preconditions.checkState(!onlineMode, "Can only set uuid when online mode is false");
  548. this.uniqueId = uuid;
  549. }
  550.  
  551. @Override
  552. public String getUUID() {
  553. return io.github.waterfallmc.waterfall.utils.UUIDUtils.undash(uniqueId.toString()); // Waterfall
  554. }
  555.  
  556. @Override
  557. public String toString() {
  558. return "[" + getAddress() + (getName() != null ? "|" + getName() : "") + "] <-> InitialHandler";
  559. }
  560.  
  561. @Override
  562. public boolean isConnected() {
  563. return !ch.isClosed();
  564. }
  565. }
  566. <<<<<<< HEAD
  567. public InetSocketAddress getAddress()
  568. {
  569. return ch.getRemoteAddress();
  570. =======
  571. public InetSocketAddress getAddress() {
  572. return (InetSocketAddress) ch.getHandle().remoteAddress();
  573. >>>>>>> API de definir skin
Advertisement
Add Comment
Please, Sign In to add comment