2 * Routines for SANE dissection
3 * Copyright 2024, James Ring <sjr@jdns.org>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
13 * A dissector for the SANE protocol (https://sane-project.gitlab.io/standard/net.html).
15 * This dissector works only for the control protocol (typically between a
16 * client on some ephemeral port and a server on port 6566). The data transfer
17 * of scanned images is done on a separate connection. Future versions of this
18 * dissector might provide dissected image information.
20 * SANE is a protocol layered on top of TCP. The main dissect_sane function
21 * relies on tcp_dissect_pdus to reassemble large messages. The protocol has no
22 * meta-information (e.g. length of packet, type of message), the only way you
23 * can know the format of a response is to see the corresponding request. The
24 * only way you can know the length of a PDU is to read and understand various
25 * fields in the request/response.
27 * For these reasons, the get_sane_pdu_len function has to pretty much do all
28 * the work of the dissector itself. There is probably a more elegant way to do
29 * this without the duplication that exists between get_sane_pdu_len and
34 * - Setup proper request / response tracking, with
35 * - references to related packets,
36 * - response time indications.
41 #include <wireshark.h>
42 #include <epan/packet.h>
43 #include <epan/proto_data.h>
44 #include <epan/prefs.h>
46 #include "packet-tcp.h"
48 #define SANE_WORD_LENGTH 4
49 #define SANE_MODULE_NAME "sane"
50 #define SANE_PORT "6566"
52 static range_t
*sane_server_ports
;
54 static dissector_handle_t sane_handle
;
56 static int hf_sane_opcode
;
57 static int hf_sane_version
;
58 static int hf_sane_version_major
;
59 static int hf_sane_version_minor
;
60 static int hf_sane_version_build
;
61 static int hf_sane_username
;
62 static int hf_sane_password
;
63 static int hf_sane_string
;
64 static int hf_sane_string_length
;
65 static int hf_sane_array_length
;
66 static int hf_sane_device_descriptor
;
67 static int hf_sane_device_name
;
68 static int hf_sane_device_vendor
;
69 static int hf_sane_device_model
;
70 static int hf_sane_device_type
;
71 static int hf_sane_resource_name
;
72 static int hf_sane_device_handle
;
73 static int hf_sane_option_descriptor
;
74 static int hf_sane_option_index
;
75 static int hf_sane_option_control_action
;
76 static int hf_sane_option_value_type
;
77 static int hf_sane_option_length
;
78 static int hf_sane_option_count
;
79 static int hf_sane_option_name
;
80 static int hf_sane_option_value
;
81 static int hf_sane_option_string_value
;
82 static int hf_sane_option_numeric_value
;
83 static int hf_sane_option_boolean_value
;
84 static int hf_sane_option_title
;
85 static int hf_sane_option_description
;
86 static int hf_sane_option_unit
;
87 static int hf_sane_option_size
;
88 static int hf_sane_option_capabilities
;
89 static int hf_sane_option_constraints
;
90 static int hf_sane_option_constraint_type
;
91 static int hf_sane_option_possible_string_value
;
92 static int hf_sane_option_possible_word_value
;
93 static int hf_sane_option_range_min
;
94 static int hf_sane_option_range_max
;
95 static int hf_sane_option_range_quant
;
96 static int hf_sane_status
;
97 static int hf_sane_data_port
;
98 static int hf_sane_byte_order
;
99 static int hf_sane_pointer_value
;
100 static int hf_sane_frame_format
;
101 static int hf_sane_scan_line_count
;
102 static int hf_sane_scan_pixel_depth
;
103 static int hf_sane_scan_pixels_per_line
;
104 static int hf_sane_scan_bytes_per_line
;
105 static int hf_sane_scan_is_last_frame
;
106 static int hf_sane_dummy_value
;
108 #define SANE_CAP_NONE 0x00000000
109 #define SANE_CAP_SOFT_SELECT 0x00000001
110 #define SANE_CAP_HARD_SELECT 0x00000002
111 #define SANE_CAP_SOFT_DETECT 0x00000004
112 #define SANE_CAP_EMULATED 0x00000008
113 #define SANE_CAP_AUTOMATIC 0x00000010
114 #define SANE_CAP_INACTIVE 0x00000020
115 #define SANE_CAP_ADVANCED 0x00000040
117 static int hf_sane_option_capability_soft_select
;
118 static int hf_sane_option_capability_hard_select
;
119 static int hf_sane_option_capability_soft_detect
;
120 static int hf_sane_option_capability_emulated
;
121 static int hf_sane_option_capability_automatic
;
122 static int hf_sane_option_capability_inactive
;
123 static int hf_sane_option_capability_advanced
;
125 #define SANE_INFO_INEXACT 0x00000001
126 #define SANE_INFO_RELOAD_OPTIONS 0x00000002
127 #define SANE_INFO_RELOAD_PARAMS 0x00000004
129 static int hf_sane_control_option_info
;
130 static int hf_sane_control_option_inexact
;
131 static int hf_sane_control_option_reload_options
;
132 static int hf_sane_control_option_reload_params
;
134 static int* const sane_cap_bits
[] = {
135 &hf_sane_option_capability_soft_select
,
136 &hf_sane_option_capability_hard_select
,
137 &hf_sane_option_capability_soft_detect
,
138 &hf_sane_option_capability_emulated
,
139 &hf_sane_option_capability_automatic
,
140 &hf_sane_option_capability_inactive
,
141 &hf_sane_option_capability_advanced
,
145 static int* const sane_control_option_info_bits
[] = {
146 &hf_sane_control_option_inexact
,
147 &hf_sane_control_option_reload_options
,
148 &hf_sane_control_option_reload_params
,
152 static int proto_sane
;
154 static int ett_sane_version
;
155 static int ett_sane_string
;
156 static int ett_sane_option
;
157 static int ett_sane_option_value
;
158 static int ett_sane_option_capabilities
;
159 static int ett_sane_option_constraints
;
160 static int ett_sane_control_option_info
;
161 static int ett_sane_device_descriptor
;
164 SANE_NET_UNKNOWN
= -1,
166 SANE_NET_GET_DEVICES
= 1,
169 SANE_NET_GET_OPTION_DESCRIPTORS
= 4,
170 SANE_NET_CONTROL_OPTION
= 5,
171 SANE_NET_GET_PARAMETERS
= 6,
174 SANE_NET_AUTHORIZE
= 9,
178 static const value_string opcode_vals
[] = {
179 {SANE_NET_INIT
, "SANE_NET_INIT"},
180 {SANE_NET_GET_DEVICES
, "SANE_NET_GET_DEVICES"},
181 {SANE_NET_OPEN
, "SANE_NET_OPEN"},
182 {SANE_NET_CLOSE
, "SANE_NET_CLOSE"},
183 {SANE_NET_GET_OPTION_DESCRIPTORS
, "SANE_NET_GET_OPTION_DESCRIPTORS"},
184 {SANE_NET_CONTROL_OPTION
, "SANE_NET_CONTROL_OPTION"},
185 {SANE_NET_GET_PARAMETERS
, "SANE_NET_GET_PARAMETERS"},
186 {SANE_NET_START
, "SANE_NET_START"},
187 {SANE_NET_CANCEL
, "SANE_NET_CANCEL"},
188 {SANE_NET_AUTHORIZE
, "SANE_NET_AUTHORIZE"},
189 {SANE_NET_EXIT
, "SANE_NET_EXIT"},
194 SANE_NO_CONSTRAINT
= 0,
195 SANE_CONSTRAINT_RANGE
= 1,
196 SANE_CONSTRAINT_WORD_LIST
= 2,
197 SANE_CONSTRAINT_STRING_LIST
= 3,
198 } sane_constraint_type
;
200 static const value_string sane_constraint_type_names
[] = {
201 {SANE_NO_CONSTRAINT
, "SANE_NO_CONSTRAINT"},
202 {SANE_CONSTRAINT_RANGE
, "SANE_CONSTRAINT_RANGE"},
203 {SANE_CONSTRAINT_WORD_LIST
, "SANE_CONSTRAINT_WORD_LIST"},
204 {SANE_CONSTRAINT_STRING_LIST
, "SANE_CONSTRAINT_STRING_LIST"},
212 SANE_TYPE_STRING
= 3,
213 SANE_TYPE_BUTTON
= 4,
217 static const value_string sane_value_types
[] = {
218 {SANE_TYPE_BOOL
, "SANE_TYPE_BOOL"},
219 {SANE_TYPE_INT
, "SANE_TYPE_INT"},
220 {SANE_TYPE_FIXED
, "SANE_TYPE_FIXED"},
221 {SANE_TYPE_STRING
, "SANE_TYPE_STRING"},
222 {SANE_TYPE_BUTTON
, "SANE_TYPE_BUTTON"},
223 {SANE_TYPE_GROUP
, "SANE_TYPE_GROUP"},
227 static const value_string control_types
[] = {
228 {0, "SANE_ACTION_GET_VALUE"},
229 {1, "SANE_ACTION_SET_VALUE"},
230 {2, "SANE_ACTION_SET_AUTO"},
240 SANE_UNIT_PERCENT
= 5,
241 SANE_UNIT_MICROSECOND
= 6,
244 static const value_string sane_option_units
[] = {
245 {SANE_UNIT_NONE
, "SANE_UNIT_NONE"},
246 {SANE_UNIT_PIXEL
, "SANE_UNIT_PIXEL"},
247 {SANE_UNIT_BIT
, "SANE_UNIT_BIT"},
248 {SANE_UNIT_MM
, "SANE_UNIT_MM"},
249 {SANE_UNIT_DPI
, "SANE_UNIT_DPI"},
250 {SANE_UNIT_PERCENT
, "SANE_UNIT_PERCENT"},
251 {SANE_UNIT_MICROSECOND
, "SANE_UNIT_MICROSECOND"},
255 static const value_string sane_option_unit_suffixes
[] = {
266 SANE_STATUS_UNKNOWN
= -1,
270 static const value_string status_values
[] = {
271 {0, "SANE_STATUS_GOOD"},
272 {1, "SANE_STATUS_UNSUPPORTED"},
273 {2, "SANE_STATUS_CANCELLED"},
274 {3, "SANE_STATUS_DEVICE_BUSY"},
275 {4, "SANE_STATUS_INVAL"},
276 {5, "SANE_STATUS_EOF"},
277 {6, "SANE_STATUS_JAMMED"},
278 {7, "SANE_STATUS_NO_DOCS"},
279 {8, "SANE_STATUS_COVER_OPEN"},
280 {9, "SANE_STATUS_IO_ERROR"},
281 {10, "SANE_STATUS_NO_MEM"},
282 {11, "SANE_STATUS_ACCESS_DENIED"},
286 static const value_string sane_frame_format_names
[] = {
287 {0, "SANE_FRAME_GRAY"},
288 {1, "SANE_FRAME_RGB"},
289 {2, "SANE_FRAME_RED"},
290 {3, "SANE_FRAME_GREEN"},
291 {4, "SANE_FRAME_BLUE"},
297 sane_rpc_code opcode
;
301 /* Keep track of current request status during first pass.
302 N.B. opcode is stored in per-frame data and read during subsequent passes.
303 Could if necessary be expanded to include frame numbers and timestamps for
304 more complete request/response tracking.
308 sane_pdu last_request
;
321 tvb_read_sane_word(tvb_sane_reader
*r
, uint32_t *dest
) {
322 if (tvb_captured_length_remaining(r
->tvb
, r
->offset
) < SANE_WORD_LENGTH
) {
327 *dest
= tvb_get_ntohl(r
->tvb
, r
->offset
);
329 r
->offset
+= SANE_WORD_LENGTH
;
330 r
->bytes_read
+= SANE_WORD_LENGTH
;
331 return SANE_WORD_LENGTH
;
334 #define WORD_OR_RETURN(r, var) \
335 do { if (tvb_read_sane_word((r), (var)) == 0) { return 0; } } while(0)
339 tvb_read_sane_string(tvb_sane_reader
*r
, wmem_allocator_t
*alloc
, char **dest
) {
341 WORD_OR_RETURN(r
, &str_len
);
343 if (tvb_captured_length_remaining(r
->tvb
, r
->offset
) < str_len
) {
348 *dest
= tvb_get_string_enc(alloc
, r
->tvb
, r
->offset
, str_len
, ENC_ASCII
| ENC_NA
);
351 r
->offset
+= str_len
;
352 r
->bytes_read
+= str_len
;
353 return SANE_WORD_LENGTH
+ str_len
;
356 #define STRING_OR_RETURN(r) \
357 do { if (tvb_read_sane_string((r), NULL, NULL) == 0) { return 0; } } while(0)
360 tvb_skip_bytes(tvb_sane_reader
*r
, int len
) {
361 if (tvb_captured_length_remaining(r
->tvb
, r
->offset
) < len
) {
366 r
->bytes_read
+= len
;
371 * Returns the expected response type for the (presumed) response in `pinfo`.
372 * This usually returns the opcode of the last request seen in the conversation,
373 * except for special handling of the authorization flow, for example:
375 * Client: SANE_NET_OPEN request (1)
376 * Server: SANE_NET_OPEN response, authentication resource set (2)
377 * Client: SANE_NET_AUTHORIZE request, username+password sent (3)
378 * Server: SANE_NET_AUTHORIZE response sent, success (4)
379 * Server: SANE_NET_OPEN response immediately sent (5)
381 * In this case, if the expected response type of PDU 5 is SANE_NET_OPEN,
382 * because the server is responding to the request sent in PDU 2.
385 get_sane_expected_response_type(sane_session
*sess
, packet_info
*pinfo
) {
387 /* Look up any previous result. N.B. as called for length *and* dissecting,
388 there may already be a value stored on first pass! */
389 if (PINFO_FD_VISITED(pinfo
) || p_get_proto_data(wmem_file_scope(), pinfo
, proto_sane
, 0)) {
390 return (sane_rpc_code
)GPOINTER_TO_UINT(p_get_proto_data(wmem_file_scope(), pinfo
, proto_sane
, 0));
393 /* First pass. Will be response to last_request if set, or AUTH request if flag set. */
394 sane_rpc_code code
= SANE_NET_UNKNOWN
;
395 if (sess
->seen_request
) {
397 code
= SANE_NET_AUTHORIZE
;
401 code
= sess
->last_request
.opcode
;
405 /* Remember this code for later queries. */
406 p_add_proto_data(wmem_file_scope(), pinfo
, proto_sane
, 0, GUINT_TO_POINTER(code
));
412 dissect_sane_word(tvb_sane_reader
*r
, proto_tree
*tree
, int hfindex
, int *word
) {
413 proto_item
*item
= proto_tree_add_item(tree
, hfindex
, r
->tvb
, r
->offset
, SANE_WORD_LENGTH
,
415 // safe to ignore the return value here, we're guaranteed to have enough bytes to
417 (void)tvb_read_sane_word(r
, word
);
422 * Dissects and returns a SANE-encoded string from `r`.
424 * Also creates a proto_item representing the string. The `format` string should
425 * contain a string format specifier (i.e. "%s"), which will be replaced with the
426 * consumed string in the proto_item's text.
429 dissect_sane_string(tvb_sane_reader
*r
, packet_info
*pinfo
, proto_tree
*tree
, int hfindex
, const char *format
) {
430 int offset
= r
->offset
;
432 int len
= tvb_read_sane_string(r
, pinfo
->pool
, &str
);
434 proto_item
*str_item
= proto_tree_add_item(tree
, hf_sane_string
, r
->tvb
, offset
, len
, ENC_NA
);
435 proto_tree
*str_tree
= proto_item_add_subtree(str_item
, ett_sane_string
);
437 proto_item_set_text(str_item
, format
, str
);
438 proto_tree_add_item(str_tree
, hf_sane_string_length
, r
->tvb
, offset
, SANE_WORD_LENGTH
, ENC_BIG_ENDIAN
);
439 proto_tree_add_item(str_tree
, hfindex
, r
->tvb
, offset
+ SANE_WORD_LENGTH
, len
- SANE_WORD_LENGTH
, ENC_NA
);
444 dissect_sane_net_init_request(tvb_sane_reader
*r
, packet_info
*pinfo
, proto_tree
*tree
) {
446 int offset
= r
->offset
;
447 proto_item
*version_item
= dissect_sane_word(r
, tree
, hf_sane_version
, &version
);
448 proto_item
*version_tree
= proto_item_add_subtree(version_item
, ett_sane_version
);
450 proto_item_append_text(version_item
, " (major: %d, minor: %d, build: %d)", version
>> 24,
451 (version
>> 16) & 0xff, version
& 0xffff);
453 proto_tree_add_item(version_tree
, hf_sane_version_major
, r
->tvb
, offset
, 1, ENC_NA
);
454 proto_tree_add_item(version_tree
, hf_sane_version_minor
, r
->tvb
, offset
+ 1, 1, ENC_NA
);
455 proto_tree_add_item(version_tree
, hf_sane_version_build
, r
->tvb
, offset
+ 2, 2, ENC_BIG_ENDIAN
);
457 dissect_sane_string(r
, pinfo
, tree
, hf_sane_username
, "Username: %s");
461 dissect_sane_net_open_request(tvb_sane_reader
*r
, packet_info
*pinfo
, proto_tree
*tree
) {
462 dissect_sane_string(r
, pinfo
, tree
, hf_sane_device_name
, "Device name: %s");
466 dissect_control_option_value(tvb_sane_reader
*r
, packet_info
*pinfo
, proto_tree
*tree
) {
468 dissect_sane_word(r
, tree
, hf_sane_option_value_type
, &value_type
);
470 proto_item
*value_item
= proto_tree_add_item(tree
, hf_sane_option_value
, r
->tvb
, r
->offset
, -1, ENC_NA
);
471 proto_tree
*value_tree
= proto_item_add_subtree(value_item
, ett_sane_option_value
);
473 int array_length
= 0;
474 proto_item
*length_item
= dissect_sane_word(r
, value_tree
, hf_sane_option_length
, &array_length
);
476 if (value_type
== SANE_TYPE_STRING
) {
477 dissect_sane_string(r
, pinfo
, value_tree
, hf_sane_option_string_value
, "Option value: '%s'");
479 proto_item_append_text(length_item
, " (vector of length %d)", array_length
/ SANE_WORD_LENGTH
);
480 dissect_sane_word(r
, value_tree
, hf_sane_array_length
, &array_length
);
482 for (int i
= 0; i
< array_length
; i
++) {
483 if (value_type
== SANE_TYPE_FIXED
) {
485 proto_item
*numeric_value
= dissect_sane_word(r
, value_tree
, hf_sane_option_numeric_value
, &value
);
486 proto_item_append_text(numeric_value
, " (%f)", ((double) value
) / (1 << 16));
487 } else if (value_type
== SANE_TYPE_INT
) {
489 proto_item
*numeric_value
= dissect_sane_word(r
, value_tree
, hf_sane_option_numeric_value
, &value
);
490 proto_item_append_text(numeric_value
, " (%d)", value
);
491 } else if (value_type
== SANE_TYPE_BOOL
) {
492 dissect_sane_word(r
, value_tree
, hf_sane_option_boolean_value
, NULL
);
499 dissect_sane_net_control_option_request(tvb_sane_reader
*r
, packet_info
*pinfo
, proto_tree
*tree
) {
500 dissect_sane_word(r
, tree
, hf_sane_device_handle
, NULL
);
501 dissect_sane_word(r
, tree
, hf_sane_option_index
, NULL
);
502 dissect_sane_word(r
, tree
, hf_sane_option_control_action
, NULL
);
503 dissect_control_option_value(r
, pinfo
, tree
);
507 dissect_sane_net_authorize_request(tvb_sane_reader
*r
, packet_info
*pinfo
, proto_tree
*tree
) {
508 dissect_sane_string(r
, pinfo
, tree
, hf_sane_resource_name
, "Authentication resource: %s");
509 dissect_sane_string(r
, pinfo
, tree
, hf_sane_username
, "Username: %s");
510 dissect_sane_string(r
, pinfo
, tree
, hf_sane_password
, "Password: %s");
513 /** Dissects a message whose only payload is a device handle. */
515 dissect_sane_device_handle_request(tvb_sane_reader
*r
, proto_tree
*tree
) {
516 dissect_sane_word(r
, tree
, hf_sane_device_handle
, NULL
);
520 dissect_sane_request(tvb_sane_reader
*r
, packet_info
*pinfo
, proto_tree
*tree
) {
521 unsigned opcode
= SANE_NET_UNKNOWN
;
522 dissect_sane_word(r
, tree
, hf_sane_opcode
, &opcode
);
523 proto_item_append_text(tree
, ": %s request", val_to_str(opcode
, opcode_vals
, "Unknown opcode (%u)"));
524 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "%s request", val_to_str(opcode
, opcode_vals
, "Unknown opcode (%u)"));
528 dissect_sane_net_init_request(r
, pinfo
, tree
);
530 case SANE_NET_GET_DEVICES
:
531 // no additional payload here
534 dissect_sane_net_open_request(r
, pinfo
, tree
);
536 case SANE_NET_CONTROL_OPTION
:
537 dissect_sane_net_control_option_request(r
, pinfo
, tree
);
541 case SANE_NET_CANCEL
:
542 case SANE_NET_GET_PARAMETERS
:
543 case SANE_NET_GET_OPTION_DESCRIPTORS
:
544 dissect_sane_device_handle_request(r
, tree
);
546 case SANE_NET_AUTHORIZE
:
547 dissect_sane_net_authorize_request(r
, pinfo
, tree
);
551 return r
->bytes_read
;
555 dissect_sane_status(tvb_sane_reader
*r
, packet_info
*pinfo
, proto_tree
*tree
, unsigned *status_ptr
) {
556 int offset
= r
->offset
;
557 unsigned status
= SANE_STATUS_UNKNOWN
;
559 // Safe to ignore the return value here, we're guaranteed to have enough bytes to
561 (void)tvb_read_sane_word(r
, &status
);
563 proto_item_append_text(tree
, " (%s)", val_to_str(status
, status_values
, "Unknown status (%u)"));
564 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " (%s)", val_to_str(status
, status_values
, "Unknown (%u)"));
566 proto_item
*status_item
= proto_tree_add_item(tree
, hf_sane_status
, r
->tvb
, offset
, SANE_WORD_LENGTH
, ENC_BIG_ENDIAN
);
567 proto_item_append_text(status_item
, " (%s)", val_to_str(status
, status_values
, "Unknown (%u)"));
570 *status_ptr
= status
;
577 dissect_sane_net_init_response(tvb_sane_reader
*r
, packet_info
*pinfo
, proto_tree
*tree
) {
579 dissect_sane_status(r
, pinfo
, tree
, &status
);
582 proto_item
*version_item
= dissect_sane_word(r
, tree
, hf_sane_version
, &version
);
583 proto_item
*version_tree
= proto_item_add_subtree(version_item
, ett_sane_version
);
585 proto_item_append_text(version_item
, " (major: %d, minor: %d, build: %d)", version
>> 24,
586 (version
>> 16) & 0xff, version
& 0xffff);
588 proto_tree_add_item(version_tree
, hf_sane_version_major
, r
->tvb
, SANE_WORD_LENGTH
, 1, ENC_NA
);
589 proto_tree_add_item(version_tree
, hf_sane_version_minor
, r
->tvb
, SANE_WORD_LENGTH
+ 1, 1, ENC_NA
);
590 proto_tree_add_item(version_tree
, hf_sane_version_build
, r
->tvb
, SANE_WORD_LENGTH
+ 2, 2, ENC_BIG_ENDIAN
);
594 dissect_sane_net_open_response(tvb_sane_reader
*r
, packet_info
*pinfo
, proto_tree
*tree
) {
595 unsigned status
= SANE_STATUS_UNKNOWN
;
596 dissect_sane_status(r
, pinfo
, tree
, &status
);
597 dissect_sane_word(r
, tree
, hf_sane_device_handle
, NULL
);
598 dissect_sane_string(r
, pinfo
, tree
, hf_sane_resource_name
, "Authentication resource: '%s'");
602 append_option_value(proto_item
*item
, int value
, unsigned units
, unsigned type
) {
606 proto_item_append_text(item
, " (%d %s)", value
,
607 val_to_str_const(units
, sane_option_unit_suffixes
, "(unknown unit)"));
609 proto_item_append_text(item
, " (%d)", value
);
612 case SANE_TYPE_FIXED
: {
613 double fixed_val
= ((double) value
) / (1 << 16);
615 proto_item_append_text(item
, " (%f %s)", fixed_val
,
616 val_to_str_const(units
, sane_option_unit_suffixes
, "(unknown unit)"));
618 proto_item_append_text(item
, " (%f)", fixed_val
);
623 proto_item_append_text(item
, " (%s)", (value
== 1) ? "True" : ((value
== 0) ? "False" : "Invalid"));
631 dissect_sane_net_get_option_descriptors_response(tvb_sane_reader
*r
, packet_info
*pinfo
, proto_tree
*tree
) {
632 int option_count
= 0;
633 dissect_sane_word(r
, tree
, hf_sane_option_count
, &option_count
);
635 for (int i
= 0; i
< option_count
; i
++) {
638 int start_offset
= r
->offset
;
639 proto_item
*option_item
= proto_tree_add_item(tree
, hf_sane_option_descriptor
, r
->tvb
, start_offset
, 0, ENC_NA
);
640 proto_tree
*option_tree
= proto_item_add_subtree(option_item
, ett_sane_option
);
641 proto_item_set_text(option_item
, "Option descriptor %d", i
);
643 dissect_sane_word(r
, option_tree
, hf_sane_pointer_value
, NULL
);
644 char *option_name
= dissect_sane_string(r
, pinfo
, option_tree
, hf_sane_option_name
, "Option name: %s");
645 if (option_name
&& *option_name
) {
646 proto_item_append_text(option_item
, " (%s)", option_name
);
648 char *option_title
= dissect_sane_string(r
, pinfo
, option_tree
, hf_sane_option_title
, "Option title: %s");
649 if (!(option_name
&& *option_name
) && (option_title
&& *option_title
)) {
650 proto_item_append_text(option_item
, " (%s)", option_title
);
652 dissect_sane_string(r
, pinfo
, option_tree
, hf_sane_option_description
, "Option description: %s");
653 dissect_sane_word(r
, option_tree
, hf_sane_option_value_type
, &type
);
654 dissect_sane_word(r
, option_tree
, hf_sane_option_unit
, &unit
);
655 dissect_sane_word(r
, option_tree
, hf_sane_option_size
, NULL
);
657 proto_tree_add_bitmask(option_tree
, r
->tvb
, r
->offset
, hf_sane_option_capabilities
,
658 ett_sane_option_capabilities
,
659 sane_cap_bits
, ENC_BIG_ENDIAN
);
660 /* XXX - Add consistency checks (expert items):
661 * SANE_CAP_SOFT_SELECT set and SANE_CAP_HARD_SELECT set
662 * SANE_CAP_SOFT_SELECT set and SANE_CAP_SOFT_DETECT not set
664 tvb_skip_bytes(r
, SANE_WORD_LENGTH
);
666 int constraint_start
= r
->offset
;
667 proto_item
*constraint_item
= proto_tree_add_item(option_tree
, hf_sane_option_constraints
, r
->tvb
, constraint_start
, 0, ENC_NA
);
668 proto_tree
*constraint_tree
= proto_item_add_subtree(constraint_item
, ett_sane_option_constraints
);
670 int constraint_type
= SANE_NO_CONSTRAINT
;
671 dissect_sane_word(r
, constraint_tree
, hf_sane_option_constraint_type
, &constraint_type
);
672 proto_item_set_text(constraint_item
, "Constraint type: %s",
673 val_to_str(constraint_type
, sane_constraint_type_names
, "Unknown (%u)"));
675 int array_length
= 0;
679 switch (constraint_type
) {
680 case SANE_CONSTRAINT_STRING_LIST
:
681 dissect_sane_word(r
, constraint_tree
, hf_sane_array_length
, &array_length
);
683 for (int j
= 0; j
< array_length
; j
++) {
684 dissect_sane_string(r
, pinfo
, constraint_tree
, hf_sane_option_possible_string_value
, "Possible value: %s");
687 case SANE_CONSTRAINT_WORD_LIST
:
688 dissect_sane_word(r
, constraint_tree
, hf_sane_array_length
, &array_length
);
690 for (int j
= 0; j
< array_length
; j
++) {
692 proto_item
*value_item
= dissect_sane_word(r
, constraint_tree
, hf_sane_option_possible_word_value
,
694 append_option_value(value_item
, value
, unit
, type
);
697 case SANE_CONSTRAINT_RANGE
:
698 dissect_sane_word(r
, constraint_tree
, hf_sane_pointer_value
, NULL
);
700 proto_item
*min_item
= dissect_sane_word(r
, constraint_tree
, hf_sane_option_range_min
, &min
);
701 append_option_value(min_item
, min
, unit
, type
);
702 proto_item
*max_item
= dissect_sane_word(r
, constraint_tree
, hf_sane_option_range_max
, &max
);
703 append_option_value(max_item
, max
, unit
, type
);
704 proto_item
*quant_item
= dissect_sane_word(r
, constraint_tree
, hf_sane_option_range_quant
, &quant
);
705 append_option_value(quant_item
, quant
, unit
, type
);
709 proto_item_set_len(constraint_item
, r
->offset
- constraint_start
);
710 proto_item_set_len(option_item
, r
->offset
- start_offset
);
715 dissect_sane_net_start_response(tvb_sane_reader
*r
, packet_info
*pinfo
, proto_tree
*tree
) {
716 dissect_sane_status(r
, pinfo
, tree
, NULL
);
717 dissect_sane_word(r
, tree
, hf_sane_data_port
, NULL
);
718 dissect_sane_word(r
, tree
, hf_sane_byte_order
, NULL
);
719 dissect_sane_string(r
, pinfo
, tree
, hf_sane_resource_name
, "Authentication resource: %s");
723 dissect_sane_net_get_parameters_response(tvb_sane_reader
*r
, packet_info
*pinfo
, proto_tree
*tree
) {
724 dissect_sane_status(r
, pinfo
, tree
, NULL
);
725 dissect_sane_word(r
, tree
, hf_sane_frame_format
, NULL
);
726 dissect_sane_word(r
, tree
, hf_sane_scan_is_last_frame
, NULL
);
727 dissect_sane_word(r
, tree
, hf_sane_scan_bytes_per_line
, NULL
);
728 dissect_sane_word(r
, tree
, hf_sane_scan_pixels_per_line
, NULL
);
729 dissect_sane_word(r
, tree
, hf_sane_scan_line_count
, NULL
);
730 dissect_sane_word(r
, tree
, hf_sane_scan_pixel_depth
, NULL
);
734 dissect_sane_net_control_option_response(tvb_sane_reader
*r
, packet_info
*pinfo
, proto_tree
*tree
) {
735 dissect_sane_status(r
, pinfo
, tree
, NULL
);
736 proto_tree_add_bitmask(tree
, r
->tvb
, r
->offset
, hf_sane_control_option_info
,
737 ett_sane_control_option_info
,
738 sane_control_option_info_bits
, ENC_BIG_ENDIAN
);
739 tvb_skip_bytes(r
, SANE_WORD_LENGTH
);
740 dissect_control_option_value(r
, pinfo
, tree
);
741 dissect_sane_string(r
, pinfo
, tree
, hf_sane_resource_name
, "Authentication resource: %s");
745 dissect_sane_dummy_response(tvb_sane_reader
*r
, proto_tree
*tree
) {
746 dissect_sane_word(r
, tree
, hf_sane_dummy_value
, NULL
);
750 dissect_sane_net_get_devices_response(tvb_sane_reader
*r
, packet_info
*pinfo
, proto_tree
*tree
) {
751 dissect_sane_status(r
, pinfo
, tree
, NULL
);
754 dissect_sane_word(r
, tree
, hf_sane_array_length
, &array_len
);
755 for (int i
= 0; i
< array_len
- 1; i
++) {
756 int offset
= r
->offset
;
757 proto_item
*device_item
= proto_tree_add_item(tree
, hf_sane_device_descriptor
, r
->tvb
, r
->offset
, -1, ENC_NA
);
758 proto_tree
*device_tree
= proto_item_add_subtree(device_item
, ett_sane_device_descriptor
);
759 proto_item_set_text(device_item
, "Device[%d] descriptor", i
);
761 dissect_sane_word(r
, device_tree
, hf_sane_pointer_value
, NULL
);
762 dissect_sane_string(r
, pinfo
, device_tree
, hf_sane_device_name
, "Device name: %s");
763 dissect_sane_string(r
, pinfo
, device_tree
, hf_sane_device_vendor
, "Device vendor: %s");
764 dissect_sane_string(r
, pinfo
, device_tree
, hf_sane_device_model
, "Device model: %s");
765 dissect_sane_string(r
, pinfo
, device_tree
, hf_sane_device_type
, "Device type: %s");
766 proto_item_set_len(device_item
, r
->offset
- offset
);
769 dissect_sane_word(r
, tree
, hf_sane_pointer_value
, NULL
);
773 dissect_sane_response(tvb_sane_reader
*r
, sane_session
*sess
, packet_info
*pinfo
, proto_tree
*tree
) {
774 sane_rpc_code opcode
= get_sane_expected_response_type(sess
, pinfo
);
776 proto_item_append_text(tree
, ": %s response", val_to_str(opcode
, opcode_vals
, "Unknown opcode (%u)"));
777 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "%s response", val_to_str(opcode
, opcode_vals
, "Unknown opcode (%u)"));
781 dissect_sane_net_init_response(r
, pinfo
, tree
);
784 dissect_sane_net_open_response(r
, pinfo
, tree
);
786 case SANE_NET_GET_OPTION_DESCRIPTORS
:
787 dissect_sane_net_get_option_descriptors_response(r
, pinfo
, tree
);
790 dissect_sane_net_start_response(r
, pinfo
, tree
);
792 case SANE_NET_GET_PARAMETERS
:
793 dissect_sane_net_get_parameters_response(r
, pinfo
, tree
);
795 case SANE_NET_CONTROL_OPTION
:
796 dissect_sane_net_control_option_response(r
, pinfo
, tree
);
798 case SANE_NET_GET_DEVICES
:
799 dissect_sane_net_get_devices_response(r
, pinfo
, tree
);
802 case SANE_NET_CANCEL
:
803 case SANE_NET_AUTHORIZE
:
804 dissect_sane_dummy_response(r
, tree
);
812 dissect_sane_pdu(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
813 tvb_sane_reader r
= {.tvb
= tvb
, .bytes_read
= 0, .offset
= 0};
815 conversation_t
*conv
= find_or_create_conversation(pinfo
);
820 sane_session
*sess
= conversation_get_proto_data(conv
, proto_sane
);
821 DISSECTOR_ASSERT_HINT(sess
, "no session found");
823 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "SANE");
824 col_clear(pinfo
->cinfo
, COL_INFO
);
826 proto_item
*sane_item
= proto_tree_add_item(tree
, proto_sane
, r
.tvb
, 0, -1, ENC_NA
);
827 proto_tree
*sane_tree
= proto_item_add_subtree(sane_item
, ett_sane
);
829 if (value_is_in_range(sane_server_ports
, pinfo
->destport
)) {
830 dissect_sane_request(&r
, pinfo
, sane_tree
);
832 dissect_sane_response(&r
, sess
, pinfo
, sane_tree
);
835 proto_item_set_len(sane_item
, r
.bytes_read
);
840 * Returns the length, in bytes, of the SANE PDU beginning at the given offset
841 * within the buffer. If the PDU appears to be a response from a client and its
842 * type cannot be determined (e.g. because Wireshark never saw the request),
843 * or if the PDU appears to be truncated and its length cannot be determined,
844 * this function returns 0.
847 get_sane_pdu_len(packet_info
*pinfo
, tvbuff_t
*tvb
, int offset
, void *data _U_
) {
848 tvb_sane_reader r
= {.tvb
= tvb
, .offset
= offset
, .bytes_read
= 0};
850 conversation_t
*conv
= find_or_create_conversation(pinfo
);
855 sane_session
*sess
= conversation_get_proto_data(conv
, proto_sane
);
858 sess
= wmem_new0(wmem_file_scope(), sane_session
);
859 conversation_add_proto_data(conv
, proto_sane
, sess
);
862 if (value_is_in_range(sane_server_ports
, pinfo
->destport
)) {
865 WORD_OR_RETURN(&r
, &opcode
);
870 .packet_num
= pinfo
->num
873 if (!PINFO_FD_VISITED(pinfo
)) {
874 sess
->seen_request
= true;
875 if (opcode
== SANE_NET_AUTHORIZE
) {
876 /* Just set this flag, so can remember op being authorised */
880 /* Remember normal request */
881 sess
->last_request
= pdu
;
888 WORD_OR_RETURN(&r
, NULL
);
889 STRING_OR_RETURN(&r
);
891 case SANE_NET_GET_DEVICES
:
895 STRING_OR_RETURN(&r
);
898 case SANE_NET_GET_OPTION_DESCRIPTORS
:
899 case SANE_NET_GET_PARAMETERS
:
901 case SANE_NET_CANCEL
:
902 WORD_OR_RETURN(&r
, NULL
);
904 case SANE_NET_CONTROL_OPTION
:
905 for (int i
= 0; i
< 4; i
++) {
906 WORD_OR_RETURN(&r
, NULL
);
909 WORD_OR_RETURN(&r
, &value_size
);
911 // Pointer to void, contains an extra word for whether the pointer is NULL
912 if (tvb_skip_bytes(&r
, SANE_WORD_LENGTH
+ value_size
) == 0) {
917 case SANE_NET_AUTHORIZE
:
918 STRING_OR_RETURN(&r
);
919 STRING_OR_RETURN(&r
);
920 STRING_OR_RETURN(&r
);
925 sane_rpc_code opcode
= get_sane_expected_response_type(sess
, pinfo
);
930 for (int i
= 0; i
< 2; i
++) {
931 WORD_OR_RETURN(&r
, NULL
);
936 WORD_OR_RETURN(&r
, NULL
);
938 WORD_OR_RETURN(&r
, NULL
);
939 // Authentication resource name
940 STRING_OR_RETURN(&r
);
943 case SANE_NET_GET_OPTION_DESCRIPTORS
:
944 WORD_OR_RETURN(&r
, &array_len
);
946 for (unsigned i
= 0; i
< array_len
; i
++) {
947 WORD_OR_RETURN(&r
, NULL
);
949 // read name, title and description
950 for (int j
= 0; j
< 3; j
++) {
951 STRING_OR_RETURN(&r
);
954 for (int j
= 0; j
< 4; j
++) {
955 WORD_OR_RETURN(&r
, NULL
);
959 unsigned constraint_type
;
960 WORD_OR_RETURN(&r
, &constraint_type
);
962 unsigned string_count
;
963 unsigned value_list_length
;
964 switch (constraint_type
) {
965 case SANE_CONSTRAINT_STRING_LIST
:
966 WORD_OR_RETURN(&r
, &string_count
);
968 for (unsigned j
= 0; j
< string_count
; j
++) {
969 STRING_OR_RETURN(&r
);
972 case SANE_CONSTRAINT_WORD_LIST
:
973 WORD_OR_RETURN(&r
, &value_list_length
);
975 for (unsigned j
= 0; j
< value_list_length
; j
++) {
976 WORD_OR_RETURN(&r
, NULL
);
979 case SANE_CONSTRAINT_RANGE
:
980 // Pointer to range, then min, max, quantization
981 for (unsigned j
= 0; j
< 4; j
++) {
982 WORD_OR_RETURN(&r
, NULL
);
988 case SANE_NET_CONTROL_OPTION
:
989 // Expected record format:
990 // SANE_Status status
992 // SANE_Word value_type
993 // SANE_Word value_size
995 // SANE_String *resource
996 // See http://sane-project.org/html/doc017.html#s5.2.6.
997 for (int i
= 0; i
< 3; i
++) {
998 WORD_OR_RETURN(&r
, NULL
);
1002 WORD_OR_RETURN(&r
, &value_len
);
1004 if (tvb_skip_bytes(&r
, value_len
+ SANE_WORD_LENGTH
) == 0) {
1008 STRING_OR_RETURN(&r
);
1010 case SANE_NET_GET_DEVICES
:
1011 WORD_OR_RETURN(&r
, NULL
);
1013 unsigned device_count
;
1014 WORD_OR_RETURN(&r
, &device_count
);
1015 for (unsigned i
= 0; i
< device_count
- 1; i
++) {
1016 WORD_OR_RETURN(&r
, NULL
);
1017 STRING_OR_RETURN(&r
);
1018 STRING_OR_RETURN(&r
);
1019 STRING_OR_RETURN(&r
);
1020 STRING_OR_RETURN(&r
);
1022 WORD_OR_RETURN(&r
, NULL
);
1024 case SANE_NET_CLOSE
:
1025 WORD_OR_RETURN(&r
, NULL
);
1027 case SANE_NET_START
:
1028 for (int i
= 0; i
< 3; i
++) {
1029 WORD_OR_RETURN(&r
, NULL
);
1031 STRING_OR_RETURN(&r
);
1033 case SANE_NET_GET_PARAMETERS
:
1034 for (int i
= 0; i
< 7; i
++) {
1035 WORD_OR_RETURN(&r
, NULL
);
1038 case SANE_NET_CANCEL
:
1039 case SANE_NET_AUTHORIZE
:
1040 WORD_OR_RETURN(&r
, NULL
);
1047 return r
.bytes_read
;
1051 dissect_sane(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
) {
1052 tcp_dissect_pdus(tvb
, pinfo
, tree
, true, SANE_WORD_LENGTH
, get_sane_pdu_len
, dissect_sane_pdu
, data
);
1053 return (int) tvb_reported_length(tvb
);
1057 apply_sane_prefs(void) {
1058 sane_server_ports
= prefs_get_range_value(SANE_MODULE_NAME
, "tcp.port");
1061 void proto_register_sane(void) {
1062 static hf_register_info hf
[] = {
1085 {&hf_sane_version_major
,
1087 "Version Major Number",
1088 "sane.version.major",
1096 {&hf_sane_version_minor
,
1098 "Version Minor Number",
1099 "sane.version.minor",
1107 {&hf_sane_version_build
,
1109 "Version Build Number",
1110 "sane.version.build",
1151 {&hf_sane_string_length
,
1154 "sane.string.length",
1162 {&hf_sane_array_length
,
1165 "sane.array.length",
1173 {&hf_sane_device_descriptor
,
1175 "Device descriptor",
1176 "sane.device.descriptor",
1184 {&hf_sane_device_name
,
1195 {&hf_sane_device_vendor
,
1198 "sane.device.vendor",
1206 {&hf_sane_device_model
,
1209 "sane.device.model",
1217 {&hf_sane_device_type
,
1228 {&hf_sane_resource_name
,
1231 "sane.resource.name",
1239 {&hf_sane_device_handle
,
1242 "sane.device.handle",
1250 {&hf_sane_option_index
,
1261 {&hf_sane_option_control_action
,
1263 "Option control action",
1264 "sane.option.action",
1267 VALS(control_types
),
1272 {&hf_sane_option_length
,
1274 "Option value length",
1275 "sane.option.length",
1284 {&hf_sane_option_value_type
,
1286 "Option value type",
1290 VALS(sane_value_types
),
1306 {&hf_sane_option_count
,
1309 "sane.option_count",
1317 {&hf_sane_pointer_value
,
1320 "sane.pointer_value",
1328 {&hf_sane_option_name
,
1339 {&hf_sane_option_title
,
1342 "sane.option.title",
1350 {&hf_sane_option_description
,
1352 "Option description",
1353 "sane.option.description",
1361 {&hf_sane_option_descriptor
,
1363 "Option descriptor",
1364 "sane.option.descriptor",
1372 {&hf_sane_option_unit
,
1378 VALS(sane_option_units
),
1383 {&hf_sane_option_size
,
1394 {&hf_sane_option_capabilities
,
1396 "Option capabilities",
1397 "sane.option.capabilities",
1406 {&hf_sane_option_capability_soft_select
,
1408 "Can be changed in software",
1409 "sane.option.soft_select",
1413 SANE_CAP_SOFT_SELECT
,
1417 {&hf_sane_option_capability_hard_select
,
1419 "Requires user intervention to change",
1420 "sane.option.hard_select",
1424 SANE_CAP_HARD_SELECT
,
1428 {&hf_sane_option_capability_soft_detect
,
1430 "Can be detected by software",
1431 "sane.option.soft_detect",
1435 SANE_CAP_SOFT_DETECT
,
1439 {&hf_sane_option_capability_emulated
,
1441 "Emulated in software",
1442 "sane.option.emulated",
1450 {&hf_sane_option_capability_automatic
,
1452 "Can be set automatically",
1453 "sane.option.automatic",
1461 {&hf_sane_option_capability_inactive
,
1464 "sane.option.inactive",
1472 {&hf_sane_option_capability_advanced
,
1475 "sane.option.advanced",
1483 {&hf_sane_option_value
,
1486 "sane.option.value",
1494 {&hf_sane_option_string_value
,
1496 "Option string value",
1497 "sane.option.value.string",
1505 {&hf_sane_option_numeric_value
,
1507 "Option numeric value",
1508 "sane.option.value.numeric",
1516 {&hf_sane_option_boolean_value
,
1518 "Option boolean value",
1519 "sane.option.value.boolean",
1527 {&hf_sane_option_constraints
,
1529 "Option constraints",
1530 "sane.option.constraints",
1538 {&hf_sane_option_constraint_type
,
1540 "Option constraint type",
1541 "sane.option.constraint_type",
1544 VALS(sane_constraint_type_names
),
1549 {&hf_sane_option_possible_string_value
,
1551 "Possible option string value",
1552 "sane.option.possible_string_value",
1560 {&hf_sane_option_possible_word_value
,
1562 "Possible option word value",
1563 "sane.option.possible_word_value",
1571 {&hf_sane_option_range_min
,
1573 "Option minimum value",
1574 "sane.option.min_value",
1582 {&hf_sane_option_range_max
,
1584 "Option maximum value",
1585 "sane.option.max_value",
1593 {&hf_sane_option_range_quant
,
1595 "Option value quantization",
1596 "sane.option.quant",
1604 {&hf_sane_data_port
,
1606 "Image data port number",
1615 {&hf_sane_byte_order
,
1617 "Image data byte order",
1626 {&hf_sane_frame_format
,
1628 "Image data frame format",
1629 "sane.scan.frame_format",
1632 VALS(sane_frame_format_names
),
1637 {&hf_sane_scan_line_count
,
1639 "Image data line count",
1640 "sane.scan.line_count",
1648 {&hf_sane_scan_pixel_depth
,
1650 "Image data pixel depth",
1651 "sane.scan.pixel_depth",
1659 {&hf_sane_scan_pixels_per_line
,
1661 "Image data pixels per line",
1662 "sane.scan.pixels_per_line",
1670 {&hf_sane_scan_bytes_per_line
,
1672 "Image data bytes per line",
1673 "sane.scan.bytes_per_line",
1681 {&hf_sane_scan_is_last_frame
,
1683 "Is last image data frame",
1684 "sane.scan.last_frame",
1692 {&hf_sane_dummy_value
,
1703 {&hf_sane_control_option_info
,
1705 "Control option info",
1706 "sane.control_option.info",
1714 {&hf_sane_control_option_inexact
,
1716 "Inexact value selected",
1717 "sane.control_option.info.inexact",
1725 {&hf_sane_control_option_reload_options
,
1727 "Client should reload options",
1728 "sane.control_option.info.reload_options",
1732 SANE_INFO_RELOAD_OPTIONS
,
1736 {&hf_sane_control_option_reload_params
,
1738 "Client should reload scan parameters",
1739 "sane.control_option.info.reload_params",
1743 SANE_INFO_RELOAD_PARAMS
,
1750 static int *ett
[] = {
1755 &ett_sane_option_value
,
1756 &ett_sane_option_capabilities
,
1757 &ett_sane_option_constraints
,
1758 &ett_sane_control_option_info
,
1759 &ett_sane_device_descriptor
,
1762 module_t
*sane_module
;
1764 proto_sane
= proto_register_protocol("Scanner Access Now Easy", "SANE", "sane");
1765 proto_register_field_array(proto_sane
, hf
, array_length(hf
));
1766 proto_register_subtree_array(ett
, array_length(ett
));
1768 register_dissector(SANE_MODULE_NAME
, dissect_sane
, proto_sane
);
1771 * XXX - Required to be notified of server port changes,
1772 * while no other preferences are registered.
1774 sane_module
= prefs_register_protocol(proto_sane
, apply_sane_prefs
);
1779 proto_reg_handoff_sane(void) {
1780 sane_handle
= create_dissector_handle(dissect_sane
, proto_sane
);
1781 dissector_add_uint_range_with_preference("tcp.port", SANE_PORT
, sane_handle
);
1786 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1791 * indent-tabs-mode: nil
1794 * vi: set shiftwidth=4 tabstop=8 expandtab:
1795 * :indentSize=4:tabSize=8:noTabs=true: