Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-ipp.c
blobd7b8a068e302c9b99386d4e5c4262f3f7ffaf2f1
1 /* packet-ipp.c
2 * Routines for IPP packet disassembly
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)
9 * Wireshark - Network traffic analyzer
10 * By Gerald Combs <gerald@wireshark.org>
11 * Copyright 1998 Gerald Combs
13 * SPDX-License-Identifier: GPL-2.0-or-later
16 #include "config.h"
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 "packet-media-type.h"
25 #include "packet-tls.h"
27 void proto_register_ipp(void);
28 void proto_reg_handoff_ipp(void);
30 static dissector_handle_t ipp_handle;
32 static int proto_ipp;
33 /* Generated from convert_proto_tree_add_text.pl */
34 static int hf_ipp_version;
35 static int hf_ipp_operation_id;
36 static int hf_ipp_status_code;
37 static int hf_ipp_request_id;
38 static int hf_ipp_name;
39 static int hf_ipp_memberattrname;
40 static int hf_ipp_outofband_value;
41 static int hf_ipp_charstring_value;
42 static int hf_ipp_boolean_value;
43 static int hf_ipp_enum_value;
44 static int hf_ipp_enum_value_printer_state;
45 static int hf_ipp_enum_value_job_state;
46 static int hf_ipp_enum_value_document_state;
47 static int hf_ipp_enum_value_operations_supported;
48 static int hf_ipp_enum_value_finishings;
49 static int hf_ipp_enum_value_orientation;
50 static int hf_ipp_enum_value_print_quality;
51 static int hf_ipp_enum_value_transmission_status;
52 static int hf_ipp_integer_value;
53 static int hf_ipp_octetstring_value;
54 static int hf_ipp_datetime_value;
55 static int hf_ipp_resolution_value;
56 static int hf_ipp_rangeofinteger_value;
57 static int hf_ipp_textwithlanguage_value;
58 static int hf_ipp_namewithlanguage_value;
59 static int hf_ipp_unknown_value;
61 static int hf_ipp_response_in;
62 static int hf_ipp_response_to;
63 static int hf_ipp_response_time;
65 typedef struct _ipp_transaction_t {
66 uint32_t req_frame;
67 uint32_t rep_frame;
68 nstime_t req_time;
69 } ipp_transaction_t;
71 typedef struct _ipp_conv_info_t {
72 wmem_map_t *pdus;
73 } ipp_conv_info_t;
75 static int ett_ipp;
76 static int ett_ipp_as;
77 static int ett_ipp_attr;
78 static int ett_ipp_member;
80 #define PRINT_JOB 0x0002
81 #define PRINT_URI 0x0003
82 #define VALIDATE_JOB 0x0004
83 #define CREATE_JOB 0x0005
84 #define SEND_DOCUMENT 0x0006
85 #define SEND_URI 0x0007
86 #define CANCEL_JOB 0x0008
87 #define GET_JOB_ATTRIBUTES 0x0009
88 #define GET_JOBS 0x000A
89 #define GET_PRINTER_ATTRIBUTES 0x000B
91 static const value_string operation_vals[] = {
92 { PRINT_JOB, "Print-Job" },
93 { PRINT_URI, "Print-URI" },
94 { VALIDATE_JOB, "Validate-Job" },
95 { CREATE_JOB, "Create-Job" },
96 { SEND_DOCUMENT, "Send-Document" },
97 { SEND_URI, "Send-URI" },
98 { CANCEL_JOB, "Cancel-Job" },
99 { GET_JOB_ATTRIBUTES, "Get-Job-Attributes" },
100 { GET_JOBS, "Get-Jobs" },
101 { GET_PRINTER_ATTRIBUTES, "Get-Printer-Attributes" },
102 { 0x000C, "Hold-Job" },
103 { 0x000D, "Release-Job" },
104 { 0x000E, "Restart-Job" },
105 { 0x0010, "Pause-Printer" },
106 { 0x0011, "Resume-Printer" },
107 { 0x0012, "Purge-Jobs" },
108 { 0x0013, "Set-Printer-Attributes" },
109 { 0x0014, "Set-Job-Attributes" },
110 { 0x0015, "Get-Printer-Supported-Values" },
111 { 0x0016, "Create-Printer-Subscriptions" },
112 { 0x0017, "Create-Job-Subscriptions" },
113 { 0x0018, "Get-Subscription-Attributes" },
114 { 0x0019, "Get-Subscriptions" },
115 { 0x001A, "Renew-Subscription" },
116 { 0x001B, "Cancel-Subscription" },
117 { 0x001C, "Get-Notifications" },
118 { 0x001D, "Reserved (ipp-indp-method)" },
119 { 0x001E, "Reserved (ipp-get-resources)" },
120 { 0x001F, "Reserved (ipp-get-resources)" },
121 { 0x0020, "Reserved (ipp-get-resources)" },
122 { 0x0021, "Reserved (ipp-install)" },
123 { 0x0022, "Enable-Printer" },
124 { 0x0023, "Disable-Printer" },
125 { 0x0024, "Pause-Printer-After-Current-Job" },
126 { 0x0025, "Hold-New-Jobs" },
127 { 0x0026, "Release-Held-New-Jobs" },
128 { 0x0027, "Deactivate-Printer" },
129 { 0x0028, "Activate-Printer" },
130 { 0x0029, "Restart-Printer" },
131 { 0x002A, "Shutdown-Printer" },
132 { 0x002B, "Startup-Printer" },
133 { 0x002C, "Reprocess-Job" },
134 { 0x002D, "Cancel-Current-Job" },
135 { 0x002E, "Suspend-Current-Job" },
136 { 0x002F, "Resume-Job" },
137 { 0x0030, "Promote-Job" },
138 { 0x0031, "Schedule-Job-After" },
139 { 0x0033, "Cancel-Document" },
140 { 0x0034, "Get-Document-Attributes" },
141 { 0x0035, "Get-Documents" },
142 { 0x0036, "Delete-Document" },
143 { 0x0037, "Set-Document-Attributes" },
144 { 0x0038, "Cancel-Jobs" },
145 { 0x0039, "Cancel-My-Jobs" },
146 { 0x003A, "Resubmit-Job" },
147 { 0x003B, "Close-Job" },
148 { 0x003C, "Identify-Printer" },
149 { 0x003D, "Validate-Document" },
150 { 0x003E, "Add-Document-Images" },
151 { 0x003F, "Acknowledge-Document" },
152 { 0x0040, "Acknowledge-Identify-Printer" },
153 { 0x0041, "Acknowledge-Job" },
154 { 0x0042, "Fetch-Document" },
155 { 0x0043, "Fetch-Job" },
156 { 0x0044, "Get-Output-Device-Attributes" },
157 { 0x0045, "Update-Active-Jobs" },
158 { 0x0046, "Deregister-Output-Device" },
159 { 0x0047, "Update-Document-Status" },
160 { 0x0048, "Update-Job-Status" },
161 { 0x0049, "Update-Output-Device-Attributes" },
162 { 0x004A, "Get-Next-Document-Data" },
163 { 0x4001, "CUPS-Get-Default" },
164 { 0x4002, "CUPS-Get-Printers" },
165 { 0x4003, "CUPS-Add-Modify-Printer" },
166 { 0x4004, "CUPS-Delete-Printer" },
167 { 0x4005, "CUPS-Get-Classes" },
168 { 0x4006, "CUPS-Add-Modify-Class" },
169 { 0x4007, "CUPS-Delete-Class" },
170 { 0x4008, "CUPS-Accept-Jobs" },
171 { 0x4009, "CUPS-Reject-Jobs" },
172 { 0x400A, "CUPS-Set-Default" },
173 { 0x400B, "CUPS-Get-Devices" },
174 { 0x400C, "CUPS-Get-PPDs" },
175 { 0x400D, "CUPS-Move-Job" },
176 { 0x400E, "CUPS-Authenticate-Job" },
177 { 0x400F, "CUPS-Get-PPD" },
178 { 0x4027, "CUPS-Get-Document" },
179 { 0x4028, "CUPS-Create-Local-Printer" },
180 { 0, NULL }
183 /* Printer States */
184 #define PRINTER_STATE_IDLE 0x3
185 #define PRINTER_STATE_PROCESSING 0x4
186 #define PRINTER_STATE_STOPPED 0x5
187 static const value_string printer_state_vals[] = {
188 { PRINTER_STATE_IDLE, "idle" },
189 { PRINTER_STATE_PROCESSING, "processing" },
190 { PRINTER_STATE_STOPPED, "stopped" },
191 { 0, NULL }
194 /* Job States */
195 static const value_string job_state_vals[] = {
196 { 3, "pending" },
197 { 4, "pending-held" },
198 { 5, "processing" },
199 { 6, "processing-stopped" },
200 { 7, "canceled" },
201 { 8, "aborted" },
202 { 9, "completed" },
203 { 0, NULL }
206 /* Document States */
207 static const value_string document_state_vals[] = {
208 { 3, "pending" },
209 { 5, "processing" },
210 { 6, "processing-stopped" },
211 { 7, "canceled" },
212 { 8, "aborted" },
213 { 9, "completed" },
214 { 0, NULL }
217 /* Finishings Values */
218 static const value_string finishings_vals[] = {
219 { 3, "none" },
220 { 4, "staple" },
221 { 5, "punch" },
222 { 6, "cover" },
223 { 7, "bind" },
224 { 8, "saddle-stitch" },
225 { 9, "edge-stitch" },
226 { 10, "fold" },
227 { 11, "trim" },
228 { 12, "bale" },
229 { 13, "booklet-maker" },
230 { 14, "jog-offset" },
231 { 15, "coat" },
232 { 16, "laminate" },
233 { 20, "staple-top-left" },
234 { 21, "staple-bottom-left" },
235 { 22, "staple-top-right" },
236 { 23, "staple-bottom-right" },
237 { 24, "edge-stitch-left" },
238 { 25, "edge-stitch-top" },
239 { 26, "edge-stitch-right" },
240 { 27, "edge-stitch-bottom" },
241 { 28, "staple-dual-left" },
242 { 29, "staple-dual-top" },
243 { 30, "staple-dual-right" },
244 { 31, "staple-dual-bottom" },
245 { 32, "staple-triple-left" },
246 { 33, "staple-triple-top" },
247 { 34, "staple-triple-right" },
248 { 35, "staple-triple-bottom" },
249 { 50, "bind-left" },
250 { 51, "bind-top" },
251 { 52, "bind-right" },
252 { 53, "bind-bottom" },
253 { 60, "trim-after-pages" },
254 { 61, "trim-after-documents" },
255 { 62, "trim-after-copies" },
256 { 63, "trim-after-job" },
257 { 70, "punch-top-left" },
258 { 71, "punch-bottom-left" },
259 { 72, "punch-top-right" },
260 { 73, "punch-bottom-right" },
261 { 74, "punch-dual-left" },
262 { 75, "punch-dual-top" },
263 { 76, "punch-dual-right" },
264 { 77, "punch-dual-bottom" },
265 { 78, "punch-triple-left" },
266 { 79, "punch-triple-top" },
267 { 80, "punch-triple-right" },
268 { 81, "punch-triple-bottom" },
269 { 82, "punch-quad-left" },
270 { 83, "punch-quad-top" },
271 { 84, "punch-quad-right" },
272 { 85, "punch-quad-bottom" },
273 { 86, "punch-multiple-left" },
274 { 87, "punch-multiple-top" },
275 { 88, "punch-multiple-right" },
276 { 89, "punch-multiple-bottom" },
277 { 90, "fold-accordion" },
278 { 91, "fold-double-gate" },
279 { 92, "fold-gate" },
280 { 93, "fold-half" },
281 { 94, "fold-half-z" },
282 { 95, "fold-left-gate" },
283 { 96, "fold-letter" },
284 { 97, "fold-parallel" },
285 { 98, "fold-poster" },
286 { 99, "fold-right-gate" },
287 { 100, "fold-z" },
288 { 0, NULL }
291 static const value_string orientation_vals[] = {
292 { 3, "portrait" },
293 { 4, "landscape" },
294 { 5, "reverse-landscape" },
295 { 6, "reverse-portrait" },
296 { 7, "none" },
297 { 0, NULL }
300 static const value_string quality_vals[] = {
301 { 3, "draft" },
302 { 4, "normal" },
303 { 5, "high" },
304 { 0, NULL }
307 static const value_string transmission_status_vals[] = {
308 { 3, "pending" },
309 { 4, "pending-retry" },
310 { 5, "processing" },
311 { 7, "canceled" },
312 { 8, "aborted" },
313 { 9, "completed" },
314 { 0, NULL }
318 #define STATUS_SUCCESSFUL 0x0000
319 #define STATUS_INFORMATIONAL 0x0100
320 #define STATUS_REDIRECTION 0x0200
321 #define STATUS_CLIENT_ERROR 0x0400
322 #define STATUS_SERVER_ERROR 0x0500
324 #define STATUS_TYPE_MASK 0xFF00
326 static const value_string status_vals[] = {
327 { 0x0000, "successful-ok" },
328 { 0x0001, "successful-ok-ignored-or-substituted-attributes" },
329 { 0x0002, "successful-ok-conflicting-attributes" },
330 { 0x0003, "successful-ok-ignored-subscriptions" },
331 { 0x0005, "successful-ok-too-many-events" },
332 { 0x0007, "successful-ok-events-complete" },
333 { 0x0400, "client-error-bad-request" },
334 { 0x0401, "client-error-forbidden" },
335 { 0x0402, "client-error-not-authenticated" },
336 { 0x0403, "client-error-not-authorized" },
337 { 0x0404, "client-error-not-possible" },
338 { 0x0405, "client-error-timeout" },
339 { 0x0406, "client-error-not-found" },
340 { 0x0407, "client-error-gone" },
341 { 0x0408, "client-error-request-entity-too-large" },
342 { 0x0409, "client-error-request-value-too-long" },
343 { 0x040A, "client-error-document-format-not-supported" },
344 { 0x040B, "client-error-attributes-or-values-not-supported" },
345 { 0x040C, "client-error-uri-scheme-not-supported" },
346 { 0x040D, "client-error-charset-not-supported" },
347 { 0x040E, "client-error-conflicting-attributes" },
348 { 0x040F, "client-error-compression-not-supported" },
349 { 0x0410, "client-error-compression-error" },
350 { 0x0411, "client-error-document-format-error" },
351 { 0x0412, "client-error-document-access-error" },
352 { 0x0413, "client-error-attributes-not-settable" },
353 { 0x0414, "client-error-ignored-all-subscriptions" },
354 { 0x0415, "client-error-too-many-subscriptions" },
355 { 0x0418, "client-error-document-password-error" },
356 { 0x0419, "client-error-document-permission-error" },
357 { 0x041A, "client-error-document-security-error" },
358 { 0x041B, "client-error-document-unprintable-error" },
359 { 0x041C, "client-error-account-info-needed" },
360 { 0x041D, "client-error-account-closed" },
361 { 0x041E, "client-error-account-limit-reached" },
362 { 0x041F, "client-error-account-authorization-failed" },
363 { 0x0420, "client-error-not-fetchable" },
364 { 0x0500, "server-error-internal-error" },
365 { 0x0501, "server-error-operation-not-supported" },
366 { 0x0502, "server-error-service-unavailable" },
367 { 0x0503, "server-error-version-not-supported" },
368 { 0x0504, "server-error-device-error" },
369 { 0x0505, "server-error-temporary-error" },
370 { 0x0506, "server-error-not-accepting-jobs" },
371 { 0x0507, "server-error-busy" },
372 { 0x0508, "server-error-job-canceled" },
373 { 0x0509, "server-error-multiple-document-jobs-not-supported" },
374 { 0x050A, "server-error-printer-is-deactivated" },
375 { 0x050B, "server-error-too-many-jobs" },
376 { 0x050C, "server-error-too-many-documents" },
377 { 0, NULL }
380 static int parse_attributes(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree);
381 static proto_tree *add_integer_tree(proto_tree *tree, tvbuff_t *tvb,
382 int offset, int name_length, const char *name, int value_length, uint8_t tag);
383 static void add_integer_value(const char *tag_desc, proto_tree *tree,
384 tvbuff_t *tvb, int offset, int name_length, const char *name, int value_length, uint8_t tag);
385 static proto_tree *add_octetstring_tree(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo,
386 int offset, int name_length, const char *name, int value_length, uint8_t tag);
387 static proto_tree *add_octetstring_value(const char *tag_desc, proto_tree *tree,
388 tvbuff_t *tvb, packet_info *pinfo, int offset, int name_length, const char *name, int value_length, uint8_t tag);
389 static proto_tree *add_charstring_tree(proto_tree *tree, tvbuff_t *tvb,
390 int offset, uint8_t tag, int name_length, const char *name, int value_length);
391 static void add_charstring_value(const char *tag_desc, proto_tree *tree,
392 tvbuff_t *tvb, int offset, int name_length, const char *name, int value_length, uint8_t tag);
393 static int ipp_fmt_collection(tvbuff_t *tvb, packet_info *pinfo, int offset, char *buffer, int bufsize);
395 static int
396 dissect_ipp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
398 proto_tree *ipp_tree;
399 proto_item *ti;
400 int offset = 0;
401 media_content_info_t *content_info = (media_content_info_t *)data;
402 bool is_request;
403 uint16_t operation_status;
404 const char *status_type;
405 uint32_t request_id;
406 conversation_t *conversation;
407 ipp_conv_info_t *ipp_info;
408 ipp_transaction_t *ipp_trans;
410 if (content_info != NULL) {
411 switch (content_info->type) {
413 case MEDIA_CONTAINER_HTTP_REQUEST:
414 is_request = true;
415 break;
417 case MEDIA_CONTAINER_HTTP_RESPONSE:
418 is_request = false;
419 break;
421 default:
422 /* This isn't strictly correct, but we should never come here anyways */
423 is_request = (pinfo->destport == pinfo->match_uint);
424 break;
426 } else {
427 /* This isn't strictly correct, but we should never come here anyways */
428 is_request = (pinfo->destport == pinfo->match_uint);
431 operation_status = tvb_get_ntohs(tvb, 2);
432 request_id = tvb_get_ntohl(tvb, 4);
434 if (proto_is_frame_protocol(pinfo->layers, "ippusb")) {
435 col_set_str(pinfo->cinfo, COL_PROTOCOL, "IPPUSB");
436 if (is_request)
437 col_add_fstr(pinfo->cinfo, COL_INFO, "IPPUSB Request (%s)", val_to_str(operation_status, operation_vals, "0x%04x"));
438 else
439 col_add_fstr(pinfo->cinfo, COL_INFO, "IPPUSB Response (%s)", val_to_str(operation_status, status_vals, "0x%04x"));
440 } else {
441 col_set_str(pinfo->cinfo, COL_PROTOCOL, "IPP");
442 if (is_request)
443 col_add_fstr(pinfo->cinfo, COL_INFO, "IPP Request (%s)", val_to_str(operation_status, operation_vals, "0x%04x"));
444 else
445 col_add_fstr(pinfo->cinfo, COL_INFO, "IPP Response (%s)", val_to_str(operation_status, status_vals, "0x%04x"));
448 ti = proto_tree_add_item(tree, proto_ipp, tvb, offset, -1, ENC_NA);
449 ipp_tree = proto_item_add_subtree(ti, ett_ipp);
451 conversation = find_or_create_conversation(pinfo);
452 ipp_info = (ipp_conv_info_t *)conversation_get_proto_data(conversation, proto_ipp);
453 if (!ipp_info) {
454 ipp_info = wmem_new(wmem_file_scope(), ipp_conv_info_t);
455 ipp_info->pdus=wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
457 conversation_add_proto_data(conversation, proto_ipp, ipp_info);
459 if (!PINFO_FD_VISITED(pinfo)) {
460 if (is_request) {
461 /* This is a request */
462 ipp_trans=wmem_new(wmem_file_scope(), ipp_transaction_t);
463 ipp_trans->req_frame = pinfo->num;
464 ipp_trans->rep_frame = 0;
465 ipp_trans->req_time = pinfo->abs_ts;
466 wmem_map_insert(ipp_info->pdus, GUINT_TO_POINTER(request_id), (void *)ipp_trans);
467 } else {
468 ipp_trans=(ipp_transaction_t *)wmem_map_lookup(ipp_info->pdus, GUINT_TO_POINTER(request_id));
469 if (ipp_trans) {
470 ipp_trans->rep_frame = pinfo->num;
473 } else {
474 ipp_trans=(ipp_transaction_t *)wmem_map_lookup(ipp_info->pdus, GUINT_TO_POINTER(request_id));
476 if (!ipp_trans) {
477 /* create a "fake" ipp_trans structure */
478 ipp_trans=wmem_new(pinfo->pool, ipp_transaction_t);
479 ipp_trans->req_frame = 0;
480 ipp_trans->rep_frame = 0;
481 ipp_trans->req_time = pinfo->abs_ts;
484 /* print state tracking in the tree */
485 if (is_request) {
486 /* This is a request */
487 if (ipp_trans->rep_frame) {
488 proto_item *it;
490 it = proto_tree_add_uint(ipp_tree, hf_ipp_response_in,
491 tvb, 0, 0, ipp_trans->rep_frame);
492 proto_item_set_generated(it);
494 } else {
495 /* This is a response */
496 if (ipp_trans->req_frame) {
497 proto_item *it;
498 nstime_t ns;
500 it = proto_tree_add_uint(ipp_tree, hf_ipp_response_to,
501 tvb, 0, 0, ipp_trans->req_frame);
502 proto_item_set_generated(it);
504 nstime_delta(&ns, &pinfo->abs_ts, &ipp_trans->req_time);
505 it = proto_tree_add_time(ipp_tree, hf_ipp_response_time, tvb, 0, 0, &ns);
506 proto_item_set_generated(it);
510 proto_tree_add_item(ipp_tree, hf_ipp_version, tvb, offset, 2, ENC_BIG_ENDIAN);
511 offset += 2;
513 if (is_request) {
514 proto_tree_add_item(ipp_tree, hf_ipp_operation_id, tvb, offset, 2, ENC_BIG_ENDIAN);
515 } else {
516 switch (operation_status & STATUS_TYPE_MASK) {
518 case STATUS_SUCCESSFUL:
519 status_type = "Successful";
520 break;
522 case STATUS_INFORMATIONAL:
523 status_type = "Informational";
524 break;
526 case STATUS_REDIRECTION:
527 status_type = "Redirection";
528 break;
530 case STATUS_CLIENT_ERROR:
531 status_type = "Client Error";
532 break;
534 case STATUS_SERVER_ERROR:
535 status_type = "Server Error";
536 break;
538 default:
539 status_type = "Unknown";
540 break;
542 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"));
544 offset += 2;
546 proto_tree_add_item(ipp_tree, hf_ipp_request_id, tvb, offset, 4, ENC_BIG_ENDIAN);
547 offset += 4;
549 offset = parse_attributes(tvb, pinfo, offset, ipp_tree);
551 if (tvb_offset_exists(tvb, offset)) {
552 call_data_dissector(tvb_new_subset_remaining(tvb, offset), pinfo, ipp_tree);
554 return tvb_captured_length(tvb);
557 #define TAG_TYPE(x) ((x) & 0xF0)
559 #define TAG_TYPE_DELIMITER 0x00
560 #define TAG_TYPE_OUTOFBAND 0x10
561 #define TAG_TYPE_INTEGER 0x20
562 #define TAG_TYPE_OCTETSTRING 0x30
563 #define TAG_TYPE_CHARSTRING 0x40
565 #define TAG_END_OF_ATTRIBUTES 0x03
567 #define TAG_INTEGER 0x21
568 #define TAG_BOOLEAN 0x22
569 #define TAG_ENUM 0x23
571 #define TAG_OCTETSTRING 0x30
572 #define TAG_DATETIME 0x31
573 #define TAG_RESOLUTION 0x32
574 #define TAG_RANGEOFINTEGER 0x33
575 #define TAG_BEGCOLLECTION 0x34
576 #define TAG_TEXTWITHLANGUAGE 0x35
577 #define TAG_NAMEWITHLANGUAGE 0x36
578 #define TAG_ENDCOLLECTION 0x37
580 #define TAG_TEXTWITHOUTLANGUAGE 0x41
581 #define TAG_NAMEWITHOUTLANGUAGE 0x42
582 #define TAG_KEYWORD 0x44
583 #define TAG_URI 0x45
584 #define TAG_URISCHEME 0x46
585 #define TAG_CHARSET 0x47
586 #define TAG_NATURALLANGUAGE 0x48
587 #define TAG_MIMEMEDIATYPE 0x49
588 #define TAG_MEMBERATTRNAME 0x4a
590 static const value_string tag_vals[] = {
591 /* Delimiter tags */
592 { 0x01, "operation-attributes-tag" },
593 { 0x02, "job-attributes-tag" },
594 { TAG_END_OF_ATTRIBUTES, "end-of-attributes-tag" },
595 { 0x04, "printer-attributes-tag" },
596 { 0x05, "unsupported-attributes-tag" },
597 { 0x06, "subscription-attributes-tag" },
598 { 0x07, "event-notification-attributes-tag" },
599 { 0x08, "resource-attributes-tag" },
600 { 0x09, "document-attributes-tag" },
602 /* Value tags */
603 { 0x10, "unsupported" },
604 { 0x12, "unknown" },
605 { 0x13, "no-value" },
606 { 0x15, "not-settable" },
607 { 0x16, "delete-attribute" },
608 { 0x17, "admin-define" },
609 { TAG_INTEGER, "integer" },
610 { TAG_BOOLEAN, "boolean" },
611 { TAG_ENUM, "enum" },
612 { TAG_OCTETSTRING, "octetString" },
613 { TAG_DATETIME, "dateTime" },
614 { TAG_RESOLUTION, "resolution" },
615 { TAG_RANGEOFINTEGER, "rangeOfInteger" },
616 { TAG_BEGCOLLECTION, "collection" }, /* Technically "begCollection" for encoding but "collection" for attribute syntax */
617 { TAG_TEXTWITHLANGUAGE, "textWithLanguage" },
618 { TAG_NAMEWITHLANGUAGE, "nameWithLanguage" },
619 { TAG_ENDCOLLECTION, "endCollection" },
620 { TAG_TEXTWITHOUTLANGUAGE, "textWithoutLanguage" },
621 { TAG_NAMEWITHOUTLANGUAGE, "nameWithoutLanguage" },
622 { TAG_KEYWORD, "keyword" },
623 { TAG_URI, "uri" },
624 { TAG_URISCHEME, "uriScheme" },
625 { TAG_CHARSET, "charset" },
626 { TAG_NATURALLANGUAGE, "naturalLanguage" },
627 { TAG_MIMEMEDIATYPE, "mimeMediaType" },
628 { TAG_MEMBERATTRNAME, "memberAttrName" },
629 { 0, NULL }
632 static int
633 parse_attributes(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree)
635 uint8_t tag;
636 const char *tag_desc;
637 char *name = "";
638 int name_length, value_length;
639 proto_tree *as_tree = tree;
640 proto_item *tas = NULL;
641 int start_offset = offset;
642 proto_tree *attr_tree = tree;
643 proto_tree *subtree = NULL;
645 while (tvb_offset_exists(tvb, offset)) {
646 tag = tvb_get_uint8(tvb, offset);
647 tag_desc = val_to_str(tag, tag_vals, "unknown-%02x");
648 if (TAG_TYPE(tag) == TAG_TYPE_DELIMITER) {
650 * If we had an attribute sequence we were
651 * working on, we're done with it; set its
652 * length to the length of all the stuff
653 * we've done so far.
655 name = "";
657 if (tas != NULL)
658 proto_item_set_len(tas, offset - start_offset);
661 * This tag starts a new attribute sequence;
662 * create a new tree under this tag when we see
663 * a non-delimiter tag, under which to put
664 * those attributes.
666 as_tree = NULL;
667 attr_tree = tree;
670 * Remember the offset at which this attribute
671 * sequence started, so we can use it to compute
672 * its length when it's finished.
674 start_offset = offset;
677 * Now create a new item for this tag.
679 subtree = proto_tree_add_subtree(tree, tvb, offset, 1, ett_ipp_as, &tas, tag_desc);
680 offset += 1;
681 if (tag == TAG_END_OF_ATTRIBUTES) {
683 * No more attributes.
685 break;
687 } else {
689 * Value tag - get the name length.
691 name_length = tvb_get_ntohs(tvb, offset + 1);
692 if (name_length != 0)
693 name = tvb_format_text(wmem_packet_scope(), tvb, offset + 1 + 2, name_length);
696 * OK, get the value length.
698 value_length = tvb_get_ntohs(tvb, offset + 1 + 2 + name_length);
699 if (tag == TAG_MEMBERATTRNAME && value_length != 0)
700 name = tvb_format_text(wmem_packet_scope(), tvb, offset + 1 + 2 + name_length + 2, value_length);
703 * OK, does the value run past the end of the
704 * frame?
706 if (as_tree == NULL) {
708 * OK, there's an attribute to hang
709 * under a delimiter tag, but we don't
710 * have a tree for that tag yet; create
711 * a tree.
713 as_tree = subtree;
714 attr_tree = as_tree;
717 switch (TAG_TYPE(tag)) {
718 case TAG_TYPE_OUTOFBAND :
719 if (name_length != 0) {
721 * This is an attribute, not
722 * an additional value, so
723 * start a tree for it.
725 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);
727 proto_tree_add_item(attr_tree, hf_ipp_outofband_value, tvb, offset, 1, ENC_NA);
728 break;
730 case TAG_TYPE_INTEGER :
731 if (name_length != 0) {
733 * This is an attribute, not
734 * an additional value, so
735 * start a tree for it.
737 attr_tree = add_integer_tree(as_tree, tvb, offset, name_length, name, value_length, tag);
739 add_integer_value(tag_desc, attr_tree, tvb, offset, name_length, name, value_length, tag);
740 break;
742 case TAG_TYPE_OCTETSTRING :
743 if (name_length != 0) {
745 * This is an attribute, not
746 * an additional value, so
747 * start a tree for it.
749 attr_tree = add_octetstring_tree(as_tree, tvb, pinfo, offset, name_length, name, value_length, tag);
751 if (tag == TAG_ENDCOLLECTION)
752 attr_tree = proto_tree_get_parent_tree(attr_tree);
753 else
754 attr_tree = add_octetstring_value(tag_desc, attr_tree, tvb, pinfo, offset, name_length, name, value_length, tag);
755 break;
757 case TAG_TYPE_CHARSTRING :
758 if (name_length != 0) {
760 * This is an attribute, not
761 * an additional value, so
762 * start a tree for it.
764 attr_tree = add_charstring_tree(as_tree, tvb, offset, tag, name_length, name, value_length);
766 add_charstring_value(tag_desc, attr_tree, tvb, offset, name_length, name, value_length, tag);
767 break;
769 default :
770 if (name_length != 0) {
772 * This is an attribute, not
773 * an additional value, so
774 * start a tree for it.
776 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);
778 proto_tree_add_item(attr_tree, hf_ipp_unknown_value, tvb, offset + 1 + 2 + name_length + 2, value_length, ENC_NA);
779 break;
781 offset += 1 + 2 + name_length + 2 + value_length;
785 return offset;
788 static proto_tree *
789 add_integer_tree(proto_tree *tree, tvbuff_t *tvb, int offset,
790 int name_length, const char *name, int value_length, uint8_t tag)
792 int count = 0;
793 const char *type = val_to_str(tag, tag_vals, "unknown-%02x");
794 char *value = NULL;
795 int valoffset = offset;
797 switch (tag) {
798 case TAG_BOOLEAN:
799 if (value_length == 1) {
800 value = wmem_strdup(wmem_packet_scope(), tvb_get_uint8(tvb, offset + 1 + 2 + name_length + 2) ? "true" : "false");
802 else {
803 value = wmem_strdup(wmem_packet_scope(), "???");
805 valoffset += 1 + 2 + name_length + 2 + value_length;
806 break;
808 case TAG_INTEGER :
812 * Add the range/integer...
815 char* temp;
817 count ++;
819 valoffset += 1 + 2 + name_length + 2;
821 if (!tvb_offset_exists(tvb, valoffset + value_length))
822 break;
824 if (value_length == 8) {
825 uint32_t lower = tvb_get_ntohl(tvb, valoffset + 0);
826 uint32_t upper = tvb_get_ntohl(tvb, valoffset + 4);
828 temp = wmem_strdup_printf(wmem_packet_scope(), "%d-%d", lower, upper);
830 else if (value_length == 4) {
831 temp = wmem_strdup_printf(wmem_packet_scope(), "%d", tvb_get_ntohl(tvb, valoffset + 0));
833 else {
834 temp = "???";
837 if (value)
838 value = wmem_strconcat(wmem_packet_scope(), value, ",", temp, NULL);
839 else
840 value = wmem_strdup(wmem_packet_scope(), temp);
842 valoffset += value_length;
845 * Move to the next value...
848 if (!tvb_offset_exists(tvb, valoffset + 3))
849 break;
851 tag = tvb_get_uint8(tvb, valoffset);
852 name_length = tvb_get_ntohs(tvb, valoffset + 1);
853 if (!tvb_offset_exists(tvb, valoffset + 1 + 2 + name_length + 2))
854 break;
856 value_length = tvb_get_ntohs(tvb, valoffset + 1 + 2 + name_length);
858 while (name_length == 0 && (tag == TAG_INTEGER || tag == TAG_RANGEOFINTEGER));
859 break;
861 case TAG_ENUM :
865 * Add the range/integer...
867 const char* temp;
869 count ++;
871 valoffset += 1 + 2 + name_length + 2;
873 if (!tvb_offset_exists(tvb, valoffset + value_length))
874 break;
876 if (value_length != 4) {
877 temp = "???";
878 } else {
879 if (!strncmp(name, "printer-state", 13)) {
880 temp = val_to_str(tvb_get_ntohl(tvb, valoffset), printer_state_vals, "unknown-%d");
882 else if (!strncmp(name, "job-state", 9)) {
883 temp = val_to_str(tvb_get_ntohl(tvb, valoffset), job_state_vals, "unknown-%d");
885 else if (!strncmp(name, "document-state", 14)) {
886 temp = val_to_str(tvb_get_ntohl(tvb, valoffset), document_state_vals, "unknown-%d");
888 else if (!strncmp(name, "operations-supported", 20)) {
889 temp = val_to_str(tvb_get_ntohl(tvb, valoffset), operation_vals, "unknown-%04x");
891 else if (!strncmp(name, "finishings", 10)) {
892 temp = val_to_str(tvb_get_ntohl(tvb, valoffset), finishings_vals, "unknown-%d");
894 else if (!strncmp(name, "orientation-requested", 21) || !strncmp(name, "media-feed-orientation", 22)) {
895 temp = val_to_str(tvb_get_ntohl(tvb, valoffset), orientation_vals, "unknown-%d");
897 else if (!strncmp(name, "print-quality", 13)) {
898 temp = val_to_str(tvb_get_ntohl(tvb, valoffset), quality_vals, "unknown-%d");
900 else if (!strncmp(name, "transmission-status", 19)) {
901 temp = val_to_str(tvb_get_ntohl(tvb, valoffset), transmission_status_vals, "unknown-%d");
903 else {
904 temp = wmem_strdup_printf(wmem_packet_scope(), "%d", tvb_get_ntohl(tvb, offset + 1 + 2 + name_length + 2));
908 if (value)
909 value = wmem_strconcat(wmem_packet_scope(), value, ",", temp, NULL);
910 else
911 value = wmem_strdup(wmem_packet_scope(), temp);
913 valoffset += value_length;
916 * Move to the next value...
919 if (!tvb_offset_exists(tvb, valoffset + 3))
920 break;
922 tag = tvb_get_uint8(tvb, valoffset);
923 name_length = tvb_get_ntohs(tvb, valoffset + 1);
924 if (!tvb_offset_exists(tvb, valoffset + 1 + 2 + name_length + 2))
925 break;
927 value_length = tvb_get_ntohs(tvb, valoffset + 1 + 2 + name_length);
929 while (name_length == 0 && tag == TAG_ENUM);
930 break;
932 default:
933 value = wmem_strdup(wmem_packet_scope(), "???");
934 break;
937 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);
940 static void
941 add_integer_value(const char *tag_desc, proto_tree *tree, tvbuff_t *tvb,
942 int offset, int name_length, const char *name, int value_length, uint8_t tag)
944 int valoffset = offset + 1 + 2 + name_length + 2;
946 if (name_length > 0)
947 proto_tree_add_item(tree, hf_ipp_name, tvb, offset + 1 + 2, name_length, ENC_ASCII);
949 switch (tag) {
950 case TAG_BOOLEAN:
951 if (value_length == 1) {
952 proto_tree_add_item(tree, hf_ipp_boolean_value, tvb, valoffset, value_length, ENC_BIG_ENDIAN);
954 else {
955 proto_tree_add_boolean_format(tree, hf_ipp_boolean_value, tvb, valoffset, value_length, 0, "boolean value: ??? %d bytes ???", value_length);
957 break;
959 case TAG_INTEGER:
960 if (value_length == 4) {
961 proto_tree_add_item(tree, hf_ipp_integer_value, tvb, valoffset, value_length, ENC_BIG_ENDIAN);
963 else {
964 proto_tree_add_int_format(tree, hf_ipp_integer_value, tvb, valoffset, value_length, 0, "integer value: ??? %d bytes ???", value_length);
966 break;
968 case TAG_ENUM:
969 if (value_length == 4) {
970 if (!strncmp(name, "printer-state", 13)) {
971 proto_tree_add_item(tree, hf_ipp_enum_value_printer_state, tvb, valoffset, value_length, ENC_BIG_ENDIAN);
973 else if (!strncmp(name, "job-state", 9)) {
974 proto_tree_add_item(tree, hf_ipp_enum_value_job_state, tvb, valoffset, value_length, ENC_BIG_ENDIAN);
976 else if (!strncmp(name, "document-state", 14)) {
977 proto_tree_add_item(tree, hf_ipp_enum_value_document_state, tvb, valoffset, value_length, ENC_BIG_ENDIAN);
979 else if (!strncmp(name, "operations-supported", 20)) {
980 proto_tree_add_item(tree, hf_ipp_enum_value_operations_supported, tvb, valoffset, value_length, ENC_BIG_ENDIAN);
982 else if (!strncmp(name, "finishings", 10)) {
983 proto_tree_add_item(tree, hf_ipp_enum_value_finishings, tvb, valoffset, value_length, ENC_BIG_ENDIAN);
985 else if (!strncmp(name, "orientation-requested", 21) || !strncmp(name, "media-feed-orientation", 22)) {
986 proto_tree_add_item(tree, hf_ipp_enum_value_orientation, tvb, valoffset, value_length, ENC_BIG_ENDIAN);
988 else if (!strncmp(name, "print-quality", 13)) {
989 proto_tree_add_item(tree, hf_ipp_enum_value_print_quality, tvb, valoffset, value_length, ENC_BIG_ENDIAN);
991 else if (!strncmp(name, "transmission-status", 19)) {
992 proto_tree_add_item(tree, hf_ipp_enum_value_transmission_status, tvb, valoffset, value_length, ENC_BIG_ENDIAN);
994 else {
995 proto_tree_add_item(tree, hf_ipp_enum_value, tvb, valoffset, value_length, ENC_BIG_ENDIAN);
998 else {
999 proto_tree_add_int_format_value(tree, hf_ipp_enum_value, tvb, valoffset, value_length, 0, "??? %d bytes ???", value_length);
1001 break;
1003 default :
1004 proto_tree_add_int_format(tree, hf_ipp_integer_value, tvb, valoffset, value_length, 0, "%s value: ??? %d bytes ???", tag_desc, value_length);
1005 break;
1009 static proto_tree *
1010 add_octetstring_tree(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, int offset, int name_length, const char *name, int value_length, uint8_t tag)
1012 int count = 0;
1013 const char *type = val_to_str(tag, tag_vals, "unknown-%02x");
1014 char *value = NULL;
1015 int valoffset = offset;
1017 switch (tag) {
1018 case TAG_OCTETSTRING :
1019 do {
1021 * Add the string...
1024 count ++;
1025 if (value)
1026 value = wmem_strconcat(wmem_packet_scope(), value, ",'", tvb_format_text(wmem_packet_scope(), tvb, valoffset + 1 + 2 + name_length + 2, value_length), "'", NULL);
1027 else
1028 value = wmem_strconcat(wmem_packet_scope(), "'", tvb_format_text(wmem_packet_scope(), tvb, valoffset + 1 + 2 + name_length + 2, value_length), "'", NULL);
1031 * Move to the next value...
1034 valoffset += 1 + 2 + name_length + 2 + value_length;
1036 if (!tvb_offset_exists(tvb, valoffset + 3))
1037 break;
1039 tag = tvb_get_uint8(tvb, valoffset);
1040 name_length = tvb_get_ntohs(tvb, valoffset + 1);
1041 if (!tvb_offset_exists(tvb, valoffset + 1 + 2 + name_length + 2))
1042 break;
1044 value_length = tvb_get_ntohs(tvb, valoffset + 1 + 2 + name_length);
1046 while (name_length == 0 && tag == TAG_OCTETSTRING);
1047 break;
1049 case TAG_DATETIME :
1050 valoffset += 1 + 2 + name_length + 2;
1052 if (value_length == 11) {
1053 uint16_t year = tvb_get_ntohs(tvb, valoffset + 0);
1054 uint8_t month = tvb_get_uint8(tvb, valoffset + 2);
1055 uint8_t day = tvb_get_uint8(tvb, valoffset + 3);
1056 uint8_t hours = tvb_get_uint8(tvb, valoffset + 4);
1057 uint8_t minutes = tvb_get_uint8(tvb, valoffset + 5);
1058 uint8_t seconds = tvb_get_uint8(tvb, valoffset + 6);
1059 uint8_t decisecs = tvb_get_uint8(tvb, valoffset + 7);
1060 uint8_t utcsign = tvb_get_uint8(tvb, valoffset + 8);
1061 if (utcsign != '+' && utcsign != '-') {
1062 // XXX Add expert info
1063 utcsign = '?';
1065 uint8_t utchours = tvb_get_uint8(tvb, valoffset + 9);
1066 uint8_t utcminutes = tvb_get_uint8(tvb, valoffset + 10);
1068 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);
1069 } else {
1070 value = wmem_strdup(wmem_packet_scope(), "???");
1073 valoffset += value_length;
1074 break;
1076 case TAG_RESOLUTION :
1077 do {
1079 * Add the resolution...
1082 char* temp;
1084 count ++;
1086 valoffset += 1 + 2 + name_length + 2;
1088 if (value_length == 9 && tvb_offset_exists(tvb, valoffset + value_length)) {
1089 int xres = tvb_get_ntohl(tvb, valoffset + 0);
1090 int yres = tvb_get_ntohl(tvb, valoffset + 4);
1091 uint8_t units = tvb_get_uint8(tvb, valoffset + 8);
1093 temp = wmem_strdup_printf(wmem_packet_scope(), "%dx%d%s", xres, yres, units == 3 ? "dpi" : units == 4 ? "dpcm" : "unknown");
1095 else {
1096 temp = "???";
1099 if (value)
1100 value = wmem_strconcat(wmem_packet_scope(), value, ",", temp, NULL);
1101 else
1102 value = wmem_strdup(wmem_packet_scope(), temp);
1104 valoffset += value_length;
1107 * Move to the next value...
1110 if (!tvb_offset_exists(tvb, valoffset + 3))
1111 break;
1113 tag = tvb_get_uint8(tvb, valoffset);
1114 name_length = tvb_get_ntohs(tvb, valoffset + 1);
1115 if (!tvb_offset_exists(tvb, valoffset + 1 + 2 + name_length + 2))
1116 break;
1118 value_length = tvb_get_ntohs(tvb, valoffset + 1 + 2 + name_length);
1120 while (name_length == 0 && tag == TAG_RESOLUTION);
1121 break;
1123 case TAG_RANGEOFINTEGER :
1124 do {
1126 * Add the range/integer...
1129 char* temp;
1131 count ++;
1133 valoffset += 1 + 2 + name_length + 2;
1135 if (!tvb_offset_exists(tvb, valoffset + value_length))
1136 break;
1138 if (value_length == 8) {
1139 uint32_t lower = tvb_get_ntohl(tvb, valoffset + 0);
1140 uint32_t upper = tvb_get_ntohl(tvb, valoffset + 4);
1142 temp = wmem_strdup_printf(wmem_packet_scope(), "%d-%d", lower, upper);
1144 else if (value_length == 4) {
1145 temp = wmem_strdup_printf(wmem_packet_scope(), "%d", tvb_get_ntohl(tvb, valoffset + 0));
1147 else {
1148 temp = "???";
1151 if (value)
1152 value = wmem_strconcat(wmem_packet_scope(), value, ",", temp, NULL);
1153 else
1154 value = wmem_strdup(wmem_packet_scope(), temp);
1156 valoffset += value_length;
1159 * Move to the next value...
1162 if (!tvb_offset_exists(tvb, valoffset + 3))
1163 break;
1165 tag = tvb_get_uint8(tvb, valoffset);
1166 name_length = tvb_get_ntohs(tvb, valoffset + 1);
1167 if (!tvb_offset_exists(tvb, valoffset + 1 + 2 + name_length + 2))
1168 break;
1170 value_length = tvb_get_ntohs(tvb, valoffset + 1 + 2 + name_length);
1172 while (name_length == 0 && (tag == TAG_RANGEOFINTEGER || tag == TAG_INTEGER));
1173 break;
1175 case TAG_TEXTWITHLANGUAGE :
1176 case TAG_NAMEWITHLANGUAGE :
1177 do {
1179 * Add the string...
1182 char *temp = NULL;
1184 count ++;
1186 if ((tag == TAG_NAMEWITHLANGUAGE || tag == TAG_TEXTWITHLANGUAGE) && value_length > 4) {
1187 int language_length = tvb_get_ntohs(tvb, valoffset + 0);
1188 int string_length;
1190 if (tvb_offset_exists(tvb, valoffset + 2 + language_length)) {
1191 string_length = tvb_get_ntohs(tvb, valoffset + 2 + language_length);
1192 if (tvb_offset_exists(tvb, valoffset + 2 + language_length + 2 + string_length)) {
1193 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));
1197 else {
1198 temp = wmem_strdup_printf(wmem_packet_scope(), "'%s'", tvb_format_text(wmem_packet_scope(), tvb, valoffset + 1 + 2 + name_length + 2, value_length));
1201 if (value)
1202 value = wmem_strconcat(wmem_packet_scope(), value, ",", temp, NULL);
1203 else
1204 value = wmem_strdup(wmem_packet_scope(), temp);
1207 * Move to the next value...
1210 valoffset += 1 + 2 + name_length + 2 + value_length;
1212 if (!tvb_offset_exists(tvb, valoffset + 3))
1213 break;
1215 tag = tvb_get_uint8(tvb, valoffset);
1216 name_length = tvb_get_ntohs(tvb, valoffset + 1);
1217 if (!tvb_offset_exists(tvb, valoffset + 1 + 2 + name_length + 2))
1218 break;
1220 value_length = tvb_get_ntohs(tvb, valoffset + 1 + 2 + name_length);
1222 while (name_length == 0 && (TAG_TYPE(tag) == TAG_TYPE_CHARSTRING || tag == TAG_NAMEWITHLANGUAGE || tag == TAG_TEXTWITHLANGUAGE));
1223 break;
1225 case TAG_BEGCOLLECTION :
1226 do {
1228 * Add the member attribute...
1231 char temp[1024];
1233 count ++;
1235 valoffset = ipp_fmt_collection(tvb, pinfo, valoffset + 1 + 2 + name_length + 2 + value_length, temp, sizeof(temp));
1237 if (value)
1238 value = wmem_strconcat(wmem_packet_scope(), value, ",", temp, NULL);
1239 else
1240 value = wmem_strdup(wmem_packet_scope(), temp);
1243 * Move to the next value...
1246 if (!tvb_offset_exists(tvb, valoffset + 3))
1247 break;
1249 tag = tvb_get_uint8(tvb, valoffset);
1250 name_length = tvb_get_ntohs(tvb, valoffset + 1);
1251 if (!tvb_offset_exists(tvb, valoffset + 1 + 2 + name_length + 2))
1252 break;
1254 value_length = tvb_get_ntohs(tvb, valoffset + 1 + 2 + name_length);
1256 while (name_length == 0 && tag == TAG_BEGCOLLECTION);
1257 break;
1259 default :
1260 if (value_length > 0 ) {
1261 value = tvb_bytes_to_str(wmem_packet_scope(), tvb, offset + 1 + 2 + name_length + 2, value_length);
1263 valoffset += 1 + 2 + name_length + 2 + value_length;
1264 break;
1267 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);
1270 static proto_tree *
1271 add_octetstring_value(const char *tag_desc, proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo,
1272 int offset, int name_length, const char *name _U_, int value_length, uint8_t tag)
1274 proto_tree *subtree = tree;
1275 char value[176];
1276 int valoffset = offset + 1 + 2 + name_length + 2;
1277 int endoffset;
1279 if (name_length > 0)
1280 proto_tree_add_item(tree, hf_ipp_name, tvb, offset + 1 + 2, name_length, ENC_ASCII);
1282 switch (tag) {
1283 case TAG_OCTETSTRING :
1284 proto_tree_add_item(tree, hf_ipp_octetstring_value, tvb, valoffset, value_length, ENC_ASCII);
1285 break;
1287 case TAG_DATETIME :
1288 if (value_length == 11) {
1289 uint16_t year = tvb_get_ntohs(tvb, valoffset + 0);
1290 uint8_t month = tvb_get_uint8(tvb, valoffset + 2);
1291 uint8_t day = tvb_get_uint8(tvb, valoffset + 3);
1292 uint8_t hours = tvb_get_uint8(tvb, valoffset + 4);
1293 uint8_t minutes = tvb_get_uint8(tvb, valoffset + 5);
1294 uint8_t seconds = tvb_get_uint8(tvb, valoffset + 6);
1295 uint8_t decisecs = tvb_get_uint8(tvb, valoffset + 7);
1296 uint8_t utcsign = tvb_get_uint8(tvb, valoffset + 8);
1297 if (utcsign != '+' && utcsign != '-') {
1298 // XXX Add expert info
1299 utcsign = '?';
1301 uint8_t utchours = tvb_get_uint8(tvb, valoffset + 9);
1302 uint8_t utcminutes = tvb_get_uint8(tvb, valoffset + 10);
1304 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);
1306 else {
1307 proto_tree_add_item(tree, hf_ipp_datetime_value, tvb, valoffset, value_length, ENC_NA);
1309 break;
1311 case TAG_RESOLUTION :
1312 if (value_length == 9) {
1313 int xres = tvb_get_ntohl(tvb, valoffset + 0);
1314 int yres = tvb_get_ntohl(tvb, valoffset + 4);
1315 uint8_t units = tvb_get_uint8(tvb, valoffset + 8);
1317 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");
1319 else {
1320 proto_tree_add_item(tree, hf_ipp_resolution_value, tvb, valoffset, value_length, ENC_NA);
1322 break;
1324 case TAG_RANGEOFINTEGER :
1325 if (value_length == 8) {
1326 int lower = tvb_get_ntohl(tvb, valoffset + 0);
1327 int upper = tvb_get_ntohl(tvb, valoffset + 4);
1329 proto_tree_add_bytes_format(tree, hf_ipp_rangeofinteger_value, tvb, valoffset, value_length, NULL, "rangeOfInteger value: %d-%d", lower, upper);
1331 else {
1332 proto_tree_add_item(tree, hf_ipp_rangeofinteger_value, tvb, valoffset, value_length, ENC_NA);
1334 break;
1336 case TAG_TEXTWITHLANGUAGE :
1337 case TAG_NAMEWITHLANGUAGE :
1338 if (value_length > 4) {
1339 int language_length = tvb_get_ntohs(tvb, valoffset + 0);
1341 if (tvb_offset_exists(tvb, valoffset + 2 + language_length)) {
1342 int string_length = tvb_get_ntohs(tvb, valoffset + 2 + language_length);
1343 if (tvb_offset_exists(tvb, valoffset + 2 + language_length + 2 + string_length)) {
1344 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));
1345 break;
1351 if (tag == TAG_NAMEWITHLANGUAGE) {
1352 proto_tree_add_item(tree, hf_ipp_namewithlanguage_value, tvb, valoffset, value_length, ENC_NA);
1354 else {
1355 proto_tree_add_item(tree, hf_ipp_textwithlanguage_value, tvb, valoffset, value_length, ENC_NA);
1357 break;
1359 case TAG_BEGCOLLECTION :
1360 endoffset = ipp_fmt_collection(tvb, pinfo, valoffset + value_length, value, sizeof(value));
1361 subtree = proto_tree_add_subtree_format(tree, tvb, valoffset, endoffset - valoffset, ett_ipp_member, NULL, "collection %s", value);
1362 break;
1364 default :
1365 proto_tree_add_string_format(tree, hf_ipp_octetstring_value, tvb, valoffset, value_length, NULL, "%s value: ??? %d bytes ???", tag_desc, value_length);
1366 break;
1369 return subtree;
1372 static proto_tree *
1373 add_charstring_tree(proto_tree *tree, tvbuff_t *tvb, int offset,
1374 uint8_t tag, int name_length, const char *name, int value_length)
1376 int count = 0, valoffset = offset;
1377 const char *type = val_to_str(tag, tag_vals, "unknown-%02x");
1378 char *value = NULL;
1380 do {
1382 * Add the string...
1385 char *temp = NULL;
1387 count ++;
1389 if ((tag == TAG_NAMEWITHLANGUAGE || tag == TAG_TEXTWITHLANGUAGE) && value_length > 4) {
1390 int language_length = tvb_get_ntohs(tvb, valoffset + 0);
1391 int string_length;
1393 if (tvb_offset_exists(tvb, valoffset + 2 + language_length)) {
1394 string_length = tvb_get_ntohs(tvb, valoffset + 2 + language_length);
1395 if (tvb_offset_exists(tvb, valoffset + 2 + language_length + 2 + string_length)) {
1396 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));
1400 else {
1401 temp = wmem_strdup_printf(wmem_packet_scope(), "'%s'", tvb_format_text(wmem_packet_scope(), tvb, valoffset + 1 + 2 + name_length + 2, value_length));
1404 if (value)
1405 value = wmem_strconcat(wmem_packet_scope(), value, ",", temp, NULL);
1406 else
1407 value = wmem_strdup(wmem_packet_scope(), temp);
1410 * Move to the next value...
1413 valoffset += 1 + 2 + name_length + 2 + value_length;
1415 if (!tvb_offset_exists(tvb, valoffset + 3))
1416 break;
1418 tag = tvb_get_uint8(tvb, valoffset);
1419 name_length = tvb_get_ntohs(tvb, valoffset + 1);
1420 if (!tvb_offset_exists(tvb, valoffset + 1 + 2 + name_length + 2))
1421 break;
1423 value_length = tvb_get_ntohs(tvb, valoffset + 1 + 2 + name_length);
1425 while (name_length == 0 && (TAG_TYPE(tag) == TAG_TYPE_CHARSTRING || tag == TAG_NAMEWITHLANGUAGE || tag == TAG_TEXTWITHLANGUAGE));
1427 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);
1430 static void
1431 add_charstring_value(const char *tag_desc, proto_tree *tree, tvbuff_t *tvb,
1432 int offset, int name_length, const char *name _U_, int value_length, uint8_t tag)
1434 proto_item *ti;
1435 int valoffset = offset + 1 + 2 + name_length + 2;
1437 if (name_length > 0)
1438 proto_tree_add_item(tree, hf_ipp_name, tvb, offset + 1 + 2, name_length, ENC_ASCII);
1440 if (tag == TAG_MEMBERATTRNAME)
1441 proto_tree_add_item(tree, hf_ipp_memberattrname, tvb, valoffset, value_length, ENC_ASCII);
1442 else {
1443 ti = proto_tree_add_item(tree, hf_ipp_charstring_value, tvb, valoffset, value_length, ENC_ASCII);
1444 if (strcmp(tag_desc, "") == 0) {
1445 proto_item_prepend_text(ti, "string ");
1446 } else {
1447 proto_item_prepend_text(ti, "%s ", tag_desc);
1452 static int
1453 // NOLINTNEXTLINE(misc-no-recursion)
1454 ipp_fmt_collection(tvbuff_t *tvb, packet_info *pinfo, int valoffset, char *buffer, int bufsize)
1456 char *bufptr = buffer, *bufend = buffer + bufsize - 1;
1457 uint8_t tag;
1458 int name_length, value_length;
1459 int overflow = 0;
1461 /* Should be larger to be meaningful, but at least prevent illegal
1462 * memory accesses.
1464 DISSECTOR_ASSERT_CMPINT(bufsize, >=, 2);
1466 *bufptr++ = '{';
1467 buffer ++;
1469 do {
1470 if (!tvb_offset_exists(tvb, valoffset + 3))
1471 break;
1473 tag = tvb_get_uint8(tvb, valoffset);
1474 name_length = tvb_get_ntohs(tvb, valoffset + 1);
1475 if (!tvb_offset_exists(tvb, valoffset + 1 + 2 + name_length + 2))
1476 break;
1478 value_length = tvb_get_ntohs(tvb, valoffset + 1 + 2 + name_length);
1480 if (!tvb_offset_exists(tvb, valoffset + 1 + 2 + name_length + 2 + value_length))
1481 break;
1483 if (tag == TAG_MEMBERATTRNAME && !overflow) {
1484 if (bufptr > buffer && bufptr < bufend)
1485 *bufptr++ = ',';
1487 if ((bufend - bufptr) < value_length) {
1488 (void) g_strlcpy(bufptr, "...", bufend - bufptr + 1);
1489 overflow = 1;
1491 else {
1492 (void) g_strlcpy(bufptr, tvb_format_text(wmem_packet_scope(), tvb, valoffset + 1 + 2 + name_length + 2, value_length), bufend - bufptr + 1);
1495 bufptr += strlen(bufptr);
1498 valoffset += 1 + 2 + name_length + 2 + value_length;
1500 if (tag == TAG_BEGCOLLECTION) {
1501 char temp[176];
1503 increment_dissection_depth(pinfo);
1504 valoffset = ipp_fmt_collection(tvb, pinfo, valoffset, temp, sizeof(temp));
1505 decrement_dissection_depth(pinfo);
1506 if (!overflow) {
1507 if ((bufend - bufptr) < (int)strlen(temp)) {
1508 (void) g_strlcpy(bufptr, "...", bufend - bufptr + 1);
1509 overflow = 1;
1511 else {
1512 (void) g_strlcpy(bufptr, temp, bufend - bufptr + 1);
1514 bufptr += strlen(bufptr);
1517 } while (tag != TAG_ENDCOLLECTION);
1519 if (bufptr < bufend)
1520 *bufptr++ = '}';
1522 *bufptr = '\0';
1523 if (bufptr == bufend) {
1524 /* buffer was already advanced past the initial '{' */
1525 ws_utf8_truncate(buffer, bufsize - 2);
1528 return (valoffset);
1532 static void
1533 ipp_fmt_version( char *result, uint32_t revision )
1535 snprintf( result, ITEM_LABEL_LENGTH, "%u.%u", (uint8_t)(( revision & 0xFF00 ) >> 8), (uint8_t)(revision & 0xFF) );
1538 void
1539 proto_register_ipp(void)
1541 static hf_register_info hf[] = {
1542 /* Generated from convert_proto_tree_add_text.pl */
1543 { &hf_ipp_version, { "version", "ipp.version", FT_UINT16, BASE_CUSTOM, CF_FUNC(ipp_fmt_version), 0x0, NULL, HFILL }},
1544 { &hf_ipp_operation_id, { "operation-id", "ipp.operation_id", FT_UINT16, BASE_HEX, VALS(operation_vals), 0x0, NULL, HFILL }},
1545 { &hf_ipp_status_code, { "status-code", "ipp.status_code", FT_UINT16, BASE_HEX, VALS(status_vals), 0x0, NULL, HFILL }},
1546 { &hf_ipp_request_id, { "request-id", "ipp.request_id", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
1547 { &hf_ipp_name, { "name", "ipp.name", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1548 { &hf_ipp_memberattrname, { "memberAttrName", "ipp.memberattrname", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1549 { &hf_ipp_boolean_value, { "boolean value", "ipp.boolean_value", FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1550 { &hf_ipp_integer_value, { "integer value", "ipp.integer_value", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
1551 { &hf_ipp_enum_value, { "enum value", "ipp.enum_value", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
1552 { &hf_ipp_enum_value_printer_state, { "printer-state", "ipp.enum_value", FT_INT32, BASE_DEC, VALS(printer_state_vals), 0x0, NULL, HFILL }},
1553 { &hf_ipp_enum_value_job_state, { "job-state", "ipp.enum_value", FT_INT32, BASE_DEC, VALS(job_state_vals), 0x0, NULL, HFILL }},
1554 { &hf_ipp_enum_value_document_state, { "document-state", "ipp.enum_value", FT_INT32, BASE_DEC, VALS(document_state_vals), 0x0, NULL, HFILL }},
1555 { &hf_ipp_enum_value_operations_supported, { "operations-supported", "ipp.enum_value", FT_INT32, BASE_DEC, VALS(operation_vals), 0x0, NULL, HFILL }},
1556 { &hf_ipp_enum_value_finishings, { "finishings", "ipp.enum_value", FT_INT32, BASE_DEC, VALS(finishings_vals), 0x0, NULL, HFILL }},
1557 { &hf_ipp_enum_value_orientation, { "orientation", "ipp.enum_value", FT_INT32, BASE_DEC, VALS(orientation_vals), 0x0, NULL, HFILL }},
1558 { &hf_ipp_enum_value_print_quality, { "print-quality", "ipp.enum_value", FT_INT32, BASE_DEC, VALS(quality_vals), 0x0, NULL, HFILL }},
1559 { &hf_ipp_enum_value_transmission_status, { "transmission-status", "ipp.enum_value", FT_INT32, BASE_DEC, VALS(transmission_status_vals), 0x0, NULL, HFILL }},
1560 { &hf_ipp_outofband_value, { "out-of-band value", "ipp.outofband_value", FT_UINT8, BASE_HEX, VALS(tag_vals), 0x0, NULL, HFILL }},
1561 { &hf_ipp_charstring_value, { "value", "ipp.charstring_value", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1562 { &hf_ipp_octetstring_value, { "octetString value", "ipp.octetstring_value", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1563 { &hf_ipp_datetime_value, { "dateTime value", "ipp.datetime_value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1564 { &hf_ipp_resolution_value, { "resolution value", "ipp.resolution_value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1565 { &hf_ipp_rangeofinteger_value, { "rangeOfInteger value", "ipp.rangeofinteger_value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1566 { &hf_ipp_textwithlanguage_value, { "textWithLanguage value", "ipp.textwithlanguage_value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1567 { &hf_ipp_namewithlanguage_value, { "nameWithLanguage value", "ipp.namewithlanguage_value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1568 { &hf_ipp_unknown_value, { "unknown value", "ipp.unknown_value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1569 { &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 }},
1570 { &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 }},
1571 { &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 }}
1573 static int *ett[] = {
1574 &ett_ipp,
1575 &ett_ipp_as,
1576 &ett_ipp_attr,
1577 &ett_ipp_member
1580 proto_ipp = proto_register_protocol("Internet Printing Protocol", "IPP", "ipp");
1582 proto_register_field_array(proto_ipp, hf, array_length(hf));
1583 proto_register_subtree_array(ett, array_length(ett));
1585 ipp_handle = register_dissector("ipp", dissect_ipp, proto_ipp);
1588 void
1589 proto_reg_handoff_ipp(void)
1592 * IPP uses the same well-known TCP port, 631, for both running atop HTTP
1593 * and atop HTTP over TLS (IPPS, RFC 7472). The latter includes both
1594 * connections that start out as HTTP and upgrade to using TLS (RFC 2817)
1595 * as well as connections that begin as TLS. Despite RFC 8010:
1596 * the "Content-Type" of the message body in each request and response
1597 * MUST be "application/ipp"
1598 * a number of implementations fail to include the Content-Type in their
1599 * chunked responses. (#18825, #5718, #6765).
1601 * For that reason, we register IPP in the HTTP port-based dissector so
1602 * that packets without a Content-Type on port 631 will use this dissector.
1603 * (RFC 8010 also notes that HTTP/2 is an OPTIONAL transport layer; we
1604 * don't have a port-based dissector dissector for HTTP/2, but hopefully
1605 * any implementations that use HTTP/2 always send the Content-Type.)
1606 * Note we check for port-based dissectors after the Content-Type; this
1607 * is good because many IPP servers will respond to non-IPP HTTP requests
1608 * on port 631 just as they would on ports 80 or 443.
1610 * We can only have a single dissector in the TCP dissector table for
1611 * port 631. If we don't register a fake helper protocol that tries
1612 * each of TLS, HTTP/2, and HTTP in order (cf. #16541, #18016), we're
1613 * currently better off having TLS be the registered dissector and HTTP
1614 * be detected heuristically, because the non-heuristic HTTP dissector
1615 * never rejects packets, even when it doesn't add anything to the tree.
1617 dissector_handle_t http_tls_handle = find_dissector_add_dependency("http-over-tls", proto_ipp);
1618 http_tcp_dissector_add(631, ipp_handle);
1619 ssl_dissector_add(631, http_tls_handle);
1620 dissector_add_string("media_type", "application/ipp", ipp_handle);
1624 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1626 * Local variables:
1627 * c-basic-offset: 4
1628 * tab-width: 8
1629 * indent-tabs-mode: nil
1630 * End:
1632 * vi: set shiftwidth=4 tabstop=8 expandtab:
1633 * :indentSize=4:tabSize=8:noTabs=true: