Coverage Report

Created: 2026-07-12 07:10

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
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
 */