1 /* packet-ipp.c
2  * Routines for IPP packet disassembly
3  *
4  * Guy Harris <guy@alum.mit.edu>
5  *     (original implementation)
6  * Michael R Sweet <michael.r.sweet@gmail.com>
7  *     (general improvements and support beyond RFC 2910/2911)
8  *
9  * Wireshark - Network traffic analyzer
10  * By Gerald Combs <gerald@wireshark.org>
11  * Copyright 1998 Gerald Combs
12  *
13  * SPDX-License-Identifier: GPL-2.0-or-later
14  */
15 
16 #include "config.h"
17 
18 #include <epan/packet.h>
19 #include <epan/strutil.h>
20 #include <epan/to_str.h>
21 #include <epan/conversation.h>
22 #include <epan/wmem_scopes.h>
23 #include "packet-http.h"
24 #include <stdio.h>
25 
26 void proto_register_ipp(void);
27 void proto_reg_handoff_ipp(void);
28 
29 static int proto_ipp = -1;
30 /* Generated from convert_proto_tree_add_text.pl */
31 static int hf_ipp_version = -1;
32 static int hf_ipp_operation_id = -1;
33 static int hf_ipp_status_code = -1;
34 static int hf_ipp_request_id = -1;
35 static int hf_ipp_name = -1;
36 static int hf_ipp_memberattrname = -1;
37 static int hf_ipp_outofband_value = -1;
38 static int hf_ipp_charstring_value = -1;
39 static int hf_ipp_boolean_value = -1;
40 static int hf_ipp_enum_value = -1;
41 static int hf_ipp_enum_value_printer_state = -1;
42 static int hf_ipp_enum_value_job_state = -1;
43 static int hf_ipp_enum_value_document_state = -1;
44 static int hf_ipp_enum_value_operations_supported = -1;
45 static int hf_ipp_enum_value_finishings = -1;
46 static int hf_ipp_enum_value_orientation = -1;
47 static int hf_ipp_enum_value_print_quality = -1;
48 static int hf_ipp_enum_value_transmission_status = -1;
49 static int hf_ipp_integer_value = -1;
50 static int hf_ipp_octetstring_value = -1;
51 static int hf_ipp_datetime_value = -1;
52 static int hf_ipp_resolution_value = -1;
53 static int hf_ipp_rangeofinteger_value = -1;
54 static int hf_ipp_textwithlanguage_value = -1;
55 static int hf_ipp_namewithlanguage_value = -1;
56 static int hf_ipp_unknown_value = -1;
57 
58 static int hf_ipp_response_in = -1;
59 static int hf_ipp_response_to = -1;
60 static int hf_ipp_response_time = -1;
61 
62 typedef struct _ipp_transaction_t {
63         guint32 req_frame;
64         guint32 rep_frame;
65         nstime_t req_time;
66 } ipp_transaction_t;
67 
68 typedef struct _ipp_conv_info_t {
69         wmem_map_t *pdus;
70 } ipp_conv_info_t;
71 
72 static gint ett_ipp = -1;
73 static gint ett_ipp_as = -1;
74 static gint ett_ipp_attr = -1;
75 static gint ett_ipp_member = -1;
76 
77 #define PRINT_JOB              0x0002
78 #define PRINT_URI              0x0003
79 #define VALIDATE_JOB           0x0004
80 #define CREATE_JOB             0x0005
81 #define SEND_DOCUMENT          0x0006
82 #define SEND_URI               0x0007
83 #define CANCEL_JOB             0x0008
84 #define GET_JOB_ATTRIBUTES     0x0009
85 #define GET_JOBS               0x000A
86 #define GET_PRINTER_ATTRIBUTES 0x000B
87 
88 static const value_string operation_vals[] = {
89     { PRINT_JOB,              "Print-Job" },
90     { PRINT_URI,              "Print-URI" },
91     { VALIDATE_JOB,           "Validate-Job" },
92     { CREATE_JOB,             "Create-Job" },
93     { SEND_DOCUMENT,          "Send-Document" },
94     { SEND_URI,               "Send-URI" },
95     { CANCEL_JOB,             "Cancel-Job" },
96     { GET_JOB_ATTRIBUTES,     "Get-Job-Attributes" },
97     { GET_JOBS,               "Get-Jobs" },
98     { GET_PRINTER_ATTRIBUTES, "Get-Printer-Attributes" },
99     { 0x000C,                 "Hold-Job" },
100     { 0x000D,                 "Release-Job" },
101     { 0x000E,                 "Restart-Job" },
102     { 0x0010,                 "Pause-Printer" },
103     { 0x0011,                 "Resume-Printer" },
104     { 0x0012,                 "Purge-Jobs" },
105     { 0x0013,                 "Set-Printer-Attributes" },
106     { 0x0014,                 "Set-Job-Attributes" },
107     { 0x0015,                 "Get-Printer-Supported-Values" },
108     { 0x0016,                 "Create-Printer-Subscriptions" },
109     { 0x0017,                 "Create-Job-Subscriptions" },
110     { 0x0018,                 "Get-Subscription-Attributes" },
111     { 0x0019,                 "Get-Subscriptions" },
112     { 0x001A,                 "Renew-Subscription" },
113     { 0x001B,                 "Cancel-Subscription" },
114     { 0x001C,                 "Get-Notifications" },
115     { 0x001D,                 "Reserved (ipp-indp-method)" },
116     { 0x001E,                 "Reserved (ipp-get-resources)" },
117     { 0x001F,                 "Reserved (ipp-get-resources)" },
118     { 0x0020,                 "Reserved (ipp-get-resources)" },
119     { 0x0021,                 "Reserved (ipp-install)" },
120     { 0x0022,                 "Enable-Printer" },
121     { 0x0023,                 "Disable-Printer" },
122     { 0x0024,                 "Pause-Printer-After-Current-Job" },
123     { 0x0025,                 "Hold-New-Jobs" },
124     { 0x0026,                 "Release-Held-New-Jobs" },
125     { 0x0027,                 "Deactivate-Printer" },
126     { 0x0028,                 "Activate-Printer" },
127     { 0x0029,                 "Restart-Printer" },
128     { 0x002A,                 "Shutdown-Printer" },
129     { 0x002B,                 "Startup-Printer" },
130     { 0x002C,                 "Reprocess-Job" },
131     { 0x002D,                 "Cancel-Current-Job" },
132     { 0x002E,                 "Suspend-Current-Job" },
133     { 0x002F,                 "Resume-Job" },
134     { 0x0030,                 "Promote-Job" },
135     { 0x0031,                 "Schedule-Job-After" },
136     { 0x0033,                 "Cancel-Document" },
137     { 0x0034,                 "Get-Document-Attributes" },
138     { 0x0035,                 "Get-Documents" },
139     { 0x0036,                 "Delete-Document" },
140     { 0x0037,                 "Set-Document-Attributes" },
141     { 0x0038,                 "Cancel-Jobs" },
142     { 0x0039,                 "Cancel-My-Jobs" },
143     { 0x003A,                 "Resubmit-Job" },
144     { 0x003B,                 "Close-Job" },
145     { 0x003C,                 "Identify-Printer" },
146     { 0x003D,                 "Validate-Document" },
147     { 0x003E,                 "Add-Document-Images" },
148     { 0x003F,                 "Acknowledge-Document" },
149     { 0x0040,                 "Acknowledge-Identify-Printer" },
150     { 0x0041,                 "Acknowledge-Job" },
151     { 0x0042,                 "Fetch-Document" },
152     { 0x0043,                 "Fetch-Job" },
153     { 0x0044,                 "Get-Output-Device-Attributes" },
154     { 0x0045,                 "Update-Active-Jobs" },
155     { 0x0046,                 "Deregister-Output-Device" },
156     { 0x0047,                 "Update-Document-Status" },
157     { 0x0048,                 "Update-Job-Status" },
158     { 0x0049,                 "Update-Output-Device-Attributes" },
159     { 0x004A,                 "Get-Next-Document-Data" },
160     { 0x4001,                 "CUPS-Get-Default" },
161     { 0x4002,                 "CUPS-Get-Printers" },
162     { 0x4003,                 "CUPS-Add-Modify-Printer" },
163     { 0x4004,                 "CUPS-Delete-Printer" },
164     { 0x4005,                 "CUPS-Get-Classes" },
165     { 0x4006,                 "CUPS-Add-Modify-Class" },
166     { 0x4007,                 "CUPS-Delete-Class" },
167     { 0x4008,                 "CUPS-Accept-Jobs" },
168     { 0x4009,                 "CUPS-Reject-Jobs" },
169     { 0x400A,                 "CUPS-Set-Default" },
170     { 0x400B,                 "CUPS-Get-Devices" },
171     { 0x400C,                 "CUPS-Get-PPDs" },
172     { 0x400D,                 "CUPS-Move-Job" },
173     { 0x400E,                 "CUPS-Authenticate-Job" },
174     { 0x400F,                 "CUPS-Get-PPD" },
175     { 0x4027,                 "CUPS-Get-Document" },
176     { 0x4028,                 "CUPS-Create-Local-Printer" },
177     { 0,                      NULL }
178 };
179 
180 /* Printer States */
181 #define PRINTER_STATE_IDLE      0x3
182 #define PRINTER_STATE_PROCESSING    0x4
183 #define PRINTER_STATE_STOPPED       0x5
184 static const value_string printer_state_vals[] = {
185     { PRINTER_STATE_IDLE,       "idle" },
186     { PRINTER_STATE_PROCESSING, "processing" },
187     { PRINTER_STATE_STOPPED,    "stopped" },
188     { 0, NULL }
189 };
190 
191 /* Job States */
192 static const value_string job_state_vals[] = {
193     { 3, "pending" },
194     { 4, "pending-held" },
195     { 5, "processing" },
196     { 6, "processing-stopped" },
197     { 7, "canceled" },
198     { 8, "aborted" },
199     { 9, "completed" },
200     { 0, NULL }
201 };
202 
203 /* Document States */
204 static const value_string document_state_vals[] = {
205     { 3, "pending" },
206     { 5, "processing" },
207     { 6, "processing-stopped" },
208     { 7, "canceled" },
209     { 8, "aborted" },
210     { 9, "completed" },
211     { 0, NULL }
212 };
213 
214 /* Finishings Values */
215 static const value_string finishings_vals[] = {
216     { 3, "none" },
217     { 4, "staple" },
218     { 5, "punch" },
219     { 6, "cover" },
220     { 7, "bind" },
221     { 8, "saddle-stitch" },
222     { 9, "edge-stitch" },
223     { 10, "fold" },
224     { 11, "trim" },
225     { 12, "bale" },
226     { 13, "booklet-maker" },
227     { 14, "jog-offset" },
228     { 15, "coat" },
229     { 16, "laminate" },
230     { 20, "staple-top-left" },
231     { 21, "staple-bottom-left" },
232     { 22, "staple-top-right" },
233     { 23, "staple-bottom-right" },
234     { 24, "edge-stitch-left" },
235     { 25, "edge-stitch-top" },
236     { 26, "edge-stitch-right" },
237     { 27, "edge-stitch-bottom" },
238     { 28, "staple-dual-left" },
239     { 29, "staple-dual-top" },
240     { 30, "staple-dual-right" },
241     { 31, "staple-dual-bottom" },
242     { 32, "staple-triple-left" },
243     { 33, "staple-triple-top" },
244     { 34, "staple-triple-right" },
245     { 35, "staple-triple-bottom" },
246     { 50, "bind-left" },
247     { 51, "bind-top" },
248     { 52, "bind-right" },
249     { 53, "bind-bottom" },
250     { 60, "trim-after-pages" },
251     { 61, "trim-after-documents" },
252     { 62, "trim-after-copies" },
253     { 63, "trim-after-job" },
254     { 70, "punch-top-left" },
255     { 71, "punch-bottom-left" },
256     { 72, "punch-top-right" },
257     { 73, "punch-bottom-right" },
258     { 74, "punch-dual-left" },
259     { 75, "punch-dual-top" },
260     { 76, "punch-dual-right" },
261     { 77, "punch-dual-bottom" },
262     { 78, "punch-triple-left" },
263     { 79, "punch-triple-top" },
264     { 80, "punch-triple-right" },
265     { 81, "punch-triple-bottom" },
266     { 82, "punch-quad-left" },
267     { 83, "punch-quad-top" },
268     { 84, "punch-quad-right" },
269     { 85, "punch-quad-bottom" },
270     { 86, "punch-multiple-left" },
271     { 87, "punch-multiple-top" },
272     { 88, "punch-multiple-right" },
273     { 89, "punch-multiple-bottom" },
274     { 90, "fold-accordion" },
275     { 91, "fold-double-gate" },
276     { 92, "fold-gate" },
277     { 93, "fold-half" },
278     { 94, "fold-half-z" },
279     { 95, "fold-left-gate" },
280     { 96, "fold-letter" },
281     { 97, "fold-parallel" },
282     { 98, "fold-poster" },
283     { 99, "fold-right-gate" },
284     { 100, "fold-z" },
285     { 0, NULL }
286 };
287 
288 static const value_string orientation_vals[] = {
289     { 3, "portrait" },
290     { 4, "landscape" },
291     { 5, "reverse-landscape" },
292     { 6, "reverse-portrait" },
293     { 7, "none" },
294     { 0, NULL }
295 };
296 
297 static const value_string quality_vals[] = {
298     { 3, "draft" },
299     { 4, "normal" },
300     { 5, "high" },
301     { 0, NULL }
302 };
303 
304 static const value_string transmission_status_vals[] = {
305     { 3, "pending" },
306     { 4, "pending-retry" },
307     { 5, "processing" },
308     { 7, "canceled" },
309     { 8, "aborted" },
310     { 9, "completed" },
311     { 0, NULL }
312 };
313 
314 
315 #define STATUS_SUCCESSFUL    0x0000
316 #define STATUS_INFORMATIONAL 0x0100
317 #define STATUS_REDIRECTION   0x0200
318 #define STATUS_CLIENT_ERROR  0x0400
319 #define STATUS_SERVER_ERROR  0x0500
320 
321 #define STATUS_TYPE_MASK     0xFF00
322 
323 static const value_string status_vals[] = {
324     { 0x0000, "successful-ok" },
325     { 0x0001, "successful-ok-ignored-or-substituted-attributes" },
326     { 0x0002, "successful-ok-conflicting-attributes" },
327     { 0x0003, "successful-ok-ignored-subscriptions" },
328     { 0x0005, "successful-ok-too-many-events" },
329     { 0x0007, "successful-ok-events-complete" },
330     { 0x0400, "client-error-bad-request" },
331     { 0x0401, "client-error-forbidden" },
332     { 0x0402, "client-error-not-authenticated" },
333     { 0x0403, "client-error-not-authorized" },
334     { 0x0404, "client-error-not-possible" },
335     { 0x0405, "client-error-timeout" },
336     { 0x0406, "client-error-not-found" },
337     { 0x0407, "client-error-gone" },
338     { 0x0408, "client-error-request-entity-too-large" },
339     { 0x0409, "client-error-request-value-too-long" },
340     { 0x040A, "client-error-document-format-not-supported" },
341     { 0x040B, "client-error-attributes-or-values-not-supported" },
342     { 0x040C, "client-error-uri-scheme-not-supported" },
343     { 0x040D, "client-error-charset-not-supported" },
344     { 0x040E, "client-error-conflicting-attributes" },
345     { 0x040F, "client-error-compression-not-supported" },
346     { 0x0410, "client-error-compression-error" },
347     { 0x0411, "client-error-document-format-error" },
348     { 0x0412, "client-error-document-access-error" },
349     { 0x0413, "client-error-attributes-not-settable" },
350     { 0x0414, "client-error-ignored-all-subscriptions" },
351     { 0x0415, "client-error-too-many-subscriptions" },
352     { 0x0418, "client-error-document-password-error" },
353     { 0x0419, "client-error-document-permission-error" },
354     { 0x041A, "client-error-document-security-error" },
355     { 0x041B, "client-error-document-unprintable-error" },
356     { 0x041C, "client-error-account-info-needed" },
357     { 0x041D, "client-error-account-closed" },
358     { 0x041E, "client-error-account-limit-reached" },
359     { 0x041F, "client-error-account-authorization-failed" },
360     { 0x0420, "client-error-not-fetchable" },
361     { 0x0500, "server-error-internal-error" },
362     { 0x0501, "server-error-operation-not-supported" },
363     { 0x0502, "server-error-service-unavailable" },
364     { 0x0503, "server-error-version-not-supported" },
365     { 0x0504, "server-error-device-error" },
366     { 0x0505, "server-error-temporary-error" },
367     { 0x0506, "server-error-not-accepting-jobs" },
368     { 0x0507, "server-error-busy" },
369     { 0x0508, "server-error-job-canceled" },
370     { 0x0509, "server-error-multiple-document-jobs-not-supported" },
371     { 0x050A, "server-error-printer-is-deactivated" },
372     { 0x050B, "server-error-too-many-jobs" },
373     { 0x050C, "server-error-too-many-documents" },
374     { 0, NULL }
375 };
376 
377 static int parse_attributes(tvbuff_t *tvb, int offset, proto_tree *tree);
378 static proto_tree *add_integer_tree(proto_tree *tree, tvbuff_t *tvb,
379                                         int offset, int name_length, const gchar *name, int value_length, guint8 tag);
380 static void add_integer_value(const gchar *tag_desc, proto_tree *tree,
381                                         tvbuff_t *tvb, int offset, int name_length, const gchar *name, int value_length, guint8 tag);
382 static proto_tree *add_octetstring_tree(proto_tree *tree, tvbuff_t *tvb,
383                                         int offset, int name_length, const gchar *name, int value_length, guint8 tag);
384 static proto_tree *add_octetstring_value(const gchar *tag_desc, proto_tree *tree,
385                                         tvbuff_t *tvb, int offset, int name_length, const gchar *name, int value_length, guint8 tag);
386 static proto_tree *add_charstring_tree(proto_tree *tree, tvbuff_t *tvb,
387                                         int offset, guint8 tag, int name_length, const gchar *name, int value_length);
388 static void add_charstring_value(const gchar *tag_desc, proto_tree *tree,
389                                         tvbuff_t *tvb, int offset, int name_length, const gchar *name, int value_length, guint8 tag);
390 static int ipp_fmt_collection(tvbuff_t *tvb, int offset, char *buffer, int bufsize);
391 
392 static int
dissect_ipp(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data)393 dissect_ipp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
394 {
395     proto_tree  *ipp_tree;
396     proto_item  *ti;
397     int         offset     = 0;
398     http_message_info_t *message_info = (http_message_info_t *)data;
399     gboolean    is_request;
400     guint16     operation_status;
401     const gchar *status_type;
402     guint32	request_id;
403     conversation_t *conversation;
404     ipp_conv_info_t *ipp_info;
405     ipp_transaction_t *ipp_trans;
406 
407     if (message_info != NULL) {
408         switch (message_info->type) {
409 
410         case HTTP_REQUEST:
411             is_request = TRUE;
412             break;
413 
414         case HTTP_RESPONSE:
415             is_request = FALSE;
416             break;
417 
418         default:
419             /* This isn't strictly correct, but we should never come here anyways */
420             is_request = (pinfo->destport == pinfo->match_uint);
421             break;
422         }
423     } else {
424         /* This isn't strictly correct, but we should never come here anyways */
425         is_request = (pinfo->destport == pinfo->match_uint);
426     }
427 
428     operation_status = tvb_get_ntohs(tvb, 2);
429     request_id       = tvb_get_ntohl(tvb, 4);
430 
431     if (proto_is_frame_protocol(pinfo->layers, "ippusb")) {
432         col_set_str(pinfo->cinfo, COL_PROTOCOL, "IPPUSB");
433         if (is_request)
434             col_add_fstr(pinfo->cinfo, COL_INFO, "IPPUSB Request (%s)", val_to_str(operation_status, operation_vals, "0x%04x"));
435         else
436             col_add_fstr(pinfo->cinfo, COL_INFO, "IPPUSB Response (%s)", val_to_str(operation_status, status_vals, "0x%04x"));
437     } else {
438         col_set_str(pinfo->cinfo, COL_PROTOCOL, "IPP");
439         if (is_request)
440             col_add_fstr(pinfo->cinfo, COL_INFO, "IPP Request (%s)", val_to_str(operation_status, operation_vals, "0x%04x"));
441         else
442             col_add_fstr(pinfo->cinfo, COL_INFO, "IPP Response (%s)", val_to_str(operation_status, status_vals, "0x%04x"));
443     }
444 
445     ti = proto_tree_add_item(tree, proto_ipp, tvb, offset, -1, ENC_NA);
446     ipp_tree = proto_item_add_subtree(ti, ett_ipp);
447 
448     conversation = find_or_create_conversation(pinfo);
449     ipp_info = (ipp_conv_info_t *)conversation_get_proto_data(conversation, proto_ipp);
450     if (!ipp_info) {
451         ipp_info = wmem_new(wmem_file_scope(), ipp_conv_info_t);
452         ipp_info->pdus=wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
453 
454         conversation_add_proto_data(conversation, proto_ipp, ipp_info);
455     }
456     if (!PINFO_FD_VISITED(pinfo)) {
457         if (is_request) {
458             /* This is a request */
459             ipp_trans=wmem_new(wmem_file_scope(), ipp_transaction_t);
460             ipp_trans->req_frame = pinfo->num;
461             ipp_trans->rep_frame = 0;
462             ipp_trans->req_time = pinfo->abs_ts;
463             wmem_map_insert(ipp_info->pdus, GUINT_TO_POINTER(request_id), (void *)ipp_trans);
464         } else {
465             ipp_trans=(ipp_transaction_t *)wmem_map_lookup(ipp_info->pdus, GUINT_TO_POINTER(request_id));
466             if (ipp_trans) {
467                 ipp_trans->rep_frame = pinfo->num;
468             }
469         }
470     } else {
471         ipp_trans=(ipp_transaction_t *)wmem_map_lookup(ipp_info->pdus, GUINT_TO_POINTER(request_id));
472     }
473     if (!ipp_trans) {
474         /* create a "fake" ipp_trans structure */
475         ipp_trans=wmem_new(wmem_packet_scope(), ipp_transaction_t);
476         ipp_trans->req_frame = 0;
477         ipp_trans->rep_frame = 0;
478         ipp_trans->req_time = pinfo->abs_ts;
479     }
480 
481     /* print state tracking in the tree */
482     if (is_request) {
483         /* This is a request */
484         if (ipp_trans->rep_frame) {
485             proto_item *it;
486 
487             it = proto_tree_add_uint(ipp_tree, hf_ipp_response_in,
488                             tvb, 0, 0, ipp_trans->rep_frame);
489             proto_item_set_generated(it);
490         }
491     } else {
492         /* This is a response */
493         if (ipp_trans->req_frame) {
494             proto_item *it;
495             nstime_t ns;
496 
497             it = proto_tree_add_uint(ipp_tree, hf_ipp_response_to,
498                             tvb, 0, 0, ipp_trans->req_frame);
499             proto_item_set_generated(it);
500 
501             nstime_delta(&ns, &pinfo->abs_ts, &ipp_trans->req_time);
502             it = proto_tree_add_time(ipp_tree, hf_ipp_response_time, tvb, 0, 0, &ns);
503             proto_item_set_generated(it);
504         }
505     }
506 
507     proto_tree_add_item(ipp_tree, hf_ipp_version, tvb, offset, 2, ENC_BIG_ENDIAN);
508     offset += 2;
509 
510     if (is_request) {
511         proto_tree_add_item(ipp_tree, hf_ipp_operation_id, tvb, offset, 2, ENC_BIG_ENDIAN);
512     } else {
513         switch (operation_status & STATUS_TYPE_MASK) {
514 
515         case STATUS_SUCCESSFUL:
516             status_type = "Successful";
517             break;
518 
519         case STATUS_INFORMATIONAL:
520             status_type = "Informational";
521             break;
522 
523         case STATUS_REDIRECTION:
524             status_type = "Redirection";
525             break;
526 
527         case STATUS_CLIENT_ERROR:
528             status_type = "Client Error";
529             break;
530 
531         case STATUS_SERVER_ERROR:
532             status_type = "Server Error";
533             break;
534 
535         default:
536             status_type = "Unknown";
537             break;
538         }
539         proto_tree_add_uint_format_value(ipp_tree, hf_ipp_status_code, tvb, offset, 2, operation_status, "%s (%s)", status_type, val_to_str(operation_status, status_vals, "0x%04x"));
540     }
541     offset += 2;
542 
543     proto_tree_add_item(ipp_tree, hf_ipp_request_id, tvb, offset, 4, ENC_BIG_ENDIAN);
544     offset += 4;
545 
546     offset = parse_attributes(tvb, offset, ipp_tree);
547 
548     if (tvb_offset_exists(tvb, offset)) {
549         call_data_dissector(tvb_new_subset_remaining(tvb, offset), pinfo, ipp_tree);
550     }
551     return tvb_captured_length(tvb);
552 }
553 
554 #define TAG_TYPE(x)       ((x) & 0xF0)
555 
556 #define TAG_TYPE_DELIMITER      0x00
557 #define TAG_TYPE_OUTOFBAND      0x10
558 #define TAG_TYPE_INTEGER        0x20
559 #define TAG_TYPE_OCTETSTRING    0x30
560 #define TAG_TYPE_CHARSTRING     0x40
561 
562 #define TAG_END_OF_ATTRIBUTES   0x03
563 
564 #define TAG_INTEGER             0x21
565 #define TAG_BOOLEAN             0x22
566 #define TAG_ENUM                0x23
567 
568 #define TAG_OCTETSTRING         0x30
569 #define TAG_DATETIME            0x31
570 #define TAG_RESOLUTION          0x32
571 #define TAG_RANGEOFINTEGER      0x33
572 #define TAG_BEGCOLLECTION       0x34
573 #define TAG_TEXTWITHLANGUAGE    0x35
574 #define TAG_NAMEWITHLANGUAGE    0x36
575 #define TAG_ENDCOLLECTION       0x37
576 
577 #define TAG_TEXTWITHOUTLANGUAGE 0x41
578 #define TAG_NAMEWITHOUTLANGUAGE 0x42
579 #define TAG_KEYWORD             0x44
580 #define TAG_URI                 0x45
581 #define TAG_URISCHEME           0x46
582 #define TAG_CHARSET             0x47
583 #define TAG_NATURALLANGUAGE     0x48
584 #define TAG_MIMEMEDIATYPE       0x49
585 #define TAG_MEMBERATTRNAME      0x4a
586 
587 static const value_string tag_vals[] = {
588     /* Delimiter tags */
589     { 0x01,                    "operation-attributes-tag" },
590     { 0x02,                    "job-attributes-tag" },
591     { TAG_END_OF_ATTRIBUTES,   "end-of-attributes-tag" },
592     { 0x04,                    "printer-attributes-tag" },
593     { 0x05,                    "unsupported-attributes-tag" },
594     { 0x06,                    "subscription-attributes-tag" },
595     { 0x07,                    "event-notification-attributes-tag" },
596     { 0x08,                    "resource-attributes-tag" },
597     { 0x09,                    "document-attributes-tag" },
598 
599     /* Value tags */
600     { 0x10,                    "unsupported" },
601     { 0x12,                    "unknown" },
602     { 0x13,                    "no-value" },
603     { 0x15,                    "not-settable" },
604     { 0x16,                    "delete-attribute" },
605     { 0x17,                    "admin-define" },
606     { TAG_INTEGER,             "integer" },
607     { TAG_BOOLEAN,             "boolean" },
608     { TAG_ENUM,                "enum" },
609     { TAG_OCTETSTRING,         "octetString" },
610     { TAG_DATETIME,            "dateTime" },
611     { TAG_RESOLUTION,          "resolution" },
612     { TAG_RANGEOFINTEGER,      "rangeOfInteger" },
613     { TAG_BEGCOLLECTION,       "collection" }, /* Technically "begCollection" for encoding but "collection" for attribute syntax */
614     { TAG_TEXTWITHLANGUAGE,    "textWithLanguage" },
615     { TAG_NAMEWITHLANGUAGE,    "nameWithLanguage" },
616     { TAG_ENDCOLLECTION,       "endCollection" },
617     { TAG_TEXTWITHOUTLANGUAGE, "textWithoutLanguage" },
618     { TAG_NAMEWITHOUTLANGUAGE, "nameWithoutLanguage" },
619     { TAG_KEYWORD,             "keyword" },
620     { TAG_URI,                 "uri" },
621     { TAG_URISCHEME,           "uriScheme" },
622     { TAG_CHARSET,             "charset" },
623     { TAG_NATURALLANGUAGE,     "naturalLanguage" },
624     { TAG_MIMEMEDIATYPE,       "mimeMediaType" },
625     { TAG_MEMBERATTRNAME,      "memberAttrName" },
626     { 0,                       NULL }
627 };
628 
629 static int
parse_attributes(tvbuff_t * tvb,int offset,proto_tree * tree)630 parse_attributes(tvbuff_t *tvb, int offset, proto_tree *tree)
631 {
632     guint8       tag;
633     const gchar *tag_desc;
634     gchar       *name = "";
635     int          name_length, value_length;
636     proto_tree  *as_tree      = tree;
637     proto_item  *tas          = NULL;
638     int          start_offset = offset;
639     proto_tree  *attr_tree    = tree;
640     proto_tree  *subtree      = NULL;
641 
642     while (tvb_offset_exists(tvb, offset)) {
643         tag = tvb_get_guint8(tvb, offset);
644         tag_desc = val_to_str(tag, tag_vals, "unknown-%02x");
645         if (TAG_TYPE(tag) == TAG_TYPE_DELIMITER) {
646             /*
647              * If we had an attribute sequence we were
648              * working on, we're done with it; set its
649              * length to the length of all the stuff
650              * we've done so far.
651              */
652             name = "";
653 
654             if (tas != NULL)
655                 proto_item_set_len(tas, offset - start_offset);
656 
657             /*
658              * This tag starts a new attribute sequence;
659              * create a new tree under this tag when we see
660              * a non-delimiter tag, under which to put
661              * those attributes.
662              */
663             as_tree   = NULL;
664             attr_tree = tree;
665 
666             /*
667              * Remember the offset at which this attribute
668              * sequence started, so we can use it to compute
669              * its length when it's finished.
670              */
671             start_offset = offset;
672 
673             /*
674              * Now create a new item for this tag.
675              */
676             subtree = proto_tree_add_subtree(tree, tvb, offset, 1, ett_ipp_as, &tas, tag_desc);
677             offset += 1;
678             if (tag == TAG_END_OF_ATTRIBUTES) {
679                 /*
680                  * No more attributes.
681                  */
682                 break;
683             }
684         } else {
685             /*
686              * Value tag - get the name length.
687              */
688             name_length = tvb_get_ntohs(tvb, offset + 1);
689             if (name_length != 0)
690               name = tvb_format_text(wmem_packet_scope(), tvb, offset + 1 + 2, name_length);
691 
692             /*
693              * OK, get the value length.
694              */
695             value_length = tvb_get_ntohs(tvb, offset + 1 + 2 + name_length);
696             if (tag == TAG_MEMBERATTRNAME && value_length != 0)
697               name = tvb_format_text(wmem_packet_scope(), tvb, offset + 1 + 2 + name_length + 2, value_length);
698 
699             /*
700              * OK, does the value run past the end of the
701              * frame?
702              */
703             if (as_tree == NULL) {
704                 /*
705                  * OK, there's an attribute to hang
706                  * under a delimiter tag, but we don't
707                  * have a tree for that tag yet; create
708                  * a tree.
709                  */
710                 as_tree = subtree;
711                 attr_tree = as_tree;
712             }
713 
714             switch (TAG_TYPE(tag)) {
715                 case TAG_TYPE_OUTOFBAND :
716                     if (name_length != 0) {
717                         /*
718                          * This is an attribute, not
719                          * an additional value, so
720                          * start a tree for it.
721                          */
722                         attr_tree = proto_tree_add_subtree_format(as_tree, tvb, offset, 1 + 2 + name_length + 2 + value_length, ett_ipp_attr, NULL, "%s (%s)", name, tag_desc);
723                     }
724                     proto_tree_add_item(attr_tree, hf_ipp_outofband_value, tvb, offset, 1, ENC_NA);
725                     break;
726 
727                 case TAG_TYPE_INTEGER :
728                     if (name_length != 0) {
729                         /*
730                          * This is an attribute, not
731                          * an additional value, so
732                          * start a tree for it.
733                          */
734                         attr_tree = add_integer_tree(as_tree, tvb, offset, name_length, name, value_length, tag);
735                     }
736                     add_integer_value(tag_desc, attr_tree, tvb, offset, name_length, name, value_length, tag);
737                     break;
738 
739                 case TAG_TYPE_OCTETSTRING :
740                     if (name_length != 0) {
741                         /*
742                          * This is an attribute, not
743                          * an additional value, so
744                          * start a tree for it.
745                          */
746                         attr_tree = add_octetstring_tree(as_tree, tvb, offset, name_length, name, value_length, tag);
747                     }
748                     if (tag == TAG_ENDCOLLECTION && attr_tree &&  attr_tree->parent)
749                         attr_tree = attr_tree->parent;
750                     else
751                         attr_tree = add_octetstring_value(tag_desc, attr_tree, tvb, offset, name_length, name, value_length, tag);
752                     break;
753 
754                 case TAG_TYPE_CHARSTRING :
755                     if (name_length != 0) {
756                         /*
757                          * This is an attribute, not
758                          * an additional value, so
759                          * start a tree for it.
760                          */
761                         attr_tree = add_charstring_tree(as_tree, tvb, offset, tag, name_length, name, value_length);
762                     }
763                     add_charstring_value(tag_desc, attr_tree, tvb, offset, name_length, name, value_length, tag);
764                     break;
765 
766                 default :
767                     if (name_length != 0) {
768                         /*
769                          * This is an attribute, not
770                          * an additional value, so
771                          * start a tree for it.
772                          */
773                         attr_tree = proto_tree_add_subtree_format(as_tree, tvb, offset, 1 + 2 + name_length + 2 + value_length, ett_ipp_attr, NULL, "%s (%s)", name, tag_desc);
774                     }
775                     proto_tree_add_item(attr_tree, hf_ipp_unknown_value, tvb, offset + 1 + 2 + name_length + 2, value_length, ENC_NA);
776                     break;
777             }
778             offset += 1 + 2 + name_length + 2 + value_length;
779         }
780     }
781 
782     return offset;
783 }
784 
785 static proto_tree *
add_integer_tree(proto_tree * tree,tvbuff_t * tvb,int offset,int name_length,const gchar * name,int value_length,guint8 tag)786 add_integer_tree(proto_tree *tree, tvbuff_t *tvb, int offset,
787                  int name_length, const gchar *name, int value_length, guint8 tag)
788 {
789     int count = 0;
790     const char *type = val_to_str_const(tag, tag_vals, "unknown-%02x");
791     gchar *value = NULL;
792     int valoffset = offset;
793 
794     switch (tag) {
795         case TAG_BOOLEAN:
796             if (value_length == 1) {
797                 value = wmem_strdup(wmem_packet_scope(), tvb_get_guint8(tvb, offset + 1 + 2 + name_length + 2) ? "true" : "false");
798             }
799             else {
800                 value = wmem_strdup(wmem_packet_scope(), "???");
801             }
802             valoffset += 1 + 2 + name_length + 2 + value_length;
803             break;
804 
805         case TAG_INTEGER :
806             do
807             {
808                /*
809                 * Add the range/integer...
810                 */
811 
812                 char* temp;
813 
814                 count ++;
815 
816                 valoffset += 1 + 2 + name_length + 2;
817 
818                 if (!tvb_offset_exists(tvb, valoffset + value_length))
819                     break;
820 
821                 if (value_length == 8) {
822                     guint32 lower = tvb_get_ntohl(tvb, valoffset + 0);
823                     guint32 upper = tvb_get_ntohl(tvb, valoffset + 4);
824 
825                     temp = wmem_strdup_printf(wmem_packet_scope(), "%d-%d", lower, upper);
826                 }
827                 else if (value_length == 4) {
828                     temp = wmem_strdup_printf(wmem_packet_scope(), "%d", tvb_get_ntohl(tvb, valoffset + 0));
829                 }
830                 else {
831                     temp = "???";
832                 }
833 
834                 if (value)
835                     value = wmem_strconcat(wmem_packet_scope(), value, ",", temp, NULL);
836                 else
837                     value = wmem_strdup(wmem_packet_scope(), temp);
838 
839                 valoffset += value_length;
840 
841                /*
842                 * Move to the next value...
843                 */
844 
845                 if (!tvb_offset_exists(tvb, valoffset + 3))
846                     break;
847 
848                 tag         = tvb_get_guint8(tvb, valoffset);
849                 name_length = tvb_get_ntohs(tvb, valoffset + 1);
850                 if (!tvb_offset_exists(tvb, valoffset + 1 + 2 + name_length + 2))
851                     break;
852 
853                 value_length = tvb_get_ntohs(tvb, valoffset + 1 + 2 + name_length);
854             }
855             while (name_length == 0 && (tag == TAG_INTEGER || tag == TAG_RANGEOFINTEGER));
856             break;
857 
858         case TAG_ENUM :
859             do
860             {
861                /*
862                 * Add the range/integer...
863                 */
864                 const gchar* temp;
865 
866                 count ++;
867 
868                 valoffset += 1 + 2 + name_length + 2;
869 
870                 if (!tvb_offset_exists(tvb, valoffset + value_length))
871                     break;
872 
873                 if (value_length != 4) {
874                     temp = "???";
875                 } else {
876                     if (!strncmp(name, "printer-state", 13)) {
877                         temp = val_to_str_const(tvb_get_ntohl(tvb, valoffset), printer_state_vals, "unknown-%d");
878                     }
879                     else if (!strncmp(name, "job-state", 9)) {
880                         temp = val_to_str_const(tvb_get_ntohl(tvb, valoffset), job_state_vals, "unknown-%d");
881                     }
882                     else if (!strncmp(name, "document-state", 14)) {
883                         temp = val_to_str_const(tvb_get_ntohl(tvb, valoffset), document_state_vals, "unknown-%d");
884                     }
885                     else if (!strncmp(name, "operations-supported", 20)) {
886                         temp = val_to_str_const(tvb_get_ntohl(tvb, valoffset), operation_vals, "unknown-%04x");
887                     }
888                     else if (!strncmp(name, "finishings", 10)) {
889                         temp = val_to_str_const(tvb_get_ntohl(tvb, valoffset), finishings_vals, "unknown-%d");
890                     }
891                     else if (!strncmp(name, "orientation-requested", 21) || !strncmp(name, "media-feed-orientation", 22)) {
892                         temp = val_to_str_const(tvb_get_ntohl(tvb, valoffset), orientation_vals, "unknown-%d");
893                     }
894                     else if (!strncmp(name, "print-quality", 13)) {
895                         temp = val_to_str_const(tvb_get_ntohl(tvb, valoffset), quality_vals, "unknown-%d");
896                     }
897                     else if (!strncmp(name, "transmission-status", 19)) {
898                         temp = val_to_str_const(tvb_get_ntohl(tvb, valoffset), transmission_status_vals, "unknown-%d");
899                     }
900                     else {
901                         temp = wmem_strdup_printf(wmem_packet_scope(), "%d", tvb_get_ntohl(tvb, offset + 1 + 2 + name_length + 2));
902                     }
903                 }
904 
905                 if (value)
906                     value = wmem_strconcat(wmem_packet_scope(), value, ",", temp, NULL);
907                 else
908                     value = wmem_strdup(wmem_packet_scope(), temp);
909 
910                 valoffset += value_length;
911 
912                /*
913                 * Move to the next value...
914                 */
915 
916                 if (!tvb_offset_exists(tvb, valoffset + 3))
917                     break;
918 
919                 tag         = tvb_get_guint8(tvb, valoffset);
920                 name_length = tvb_get_ntohs(tvb, valoffset + 1);
921                 if (!tvb_offset_exists(tvb, valoffset + 1 + 2 + name_length + 2))
922                     break;
923 
924                 value_length = tvb_get_ntohs(tvb, valoffset + 1 + 2 + name_length);
925             }
926             while (name_length == 0 && tag == TAG_ENUM);
927             break;
928 
929         default:
930             value = wmem_strdup(wmem_packet_scope(), "???");
931             break;
932     }
933 
934     return proto_tree_add_subtree_format(tree, tvb, offset, valoffset - offset, ett_ipp_attr, NULL, "%s (%s%s): %s", name, count > 1 ? "1setOf " : "", type, value);
935 }
936 
937 static void
add_integer_value(const gchar * tag_desc,proto_tree * tree,tvbuff_t * tvb,int offset,int name_length,const gchar * name,int value_length,guint8 tag)938 add_integer_value(const gchar *tag_desc, proto_tree *tree, tvbuff_t *tvb,
939                   int offset, int name_length, const gchar *name, int value_length, guint8 tag)
940 {
941     int valoffset = offset + 1 + 2 + name_length + 2;
942 
943     if (name_length > 0)
944         proto_tree_add_item(tree, hf_ipp_name, tvb, offset + 1 + 2, name_length, ENC_ASCII|ENC_NA);
945 
946     switch (tag) {
947         case TAG_BOOLEAN:
948             if (value_length == 1) {
949                 proto_tree_add_item(tree, hf_ipp_boolean_value, tvb, valoffset, value_length, ENC_BIG_ENDIAN);
950             }
951             else {
952                 proto_tree_add_boolean_format(tree, hf_ipp_boolean_value, tvb, valoffset, value_length, 0, "boolean value: ??? %d bytes ???", value_length);
953             }
954             break;
955 
956         case TAG_INTEGER:
957             if (value_length == 4) {
958                 proto_tree_add_item(tree, hf_ipp_integer_value, tvb, valoffset, value_length, ENC_BIG_ENDIAN);
959             }
960             else {
961                 proto_tree_add_int_format(tree, hf_ipp_integer_value, tvb, valoffset, value_length, 0, "integer value: ??? %d bytes ???", value_length);
962             }
963             break;
964 
965         case TAG_ENUM:
966             if (value_length == 4) {
967                 if (!strncmp(name, "printer-state", 13)) {
968                     proto_tree_add_item(tree, hf_ipp_enum_value_printer_state, tvb, valoffset, value_length, ENC_BIG_ENDIAN);
969                 }
970                 else if (!strncmp(name, "job-state", 9)) {
971                     proto_tree_add_item(tree, hf_ipp_enum_value_job_state, tvb, valoffset, value_length, ENC_BIG_ENDIAN);
972                 }
973                 else if (!strncmp(name, "document-state", 14)) {
974                     proto_tree_add_item(tree, hf_ipp_enum_value_document_state, tvb, valoffset, value_length, ENC_BIG_ENDIAN);
975                 }
976                 else if (!strncmp(name, "operations-supported", 20)) {
977                     proto_tree_add_item(tree, hf_ipp_enum_value_operations_supported, tvb, valoffset, value_length, ENC_BIG_ENDIAN);
978                 }
979                 else if (!strncmp(name, "finishings", 10)) {
980                     proto_tree_add_item(tree, hf_ipp_enum_value_finishings, tvb, valoffset, value_length, ENC_BIG_ENDIAN);
981                 }
982                 else if (!strncmp(name, "orientation-requested", 21) || !strncmp(name, "media-feed-orientation", 22)) {
983                     proto_tree_add_item(tree, hf_ipp_enum_value_orientation, tvb, valoffset, value_length, ENC_BIG_ENDIAN);
984                 }
985                 else if (!strncmp(name, "print-quality", 13)) {
986                     proto_tree_add_item(tree, hf_ipp_enum_value_print_quality, tvb, valoffset, value_length, ENC_BIG_ENDIAN);
987                 }
988                 else if (!strncmp(name, "transmission-status", 19)) {
989                     proto_tree_add_item(tree, hf_ipp_enum_value_transmission_status, tvb, valoffset, value_length, ENC_BIG_ENDIAN);
990                 }
991                 else {
992                     proto_tree_add_item(tree, hf_ipp_enum_value, tvb, valoffset, value_length, ENC_BIG_ENDIAN);
993                 }
994             }
995             else {
996                 proto_tree_add_int_format_value(tree, hf_ipp_enum_value, tvb, valoffset, value_length, 0, "??? %d bytes ???", value_length);
997             }
998             break;
999 
1000         default :
1001             proto_tree_add_int_format(tree, hf_ipp_integer_value, tvb, valoffset, value_length, 0, "%s value: ??? %d bytes ???", tag_desc, value_length);
1002             break;
1003     }
1004 }
1005 
1006 static proto_tree *
add_octetstring_tree(proto_tree * tree,tvbuff_t * tvb,int offset,int name_length,const gchar * name,int value_length,guint8 tag)1007 add_octetstring_tree(proto_tree *tree, tvbuff_t *tvb, int offset, int name_length, const gchar *name, int value_length, guint8 tag)
1008 {
1009     int count = 0;
1010     const char *type = val_to_str_const(tag, tag_vals, "unknown-%02x");
1011     gchar *value = NULL;
1012     int valoffset = offset;
1013 
1014     switch (tag) {
1015         case TAG_OCTETSTRING :
1016             do {
1017                /*
1018                 * Add the string...
1019                 */
1020 
1021                 count ++;
1022                 if (value)
1023                     value = wmem_strconcat(wmem_packet_scope(), value, ",'", tvb_format_text(wmem_packet_scope(), tvb, valoffset + 1 + 2 + name_length + 2, value_length), "'", NULL);
1024                 else
1025                     value = wmem_strconcat(wmem_packet_scope(), "'", tvb_format_text(wmem_packet_scope(), tvb, valoffset + 1 + 2 + name_length + 2, value_length), "'", NULL);
1026 
1027                /*
1028                 * Move to the next value...
1029                 */
1030 
1031                 valoffset += 1 + 2 + name_length + 2 + value_length;
1032 
1033                 if (!tvb_offset_exists(tvb, valoffset + 3))
1034                     break;
1035 
1036                 tag         = tvb_get_guint8(tvb, valoffset);
1037                 name_length = tvb_get_ntohs(tvb, valoffset + 1);
1038                 if (!tvb_offset_exists(tvb, valoffset + 1 + 2 + name_length + 2))
1039                     break;
1040 
1041                 value_length = tvb_get_ntohs(tvb, valoffset + 1 + 2 + name_length);
1042             }
1043             while (name_length == 0 && tag == TAG_OCTETSTRING);
1044             break;
1045 
1046         case TAG_DATETIME :
1047             valoffset += 1 + 2 + name_length + 2;
1048 
1049             if (value_length == 11) {
1050                 guint16 year = tvb_get_ntohs(tvb, valoffset + 0);
1051                 guint8 month = tvb_get_guint8(tvb, valoffset + 2);
1052                 guint8 day = tvb_get_guint8(tvb, valoffset + 3);
1053                 guint8 hours = tvb_get_guint8(tvb, valoffset + 4);
1054                 guint8 minutes = tvb_get_guint8(tvb, valoffset + 5);
1055                 guint8 seconds = tvb_get_guint8(tvb, valoffset + 6);
1056                 guint8 decisecs = tvb_get_guint8(tvb, valoffset + 7);
1057                 guint8 utcsign = tvb_get_guint8(tvb, valoffset + 8);
1058                 guint8 utchours = tvb_get_guint8(tvb, valoffset + 9);
1059                 guint8 utcminutes = tvb_get_guint8(tvb, valoffset + 10);
1060 
1061                 value = wmem_strdup_printf(wmem_packet_scope(), "%04d-%02d-%02dT%02d:%02d:%02d.%d%c%02d%02d", year, month, day, hours, minutes, seconds, decisecs, utcsign, utchours, utcminutes);
1062             } else {
1063                 value = wmem_strdup(wmem_packet_scope(), "???");
1064             }
1065 
1066             valoffset += value_length;
1067             break;
1068 
1069         case TAG_RESOLUTION :
1070             do {
1071                /*
1072                 * Add the resolution...
1073                 */
1074 
1075                 char* temp;
1076 
1077                 count ++;
1078 
1079                 valoffset += 1 + 2 + name_length + 2;
1080 
1081                 if (value_length == 9 && tvb_offset_exists(tvb, valoffset + value_length)) {
1082                     int xres = tvb_get_ntohl(tvb, valoffset + 0);
1083                     int yres = tvb_get_ntohl(tvb, valoffset + 4);
1084                     guint8 units = tvb_get_guint8(tvb, valoffset + 8);
1085 
1086                     temp = wmem_strdup_printf(wmem_packet_scope(), "%dx%d%s", xres, yres, units == 3 ? "dpi" : units == 4 ? "dpcm" : "unknown");
1087                 }
1088                 else {
1089                     temp = "???";
1090                 }
1091 
1092                 if (value)
1093                     value = wmem_strconcat(wmem_packet_scope(), value, ",", temp, NULL);
1094                 else
1095                     value = wmem_strdup(wmem_packet_scope(), temp);
1096 
1097                 valoffset += value_length;
1098 
1099                /*
1100                 * Move to the next value...
1101                 */
1102 
1103                 if (!tvb_offset_exists(tvb, valoffset + 3))
1104                     break;
1105 
1106                 tag         = tvb_get_guint8(tvb, valoffset);
1107                 name_length = tvb_get_ntohs(tvb, valoffset + 1);
1108                 if (!tvb_offset_exists(tvb, valoffset + 1 + 2 + name_length + 2))
1109                     break;
1110 
1111                 value_length = tvb_get_ntohs(tvb, valoffset + 1 + 2 + name_length);
1112             }
1113             while (name_length == 0 && tag == TAG_RESOLUTION);
1114             break;
1115 
1116         case TAG_RANGEOFINTEGER :
1117             do {
1118                /*
1119                 * Add the range/integer...
1120                 */
1121 
1122                 char* temp;
1123 
1124                 count ++;
1125 
1126                 valoffset += 1 + 2 + name_length + 2;
1127 
1128                 if (!tvb_offset_exists(tvb, valoffset + value_length))
1129                     break;
1130 
1131                 if (value_length == 8) {
1132                     guint32 lower = tvb_get_ntohl(tvb, valoffset + 0);
1133                     guint32 upper = tvb_get_ntohl(tvb, valoffset + 4);
1134 
1135                     temp = wmem_strdup_printf(wmem_packet_scope(), "%d-%d", lower, upper);
1136                 }
1137                 else if (value_length == 4) {
1138                     temp = wmem_strdup_printf(wmem_packet_scope(), "%d", tvb_get_ntohl(tvb, valoffset + 0));
1139                 }
1140                 else {
1141                     temp = "???";
1142                 }
1143 
1144                 if (value)
1145                     value = wmem_strconcat(wmem_packet_scope(), value, ",", temp, NULL);
1146                 else
1147                     value = wmem_strdup(wmem_packet_scope(), temp);
1148 
1149                 valoffset += value_length;
1150 
1151                /*
1152                 * Move to the next value...
1153                 */
1154 
1155                 if (!tvb_offset_exists(tvb, valoffset + 3))
1156                     break;
1157 
1158                 tag         = tvb_get_guint8(tvb, valoffset);
1159                 name_length = tvb_get_ntohs(tvb, valoffset + 1);
1160                 if (!tvb_offset_exists(tvb, valoffset + 1 + 2 + name_length + 2))
1161                     break;
1162 
1163                 value_length = tvb_get_ntohs(tvb, valoffset + 1 + 2 + name_length);
1164             }
1165             while (name_length == 0 && (tag == TAG_RANGEOFINTEGER || tag == TAG_INTEGER));
1166             break;
1167 
1168         case TAG_TEXTWITHLANGUAGE :
1169         case TAG_NAMEWITHLANGUAGE :
1170             do {
1171                /*
1172                 * Add the string...
1173                 */
1174 
1175                 char *temp = NULL;
1176 
1177                 count ++;
1178 
1179                 if ((tag == TAG_NAMEWITHLANGUAGE || tag == TAG_TEXTWITHLANGUAGE) && value_length > 4) {
1180                     int language_length = tvb_get_ntohs(tvb, valoffset + 0);
1181                     int string_length;
1182 
1183                     if (tvb_offset_exists(tvb, valoffset + 2 + language_length)) {
1184                         string_length = tvb_get_ntohs(tvb, valoffset + 2 + language_length);
1185                         if (tvb_offset_exists(tvb, valoffset + 2 + language_length + 2 + string_length)) {
1186                             temp = wmem_strdup_printf(wmem_packet_scope(), "'%s'(%s)", tvb_format_text(wmem_packet_scope(), tvb, valoffset + 1 + 2 + name_length + 2 + 2 + language_length + 2, string_length), tvb_format_text(wmem_packet_scope(), tvb, valoffset + 1 + 2 + name_length + 2 + 2, language_length));
1187                         }
1188                     }
1189                 }
1190                 else {
1191                     temp = wmem_strdup_printf(wmem_packet_scope(), "'%s'", tvb_format_text(wmem_packet_scope(), tvb, valoffset + 1 + 2 + name_length + 2, value_length));
1192                 }
1193 
1194                 if (value)
1195                     value = wmem_strconcat(wmem_packet_scope(), value, ",", temp, NULL);
1196                 else
1197                     value = wmem_strdup(wmem_packet_scope(), temp);
1198 
1199                /*
1200                 * Move to the next value...
1201                 */
1202 
1203                 valoffset += 1 + 2 + name_length + 2 + value_length;
1204 
1205                 if (!tvb_offset_exists(tvb, valoffset + 3))
1206                     break;
1207 
1208                 tag         = tvb_get_guint8(tvb, valoffset);
1209                 name_length = tvb_get_ntohs(tvb, valoffset + 1);
1210                 if (!tvb_offset_exists(tvb, valoffset + 1 + 2 + name_length + 2))
1211                     break;
1212 
1213                 value_length = tvb_get_ntohs(tvb, valoffset + 1 + 2 + name_length);
1214             }
1215             while (name_length == 0 && (TAG_TYPE(tag) == TAG_TYPE_CHARSTRING || tag == TAG_NAMEWITHLANGUAGE || tag == TAG_TEXTWITHLANGUAGE));
1216             break;
1217 
1218         case TAG_BEGCOLLECTION :
1219             do {
1220                /*
1221                 * Add the member attribute...
1222                 */
1223 
1224                 char temp[1024];
1225 
1226                 count ++;
1227 
1228                 valoffset = ipp_fmt_collection(tvb, valoffset + 1 + 2 + name_length + 2 + value_length, temp, sizeof(temp));
1229 
1230                 if (value)
1231                     value = wmem_strconcat(wmem_packet_scope(), value, ",", temp, NULL);
1232                 else
1233                     value = wmem_strdup(wmem_packet_scope(), temp);
1234 
1235                /*
1236                 * Move to the next value...
1237                 */
1238 
1239                 if (!tvb_offset_exists(tvb, valoffset + 3))
1240                     break;
1241 
1242                 tag         = tvb_get_guint8(tvb, valoffset);
1243                 name_length = tvb_get_ntohs(tvb, valoffset + 1);
1244                 if (!tvb_offset_exists(tvb, valoffset + 1 + 2 + name_length + 2))
1245                     break;
1246 
1247                 value_length = tvb_get_ntohs(tvb, valoffset + 1 + 2 + name_length);
1248             }
1249             while (name_length == 0 && tag == TAG_BEGCOLLECTION);
1250             break;
1251 
1252         default :
1253             value = tvb_bytes_to_str(wmem_packet_scope(), tvb, offset + 1 + 2 + name_length + 2, value_length);
1254 
1255             valoffset += 1 + 2 + name_length + 2 + value_length;
1256             break;
1257     }
1258 
1259     return proto_tree_add_subtree_format(tree, tvb, offset, valoffset - offset, ett_ipp_attr, NULL, "%s (%s%s): %s", name, count > 1 ? "1setOf " : "", type, value);
1260 }
1261 
1262 static proto_tree *
add_octetstring_value(const gchar * tag_desc,proto_tree * tree,tvbuff_t * tvb,int offset,int name_length,const gchar * name _U_,int value_length,guint8 tag)1263 add_octetstring_value(const gchar *tag_desc, proto_tree *tree, tvbuff_t *tvb,
1264                       int offset, int name_length, const gchar *name _U_, int value_length, guint8 tag)
1265 {
1266     proto_tree *subtree = tree;
1267     char value[176];
1268     int valoffset = offset + 1 + 2 + name_length + 2;
1269     int endoffset;
1270 
1271     if (name_length > 0)
1272         proto_tree_add_item(tree, hf_ipp_name, tvb, offset + 1 + 2, name_length, ENC_ASCII|ENC_NA);
1273 
1274     switch (tag) {
1275         case TAG_OCTETSTRING :
1276             proto_tree_add_item(tree, hf_ipp_octetstring_value, tvb, valoffset, value_length, ENC_ASCII|ENC_NA);
1277             break;
1278 
1279         case TAG_DATETIME :
1280             if (value_length == 11) {
1281                 guint16 year = tvb_get_ntohs(tvb, valoffset + 0);
1282                 guint8 month = tvb_get_guint8(tvb, valoffset + 2);
1283                 guint8 day = tvb_get_guint8(tvb, valoffset + 3);
1284                 guint8 hours = tvb_get_guint8(tvb, valoffset + 4);
1285                 guint8 minutes = tvb_get_guint8(tvb, valoffset + 5);
1286                 guint8 seconds = tvb_get_guint8(tvb, valoffset + 6);
1287                 guint8 decisecs = tvb_get_guint8(tvb, valoffset + 7);
1288                 guint8 utcsign = tvb_get_guint8(tvb, valoffset + 8);
1289                 guint8 utchours = tvb_get_guint8(tvb, valoffset + 9);
1290                 guint8 utcminutes = tvb_get_guint8(tvb, valoffset + 10);
1291 
1292                 proto_tree_add_bytes_format(tree, hf_ipp_datetime_value, tvb, valoffset, value_length, NULL, "dateTime value: %04d-%02d-%02dT%02d:%02d:%02d.%d%c%02d%02d", year, month, day, hours, minutes, seconds, decisecs, utcsign, utchours, utcminutes);
1293             }
1294             else {
1295                 proto_tree_add_item(tree, hf_ipp_datetime_value, tvb, valoffset, value_length, ENC_NA);
1296             }
1297             break;
1298 
1299         case TAG_RESOLUTION :
1300             if (value_length == 9) {
1301                 int xres = tvb_get_ntohl(tvb, valoffset + 0);
1302                 int yres = tvb_get_ntohl(tvb, valoffset + 4);
1303                 guint8 units = tvb_get_guint8(tvb, valoffset + 8);
1304 
1305                 proto_tree_add_bytes_format(tree, hf_ipp_resolution_value, tvb, valoffset, value_length, NULL, "resolution value: %dx%d%s", xres, yres, units == 3 ? "dpi" : units == 4 ? "dpcm" : "unknown");
1306             }
1307             else {
1308                 proto_tree_add_item(tree, hf_ipp_resolution_value, tvb, valoffset, value_length, ENC_NA);
1309             }
1310             break;
1311 
1312         case TAG_RANGEOFINTEGER :
1313             if (value_length == 8) {
1314                 int lower = tvb_get_ntohl(tvb, valoffset + 0);
1315                 int upper = tvb_get_ntohl(tvb, valoffset + 4);
1316 
1317                 proto_tree_add_bytes_format(tree, hf_ipp_rangeofinteger_value, tvb, valoffset, value_length, NULL, "rangeOfInteger value: %d-%d", lower, upper);
1318             }
1319             else {
1320                 proto_tree_add_item(tree, hf_ipp_rangeofinteger_value, tvb, valoffset, value_length, ENC_NA);
1321             }
1322             break;
1323 
1324         case TAG_TEXTWITHLANGUAGE :
1325         case TAG_NAMEWITHLANGUAGE :
1326             if (value_length > 4) {
1327                 int language_length = tvb_get_ntohs(tvb, valoffset + 0);
1328 
1329                 if (tvb_offset_exists(tvb, valoffset + 2 + language_length)) {
1330                     int string_length = tvb_get_ntohs(tvb, valoffset + 2 + language_length);
1331                     if (tvb_offset_exists(tvb, valoffset + 2 + language_length + 2 + string_length)) {
1332                         proto_tree_add_bytes_format(tree, tag == TAG_NAMEWITHLANGUAGE ? hf_ipp_namewithlanguage_value : hf_ipp_textwithlanguage_value, tvb, valoffset, value_length, NULL, "%s value: '%s'(%s)", tag_desc, tvb_format_text(wmem_packet_scope(), tvb, valoffset + 1 + 2 + name_length + 2 + 2 + language_length + 2, string_length), tvb_format_text(wmem_packet_scope(), tvb, valoffset + 1 + 2 + name_length + 2 + 2, language_length));
1333                         break;
1334                     }
1335                 }
1336 
1337             }
1338 
1339             if (tag == TAG_NAMEWITHLANGUAGE) {
1340                 proto_tree_add_item(tree, hf_ipp_namewithlanguage_value, tvb, valoffset, value_length, ENC_NA);
1341             }
1342             else {
1343                 proto_tree_add_item(tree, hf_ipp_textwithlanguage_value, tvb, valoffset, value_length, ENC_NA);
1344             }
1345             break;
1346 
1347         case TAG_BEGCOLLECTION :
1348             endoffset = ipp_fmt_collection(tvb, valoffset + value_length, value, sizeof(value));
1349             subtree = proto_tree_add_subtree_format(tree, tvb, valoffset, endoffset - valoffset, ett_ipp_member, NULL, "collection %s", value);
1350             break;
1351 
1352         default :
1353             proto_tree_add_string_format(tree, hf_ipp_octetstring_value, tvb, valoffset, value_length, NULL, "%s value: ??? %d bytes ???", tag_desc, value_length);
1354             break;
1355     }
1356 
1357     return subtree;
1358 }
1359 
1360 static proto_tree *
add_charstring_tree(proto_tree * tree,tvbuff_t * tvb,int offset,guint8 tag,int name_length,const gchar * name,int value_length)1361 add_charstring_tree(proto_tree *tree, tvbuff_t *tvb, int offset,
1362                     guint8 tag, int name_length, const gchar *name, int value_length)
1363 {
1364     int count = 0, valoffset = offset;
1365     const char *type = val_to_str_const(tag, tag_vals, "unknown-%02x");
1366     gchar *value = NULL;
1367 
1368     do {
1369        /*
1370         * Add the string...
1371         */
1372 
1373         gchar *temp = NULL;
1374 
1375         count ++;
1376 
1377         if ((tag == TAG_NAMEWITHLANGUAGE || tag == TAG_TEXTWITHLANGUAGE) && value_length > 4) {
1378             int language_length = tvb_get_ntohs(tvb, valoffset + 0);
1379             int string_length;
1380 
1381             if (tvb_offset_exists(tvb, valoffset + 2 + language_length)) {
1382                 string_length = tvb_get_ntohs(tvb, valoffset + 2 + language_length);
1383                 if (tvb_offset_exists(tvb, valoffset + 2 + language_length + 2 + string_length)) {
1384                     temp = wmem_strdup_printf(wmem_packet_scope(), "'%s'(%s)", tvb_format_text(wmem_packet_scope(), tvb, valoffset + 1 + 2 + name_length + 2 + 2 + language_length + 2, string_length), tvb_format_text(wmem_packet_scope(), tvb, valoffset + 1 + 2 + name_length + 2 + 2, language_length));
1385                 }
1386             }
1387         }
1388         else {
1389             temp = wmem_strdup_printf(wmem_packet_scope(), "'%s'", tvb_format_text(wmem_packet_scope(), tvb, valoffset + 1 + 2 + name_length + 2, value_length));
1390         }
1391 
1392         if (value)
1393             value = wmem_strconcat(wmem_packet_scope(), value, ",", temp, NULL);
1394         else
1395             value = wmem_strdup(wmem_packet_scope(), temp);
1396 
1397        /*
1398         * Move to the next value...
1399         */
1400 
1401         valoffset += 1 + 2 + name_length + 2 + value_length;
1402 
1403         if (!tvb_offset_exists(tvb, valoffset + 3))
1404             break;
1405 
1406         tag         = tvb_get_guint8(tvb, valoffset);
1407         name_length = tvb_get_ntohs(tvb, valoffset + 1);
1408         if (!tvb_offset_exists(tvb, valoffset + 1 + 2 + name_length + 2))
1409             break;
1410 
1411         value_length = tvb_get_ntohs(tvb, valoffset + 1 + 2 + name_length);
1412     }
1413     while (name_length == 0 && (TAG_TYPE(tag) == TAG_TYPE_CHARSTRING || tag == TAG_NAMEWITHLANGUAGE || tag == TAG_TEXTWITHLANGUAGE));
1414 
1415     return proto_tree_add_subtree_format(tree, tvb, offset, valoffset - offset, ett_ipp_attr, NULL, "%s (%s%s): %s", name, count > 1 ? "1setOf " : "", type, value);
1416 }
1417 
1418 static void
add_charstring_value(const gchar * tag_desc,proto_tree * tree,tvbuff_t * tvb,int offset,int name_length,const gchar * name _U_,int value_length,guint8 tag)1419 add_charstring_value(const gchar *tag_desc, proto_tree *tree, tvbuff_t *tvb,
1420                      int offset, int name_length, const gchar *name _U_, int value_length, guint8 tag)
1421 {
1422     int valoffset = offset + 1 + 2 + name_length + 2;
1423 
1424     if (name_length > 0)
1425         proto_tree_add_item(tree, hf_ipp_name, tvb, offset + 1 + 2, name_length, ENC_ASCII|ENC_NA);
1426 
1427     if (tag == TAG_MEMBERATTRNAME)
1428         proto_tree_add_item(tree, hf_ipp_memberattrname, tvb, valoffset, value_length, ENC_ASCII|ENC_NA);
1429     else
1430         proto_tree_add_string_format(tree, hf_ipp_charstring_value, tvb, valoffset, value_length, NULL, "%s value: '%s'", tag_desc, tvb_format_text(wmem_packet_scope(), tvb, valoffset, value_length));
1431 }
1432 
1433 static int
ipp_fmt_collection(tvbuff_t * tvb,int valoffset,char * buffer,int bufsize)1434 ipp_fmt_collection(tvbuff_t *tvb, int valoffset, char *buffer, int bufsize)
1435 {
1436     char *bufptr = buffer, *bufend = buffer + bufsize - 1;
1437     guint8 tag;
1438     int name_length, value_length;
1439     int overflow = 0;
1440 
1441     *bufptr++ = '{';
1442     buffer ++;
1443 
1444     do {
1445         if (!tvb_offset_exists(tvb, valoffset + 3))
1446             break;
1447 
1448         tag         = tvb_get_guint8(tvb, valoffset);
1449         name_length = tvb_get_ntohs(tvb, valoffset + 1);
1450         if (!tvb_offset_exists(tvb, valoffset + 1 + 2 + name_length + 2))
1451             break;
1452 
1453         value_length = tvb_get_ntohs(tvb, valoffset + 1 + 2 + name_length);
1454 
1455         if (!tvb_offset_exists(tvb, valoffset + 1 + 2 + name_length + 2 + value_length))
1456             break;
1457 
1458         if (tag == TAG_MEMBERATTRNAME && !overflow) {
1459             if (bufptr > buffer && bufptr < bufend)
1460                 *bufptr++ = ',';
1461 
1462             if ((bufend - bufptr) < value_length) {
1463                 (void) g_strlcpy(bufptr, "...", bufend - bufptr + 1);
1464                 overflow = 1;
1465             }
1466             else {
1467                 (void) g_strlcpy(bufptr, tvb_format_text(wmem_packet_scope(), tvb, valoffset + 1 + 2 + name_length + 2, value_length), bufend - bufptr + 1);
1468             }
1469 
1470             bufptr += strlen(bufptr);
1471         }
1472 
1473         valoffset += 1 + 2 + name_length + 2 + value_length;
1474 
1475     if (tag == TAG_BEGCOLLECTION) {
1476             char temp[176];
1477 
1478             valoffset = ipp_fmt_collection(tvb, valoffset, temp, sizeof(temp));
1479             if (!overflow) {
1480                 if ((bufend - bufptr) < (int)strlen(temp)) {
1481                     (void) g_strlcpy(bufptr, "...", bufend - bufptr + 1);
1482                     overflow = 1;
1483                 }
1484                 else {
1485                     (void) g_strlcpy(bufptr, temp, bufend - bufptr + 1);
1486                 }
1487                 bufptr += strlen(bufptr);
1488             }
1489         }
1490     } while (tag != TAG_ENDCOLLECTION);
1491 
1492     if (bufptr < bufend)
1493       *bufptr++ = '}';
1494 
1495     *bufptr = '\0';
1496 
1497     return (valoffset);
1498 }
1499 
1500 
1501 static void
ipp_fmt_version(gchar * result,guint32 revision)1502 ipp_fmt_version( gchar *result, guint32 revision )
1503 {
1504    g_snprintf( result, ITEM_LABEL_LENGTH, "%u.%u", (guint8)(( revision & 0xFF00 ) >> 8), (guint8)(revision & 0xFF) );
1505 }
1506 
1507 void
proto_register_ipp(void)1508 proto_register_ipp(void)
1509 {
1510     static hf_register_info hf[] = {
1511       /* Generated from convert_proto_tree_add_text.pl */
1512       { &hf_ipp_version, { "version", "ipp.version", FT_UINT16, BASE_CUSTOM, CF_FUNC(ipp_fmt_version), 0x0, NULL, HFILL }},
1513       { &hf_ipp_operation_id, { "operation-id", "ipp.operation_id", FT_UINT16, BASE_HEX, VALS(operation_vals), 0x0, NULL, HFILL }},
1514       { &hf_ipp_status_code, { "status-code", "ipp.status_code", FT_UINT16, BASE_HEX, VALS(status_vals), 0x0, NULL, HFILL }},
1515       { &hf_ipp_request_id, { "request-id", "ipp.request_id", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
1516       { &hf_ipp_name, { "name", "ipp.name", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1517       { &hf_ipp_memberattrname, { "memberAttrName", "ipp.memberattrname", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1518       { &hf_ipp_boolean_value, { "boolean value", "ipp.boolean_value", FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1519       { &hf_ipp_integer_value, { "integer value", "ipp.integer_value", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
1520       { &hf_ipp_enum_value, { "enum value", "ipp.enum_value", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
1521       { &hf_ipp_enum_value_printer_state, { "printer-state", "ipp.enum_value", FT_INT32, BASE_DEC, VALS(printer_state_vals), 0x0, NULL, HFILL }},
1522       { &hf_ipp_enum_value_job_state, { "job-state", "ipp.enum_value", FT_INT32, BASE_DEC, VALS(job_state_vals), 0x0, NULL, HFILL }},
1523       { &hf_ipp_enum_value_document_state, { "document-state", "ipp.enum_value", FT_INT32, BASE_DEC, VALS(document_state_vals), 0x0, NULL, HFILL }},
1524       { &hf_ipp_enum_value_operations_supported, { "operations-supported", "ipp.enum_value", FT_INT32, BASE_DEC, VALS(operation_vals), 0x0, NULL, HFILL }},
1525       { &hf_ipp_enum_value_finishings, { "finishings", "ipp.enum_value", FT_INT32, BASE_DEC, VALS(finishings_vals), 0x0, NULL, HFILL }},
1526       { &hf_ipp_enum_value_orientation, { "orientation", "ipp.enum_value", FT_INT32, BASE_DEC, VALS(orientation_vals), 0x0, NULL, HFILL }},
1527       { &hf_ipp_enum_value_print_quality, { "print-quality", "ipp.enum_value", FT_INT32, BASE_DEC, VALS(quality_vals), 0x0, NULL, HFILL }},
1528       { &hf_ipp_enum_value_transmission_status, { "transmission-status", "ipp.enum_value", FT_INT32, BASE_DEC, VALS(transmission_status_vals), 0x0, NULL, HFILL }},
1529       { &hf_ipp_outofband_value, { "out-of-band value", "ipp.outofband_value", FT_UINT8, BASE_HEX, VALS(tag_vals), 0x0, NULL, HFILL }},
1530       { &hf_ipp_charstring_value, { "string value", "ipp.charstring_value", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1531       { &hf_ipp_octetstring_value, { "octetString value", "ipp.octetstring_value", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1532       { &hf_ipp_datetime_value, { "dateTime value", "ipp.datetime_value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1533       { &hf_ipp_resolution_value, { "resolution value", "ipp.resolution_value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1534       { &hf_ipp_rangeofinteger_value, { "rangeOfInteger value", "ipp.rangeofinteger_value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1535       { &hf_ipp_textwithlanguage_value, { "textWithLanguage value", "ipp.textwithlanguage_value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1536       { &hf_ipp_namewithlanguage_value, { "nameWithLanguage value", "ipp.namewithlanguage_value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1537       { &hf_ipp_unknown_value, { "unknown value", "ipp.unknown_value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1538       { &hf_ipp_response_in, { "Response In", "ipp.response_in", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0x0, "The response to this IPP request is in this frame", HFILL }},
1539       { &hf_ipp_response_to, { "Request In", "ipp.response_to", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0x0, "This is a response to the IPP request in this frame", HFILL }},
1540       { &hf_ipp_response_time, { "Response Time", "ipp.response_time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, "The time between the Request and the Response", HFILL }}
1541     };
1542     static gint *ett[] = {
1543         &ett_ipp,
1544         &ett_ipp_as,
1545         &ett_ipp_attr,
1546         &ett_ipp_member
1547     };
1548 
1549     proto_ipp = proto_register_protocol("Internet Printing Protocol", "IPP", "ipp");
1550 
1551     proto_register_field_array(proto_ipp, hf, array_length(hf));
1552     proto_register_subtree_array(ett, array_length(ett));
1553 }
1554 
1555 void
proto_reg_handoff_ipp(void)1556 proto_reg_handoff_ipp(void)
1557 {
1558     dissector_handle_t ipp_handle;
1559 
1560     /*
1561      * Register ourselves as running atop HTTP and using port 631.
1562      */
1563     ipp_handle = create_dissector_handle(dissect_ipp, proto_ipp);
1564     http_tcp_dissector_add(631, ipp_handle);
1565     dissector_add_string("media_type", "application/ipp", ipp_handle);
1566 }
1567 
1568 /*
1569  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
1570  *
1571  * Local variables:
1572  * c-basic-offset: 4
1573  * tab-width: 8
1574  * indent-tabs-mode: nil
1575  * End:
1576  *
1577  * vi: set shiftwidth=4 tabstop=8 expandtab:
1578  * :indentSize=4:tabSize=8:noTabs=true:
1579  */
1580