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  *  http://www.irchelp.org/irchelp/rfc/ctcpspec.html
23  *
24  * and
25  *
26  *  http://www.invlogic.com/irc/ctcp.html
27  */
28 
29 #include "config.h"
30 
31 #include <epan/packet.h>
32 #include <epan/expert.h>
33 
34 void proto_register_irc(void);
35 void proto_reg_handoff_irc(void);
36 
37 static int proto_irc = -1;
38 static int hf_irc_request = -1;
39 static int hf_irc_request_prefix = -1;
40 static int hf_irc_request_command = -1;
41 static int hf_irc_request_command_param = -1;
42 static int hf_irc_request_trailer = -1;
43 static int hf_irc_response = -1;
44 static int hf_irc_response_prefix = -1;
45 static int hf_irc_response_command = -1;
46 static int hf_irc_response_num_command = -1;
47 static int hf_irc_response_command_param = -1;
48 static int hf_irc_response_trailer = -1;
49 static int hf_irc_ctcp = -1;
50 
51 static gint ett_irc = -1;
52 static gint ett_irc_request = -1;
53 static gint ett_irc_request_command = -1;
54 static gint ett_irc_response = -1;
55 static gint ett_irc_response_command = -1;
56 
57 static expert_field ei_irc_missing_end_delimiter = EI_INIT;
58 static expert_field ei_irc_numeric_request_command = EI_INIT;
59 static expert_field ei_irc_response_command = EI_INIT;
60 static expert_field ei_irc_prefix_missing_ending_space = EI_INIT;
61 static expert_field ei_irc_request_command = EI_INIT;
62 static expert_field ei_irc_tag_data_invalid = EI_INIT;
63 
64 /* This must be a null-terminated string */
65 static const guint8 TAG_DELIMITER[] = {0x01, 0x00};
66 /* patterns used for tvb_ws_mempbrk_pattern_guint8 */
67 static ws_mempbrk_pattern pbrk_tag_delimiter;
68 
69 
70 #define TCP_PORT_RANGE          "6667,57000" /* Not IANA registered */
71 
72 static void
dissect_irc_tag_data(proto_tree * tree,proto_item * item,tvbuff_t * tvb,int offset,int datalen,packet_info * pinfo,const guint8 * command)73 dissect_irc_tag_data(proto_tree *tree, proto_item *item, tvbuff_t *tvb, int offset, int datalen, packet_info *pinfo, const guint8* command)
74 {
75     guchar found_start_needle = 0,
76            found_end_needle   = 0;
77     gint   tag_start_offset, tag_end_offset;
78 
79     tag_start_offset = tvb_ws_mempbrk_pattern_guint8(tvb, offset, datalen, &pbrk_tag_delimiter, &found_start_needle);
80     if (tag_start_offset == -1)
81     {
82         /* no tag data */
83         return;
84     }
85 
86     tag_end_offset = tvb_ws_mempbrk_pattern_guint8(tvb, offset, datalen-offset, &pbrk_tag_delimiter, &found_end_needle);
87     if (tag_end_offset == -1)
88     {
89         expert_add_info(pinfo, item, &ei_irc_missing_end_delimiter);
90         return;
91     }
92 
93     if ((strcmp(command, "NOTICE") != 0) &&
94        (strcmp(command, "PRIVMSG") != 0))
95     {
96         expert_add_info(pinfo, item, &ei_irc_tag_data_invalid);
97     }
98 
99     /* Placeholder to call CTCP dissector, strip out delimiter */
100     proto_tree_add_item(tree, hf_irc_ctcp, tvb, offset+1, datalen-2, ENC_ASCII|ENC_NA);
101 }
102 
103 static void
dissect_irc_request(proto_tree * tree,tvbuff_t * tvb,packet_info * pinfo,int offset,int linelen)104 dissect_irc_request(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, int offset, int linelen)
105 {
106     proto_tree   *request_tree, *command_tree = NULL;
107     proto_item   *request_item;
108     int           start_offset                = offset;
109     int           end_offset                  = start_offset+linelen;
110     gint          eop_offset                  = -1,
111                   eoc_offset                  = -1,
112                   eocp_offset,
113                   tag_start_offset, tag_end_offset;
114     const guint8 *str_command;
115     guchar        found_tag_needle            = 0;
116     gboolean      first_command_param         = TRUE;
117 
118     request_item = proto_tree_add_item(tree, hf_irc_request, tvb, offset, linelen, ENC_ASCII|ENC_NA);
119     if (linelen <= 0)
120         return;
121 
122     request_tree = proto_item_add_subtree(request_item, ett_irc_request );
123 
124     /* Check if message has a prefix */
125     if (tvb_get_guint8(tvb, offset) == ':')
126     {
127         /* find the end of the prefix */
128         eop_offset = tvb_find_guint8(tvb, offset+1, linelen-1, ' ');
129         if (eop_offset == -1)
130         {
131             expert_add_info(pinfo, request_item, &ei_irc_prefix_missing_ending_space);
132             return;
133         }
134 
135         proto_tree_add_item(request_tree, hf_irc_request_prefix, tvb, offset+1, eop_offset-offset-1, ENC_ASCII|ENC_NA);
136         offset = eop_offset+1;
137     }
138 
139     /* clear out any whitespace before command */
140     while(offset < end_offset && tvb_get_guint8(tvb, offset) == ' ')
141     {
142         offset++;
143     }
144     if (offset == end_offset)
145     {
146         expert_add_info(pinfo, request_item, &ei_irc_request_command);
147         return;
148     }
149 
150     eoc_offset = tvb_find_guint8(tvb, offset, end_offset-offset, ' ');
151     if (eoc_offset == -1)
152     {
153         const guint8* col_str;
154         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);
155         col_append_fstr( pinfo->cinfo, COL_INFO, " (%s)", col_str);
156 
157         /* Warn if there is a "numeric" command */
158         if ((end_offset-offset == 3) &&
159             (g_ascii_isdigit(tvb_get_guint8(tvb, offset))) &&
160             (g_ascii_isdigit(tvb_get_guint8(tvb, offset+1))) &&
161             (g_ascii_isdigit(tvb_get_guint8(tvb, offset+2))))
162         {
163             expert_add_info(pinfo, request_item, &ei_irc_numeric_request_command);
164         }
165         return;
166     }
167 
168     proto_tree_add_item_ret_string(request_tree, hf_irc_request_command, tvb, offset, eoc_offset-offset, ENC_ASCII|ENC_NA, pinfo->pool, &str_command);
169     col_append_fstr( pinfo->cinfo, COL_INFO, " (%s)", str_command);
170 
171     /* Warn if there is a "numeric" command */
172     if ((eoc_offset-offset == 3) &&
173        (g_ascii_isdigit(tvb_get_guint8(tvb, offset))) &&
174        (g_ascii_isdigit(tvb_get_guint8(tvb, offset+1))) &&
175        (g_ascii_isdigit(tvb_get_guint8(tvb, offset+2))))
176     {
177         expert_add_info(pinfo, request_item, &ei_irc_numeric_request_command);
178     }
179 
180     offset = eoc_offset+1;
181 
182     /* clear out any whitespace before command parameter */
183     while(offset < end_offset && tvb_get_guint8(tvb, offset) == ' ')
184     {
185         offset++;
186     }
187     if (offset == end_offset)
188     {
189         /* No command parameters */
190         return;
191     }
192 
193     /* Check if message has a trailer */
194     if (tvb_get_guint8(tvb, offset) == ':')
195     {
196         proto_tree_add_item(request_tree, hf_irc_request_trailer, tvb, offset+1, end_offset-offset-1, ENC_ASCII|ENC_NA);
197         dissect_irc_tag_data(request_tree, request_item, tvb, offset+1, end_offset-offset-1, pinfo, str_command);
198         return;
199     }
200 
201     while(offset < end_offset)
202     {
203         eocp_offset = tvb_find_guint8(tvb, offset, end_offset-offset, ' ');
204         tag_start_offset = tvb_ws_mempbrk_pattern_guint8(tvb, offset, end_offset-offset, &pbrk_tag_delimiter, &found_tag_needle);
205 
206         /* Create subtree when the first parameter is found */
207         if (first_command_param)
208         {
209             command_tree = proto_tree_add_subtree(request_tree, tvb, offset, end_offset-offset,
210                                              ett_irc_request_command, NULL, "Command parameters");
211             first_command_param = FALSE;
212         }
213 
214         if (((eocp_offset == -1) && (tag_start_offset == -1)) ||
215             ((eocp_offset != -1) && (tag_start_offset == -1)) ||
216             (eocp_offset < tag_start_offset))
217         {
218             /* regular message should be dissected */
219 
220             if (eocp_offset == -1)
221             {
222                 proto_tree_add_item(command_tree, hf_irc_request_command_param, tvb, offset, end_offset-offset, ENC_ASCII|ENC_NA);
223                 return;
224             }
225 
226             proto_tree_add_item(command_tree, hf_irc_request_command_param, tvb, offset, eocp_offset-offset, ENC_ASCII|ENC_NA);
227             offset = eocp_offset+1;
228 
229             /* clear out any whitespace before next command parameter */
230             while(offset < end_offset && tvb_get_guint8(tvb, offset) == ' ')
231             {
232                 offset++;
233             }
234             if (offset == end_offset)
235             {
236                 break;
237             }
238 
239             /* Check if message has a trailer */
240             if (tvb_get_guint8(tvb, offset) == ':')
241             {
242                 proto_tree_add_item(request_tree, hf_irc_request_trailer, tvb, offset+1, end_offset-offset-1, ENC_ASCII|ENC_NA);
243                 dissect_irc_tag_data(request_tree, request_item, tvb, offset+1, end_offset-offset-1, pinfo, str_command);
244                 return;
245             }
246         }
247         else if (((eocp_offset == -1) && (tag_start_offset != -1)) ||
248                (eocp_offset > tag_start_offset))
249         {
250             /* tag data dissected */
251 
252             found_tag_needle = 0;
253             tag_end_offset = tvb_ws_mempbrk_pattern_guint8(tvb, tag_start_offset+1, end_offset-tag_start_offset-1, &pbrk_tag_delimiter, &found_tag_needle);
254             if (tag_end_offset == -1)
255             {
256                 expert_add_info(pinfo, request_item, &ei_irc_missing_end_delimiter);
257                 return;
258             }
259 
260             dissect_irc_tag_data(request_tree, request_item, tvb, tag_start_offset, tag_end_offset-tag_start_offset, pinfo, str_command);
261             offset = tag_end_offset+1;
262         }
263     }
264 }
265 
266 static void
dissect_irc_response(proto_tree * tree,tvbuff_t * tvb,packet_info * pinfo,int offset,int linelen)267 dissect_irc_response(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, int offset, int linelen)
268 {
269     proto_tree   *response_tree, *command_tree = NULL;
270     proto_item   *response_item, *hidden_item;
271     int           start_offset                 = offset;
272     int           end_offset                   = start_offset+linelen;
273     gint          eop_offset                   = -1,
274                   eoc_offset                   = -1,
275                   eocp_offset,
276                   tag_start_offset, tag_end_offset;
277     const guint8* str_command;
278     guint16       num_command;
279     guchar        found_tag_needle             = 0;
280     gboolean      first_command_param          = TRUE;
281 
282     response_item = proto_tree_add_item(tree, hf_irc_response, tvb, offset, linelen, ENC_ASCII|ENC_NA);
283     if (linelen <= 0)
284         return;
285 
286     response_tree = proto_item_add_subtree(response_item, ett_irc_response );
287 
288     /* Check if message has a prefix */
289     if (tvb_get_guint8(tvb, offset) == ':')
290     {
291         /* find the end of the prefix */
292         eop_offset = tvb_find_guint8(tvb, offset+1, linelen-1, ' ');
293         if (eop_offset == -1)
294         {
295             expert_add_info(pinfo, response_item, &ei_irc_prefix_missing_ending_space);
296             return;
297         }
298 
299         proto_tree_add_item(response_tree, hf_irc_response_prefix, tvb, offset+1, eop_offset-offset-1, ENC_ASCII|ENC_NA);
300         offset = eop_offset+1;
301     }
302 
303     /* clear out any whitespace before command */
304     while(offset < end_offset && tvb_get_guint8(tvb, offset) == ' ')
305     {
306         offset++;
307     }
308     if (offset == end_offset)
309     {
310         expert_add_info(pinfo, response_item, &ei_irc_response_command);
311         return;
312     }
313 
314     eoc_offset = tvb_find_guint8(tvb, offset, end_offset-offset, ' ');
315     if (eoc_offset == -1)
316     {
317         const guint8* col_str;
318         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);
319         col_append_fstr( pinfo->cinfo, COL_INFO, " (%s)", col_str);
320 
321         /* if response command is numeric, allow it to be filtered as an integer */
322         if ((end_offset-offset == 3) &&
323             (g_ascii_isdigit(tvb_get_guint8(tvb, offset))) &&
324             (g_ascii_isdigit(tvb_get_guint8(tvb, offset+1))) &&
325             (g_ascii_isdigit(tvb_get_guint8(tvb, offset+2))))
326         {
327             num_command = ((tvb_get_guint8(tvb, offset)-0x30)*100) + ((tvb_get_guint8(tvb, offset+1)-0x30)*10) + (tvb_get_guint8(tvb, offset+2)-0x30);
328             hidden_item = proto_tree_add_uint(response_tree, hf_irc_response_num_command, tvb, offset, end_offset-offset, num_command);
329             proto_item_set_hidden(hidden_item);
330         }
331         return;
332     }
333 
334     proto_tree_add_item_ret_string(response_tree, hf_irc_response_command, tvb, offset, eoc_offset-offset, ENC_ASCII|ENC_NA, pinfo->pool, &str_command);
335     col_append_fstr( pinfo->cinfo, COL_INFO, " (%s)", str_command);
336 
337     /* if response command is numeric, allow it to be filtered as an integer */
338     if ((eoc_offset-offset == 3) &&
339        (g_ascii_isdigit(tvb_get_guint8(tvb, offset))) &&
340        (g_ascii_isdigit(tvb_get_guint8(tvb, offset+1))) &&
341        (g_ascii_isdigit(tvb_get_guint8(tvb, offset+2))))
342     {
343         num_command = ((tvb_get_guint8(tvb, offset)-0x30)*100) + ((tvb_get_guint8(tvb, offset+1)-0x30)*10) + (tvb_get_guint8(tvb, offset+2)-0x30);
344         hidden_item = proto_tree_add_uint(response_tree, hf_irc_response_num_command, tvb, offset, eoc_offset-offset, num_command);
345         proto_item_set_hidden(hidden_item);
346     }
347 
348     offset = eoc_offset+1;
349 
350     /* clear out any whitespace before command parameter */
351     while(offset < end_offset && tvb_get_guint8(tvb, offset) == ' ')
352     {
353         offset++;
354     }
355     if (offset == end_offset)
356     {
357         /* No command parameters */
358         return;
359     }
360 
361     /* Check if message has a trailer */
362     if (tvb_get_guint8(tvb, offset) == ':')
363     {
364         proto_tree_add_item(response_tree, hf_irc_response_trailer, tvb, offset+1, end_offset-offset-1, ENC_ASCII|ENC_NA);
365         dissect_irc_tag_data(response_tree, response_item, tvb, offset+1, end_offset-offset-1, pinfo, str_command);
366         return;
367     }
368 
369     while(offset < end_offset)
370     {
371         eocp_offset = tvb_find_guint8(tvb, offset, end_offset-offset, ' ');
372         tag_start_offset = tvb_ws_mempbrk_pattern_guint8(tvb, offset, end_offset-offset, &pbrk_tag_delimiter, &found_tag_needle);
373 
374         /* Create subtree when the first parameter is found */
375         if (first_command_param)
376         {
377             command_tree = proto_tree_add_subtree(response_tree, tvb, offset, end_offset-offset,
378                                         ett_irc_response_command , NULL, "Command parameters");
379             first_command_param = FALSE;
380         }
381 
382         if ((tag_start_offset == -1) || (eocp_offset < tag_start_offset))
383         {
384             /* regular message should be dissected */
385 
386             if (eocp_offset == -1)
387             {
388                 proto_tree_add_item(command_tree, hf_irc_response_command_param, tvb, offset, end_offset-offset, ENC_ASCII|ENC_NA);
389                 return;
390             }
391 
392             proto_tree_add_item(command_tree, hf_irc_response_command_param, tvb, offset, eocp_offset-offset, ENC_ASCII|ENC_NA);
393             offset = eocp_offset+1;
394 
395             /* clear out any whitespace before next command parameter */
396             while(offset < end_offset && tvb_get_guint8(tvb, offset) == ' ')
397             {
398                 offset++;
399             }
400             if (offset == end_offset)
401             {
402                 break;
403             }
404 
405             /* Check if message has a trailer */
406             if (tvb_get_guint8(tvb, offset) == ':')
407             {
408                 proto_tree_add_item(response_tree, hf_irc_response_trailer, tvb, offset+1, end_offset-offset-1, ENC_ASCII|ENC_NA);
409                 dissect_irc_tag_data(response_tree, response_item, tvb, offset+1, end_offset-offset-1, pinfo, str_command);
410                 return;
411             }
412         }
413         else if ((eocp_offset == -1) || (eocp_offset > tag_start_offset))
414         {
415             /* tag data dissected */
416 
417             found_tag_needle = 0;
418             tag_end_offset = tvb_ws_mempbrk_pattern_guint8(tvb, tag_start_offset+1, end_offset-tag_start_offset-1, &pbrk_tag_delimiter, &found_tag_needle);
419             if (tag_end_offset == -1)
420             {
421                 expert_add_info(pinfo, response_item, &ei_irc_missing_end_delimiter);
422                 return;
423             }
424 
425             dissect_irc_tag_data(response_tree, response_item, tvb, tag_start_offset, tag_end_offset-tag_start_offset, pinfo, str_command);
426             offset = tag_end_offset+1;
427         }
428     }
429 }
430 
431 static int
dissect_irc(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data _U_)432 dissect_irc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
433 {
434     proto_tree *irc_tree, *ti;
435     gint        offset = 0;
436     gint        next_offset;
437     int         linelen;
438 
439     col_set_str(pinfo->cinfo, COL_PROTOCOL, "IRC");
440 
441     col_set_str(pinfo->cinfo, COL_INFO,
442         (pinfo->match_uint == pinfo->destport) ? "Request" : "Response");
443 
444     ti = proto_tree_add_item(tree, proto_irc, tvb, 0, -1, ENC_NA);
445     irc_tree = proto_item_add_subtree(ti, ett_irc);
446 
447     /*
448      * Process the packet data, a line at a time.
449      */
450     while (tvb_offset_exists(tvb, offset))
451     {
452         /*
453          * Find the end of the line.
454          */
455         linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
456         if (next_offset == offset) {
457             /*
458              * XXX - we really want the "show data a
459              * line at a time" loops in various
460              * dissectors to do reassembly and to
461              * throw an exception if there's no
462              * line ending in the current packet
463              * and we're not doing reassembly.
464              */
465             break;
466         }
467 
468         if (linelen != 0)
469         {
470             if (pinfo->match_uint == pinfo->destport)
471             {
472                 dissect_irc_request(irc_tree, tvb, pinfo, offset, linelen);
473             }
474             else
475             {
476                 dissect_irc_response(irc_tree, tvb, pinfo, offset, linelen);
477             }
478         }
479         offset = next_offset;
480     }
481     return tvb_captured_length(tvb);
482 }
483 
484 void
proto_register_irc(void)485 proto_register_irc(void)
486 {
487     static hf_register_info hf[] = {
488         { &hf_irc_response, { "Response", "irc.response", FT_STRING, STR_ASCII,
489           NULL, 0x0, "Line of response message", HFILL }},
490 
491         { &hf_irc_request, { "Request", "irc.request", FT_STRING, STR_ASCII,
492           NULL, 0x0, "Line of request message", HFILL }},
493 
494         { &hf_irc_request_prefix, { "Prefix", "irc.request.prefix", FT_STRING, STR_ASCII,
495           NULL, 0x0, "Request prefix", HFILL }},
496 
497         { &hf_irc_request_command, { "Command", "irc.request.command", FT_STRING, STR_ASCII,
498           NULL, 0x0, "Request command", HFILL }},
499 
500         { &hf_irc_request_command_param, { "Parameter", "irc.request.command_parameter", FT_STRING, STR_ASCII,
501           NULL, 0x0, "Request command parameter", HFILL }},
502 
503         { &hf_irc_request_trailer, { "Trailer", "irc.request.trailer", FT_STRING, STR_ASCII,
504           NULL, 0x0, "Request trailer", HFILL }},
505 
506         { &hf_irc_response_prefix, { "Prefix", "irc.response.prefix", FT_STRING, STR_ASCII,
507           NULL, 0x0, "Response prefix", HFILL }},
508 
509         { &hf_irc_response_command, { "Command", "irc.response.command", FT_STRING, STR_ASCII,
510           NULL, 0x0, "Response command", HFILL }},
511 
512         { &hf_irc_response_num_command, { "Command", "irc.response.num_command", FT_UINT16, BASE_DEC,
513           NULL, 0x0, "Response (numeric) command", HFILL }},
514 
515         { &hf_irc_response_command_param, { "Parameter", "irc.response.command_parameter", FT_STRING, STR_ASCII,
516           NULL, 0x0, "Response command parameter", HFILL }},
517 
518         { &hf_irc_response_trailer, { "Trailer", "irc.response.trailer", FT_STRING, STR_ASCII,
519           NULL, 0x0, "Response trailer", HFILL }},
520 
521         { &hf_irc_ctcp, { "CTCP Data", "irc.ctcp", FT_STRING, STR_ASCII,
522           NULL, 0x0, "Placeholder to dissect CTCP data", HFILL }}
523     };
524 
525     static gint *ett[] = {
526         &ett_irc,
527         &ett_irc_request,
528         &ett_irc_request_command,
529         &ett_irc_response,
530         &ett_irc_response_command
531     };
532 
533     static ei_register_info ei[] = {
534         { &ei_irc_missing_end_delimiter, { "irc.missing_end_delimiter", PI_MALFORMED, PI_ERROR, "Missing ending tag delimiter (0x01)", EXPFILL }},
535         { &ei_irc_tag_data_invalid, { "irc.tag_data_invalid", PI_PROTOCOL, PI_WARN, "Tag data outside of NOTICE or PRIVMSG command", EXPFILL }},
536         { &ei_irc_prefix_missing_ending_space, { "irc.prefix_missing_ending_space", PI_MALFORMED, PI_ERROR, "Prefix missing ending <space>", EXPFILL }},
537         { &ei_irc_request_command, { "irc.request.command.missing", PI_MALFORMED, PI_ERROR, "Request has no command", EXPFILL }},
538         { &ei_irc_numeric_request_command, { "irc.request.command.numeric", PI_PROTOCOL, PI_WARN, "Numeric command not allowed in request", EXPFILL }},
539         { &ei_irc_response_command, { "irc.response.command.missing", PI_MALFORMED, PI_ERROR, "Response has no command", EXPFILL }},
540     };
541 
542     expert_module_t* expert_irc;
543 
544     proto_irc = proto_register_protocol("Internet Relay Chat", "IRC", "irc");
545     proto_register_field_array(proto_irc, hf, array_length(hf));
546     proto_register_subtree_array(ett, array_length(ett));
547     expert_irc = expert_register_protocol(proto_irc);
548     expert_register_field_array(expert_irc, ei, array_length(ei));
549 
550     /* compile patterns */
551     ws_mempbrk_compile(&pbrk_tag_delimiter, TAG_DELIMITER);
552 }
553 
554 void
proto_reg_handoff_irc(void)555 proto_reg_handoff_irc(void)
556 {
557     dissector_handle_t irc_handle;
558 
559     irc_handle = create_dissector_handle(dissect_irc, proto_irc);
560     dissector_add_uint_range_with_preference("tcp.port", TCP_PORT_RANGE, irc_handle);
561 }
562 
563 /*
564  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
565  *
566  * Local variables:
567  * c-basic-offset: 4
568  * tab-width: 8
569  * indent-tabs-mode: nil
570  * End:
571  *
572  * vi: set shiftwidth=4 tabstop=8 expandtab:
573  * :indentSize=4:tabSize=8:noTabs=true:
574  */
575