Moved dissect_ndr_nt_NTTIME() from packet-dcerpc-samr.c to packet-dcerpc-nt.c
[obnox/wireshark/wip.git] / packet-cups.c
1 /* packet-cups.c
2 * Routines for Common Unix Printing System (CUPS) Browsing Protocol
3 * packet disassembly for the Ethereal network traffic analyzer.
4 *
5 * Charles Levert <charles@comm.polymtl.ca>
6 * Copyright 2001 Charles Levert
7 *
8 * $Id: packet-cups.c,v 1.10 2002/01/24 09:20:47 guy Exp $
9
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 * GNU General Public License for more details.
19
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23 *
24 */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #ifdef HAVE_SYS_TYPES_H
31 #include <sys/types.h>
32 #endif
33
34 #include <string.h>
35 #include <ctype.h>
36
37 #include <glib.h>
38 #include <epan/packet.h>
39 #include <epan/strutil.h>
40
41 /**********************************************************************/
42
43 /* From cups/cups.h, GNU GPL, Copyright 1997-2001 by Easy Software Products. */
44 typedef guint32 cups_ptype_t;           /**** Printer Type/Capability Bits ****/
45 enum                                    /* Not a typedef'd enum so we can OR */
46 {
47   CUPS_PRINTER_LOCAL = 0x0000,          /* Local printer or class */
48   CUPS_PRINTER_CLASS = 0x0001,          /* Printer class */
49   CUPS_PRINTER_REMOTE = 0x0002,         /* Remote printer or class */
50   CUPS_PRINTER_BW = 0x0004,             /* Can do B&W printing */
51   CUPS_PRINTER_COLOR = 0x0008,          /* Can do color printing */
52   CUPS_PRINTER_DUPLEX = 0x0010,         /* Can do duplexing */
53   CUPS_PRINTER_STAPLE = 0x0020,         /* Can staple output */
54   CUPS_PRINTER_COPIES = 0x0040,         /* Can do copies */
55   CUPS_PRINTER_COLLATE = 0x0080,        /* Can collage copies */
56   CUPS_PRINTER_PUNCH = 0x0100,          /* Can punch output */
57   CUPS_PRINTER_COVER = 0x0200,          /* Can cover output */
58   CUPS_PRINTER_BIND = 0x0400,           /* Can bind output */
59   CUPS_PRINTER_SORT = 0x0800,           /* Can sort output */
60   CUPS_PRINTER_SMALL = 0x1000,          /* Can do Letter/Legal/A4 */
61   CUPS_PRINTER_MEDIUM = 0x2000,         /* Can do Tabloid/B/C/A3/A2 */
62   CUPS_PRINTER_LARGE = 0x4000,          /* Can do D/E/A1/A0 */
63   CUPS_PRINTER_VARIABLE = 0x8000,       /* Can do variable sizes */
64   CUPS_PRINTER_IMPLICIT = 0x10000,      /* Implicit class */
65   CUPS_PRINTER_DEFAULT = 0x20000,       /* Default printer on network */
66   CUPS_PRINTER_OPTIONS = 0xfffc         /* ~(CLASS | REMOTE | IMPLICIT) */
67 };
68 /* End insert from cups/cups.h */
69
70 typedef struct {
71         guint32 bit;
72         char    *on_string;
73         char    *off_string;
74 } cups_ptype_bit_info;
75
76 static const cups_ptype_bit_info cups_ptype_bits[] = {
77         { CUPS_PRINTER_DEFAULT,
78           "Default printer on network", "Not default printer" },
79         { CUPS_PRINTER_IMPLICIT,
80           "Implicit class", "Explicit class" },
81         { CUPS_PRINTER_VARIABLE,
82           "Can print variable sizes", "Cannot print variable sizes" },
83         { CUPS_PRINTER_LARGE,
84           "Can print up to 36x48 inches", "Cannot print up to 36x48 inches" },
85         { CUPS_PRINTER_MEDIUM,
86           "Can print up to 18x24 inches", "Cannot print up to 18x24 inches" },
87         { CUPS_PRINTER_SMALL,
88           "Can print up to 9x14 inches", "Cannot print up to 9x14 inches" },
89         { CUPS_PRINTER_SORT,
90           "Can sort", "Cannot sort" },
91         { CUPS_PRINTER_BIND,
92           "Can bind", "Cannot bind" },
93         { CUPS_PRINTER_COVER,
94           "Can cover", "Cannot cover" },
95         { CUPS_PRINTER_PUNCH,
96           "Can punch holes", "Cannot punch holes" },
97         { CUPS_PRINTER_COLLATE,
98           "Can do fast collating", "Cannot do fast collating" },
99         { CUPS_PRINTER_COPIES,
100           "Can do fast copies", "Cannot do fast copies" },
101         { CUPS_PRINTER_STAPLE,
102           "Can staple", "Cannot staple" },
103         { CUPS_PRINTER_DUPLEX,
104           "Can duplex", "Cannot duplex" },
105         { CUPS_PRINTER_COLOR,
106           "Can print color", "Cannot print color" },
107         { CUPS_PRINTER_BW,
108           "Can print black", "Cannot print black" },
109         { CUPS_PRINTER_REMOTE,
110           "Remote", "Local (illegal)" },
111         { CUPS_PRINTER_CLASS,
112           "Printer class", "Single printer" }
113 };
114
115 #define N_CUPS_PTYPE_BITS       (sizeof cups_ptype_bits / sizeof cups_ptype_bits[0])
116
117 typedef enum _cups_state {
118         CUPS_IDLE = 3,
119         CUPS_PROCESSING,
120         CUPS_STOPPED
121 } cups_state_t;
122
123 static const value_string cups_state_values[] = {
124         { CUPS_IDLE,            "idle" },
125         { CUPS_PROCESSING,      "processing" },
126         { CUPS_STOPPED,         "stopped" },
127         { 0,                    NULL }
128 };
129
130 static int proto_cups = -1;
131 static int hf_cups_ptype = -1;
132 static int hf_cups_state = -1;
133
134 static gint ett_cups = -1;
135 static gint ett_cups_ptype = -1;
136
137 /* This protocol is heavily related to IPP, but it is CUPS-specific
138    and non-standard. */
139 #define UDP_PORT_CUPS   631
140 #define PROTO_TAG_CUPS  "CUPS"
141
142 static guint get_hex_uint(tvbuff_t *tvb, gint offset,
143     gint *next_offset);
144 static gboolean skip_space(tvbuff_t *tvb, gint offset,
145     gint *next_offset);
146 static const guint8* get_quoted_string(tvbuff_t *tvb, gint offset,
147     gint *next_offset, guint *len);
148 static const guint8* get_unquoted_string(tvbuff_t *tvb, gint offset,
149     gint *next_offset, guint *len);
150
151 /**********************************************************************/
152
153 static void
154 dissect_cups(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
155 {
156         proto_tree      *cups_tree = 0;
157         proto_tree      *ptype_subtree = 0;
158         proto_item      *ti = 0;
159         gint            offset = 0;
160         gint            next_offset;
161         guint           len;
162         unsigned int    u;
163         const guint8    *str;
164         cups_ptype_t    ptype;
165         unsigned int    state;
166
167         if (check_col(pinfo->cinfo, COL_PROTOCOL))
168                 col_set_str(pinfo->cinfo, COL_PROTOCOL, PROTO_TAG_CUPS);
169         if (check_col(pinfo->cinfo, COL_INFO))
170                 col_clear(pinfo->cinfo, COL_INFO);
171
172         if (tree) {
173                 ti = proto_tree_add_item(tree, proto_cups, tvb, offset, -1,
174                     FALSE);
175                 cups_tree = proto_item_add_subtree(ti, ett_cups);
176         }
177
178         /* Format (1450 bytes max.):  */
179         /* type state uri ["location" ["info" ["make-and-model"]]]\n */
180
181         ptype = get_hex_uint(tvb, offset, &next_offset);
182         len = next_offset - offset;
183         if (len != 0) {
184                 if (cups_tree) {
185                         ti = proto_tree_add_uint(cups_tree, hf_cups_ptype,
186                             tvb, offset, len, ptype);
187                         ptype_subtree = proto_item_add_subtree(ti,
188                             ett_cups_ptype);
189                         for (u = 0; u < N_CUPS_PTYPE_BITS; u++) {
190                                 proto_tree_add_text(ptype_subtree, tvb,
191                                     offset, len, "%s",
192                                     decode_boolean_bitfield(ptype,
193                                       cups_ptype_bits[u].bit, sizeof (ptype)*8,
194                                       cups_ptype_bits[u].on_string,
195                                       cups_ptype_bits[u].off_string));
196                         }
197                 }
198         }
199         offset = next_offset;
200
201         if (!skip_space(tvb, offset, &next_offset))
202                 return; /* end of packet */
203         offset = next_offset;
204
205         state = get_hex_uint(tvb, offset, &next_offset);
206         len = next_offset - offset;
207         if (len != 0) {
208                 if (cups_tree)
209                         proto_tree_add_uint(cups_tree, hf_cups_state,
210                             tvb, offset, len, state);
211         }
212         offset = next_offset;
213
214         if (!skip_space(tvb, offset, &next_offset))
215                 return; /* end of packet */
216         offset = next_offset;
217
218         str = get_unquoted_string(tvb, offset, &next_offset, &len);
219         if (str == NULL)
220                 return; /* separator/terminator not found */
221         if (cups_tree)
222                 proto_tree_add_text(cups_tree, tvb, offset, len,
223                     "URI: %.*s",
224                     (guint16) len, str);
225         if (check_col(pinfo->cinfo, COL_INFO))
226                 col_add_fstr(pinfo->cinfo, COL_INFO,
227                     "%.*s (%s)",
228                     (guint16) len, str,
229                     val_to_str(state, cups_state_values, "0x%x"));
230         offset = next_offset;
231
232         if (!cups_tree)
233                 return;
234
235         if (!skip_space(tvb, offset, &next_offset))
236                 return; /* end of packet */
237         offset = next_offset;
238
239         str = get_quoted_string(tvb, offset, &next_offset, &len);
240         if (str == NULL)
241                 return; /* separator/terminator not found */
242         proto_tree_add_text(cups_tree, tvb, offset+1, len,
243             "Location: \"%.*s\"",
244             (guint16) len, str);
245         offset = next_offset;
246
247         if (!skip_space(tvb, offset, &next_offset))
248                 return; /* end of packet */
249         offset = next_offset;
250
251         str = get_quoted_string(tvb, offset, &next_offset, &len);
252         if (str == NULL)
253                 return; /* separator/terminator not found */
254         proto_tree_add_text(cups_tree, tvb, offset+1, len,
255             "Information: \"%.*s\"",
256             (guint16) len, str);
257         offset = next_offset;
258
259         if (!skip_space(tvb, offset, &next_offset))
260                 return; /* end of packet */
261         offset = next_offset;
262
263         str = get_quoted_string(tvb, offset, &next_offset, &len);
264         if (str == NULL)
265                 return; /* separator/terminator not found */
266         proto_tree_add_text(cups_tree, tvb, offset+1, len,
267             "Make and model: \"%.*s\"",
268             (guint16) len, str);
269         offset = next_offset;
270
271         return;
272 }
273
274 static guint
275 get_hex_uint(tvbuff_t *tvb, gint offset, gint *next_offset)
276 {
277         int c;
278         guint u = 0;
279
280         while (isxdigit(c = tvb_get_guint8(tvb, offset))) {
281                 if (isdigit(c))
282                         c -= '0';
283                 else if (isupper(c))
284                         c -= 'A' - 10;
285                 else if (islower(c))
286                         c -= 'a' - 10;
287                 else
288                         c = 0; /* This should not happen. */
289
290                 u = 16*u + c;
291
292                 offset++;
293         }
294
295         *next_offset = offset;
296
297         return u;
298 }
299
300 static gboolean
301 skip_space(tvbuff_t *tvb, gint offset, gint *next_offset)
302 {
303         int c;
304
305         while ((c = tvb_get_guint8(tvb, offset)) == ' ')
306                 offset++;
307         if (c == '\r' || c == '\n')
308                 return FALSE;   /* end of packet */
309
310         *next_offset = offset;
311
312         return TRUE;
313 }
314
315 static const guint8*
316 get_quoted_string(tvbuff_t *tvb, gint offset, gint *next_offset, guint *len)
317 {
318         int c;
319         const guint8* s = NULL;
320         guint l = 0;
321         gint o;
322
323         c = tvb_get_guint8(tvb, offset);
324         if (c == '"') {
325                 o = tvb_find_guint8(tvb, offset+1, -1, '"');
326                 if (o != -1) {
327                         offset++;
328                         l = o - offset;
329                         s = tvb_get_ptr(tvb, offset, l);
330                         offset = o + 1;
331                 }
332         }
333
334         *next_offset = offset;
335         *len = l;
336
337         return s;
338 }
339
340 static const guint8*
341 get_unquoted_string(tvbuff_t *tvb, gint offset, gint *next_offset, guint *len)
342 {
343         const guint8* s = NULL;
344         guint l = 0;
345         gint o;
346
347         o = tvb_pbrk_guint8(tvb, offset, -1, " \t\r\n");
348         if (o != -1) {
349                 l = o - offset;
350                 s = tvb_get_ptr(tvb, offset, l);
351                 offset = o;
352         }
353
354         *next_offset = offset;
355         *len = l;
356
357         return s;
358 }
359
360 /**********************************************************************/
361
362 void
363 proto_register_cups(void)
364 {
365         static hf_register_info hf[] = {
366                 /* This one could be split in separate fields. */
367                 { &hf_cups_ptype,
368                         { "Type",       "cups.ptype", FT_UINT32, BASE_HEX,
369                           NULL, 0x0, "", HFILL }},
370
371                 { &hf_cups_state,
372                         { "State",      "cups.state", FT_UINT8, BASE_HEX,
373                           VALS(cups_state_values), 0x0, "", HFILL }}
374         };
375
376         static gint *ett[] = {
377                 &ett_cups,
378                 &ett_cups_ptype
379         };
380
381         proto_cups = proto_register_protocol(
382             "Common Unix Printing System (CUPS) Browsing Protocol",
383             "CUPS", "cups");
384         proto_register_field_array(proto_cups, hf, array_length(hf));
385         proto_register_subtree_array(ett, array_length(ett));
386 }
387
388 void
389 proto_reg_handoff_cups(void)
390 {
391         dissector_handle_t cups_handle;
392
393         cups_handle = create_dissector_handle(dissect_cups, proto_cups);
394         dissector_add("udp.port", UDP_PORT_CUPS, cups_handle);
395 }