Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-sane.c
blob27ff13b90e6f75d5488b04adf9ecc7498e35c87f
1 /* packet-sane.c
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
12 /**
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
30 * dissect_sane_pdu.
33 /* TODO
34 * - Setup proper request / response tracking, with
35 * - references to related packets,
36 * - response time indications.
39 #include "config.h"
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,
142 NULL,
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,
149 NULL,
152 static int proto_sane;
153 static int ett_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;
163 typedef enum {
164 SANE_NET_UNKNOWN = -1,
165 SANE_NET_INIT = 0,
166 SANE_NET_GET_DEVICES = 1,
167 SANE_NET_OPEN = 2,
168 SANE_NET_CLOSE = 3,
169 SANE_NET_GET_OPTION_DESCRIPTORS = 4,
170 SANE_NET_CONTROL_OPTION = 5,
171 SANE_NET_GET_PARAMETERS = 6,
172 SANE_NET_START = 7,
173 SANE_NET_CANCEL = 8,
174 SANE_NET_AUTHORIZE = 9,
175 SANE_NET_EXIT = 10,
176 } sane_rpc_code;
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"},
190 {0, NULL},
193 typedef enum {
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"},
205 {0, NULL},
208 typedef enum {
209 SANE_TYPE_BOOL = 0,
210 SANE_TYPE_INT = 1,
211 SANE_TYPE_FIXED = 2,
212 SANE_TYPE_STRING = 3,
213 SANE_TYPE_BUTTON = 4,
214 SANE_TYPE_GROUP = 5,
215 } sane_value_type;
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"},
224 {0, NULL},
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"},
231 {0, NULL},
234 typedef enum {
235 SANE_UNIT_NONE = 0,
236 SANE_UNIT_PIXEL = 1,
237 SANE_UNIT_BIT = 2,
238 SANE_UNIT_MM = 3,
239 SANE_UNIT_DPI = 4,
240 SANE_UNIT_PERCENT = 5,
241 SANE_UNIT_MICROSECOND = 6,
242 } sane_option_unit;
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"},
252 {0, NULL},
255 static const value_string sane_option_unit_suffixes[] = {
256 {1, "px"},
257 {2, "bits"},
258 {3, "mm"},
259 {4, "dpi"},
260 {5, "%"},
261 {6, "ms"},
262 {0, NULL},
265 typedef enum {
266 SANE_STATUS_UNKNOWN = -1,
267 SANE_STATUS_OK = 0,
268 } sane_status;
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"},
283 {0, NULL},
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"},
292 {0, NULL},
295 typedef struct {
296 bool is_request;
297 sane_rpc_code opcode;
298 uint32_t packet_num;
299 } sane_pdu;
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.
306 typedef struct {
307 bool seen_request;
308 sane_pdu last_request;
309 bool auth;
310 } sane_session;
313 typedef struct {
314 tvbuff_t *tvb;
315 int offset;
316 int bytes_read;
317 } tvb_sane_reader;
320 static int
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) {
323 return 0;
326 if (dest) {
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)
338 static int
339 tvb_read_sane_string(tvb_sane_reader *r, wmem_allocator_t *alloc, char **dest) {
340 int str_len;
341 WORD_OR_RETURN(r, &str_len);
343 if (tvb_captured_length_remaining(r->tvb, r->offset) < str_len) {
344 return 0;
347 if (dest) {
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)
359 static int
360 tvb_skip_bytes(tvb_sane_reader *r, int len) {
361 if (tvb_captured_length_remaining(r->tvb, r->offset) < len) {
362 return 0;
365 r->offset += len;
366 r->bytes_read += len;
367 return 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.
384 static sane_rpc_code
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) {
396 if (sess->auth) {
397 code = SANE_NET_AUTHORIZE;
398 sess->auth = false;
400 else {
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));
408 return code;
411 static proto_item *
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,
414 ENC_BIG_ENDIAN);
415 // safe to ignore the return value here, we're guaranteed to have enough bytes to
416 // read a word.
417 (void)tvb_read_sane_word(r, word);
418 return item;
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.
428 static char *
429 dissect_sane_string(tvb_sane_reader *r, packet_info *pinfo, proto_tree *tree, int hfindex, const char *format) {
430 int offset = r->offset;
431 char *str = "";
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);
440 return str;
443 static void
444 dissect_sane_net_init_request(tvb_sane_reader *r, packet_info *pinfo, proto_tree *tree) {
445 int version = 0;
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");
460 static void
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");
465 static void
466 dissect_control_option_value(tvb_sane_reader *r, packet_info *pinfo, proto_tree *tree) {
467 int value_type = 0;
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'");
478 } else {
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) {
484 int value = 0;
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) {
488 int value = 0;
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);
498 static void
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);
506 static void
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. */
514 static void
515 dissect_sane_device_handle_request(tvb_sane_reader *r, proto_tree *tree) {
516 dissect_sane_word(r, tree, hf_sane_device_handle, NULL);
519 static int
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)"));
526 switch (opcode) {
527 case SANE_NET_INIT:
528 dissect_sane_net_init_request(r, pinfo, tree);
529 break;
530 case SANE_NET_GET_DEVICES:
531 // no additional payload here
532 break;
533 case SANE_NET_OPEN:
534 dissect_sane_net_open_request(r, pinfo, tree);
535 break;
536 case SANE_NET_CONTROL_OPTION:
537 dissect_sane_net_control_option_request(r, pinfo, tree);
538 break;
539 case SANE_NET_CLOSE:
540 case SANE_NET_START:
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);
545 break;
546 case SANE_NET_AUTHORIZE:
547 dissect_sane_net_authorize_request(r, pinfo, tree);
548 break;
551 return r->bytes_read;
554 static proto_item *
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
560 // read a word.
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)"));
569 if (status_ptr) {
570 *status_ptr = status;
573 return status_item;
576 static void
577 dissect_sane_net_init_response(tvb_sane_reader *r, packet_info *pinfo, proto_tree *tree) {
578 unsigned status;
579 dissect_sane_status(r, pinfo, tree, &status);
581 int version = 0;
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);
593 static void
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'");
601 static void
602 append_option_value(proto_item *item, int value, unsigned units, unsigned type) {
603 switch (type) {
604 case SANE_TYPE_INT:
605 if (units) {
606 proto_item_append_text(item, " (%d %s)", value,
607 val_to_str_const(units, sane_option_unit_suffixes, "(unknown unit)"));
608 } else {
609 proto_item_append_text(item, " (%d)", value);
611 break;
612 case SANE_TYPE_FIXED: {
613 double fixed_val = ((double) value) / (1 << 16);
614 if (units) {
615 proto_item_append_text(item, " (%f %s)", fixed_val,
616 val_to_str_const(units, sane_option_unit_suffixes, "(unknown unit)"));
617 } else {
618 proto_item_append_text(item, " (%f)", fixed_val);
620 break;
622 case SANE_TYPE_BOOL:
623 proto_item_append_text(item, " (%s)", (value == 1) ? "True" : ((value == 0) ? "False" : "Invalid"));
624 break;
625 default:
626 break;
630 static void
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++) {
636 int unit = 0;
637 int type = 0;
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;
676 int min = 0;
677 int max = 0;
678 int quant = 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");
686 break;
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++) {
691 int value = 0;
692 proto_item *value_item = dissect_sane_word(r, constraint_tree, hf_sane_option_possible_word_value,
693 &value);
694 append_option_value(value_item, value, unit, type);
696 break;
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);
706 break;
709 proto_item_set_len(constraint_item, r->offset - constraint_start);
710 proto_item_set_len(option_item, r->offset - start_offset);
714 static void
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");
722 static void
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);
733 static void
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");
744 static void
745 dissect_sane_dummy_response(tvb_sane_reader *r, proto_tree *tree) {
746 dissect_sane_word(r, tree, hf_sane_dummy_value, NULL);
749 static void
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);
753 int array_len = 0;
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);
772 static void
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)"));
779 switch (opcode) {
780 case SANE_NET_INIT:
781 dissect_sane_net_init_response(r, pinfo, tree);
782 break;
783 case SANE_NET_OPEN:
784 dissect_sane_net_open_response(r, pinfo, tree);
785 break;
786 case SANE_NET_GET_OPTION_DESCRIPTORS:
787 dissect_sane_net_get_option_descriptors_response(r, pinfo, tree);
788 break;
789 case SANE_NET_START:
790 dissect_sane_net_start_response(r, pinfo, tree);
791 break;
792 case SANE_NET_GET_PARAMETERS:
793 dissect_sane_net_get_parameters_response(r, pinfo, tree);
794 break;
795 case SANE_NET_CONTROL_OPTION:
796 dissect_sane_net_control_option_response(r, pinfo, tree);
797 break;
798 case SANE_NET_GET_DEVICES:
799 dissect_sane_net_get_devices_response(r, pinfo, tree);
800 break;
801 case SANE_NET_CLOSE:
802 case SANE_NET_CANCEL:
803 case SANE_NET_AUTHORIZE:
804 dissect_sane_dummy_response(r, tree);
805 break;
806 default:
807 break;
811 static int
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);
816 if (!conv) {
817 return 0;
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);
831 } else {
832 dissect_sane_response(&r, sess, pinfo, sane_tree);
835 proto_item_set_len(sane_item, r.bytes_read);
836 return 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.
846 static unsigned
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);
851 if (!conv) {
852 return 0;
855 sane_session *sess = conversation_get_proto_data(conv, proto_sane);
857 if (!sess) {
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)) {
863 /* REQUEST */
864 unsigned opcode;
865 WORD_OR_RETURN(&r, &opcode);
867 sane_pdu pdu = {
868 .is_request = true,
869 .opcode = 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 */
877 sess->auth = true;
879 else {
880 /* Remember normal request */
881 sess->last_request = pdu;
882 sess->auth = false;
886 switch (opcode) {
887 case SANE_NET_INIT:
888 WORD_OR_RETURN(&r, NULL);
889 STRING_OR_RETURN(&r);
890 break;
891 case SANE_NET_GET_DEVICES:
892 case SANE_NET_EXIT:
893 break;
894 case SANE_NET_OPEN:
895 STRING_OR_RETURN(&r);
896 break;
897 case SANE_NET_CLOSE:
898 case SANE_NET_GET_OPTION_DESCRIPTORS:
899 case SANE_NET_GET_PARAMETERS:
900 case SANE_NET_START:
901 case SANE_NET_CANCEL:
902 WORD_OR_RETURN(&r, NULL);
903 break;
904 case SANE_NET_CONTROL_OPTION:
905 for (int i = 0; i < 4; i++) {
906 WORD_OR_RETURN(&r, NULL);
908 unsigned value_size;
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) {
913 return 0;
916 break;
917 case SANE_NET_AUTHORIZE:
918 STRING_OR_RETURN(&r);
919 STRING_OR_RETURN(&r);
920 STRING_OR_RETURN(&r);
921 break;
923 } else {
924 /* RESPONSE */
925 sane_rpc_code opcode = get_sane_expected_response_type(sess, pinfo);
926 unsigned array_len;
928 switch (opcode) {
929 case SANE_NET_INIT:
930 for (int i = 0; i < 2; i++) {
931 WORD_OR_RETURN(&r, NULL);
933 break;
934 case SANE_NET_OPEN:
935 // Status word
936 WORD_OR_RETURN(&r, NULL);
937 // Device handle
938 WORD_OR_RETURN(&r, NULL);
939 // Authentication resource name
940 STRING_OR_RETURN(&r);
941 break;
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);
958 // constraint type
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);
971 break;
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);
978 break;
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);
984 break;
987 break;
988 case SANE_NET_CONTROL_OPTION:
989 // Expected record format:
990 // SANE_Status status
991 // SANE_Word info
992 // SANE_Word value_type
993 // SANE_Word value_size
994 // void *value
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);
1001 unsigned value_len;
1002 WORD_OR_RETURN(&r, &value_len);
1004 if (tvb_skip_bytes(&r, value_len + SANE_WORD_LENGTH) == 0) {
1005 return 0;
1008 STRING_OR_RETURN(&r);
1009 break;
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);
1023 break;
1024 case SANE_NET_CLOSE:
1025 WORD_OR_RETURN(&r, NULL);
1026 break;
1027 case SANE_NET_START:
1028 for (int i = 0; i < 3; i++) {
1029 WORD_OR_RETURN(&r, NULL);
1031 STRING_OR_RETURN(&r);
1032 break;
1033 case SANE_NET_GET_PARAMETERS:
1034 for (int i = 0; i < 7; i++) {
1035 WORD_OR_RETURN(&r, NULL);
1037 break;
1038 case SANE_NET_CANCEL:
1039 case SANE_NET_AUTHORIZE:
1040 WORD_OR_RETURN(&r, NULL);
1041 break;
1042 default:
1043 break;
1047 return r.bytes_read;
1050 static int
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);
1056 static void
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[] = {
1063 {&hf_sane_opcode,
1065 "Opcode",
1066 "sane.opcode",
1067 FT_UINT32,
1068 BASE_DEC,
1069 VALS(opcode_vals),
1071 "RPC request type",
1072 HFILL,
1074 {&hf_sane_version,
1076 "Version",
1077 "sane.version",
1078 FT_UINT32,
1079 BASE_HEX,
1080 NULL,
1082 "Protocol version",
1083 HFILL,
1085 {&hf_sane_version_major,
1087 "Version Major Number",
1088 "sane.version.major",
1089 FT_UINT8,
1090 BASE_HEX,
1091 NULL,
1093 NULL,
1094 HFILL,
1096 {&hf_sane_version_minor,
1098 "Version Minor Number",
1099 "sane.version.minor",
1100 FT_UINT8,
1101 BASE_HEX,
1102 NULL,
1104 NULL,
1105 HFILL,
1107 {&hf_sane_version_build,
1109 "Version Build Number",
1110 "sane.version.build",
1111 FT_UINT16,
1112 BASE_HEX,
1113 NULL,
1115 NULL,
1116 HFILL,
1118 {&hf_sane_username,
1120 "Username",
1121 "sane.username",
1122 FT_STRING,
1123 BASE_NONE,
1124 NULL,
1126 NULL,
1127 HFILL,
1129 {&hf_sane_password,
1131 "Password",
1132 "sane.password",
1133 FT_STRING,
1134 BASE_NONE,
1135 NULL,
1137 NULL,
1138 HFILL,
1140 {&hf_sane_string,
1142 "String",
1143 "sane.string",
1144 FT_NONE,
1145 BASE_NONE,
1146 NULL,
1148 NULL,
1149 HFILL,
1151 {&hf_sane_string_length,
1153 "String length",
1154 "sane.string.length",
1155 FT_UINT32,
1156 BASE_DEC,
1157 NULL,
1159 NULL,
1160 HFILL,
1162 {&hf_sane_array_length,
1164 "Array length",
1165 "sane.array.length",
1166 FT_UINT32,
1167 BASE_DEC,
1168 NULL,
1170 NULL,
1171 HFILL,
1173 {&hf_sane_device_descriptor,
1175 "Device descriptor",
1176 "sane.device.descriptor",
1177 FT_NONE,
1178 BASE_NONE,
1179 NULL,
1181 NULL,
1182 HFILL,
1184 {&hf_sane_device_name,
1186 "Device name",
1187 "sane.device.name",
1188 FT_STRING,
1189 BASE_NONE,
1190 NULL,
1192 NULL,
1193 HFILL,
1195 {&hf_sane_device_vendor,
1197 "Device vendor",
1198 "sane.device.vendor",
1199 FT_STRING,
1200 BASE_NONE,
1201 NULL,
1203 NULL,
1204 HFILL,
1206 {&hf_sane_device_model,
1208 "Device model",
1209 "sane.device.model",
1210 FT_STRING,
1211 BASE_NONE,
1212 NULL,
1214 NULL,
1215 HFILL,
1217 {&hf_sane_device_type,
1219 "Device type",
1220 "sane.device.type",
1221 FT_STRING,
1222 BASE_NONE,
1223 NULL,
1225 NULL,
1226 HFILL,
1228 {&hf_sane_resource_name,
1230 "Resource name",
1231 "sane.resource.name",
1232 FT_STRING,
1233 BASE_NONE,
1234 NULL,
1236 NULL,
1237 HFILL,
1239 {&hf_sane_device_handle,
1241 "Device handle",
1242 "sane.device.handle",
1243 FT_UINT32,
1244 BASE_DEC,
1245 NULL,
1247 NULL,
1248 HFILL,
1250 {&hf_sane_option_index,
1252 "Option index",
1253 "sane.option",
1254 FT_UINT32,
1255 BASE_DEC,
1256 NULL,
1258 NULL,
1259 HFILL,
1261 {&hf_sane_option_control_action,
1263 "Option control action",
1264 "sane.option.action",
1265 FT_UINT32,
1266 BASE_DEC,
1267 VALS(control_types),
1269 NULL,
1270 HFILL,
1272 {&hf_sane_option_length,
1274 "Option value length",
1275 "sane.option.length",
1276 FT_UINT32,
1277 BASE_DEC,
1278 NULL,
1280 NULL,
1281 HFILL,
1284 {&hf_sane_option_value_type,
1286 "Option value type",
1287 "sane.option.type",
1288 FT_UINT32,
1289 BASE_DEC,
1290 VALS(sane_value_types),
1292 NULL,
1293 HFILL,
1295 {&hf_sane_status,
1297 "Status",
1298 "sane.status",
1299 FT_UINT32,
1300 BASE_DEC,
1301 NULL,
1303 NULL,
1304 HFILL,
1306 {&hf_sane_option_count,
1308 "Option count",
1309 "sane.option_count",
1310 FT_UINT32,
1311 BASE_DEC,
1312 NULL,
1314 NULL,
1315 HFILL,
1317 {&hf_sane_pointer_value,
1319 "Pointer value",
1320 "sane.pointer_value",
1321 FT_UINT32,
1322 BASE_HEX,
1323 NULL,
1325 NULL,
1326 HFILL,
1328 {&hf_sane_option_name,
1330 "Option name",
1331 "sane.option.name",
1332 FT_STRING,
1333 BASE_NONE,
1334 NULL,
1336 NULL,
1337 HFILL,
1339 {&hf_sane_option_title,
1341 "Option title",
1342 "sane.option.title",
1343 FT_STRING,
1344 BASE_NONE,
1345 NULL,
1347 NULL,
1348 HFILL,
1350 {&hf_sane_option_description,
1352 "Option description",
1353 "sane.option.description",
1354 FT_STRING,
1355 BASE_NONE,
1356 NULL,
1358 NULL,
1359 HFILL,
1361 {&hf_sane_option_descriptor,
1363 "Option descriptor",
1364 "sane.option.descriptor",
1365 FT_BYTES,
1366 BASE_NONE,
1367 NULL,
1369 NULL,
1370 HFILL,
1372 {&hf_sane_option_unit,
1374 "Option unit",
1375 "sane.option.unit",
1376 FT_UINT32,
1377 BASE_DEC,
1378 VALS(sane_option_units),
1380 NULL,
1381 HFILL,
1383 {&hf_sane_option_size,
1385 "Option size",
1386 "sane.option.size",
1387 FT_UINT32,
1388 BASE_DEC,
1389 NULL,
1391 NULL,
1392 HFILL,
1394 {&hf_sane_option_capabilities,
1396 "Option capabilities",
1397 "sane.option.capabilities",
1398 FT_UINT32,
1399 BASE_HEX,
1400 NULL,
1402 NULL,
1403 HFILL,
1406 {&hf_sane_option_capability_soft_select,
1408 "Can be changed in software",
1409 "sane.option.soft_select",
1410 FT_BOOLEAN,
1412 NULL,
1413 SANE_CAP_SOFT_SELECT,
1414 NULL,
1415 HFILL,
1417 {&hf_sane_option_capability_hard_select,
1419 "Requires user intervention to change",
1420 "sane.option.hard_select",
1421 FT_BOOLEAN,
1423 NULL,
1424 SANE_CAP_HARD_SELECT,
1425 NULL,
1426 HFILL,
1428 {&hf_sane_option_capability_soft_detect,
1430 "Can be detected by software",
1431 "sane.option.soft_detect",
1432 FT_BOOLEAN,
1434 NULL,
1435 SANE_CAP_SOFT_DETECT,
1436 NULL,
1437 HFILL,
1439 {&hf_sane_option_capability_emulated,
1441 "Emulated in software",
1442 "sane.option.emulated",
1443 FT_BOOLEAN,
1445 NULL,
1446 SANE_CAP_EMULATED,
1447 NULL,
1448 HFILL,
1450 {&hf_sane_option_capability_automatic,
1452 "Can be set automatically",
1453 "sane.option.automatic",
1454 FT_BOOLEAN,
1456 NULL,
1457 SANE_CAP_AUTOMATIC,
1458 NULL,
1459 HFILL,
1461 {&hf_sane_option_capability_inactive,
1463 "Inactive",
1464 "sane.option.inactive",
1465 FT_BOOLEAN,
1467 NULL,
1468 SANE_CAP_INACTIVE,
1469 NULL,
1470 HFILL,
1472 {&hf_sane_option_capability_advanced,
1474 "Advanced option",
1475 "sane.option.advanced",
1476 FT_BOOLEAN,
1478 NULL,
1479 SANE_CAP_ADVANCED,
1480 NULL,
1481 HFILL,
1483 {&hf_sane_option_value,
1485 "Option value",
1486 "sane.option.value",
1487 FT_NONE,
1488 BASE_NONE,
1489 NULL,
1491 NULL,
1492 HFILL,
1494 {&hf_sane_option_string_value,
1496 "Option string value",
1497 "sane.option.value.string",
1498 FT_STRING,
1499 BASE_NONE,
1500 NULL,
1502 NULL,
1503 HFILL,
1505 {&hf_sane_option_numeric_value,
1507 "Option numeric value",
1508 "sane.option.value.numeric",
1509 FT_UINT32,
1510 BASE_HEX,
1511 NULL,
1513 NULL,
1514 HFILL,
1516 {&hf_sane_option_boolean_value,
1518 "Option boolean value",
1519 "sane.option.value.boolean",
1520 FT_BOOLEAN,
1521 BASE_NONE,
1522 NULL,
1524 NULL,
1525 HFILL,
1527 {&hf_sane_option_constraints,
1529 "Option constraints",
1530 "sane.option.constraints",
1531 FT_BYTES,
1532 BASE_NONE,
1533 NULL,
1535 NULL,
1536 HFILL,
1538 {&hf_sane_option_constraint_type,
1540 "Option constraint type",
1541 "sane.option.constraint_type",
1542 FT_UINT32,
1543 BASE_DEC,
1544 VALS(sane_constraint_type_names),
1546 NULL,
1547 HFILL,
1549 {&hf_sane_option_possible_string_value,
1551 "Possible option string value",
1552 "sane.option.possible_string_value",
1553 FT_STRING,
1554 BASE_NONE,
1555 NULL,
1557 NULL,
1558 HFILL,
1560 {&hf_sane_option_possible_word_value,
1562 "Possible option word value",
1563 "sane.option.possible_word_value",
1564 FT_UINT32,
1565 BASE_HEX,
1566 NULL,
1568 NULL,
1569 HFILL,
1571 {&hf_sane_option_range_min,
1573 "Option minimum value",
1574 "sane.option.min_value",
1575 FT_UINT32,
1576 BASE_HEX,
1577 NULL,
1579 NULL,
1580 HFILL,
1582 {&hf_sane_option_range_max,
1584 "Option maximum value",
1585 "sane.option.max_value",
1586 FT_UINT32,
1587 BASE_HEX,
1588 NULL,
1590 NULL,
1591 HFILL,
1593 {&hf_sane_option_range_quant,
1595 "Option value quantization",
1596 "sane.option.quant",
1597 FT_UINT32,
1598 BASE_HEX,
1599 NULL,
1601 NULL,
1602 HFILL,
1604 {&hf_sane_data_port,
1606 "Image data port number",
1607 "sane.data_port",
1608 FT_UINT32,
1609 BASE_DEC,
1610 NULL,
1612 NULL,
1613 HFILL,
1615 {&hf_sane_byte_order,
1617 "Image data byte order",
1618 "sane.byte_order",
1619 FT_UINT32,
1620 BASE_HEX,
1621 NULL,
1623 NULL,
1624 HFILL,
1626 {&hf_sane_frame_format,
1628 "Image data frame format",
1629 "sane.scan.frame_format",
1630 FT_UINT32,
1631 BASE_DEC,
1632 VALS(sane_frame_format_names),
1634 NULL,
1635 HFILL,
1637 {&hf_sane_scan_line_count,
1639 "Image data line count",
1640 "sane.scan.line_count",
1641 FT_UINT32,
1642 BASE_DEC,
1643 NULL,
1645 NULL,
1646 HFILL,
1648 {&hf_sane_scan_pixel_depth,
1650 "Image data pixel depth",
1651 "sane.scan.pixel_depth",
1652 FT_UINT32,
1653 BASE_DEC,
1654 NULL,
1656 NULL,
1657 HFILL,
1659 {&hf_sane_scan_pixels_per_line,
1661 "Image data pixels per line",
1662 "sane.scan.pixels_per_line",
1663 FT_UINT32,
1664 BASE_DEC,
1665 NULL,
1667 NULL,
1668 HFILL,
1670 {&hf_sane_scan_bytes_per_line,
1672 "Image data bytes per line",
1673 "sane.scan.bytes_per_line",
1674 FT_UINT32,
1675 BASE_DEC,
1676 NULL,
1678 NULL,
1679 HFILL,
1681 {&hf_sane_scan_is_last_frame,
1683 "Is last image data frame",
1684 "sane.scan.last_frame",
1685 FT_BOOLEAN,
1686 BASE_NONE,
1687 NULL,
1689 NULL,
1690 HFILL,
1692 {&hf_sane_dummy_value,
1694 "Dummy value",
1695 "sane.dummy_value",
1696 FT_UINT32,
1697 BASE_DEC,
1698 NULL,
1700 NULL,
1701 HFILL,
1703 {&hf_sane_control_option_info,
1705 "Control option info",
1706 "sane.control_option.info",
1707 FT_UINT32,
1708 BASE_HEX,
1709 NULL,
1711 NULL,
1712 HFILL,
1714 {&hf_sane_control_option_inexact,
1716 "Inexact value selected",
1717 "sane.control_option.info.inexact",
1718 FT_BOOLEAN,
1720 NULL,
1721 SANE_INFO_INEXACT,
1722 NULL,
1723 HFILL,
1725 {&hf_sane_control_option_reload_options,
1727 "Client should reload options",
1728 "sane.control_option.info.reload_options",
1729 FT_BOOLEAN,
1731 NULL,
1732 SANE_INFO_RELOAD_OPTIONS,
1733 NULL,
1734 HFILL,
1736 {&hf_sane_control_option_reload_params,
1738 "Client should reload scan parameters",
1739 "sane.control_option.info.reload_params",
1740 FT_BOOLEAN,
1742 NULL,
1743 SANE_INFO_RELOAD_PARAMS,
1744 NULL,
1745 HFILL,
1750 static int *ett[] = {
1751 &ett_sane,
1752 &ett_sane_version,
1753 &ett_sane_string,
1754 &ett_sane_option,
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);
1775 (void)sane_module;
1778 void
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);
1782 apply_sane_prefs();
1786 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1788 * Local variables:
1789 * c-basic-offset: 4
1790 * tab-width: 8
1791 * indent-tabs-mode: nil
1792 * End:
1794 * vi: set shiftwidth=4 tabstop=8 expandtab:
1795 * :indentSize=4:tabSize=8:noTabs=true: