2 * Routines for Common Unix Printing System (CUPS) Browsing Protocol
3 * packet disassembly for the Wireshark network traffic analyzer.
5 * Charles Levert <charles@comm.polymtl.ca>
6 * Copyright 2001 Charles Levert
8 * SPDX-License-Identifier: GPL-2.0-or-later
14 #include <epan/packet.h>
16 #include <wsutil/str_util.h>
18 /**********************************************************************/
20 void proto_register_cups(void);
21 void proto_reg_handoff_cups(void);
23 static dissector_handle_t cups_handle
;
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 */
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) */
50 /* End insert from cups/cups.h */
52 typedef enum _cups_state
{
58 static const value_string cups_state_values
[] = {
59 { CUPS_IDLE
, "idle" },
60 { CUPS_PROCESSING
, "processing" },
61 { CUPS_STOPPED
, "stopped" },
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" };
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
;
95 static int ett_cups_ptype
;
97 /* patterns used for tvb_ws_mempbrk_pattern_uint8 */
98 static ws_mempbrk_pattern pbrk_whitespace
;
100 /* This protocol is heavily related to IPP, but it is CUPS-specific
102 #define UDP_PORT_CUPS 631
103 #define PROTO_TAG_CUPS "CUPS"
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
);
112 /**********************************************************************/
115 dissect_cups(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
117 proto_tree
*cups_tree
= NULL
;
118 proto_tree
*ptype_subtree
= NULL
;
119 proto_item
*ti
= NULL
;
127 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, PROTO_TAG_CUPS
);
128 col_clear(pinfo
->cinfo
, COL_INFO
);
130 ti
= proto_tree_add_item(tree
, proto_cups
, tvb
, offset
, -1, ENC_NA
);
131 cups_tree
= proto_item_add_subtree(ti
, ett_cups
);
133 /* Format (1450 bytes max.): */
134 /* type state uri ["location" ["info" ["make-and-model"]]]\n */
136 ptype
= get_hex_uint(tvb
, offset
, &next_offset
);
137 len
= next_offset
- offset
;
139 ti
= proto_tree_add_uint(cups_tree
, hf_cups_ptype
, tvb
, offset
, len
, ptype
);
140 ptype_subtree
= proto_item_add_subtree(ti
, ett_cups_ptype
);
141 proto_tree_add_item(ptype_subtree
, hf_cups_ptype_default
, tvb
, offset
, len
, ENC_BIG_ENDIAN
);
142 proto_tree_add_item(ptype_subtree
, hf_cups_ptype_implicit
, tvb
, offset
, len
, ENC_BIG_ENDIAN
);
143 proto_tree_add_item(ptype_subtree
, hf_cups_ptype_variable
, tvb
, offset
, len
, ENC_BIG_ENDIAN
);
144 proto_tree_add_item(ptype_subtree
, hf_cups_ptype_large
, tvb
, offset
, len
, ENC_BIG_ENDIAN
);
145 proto_tree_add_item(ptype_subtree
, hf_cups_ptype_medium
, tvb
, offset
, len
, ENC_BIG_ENDIAN
);
146 proto_tree_add_item(ptype_subtree
, hf_cups_ptype_small
, tvb
, offset
, len
, ENC_BIG_ENDIAN
);
147 proto_tree_add_item(ptype_subtree
, hf_cups_ptype_sort
, tvb
, offset
, len
, ENC_BIG_ENDIAN
);
148 proto_tree_add_item(ptype_subtree
, hf_cups_ptype_bind
, tvb
, offset
, len
, ENC_BIG_ENDIAN
);
149 proto_tree_add_item(ptype_subtree
, hf_cups_ptype_cover
, tvb
, offset
, len
, ENC_BIG_ENDIAN
);
150 proto_tree_add_item(ptype_subtree
, hf_cups_ptype_punch
, tvb
, offset
, len
, ENC_BIG_ENDIAN
);
151 proto_tree_add_item(ptype_subtree
, hf_cups_ptype_collate
, tvb
, offset
, len
, ENC_BIG_ENDIAN
);
152 proto_tree_add_item(ptype_subtree
, hf_cups_ptype_copies
, tvb
, offset
, len
, ENC_BIG_ENDIAN
);
153 proto_tree_add_item(ptype_subtree
, hf_cups_ptype_staple
, tvb
, offset
, len
, ENC_BIG_ENDIAN
);
154 proto_tree_add_item(ptype_subtree
, hf_cups_ptype_duplex
, tvb
, offset
, len
, ENC_BIG_ENDIAN
);
155 proto_tree_add_item(ptype_subtree
, hf_cups_ptype_color
, tvb
, offset
, len
, ENC_BIG_ENDIAN
);
156 proto_tree_add_item(ptype_subtree
, hf_cups_ptype_bw
, tvb
, offset
, len
, ENC_BIG_ENDIAN
);
157 proto_tree_add_item(ptype_subtree
, hf_cups_ptype_remote
, tvb
, offset
, len
, ENC_BIG_ENDIAN
);
158 proto_tree_add_item(ptype_subtree
, hf_cups_ptype_class
, tvb
, offset
, len
, ENC_BIG_ENDIAN
);
160 offset
= next_offset
;
162 if (!skip_space(tvb
, offset
, &next_offset
))
163 return offset
; /* end of packet */
164 offset
= next_offset
;
166 state
= get_hex_uint(tvb
, offset
, &next_offset
);
167 len
= next_offset
- offset
;
169 proto_tree_add_uint(cups_tree
, hf_cups_state
, tvb
, offset
, len
, state
);
171 offset
= next_offset
;
173 if (!skip_space(tvb
, offset
, &next_offset
))
174 return offset
; /* end of packet */
175 offset
= next_offset
;
177 str
= get_unquoted_string(pinfo
->pool
, tvb
, offset
, &next_offset
, &len
);
179 return offset
; /* separator/terminator not found */
181 proto_tree_add_string(cups_tree
, hf_cups_uri
, tvb
, offset
, len
, str
);
182 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "%s (%s)",
183 str
, val_to_str(state
, cups_state_values
, "0x%x"));
184 offset
= next_offset
;
189 if (!skip_space(tvb
, offset
, &next_offset
))
190 return offset
; /* end of packet */
191 offset
= next_offset
;
193 str
= get_quoted_string(pinfo
->pool
, tvb
, offset
, &next_offset
, &len
);
195 return offset
; /* separator/terminator not found */
196 proto_tree_add_string(cups_tree
, hf_cups_location
, tvb
, offset
+1, len
, str
);
197 offset
= next_offset
;
199 if (!skip_space(tvb
, offset
, &next_offset
))
200 return offset
; /* end of packet */
201 offset
= next_offset
;
203 str
= get_quoted_string(pinfo
->pool
, tvb
, offset
, &next_offset
, &len
);
205 return offset
; /* separator/terminator not found */
206 proto_tree_add_string(cups_tree
, hf_cups_information
, tvb
, offset
+1, len
, str
);
207 offset
= next_offset
;
209 if (!skip_space(tvb
, offset
, &next_offset
))
210 return offset
; /* end of packet */
211 offset
= next_offset
;
213 str
= get_quoted_string(pinfo
->pool
, tvb
, offset
, &next_offset
, &len
);
215 return offset
; /* separator/terminator not found */
216 proto_tree_add_string(cups_tree
, hf_cups_make_model
, tvb
, offset
+1, len
, str
);
222 get_hex_uint(tvbuff_t
*tvb
, int offset
, int *next_offset
)
227 while (g_ascii_isxdigit(c
= tvb_get_uint8(tvb
, offset
))) {
228 u
= 16*u
+ ws_xton(c
);
233 *next_offset
= offset
;
239 skip_space(tvbuff_t
*tvb
, int offset
, int *next_offset
)
243 while ((c
= tvb_get_uint8(tvb
, offset
)) == ' ')
245 if (c
== '\r' || c
== '\n')
246 return false; /* end of packet */
248 *next_offset
= offset
;
253 static const uint8_t*
254 get_quoted_string(wmem_allocator_t
*scope
, tvbuff_t
*tvb
, int offset
, int *next_offset
, unsigned *len
)
257 const uint8_t* s
= NULL
;
261 c
= tvb_get_uint8(tvb
, offset
);
263 o
= tvb_find_uint8(tvb
, offset
+1, -1, '"');
267 s
= tvb_get_string_enc(scope
, tvb
, offset
, l
, ENC_UTF_8
);
272 *next_offset
= offset
;
278 static const uint8_t*
279 get_unquoted_string(wmem_allocator_t
*scope
, tvbuff_t
*tvb
, int offset
, int *next_offset
, unsigned *len
)
281 const uint8_t* s
= NULL
;
285 o
= tvb_ws_mempbrk_pattern_uint8(tvb
, offset
, -1, &pbrk_whitespace
, NULL
);
288 s
= tvb_get_string_enc(scope
, tvb
, offset
, l
, ENC_UTF_8
);
292 *next_offset
= offset
;
298 /**********************************************************************/
301 proto_register_cups(void)
303 static hf_register_info hf
[] = {
305 { "Type", "cups.ptype", FT_UINT32
, BASE_HEX
,
306 NULL
, 0x0, NULL
, HFILL
}},
307 { &hf_cups_ptype_default
,
308 { "Default printer on network", "cups.ptype.default", FT_BOOLEAN
, 32,
309 TFS(&tfs_yes_no
), CUPS_PRINTER_DEFAULT
, NULL
, HFILL
}},
310 { &hf_cups_ptype_implicit
,
311 { "Class", "cups.ptype.implicit", FT_BOOLEAN
, 32,
312 TFS(&tfs_implicit_explicit
), CUPS_PRINTER_IMPLICIT
, NULL
, HFILL
}},
313 { &hf_cups_ptype_variable
,
314 { "Can print variable sizes", "cups.ptype.variable", FT_BOOLEAN
, 32,
315 TFS(&tfs_yes_no
), CUPS_PRINTER_VARIABLE
, NULL
, HFILL
}},
316 { &hf_cups_ptype_large
,
317 { "Can print up to 36x48 inches", "cups.ptype.large", FT_BOOLEAN
, 32,
318 TFS(&tfs_yes_no
), CUPS_PRINTER_LARGE
, NULL
, HFILL
}},
319 { &hf_cups_ptype_medium
,
320 { "Can print up to 18x24 inches", "cups.ptype.medium", FT_BOOLEAN
, 32,
321 TFS(&tfs_yes_no
), CUPS_PRINTER_MEDIUM
, NULL
, HFILL
}},
322 { &hf_cups_ptype_small
,
323 { "Can print up to 9x14 inches", "cups.ptype.small", FT_BOOLEAN
, 32,
324 TFS(&tfs_yes_no
), CUPS_PRINTER_SMALL
, NULL
, HFILL
}},
325 { &hf_cups_ptype_sort
,
326 { "Can sort", "cups.ptype.sort", FT_BOOLEAN
, 32,
327 TFS(&tfs_yes_no
), CUPS_PRINTER_SORT
, NULL
, HFILL
}},
328 { &hf_cups_ptype_bind
,
329 { "Can bind", "cups.ptype.bind", FT_BOOLEAN
, 32,
330 TFS(&tfs_yes_no
), CUPS_PRINTER_BIND
, NULL
, HFILL
}},
331 { &hf_cups_ptype_cover
,
332 { "Can cover", "cups.ptype.cover", FT_BOOLEAN
, 32,
333 TFS(&tfs_yes_no
), CUPS_PRINTER_COVER
, NULL
, HFILL
}},
334 { &hf_cups_ptype_punch
,
335 { "Can punch holes", "cups.ptype.punch", FT_BOOLEAN
, 32,
336 TFS(&tfs_yes_no
), CUPS_PRINTER_PUNCH
, NULL
, HFILL
}},
337 { &hf_cups_ptype_collate
,
338 { "Can do fast collating", "cups.ptype.collate", FT_BOOLEAN
, 32,
339 TFS(&tfs_yes_no
), CUPS_PRINTER_COLLATE
, NULL
, HFILL
}},
340 { &hf_cups_ptype_copies
,
341 { "Can do fast copies", "cups.ptype.copies", FT_BOOLEAN
, 32,
342 TFS(&tfs_yes_no
), CUPS_PRINTER_COPIES
, NULL
, HFILL
}},
343 { &hf_cups_ptype_staple
,
344 { "Can staple", "cups.ptype.staple", FT_BOOLEAN
, 32,
345 TFS(&tfs_yes_no
), CUPS_PRINTER_STAPLE
, NULL
, HFILL
}},
346 { &hf_cups_ptype_duplex
,
347 { "Can duplex", "cups.ptype.duplex", FT_BOOLEAN
, 32,
348 TFS(&tfs_yes_no
), CUPS_PRINTER_DUPLEX
, NULL
, HFILL
}},
349 { &hf_cups_ptype_color
,
350 { "Can print color", "cups.ptype.color", FT_BOOLEAN
, 32,
351 TFS(&tfs_yes_no
), CUPS_PRINTER_COLOR
, NULL
, HFILL
}},
353 { "Can print black", "cups.ptype.bw", FT_BOOLEAN
, 32,
354 TFS(&tfs_yes_no
), CUPS_PRINTER_BW
, NULL
, HFILL
}},
355 { &hf_cups_ptype_remote
,
356 { "Remote", "cups.ptype.remote", FT_BOOLEAN
, 32,
357 TFS(&tfs_yes_no
), CUPS_PRINTER_REMOTE
, NULL
, HFILL
}},
358 { &hf_cups_ptype_class
,
359 { "Class", "cups.ptype.class", FT_BOOLEAN
, 32,
360 TFS(&tfs_printer_class
), CUPS_PRINTER_CLASS
, NULL
, HFILL
}},
362 { "State", "cups.state", FT_UINT8
, BASE_HEX
,
363 VALS(cups_state_values
), 0x0, NULL
, HFILL
}},
365 { "URI", "cups.uri", FT_STRING
, BASE_NONE
,
366 NULL
, 0x0, NULL
, HFILL
}},
368 { "Location", "cups.location", FT_STRING
, BASE_NONE
,
369 NULL
, 0x0, NULL
, HFILL
}},
370 { &hf_cups_information
,
371 { "Information", "cups.information", FT_STRING
, BASE_NONE
,
372 NULL
, 0x0, NULL
, HFILL
}},
373 { &hf_cups_make_model
,
374 { "Make and model", "cups.make_model", FT_STRING
, BASE_NONE
,
375 NULL
, 0x0, NULL
, HFILL
}},
378 static int *ett
[] = {
383 proto_cups
= proto_register_protocol("Common Unix Printing System (CUPS) Browsing Protocol", "CUPS", "cups");
384 cups_handle
= register_dissector("cups", dissect_cups
, proto_cups
);
385 proto_register_field_array(proto_cups
, hf
, array_length(hf
));
386 proto_register_subtree_array(ett
, array_length(ett
));
388 /* compile patterns */
389 ws_mempbrk_compile(&pbrk_whitespace
, " \t\r\n");
393 proto_reg_handoff_cups(void)
395 dissector_add_uint_with_preference("udp.port", UDP_PORT_CUPS
, cups_handle
);
399 * Editor modelines - https://www.wireshark.org/tools/modelines.html
404 * indent-tabs-mode: nil
407 * vi: set shiftwidth=4 tabstop=8 expandtab:
408 * :indentSize=4:tabSize=8:noTabs=true: