From 9289825d87909c671a0ec4f7d9598cfd4184252c Mon Sep 17 00:00:00 2001 From: Dan Mulloy Date: Wed, 1 Mar 2017 17:02:46 -0500 Subject: [PATCH] Add packet logging for debugging --- .../com/comphenix/protocol/PacketType.java | 4 +- .../protocol/injector/netty/WirePacket.java | 16 +- .../com/comphenix/protocol/PacketLogging.java | 232 ++++++++++++++++++ .../com/comphenix/protocol/ProtocolLib.java | 8 +- .../ProtocolLib/src/main/resources/plugin.yml | 5 + 5 files changed, 259 insertions(+), 6 deletions(-) create mode 100644 modules/ProtocolLib/src/main/java/com/comphenix/protocol/PacketLogging.java diff --git a/modules/API/src/main/java/com/comphenix/protocol/PacketType.java b/modules/API/src/main/java/com/comphenix/protocol/PacketType.java index 555aa26f..0ce84d0f 100644 --- a/modules/API/src/main/java/com/comphenix/protocol/PacketType.java +++ b/modules/API/src/main/java/com/comphenix/protocol/PacketType.java @@ -715,11 +715,11 @@ public class PacketType implements Serializable, Cloneable, Comparable sendingTypes = new ArrayList<>(); + private List receivingTypes = new ArrayList<>(); + + private ListeningWhitelist sendingWhitelist; + private ListeningWhitelist receivingWhitelist; + + private Logger fileLogger; + private LogLocation location = LogLocation.FILE; + + private final ProtocolManager manager; + private final Plugin plugin; + + PacketLogging(Plugin plugin, ProtocolManager manager) { + this.plugin = plugin; + this.manager = manager; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + PacketType type = null; + + if (args.length > 2) { + Protocol protocol; + + try { + protocol = Protocol.valueOf(args[0].toUpperCase()); + } catch (IllegalArgumentException ex) { + sender.sendMessage(ChatColor.RED + "Unknown protocol " + args[0]); + return true; + } + + Sender pSender; + + try { + pSender = Sender.valueOf(args[1].toUpperCase()); + } catch (IllegalArgumentException ex) { + sender.sendMessage(ChatColor.RED + "Unknown sender: " + args[1]); + return true; + } + + try { + try { + int id = Integer.parseInt(args[2]); + type = PacketType.findCurrent(protocol, pSender, id); + } catch (NumberFormatException ex) { + type = PacketType.findCurrent(protocol, pSender, args[2]); + } + } catch (IllegalArgumentException ex) { + sender.sendMessage(ChatColor.RED + "Unknown packet: " + PacketType.format(protocol, pSender, args[2])); + return true; + } + + if (args.length > 3) { + if (args[3].equalsIgnoreCase("console")) { + this.location = LogLocation.CONSOLE; + } else { + this.location = LogLocation.FILE; + } + } + + if (pSender == Sender.CLIENT) { + if (receivingTypes.contains(type)) { + receivingTypes.remove(type); + } else { + receivingTypes.add(type); + } + } else { + if (sendingTypes.contains(type)) { + sendingTypes.remove(type); + } else { + sendingTypes.add(type); + } + } + + startLogging(); + sender.sendMessage(ChatColor.GREEN + "Now logging " + type.getPacketClass().getSimpleName()); + return true; + } + + sender.sendMessage(ChatColor.RED + "Invalid syntax: /packetlog [location]"); + return true; + } + + private void startLogging() { + manager.removePacketListener(this); + + if (sendingTypes.isEmpty() && receivingTypes.isEmpty()) { + return; + } + + this.sendingWhitelist = ListeningWhitelist.newBuilder().types(sendingTypes).build(); + this.receivingWhitelist = ListeningWhitelist.newBuilder().types(receivingTypes).build(); + + if (location == LogLocation.FILE && fileLogger == null) { + fileLogger = Logger.getLogger("ProtocolLib-FileLogging"); + + for (Handler handler : fileLogger.getHandlers()) + fileLogger.removeHandler(handler); + fileLogger.setUseParentHandlers(false); + + try { + File logFile = new File(plugin.getDataFolder(), "log.log"); + FileHandler handler = new FileHandler(logFile.getAbsolutePath(), true); + handler.setFormatter(new LogFormatter()); + fileLogger.addHandler(handler); + } catch (IOException ex) { + plugin.getLogger().log(Level.SEVERE, "Failed to obtain log file:", ex); + return; + } + } + + manager.addPacketListener(this); + } + + @Override + public void onPacketSending(PacketEvent event) { + log(event); + } + + @Override + public void onPacketReceiving(PacketEvent event) { + log(event); + } + + private void log(PacketEvent event) { + ByteBuf buffer = WirePacket.bufferFromPacket(event.getPacket()); + String hexDump = ByteBufUtil.hexDump(buffer); + + if (location == LogLocation.FILE) { + fileLogger.log(Level.INFO, event.getPacketType() + ":"); + fileLogger.log(Level.INFO, hexDump); + fileLogger.log(Level.INFO, ""); + } else { + System.out.println(event.getPacketType() + ":"); + System.out.println(hexDump); + System.out.println(); + } + } + + @Override + public ListeningWhitelist getSendingWhitelist() { + return sendingWhitelist; + } + + @Override + public ListeningWhitelist getReceivingWhitelist() { + return receivingWhitelist; + } + + @Override + public Plugin getPlugin() { + return plugin; + } + + private static enum LogLocation { + CONSOLE, + FILE; + } + + private static class LogFormatter extends Formatter { + private static final SimpleDateFormat DATE = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + private static final String FORMAT = "[{0}] {1}"; + + @Override + public String format(LogRecord record) { + String string = formatMessage(record); + if (string.isEmpty()) { + return LINE_SEPARATOR; + } + + StringBuilder message = new StringBuilder(); + message.append(MessageFormat.format(FORMAT, DATE.format(record.getMillis()), string)); + message.append(LINE_SEPARATOR); + return message.toString(); + } + } +} \ No newline at end of file diff --git a/modules/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLib.java b/modules/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLib.java index 412736e7..b40ced09 100644 --- a/modules/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLib.java +++ b/modules/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLib.java @@ -90,7 +90,8 @@ public class ProtocolLib extends JavaPlugin { private enum ProtocolCommand { FILTER, PACKET, - PROTOCOL + PROTOCOL, + LOGGING; } /** @@ -143,6 +144,7 @@ public class ProtocolLib extends JavaPlugin { private CommandProtocol commandProtocol; private CommandPacket commandPacket; private CommandFilter commandFilter; + private PacketLogging packetLogging; // Whether or not disable is not needed private boolean skipDisable; @@ -269,6 +271,9 @@ public class ProtocolLib extends JavaPlugin { case PACKET: commandPacket = new CommandPacket(reporter, this, logger, commandFilter, protocolManager); break; + case LOGGING: + packetLogging = new PacketLogging(this, protocolManager); + break; } } catch (OutOfMemoryError e) { throw e; @@ -404,6 +409,7 @@ public class ProtocolLib extends JavaPlugin { registerCommand(CommandProtocol.NAME, commandProtocol); registerCommand(CommandPacket.NAME, commandPacket); registerCommand(CommandFilter.NAME, commandFilter); + registerCommand(PacketLogging.NAME, packetLogging); // Player login and logout events protocolManager.registerEvents(manager, this); diff --git a/modules/ProtocolLib/src/main/resources/plugin.yml b/modules/ProtocolLib/src/main/resources/plugin.yml index 2f3bc9aa..0f3d14fa 100644 --- a/modules/ProtocolLib/src/main/resources/plugin.yml +++ b/modules/ProtocolLib/src/main/resources/plugin.yml @@ -24,6 +24,11 @@ commands: aliases: [packet_filter] permission: protocol.admin permission-message: You don't have + packetlog: + description: Logs hex representations of packets to a file or console + usage: / [location] + permission: protocol.admin + permission-message: You don't have permissions: protocol.*: