/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 | 15 | #define TCP_PORT_RANGE "6667,57000" /* Not IANA registered */ |
80 | | |
81 | 623 | #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 | 608 | { |
86 | 608 | proto_tree *ctcp_tree; |
87 | 608 | proto_item *ti; |
88 | 608 | const uint8_t *str_command, *str_params; |
89 | 608 | unsigned space_offset = -1; |
90 | 608 | dissector_handle_t ctcp_handle; |
91 | | |
92 | 608 | ctcp_handle = dissector_get_uint_handle(irc_ctcp_table, IRCCTCP_DEF); |
93 | 608 | if (ctcp_handle == NULL) { |
94 | 0 | return tvb_captured_length(tvb); |
95 | 0 | } |
96 | | |
97 | 608 | ti = proto_tree_add_item(tree, proto_irc_ctcp, tvb, 0, -1, ENC_NA); |
98 | 608 | 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 | 608 | proto_tree_add_item(ctcp_tree, hf_irc_ctcp_delimiter_odd, tvb, 0, 1, ENC_ASCII); |
104 | | |
105 | 608 | if (!tvb_find_uint8_remaining(tvb, 1, ' ', &space_offset)) { |
106 | 522 | 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 | 522 | } |
108 | 86 | else { |
109 | 86 | 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 | 86 | 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 | 86 | } |
112 | | |
113 | 608 | proto_tree_add_item(ctcp_tree, hf_irc_ctcp_delimiter_even, tvb, tvb_reported_length(tvb)-1, 1, ENC_ASCII); |
114 | | |
115 | 608 | return tvb_captured_length(tvb); |
116 | 608 | } |
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 | 612 | { |
121 | 612 | unsigned char found_start_needle = 0, |
122 | 612 | found_end_needle = 0; |
123 | 612 | unsigned tag_start_offset, tag_end_offset; |
124 | 612 | tvbuff_t *next_tvb; |
125 | | |
126 | 612 | 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 | 611 | 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 | 608 | if ((strcmp(command, "NOTICE") != 0) && |
139 | 608 | (strcmp(command, "PRIVMSG") != 0)) |
140 | 608 | { |
141 | 608 | expert_add_info(pinfo, item, &ei_irc_tag_data_invalid); |
142 | 608 | } |
143 | | |
144 | | /* Placeholder to call CTCP dissector, strip out delimiter */ |
145 | 608 | if(tree) { |
146 | 608 | next_tvb = tvb_new_subset_length(tvb, tag_start_offset, 1 + tag_end_offset - tag_start_offset); |
147 | 608 | dissect_irc_ctcp(next_tvb, pinfo, tree, NULL); |
148 | 608 | } |
149 | 608 | } |
150 | | |
151 | | static void |
152 | | dissect_irc_request(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, unsigned offset, unsigned linelen) |
153 | 70 | { |
154 | 70 | proto_tree *request_tree, *command_tree = NULL; |
155 | 70 | proto_item *request_item; |
156 | 70 | unsigned start_offset = offset; |
157 | 70 | unsigned end_offset = start_offset+linelen; |
158 | 70 | unsigned eop_offset = -1, |
159 | 70 | eoc_offset = -1, |
160 | 70 | eocp_offset, |
161 | 70 | tag_start_offset, tag_end_offset; |
162 | 70 | const char *str_command; |
163 | 70 | unsigned char found_tag_needle = 0; |
164 | 70 | bool first_command_param = true; |
165 | | |
166 | 70 | request_item = proto_tree_add_item(tree, hf_irc_request, tvb, offset, linelen, ENC_ASCII); |
167 | 70 | if (linelen <= 0) |
168 | 1 | return; |
169 | | |
170 | 69 | request_tree = proto_item_add_subtree(request_item, ett_irc_request ); |
171 | | |
172 | | /* Check if message has a prefix */ |
173 | 69 | 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 | 99 | while(offset < end_offset && tvb_get_uint8(tvb, offset) == ' ') |
188 | 31 | { |
189 | 31 | offset++; |
190 | 31 | } |
191 | 68 | 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 | 68 | 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 | 60 | 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 | 60 | col_append_fstr( pinfo->cinfo, COL_INFO, " (%s)", str_command); |
216 | | |
217 | | /* Warn if there is a "numeric" command */ |
218 | 60 | if ((eoc_offset-offset == 3) && |
219 | 4 | (g_ascii_isdigit(tvb_get_uint8(tvb, offset))) && |
220 | 0 | (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 | 60 | offset = eoc_offset+1; |
227 | | |
228 | | /* clear out any whitespace before command parameter */ |
229 | 115 | while(offset < end_offset && tvb_get_uint8(tvb, offset) == ' ') |
230 | 55 | { |
231 | 55 | offset++; |
232 | 55 | } |
233 | 60 | if (offset == end_offset) |
234 | 2 | { |
235 | | /* No command parameters */ |
236 | 2 | return; |
237 | 2 | } |
238 | | |
239 | | /* Check if message has a trailer */ |
240 | 58 | 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 | 561 | while(offset < end_offset) |
248 | 561 | { |
249 | 561 | bool eocp_found, tag_start_found, tag_end_found; |
250 | 561 | eocp_found = tvb_find_uint8_length(tvb, offset, end_offset-offset, ' ', &eocp_offset); |
251 | 561 | 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 | 561 | if (first_command_param) |
255 | 57 | { |
256 | 57 | command_tree = proto_tree_add_subtree(request_tree, tvb, offset, end_offset-offset, |
257 | 57 | ett_irc_request_command, NULL, "Command parameters"); |
258 | 57 | first_command_param = false; |
259 | 57 | } |
260 | | |
261 | 561 | if (((eocp_found == false) && (tag_start_found == false)) || |
262 | 533 | ((eocp_found == true) && (tag_start_found == false)) || |
263 | 465 | (eocp_offset < tag_start_offset)) |
264 | 334 | { |
265 | | /* regular message should be dissected */ |
266 | | |
267 | 334 | if (eocp_found == false) |
268 | 28 | { |
269 | 28 | proto_tree_add_item(command_tree, hf_irc_request_command_param, tvb, offset, end_offset-offset, ENC_ASCII); |
270 | 28 | return; |
271 | 28 | } |
272 | | |
273 | 306 | proto_tree_add_item(command_tree, hf_irc_request_command_param, tvb, offset, eocp_offset-offset, ENC_ASCII); |
274 | 306 | offset = eocp_offset+1; |
275 | | |
276 | | /* clear out any whitespace before next command parameter */ |
277 | 676 | while(offset < end_offset && tvb_get_uint8(tvb, offset) == ' ') |
278 | 370 | { |
279 | 370 | offset++; |
280 | 370 | } |
281 | 306 | if (offset == end_offset) |
282 | 2 | { |
283 | 2 | break; |
284 | 2 | } |
285 | | |
286 | | /* Check if message has a trailer */ |
287 | 304 | 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 | 304 | } |
294 | 227 | else if (((eocp_found == false) && (tag_start_found == false)) || |
295 | 227 | (eocp_offset > tag_start_offset)) |
296 | 227 | { |
297 | | /* tag data dissected */ |
298 | | |
299 | 227 | found_tag_needle = 0; |
300 | 227 | 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 | 227 | if (tag_end_found == false) |
302 | 25 | { |
303 | 25 | expert_add_info(pinfo, request_item, &ei_irc_missing_end_delimiter); |
304 | 25 | return; |
305 | 25 | } |
306 | | |
307 | 202 | dissect_irc_tag_data(request_tree, request_item, tvb, tag_start_offset, tag_end_offset-tag_start_offset, pinfo, str_command); |
308 | 202 | offset = tag_end_offset+1; |
309 | 202 | } |
310 | 561 | } |
311 | 57 | } |
312 | | |
313 | | static void |
314 | | dissect_irc_response(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, unsigned offset, unsigned linelen) |
315 | 83 | { |
316 | 83 | proto_tree *response_tree, *command_tree = NULL; |
317 | 83 | proto_item *response_item, *hidden_item; |
318 | 83 | unsigned start_offset = offset; |
319 | 83 | unsigned end_offset = start_offset+linelen; |
320 | 83 | unsigned eop_offset = -1, |
321 | 83 | eoc_offset = -1, |
322 | 83 | eocp_offset, |
323 | 83 | tag_start_offset, tag_end_offset; |
324 | 83 | const char* str_command; |
325 | 83 | uint16_t num_command; |
326 | 83 | unsigned char found_tag_needle = 0; |
327 | 83 | bool first_command_param = true; |
328 | | |
329 | 83 | response_item = proto_tree_add_item(tree, hf_irc_response, tvb, offset, linelen, ENC_ASCII); |
330 | 83 | if (linelen <= 0) |
331 | 1 | return; |
332 | | |
333 | 82 | response_tree = proto_item_add_subtree(response_item, ett_irc_response ); |
334 | | |
335 | | /* Check if message has a prefix */ |
336 | 82 | if (tvb_get_uint8(tvb, offset) == ':') |
337 | 3 | { |
338 | | /* find the end of the prefix */ |
339 | 3 | if (!tvb_find_uint8_length(tvb, offset + 1, linelen - 1, ' ', &eop_offset)) |
340 | 1 | { |
341 | 1 | expert_add_info(pinfo, response_item, &ei_irc_prefix_missing_ending_space); |
342 | 1 | return; |
343 | 1 | } |
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 | 131 | while(offset < end_offset && tvb_get_uint8(tvb, offset) == ' ') |
351 | 50 | { |
352 | 50 | offset++; |
353 | 50 | } |
354 | 81 | 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 | 80 | if (!tvb_find_uint8_length(tvb, offset, end_offset - offset, ' ', &eoc_offset)) |
361 | 9 | { |
362 | 9 | const uint8_t* col_str; |
363 | 9 | 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 | 9 | 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 | 9 | 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 | 9 | return; |
377 | 9 | } |
378 | | |
379 | 71 | 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 | 71 | 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 | 71 | if ((eoc_offset-offset == 3) && |
384 | 15 | (g_ascii_isdigit(tvb_get_uint8(tvb, offset))) && |
385 | 3 | (g_ascii_isdigit(tvb_get_uint8(tvb, offset+1))) && |
386 | 2 | (g_ascii_isdigit(tvb_get_uint8(tvb, offset+2)))) |
387 | 2 | { |
388 | 2 | 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 | 2 | hidden_item = proto_tree_add_uint(response_tree, hf_irc_response_num_command, tvb, offset, eoc_offset-offset, num_command); |
390 | 2 | proto_item_set_hidden(hidden_item); |
391 | 2 | } |
392 | | |
393 | 71 | offset = eoc_offset+1; |
394 | | |
395 | | /* clear out any whitespace before command parameter */ |
396 | 149 | while(offset < end_offset && tvb_get_uint8(tvb, offset) == ' ') |
397 | 78 | { |
398 | 78 | offset++; |
399 | 78 | } |
400 | 71 | if (offset == end_offset) |
401 | 2 | { |
402 | | /* No command parameters */ |
403 | 2 | return; |
404 | 2 | } |
405 | | |
406 | | /* Check if message has a trailer */ |
407 | 69 | 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.00k | while(offset < end_offset) |
415 | 1.00k | { |
416 | 1.00k | bool eocp_found = tvb_find_uint8_length(tvb, offset, end_offset-offset, ' ', &eocp_offset); |
417 | 1.00k | 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.00k | if (first_command_param) |
421 | 67 | { |
422 | 67 | command_tree = proto_tree_add_subtree(response_tree, tvb, offset, end_offset-offset, |
423 | 67 | ett_irc_response_command , NULL, "Command parameters"); |
424 | 67 | first_command_param = false; |
425 | 67 | } |
426 | | |
427 | 1.00k | if ((tag_start_found == false) || (eocp_offset < tag_start_offset)) |
428 | 574 | { |
429 | | /* regular message should be dissected */ |
430 | | |
431 | 574 | if (eocp_found == false) |
432 | 32 | { |
433 | 32 | proto_tree_add_item(command_tree, hf_irc_response_command_param, tvb, offset, end_offset-offset, ENC_ASCII); |
434 | 32 | return; |
435 | 32 | } |
436 | | |
437 | 542 | proto_tree_add_item(command_tree, hf_irc_response_command_param, tvb, offset, eocp_offset-offset, ENC_ASCII); |
438 | 542 | offset = eocp_offset+1; |
439 | | |
440 | | /* clear out any whitespace before next command parameter */ |
441 | 837 | while(offset < end_offset && tvb_get_uint8(tvb, offset) == ' ') |
442 | 295 | { |
443 | 295 | offset++; |
444 | 295 | } |
445 | 542 | if (offset == end_offset) |
446 | 4 | { |
447 | 4 | break; |
448 | 4 | } |
449 | | |
450 | | /* Check if message has a trailer */ |
451 | 538 | if (tvb_get_uint8(tvb, offset) == ':') |
452 | 2 | { |
453 | 2 | proto_tree_add_item(response_tree, hf_irc_response_trailer, tvb, offset+1, end_offset-offset-1, ENC_ASCII); |
454 | 2 | dissect_irc_tag_data(response_tree, response_item, tvb, offset+1, end_offset-offset-1, pinfo, str_command); |
455 | 2 | return; |
456 | 2 | } |
457 | 538 | } |
458 | 430 | else if ((eocp_found == false) || (eocp_offset > tag_start_offset)) |
459 | 430 | { |
460 | | /* tag data dissected */ |
461 | | |
462 | 430 | found_tag_needle = 0; |
463 | 430 | 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 | 27 | { |
465 | 27 | expert_add_info(pinfo, response_item, &ei_irc_missing_end_delimiter); |
466 | 27 | return; |
467 | 27 | } |
468 | | |
469 | 403 | dissect_irc_tag_data(response_tree, response_item, tvb, tag_start_offset, tag_end_offset-tag_start_offset, pinfo, str_command); |
470 | 403 | offset = tag_end_offset+1; |
471 | 403 | } |
472 | 1.00k | } |
473 | 67 | } |
474 | | |
475 | | static int |
476 | | dissect_irc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) |
477 | 153 | { |
478 | 153 | proto_tree *irc_tree, *ti; |
479 | 153 | unsigned offset = 0; |
480 | 153 | unsigned next_offset; |
481 | 153 | unsigned linelen; |
482 | | |
483 | 153 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "IRC"); |
484 | | |
485 | 153 | col_set_str(pinfo->cinfo, COL_INFO, |
486 | 153 | (pinfo->match_uint == pinfo->destport) ? "Request" : "Response"); |
487 | | |
488 | 153 | ti = proto_tree_add_item(tree, proto_irc, tvb, 0, -1, ENC_NA); |
489 | 153 | irc_tree = proto_item_add_subtree(ti, ett_irc); |
490 | | |
491 | | /* |
492 | | * Process the packet data, a line at a time. |
493 | | */ |
494 | 1.00k | while (tvb_offset_exists(tvb, offset)) |
495 | 848 | { |
496 | | /* |
497 | | * Find the end of the line. |
498 | | */ |
499 | 848 | bool linelen_found = tvb_find_line_end_remaining(tvb, offset, &linelen , &next_offset); |
500 | 848 | 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 | 848 | if (linelen_found == false) |
513 | 153 | { |
514 | 153 | if (pinfo->match_uint == pinfo->destport) |
515 | 70 | { |
516 | 70 | dissect_irc_request(irc_tree, tvb, pinfo, offset, linelen); |
517 | 70 | } |
518 | 83 | else |
519 | 83 | { |
520 | 83 | dissect_irc_response(irc_tree, tvb, pinfo, offset, linelen); |
521 | 83 | } |
522 | 153 | } |
523 | 848 | offset = next_offset; |
524 | 848 | } |
525 | 153 | return tvb_captured_length(tvb); |
526 | 153 | } |
527 | | |
528 | | void |
529 | | proto_register_irc(void) |
530 | 15 | { |
531 | 15 | static hf_register_info hf[] = { |
532 | 15 | { &hf_irc_response, { "Response", "irc.response", FT_STRING, BASE_NONE, |
533 | 15 | NULL, 0x0, "Line of response message", HFILL }}, |
534 | | |
535 | 15 | { &hf_irc_request, { "Request", "irc.request", FT_STRING, BASE_NONE, |
536 | 15 | NULL, 0x0, "Line of request message", HFILL }}, |
537 | | |
538 | 15 | { &hf_irc_request_prefix, { "Prefix", "irc.request.prefix", FT_STRING, BASE_NONE, |
539 | 15 | NULL, 0x0, "Request prefix", HFILL }}, |
540 | | |
541 | 15 | { &hf_irc_request_command, { "Command", "irc.request.command", FT_STRING, BASE_NONE, |
542 | 15 | NULL, 0x0, "Request command", HFILL }}, |
543 | | |
544 | 15 | { &hf_irc_request_command_param, { "Parameter", "irc.request.command_parameter", FT_STRING, BASE_NONE, |
545 | 15 | NULL, 0x0, "Request command parameter", HFILL }}, |
546 | | |
547 | 15 | { &hf_irc_request_trailer, { "Trailer", "irc.request.trailer", FT_STRING, BASE_NONE, |
548 | 15 | NULL, 0x0, "Request trailer", HFILL }}, |
549 | | |
550 | 15 | { &hf_irc_response_prefix, { "Prefix", "irc.response.prefix", FT_STRING, BASE_NONE, |
551 | 15 | NULL, 0x0, "Response prefix", HFILL }}, |
552 | | |
553 | 15 | { &hf_irc_response_command, { "Command", "irc.response.command", FT_STRING, BASE_NONE, |
554 | 15 | NULL, 0x0, "Response command", HFILL }}, |
555 | | |
556 | 15 | { &hf_irc_response_num_command, { "Command", "irc.response.num_command", FT_UINT16, BASE_DEC, |
557 | 15 | NULL, 0x0, "Response (numeric) command", HFILL }}, |
558 | | |
559 | 15 | { &hf_irc_response_command_param, { "Parameter", "irc.response.command_parameter", FT_STRING, BASE_NONE, |
560 | 15 | NULL, 0x0, "Response command parameter", HFILL }}, |
561 | | |
562 | 15 | { &hf_irc_response_trailer, { "Trailer", "irc.response.trailer", FT_STRING, BASE_NONE, |
563 | 15 | NULL, 0x0, "Response trailer", HFILL }}, |
564 | | |
565 | 15 | { &hf_irc_ctcp_command, { "Command", "irc.ctcp.command", FT_STRING, BASE_NONE, |
566 | 15 | NULL, 0x0, "CTCP command", HFILL }}, |
567 | | |
568 | 15 | { &hf_irc_ctcp_params, { "Parameters", "irc.ctcp.parameters", FT_STRING, BASE_NONE, |
569 | 15 | NULL, 0x0, "CTCP parameters", HFILL }}, |
570 | | |
571 | 15 | { &hf_irc_ctcp_delimiter_odd, { "Odd Delimiter", "irc.ctcp.delimiter.odd", FT_STRING, BASE_NONE, |
572 | 15 | NULL, 0x0, "CTCP odd delimiter", HFILL }}, |
573 | | |
574 | 15 | { &hf_irc_ctcp_delimiter_even, { "Even Delimiter", "irc.ctcp.delimiter.even", FT_STRING, BASE_NONE, |
575 | 15 | NULL, 0x0, "CTCP even delimiter", HFILL }}, |
576 | 15 | }; |
577 | | |
578 | 15 | static int *ett[] = { |
579 | 15 | &ett_irc, |
580 | 15 | &ett_irc_request, |
581 | 15 | &ett_irc_request_command, |
582 | 15 | &ett_irc_response, |
583 | 15 | &ett_irc_response_command, |
584 | 15 | &ett_irc_ctcp |
585 | 15 | }; |
586 | | |
587 | 15 | static ei_register_info ei[] = { |
588 | 15 | { &ei_irc_missing_end_delimiter, { "irc.missing_end_delimiter", PI_MALFORMED, PI_ERROR, "Missing ending tag delimiter (0x01)", EXPFILL }}, |
589 | 15 | { &ei_irc_tag_data_invalid, { "irc.tag_data_invalid", PI_PROTOCOL, PI_WARN, "Tag data outside of NOTICE or PRIVMSG command", EXPFILL }}, |
590 | 15 | { &ei_irc_prefix_missing_ending_space, { "irc.prefix_missing_ending_space", PI_MALFORMED, PI_ERROR, "Prefix missing ending <space>", EXPFILL }}, |
591 | 15 | { &ei_irc_request_command, { "irc.request.command.missing", PI_MALFORMED, PI_ERROR, "Request has no command", EXPFILL }}, |
592 | 15 | { &ei_irc_numeric_request_command, { "irc.request.command.numeric", PI_PROTOCOL, PI_WARN, "Numeric command not allowed in request", EXPFILL }}, |
593 | 15 | { &ei_irc_response_command, { "irc.response.command.missing", PI_MALFORMED, PI_ERROR, "Response has no command", EXPFILL }}, |
594 | 15 | }; |
595 | | |
596 | 15 | expert_module_t* expert_irc; |
597 | | |
598 | 15 | proto_irc = proto_register_protocol("Internet Relay Chat", "IRC", "irc"); |
599 | 15 | register_dissector("irc", dissect_irc, proto_irc); |
600 | 15 | proto_register_field_array(proto_irc, hf, array_length(hf)); |
601 | 15 | proto_register_subtree_array(ett, array_length(ett)); |
602 | 15 | expert_irc = expert_register_protocol(proto_irc); |
603 | 15 | expert_register_field_array(expert_irc, ei, array_length(ei)); |
604 | | |
605 | | /* subdissector code */ |
606 | 15 | irc_ctcp_table = register_dissector_table("irc.ctcp", "CTCP Protocol", |
607 | 15 | proto_irc, FT_UINT8, BASE_DEC); |
608 | 15 | 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 | 15 | ws_mempbrk_compile(&pbrk_tag_delimiter, TAG_DELIMITER); |
612 | 15 | } |
613 | | |
614 | | void |
615 | | proto_reg_handoff_irc(void) |
616 | 15 | { |
617 | 15 | dissector_add_uint_range_with_preference("tcp.port", TCP_PORT_RANGE, find_dissector("irc")); |
618 | | |
619 | 15 | dissector_add_uint("irc.ctcp", IRCCTCP_DEF, create_dissector_handle(dissect_irc_ctcp, proto_irc_ctcp)); |
620 | 15 | } |
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 | | */ |