/* packet-cups.c * Routines for Common Unix Printing System (CUPS) Browsing Protocol * packet disassembly for the Ethereal network traffic analyzer. * * Charles Levert * Copyright 2001 Charles Levert * * $Id: packet-cups.c,v 1.5 2001/06/18 02:17:45 guy Exp $ * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_SYS_TYPES_H #include #endif #include #include #include #include "packet.h" #include "strutil.h" /**********************************************************************/ /* From cups/cups.h, GNU GPL, Copyright 1997-2001 by Easy Software Products. */ typedef guint32 cups_ptype_t; /**** Printer Type/Capability Bits ****/ enum /* Not a typedef'd enum so we can OR */ { CUPS_PRINTER_LOCAL = 0x0000, /* Local printer or class */ CUPS_PRINTER_CLASS = 0x0001, /* Printer class */ CUPS_PRINTER_REMOTE = 0x0002, /* Remote printer or class */ CUPS_PRINTER_BW = 0x0004, /* Can do B&W printing */ CUPS_PRINTER_COLOR = 0x0008, /* Can do color printing */ CUPS_PRINTER_DUPLEX = 0x0010, /* Can do duplexing */ CUPS_PRINTER_STAPLE = 0x0020, /* Can staple output */ CUPS_PRINTER_COPIES = 0x0040, /* Can do copies */ CUPS_PRINTER_COLLATE = 0x0080, /* Can collage copies */ CUPS_PRINTER_PUNCH = 0x0100, /* Can punch output */ CUPS_PRINTER_COVER = 0x0200, /* Can cover output */ CUPS_PRINTER_BIND = 0x0400, /* Can bind output */ CUPS_PRINTER_SORT = 0x0800, /* Can sort output */ CUPS_PRINTER_SMALL = 0x1000, /* Can do Letter/Legal/A4 */ CUPS_PRINTER_MEDIUM = 0x2000, /* Can do Tabloid/B/C/A3/A2 */ CUPS_PRINTER_LARGE = 0x4000, /* Can do D/E/A1/A0 */ CUPS_PRINTER_VARIABLE = 0x8000, /* Can do variable sizes */ CUPS_PRINTER_IMPLICIT = 0x10000, /* Implicit class */ CUPS_PRINTER_DEFAULT = 0x20000, /* Default printer on network */ CUPS_PRINTER_OPTIONS = 0xfffc /* ~(CLASS | REMOTE | IMPLICIT) */ }; /* End insert from cups/cups.h */ typedef struct { guint32 bit; char *on_string; char *off_string; } cups_ptype_bit_info; static const cups_ptype_bit_info cups_ptype_bits[] = { { CUPS_PRINTER_DEFAULT, "Default printer on network", "Not default printer" }, { CUPS_PRINTER_IMPLICIT, "Implicit class", "Explicit class" }, { CUPS_PRINTER_VARIABLE, "Can print variable sizes", "Cannot print variable sizes" }, { CUPS_PRINTER_LARGE, "Can print up to 36x48 inches", "Cannot print up to 36x48 inches" }, { CUPS_PRINTER_MEDIUM, "Can print up to 18x24 inches", "Cannot print up to 18x24 inches" }, { CUPS_PRINTER_SMALL, "Can print up to 9x14 inches", "Cannot print up to 9x14 inches" }, { CUPS_PRINTER_SORT, "Can sort", "Cannot sort" }, { CUPS_PRINTER_BIND, "Can bind", "Cannot bind" }, { CUPS_PRINTER_COVER, "Can cover", "Cannot cover" }, { CUPS_PRINTER_PUNCH, "Can punch holes", "Cannot punch holes" }, { CUPS_PRINTER_COLLATE, "Can do fast collating", "Cannot do fast collating" }, { CUPS_PRINTER_COPIES, "Can do fast copies", "Cannot do fast copies" }, { CUPS_PRINTER_STAPLE, "Can staple", "Cannot staple" }, { CUPS_PRINTER_DUPLEX, "Can duplex", "Cannot duplex" }, { CUPS_PRINTER_COLOR, "Can print color", "Cannot print color" }, { CUPS_PRINTER_BW, "Can print black", "Cannot print black" }, { CUPS_PRINTER_REMOTE, "Remote", "Local (illegal)" }, { CUPS_PRINTER_CLASS, "Printer class", "Single printer" } }; #define N_CUPS_PTYPE_BITS (sizeof cups_ptype_bits / sizeof cups_ptype_bits[0]) typedef enum _cups_state { CUPS_IDLE = 3, CUPS_PROCESSING, CUPS_STOPPED } cups_state_t; static const value_string cups_state_values[] = { { CUPS_IDLE, "idle" }, { CUPS_PROCESSING, "processing" }, { CUPS_STOPPED, "stopped" } }; static int proto_cups = -1; static int hf_cups_ptype = -1; static int hf_cups_state = -1; static gint ett_cups = -1; static gint ett_cups_ptype = -1; /* This protocol is heavily related to IPP, but it is CUPS-specific and non-standard. */ #define UDP_PORT_CUPS 631 #define PROTO_TAG_CUPS "CUPS" static guint get_hex_uint(tvbuff_t *tvb, gint offset, gint *next_offset); static gboolean skip_space(tvbuff_t *tvb, gint offset, gint *next_offset); static const guint8* get_quoted_string(tvbuff_t *tvb, gint offset, gint *next_offset, guint *len); static const guint8* get_unquoted_string(tvbuff_t *tvb, gint offset, gint *next_offset, guint *len); /**********************************************************************/ static void dissect_cups(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { proto_tree *cups_tree = 0; proto_tree *ptype_subtree = 0; proto_item *ti = 0; gint offset = 0; gint next_offset; guint len; unsigned int u; const guint8 *str; cups_ptype_t ptype; unsigned int state; if (check_col(pinfo->fd, COL_PROTOCOL)) col_set_str(pinfo->fd, COL_PROTOCOL, PROTO_TAG_CUPS); if (check_col(pinfo->fd, COL_INFO)) col_clear(pinfo->fd, COL_INFO); if (tree) { ti = proto_tree_add_item(tree, proto_cups, tvb, offset, tvb_length_remaining(tvb, offset), FALSE); cups_tree = proto_item_add_subtree(ti, ett_cups); } /* Format (1450 bytes max.): */ /* type state uri ["location" ["info" ["make-and-model"]]]\n */ ptype = get_hex_uint(tvb, offset, &next_offset); len = next_offset - offset; if (cups_tree) { ti = proto_tree_add_uint(cups_tree, hf_cups_ptype, tvb, offset, len, ptype); ptype_subtree = proto_item_add_subtree(ti, ett_cups_ptype); for (u = 0; u < N_CUPS_PTYPE_BITS; u++) { proto_tree_add_text(ptype_subtree, tvb, offset, len, "%s", decode_boolean_bitfield(ptype, cups_ptype_bits[u].bit, sizeof (ptype)*8, cups_ptype_bits[u].on_string, cups_ptype_bits[u].off_string)); } } offset = next_offset; if (!skip_space(tvb, offset, &next_offset)) return; /* end of packet */ offset = next_offset; state = get_hex_uint(tvb, offset, &next_offset); len = next_offset - offset; if (cups_tree) proto_tree_add_uint(cups_tree, hf_cups_state, tvb, offset, len, state); offset = next_offset; if (!skip_space(tvb, offset, &next_offset)) return; /* end of packet */ offset = next_offset; str = get_unquoted_string(tvb, offset, &next_offset, &len); if (cups_tree) proto_tree_add_text(cups_tree, tvb, offset, len, "URI: %.*s", (guint16) len, str); if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "%.*s (%s)", (guint16) len, str, val_to_str(state, cups_state_values, "0x%x")); offset = next_offset; if (!cups_tree) return; if (!skip_space(tvb, offset, &next_offset)) return; /* end of packet */ offset = next_offset; str = get_quoted_string(tvb, offset, &next_offset, &len); proto_tree_add_text(cups_tree, tvb, offset+1, len, "Location: \"%.*s\"", (guint16) len, str); offset = next_offset; if (!skip_space(tvb, offset, &next_offset)) return; /* end of packet */ offset = next_offset; str = get_quoted_string(tvb, offset, &next_offset, &len); proto_tree_add_text(cups_tree, tvb, offset+1, len, "Information: \"%.*s\"", (guint16) len, str); offset = next_offset; if (!skip_space(tvb, offset, &next_offset)) return; /* end of packet */ offset = next_offset; str = get_quoted_string(tvb, offset, &next_offset, &len); proto_tree_add_text(cups_tree, tvb, offset+1, len, "Make and model: \"%.*s\"", (guint16) len, str); offset = next_offset; return; } static guint get_hex_uint(tvbuff_t *tvb, gint offset, gint *next_offset) { int c; guint u = 0; while (isxdigit(c = tvb_get_guint8(tvb, offset))) { if (isdigit(c)) c -= '0'; else if (isupper(c)) c -= 'A' - 10; else if (islower(c)) c -= 'a' - 10; else c = 0; /* This should not happen. */ u = 16*u + c; offset++; } *next_offset = offset; return u; } static gboolean skip_space(tvbuff_t *tvb, gint offset, gint *next_offset) { int c; while ((c = tvb_get_guint8(tvb, offset)) == ' ') offset++; if (c == '\r' || c == '\n') return FALSE; /* end of packet */ *next_offset = offset; return TRUE; } static const guint8* get_quoted_string(tvbuff_t *tvb, gint offset, gint *next_offset, guint *len) { int c; const guint8* s = NULL; guint l = 0; gint o; c = tvb_get_guint8(tvb, offset); if (c == '"') { o = tvb_find_guint8(tvb, offset+1, -1, '"'); if (o != -1) { offset++; l = o - offset; s = tvb_get_ptr(tvb, offset, l); offset = o + 1; } } *next_offset = offset; *len = l; return s; } static const guint8* get_unquoted_string(tvbuff_t *tvb, gint offset, gint *next_offset, guint *len) { const guint8* s = NULL; guint l = 0; gint o; o = tvb_pbrk_guint8(tvb, offset, -1, " \t\r\n"); if (o != -1) { l = o - offset; s = tvb_get_ptr(tvb, offset, l); offset = o; } *next_offset = offset; *len = l; return s; } /**********************************************************************/ void proto_register_cups(void) { static hf_register_info hf[] = { /* This one could be split in separate fields. */ { &hf_cups_ptype, { "Type", "cups.ptype", FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL }}, { &hf_cups_state, { "State", "cups.state", FT_UINT8, BASE_HEX, VALS(cups_state_values), 0x0, "", HFILL }} }; static gint *ett[] = { &ett_cups, &ett_cups_ptype }; proto_cups = proto_register_protocol( "Common Unix Printing System (CUPS) Browsing Protocol", "CUPS", "cups"); proto_register_field_array(proto_cups, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); } void proto_reg_handoff_cups(void) { dissector_add("udp.port", UDP_PORT_CUPS, dissect_cups, proto_cups); }