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