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