Move "bytes_to_str()" to "strutil.c" from "packet.c" - it's just a
[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.14 2000/11/13 07:18:46 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(const u_char *pd, int offset, frame_data *fd,
147     proto_tree *tree);
148 static proto_tree *add_integer_tree(proto_tree *tree, const u_char *pd,
149     int offset, int name_length, int value_length);
150 static void add_integer_value(guint tag, gchar *tag_desc, proto_tree *tree,
151     const u_char *pd, int offset, int name_length, int value_length);
152 static proto_tree *add_octetstring_tree(proto_tree *tree, const u_char *pd,
153     int offset, int name_length, int value_length);
154 static void add_octetstring_value(guint tag, gchar *tag_desc, proto_tree *tree,
155     const u_char *pd, int offset, int name_length, int value_length);
156 static proto_tree *add_charstring_tree(proto_tree *tree, const u_char *pd,
157     int offset, int name_length, int value_length);
158 static void add_charstring_value(guint tag, gchar *tag_desc, proto_tree *tree,
159     const u_char *pd, int offset, int name_length, int value_length);
160 static int add_value_head(guint tag, gchar *tag_desc, proto_tree *tree,
161     const u_char *pd, int offset, int name_length, int value_length);
162
163 void dissect_ipp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
164 {
165         proto_tree *ipp_tree;
166         proto_item *ti;
167         gboolean is_request = (pi.destport == 631);
168         guint16 status_code;
169         gchar *status_fmt;
170
171         OLD_CHECK_DISPLAY_AS_DATA(proto_ipp, pd, offset, fd, tree);
172
173         if (check_col(fd, COL_PROTOCOL))
174                 col_add_str(fd, COL_PROTOCOL, "IPP");
175         if (check_col(fd, COL_INFO)) {
176                 if (is_request)
177                         col_add_str(fd, COL_INFO, "IPP request");
178                 else
179                         col_add_str(fd, COL_INFO, "IPP response");
180         }
181
182         if (tree) {
183                 ti = proto_tree_add_item(tree, proto_ipp, NullTVB, offset, END_OF_FRAME, FALSE);
184                 ipp_tree = proto_item_add_subtree(ti, ett_ipp);
185
186                 proto_tree_add_text(ipp_tree, NullTVB, offset, 2, "Version: %u.%u",
187                     pd[offset], pd[offset + 1]);
188                 offset += 2;
189
190                 if (is_request) {
191                         proto_tree_add_text(ipp_tree, NullTVB, offset, 2, "Operation-id: %s",
192                             val_to_str(pntohs(&pd[offset]), operation_vals,
193                                 "Unknown (0x%04x)"));
194                 } else {
195                         status_code = pntohs(&pd[offset]);
196                         switch (status_code & STATUS_TYPE_MASK) {
197
198                         case STATUS_SUCCESSFUL:
199                                 status_fmt = "Successful (0x%04x)";
200                                 break;
201
202                         case STATUS_INFORMATIONAL:
203                                 status_fmt = "Informational (0x%04x)";
204                                 break;
205
206                         case STATUS_REDIRECTION:
207                                 status_fmt = "Redirection (0x%04x)";
208                                 break;
209
210                         case STATUS_CLIENT_ERROR:
211                                 status_fmt = "Client error (0x%04x)";
212                                 break;
213
214                         case STATUS_SERVER_ERROR:
215                                 status_fmt = "Server error (0x%04x)";
216                                 break;
217
218                         default:
219                                 status_fmt = "Unknown (0x%04x)";
220                                 break;
221                         }
222                         proto_tree_add_text(ipp_tree, NullTVB, offset, 2, "Status-code: %s",
223                             val_to_str(status_code, status_vals, status_fmt));
224                 }
225                 offset += 2;
226
227                 proto_tree_add_text(ipp_tree, NullTVB, offset, 4, "Request ID: %u",
228                     pntohl(&pd[offset]));
229                 offset += 4;
230
231                 offset = parse_attributes(pd, offset, fd, ipp_tree);
232
233                 if (IS_DATA_IN_FRAME(offset))
234                         old_dissect_data(pd, offset, fd, ipp_tree);
235         }
236 }
237
238 #define TAG_TYPE(tag)           ((tag) & 0xF0)
239 #define TAG_TYPE_DELIMITER      0x00
240 #define TAG_TYPE_INTEGER        0x20
241 #define TAG_TYPE_OCTETSTRING    0x30
242 #define TAG_TYPE_CHARSTRING     0x40
243
244 #define TAG_END_OF_ATTRIBUTES   0x03
245
246 #define TAG_INTEGER             0x21
247 #define TAG_BOOLEAN             0x22
248 #define TAG_ENUM                0x23
249
250 #define TAG_OCTETSTRING         0x30
251 #define TAG_DATETIME            0x31
252 #define TAG_RESOLUTION          0x32
253 #define TAG_RANGEOFINTEGER      0x33
254 #define TAG_TEXTWITHLANGUAGE    0x35
255 #define TAG_NAMEWITHLANGUAGE    0x36
256
257 #define TAG_TEXTWITHOUTLANGUAGE 0x41
258 #define TAG_NAMEWITHOUTLANGUAGE 0x42
259 #define TAG_KEYWORD             0x44
260 #define TAG_URI                 0x45
261 #define TAG_URISCHEME           0x46
262 #define TAG_CHARSET             0x47
263 #define TAG_NATURALLANGUAGE     0x48
264 #define TAG_MIMEMEDIATYPE       0x49
265
266 static const value_string tag_vals[] = {
267         /* Delimiter tags */
268         { 0x01,                    "Operation attributes" },
269         { 0x02,                    "Job attributes" },
270         { TAG_END_OF_ATTRIBUTES,   "End of attributes" },
271         { 0x04,                    "Printer attributes" },
272         { 0x05,                    "Unsupported attributes" },
273
274         /* Value tags */
275         { 0x10,                    "Unsupported" },
276         { 0x12,                    "Unknown" },
277         { 0x13,                    "No value" },
278         { TAG_INTEGER,             "Integer" },
279         { TAG_BOOLEAN,             "Boolean" },
280         { TAG_ENUM,                "Enum" },
281         { TAG_OCTETSTRING,         "Octet string" },
282         { TAG_DATETIME,            "Date/Time" },
283         { TAG_RESOLUTION,          "Resolution" },
284         { TAG_RANGEOFINTEGER,      "Range of integer" },
285         { TAG_TEXTWITHLANGUAGE,    "Text with language" },
286         { TAG_NAMEWITHLANGUAGE,    "Name with language" },
287         { TAG_TEXTWITHOUTLANGUAGE, "Text without language" },
288         { TAG_NAMEWITHOUTLANGUAGE, "Name without language" },
289         { TAG_KEYWORD,             "Keyword" },
290         { TAG_URI,                 "URI" },
291         { TAG_URISCHEME,           "URI scheme" },
292         { TAG_CHARSET,             "Character set" },
293         { TAG_NATURALLANGUAGE,     "Natural language" },
294         { TAG_MIMEMEDIATYPE,       "MIME media type" },
295         { 0,                       NULL }
296 };
297
298 static int
299 parse_attributes(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
300 {
301         guint8 tag;
302         gchar *tag_desc;
303         int name_length, value_length;
304         proto_tree *as_tree = tree;
305         proto_item *tas = NULL;
306         int start_offset = offset;
307         proto_tree *attr_tree = tree;
308
309         while (IS_DATA_IN_FRAME(offset)) {
310                 tag = pd[offset];
311                 tag_desc = val_to_str(tag, tag_vals, "Reserved (0x%02x)");
312                 if (TAG_TYPE(tag) == TAG_TYPE_DELIMITER) {
313                         /*
314                          * If we had an attribute sequence we were
315                          * working on, we're done with it; set its
316                          * length to the length of all the stuff
317                          * we've done so far.
318                          */
319                         if (tas != NULL)
320                                 proto_item_set_len(tas, offset - start_offset);
321
322                         /*
323                          * This tag starts a new attribute sequence;
324                          * create a new tree under this tag when we see
325                          * a non-delimiter tag, under which to put
326                          * those attributes.
327                          */
328                         as_tree = NULL;
329                         attr_tree = tree;
330
331                         /*
332                          * Remember the offset at which this attribute
333                          * sequence started, so we can use it to compute
334                          * its length when it's finished.
335                          */
336                         start_offset = offset;
337
338                         /*
339                          * Now create a new item for this tag.
340                          */
341                         tas = proto_tree_add_text(tree, NullTVB, offset, 1,
342                             "%s", tag_desc);
343                         offset++;
344                         if (tag == TAG_END_OF_ATTRIBUTES) {
345                                 /*
346                                  * No more attributes.
347                                  */
348                                 break;
349                         }
350                 } else {
351                         /*
352                          * Value tag - get the name length.
353                          */
354                         if (!BYTES_ARE_IN_FRAME(offset + 1, 2)) {
355                                 /*
356                                  * We ran past the end of the frame.
357                                  * Quit (we need to be able to handle
358                                  * stuff that crosses frames to do more)
359                                  */
360                                 break;
361                         }
362                         name_length = pntohs(&pd[offset + 1]);
363
364                         /*
365                          * OK, get the value length.
366                          */
367                         if (!BYTES_ARE_IN_FRAME(offset + 1 + 2, name_length)) {
368                                 /*
369                                  * We ran past the end of the frame.
370                                  * Quit (we need to be able to handle
371                                  * stuff that crosses frames to do more)
372                                  */
373                                 break;
374                         }
375                         value_length = pntohs(&pd[offset + 1 + 2 + name_length]);
376
377                         /*
378                          * OK, does the value run past the end of the
379                          * frame?
380                          */
381                         if (!BYTES_ARE_IN_FRAME(offset + 1 + 2 + name_length + 2,
382                             value_length)) {
383                                 /*
384                                  * We ran past the end of the frame.
385                                  * Quit (we need to be able to handle
386                                  * stuff that crosses frames to do more)
387                                  */
388                                 break;
389                         }
390                         if (as_tree == NULL) {
391                                 /*
392                                  * OK, there's an attribute to hang
393                                  * under a delimiter tag, but we don't
394                                  * have a tree for that tag yet; create
395                                  * a tree.
396                                  */
397                                 as_tree = proto_item_add_subtree(tas,
398                                     ett_ipp_as);
399                                 attr_tree = as_tree;
400                         }
401
402                         switch (TAG_TYPE(tag)) {
403
404                         case TAG_TYPE_INTEGER:
405                                 if (name_length != 0) {
406                                         /*
407                                          * This is an attribute, not
408                                          * an additional value, so
409                                          * start a tree for it.
410                                          */
411                                         attr_tree = add_integer_tree(as_tree,
412                                             pd, offset, name_length,
413                                             value_length);
414                                 }
415                                 add_integer_value(tag, tag_desc, attr_tree, pd,
416                                     offset, name_length, value_length);
417                                 break;
418
419                         case TAG_TYPE_OCTETSTRING:
420                                 if (name_length != 0) {
421                                         /*
422                                          * This is an attribute, not
423                                          * an additional value, so
424                                          * start a tree for it.
425                                          */
426                                         attr_tree = add_octetstring_tree(as_tree,
427                                             pd, offset, name_length,
428                                             value_length);
429                                 }
430                                 add_octetstring_value(tag, tag_desc,
431                                     attr_tree, pd, offset, name_length,
432                                     value_length);
433                                 break;
434
435                         case TAG_TYPE_CHARSTRING:
436                                 if (name_length != 0) {
437                                         /*
438                                          * This is an attribute, not
439                                          * an additional value, so
440                                          * start a tree for it.
441                                          */
442                                         attr_tree = add_charstring_tree(as_tree,
443                                             pd, offset, name_length,
444                                             value_length);
445                                 }
446                                 add_charstring_value(tag, tag_desc,
447                                     attr_tree, pd, offset, name_length,
448                                     value_length);
449                                 break;
450                         }
451                         offset += 1 + 2 + name_length + 2 + value_length;
452                 }
453         }
454
455         return offset;
456 }
457
458 static proto_tree *
459 add_integer_tree(proto_tree *tree, const u_char *pd, int offset,
460     int name_length, int value_length)
461 {
462         proto_item *ti;
463
464         if (value_length != 4) {
465                 ti = proto_tree_add_text(tree, NullTVB, offset,
466                     1 + 2 + name_length + 2 + value_length,
467                     "%.*s: Invalid integer (length is %u, should be 4)",
468                     name_length, &pd[offset + 1 + 2],
469                     value_length);
470         } else {
471                 ti = proto_tree_add_text(tree, NullTVB, offset,
472                     1 + 2 + name_length + 2 + value_length,
473                     "%.*s: %u",
474                     name_length, &pd[offset + 1 + 2],
475                     pntohl(&pd[offset + 1 + 2 + name_length + 2]));
476         }
477         return proto_item_add_subtree(ti, ett_ipp_attr);
478 }
479
480 static void
481 add_integer_value(guint tag, gchar *tag_desc, proto_tree *tree,
482     const u_char *pd, int offset, int name_length, int value_length)
483 {
484         offset = add_value_head(tag, tag_desc, tree, pd, offset,
485             name_length, value_length);
486         if (value_length == 4) {
487                 proto_tree_add_text(tree, NullTVB, offset, value_length,
488                     "Value: %u", pntohl(&pd[offset]));
489         }
490 }
491
492 static proto_tree *
493 add_octetstring_tree(proto_tree *tree, const u_char *pd, int offset,
494     int name_length, int value_length)
495 {
496         proto_item *ti;
497
498         ti = proto_tree_add_text(tree, NullTVB, offset,
499             1 + 2 + name_length + 2 + value_length,
500             "%.*s: %s",
501             name_length,
502             &pd[offset + 1 + 2],
503             bytes_to_str(&pd[offset + 1 + 2 + name_length + 2], value_length));
504         return proto_item_add_subtree(ti, ett_ipp_attr);
505 }
506
507 static void
508 add_octetstring_value(guint tag, gchar *tag_desc, proto_tree *tree,
509     const u_char *pd, int offset, int name_length, int value_length)
510 {
511         offset = add_value_head(tag, tag_desc, tree, pd, offset,
512             name_length, value_length);
513         proto_tree_add_text(tree, NullTVB, offset, value_length,
514             "Value: %s", bytes_to_str(&pd[offset], value_length));
515 }
516
517 static proto_tree *
518 add_charstring_tree(proto_tree *tree, const u_char *pd, int offset,
519     int name_length, int value_length)
520 {
521         proto_item *ti;
522
523         ti = proto_tree_add_text(tree, NullTVB, offset,
524             1 + 2 + name_length + 2 + value_length,
525             "%.*s: %.*s",
526             name_length, &pd[offset + 1 + 2],
527             value_length, &pd[offset + 1 + 2 + name_length + 2]);
528         return proto_item_add_subtree(ti, ett_ipp_attr);
529 }
530
531 static void
532 add_charstring_value(guint tag, gchar *tag_desc, proto_tree *tree,
533     const u_char *pd, int offset, int name_length, int value_length)
534 {
535         offset = add_value_head(tag, tag_desc, tree, pd, offset,
536             name_length, value_length);
537         proto_tree_add_text(tree, NullTVB, offset, value_length,
538             "Value: %.*s", value_length, &pd[offset]);
539 }
540
541 static int
542 add_value_head(guint tag, gchar *tag_desc, proto_tree *tree,
543     const u_char *pd, int offset, int name_length, int value_length)
544 {
545         proto_tree_add_text(tree, NullTVB, offset, 1, "Tag: %s", tag_desc);
546         offset += 1;
547         proto_tree_add_text(tree, NullTVB, offset, 2, "Name length: %u",
548             name_length);
549         offset += 2;
550         if (name_length != 0) {
551                 proto_tree_add_text(tree, NullTVB, offset, name_length,
552                     "Name: %.*s", name_length, &pd[offset]);
553         }
554         offset += name_length;
555         proto_tree_add_text(tree, NullTVB, offset, 2, "Value length: %u",
556             value_length);
557         offset += 2;
558         return offset;
559 }
560
561 void
562 proto_register_ipp(void)
563 {
564 /*        static hf_register_info hf[] = {
565                 { &variable,
566                 { "Name",           "ipp.abbreviation", TYPE, VALS_POINTER }},
567         };*/
568         static gint *ett[] = {
569                 &ett_ipp,
570                 &ett_ipp_as,
571                 &ett_ipp_attr,
572         };
573
574         proto_ipp = proto_register_protocol("Internet Printing Protocol", "ipp");
575  /*       proto_register_field_array(proto_ipp, hf, array_length(hf));*/
576         proto_register_subtree_array(ett, array_length(ett));
577 }
578
579 void
580 proto_reg_handoff_ipp(void)
581 {
582         /* XXX - should we just register ourselves as a dissector,
583            and call the HTTP dissector?
584
585            Should we pass to it a pointer to our dissector, so that
586            it knows to call us?
587
588            Or should the HTTP dissector decide that the payload is
589            IPP based on the MIME headers? */
590         dissector_add("tcp.port", 631, dissect_http);
591 }