/src/wireshark/epan/dissectors/packet-irc.c
Line | Count | Source |
1 | | /* packet-irc.c |
2 | | * |
3 | | * Wireshark - Network traffic analyzer |
4 | | * By Gerald Combs <gerald@wireshark.org> |
5 | | * Copyright 1998 Gerald Combs |
6 | | * |
7 | | * Copied from packet-tftp.c |
8 | | * |
9 | | * SPDX-License-Identifier: GPL-2.0-or-later |
10 | | */ |
11 | | |
12 | | /* |
13 | | * Routines for IRC packet dissection |
14 | | * |
15 | | * See |
16 | | * |
17 | | * http://www.irchelp.org/irchelp/rfc/ |
18 | | * |
19 | | * and the RFCs and other documents it mentions, such as RFC 1459, RFCs |
20 | | * 2810, 2811, 2812, and 2813, |
21 | | * |
22 | | * For CTCP, see : |
23 | | * http://www.irchelp.org/irchelp/rfc/ctcpspec.html |
24 | | * http://web.archive.org/web/20031203073050/http://www.invlogic.com/irc/ctcp.html |
25 | | * https://www.ietf.org/archive/id/draft-oakley-irc-ctcp-02.txt |
26 | | * |
27 | | * TODO: IRC - handle final CRLF |
28 | | * CTCP - multiple CTCP commands |
29 | | */ |
30 | | |
31 | | #include "config.h" |
32 | | |
33 | | #include <epan/packet.h> |
34 | | #include <epan/expert.h> |
35 | | #include <wsutil/array.h> |
36 | | |
37 | | void proto_register_irc(void); |
38 | | void proto_reg_handoff_irc(void); |
39 | | |
40 | | static int proto_irc; |
41 | | static int hf_irc_request; |
42 | | static int hf_irc_request_prefix; |
43 | | static int hf_irc_request_command; |
44 | | static int hf_irc_request_command_param; |
45 | | static int hf_irc_request_trailer; |
46 | | static int hf_irc_response; |
47 | | static int hf_irc_response_prefix; |
48 | | static int hf_irc_response_command; |
49 | | static int hf_irc_response_num_command; |
50 | | static int hf_irc_response_command_param; |
51 | | static int hf_irc_response_trailer; |
52 | | static int proto_irc_ctcp; |
53 | | static int hf_irc_ctcp_command; |
54 | | static int hf_irc_ctcp_params; |
55 | | static int hf_irc_ctcp_delimiter_odd; |
56 | | static int hf_irc_ctcp_delimiter_even; |
57 | | |
58 | | static int ett_irc; |
59 | | static int ett_irc_request; |
60 | | static int ett_irc_request_command; |
61 | | static int ett_irc_response; |
62 | | static int ett_irc_response_command; |
63 | | static int ett_irc_ctcp; |
64 | | |
65 | | static expert_field ei_irc_missing_end_delimiter; |
66 | | static expert_field ei_irc_numeric_request_command; |
67 | | static expert_field ei_irc_response_command; |
68 | | static expert_field ei_irc_prefix_missing_ending_space; |
69 | | static expert_field ei_irc_request_command; |
70 | | static expert_field ei_irc_tag_data_invalid; |
71 | | |
72 | | /* This must be a null-terminated string */ |
73 | | static const char TAG_DELIMITER[] = "\x01"; |
74 | | /* patterns used for tvb_ws_mempbrk_pattern_uint8 */ |
75 | | static ws_mempbrk_pattern pbrk_tag_delimiter; |
76 | | |
77 | | static dissector_table_t irc_ctcp_table; |
78 | | |
79 | 16 | #define TCP_PORT_RANGE "6667,57000" /* Not IANA registered */ |
80 | | |
81 | 772 | #define IRCCTCP_DEF 0x01 /* Only known value */ |
82 | | |
83 | | static int |
84 | | dissect_irc_ctcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) |
85 | 756 | { |
86 | 756 | proto_tree *ctcp_tree; |
87 | 756 | proto_item *ti; |
88 | 756 | const uint8_t *str_command, *str_params; |
89 | 756 | unsigned space_offset = -1; |
90 | 756 | dissector_handle_t ctcp_handle; |
91 | | |
92 | 756 | ctcp_handle = dissector_get_uint_handle(irc_ctcp_table, IRCCTCP_DEF); |
93 | 756 | if (ctcp_handle == NULL) { |
94 | 0 | return tvb_captured_length(tvb); |
95 | 0 | } |
96 | | |
97 | 756 | ti = proto_tree_add_item(tree, proto_irc_ctcp, tvb, 0, -1, ENC_NA); |
98 | 756 | ctcp_tree = proto_item_add_subtree(ti, ett_irc); |
99 | | |
100 | | /* We know for sure that the tvb data starts and ends with ^A (TAG_DELIMITER), |
101 | | * so we aren't checking this again here. |
102 | | */ |
103 | 756 | proto_tree_add_item(ctcp_tree, hf_irc_ctcp_delimiter_odd, tvb, 0, 1, ENC_ASCII); |
104 | | |
105 | 756 | if (!tvb_find_uint8_remaining(tvb, 1, ' ', &space_offset)) { |
106 | 666 | proto_tree_add_item_ret_string(ctcp_tree, hf_irc_ctcp_command, tvb, 1, tvb_reported_length(tvb)-2, ENC_ASCII|ENC_NA, pinfo->pool, &str_command); |
107 | 666 | } |
108 | 90 | else { |
109 | 90 | proto_tree_add_item_ret_string(ctcp_tree, hf_irc_ctcp_command, tvb, 0, space_offset, ENC_ASCII|ENC_NA, pinfo->pool, &str_command); |
110 | 90 | proto_tree_add_item_ret_string(ctcp_tree, hf_irc_ctcp_params, tvb, space_offset+1, tvb_reported_length(tvb)-space_offset-1, ENC_ASCII|ENC_NA, pinfo->pool, &str_params); |
111 | 90 | } |
112 | | |
113 | 756 | proto_tree_add_item(ctcp_tree, hf_irc_ctcp_delimiter_even, tvb, tvb_reported_length(tvb)-1, 1, ENC_ASCII); |
114 | | |
115 | 756 | return tvb_captured_length(tvb); |
116 | 756 | } |
117 | | |
118 | | static void |
119 | | dissect_irc_tag_data(proto_tree *tree, proto_item *item, tvbuff_t *tvb, unsigned offset, unsigned datalen, packet_info *pinfo, const char* command) |
120 | 760 | { |
121 | 760 | unsigned char found_start_needle = 0, |
122 | 760 | found_end_needle = 0; |
123 | 760 | unsigned tag_start_offset, tag_end_offset; |
124 | 760 | tvbuff_t *next_tvb; |
125 | | |
126 | 760 | if (!tvb_ws_mempbrk_uint8_length(tvb, offset, datalen, &pbrk_tag_delimiter, &tag_start_offset, &found_start_needle)) |
127 | 1 | { |
128 | | /* no tag data */ |
129 | 1 | return; |
130 | 1 | } |
131 | | |
132 | 759 | if (!tvb_ws_mempbrk_uint8_length(tvb, tag_start_offset + 1, datalen, &pbrk_tag_delimiter, &tag_end_offset, &found_end_needle)) |
133 | 3 | { |
134 | 3 | expert_add_info(pinfo, item, &ei_irc_missing_end_delimiter); |
135 | 3 | return; |
136 | 3 | } |
137 | | |
138 | 756 | if ((strcmp(command, "NOTICE") != 0) && |
139 | 756 | (strcmp(command, "PRIVMSG") != 0)) |
140 | 756 | { |
141 | 756 | expert_add_info(pinfo, item, &ei_irc_tag_data_invalid); |
142 | 756 | } |
143 | | |
144 | | /* Placeholder to call CTCP dissector, strip out delimiter */ |
145 | 756 | if(tree) { |
146 | 756 | next_tvb = tvb_new_subset_length(tvb, tag_start_offset, 1 + tag_end_offset - tag_start_offset); |
147 | 756 | dissect_irc_ctcp(next_tvb, pinfo, tree, NULL); |
148 | 756 | } |
149 | 756 | } |
150 | | |
151 | | static void |
152 | | dissect_irc_request(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, unsigned offset, unsigned linelen) |
153 | 77 | { |
154 | 77 | proto_tree *request_tree, *command_tree = NULL; |
155 | 77 | proto_item *request_item; |
156 | 77 | unsigned start_offset = offset; |
157 | 77 | unsigned end_offset = start_offset+linelen; |
158 | 77 | unsigned eop_offset = -1, |
159 | 77 | eoc_offset = -1, |
160 | 77 | eocp_offset, |
161 | 77 | tag_start_offset, tag_end_offset; |
162 | 77 | const char *str_command; |
163 | 77 | unsigned char found_tag_needle = 0; |
164 | 77 | bool first_command_param = true; |
165 | | |
166 | 77 | request_item = proto_tree_add_item(tree, hf_irc_request, tvb, offset, linelen, ENC_ASCII); |
167 | 77 | if (linelen <= 0) |
168 | 1 | return; |
169 | | |
170 | 76 | request_tree = proto_item_add_subtree(request_item, ett_irc_request ); |
171 | | |
172 | | /* Check if message has a prefix */ |
173 | 76 | if (tvb_get_uint8(tvb, offset) == ':') |
174 | 3 | { |
175 | | /* find the end of the prefix */ |
176 | 3 | if (!tvb_find_uint8_length(tvb, offset + 1, linelen - 1, ' ', &eop_offset)) |
177 | 1 | { |
178 | 1 | expert_add_info(pinfo, request_item, &ei_irc_prefix_missing_ending_space); |
179 | 1 | return; |
180 | 1 | } |
181 | | |
182 | 2 | proto_tree_add_item(request_tree, hf_irc_request_prefix, tvb, offset+1, eop_offset-offset-1, ENC_ASCII); |
183 | 2 | offset = eop_offset+1; |
184 | 2 | } |
185 | | |
186 | | /* clear out any whitespace before command */ |
187 | 107 | while(offset < end_offset && tvb_get_uint8(tvb, offset) == ' ') |
188 | 32 | { |
189 | 32 | offset++; |
190 | 32 | } |
191 | 75 | if (offset == end_offset) |
192 | 0 | { |
193 | 0 | expert_add_info(pinfo, request_item, &ei_irc_request_command); |
194 | 0 | return; |
195 | 0 | } |
196 | | |
197 | 75 | if (!tvb_find_uint8_length(tvb, offset, end_offset - offset, ' ', &eoc_offset)) |
198 | 8 | { |
199 | 8 | const uint8_t* col_str; |
200 | 8 | proto_tree_add_item_ret_string(request_tree, hf_irc_request_command, tvb, offset, end_offset-offset, ENC_ASCII|ENC_NA, pinfo->pool, &col_str); |
201 | 8 | col_append_fstr( pinfo->cinfo, COL_INFO, " (%s)", col_str); |
202 | | |
203 | | /* Warn if there is a "numeric" command */ |
204 | 8 | if ((end_offset-offset == 3) && |
205 | 2 | (g_ascii_isdigit(tvb_get_uint8(tvb, offset))) && |
206 | 0 | (g_ascii_isdigit(tvb_get_uint8(tvb, offset+1))) && |
207 | 0 | (g_ascii_isdigit(tvb_get_uint8(tvb, offset+2)))) |
208 | 0 | { |
209 | 0 | expert_add_info(pinfo, request_item, &ei_irc_numeric_request_command); |
210 | 0 | } |
211 | 8 | return; |
212 | 8 | } |
213 | | |
214 | 67 | proto_tree_add_item_ret_string(request_tree, hf_irc_request_command, tvb, offset, eoc_offset-offset, ENC_ASCII|ENC_NA, pinfo->pool, (const uint8_t**)&str_command); |
215 | 67 | col_append_fstr( pinfo->cinfo, COL_INFO, " (%s)", str_command); |
216 | | |
217 | | /* Warn if there is a "numeric" command */ |
218 | 67 | if ((eoc_offset-offset == 3) && |
219 | 6 | (g_ascii_isdigit(tvb_get_uint8(tvb, offset))) && |
220 | 1 | (g_ascii_isdigit(tvb_get_uint8(tvb, offset+1))) && |
221 | 0 | (g_ascii_isdigit(tvb_get_uint8(tvb, offset+2)))) |
222 | 0 | { |
223 | 0 | expert_add_info(pinfo, request_item, &ei_irc_numeric_request_command); |
224 | 0 | } |
225 | | |
226 | 67 | offset = eoc_offset+1; |
227 | | |
228 | | /* clear out any whitespace before command parameter */ |
229 | 130 | while(offset < end_offset && tvb_get_uint8(tvb, offset) == ' ') |
230 | 63 | { |
231 | 63 | offset++; |
232 | 63 | } |
233 | 67 | if (offset == end_offset) |
234 | 3 | { |
235 | | /* No command parameters */ |
236 | 3 | return; |
237 | 3 | } |
238 | | |
239 | | /* Check if message has a trailer */ |
240 | 64 | if (tvb_get_uint8(tvb, offset) == ':') |
241 | 1 | { |
242 | 1 | proto_tree_add_item(request_tree, hf_irc_request_trailer, tvb, offset+1, end_offset-offset-1, ENC_ASCII); |
243 | 1 | dissect_irc_tag_data(request_tree, request_item, tvb, offset+1, end_offset-offset-1, pinfo, str_command); |
244 | 1 | return; |
245 | 1 | } |
246 | | |
247 | 699 | while(offset < end_offset) |
248 | 698 | { |
249 | 698 | bool eocp_found, tag_start_found, tag_end_found; |
250 | 698 | eocp_found = tvb_find_uint8_length(tvb, offset, end_offset-offset, ' ', &eocp_offset); |
251 | 698 | tag_start_found = tvb_ws_mempbrk_uint8_length(tvb, offset, end_offset-offset, &pbrk_tag_delimiter, &tag_start_offset , &found_tag_needle); |
252 | | |
253 | | /* Create subtree when the first parameter is found */ |
254 | 698 | if (first_command_param) |
255 | 63 | { |
256 | 63 | command_tree = proto_tree_add_subtree(request_tree, tvb, offset, end_offset-offset, |
257 | 63 | ett_irc_request_command, NULL, "Command parameters"); |
258 | 63 | first_command_param = false; |
259 | 63 | } |
260 | | |
261 | 698 | if (((eocp_found == false) && (tag_start_found == false)) || |
262 | 666 | ((eocp_found == true) && (tag_start_found == false)) || |
263 | 590 | (eocp_offset < tag_start_offset)) |
264 | 366 | { |
265 | | /* regular message should be dissected */ |
266 | | |
267 | 366 | if (eocp_found == false) |
268 | 32 | { |
269 | 32 | proto_tree_add_item(command_tree, hf_irc_request_command_param, tvb, offset, end_offset-offset, ENC_ASCII); |
270 | 32 | return; |
271 | 32 | } |
272 | | |
273 | 334 | proto_tree_add_item(command_tree, hf_irc_request_command_param, tvb, offset, eocp_offset-offset, ENC_ASCII); |
274 | 334 | offset = eocp_offset+1; |
275 | | |
276 | | /* clear out any whitespace before next command parameter */ |
277 | 691 | while(offset < end_offset && tvb_get_uint8(tvb, offset) == ' ') |
278 | 357 | { |
279 | 357 | offset++; |
280 | 357 | } |
281 | 334 | if (offset == end_offset) |
282 | 5 | { |
283 | 5 | break; |
284 | 5 | } |
285 | | |
286 | | /* Check if message has a trailer */ |
287 | 329 | if (tvb_get_uint8(tvb, offset) == ':') |
288 | 2 | { |
289 | 2 | proto_tree_add_item(request_tree, hf_irc_request_trailer, tvb, offset+1, end_offset-offset-1, ENC_ASCII); |
290 | 2 | dissect_irc_tag_data(request_tree, request_item, tvb, offset+1, end_offset-offset-1, pinfo, str_command); |
291 | 2 | return; |
292 | 2 | } |
293 | 329 | } |
294 | 332 | else if (((eocp_found == false) && (tag_start_found == false)) || |
295 | 332 | (eocp_offset > tag_start_offset)) |
296 | 332 | { |
297 | | /* tag data dissected */ |
298 | | |
299 | 332 | found_tag_needle = 0; |
300 | 332 | tag_end_found = tvb_ws_mempbrk_uint8_length(tvb, tag_start_offset+1, end_offset-tag_start_offset-1, &pbrk_tag_delimiter, &tag_end_offset , &found_tag_needle); |
301 | 332 | if (tag_end_found == false) |
302 | 23 | { |
303 | 23 | expert_add_info(pinfo, request_item, &ei_irc_missing_end_delimiter); |
304 | 23 | return; |
305 | 23 | } |
306 | | |
307 | 309 | dissect_irc_tag_data(request_tree, request_item, tvb, tag_start_offset, tag_end_offset-tag_start_offset, pinfo, str_command); |
308 | 309 | offset = tag_end_offset+1; |
309 | 309 | } |
310 | 698 | } |
311 | 63 | } |
312 | | |
313 | | static void |
314 | | dissect_irc_response(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, unsigned offset, unsigned linelen) |
315 | 91 | { |
316 | 91 | proto_tree *response_tree, *command_tree = NULL; |
317 | 91 | proto_item *response_item, *hidden_item; |
318 | 91 | unsigned start_offset = offset; |
319 | 91 | unsigned end_offset = start_offset+linelen; |
320 | 91 | unsigned eop_offset = -1, |
321 | 91 | eoc_offset = -1, |
322 | 91 | eocp_offset, |
323 | 91 | tag_start_offset, tag_end_offset; |
324 | 91 | const char* str_command; |
325 | 91 | uint16_t num_command; |
326 | 91 | unsigned char found_tag_needle = 0; |
327 | 91 | bool first_command_param = true; |
328 | | |
329 | 91 | response_item = proto_tree_add_item(tree, hf_irc_response, tvb, offset, linelen, ENC_ASCII); |
330 | 91 | if (linelen <= 0) |
331 | 1 | return; |
332 | | |
333 | 90 | response_tree = proto_item_add_subtree(response_item, ett_irc_response ); |
334 | | |
335 | | /* Check if message has a prefix */ |
336 | 90 | if (tvb_get_uint8(tvb, offset) == ':') |
337 | 4 | { |
338 | | /* find the end of the prefix */ |
339 | 4 | if (!tvb_find_uint8_length(tvb, offset + 1, linelen - 1, ' ', &eop_offset)) |
340 | 2 | { |
341 | 2 | expert_add_info(pinfo, response_item, &ei_irc_prefix_missing_ending_space); |
342 | 2 | return; |
343 | 2 | } |
344 | | |
345 | 2 | proto_tree_add_item(response_tree, hf_irc_response_prefix, tvb, offset+1, eop_offset-offset-1, ENC_ASCII); |
346 | 2 | offset = eop_offset+1; |
347 | 2 | } |
348 | | |
349 | | /* clear out any whitespace before command */ |
350 | 148 | while(offset < end_offset && tvb_get_uint8(tvb, offset) == ' ') |
351 | 60 | { |
352 | 60 | offset++; |
353 | 60 | } |
354 | 88 | if (offset == end_offset) |
355 | 1 | { |
356 | 1 | expert_add_info(pinfo, response_item, &ei_irc_response_command); |
357 | 1 | return; |
358 | 1 | } |
359 | | |
360 | 87 | if (!tvb_find_uint8_length(tvb, offset, end_offset - offset, ' ', &eoc_offset)) |
361 | 7 | { |
362 | 7 | const uint8_t* col_str; |
363 | 7 | proto_tree_add_item_ret_string(response_tree, hf_irc_response_command, tvb, offset, end_offset-offset, ENC_ASCII|ENC_NA, pinfo->pool, &col_str); |
364 | 7 | col_append_fstr( pinfo->cinfo, COL_INFO, " (%s)", col_str); |
365 | | |
366 | | /* if response command is numeric, allow it to be filtered as an integer */ |
367 | 7 | if ((end_offset-offset == 3) && |
368 | 2 | (g_ascii_isdigit(tvb_get_uint8(tvb, offset))) && |
369 | 0 | (g_ascii_isdigit(tvb_get_uint8(tvb, offset+1))) && |
370 | 0 | (g_ascii_isdigit(tvb_get_uint8(tvb, offset+2)))) |
371 | 0 | { |
372 | 0 | num_command = ((tvb_get_uint8(tvb, offset)-0x30)*100) + ((tvb_get_uint8(tvb, offset+1)-0x30)*10) + (tvb_get_uint8(tvb, offset+2)-0x30); |
373 | 0 | hidden_item = proto_tree_add_uint(response_tree, hf_irc_response_num_command, tvb, offset, end_offset-offset, num_command); |
374 | 0 | proto_item_set_hidden(hidden_item); |
375 | 0 | } |
376 | 7 | return; |
377 | 7 | } |
378 | | |
379 | 80 | proto_tree_add_item_ret_string(response_tree, hf_irc_response_command, tvb, offset, eoc_offset-offset, ENC_ASCII|ENC_NA, pinfo->pool, (const uint8_t**)&str_command); |
380 | 80 | col_append_fstr( pinfo->cinfo, COL_INFO, " (%s)", str_command); |
381 | | |
382 | | /* if response command is numeric, allow it to be filtered as an integer */ |
383 | 80 | if ((eoc_offset-offset == 3) && |
384 | 17 | (g_ascii_isdigit(tvb_get_uint8(tvb, offset))) && |
385 | 4 | (g_ascii_isdigit(tvb_get_uint8(tvb, offset+1))) && |
386 | 3 | (g_ascii_isdigit(tvb_get_uint8(tvb, offset+2)))) |
387 | 3 | { |
388 | 3 | num_command = ((tvb_get_uint8(tvb, offset)-0x30)*100) + ((tvb_get_uint8(tvb, offset+1)-0x30)*10) + (tvb_get_uint8(tvb, offset+2)-0x30); |
389 | 3 | hidden_item = proto_tree_add_uint(response_tree, hf_irc_response_num_command, tvb, offset, eoc_offset-offset, num_command); |
390 | 3 | proto_item_set_hidden(hidden_item); |
391 | 3 | } |
392 | | |
393 | 80 | offset = eoc_offset+1; |
394 | | |
395 | | /* clear out any whitespace before command parameter */ |
396 | 242 | while(offset < end_offset && tvb_get_uint8(tvb, offset) == ' ') |
397 | 162 | { |
398 | 162 | offset++; |
399 | 162 | } |
400 | 80 | if (offset == end_offset) |
401 | 2 | { |
402 | | /* No command parameters */ |
403 | 2 | return; |
404 | 2 | } |
405 | | |
406 | | /* Check if message has a trailer */ |
407 | 78 | if (tvb_get_uint8(tvb, offset) == ':') |
408 | 2 | { |
409 | 2 | proto_tree_add_item(response_tree, hf_irc_response_trailer, tvb, offset+1, end_offset-offset-1, ENC_ASCII); |
410 | 2 | dissect_irc_tag_data(response_tree, response_item, tvb, offset+1, end_offset-offset-1, pinfo, str_command); |
411 | 2 | return; |
412 | 2 | } |
413 | | |
414 | 1.03k | while(offset < end_offset) |
415 | 1.03k | { |
416 | 1.03k | bool eocp_found = tvb_find_uint8_length(tvb, offset, end_offset-offset, ' ', &eocp_offset); |
417 | 1.03k | bool tag_start_found = tvb_ws_mempbrk_uint8_length(tvb, offset, end_offset-offset, &pbrk_tag_delimiter, &tag_start_offset , &found_tag_needle); |
418 | | |
419 | | /* Create subtree when the first parameter is found */ |
420 | 1.03k | if (first_command_param) |
421 | 76 | { |
422 | 76 | command_tree = proto_tree_add_subtree(response_tree, tvb, offset, end_offset-offset, |
423 | 76 | ett_irc_response_command , NULL, "Command parameters"); |
424 | 76 | first_command_param = false; |
425 | 76 | } |
426 | | |
427 | 1.03k | if ((tag_start_found == false) || (eocp_offset < tag_start_offset)) |
428 | 561 | { |
429 | | /* regular message should be dissected */ |
430 | | |
431 | 561 | if (eocp_found == false) |
432 | 36 | { |
433 | 36 | proto_tree_add_item(command_tree, hf_irc_response_command_param, tvb, offset, end_offset-offset, ENC_ASCII); |
434 | 36 | return; |
435 | 36 | } |
436 | | |
437 | 525 | proto_tree_add_item(command_tree, hf_irc_response_command_param, tvb, offset, eocp_offset-offset, ENC_ASCII); |
438 | 525 | offset = eocp_offset+1; |
439 | | |
440 | | /* clear out any whitespace before next command parameter */ |
441 | 867 | while(offset < end_offset && tvb_get_uint8(tvb, offset) == ' ') |
442 | 342 | { |
443 | 342 | offset++; |
444 | 342 | } |
445 | 525 | if (offset == end_offset) |
446 | 6 | { |
447 | 6 | break; |
448 | 6 | } |
449 | | |
450 | | /* Check if message has a trailer */ |
451 | 519 | if (tvb_get_uint8(tvb, offset) == ':') |
452 | 1 | { |
453 | 1 | proto_tree_add_item(response_tree, hf_irc_response_trailer, tvb, offset+1, end_offset-offset-1, ENC_ASCII); |
454 | 1 | dissect_irc_tag_data(response_tree, response_item, tvb, offset+1, end_offset-offset-1, pinfo, str_command); |
455 | 1 | return; |
456 | 1 | } |
457 | 519 | } |
458 | 475 | else if ((eocp_found == false) || (eocp_offset > tag_start_offset)) |
459 | 475 | { |
460 | | /* tag data dissected */ |
461 | | |
462 | 475 | found_tag_needle = 0; |
463 | 475 | if (!tvb_ws_mempbrk_uint8_length(tvb, tag_start_offset + 1, end_offset - tag_start_offset - 1, &pbrk_tag_delimiter, &tag_end_offset, &found_tag_needle)) |
464 | 30 | { |
465 | 30 | expert_add_info(pinfo, response_item, &ei_irc_missing_end_delimiter); |
466 | 30 | return; |
467 | 30 | } |
468 | | |
469 | 445 | dissect_irc_tag_data(response_tree, response_item, tvb, tag_start_offset, tag_end_offset-tag_start_offset, pinfo, str_command); |
470 | 445 | offset = tag_end_offset+1; |
471 | 445 | } |
472 | 1.03k | } |
473 | 76 | } |
474 | | |
475 | | static int |
476 | | dissect_irc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) |
477 | 168 | { |
478 | 168 | proto_tree *irc_tree, *ti; |
479 | 168 | unsigned offset = 0; |
480 | 168 | unsigned next_offset; |
481 | 168 | unsigned linelen; |
482 | | |
483 | 168 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "IRC"); |
484 | | |
485 | 168 | col_set_str(pinfo->cinfo, COL_INFO, |
486 | 168 | (pinfo->match_uint == pinfo->destport) ? "Request" : "Response"); |
487 | | |
488 | 168 | ti = proto_tree_add_item(tree, proto_irc, tvb, 0, -1, ENC_NA); |
489 | 168 | irc_tree = proto_item_add_subtree(ti, ett_irc); |
490 | | |
491 | | /* |
492 | | * Process the packet data, a line at a time. |
493 | | */ |
494 | 1.04k | while (tvb_offset_exists(tvb, offset)) |
495 | 875 | { |
496 | | /* |
497 | | * Find the end of the line. |
498 | | */ |
499 | 875 | bool linelen_found = tvb_find_line_end_remaining(tvb, offset, &linelen , &next_offset); |
500 | 875 | if (next_offset == offset) { |
501 | | /* |
502 | | * XXX - we really want the "show data a |
503 | | * line at a time" loops in various |
504 | | * dissectors to do reassembly and to |
505 | | * throw an exception if there's no |
506 | | * line ending in the current packet |
507 | | * and we're not doing reassembly. |
508 | | */ |
509 | 0 | break; |
510 | 0 | } |
511 | | |
512 | 875 | if (linelen_found == false) |
513 | 168 | { |
514 | 168 | if (pinfo->match_uint == pinfo->destport) |
515 | 77 | { |
516 | 77 | dissect_irc_request(irc_tree, tvb, pinfo, offset, linelen); |
517 | 77 | } |
518 | 91 | else |
519 | 91 | { |
520 | 91 | dissect_irc_response(irc_tree, tvb, pinfo, offset, linelen); |
521 | 91 | } |
522 | 168 | } |
523 | 875 | offset = next_offset; |
524 | 875 | } |
525 | 168 | return tvb_captured_length(tvb); |
526 | 168 | } |
527 | | |
528 | | void |
529 | | proto_register_irc(void) |
530 | 16 | { |
531 | 16 | static hf_register_info hf[] = { |
532 | 16 | { &hf_irc_response, { "Response", "irc.response", FT_STRING, BASE_NONE, |
533 | 16 | NULL, 0x0, "Line of response message", HFILL }}, |
534 | | |
535 | 16 | { &hf_irc_request, { "Request", "irc.request", FT_STRING, BASE_NONE, |
536 | 16 | NULL, 0x0, "Line of request message", HFILL }}, |
537 | | |
538 | 16 | { &hf_irc_request_prefix, { "Prefix", "irc.request.prefix", FT_STRING, BASE_NONE, |
539 | 16 | NULL, 0x0, "Request prefix", HFILL }}, |
540 | | |
541 | 16 | { &hf_irc_request_command, { "Command", "irc.request.command", FT_STRING, BASE_NONE, |
542 | 16 | NULL, 0x0, "Request command", HFILL }}, |
543 | | |
544 | 16 | { &hf_irc_request_command_param, { "Parameter", "irc.request.command_parameter", FT_STRING, BASE_NONE, |
545 | 16 | NULL, 0x0, "Request command parameter", HFILL }}, |
546 | | |
547 | 16 | { &hf_irc_request_trailer, { "Trailer", "irc.request.trailer", FT_STRING, BASE_NONE, |
548 | 16 | NULL, 0x0, "Request trailer", HFILL }}, |
549 | | |
550 | 16 | { &hf_irc_response_prefix, { "Prefix", "irc.response.prefix", FT_STRING, BASE_NONE, |
551 | 16 | NULL, 0x0, "Response prefix", HFILL }}, |
552 | | |
553 | 16 | { &hf_irc_response_command, { "Command", "irc.response.command", FT_STRING, BASE_NONE, |
554 | 16 | NULL, 0x0, "Response command", HFILL }}, |
555 | | |
556 | 16 | { &hf_irc_response_num_command, { "Command", "irc.response.num_command", FT_UINT16, BASE_DEC, |
557 | 16 | NULL, 0x0, "Response (numeric) command", HFILL }}, |
558 | | |
559 | 16 | { &hf_irc_response_command_param, { "Parameter", "irc.response.command_parameter", FT_STRING, BASE_NONE, |
560 | 16 | NULL, 0x0, "Response command parameter", HFILL }}, |
561 | | |
562 | 16 | { &hf_irc_response_trailer, { "Trailer", "irc.response.trailer", FT_STRING, BASE_NONE, |
563 | 16 | NULL, 0x0, "Response trailer", HFILL }}, |
564 | | |
565 | 16 | { &hf_irc_ctcp_command, { "Command", "irc.ctcp.command", FT_STRING, BASE_NONE, |
566 | 16 | NULL, 0x0, "CTCP command", HFILL }}, |
567 | | |
568 | 16 | { &hf_irc_ctcp_params, { "Parameters", "irc.ctcp.parameters", FT_STRING, BASE_NONE, |
569 | 16 | NULL, 0x0, "CTCP parameters", HFILL }}, |
570 | | |
571 | 16 | { &hf_irc_ctcp_delimiter_odd, { "Odd Delimiter", "irc.ctcp.delimiter.odd", FT_STRING, BASE_NONE, |
572 | 16 | NULL, 0x0, "CTCP odd delimiter", HFILL }}, |
573 | | |
574 | 16 | { &hf_irc_ctcp_delimiter_even, { "Even Delimiter", "irc.ctcp.delimiter.even", FT_STRING, BASE_NONE, |
575 | 16 | NULL, 0x0, "CTCP even delimiter", HFILL }}, |
576 | 16 | }; |
577 | | |
578 | 16 | static int *ett[] = { |
579 | 16 | &ett_irc, |
580 | 16 | &ett_irc_request, |
581 | 16 | &ett_irc_request_command, |
582 | 16 | &ett_irc_response, |
583 | 16 | &ett_irc_response_command, |
584 | 16 | &ett_irc_ctcp |
585 | 16 | }; |
586 | | |
587 | 16 | static ei_register_info ei[] = { |
588 | 16 | { &ei_irc_missing_end_delimiter, { "irc.missing_end_delimiter", PI_MALFORMED, PI_ERROR, "Missing ending tag delimiter (0x01)", EXPFILL }}, |
589 | 16 | { &ei_irc_tag_data_invalid, { "irc.tag_data_invalid", PI_PROTOCOL, PI_WARN, "Tag data outside of NOTICE or PRIVMSG command", EXPFILL }}, |
590 | 16 | { &ei_irc_prefix_missing_ending_space, { "irc.prefix_missing_ending_space", PI_MALFORMED, PI_ERROR, "Prefix missing ending <space>", EXPFILL }}, |
591 | 16 | { &ei_irc_request_command, { "irc.request.command.missing", PI_MALFORMED, PI_ERROR, "Request has no command", EXPFILL }}, |
592 | 16 | { &ei_irc_numeric_request_command, { "irc.request.command.numeric", PI_PROTOCOL, PI_WARN, "Numeric command not allowed in request", EXPFILL }}, |
593 | 16 | { &ei_irc_response_command, { "irc.response.command.missing", PI_MALFORMED, PI_ERROR, "Response has no command", EXPFILL }}, |
594 | 16 | }; |
595 | | |
596 | 16 | expert_module_t* expert_irc; |
597 | | |
598 | 16 | proto_irc = proto_register_protocol("Internet Relay Chat", "IRC", "irc"); |
599 | 16 | register_dissector("irc", dissect_irc, proto_irc); |
600 | 16 | proto_register_field_array(proto_irc, hf, array_length(hf)); |
601 | 16 | proto_register_subtree_array(ett, array_length(ett)); |
602 | 16 | expert_irc = expert_register_protocol(proto_irc); |
603 | 16 | expert_register_field_array(expert_irc, ei, array_length(ei)); |
604 | | |
605 | | /* subdissector code */ |
606 | 16 | irc_ctcp_table = register_dissector_table("irc.ctcp", "CTCP Protocol", |
607 | 16 | proto_irc, FT_UINT8, BASE_DEC); |
608 | 16 | proto_irc_ctcp = proto_register_protocol_in_name_only("Client To Client Protocol", "CTCP", "irc.ctcp", proto_irc, FT_BYTES); |
609 | | |
610 | | /* compile patterns */ |
611 | 16 | ws_mempbrk_compile(&pbrk_tag_delimiter, TAG_DELIMITER); |
612 | 16 | } |
613 | | |
614 | | void |
615 | | proto_reg_handoff_irc(void) |
616 | 16 | { |
617 | 16 | dissector_add_uint_range_with_preference("tcp.port", TCP_PORT_RANGE, find_dissector("irc")); |
618 | | |
619 | 16 | dissector_add_uint("irc.ctcp", IRCCTCP_DEF, create_dissector_handle(dissect_irc_ctcp, proto_irc_ctcp)); |
620 | 16 | } |
621 | | |
622 | | /* |
623 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
624 | | * |
625 | | * Local variables: |
626 | | * c-basic-offset: 4 |
627 | | * tab-width: 8 |
628 | | * indent-tabs-mode: nil |
629 | | * End: |
630 | | * |
631 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
632 | | * :indentSize=4:tabSize=8:noTabs=true: |
633 | | */ |