1
+ /**
2
+ * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
3
+ * Copyright (C) 2017 Dan Mulloy
4
+ *
5
+ * This program is free software; you can redistribute it and/or modify it under the terms of the
6
+ * GNU General Public License as published by the Free Software Foundation; either version 2 of
7
+ * the License, or (at your option) any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
10
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
+ * See the GNU General Public License for more details.
12
+ *
13
+ * You should have received a copy of the GNU General Public License along with this program;
14
+ * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
15
+ * 02111-1307 USA
16
+ */
17
+ package com .comphenix .protocol ;
18
+
19
+ import java .io .File ;
20
+ import java .io .IOException ;
21
+ import java .text .MessageFormat ;
22
+ import java .text .SimpleDateFormat ;
23
+ import java .util .ArrayList ;
24
+ import java .util .List ;
25
+ import java .util .logging .FileHandler ;
26
+ import java .util .logging .Formatter ;
27
+ import java .util .logging .Handler ;
28
+ import java .util .logging .Level ;
29
+ import java .util .logging .LogRecord ;
30
+ import java .util .logging .Logger ;
31
+
32
+ import org .bukkit .ChatColor ;
33
+ import org .bukkit .command .Command ;
34
+ import org .bukkit .command .CommandExecutor ;
35
+ import org .bukkit .command .CommandSender ;
36
+ import org .bukkit .plugin .Plugin ;
37
+
38
+ import com .comphenix .protocol .PacketType .Protocol ;
39
+ import com .comphenix .protocol .PacketType .Sender ;
40
+ import com .comphenix .protocol .events .ListeningWhitelist ;
41
+ import com .comphenix .protocol .events .PacketEvent ;
42
+ import com .comphenix .protocol .events .PacketListener ;
43
+ import com .comphenix .protocol .injector .netty .WirePacket ;
44
+
45
+ import io .netty .buffer .ByteBuf ;
46
+ import io .netty .buffer .ByteBufUtil ;
47
+
48
+ /**
49
+ * Logs packets to a given stream
50
+ * @author dmulloy2
51
+ */
52
+ public class PacketLogging implements CommandExecutor , PacketListener {
53
+ public static final String NAME = "packetlog" ;
54
+
55
+ private List <PacketType > sendingTypes = new ArrayList <>();
56
+ private List <PacketType > receivingTypes = new ArrayList <>();
57
+
58
+ private ListeningWhitelist sendingWhitelist ;
59
+ private ListeningWhitelist receivingWhitelist ;
60
+
61
+ private Logger fileLogger ;
62
+ private LogLocation location = LogLocation .FILE ;
63
+
64
+ private final ProtocolManager manager ;
65
+ private final Plugin plugin ;
66
+
67
+ PacketLogging (Plugin plugin , ProtocolManager manager ) {
68
+ this .plugin = plugin ;
69
+ this .manager = manager ;
70
+ }
71
+
72
+ @ Override
73
+ public boolean onCommand (CommandSender sender , Command command , String label , String [] args ) {
74
+ PacketType type = null ;
75
+
76
+ if (args .length > 2 ) {
77
+ Protocol protocol ;
78
+
79
+ try {
80
+ protocol = Protocol .valueOf (args [0 ].toUpperCase ());
81
+ } catch (IllegalArgumentException ex ) {
82
+ sender .sendMessage (ChatColor .RED + "Unknown protocol " + args [0 ]);
83
+ return true ;
84
+ }
85
+
86
+ Sender pSender ;
87
+
88
+ try {
89
+ pSender = Sender .valueOf (args [1 ].toUpperCase ());
90
+ } catch (IllegalArgumentException ex ) {
91
+ sender .sendMessage (ChatColor .RED + "Unknown sender: " + args [1 ]);
92
+ return true ;
93
+ }
94
+
95
+ try {
96
+ try {
97
+ int id = Integer .parseInt (args [2 ]);
98
+ type = PacketType .findCurrent (protocol , pSender , id );
99
+ } catch (NumberFormatException ex ) {
100
+ type = PacketType .findCurrent (protocol , pSender , args [2 ]);
101
+ }
102
+ } catch (IllegalArgumentException ex ) {
103
+ sender .sendMessage (ChatColor .RED + "Unknown packet: " + PacketType .format (protocol , pSender , args [2 ]));
104
+ return true ;
105
+ }
106
+
107
+ if (args .length > 3 ) {
108
+ if (args [3 ].equalsIgnoreCase ("console" )) {
109
+ this .location = LogLocation .CONSOLE ;
110
+ } else {
111
+ this .location = LogLocation .FILE ;
112
+ }
113
+ }
114
+
115
+ if (pSender == Sender .CLIENT ) {
116
+ if (receivingTypes .contains (type )) {
117
+ receivingTypes .remove (type );
118
+ } else {
119
+ receivingTypes .add (type );
120
+ }
121
+ } else {
122
+ if (sendingTypes .contains (type )) {
123
+ sendingTypes .remove (type );
124
+ } else {
125
+ sendingTypes .add (type );
126
+ }
127
+ }
128
+
129
+ startLogging ();
130
+ sender .sendMessage (ChatColor .GREEN + "Now logging " + type .getPacketClass ().getSimpleName ());
131
+ return true ;
132
+ }
133
+
134
+ sender .sendMessage (ChatColor .RED + "Invalid syntax: /packetlog <protocol> <sender> <packet> [location]" );
135
+ return true ;
136
+ }
137
+
138
+ private void startLogging () {
139
+ manager .removePacketListener (this );
140
+
141
+ if (sendingTypes .isEmpty () && receivingTypes .isEmpty ()) {
142
+ return ;
143
+ }
144
+
145
+ this .sendingWhitelist = ListeningWhitelist .newBuilder ().types (sendingTypes ).build ();
146
+ this .receivingWhitelist = ListeningWhitelist .newBuilder ().types (receivingTypes ).build ();
147
+
148
+ if (location == LogLocation .FILE && fileLogger == null ) {
149
+ fileLogger = Logger .getLogger ("ProtocolLib-FileLogging" );
150
+
151
+ for (Handler handler : fileLogger .getHandlers ())
152
+ fileLogger .removeHandler (handler );
153
+ fileLogger .setUseParentHandlers (false );
154
+
155
+ try {
156
+ File logFile = new File (plugin .getDataFolder (), "log.log" );
157
+ FileHandler handler = new FileHandler (logFile .getAbsolutePath (), true );
158
+ handler .setFormatter (new LogFormatter ());
159
+ fileLogger .addHandler (handler );
160
+ } catch (IOException ex ) {
161
+ plugin .getLogger ().log (Level .SEVERE , "Failed to obtain log file:" , ex );
162
+ return ;
163
+ }
164
+ }
165
+
166
+ manager .addPacketListener (this );
167
+ }
168
+
169
+ @ Override
170
+ public void onPacketSending (PacketEvent event ) {
171
+ log (event );
172
+ }
173
+
174
+ @ Override
175
+ public void onPacketReceiving (PacketEvent event ) {
176
+ log (event );
177
+ }
178
+
179
+ private void log (PacketEvent event ) {
180
+ ByteBuf buffer = WirePacket .bufferFromPacket (event .getPacket ());
181
+ String hexDump = ByteBufUtil .hexDump (buffer );
182
+
183
+ if (location == LogLocation .FILE ) {
184
+ fileLogger .log (Level .INFO , event .getPacketType () + ":" );
185
+ fileLogger .log (Level .INFO , hexDump );
186
+ fileLogger .log (Level .INFO , "" );
187
+ } else {
188
+ System .out .println (event .getPacketType () + ":" );
189
+ System .out .println (hexDump );
190
+ System .out .println ();
191
+ }
192
+ }
193
+
194
+ @ Override
195
+ public ListeningWhitelist getSendingWhitelist () {
196
+ return sendingWhitelist ;
197
+ }
198
+
199
+ @ Override
200
+ public ListeningWhitelist getReceivingWhitelist () {
201
+ return receivingWhitelist ;
202
+ }
203
+
204
+ @ Override
205
+ public Plugin getPlugin () {
206
+ return plugin ;
207
+ }
208
+
209
+ private static enum LogLocation {
210
+ CONSOLE ,
211
+ FILE ;
212
+ }
213
+
214
+ private static class LogFormatter extends Formatter {
215
+ private static final SimpleDateFormat DATE = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss" );
216
+ private static final String LINE_SEPARATOR = System .getProperty ("line.separator" );
217
+ private static final String FORMAT = "[{0}] {1}" ;
218
+
219
+ @ Override
220
+ public String format (LogRecord record ) {
221
+ String string = formatMessage (record );
222
+ if (string .isEmpty ()) {
223
+ return LINE_SEPARATOR ;
224
+ }
225
+
226
+ StringBuilder message = new StringBuilder ();
227
+ message .append (MessageFormat .format (FORMAT , DATE .format (record .getMillis ()), string ));
228
+ message .append (LINE_SEPARATOR );
229
+ return message .toString ();
230
+ }
231
+ }
232
+ }
0 commit comments