Coverage Report

Created: 2025-02-15 06:25

/src/wireshark/epan/dissectors/packet-gelf.c
Line
Count
Source (jump to first uncovered line)
1
/* packet-gelf.c
2
 * Routines for Graylog Extended Log Format (GELF) dissection
3
 *
4
 * Slava Bacherikov <slava@bacher09.org>
5
 *
6
 * Wireshark - Network traffic analyzer
7
 * By Gerald Combs <gerald@wireshark.org>
8
 * Copyright 1998
9
 *
10
 * SPDX-License-Identifier: GPL-2.0-or-later
11
 */
12
13
#include "config.h"
14
15
#include <epan/packet.h>
16
#include <epan/expert.h>
17
#include <epan/to_str.h>
18
#include <epan/reassemble.h>
19
20
0
#define HEADER_GZIP 0x1f8b
21
0
#define HEADER_CHUNKED 0x1e0f
22
/* not sure if this really used
23
seen this here: https://github.com/lusis/gelfd/blob/229cf5f1f913a35db648b195300d1aaae841d522/lib/gelfd.rb#L7 */
24
0
#define HEADER_UNCOMPRESSED 0x1f3c
25
0
#define HEADER_UNCOMPRESSED_PLAIN 0x7b22 // json payload without real header
26
27
/* minimal size of json message with only required fields */
28
0
#define MIN_PLAIN_MSG 48
29
0
#define MIN_ZLIB_MSG  46
30
31
/* make 32 bit message id from 64 bit message id */
32
0
#define BUILD_MESSAGE_ID(X) ((X[3] << 3 | X[2] << 2 | X[1] << 1 | X[0]) ^ \
33
0
                            (X[4] << 3 | X[5] << 2 | X[6] << 1 | X[7]))
34
35
36
void proto_register_gelf(void);
37
void proto_reg_handoff_gelf(void);
38
39
static dissector_handle_t json_handle;
40
static int proto_gelf;
41
static dissector_handle_t gelf_udp_handle;
42
43
static int ett_gelf;
44
static int hf_gelf_pdu_type;
45
static int hf_gelf_pdu_message_id;
46
static int hf_gelf_pdu_chunk_number;
47
static int hf_gelf_pdu_chunk_count;
48
static int hf_gelf_pdu_chunked;
49
50
static const value_string gelf_udp_types[] = {
51
    { HEADER_GZIP, "gzip" },
52
    { 0x7801, "zlib" },
53
    { 0x785e, "zlib" },
54
    { 0x789c, "zlib" },
55
    { 0x78da, "zlib" },
56
    { HEADER_CHUNKED, "chunked" },
57
    { HEADER_UNCOMPRESSED, "uncompressed" },
58
    { HEADER_UNCOMPRESSED_PLAIN, "uncompressed plain json" },
59
    { 0, NULL }
60
};
61
62
static reassembly_table gelf_udp_reassembly_table;
63
64
static int ett_gelf_fragment;
65
static int ett_gelf_fragments;
66
67
static int hf_gelf_fragments;
68
static int hf_gelf_fragment;
69
static int hf_gelf_fragment_overlap;
70
static int hf_gelf_fragment_overlap_conflict;
71
static int hf_gelf_fragment_multiple_tails;
72
static int hf_gelf_fragment_too_long_fragment;
73
static int hf_gelf_fragment_error;
74
static int hf_gelf_fragment_count;
75
static int hf_gelf_reassembled_in;
76
static int hf_gelf_reassembled_length;
77
78
static const fragment_items gelf_fragment_items = {
79
    &ett_gelf_fragment,
80
    &ett_gelf_fragments,
81
    &hf_gelf_fragments,
82
    &hf_gelf_fragment,
83
    &hf_gelf_fragment_overlap,
84
    &hf_gelf_fragment_overlap_conflict,
85
    &hf_gelf_fragment_multiple_tails,
86
    &hf_gelf_fragment_too_long_fragment,
87
    &hf_gelf_fragment_error,
88
    &hf_gelf_fragment_count,
89
    &hf_gelf_reassembled_in,
90
    &hf_gelf_reassembled_length,
91
    NULL,
92
    "GELF fragments"
93
};
94
95
static expert_field ei_gelf_invalid_header;
96
static expert_field ei_gelf_broken_compression;
97
98
static inline bool
99
0
is_simple_zlib(uint16_t header) {
100
0
    return header == 0x7801 || header == 0x785e || header == 0x789c || header == 0x78da;
101
0
}
102
103
static int
104
dissect_gelf_simple_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, uint16_t header,
105
                        proto_item* pdu_item)
