Coverage Report

Created: 2025-02-15 06:25

/src/wireshark/epan/dissectors/packet-cups.c
Line
Count
Source (jump to first uncovered line)
1
/* packet-cups.c
2
 * Routines for Common Unix Printing System (CUPS) Browsing Protocol
3
 * packet disassembly for the Wireshark network traffic analyzer.
4
 *
5
 * Charles Levert <charles@comm.polymtl.ca>
6
 * Copyright 2001 Charles Levert
7
 *
8
 * SPDX-License-Identifier: GPL-2.0-or-later
9
 *
10
 */
11
12
#include "config.h"
13
14
#include <epan/packet.h>
15
#include <epan/tfs.h>
16
#include <wsutil/str_util.h>
17
18
/**********************************************************************/
19
20
void proto_register_cups(void);
21
void proto_reg_handoff_cups(void);
22
23
static dissector_handle_t cups_handle;
24
25
/* From cups/cups.h, GNU GPL, Copyright 1997-2001 by Easy Software Products. */
26
typedef uint32_t cups_ptype_t;           /**** Printer Type/Capability Bits ****/
27
enum                                    /* Not a typedef'd enum so we can OR */
28
{
29
    CUPS_PRINTER_LOCAL = 0x0000,          /* Local printer or class */
30
    CUPS_PRINTER_CLASS = 0x0001,          /* Printer class */
31
    CUPS_PRINTER_REMOTE = 0x0002,         /* Remote printer or class */
32
    CUPS_PRINTER_BW = 0x0004,             /* Can do B&W printing */
33
    CUPS_PRINTER_COLOR = 0x0008,          /* Can do color printing */
34
    CUPS_PRINTER_DUPLEX = 0x0010,         /* Can do duplexing */
35
    CUPS_PRINTER_STAPLE = 0x0020,         /* Can staple output */
36
    CUPS_PRINTER_COPIES = 0x0040,         /* Can do copies */
37
    CUPS_PRINTER_COLLATE = 0x0080,        /* Can collage copies */
38
    CUPS_PRINTER_PUNCH = 0x0100,          /* Can punch output */
39
    CUPS_PRINTER_COVER = 0x0200,          /* Can cover output */
40
    CUPS_PRINTER_BIND = 0x0400,           /* Can bind output */
41
    CUPS_PRINTER_SORT = 0x0800,           /* Can sort output */
42
    CUPS_PRINTER_SMALL = 0x1000,          /* Can do Letter/Legal/A4 */
43
    CUPS_PRINTER_MEDIUM = 0x2000,         /* Can do Tabloid/B/C/A3/A2 */
44
    CUPS_PRINTER_LARGE = 0x4000,          /* Can do D/E/A1/A0 */
45
    CUPS_PRINTER_VARIABLE = 0x8000,       /* Can do variable sizes */
46
    CUPS_PRINTER_IMPLICIT = 0x10000,      /* Implicit class */
47
    CUPS_PRINTER_DEFAULT = 0x20000,       /* Default printer on network */
48
    CUPS_PRINTER_OPTIONS = 0xfffc         /* ~(CLASS | REMOTE | IMPLICIT) */
49
};
50
/* End insert from cups/cups.h */
51
52
typedef enum _cups_state {
53
    CUPS_IDLE = 3,
54
    CUPS_PROCESSING,
55
    CUPS_STOPPED
56
} cups_state_t;
57
58
static const value_string cups_state_values[] = {
59
    { CUPS_IDLE,       "idle" },
60
    { CUPS_PROCESSING, "processing" },
61
    { CUPS_STOPPED,    "stopped" },
62
    { 0,               NULL }
63
};
64
65
static const true_false_string tfs_implicit_explicit = { "Implicit class", "Explicit class" };
66
static const true_false_string tfs_printer_class = { "Printer class", "Single printer" };
67
68
static int proto_cups;
69
static int hf_cups_ptype;
70
static int hf_cups_ptype_default;
71
static int hf_cups_ptype_implicit;
72
static int hf_cups_ptype_variable;
73
static int hf_cups_ptype_large;
74
static int hf_cups_ptype_medium;
75
static int hf_cups_ptype_small;
76
static int hf_cups_ptype_sort;
77
static int hf_cups_ptype_bind;
78
static int hf_cups_ptype_cover;
79
static int hf_cups_ptype_punch;
80
static int hf_cups_ptype_collate;
81
static int hf_cups_ptype_copies;
82
static int hf_cups_ptype_staple;
83
static int hf_cups_ptype_duplex;
84
static int hf_cups_ptype_color;
85
static int hf_cups_ptype_bw;
86
static int hf_cups_ptype_remote;
87
static int hf_cups_ptype_class;
88
static int hf_cups_state;
89
static int hf_cups_uri;
90
static int hf_cups_location;
91
static int hf_cups_information;
92
static int hf_cups_make_model;
93
94
static int ett_cups;
95
static int ett_cups_ptype;
96
97
/* patterns used for tvb_ws_mempbrk_pattern_uint8 */
98
static ws_mempbrk_pattern pbrk_whitespace;
99
100
/* This protocol is heavily related to IPP, but it is CUPS-specific
101
   and non-standard. */
