Coverage Report

Created: 2026-08-14 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
 */