106
0
{
107
0
    int len;
108
0
    tvbuff_t *next_tvb;
109
110
0
    len = tvb_captured_length(tvb);
111
0
    if (header == HEADER_GZIP || is_simple_zlib(header)) {
112
0
        next_tvb = tvb_child_uncompress_zlib(tvb, tvb, 0, len);
113
0
        if (next_tvb) {
114
0
            add_new_data_source(pinfo, next_tvb, "compressed data");
115
0
            call_dissector(json_handle, next_tvb, pinfo, tree);
116
0
        } else {
117
0
            expert_add_info_format(pinfo, pdu_item, &ei_gelf_broken_compression,
118
0
                                   "Can't uncompress message");
119
0
        }
120
0
        return len;
121
0
    } else if (header == HEADER_UNCOMPRESSED) {
122
0
        next_tvb = tvb_new_subset_remaining(tvb, 2);
123
0
        if (next_tvb) {
124
0
            call_dissector(json_handle, next_tvb, pinfo, tree);
125
0
        }
126
0
        return len;
127
0
    } else if (header == HEADER_UNCOMPRESSED_PLAIN) {
128
0
        if (call_dissector(json_handle, tvb, pinfo, tree) == 0) {
129
0
            return 0;
130
0
        }
131
0
        return len;
132
0
    }
133
0
    return 0;
134
0
}
135
136
static int
137
dissect_gelf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, bool heur_check)
138
0
{
139
0
    uint16_t header;
140
0
    unsigned captured_length;
141
0
    proto_item *it;
142
143
0
    captured_length = tvb_captured_length(tvb);
144
145
0
    if (captured_length < 2)
146
0
        return 0;
147
148
0
    header = tvb_get_ntohs(tvb, 0);
149
150
0
    if (heur_check) {
151
0
        unsigned min_len;
152
0
        uint8_t number, count;
153
154
0
        switch(header) {
155
0
            case HEADER_GZIP:
156
0
                min_len = MIN_ZLIB_MSG;
157
0
                break;
158
0
            case HEADER_UNCOMPRESSED_PLAIN:
159
0
                min_len = MIN_PLAIN_MSG;
160
0
                break;
161
0
            case HEADER_UNCOMPRESSED:
162
0
                min_len = MIN_PLAIN_MSG + 2;
163
0
                break;
164
0
            case HEADER_CHUNKED:
165
                /* 10 bytes is chunked header + 2 bytes of data */
166
0
                min_len = 10 + 2;
167
0
                break;
168
0
            default:
169
0
                if (is_simple_zlib(header)) {
170
0
                    min_len = MIN_ZLIB_MSG;
171
0
                } else {
172
0
                    return 0;
173
0
                }
174
0
                break;
175
0
        }
176
177
0
        if (tvb_reported_length(tvb) < min_len)
178
0
            return 0;
179
180
0
        if (header == HEADER_CHUNKED && captured_length >= 10) {
181
0
            number = tvb_get_uint8(tvb, 10);
182
0
            count = tvb_get_uint8(tvb, 11);
183
0
            if (number >= count)
184
0
                return 0;
185
0
        }
186
0
    }
187
188
189
0
    proto_item *ti = proto_tree_add_item(tree, proto_gelf, tvb, 0, -1, ENC_NA);
190
0
    proto_tree *gelf_tree = proto_item_add_subtree(ti, ett_gelf);
191
0
    proto_item *pdu_item = proto_tree_add_item(gelf_tree, hf_gelf_pdu_type, tvb, 0, 2, ENC_BIG_ENDIAN);
192
0
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "GELF");
193
194
0
    if (header == HEADER_CHUNKED) {
195
0
        uint32_t number, count, short_id, data_len;
196
0
        GByteArray *bytes;
197
0
        char message_id[17];
198
0
        bool more_frags;
199
0
        fragment_head *fd_head;
200
201
0
        message_id[0] = '\0';
202
0
        bytes = g_byte_array_sized_new(8);
203
204
0
        it = proto_tree_add_boolean(gelf_tree, hf_gelf_pdu_chunked, tvb, 0, 2, true);
205
0
        proto_item_set_generated(it);
206
0
        proto_tree_add_bytes_item(gelf_tree, hf_gelf_pdu_message_id, tvb, 2, 8, ENC_BIG_ENDIAN, bytes,
207
0
                                  NULL, NULL);
208
0
        proto_tree_add_item_ret_uint(gelf_tree, hf_gelf_pdu_chunk_number, tvb, 10, 1, ENC_BIG_ENDIAN,
209
0
                                     &number);
210
0
        proto_tree_add_item_ret_uint(gelf_tree, hf_gelf_pdu_chunk_count, tvb, 11, 1, ENC_BIG_ENDIAN,
211
0
                                     &count);
212
0
        bytes_to_hexstr(message_id, bytes->data, 8);
213
0
        message_id[16] = '\0';
214
        // HACK: convert 64 bit message id to 32 bit :)
215
0
        short_id = BUILD_MESSAGE_ID(bytes->data);
216
0
        g_byte_array_free(bytes, true);
217
0
        col_add_fstr(pinfo->cinfo, COL_INFO, "Chunked packet: id: %s, number %u, count %u", message_id,
218
0
                     number, count);
219
0
        data_len = tvb_captured_length_remaining(tvb, 12);
220
0
        more_frags = (count == number + 1) ? false : true;
221
0
        fd_head = fragment_add_seq_check(&gelf_udp_reassembly_table, tvb, 12, pinfo, short_id, NULL, number,
222
0
                                         data_len, more_frags);
223
0
        if (fd_head != NULL) {
224
0
            tvbuff_t *newtvb;
225
0
            newtvb = process_reassembled_data(tvb, 12, pinfo, "Reassembled GELF", fd_head,
226
0
                                              &gelf_fragment_items, NULL, gelf_tree);
227
0
            if (newtvb != NULL) {
228
0
                uint16_t newheader = tvb_get_ntohs(newtvb, 0);
229
0
                dissect_gelf_simple_udp(newtvb, pinfo, tree, newheader, pdu_item);
230
0
           }
231
0
        }
232
0
        return captured_length;
233
0
    } else {
234
0
        it = proto_tree_add_boolean(gelf_tree, hf_gelf_pdu_chunked, tvb, 0, 2, false);
235
0
        proto_item_set_generated(it);
236
237
0
        switch(header) {
238
0
            case HEADER_GZIP:
239
0
                col_set_str(pinfo->cinfo, COL_INFO, "GZIP");
240
0
                break;
241
0
            case HEADER_UNCOMPRESSED_PLAIN:
242
0
                col_set_str(pinfo->cinfo, COL_INFO, "uncompressed plain");
243
0
                break;
244
0
            case HEADER_UNCOMPRESSED:
245
0
                col_set_str(pinfo->cinfo, COL_INFO, "uncompressed");
246
0
                break;
247
0
            default:
248
0
                if (is_simple_zlib(header)) {
249
0
                    col_set_str(pinfo->cinfo, COL_INFO, "ZLIB");
250
0
                } else {
251
0
                    expert_add_info_format(pinfo, pdu_item, &ei_gelf_invalid_header,
252
0
                                           "Invalid header magic");
253
0
                    return 0;
254
0
                }
255
0
                break;
256
0
        }
257
258
0
        return dissect_gelf_simple_udp(tvb, pinfo, tree, header, pdu_item);
259
0
    }
260
0
}
261
262
static int
263
0
dissect_gelf_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
264
0
    return dissect_gelf(tvb, pinfo, tree, false);
265
0
}
266
267
static bool
268
dissect_gelf_heur_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
269
0
{
270
0
    if (dissect_gelf(tvb, pinfo, tree, true) > 0) {
271
0
        return true;
272
0
    } else {
273
0
        return false;
274
0
    }
275
0
}
276
277
void
278
proto_register_gelf(void)
279
14
{
280
14
    static  hf_register_info hf[] = {
281
14
        { &hf_gelf_pdu_type,
282
14
            {
283
14
                "GELF Type", "gelf.type", FT_UINT16,
284
14
                BASE_HEX, VALS(gelf_udp_types), 0x0,
285
14
                NULL, HFILL
286
14
            }
287
14
        },
288
14
        { &hf_gelf_pdu_message_id,
289
14
            {
290
14
                "Message id", "gelf.chunk.msg_id", FT_BYTES,
291
14
                BASE_NONE, NULL, 0x0,
292
14
                NULL, HFILL
293
14
            }
294
14
        },
295
14
        { &hf_gelf_pdu_chunk_number,
296
14
            {
297
14
                "Chunk number", "gelf.chunk.number", FT_UINT8,
298
14
                BASE_DEC, NULL, 0x0,
299
14
                NULL, HFILL
300
14
            }
301
14
        },
302
14
        { &hf_gelf_pdu_chunk_count,
303
14
            {
304
14
                "Chunk count", "gelf.chunk.count", FT_UINT8,
305
14
                BASE_DEC, NULL, 0x0,
306
14
                NULL, HFILL
307
14
            }
308
14
        },
309
14
        { &hf_gelf_pdu_chunked,
310
14
            {
311
14
                "Chunked message", "gelf.chunked", FT_BOOLEAN,
312
14
                BASE_NONE, NULL, 0x0,
313
14
                NULL, HFILL
314
14
            }
315
14
        }
316
14
        /* Fragmentation */,
317
14
        { &hf_gelf_fragments,
318
14
            {
319
14
                "GELF fragments", "gelf.fragments", FT_NONE, BASE_NONE,
320
14
                NULL, 0x00, NULL, HFILL
321
14
            }
322
14
        },
323
14
        { &hf_gelf_fragment,
324
14
            {
325
14
                "GELF fragment", "gelf.fragment", FT_FRAMENUM, BASE_NONE,
326
14
                NULL, 0x00, NULL, HFILL
327
14
            }
328
14
        },
329
14
        { &hf_gelf_fragment_overlap,
330
14
            {
331
14
                "GELF fragment overlap", "gelf.fragment.overlap", FT_BOOLEAN,
332
14
                BASE_NONE, NULL, 0x00, NULL, HFILL
333
14
            }
334
14
        },
335
14
        { &hf_gelf_fragment_overlap_conflict,
336
14
            {
337
14
                "GELF fragment overlapping with conflicting data",
338
14
                "gelf.fragment.overlap.conflicts", FT_BOOLEAN, BASE_NONE,
339
14
                NULL, 0x00, NULL, HFILL
340
14
            }
341
14
        },
342
14
        { &hf_gelf_fragment_multiple_tails,
343
14
            {
344
14
                "GELF has multiple tail fragments",
345
14
                "gelf.fragment.multiple_tails", FT_BOOLEAN, BASE_NONE,
346
14
                NULL, 0x00, NULL, HFILL
347
14
            }
348
14
        },
349
14
        { &hf_gelf_fragment_too_long_fragment,
350
14
            {
351
14
                "GELF fragment too long", "gelf.fragment.too_long_fragment",
352
14
                FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL
353
14
            }
354
14
        },
355
14
        { &hf_gelf_fragment_error,
356
14
            {
357
14
                "GELF defragmentation error", "gelf.fragment.error", FT_FRAMENUM,
358
14
                BASE_NONE, NULL, 0x00, NULL, HFILL
359
14
            }
360
14
        },
361
14
        { &hf_gelf_fragment_count,
362
14
            {
363
14
                "GELF fragment count", "gelf.fragment.count", FT_UINT32, BASE_DEC,
364
14
                NULL, 0x00, NULL, HFILL
365
14
            }
366
14
        },
367
14
        { &hf_gelf_reassembled_in,
368
14
            {
369
14
                "Reassembled GELF in frame", "gelf.reassembled.in", FT_FRAMENUM, BASE_NONE,
370
14
                NULL, 0x00, "This GELF packet is reassembled in this frame", HFILL
371
14
            }
372
14
        },
373
14
        { &hf_gelf_reassembled_length,
374
14
            {
375
14
                "Reassembled GELF length", "gelf.reassembled.length", FT_UINT32, BASE_DEC,
376
14
                NULL, 0x00, "The total length of the reassembled payload", HFILL
377
14
            }
378
14
        },
379
14
    };
380
381
14
    static ei_register_info ei_gelf[] = {
382
14
        { &ei_gelf_invalid_header,
383
14
            {
384
14
                "gelf.invalid_header", PI_MALFORMED, PI_ERROR, "Invalid header", EXPFILL
385
14
            }
386
14
        },
387
14
        { &ei_gelf_broken_compression,
388
14
            {
389
14
                "gelf.broken_compression", PI_MALFORMED, PI_ERROR, "Can't unpack message", EXPFILL
390
14
            }
391
14
        }
392
14
    };
393
394
14
    static int *ett[] = {
395
14
        &ett_gelf,
396
14
        &ett_gelf_fragment,
397
14
        &ett_gelf_fragments
398
14
    };
399
400
14
    expert_module_t *expert_gelf;
401
402
14
    proto_gelf = proto_register_protocol("Graylog Extended Log Format", "GELF", "gelf");
403
14
    gelf_udp_handle = register_dissector("gelf-udp", dissect_gelf_udp, proto_gelf);
404
14
    proto_register_field_array(proto_gelf, hf, array_length(hf));
405
14
    proto_register_subtree_array(ett, array_length(ett));
406
14
    expert_gelf = expert_register_protocol(proto_gelf);
407
14
    expert_register_field_array(expert_gelf, ei_gelf, array_length(ei_gelf));
408
14
    reassembly_table_register(&gelf_udp_reassembly_table, &addresses_reassembly_table_functions);
409
14
}
410
411
412
void
413
proto_reg_handoff_gelf(void)
414
14
{
415
14
    dissector_add_for_decode_as("udp.port", gelf_udp_handle);
416
14
    heur_dissector_add("udp", dissect_gelf_heur_udp,  "GELF over UDP", "gelf_udp", proto_gelf,
417
14
                       HEURISTIC_DISABLE);
418
14
    json_handle = find_dissector_add_dependency("json", proto_gelf);
419
14
}
420
421
/*
422
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
423
 *
424
 * Local variables:
425
 * c-basic-offset: 4
426
 * tab-width: 8
427
 * indent-tabs-mode: nil
428
 * End:
429
 *
430
 * vi: set shiftwidth=4 tabstop=8 expandtab:
431
 * :indentSize=4:tabSize=8:noTabs=true:
432
 */