102
14
#define UDP_PORT_CUPS  631
103
7
#define PROTO_TAG_CUPS "CUPS"
104
105
static unsigned get_hex_uint(tvbuff_t *tvb, int offset, int *next_offset);
106
static bool skip_space(tvbuff_t *tvb, int offset, int *next_offset);
107
static const uint8_t* get_quoted_string(wmem_allocator_t *scope, tvbuff_t *tvb, int offset,
108
    int *next_offset, unsigned *len);
109
static const uint8_t* get_unquoted_string(wmem_allocator_t *scope, tvbuff_t *tvb, int offset,
110
    int *next_offset, unsigned *len);
111
112
/**********************************************************************/
113
114
static int
115
dissect_cups(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
116
7
{
117
7
    proto_tree   *cups_tree = NULL;
118
7
    proto_tree   *ptype_subtree = NULL;
119
7
    proto_item   *ti = NULL;
120
7
    int           offset = 0;
121
7
    int           next_offset;
122
7
    unsigned      len;
123
7
    const uint8_t *str;
124
7
    cups_ptype_t  ptype;
125
7
    unsigned int  state;
126
127
7
    col_set_str(pinfo->cinfo, COL_PROTOCOL, PROTO_TAG_CUPS);
128
7
    col_clear(pinfo->cinfo, COL_INFO);
129
130
7
    ti = proto_tree_add_item(tree, proto_cups, tvb, offset, -1, ENC_NA);
131
7
    cups_tree = proto_item_add_subtree(ti, ett_cups);
132
133
    /* Format (1450 bytes max.):  */
134
    /* type state uri ["location" ["info" ["make-and-model"]]]\n */
135
136
7
    ptype = get_hex_uint(tvb, offset, &next_offset);
137
7
    len = next_offset - offset;
138
7
    if (len != 0) {
139
1
        ti = proto_tree_add_uint(cups_tree, hf_cups_ptype, tvb, offset, len, ptype);
140
1
        ptype_subtree = proto_item_add_subtree(ti, ett_cups_ptype);
141
1
        proto_tree_add_item(ptype_subtree, hf_cups_ptype_default, tvb, offset, len, ENC_BIG_ENDIAN);
142
1
        proto_tree_add_item(ptype_subtree, hf_cups_ptype_implicit, tvb, offset, len, ENC_BIG_ENDIAN);
143
1
        proto_tree_add_item(ptype_subtree, hf_cups_ptype_variable, tvb, offset, len, ENC_BIG_ENDIAN);
144
1
        proto_tree_add_item(ptype_subtree, hf_cups_ptype_large, tvb, offset, len, ENC_BIG_ENDIAN);
145
1
        proto_tree_add_item(ptype_subtree, hf_cups_ptype_medium, tvb, offset, len, ENC_BIG_ENDIAN);
146
1
        proto_tree_add_item(ptype_subtree, hf_cups_ptype_small, tvb, offset, len, ENC_BIG_ENDIAN);
147
1
        proto_tree_add_item(ptype_subtree, hf_cups_ptype_sort, tvb, offset, len, ENC_BIG_ENDIAN);
148
1
        proto_tree_add_item(ptype_subtree, hf_cups_ptype_bind, tvb, offset, len, ENC_BIG_ENDIAN);
149
1
        proto_tree_add_item(ptype_subtree, hf_cups_ptype_cover, tvb, offset, len, ENC_BIG_ENDIAN);
150
1
        proto_tree_add_item(ptype_subtree, hf_cups_ptype_punch, tvb, offset, len, ENC_BIG_ENDIAN);
151
1
        proto_tree_add_item(ptype_subtree, hf_cups_ptype_collate, tvb, offset, len, ENC_BIG_ENDIAN);
152
1
        proto_tree_add_item(ptype_subtree, hf_cups_ptype_copies, tvb, offset, len, ENC_BIG_ENDIAN);
153
1
        proto_tree_add_item(ptype_subtree, hf_cups_ptype_staple, tvb, offset, len, ENC_BIG_ENDIAN);
154
1
        proto_tree_add_item(ptype_subtree, hf_cups_ptype_duplex, tvb, offset, len, ENC_BIG_ENDIAN);
155
1
        proto_tree_add_item(ptype_subtree, hf_cups_ptype_color, tvb, offset, len, ENC_BIG_ENDIAN);
156
1
        proto_tree_add_item(ptype_subtree, hf_cups_ptype_bw, tvb, offset, len, ENC_BIG_ENDIAN);
157
1
        proto_tree_add_item(ptype_subtree, hf_cups_ptype_remote, tvb, offset, len, ENC_BIG_ENDIAN);
158
1
        proto_tree_add_item(ptype_subtree, hf_cups_ptype_class, tvb, offset, len, ENC_BIG_ENDIAN);
159
1
    }
160
7
    offset = next_offset;
161
162
7
    if (!skip_space(tvb, offset, &next_offset))
163
1
        return offset;    /* end of packet */
164
6
    offset = next_offset;
165
166
6
    state = get_hex_uint(tvb, offset, &next_offset);
167
6
    len = next_offset - offset;
168
6
    if (len != 0) {
169
0
        proto_tree_add_uint(cups_tree, hf_cups_state, tvb, offset, len, state);
170
0
    }
171
6
    offset = next_offset;
172
173
6
    if (!skip_space(tvb, offset, &next_offset))
174
0
        return offset;    /* end of packet */
175
6
    offset = next_offset;
176
177
6
    str = get_unquoted_string(pinfo->pool, tvb, offset, &next_offset, &len);
178
6
    if (str == NULL)
179
3
        return offset;    /* separator/terminator not found */
180
181
3
    proto_tree_add_string(cups_tree, hf_cups_uri, tvb, offset, len, str);
182
3
    col_add_fstr(pinfo->cinfo, COL_INFO, "%s (%s)",
183
3
            str, val_to_str(state, cups_state_values, "0x%x"));
184
3
    offset = next_offset;
185
186
3
    if (!cups_tree)
187
0
        return offset;
188
189
3
    if (!skip_space(tvb, offset, &next_offset))
190
2
        return offset;    /* end of packet */
191
1
    offset = next_offset;
192
193
1
    str = get_quoted_string(pinfo->pool, tvb, offset, &next_offset, &len);
194
1
    if (str == NULL)
195
1
        return offset;    /* separator/terminator not found */
196
0
    proto_tree_add_string(cups_tree, hf_cups_location, tvb, offset+1, len, str);
197
0
    offset = next_offset;
198
199
0
    if (!skip_space(tvb, offset, &next_offset))
200
0
        return offset;    /* end of packet */
201
0
    offset = next_offset;
202
203
0
    str = get_quoted_string(pinfo->pool, tvb, offset, &next_offset, &len);
204
0
    if (str == NULL)
205
0
        return offset;    /* separator/terminator not found */
206
0
    proto_tree_add_string(cups_tree, hf_cups_information, tvb, offset+1, len, str);
207
0
    offset = next_offset;
208
209
0
    if (!skip_space(tvb, offset, &next_offset))
210
0
        return offset;    /* end of packet */
211
0
    offset = next_offset;
212
213
0
    str = get_quoted_string(pinfo->pool, tvb, offset, &next_offset, &len);
214
0
    if (str == NULL)
215
0
        return offset;    /* separator/terminator not found */
216
0
    proto_tree_add_string(cups_tree, hf_cups_make_model, tvb, offset+1, len, str);
217
218
0
    return next_offset;
219
0
}
220
221
static unsigned
222
get_hex_uint(tvbuff_t *tvb, int offset, int *next_offset)
223
13
{
224
13
    int c;
225
13
    unsigned u = 0;
226
227
13
    while (g_ascii_isxdigit(c = tvb_get_uint8(tvb, offset))) {
228
1
        u = 16*u + ws_xton(c);
229
230
1
        offset++;
231
1
    }
232
233
13
    *next_offset = offset;
234
235
13
    return u;
236
13
}
237
238
static bool
239
skip_space(tvbuff_t *tvb, int offset, int *next_offset)
240
16
{
241
16
    int c;
242
243
17
    while ((c = tvb_get_uint8(tvb, offset)) == ' ')
244
1
        offset++;
245
16
    if (c == '\r' || c == '\n')
246
3
        return false;    /* end of packet */
247
248
13
    *next_offset = offset;
249
250
13
    return true;
251
16
}
252
253
static const uint8_t*
254
get_quoted_string(wmem_allocator_t *scope, tvbuff_t *tvb, int offset, int *next_offset, unsigned *len)
255
1
{
256
1
    int c;
257
1
    const uint8_t* s = NULL;
258
1
    unsigned l = 0;
259
1
    int o;
260
261
1
    c = tvb_get_uint8(tvb, offset);
262
1
    if (c == '"') {
263
0
        o = tvb_find_uint8(tvb, offset+1, -1, '"');
264
0
        if (o != -1) {
265
0
            offset++;
266
0
            l = o - offset;
267
0
            s = tvb_get_string_enc(scope, tvb, offset, l, ENC_UTF_8);
268
0
            offset = o + 1;
269
0
        }
270
0
    }
271
272
1
    *next_offset = offset;
273
1
    *len = l;
274
275
1
    return s;
276
1
}
277
278
static const uint8_t*
279
get_unquoted_string(wmem_allocator_t *scope, tvbuff_t *tvb, int offset, int *next_offset, unsigned *len)
280
6
{
281
6
    const uint8_t* s = NULL;
282
6
    unsigned l = 0;
283
6
    int o;
284
285
6
    o = tvb_ws_mempbrk_pattern_uint8(tvb, offset, -1, &pbrk_whitespace, NULL);
286
6
    if (o != -1) {
287
3
        l = o - offset;
288
3
        s = tvb_get_string_enc(scope, tvb, offset, l, ENC_UTF_8);
289
3
        offset = o;
290
3
    }
291
292
6
    *next_offset = offset;
293
6
    *len = l;
294
295
6
    return s;
296
6
}
297
298
/**********************************************************************/
299
300
void
301
proto_register_cups(void)
302
14
{
303
14
    static hf_register_info hf[] = {
304
14
        { &hf_cups_ptype,
305
14
            { "Type",     "cups.ptype", FT_UINT32, BASE_HEX,
306
14
              NULL, 0x0, NULL, HFILL }},
307
14
        { &hf_cups_ptype_default,
308
14
            { "Default printer on network", "cups.ptype.default", FT_BOOLEAN, 32,
309
14
                TFS(&tfs_yes_no), CUPS_PRINTER_DEFAULT, NULL, HFILL }},
310
14
        { &hf_cups_ptype_implicit,
311
14
            { "Class", "cups.ptype.implicit", FT_BOOLEAN, 32,
312
14
                TFS(&tfs_implicit_explicit), CUPS_PRINTER_IMPLICIT, NULL, HFILL }},
313
14
        { &hf_cups_ptype_variable,
314
14
            { "Can print variable sizes", "cups.ptype.variable", FT_BOOLEAN, 32,
315
14
                TFS(&tfs_yes_no), CUPS_PRINTER_VARIABLE, NULL, HFILL }},
316
14
        { &hf_cups_ptype_large,
317
14
            { "Can print up to 36x48 inches", "cups.ptype.large", FT_BOOLEAN, 32,
318
14
                TFS(&tfs_yes_no), CUPS_PRINTER_LARGE, NULL, HFILL }},
319
14
        { &hf_cups_ptype_medium,
320
14
            { "Can print up to 18x24 inches", "cups.ptype.medium", FT_BOOLEAN, 32,
321
14
                TFS(&tfs_yes_no), CUPS_PRINTER_MEDIUM, NULL, HFILL }},
322
14
        { &hf_cups_ptype_small,
323
14
            { "Can print up to 9x14 inches", "cups.ptype.small", FT_BOOLEAN, 32,
324
14
                TFS(&tfs_yes_no), CUPS_PRINTER_SMALL, NULL, HFILL }},
325
14
        { &hf_cups_ptype_sort,
326
14
            { "Can sort", "cups.ptype.sort", FT_BOOLEAN, 32,
327
14
                TFS(&tfs_yes_no), CUPS_PRINTER_SORT, NULL, HFILL }},
328
14
        { &hf_cups_ptype_bind,
329
14
            { "Can bind", "cups.ptype.bind", FT_BOOLEAN, 32,
330
14
                TFS(&tfs_yes_no), CUPS_PRINTER_BIND, NULL, HFILL }},
331
14
        { &hf_cups_ptype_cover,
332
14
            { "Can cover", "cups.ptype.cover", FT_BOOLEAN, 32,
333
14
                TFS(&tfs_yes_no), CUPS_PRINTER_COVER, NULL, HFILL }},
334
14
        { &hf_cups_ptype_punch,
335
14
            { "Can punch holes", "cups.ptype.punch", FT_BOOLEAN, 32,
336
14
                TFS(&tfs_yes_no), CUPS_PRINTER_PUNCH, NULL, HFILL }},
337
14
        { &hf_cups_ptype_collate,
338
14
            { "Can do fast collating", "cups.ptype.collate", FT_BOOLEAN, 32,
339
14
                TFS(&tfs_yes_no), CUPS_PRINTER_COLLATE, NULL, HFILL }},
340
14
        { &hf_cups_ptype_copies,
341
14
            { "Can do fast copies", "cups.ptype.copies", FT_BOOLEAN, 32,
342
14
                TFS(&tfs_yes_no), CUPS_PRINTER_COPIES, NULL, HFILL }},
343
14
        { &hf_cups_ptype_staple,
344
14
            { "Can staple", "cups.ptype.staple", FT_BOOLEAN, 32,
345
14
                TFS(&tfs_yes_no), CUPS_PRINTER_STAPLE, NULL, HFILL }},
346
14
        { &hf_cups_ptype_duplex,
347
14
            { "Can duplex", "cups.ptype.duplex", FT_BOOLEAN, 32,
348
14
                TFS(&tfs_yes_no), CUPS_PRINTER_DUPLEX, NULL, HFILL }},
349
14
        { &hf_cups_ptype_color,
350
14
            { "Can print color", "cups.ptype.color", FT_BOOLEAN, 32,
351
14
                TFS(&tfs_yes_no), CUPS_PRINTER_COLOR, NULL, HFILL }},
352
14
        { &hf_cups_ptype_bw,
353
14
            { "Can print black", "cups.ptype.bw", FT_BOOLEAN, 32,
354
14
                TFS(&tfs_yes_no), CUPS_PRINTER_BW, NULL, HFILL }},
355
14
        { &hf_cups_ptype_remote,
356
14
            { "Remote", "cups.ptype.remote", FT_BOOLEAN, 32,
357
14
                TFS(&tfs_yes_no), CUPS_PRINTER_REMOTE, NULL, HFILL }},
358
14
        { &hf_cups_ptype_class,
359
14
            { "Class", "cups.ptype.class", FT_BOOLEAN, 32,
360
14
                TFS(&tfs_printer_class), CUPS_PRINTER_CLASS, NULL, HFILL }},
361
14
        { &hf_cups_state,
362
14
            { "State",    "cups.state", FT_UINT8, BASE_HEX,
363
14
                VALS(cups_state_values), 0x0, NULL, HFILL }},
364
14
        { &hf_cups_uri,
365
14
            { "URI",    "cups.uri", FT_STRING, BASE_NONE,
366
14
                NULL, 0x0, NULL, HFILL }},
367
14
        { &hf_cups_location,
368
14
            { "Location",    "cups.location", FT_STRING, BASE_NONE,
369
14
                NULL, 0x0, NULL, HFILL }},
370
14
        { &hf_cups_information,
371
14
            { "Information",    "cups.information", FT_STRING, BASE_NONE,
372
14
                NULL, 0x0, NULL, HFILL }},
373
14
        { &hf_cups_make_model,
374
14
            { "Make and model", "cups.make_model", FT_STRING, BASE_NONE,
375
14
                NULL, 0x0, NULL, HFILL }},
376
14
    };
377
378
14
    static int *ett[] = {
379
14
        &ett_cups,
380
14
        &ett_cups_ptype
381
14
    };
382
383
14
    proto_cups = proto_register_protocol("Common Unix Printing System (CUPS) Browsing Protocol", "CUPS", "cups");
384
14
    cups_handle = register_dissector("cups", dissect_cups, proto_cups);
385
14
    proto_register_field_array(proto_cups, hf, array_length(hf));
386
14
    proto_register_subtree_array(ett, array_length(ett));
387
388
    /* compile patterns */
389
14
    ws_mempbrk_compile(&pbrk_whitespace, " \t\r\n");
390
14
}
391
392
void
393
proto_reg_handoff_cups(void)
394
14
{
395
14
    dissector_add_uint_with_preference("udp.port", UDP_PORT_CUPS, cups_handle);
396
14
}
397
398
/*
399
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
400
 *
401
 * Local variables:
402
 * c-basic-offset: 4
403
 * tab-width: 8
404
 * indent-tabs-mode: nil
405
 * End:
406
 *
407
 * vi: set shiftwidth=4 tabstop=8 expandtab:
408
 * :indentSize=4:tabSize=8:noTabs=true:
409
 */