Update from Paul Ionescu to set the reported length of the tvbuff for
[obnox/wireshark/wip.git] / packet-ipp.c
1 /* packet-ipp.c
2  * Routines for IPP packet disassembly
3  *
4  * Guy Harris <guy@alum.mit.edu>
5  *
6  * $Id: packet-ipp.c,v 1.18 2001/01/03 06:55:29 guy Exp $
7  *
8  * Ethereal - Network traffic analyzer
9  * By Gerald Combs <gerald@zing.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * 
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  * 
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  * 
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  *
27  *
28  */
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #ifdef HAVE_SYS_TYPES_H
35 #include <sys/types.h>
36 #endif
37
38 #include <string.h>
39 #include <ctype.h>
40
41 #include <glib.h>
42 #include "packet.h"
43 #include "strutil.h"
44 #include "packet-http.h"
45
46 static int proto_ipp = -1;
47
48 static gint ett_ipp = -1;
49 static gint ett_ipp_as = -1;
50 static gint ett_ipp_attr = -1;
51
52 #define PRINT_JOB               0x0002
53 #define PRINT_URI               0x0003
54 #define VALIDATE_JOB            0x0004
55 #define CREATE_JOB              0x0005
56 #define SEND_DOCUMENT           0x0006
57 #define SEND_URI                0x0007
58 #define CANCEL_JOB              0x0008
59 #define GET_JOB_ATTRIBUTES      0x0009
60 #define GET_JOBS                0x000A
61 #define GET_PRINTER_ATTRIBUTES  0x000B
62
63 static const value_string operation_vals[] = {
64     { PRINT_JOB,              "Print-Job" },
65     { PRINT_URI,              "Print-URI" },
66     { VALIDATE_JOB,           "Validate-Job" },
67     { CREATE_JOB,             "Create-Job" },
68     { SEND_DOCUMENT,          "Send-Document" },
69     { SEND_URI,               "Send-URI" },
70     { CANCEL_JOB,             "Cancel-Job" },
71     { GET_JOB_ATTRIBUTES,     "Get-Job-Attributes" },
72     { GET_JOBS,               "Get-Jobs" },
73     { GET_PRINTER_ATTRIBUTES, "Get-Printer-Attributes" },
74     { 0,                      NULL }
75 };
76
77 #define STATUS_SUCCESSFUL       0x0000
78 #define STATUS_INFORMATIONAL    0x0100
79 #define STATUS_REDIRECTION      0x0200
80 #define STATUS_CLIENT_ERROR     0x0400
81 #define STATUS_SERVER_ERROR     0x0500
82
83 #define STATUS_TYPE_MASK        0xFF00
84
85 #define SUCCESSFUL_OK                           0x0000
86 #define SUCCESSFUL_OK_IGN_OR_SUB_ATTR           0x0001
87 #define SUCCESSFUL_OK_CONFLICTING_ATTR          0x0002
88
89 #define CLIENT_ERROR_BAD_REQUEST                0x0400
90 #define CLIENT_ERROR_FORBIDDEN                  0x0401
91 #define CLIENT_ERROR_NOT_AUTHENTICATED          0x0402
92 #define CLIENT_ERROR_NOT_AUTHORIZED             0x0403
93 #define CLIENT_ERROR_NOT_POSSIBLE               0x0404
94 #define CLIENT_ERROR_TIMEOUT                    0x0405
95 #define CLIENT_ERROR_NOT_FOUND                  0x0406
96 #define CLIENT_ERROR_GONE                       0x0407
97 #define CLIENT_ERROR_REQ_ENTITY_TOO_LRG         0x0408
98 #define CLIENT_ERROR_REQ_VALUE_TOO_LONG         0x0409
99 #define CLIENT_ERROR_DOC_FMT_NOT_SUPP           0x040A
100 #define CLIENT_ERROR_ATTR_OR_VAL_NOT_SUPP       0x040B
101 #define CLIENT_ERROR_URI_SCHEME_NOT_SUPP        0x040C
102 #define CLIENT_ERROR_CHARSET_NOT_SUPP           0x040D
103 #define CLIENT_ERROR_CONFLICTING_ATTRS          0x040E
104
105 #define SERVER_ERROR_INTERNAL_ERROR             0x0500
106 #define SERVER_ERROR_OPERATION_NOT_SUPP         0x0501
107 #define SERVER_ERROR_SERVICE_UNAVAIL            0x0502
108 #define SERVER_ERROR_VERSION_NOT_SUPP           0x0503
109 #define SERVER_ERROR_DEVICE_ERROR               0x0504
110 #define SERVER_ERROR_TEMPORARY_ERROR            0x0505
111 #define SERVER_ERROR_NOT_ACCEPTING_JOBS         0x0506
112 #define SERVER_ERROR_BUSY                       0x0507
113 #define SERVER_ERROR_JOB_CANCELED               0x0508
114
115 static const value_string status_vals[] = {
116     { SUCCESSFUL_OK,                     "Successful-OK" },
117     { SUCCESSFUL_OK_IGN_OR_SUB_ATTR,     "Successful-OK-Ignored-Or-Substituted-Attributes" },
118     { SUCCESSFUL_OK_CONFLICTING_ATTR,    "Successful-OK-Conflicting-Attributes" },
119     { CLIENT_ERROR_BAD_REQUEST,          "Client-Error-Bad-Request" },
120     { CLIENT_ERROR_FORBIDDEN,            "Client-Error-Forbidden" },
121     { CLIENT_ERROR_NOT_AUTHENTICATED,    "Client-Error-Not-Authenticated" },
122     { CLIENT_ERROR_NOT_AUTHORIZED,       "Client-Error-Not-Authorized" },
123     { CLIENT_ERROR_NOT_POSSIBLE,         "Client-Error-Not-Possible" },
124     { CLIENT_ERROR_TIMEOUT,              "Client-Error-Timeout" },
125     { CLIENT_ERROR_NOT_FOUND,            "Client-Error-Not-Found" },
126     { CLIENT_ERROR_GONE,                 "Client-Error-Gone" },
127     { CLIENT_ERROR_REQ_ENTITY_TOO_LRG,   "Client-Error-Request-Entity-Too-Large" },
128     { CLIENT_ERROR_REQ_VALUE_TOO_LONG,   "Client-Error-Request-Value-Too-Long" },
129     { CLIENT_ERROR_DOC_FMT_NOT_SUPP,     "Client-Error-Document-Format-Not-Supported" },
130     { CLIENT_ERROR_ATTR_OR_VAL_NOT_SUPP, "Client-Error-Attributes-Or-Values-Not-Supported" },
131     { CLIENT_ERROR_URI_SCHEME_NOT_SUPP,  "Client-Error-URI-Scheme-Not-Supported" },
132     { CLIENT_ERROR_CHARSET_NOT_SUPP,     "Client-Error-Charset-Not-Supported" },
133     { CLIENT_ERROR_CONFLICTING_ATTRS,    "Client-Error-Conflicting-Attributes" },
134     { SERVER_ERROR_INTERNAL_ERROR,       "Server-Error-Internal-Error" },
135     { SERVER_ERROR_OPERATION_NOT_SUPP,   "Server-Error-Operation-Not-Supported" },
136     { SERVER_ERROR_SERVICE_UNAVAIL,      "Server-Error-Service-Unavailable" },
137     { SERVER_ERROR_VERSION_NOT_SUPP,     "Server-Error-Version-Not-Supported" },
138     { SERVER_ERROR_DEVICE_ERROR,         "Server-Error-Device-Error" },
139     { SERVER_ERROR_TEMPORARY_ERROR,      "Server-Error-Temporary-Error" },
140     { SERVER_ERROR_NOT_ACCEPTING_JOBS,   "Server-Error-Not-Accepting-Jobs" },
141     { SERVER_ERROR_BUSY,                 "Server-Error-Busy" },
142     { SERVER_ERROR_JOB_CANCELED,         "Server-Error-Job-Canceled" },
143     { 0,                                 NULL }
144 };
145
146 static int parse_attributes(tvbuff_t *tvb, int offset, proto_tree *tree);
147 static proto_tree *add_integer_tree(proto_tree *tree, tvbuff_t *tvb,
148     int offset, int name_length, int value_length);
149 static void add_integer_value(guint tag, gchar *tag_desc, proto_tree *tree,
150     tvbuff_t *tvb, int offset, int name_length, int value_length);
151 static proto_tree *add_octetstring_tree(proto_tree *tree, tvbuff_t *tvb,
152     int offset, int name_length, int value_length);
153 static void add_octetstring_value(guint tag, gchar *tag_desc, proto_tree *tree,
154     tvbuff_t *tvb, int offset, int name_length, int value_length);
155 static proto_tree *add_charstring_tree(proto_tree *tree, tvbuff_t *tvb,
156     int offset, int name_length, int value_length);
157 static void add_charstring_value(guint tag, gchar *tag_desc, proto_tree *tree,
158     tvbuff_t *tvb, int offset, int name_length, int value_length);
159 static int add_value_head(guint tag, gchar *tag_desc, proto_tree *tree,
160     tvbuff_t *tvb, int offset, int name_length, int value_length);
161
162 static void
163 dissect_ipp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
164 {
165         proto_tree *ipp_tree;
166         proto_item *ti;
167         int offset = 0;
168         gboolean is_request = (pinfo->destport == 631);
169         guint16 status_code;
170         gchar *status_fmt;
171
172         CHECK_DISPLAY_AS_DATA(proto_ipp, tvb, pinfo, tree);
173
174         pinfo->current_proto = "IPP";
175
176         if (check_col(pinfo->fd, COL_PROTOCOL))
177                 col_set_str(pinfo->fd, COL_PROTOCOL, "IPP");
178         if (check_col(pinfo->fd, COL_INFO)) {
179                 if (is_request)
180                         col_set_str(pinfo->fd, COL_INFO, "IPP request");
181                 else
182                         col_set_str(pinfo->fd, COL_INFO, "IPP response");
183         }
184
185         if (tree) {
186                 ti = proto_tree_add_item(tree, proto_ipp, tvb, offset,
187                     tvb_length_remaining(tvb, offset), FALSE);
188                 ipp_tree = proto_item_add_subtree(ti, ett_ipp);
189
190                 proto_tree_add_text(ipp_tree, tvb, offset, 2, "Version: %u.%u",
191                     tvb_get_guint8(tvb, offset),
192                     tvb_get_guint8(tvb, offset + 1));
193                 offset += 2;
194
195                 if (is_request) {
196                         proto_tree_add_text(ipp_tree, tvb, offset, 2, "Operation-id: %s",
197                             val_to_str(tvb_get_ntohs(tvb, offset), operation_vals,
198                                 "Unknown (0x%04x)"));
199                 } else {
200                         status_code = tvb_get_ntohs(tvb, offset);
201                         switch (status_code & STATUS_TYPE_MASK) {
202
203                         case STATUS_SUCCESSFUL:
204                                 status_fmt = "Successful (0x%04x)";
205                                 break;
206
207                         case STATUS_INFORMATIONAL:
208                                 status_fmt = "Informational (0x%04x)";
209                                 break;
210
211                         case STATUS_REDIRECTION:
212                                 status_fmt = "Redirection (0x%04x)";
213                                 break;
214
215                         case STATUS_CLIENT_ERROR:
216                                 status_fmt = "Client error (0x%04x)";
217                                 break;
218
219                         case STATUS_SERVER_ERROR:
220                                 status_fmt = "Server error (0x%04x)";
221                                 break;
222
223                         default:
224                                 status_fmt = "Unknown (0x%04x)";
225                                 break;
226                         }
227                         proto_tree_add_text(ipp_tree, tvb, offset, 2, "Status-code: %s",
228                             val_to_str(status_code, status_vals, status_fmt));
229                 }
230                 offset += 2;
231
232                 proto_tree_add_text(ipp_tree, tvb, offset, 4, "Request ID: %u",
233                     tvb_get_ntohl(tvb, offset));
234                 offset += 4;
235
236                 offset = parse_attributes(tvb, offset, ipp_tree);
237
238                 if (tvb_offset_exists(tvb, offset))
239                         dissect_data(tvb, offset, pinfo, ipp_tree);
240         }
241 }
242
243 #define TAG_TYPE(tag)           ((tag) & 0xF0)
244 #define TAG_TYPE_DELIMITER      0x00
245 #define TAG_TYPE_INTEGER        0x20
246 #define TAG_TYPE_OCTETSTRING    0x30
247 #define TAG_TYPE_CHARSTRING     0x40
248
249 #define TAG_END_OF_ATTRIBUTES   0x03
250
251 #define TAG_INTEGER             0x21
252 #define TAG_BOOLEAN             0x22
253 #define TAG_ENUM                0x23
254
255 #define TAG_OCTETSTRING         0x30
256 #define TAG_DATETIME            0x31
257 #define TAG_RESOLUTION          0x32
258 #define TAG_RANGEOFINTEGER      0x33
259 #define TAG_TEXTWITHLANGUAGE    0x35
260 #define TAG_NAMEWITHLANGUAGE    0x36
261
262 #define TAG_TEXTWITHOUTLANGUAGE 0x41
263 #define TAG_NAMEWITHOUTLANGUAGE 0x42
264 #define TAG_KEYWORD             0x44
265 #define TAG_URI                 0x45
266 #define TAG_URISCHEME           0x46
267 #define TAG_CHARSET             0x47
268 #define TAG_NATURALLANGUAGE     0x48
269 #define TAG_MIMEMEDIATYPE       0x49
270
271 static const value_string tag_vals[] = {
272         /* Delimiter tags */
273         { 0x01,                    "Operation attributes" },
274         { 0x02,                    "Job attributes" },
275         { TAG_END_OF_ATTRIBUTES,   "End of attributes" },
276         { 0x04,                    "Printer attributes" },
277         { 0x05,                    "Unsupported attributes" },
278
279         /* Value tags */
280         { 0x10,                    "Unsupported" },
281         { 0x12,                    "Unknown" },
282         { 0x13,                    "No value" },
283         { TAG_INTEGER,             "Integer" },
284         { TAG_BOOLEAN,             "Boolean" },
285         { TAG_ENUM,                "Enum" },
286         { TAG_OCTETSTRING,         "Octet string" },
287         { TAG_DATETIME,            "Date/Time" },
288         { TAG_RESOLUTION,          "Resolution" },
289         { TAG_RANGEOFINTEGER,      "Range of integer" },
290         { TAG_TEXTWITHLANGUAGE,    "Text with language" },
291         { TAG_NAMEWITHLANGUAGE,    "Name with language" },
292         { TAG_TEXTWITHOUTLANGUAGE, "Text without language" },
293         { TAG_NAMEWITHOUTLANGUAGE, "Name without language" },
294         { TAG_KEYWORD,             "Keyword" },
295         { TAG_URI,                 "URI" },
296         { TAG_URISCHEME,           "URI scheme" },
297         { TAG_CHARSET,             "Character set" },
298         { TAG_NATURALLANGUAGE,     "Natural language" },
299         { TAG_MIMEMEDIATYPE,       "MIME media type" },
300         { 0,                       NULL }
301 };
302
303 static int
304 parse_attributes(tvbuff_t *tvb, int offset, proto_tree *tree)
305 {
306         guint8 tag;
307         gchar *tag_desc;
308         int name_length, value_length;
309         proto_tree *as_tree = tree;
310         proto_item *tas = NULL;
311         int start_offset = offset;
312         proto_tree *attr_tree = tree;
313
314         while (tvb_offset_exists(tvb, offset)) {
315                 tag = tvb_get_guint8(tvb, offset);
316                 tag_desc = val_to_str(tag, tag_vals, "Reserved (0x%02x)");
317                 if (TAG_TYPE(tag) == TAG_TYPE_DELIMITER) {
318                         /*
319                          * If we had an attribute sequence we were
320                          * working on, we're done with it; set its
321                          * length to the length of all the stuff
322                          * we've done so far.
323                          */
324                         if (tas != NULL)
325                                 proto_item_set_len(tas, offset - start_offset);
326
327                         /*
328                          * This tag starts a new attribute sequence;
329                          * create a new tree under this tag when we see
330                          * a non-delimiter tag, under which to put
331                          * those attributes.
332                          */
333                         as_tree = NULL;
334                         attr_tree = tree;
335
336                         /*
337                          * Remember the offset at which this attribute
338                          * sequence started, so we can use it to compute
339                          * its length when it's finished.
340                          */
341                         start_offset = offset;
342
343                         /*
344                          * Now create a new item for this tag.
345                          */
346                         tas = proto_tree_add_text(tree, tvb, offset, 1,
347                             "%s", tag_desc);
348                         offset++;
349                         if (tag == TAG_END_OF_ATTRIBUTES) {
350                                 /*
351                                  * No more attributes.
352                                  */
353                                 break;
354                         }
355                 } else {
356                         /*
357                          * Value tag - get the name length.
358                          */
359                         name_length = tvb_get_ntohs(tvb, offset + 1);
360
361                         /*
362                          * OK, get the value length.
363                          */
364                         value_length = tvb_get_ntohs(tvb, offset + 1 + 2 + name_length);
365
366                         /*
367                          * OK, does the value run past the end of the
368                          * frame?
369                          */
370                         if (as_tree == NULL) {
371                                 /*
372                                  * OK, there's an attribute to hang
373                                  * under a delimiter tag, but we don't
374                                  * have a tree for that tag yet; create
375                                  * a tree.
376                                  */
377                                 as_tree = proto_item_add_subtree(tas,
378                                     ett_ipp_as);
379                                 attr_tree = as_tree;
380                         }
381
382                         switch (TAG_TYPE(tag)) {
383
384                         case TAG_TYPE_INTEGER:
385                                 if (name_length != 0) {
386                                         /*
387                                          * This is an attribute, not
388                                          * an additional value, so
389                                          * start a tree for it.
390                                          */
391                                         attr_tree = add_integer_tree(as_tree,
392                                             tvb, offset, name_length,
393                                             value_length);
394                                 }
395                                 add_integer_value(tag, tag_desc, attr_tree, tvb,
396                                     offset, name_length, value_length);
397                                 break;
398
399                         case TAG_TYPE_OCTETSTRING:
400                                 if (name_length != 0) {
401                                         /*
402                                          * This is an attribute, not
403                                          * an additional value, so
404                                          * start a tree for it.
405                                          */
406                                         attr_tree = add_octetstring_tree(as_tree,
407                                             tvb, offset, name_length,
408                                             value_length);
409                                 }
410                                 add_octetstring_value(tag, tag_desc,
411                                     attr_tree, tvb, offset, name_length,
412                                     value_length);
413                                 break;
414
415                         case TAG_TYPE_CHARSTRING:
416                                 if (name_length != 0) {
417                                         /*
418                                          * This is an attribute, not
419                                          * an additional value, so
420                                          * start a tree for it.
421                                          */
422                                         attr_tree = add_charstring_tree(as_tree,
423                                             tvb, offset, name_length,
424                                             value_length);
425                                 }
426                                 add_charstring_value(tag, tag_desc,
427                                     attr_tree, tvb, offset, name_length,
428                                     value_length);
429                                 break;
430                         }
431                         offset += 1 + 2 + name_length + 2 + value_length;
432                 }
433         }
434
435         return offset;
436 }
437
438 static proto_tree *
439 add_integer_tree(proto_tree *tree, tvbuff_t *tvb, int offset,
440     int name_length, int value_length)
441 {
442         proto_item *ti;
443
444         if (value_length != 4) {
445                 ti = proto_tree_add_text(tree, tvb, offset,
446                     1 + 2 + name_length + 2 + value_length,
447                     "%.*s: Invalid integer (length is %u, should be 4)",
448                     name_length,
449                     tvb_get_ptr(tvb, offset + 1 + 2, name_length),
450                     value_length);
451         } else {
452                 ti = proto_tree_add_text(tree, tvb, offset,
453                     1 + 2 + name_length + 2 + value_length,
454                     "%.*s: %u",
455                     name_length,
456                     tvb_get_ptr(tvb, offset + 1 + 2, name_length),
457                     tvb_get_ntohl(tvb, offset + 1 + 2 + name_length + 2));
458         }
459         return proto_item_add_subtree(ti, ett_ipp_attr);
460 }
461
462 static void
463 add_integer_value(guint tag, gchar *tag_desc, proto_tree *tree,
464     tvbuff_t *tvb, int offset, int name_length, int value_length)
465 {
466         offset = add_value_head(tag, tag_desc, tree, tvb, offset,
467             name_length, value_length);
468         if (value_length == 4) {
469                 proto_tree_add_text(tree, tvb, offset, value_length,
470                     "Value: %u", tvb_get_ntohl(tvb, offset));
471         }
472 }
473
474 static proto_tree *
475 add_octetstring_tree(proto_tree *tree, tvbuff_t *tvb, int offset,
476     int name_length, int value_length)
477 {
478         proto_item *ti;
479
480         ti = proto_tree_add_text(tree, tvb, offset,
481             1 + 2 + name_length + 2 + value_length,
482             "%.*s: %s",
483             name_length,
484             tvb_get_ptr(tvb, offset + 1 + 2, name_length),
485             tvb_bytes_to_str(tvb, offset + 1 + 2 + name_length + 2, value_length));
486         return proto_item_add_subtree(ti, ett_ipp_attr);
487 }
488
489 static void
490 add_octetstring_value(guint tag, gchar *tag_desc, proto_tree *tree,
491     tvbuff_t *tvb, int offset, int name_length, int value_length)
492 {
493         offset = add_value_head(tag, tag_desc, tree, tvb, offset,
494             name_length, value_length);
495         proto_tree_add_text(tree, tvb, offset, value_length,
496             "Value: %s", tvb_bytes_to_str(tvb, offset, value_length));
497 }
498
499 static proto_tree *
500 add_charstring_tree(proto_tree *tree, tvbuff_t *tvb, int offset,
501     int name_length, int value_length)
502 {
503         proto_item *ti;
504
505         ti = proto_tree_add_text(tree, tvb, offset,
506             1 + 2 + name_length + 2 + value_length,
507             "%.*s: %.*s",
508             name_length,
509             tvb_get_ptr(tvb, offset + 1 + 2, name_length),
510             value_length,
511             tvb_get_ptr(tvb, offset + 1 + 2 + name_length + 2, value_length));
512         return proto_item_add_subtree(ti, ett_ipp_attr);
513 }
514
515 static void
516 add_charstring_value(guint tag, gchar *tag_desc, proto_tree *tree,
517     tvbuff_t *tvb, int offset, int name_length, int value_length)
518 {
519         offset = add_value_head(tag, tag_desc, tree, tvb, offset,
520             name_length, value_length);
521         proto_tree_add_text(tree, tvb, offset, value_length,
522             "Value: %.*s", value_length, tvb_get_ptr(tvb, offset, value_length));
523 }
524
525 static int
526 add_value_head(guint tag, gchar *tag_desc, proto_tree *tree,
527     tvbuff_t *tvb, int offset, int name_length, int value_length)
528 {
529         proto_tree_add_text(tree, tvb, offset, 1, "Tag: %s", tag_desc);
530         offset += 1;
531         proto_tree_add_text(tree, tvb, offset, 2, "Name length: %u",
532             name_length);
533         offset += 2;
534         if (name_length != 0) {
535                 proto_tree_add_text(tree, tvb, offset, name_length,
536                     "Name: %.*s", name_length,
537                     tvb_get_ptr(tvb, offset, name_length));
538         }
539         offset += name_length;
540         proto_tree_add_text(tree, tvb, offset, 2, "Value length: %u",
541             value_length);
542         offset += 2;
543         return offset;
544 }
545
546 void
547 proto_register_ipp(void)
548 {
549 /*        static hf_register_info hf[] = {
550                 { &variable,
551                 { "Name",           "ipp.abbreviation", TYPE, VALS_POINTER }},
552         };*/
553         static gint *ett[] = {
554                 &ett_ipp,
555                 &ett_ipp_as,
556                 &ett_ipp_attr,
557         };
558
559         proto_ipp = proto_register_protocol("Internet Printing Protocol",
560             "IPP", "ipp");
561  /*       proto_register_field_array(proto_ipp, hf, array_length(hf));*/
562         proto_register_subtree_array(ett, array_length(ett));
563
564         /*
565          * Register the dissector by name, so other dissectors can
566          * grab it by name rather than just referring to it directly
567          * (you can't refer to it directly from a plugin dissector
568          * on Windows without stuffing it into the Big Transfer Vector).
569          */
570         register_dissector("ipp", dissect_ipp);
571 }
572
573 void
574 proto_reg_handoff_ipp(void)
575 {
576         /* XXX - should we just register ourselves as a dissector,
577            and call the HTTP dissector?
578
579            Should we pass to it a pointer to our dissector, so that
580            it knows to call us?
581
582            Or should the HTTP dissector decide that the payload is
583            IPP based on the MIME headers? */
584         dissector_add("tcp.port", 631, dissect_http);
585 }