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