/src/wireshark/epan/dissectors/packet-sane.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* packet-sane.c |
2 | | * Routines for SANE dissection |
3 | | * Copyright 2024, James Ring <sjr@jdns.org> |
4 | | * |
5 | | * Wireshark - Network traffic analyzer |
6 | | * By Gerald Combs <gerald@wireshark.org> |
7 | | * Copyright 1998 Gerald Combs |
8 | | * |
9 | | * SPDX-License-Identifier: GPL-2.0-or-later |
10 | | */ |
11 | | |
12 | | /** |
13 | | * A dissector for the SANE protocol (https://sane-project.gitlab.io/standard/net.html). |
14 | | * |
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. |
19 | | * |
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. |
26 | | * |
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. |
31 | | */ |
32 | | |
33 | | /* TODO |
34 | | * - Setup proper request / response tracking, with |
35 | | * - references to related packets, |
36 | | * - response time indications. |
37 | | */ |
38 | | |
39 | | #include "config.h" |
40 | | |
41 | | #include <wireshark.h> |
42 | | #include <epan/packet.h> |
43 | | #include <epan/proto_data.h> |
44 | | #include <epan/prefs.h> |
45 | | |
46 | | #include "packet-tcp.h" |
47 | | |
48 | 14.6k | #define SANE_WORD_LENGTH 4 |
49 | 28 | #define SANE_MODULE_NAME "sane" |
50 | 14 | #define SANE_PORT "6566" |
51 | | |
52 | | static range_t *sane_server_ports; |
53 | | |
54 | | static dissector_handle_t sane_handle; |
55 | | |
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; |
107 | | |
108 | | #define SANE_CAP_NONE 0x00000000 |
109 | 14 | #define SANE_CAP_SOFT_SELECT 0x00000001 |
110 | 14 | #define SANE_CAP_HARD_SELECT 0x00000002 |
111 | 14 | #define SANE_CAP_SOFT_DETECT 0x00000004 |
112 | 14 | #define SANE_CAP_EMULATED 0x00000008 |
113 | 14 | #define SANE_CAP_AUTOMATIC 0x00000010 |
114 | 14 | #define SANE_CAP_INACTIVE 0x00000020 |
115 | 14 | #define SANE_CAP_ADVANCED 0x00000040 |
116 | | |
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; |
124 | | |
125 | 14 | #define SANE_INFO_INEXACT 0x00000001 |
126 | 14 | #define SANE_INFO_RELOAD_OPTIONS 0x00000002 |
127 | 14 | #define SANE_INFO_RELOAD_PARAMS 0x00000004 |
128 | | |
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; |
133 | | |
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, |
143 | | }; |
144 | | |
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, |
150 | | }; |
151 | | |
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; |
162 | | |
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; |
177 | | |
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}, |
191 | | }; |
192 | | |
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; |
199 | | |
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}, |
206 | | }; |
207 | | |
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; |
216 | | |
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}, |
225 | | }; |
226 | | |
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}, |
232 | | }; |
233 | | |
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; |
243 | | |
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}, |
253 | | }; |
254 | | |
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}, |
263 | | }; |
264 | | |
265 | | typedef enum { |
266 | | SANE_STATUS_UNKNOWN = -1, |
267 | | SANE_STATUS_OK = 0, |
268 | | } sane_status; |
269 | | |
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}, |
284 | | }; |
285 | | |
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}, |
293 | | }; |
294 | | |
295 | | typedef struct { |
296 | | bool is_request; |
297 | | sane_rpc_code opcode; |
298 | | uint32_t packet_num; |
299 | | } sane_pdu; |
300 | | |
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. |
305 | | */ |
306 | | typedef struct { |
307 | | bool seen_request; |
308 | | sane_pdu last_request; |
309 | | bool auth; |
310 | | } sane_session; |
311 | | |
312 | | |
313 | | typedef struct { |
314 | | tvbuff_t *tvb; |
315 | | int offset; |
316 | | int bytes_read; |
317 | | } tvb_sane_reader; |
318 | | |
319 | | |
320 | | static int |
321 | 3.19k | tvb_read_sane_word(tvb_sane_reader *r, uint32_t *dest) { |
322 | 3.19k | if (tvb_captured_length_remaining(r->tvb, r->offset) < SANE_WORD_LENGTH) { |
323 | 39 | return 0; |
324 | 39 | } |
325 | | |
326 | 3.16k | if (dest) { |
327 | 2.95k | *dest = tvb_get_ntohl(r->tvb, r->offset); |
328 | 2.95k | } |
329 | 3.16k | r->offset += SANE_WORD_LENGTH; |
330 | 3.16k | r->bytes_read += SANE_WORD_LENGTH; |
331 | 3.16k | return SANE_WORD_LENGTH; |
332 | 3.19k | } |
333 | | |
334 | | #define WORD_OR_RETURN(r, var) \ |
335 | 1.73k | do { if (tvb_read_sane_word((r), (var)) == 0) { return 0; } } while(0) |
336 | | |
337 | | |
338 | | static int |
339 | 196 | tvb_read_sane_string(tvb_sane_reader *r, wmem_allocator_t *alloc, char **dest) { |
340 | 196 | int str_len; |
341 | 196 | WORD_OR_RETURN(r, &str_len); |
342 | | |
343 | 171 | if (tvb_captured_length_remaining(r->tvb, r->offset) < str_len) { |
344 | 14 | return 0; |
345 | 14 | } |
346 | | |
347 | 157 | if (dest) { |
348 | 61 | *dest = tvb_get_string_enc(alloc, r->tvb, r->offset, str_len, ENC_ASCII | ENC_NA); |
349 | 61 | } |
350 | | |
351 | 157 | r->offset += str_len; |
352 | 157 | r->bytes_read += str_len; |
353 | 157 | return SANE_WORD_LENGTH + str_len; |
354 | 171 | } |
355 | | |
356 | | #define STRING_OR_RETURN(r) \ |
357 | 111 | do { if (tvb_read_sane_string((r), NULL, NULL) == 0) { return 0; } } while(0) |
358 | | |
359 | | static int |
360 | 9 | tvb_skip_bytes(tvb_sane_reader *r, int len) { |
361 | 9 | if (tvb_captured_length_remaining(r->tvb, r->offset) < len) { |
362 | 1 | return 0; |
363 | 1 | } |
364 | | |
365 | 8 | r->offset += len; |
366 | 8 | r->bytes_read += len; |
367 | 8 | return len; |
368 | 9 | } |
369 | | |
370 | | /** |
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: |
374 | | * |
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) |
380 | | * |
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. |
383 | | */ |
384 | | static sane_rpc_code |
385 | 0 | get_sane_expected_response_type(sane_session *sess, packet_info *pinfo) { |
386 | | |
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 | 0 | if (PINFO_FD_VISITED(pinfo) || p_get_proto_data(wmem_file_scope(), pinfo, proto_sane, 0)) { |
390 | 0 | return (sane_rpc_code)GPOINTER_TO_UINT(p_get_proto_data(wmem_file_scope(), pinfo, proto_sane, 0)); |
391 | 0 | } |
392 | | |
393 | | /* First pass. Will be response to last_request if set, or AUTH request if flag set. */ |
394 | 0 | sane_rpc_code code = SANE_NET_UNKNOWN; |
395 | 0 | if (sess->seen_request) { |
396 | 0 | if (sess->auth) { |
397 | 0 | code = SANE_NET_AUTHORIZE; |
398 | 0 | sess->auth = false; |
399 | 0 | } |
400 | 0 | else { |
401 | 0 | code = sess->last_request.opcode; |
402 | 0 | } |
403 | 0 | } |
404 | | |
405 | | /* Remember this code for later queries. */ |
406 | 0 | p_add_proto_data(wmem_file_scope(), pinfo, proto_sane, 0, GUINT_TO_POINTER(code)); |
407 | |
|
408 | 0 | return code; |
409 | 0 | } |
410 | | |
411 | | static proto_item * |
412 | 1.47k | dissect_sane_word(tvb_sane_reader *r, proto_tree *tree, int hfindex, int *word) { |
413 | 1.47k | proto_item *item = proto_tree_add_item(tree, hfindex, r->tvb, r->offset, SANE_WORD_LENGTH, |
414 | 1.47k | ENC_BIG_ENDIAN); |
415 | | // safe to ignore the return value here, we're guaranteed to have enough bytes to |
416 | | // read a word. |
417 | 1.47k | (void)tvb_read_sane_word(r, word); |
418 | 1.47k | return item; |
419 | 1.47k | } |
420 | | |
421 | | /** |
422 | | * Dissects and returns a SANE-encoded string from `r`. |
423 | | * |
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. |
427 | | */ |
428 | | static char * |
429 | 85 | dissect_sane_string(tvb_sane_reader *r, packet_info *pinfo, proto_tree *tree, int hfindex, const char *format) { |
430 | 85 | int offset = r->offset; |
431 | 85 | char *str = ""; |
432 | 85 | int len = tvb_read_sane_string(r, pinfo->pool, &str); |
433 | | |
434 | 85 | proto_item *str_item = proto_tree_add_item(tree, hf_sane_string, r->tvb, offset, len, ENC_NA); |
435 | 85 | proto_tree *str_tree = proto_item_add_subtree(str_item, ett_sane_string); |
436 | | |
437 | 85 | proto_item_set_text(str_item, format, str); |
438 | 85 | proto_tree_add_item(str_tree, hf_sane_string_length, r->tvb, offset, SANE_WORD_LENGTH, ENC_BIG_ENDIAN); |
439 | 85 | proto_tree_add_item(str_tree, hfindex, r->tvb, offset + SANE_WORD_LENGTH, len - SANE_WORD_LENGTH, ENC_NA); |
440 | 85 | return str; |
441 | 85 | } |
442 | | |
443 | | static void |
444 | 79 | dissect_sane_net_init_request(tvb_sane_reader *r, packet_info *pinfo, proto_tree *tree) { |
445 | 79 | int version = 0; |
446 | 79 | int offset = r->offset; |
447 | 79 | proto_item *version_item = dissect_sane_word(r, tree, hf_sane_version, &version); |
448 | 79 | proto_item *version_tree = proto_item_add_subtree(version_item, ett_sane_version); |
449 | | |
450 | 79 | proto_item_append_text(version_item, " (major: %d, minor: %d, build: %d)", version >> 24, |
451 | 79 | (version >> 16) & 0xff, version & 0xffff); |
452 | | |
453 | 79 | proto_tree_add_item(version_tree, hf_sane_version_major, r->tvb, offset, 1, ENC_NA); |
454 | 79 | proto_tree_add_item(version_tree, hf_sane_version_minor, r->tvb, offset + 1, 1, ENC_NA); |
455 | 79 | proto_tree_add_item(version_tree, hf_sane_version_build, r->tvb, offset + 2, 2, ENC_BIG_ENDIAN); |
456 | | |
457 | 79 | dissect_sane_string(r, pinfo, tree, hf_sane_username, "Username: %s"); |
458 | 79 | } |
459 | | |
460 | | static void |
461 | 2 | dissect_sane_net_open_request(tvb_sane_reader *r, packet_info *pinfo, proto_tree *tree) { |
462 | 2 | dissect_sane_string(r, pinfo, tree, hf_sane_device_name, "Device name: %s"); |
463 | 2 | } |
464 | | |
465 | | static void |
466 | 8 | dissect_control_option_value(tvb_sane_reader *r, packet_info *pinfo, proto_tree *tree) { |
467 | 8 | int value_type = 0; |
468 | 8 | dissect_sane_word(r, tree, hf_sane_option_value_type, &value_type); |
469 | | |
470 | 8 | proto_item *value_item = proto_tree_add_item(tree, hf_sane_option_value, r->tvb, r->offset, -1, ENC_NA); |
471 | 8 | proto_tree *value_tree = proto_item_add_subtree(value_item, ett_sane_option_value); |
472 | | |
473 | 8 | int array_length = 0; |
474 | 8 | proto_item *length_item = dissect_sane_word(r, value_tree, hf_sane_option_length, &array_length); |
475 | | |
476 | 8 | if (value_type == SANE_TYPE_STRING) { |
477 | 0 | dissect_sane_string(r, pinfo, value_tree, hf_sane_option_string_value, "Option value: '%s'"); |
478 | 8 | } else { |
479 | 8 | proto_item_append_text(length_item, " (vector of length %d)", array_length / SANE_WORD_LENGTH); |
480 | 8 | dissect_sane_word(r, value_tree, hf_sane_array_length, &array_length); |
481 | | |
482 | 894k | for (int i = 0; i < array_length; i++) { |
483 | 894k | if (value_type == SANE_TYPE_FIXED) { |
484 | 0 | int value = 0; |
485 | 0 | proto_item *numeric_value = dissect_sane_word(r, value_tree, hf_sane_option_numeric_value, &value); |
486 | 0 | proto_item_append_text(numeric_value, " (%f)", ((double) value) / (1 << 16)); |
487 | 894k | } else if (value_type == SANE_TYPE_INT) { |
488 | 0 | int value = 0; |
489 | 0 | proto_item *numeric_value = dissect_sane_word(r, value_tree, hf_sane_option_numeric_value, &value); |
490 | 0 | proto_item_append_text(numeric_value, " (%d)", value); |
491 | 894k | } else if (value_type == SANE_TYPE_BOOL) { |
492 | 0 | dissect_sane_word(r, value_tree, hf_sane_option_boolean_value, NULL); |
493 | 0 | } |
494 | 894k | } |
495 | 8 | } |
496 | 8 | } |
497 | | |
498 | | static void |
499 | 8 | dissect_sane_net_control_option_request(tvb_sane_reader *r, packet_info *pinfo, proto_tree *tree) { |
500 | 8 | dissect_sane_word(r, tree, hf_sane_device_handle, NULL); |
501 | 8 | dissect_sane_word(r, tree, hf_sane_option_index, NULL); |
502 | 8 | dissect_sane_word(r, tree, hf_sane_option_control_action, NULL); |
503 | 8 | dissect_control_option_value(r, pinfo, tree); |
504 | 8 | } |
505 | | |
506 | | static void |
507 | 2 | dissect_sane_net_authorize_request(tvb_sane_reader *r, packet_info *pinfo, proto_tree *tree) { |
508 | 2 | dissect_sane_string(r, pinfo, tree, hf_sane_resource_name, "Authentication resource: %s"); |
509 | 2 | dissect_sane_string(r, pinfo, tree, hf_sane_username, "Username: %s"); |
510 | 2 | dissect_sane_string(r, pinfo, tree, hf_sane_password, "Password: %s"); |
511 | 2 | } |
512 | | |
513 | | /** Dissects a message whose only payload is a device handle. */ |
514 | | static void |
515 | 19 | dissect_sane_device_handle_request(tvb_sane_reader *r, proto_tree *tree) { |
516 | 19 | dissect_sane_word(r, tree, hf_sane_device_handle, NULL); |
517 | 19 | } |
518 | | |
519 | | static int |
520 | 1.32k | dissect_sane_request(tvb_sane_reader *r, packet_info *pinfo, proto_tree *tree) { |
521 | 1.32k | unsigned opcode = SANE_NET_UNKNOWN; |
522 | 1.32k | dissect_sane_word(r, tree, hf_sane_opcode, &opcode); |
523 | 1.32k | proto_item_append_text(tree, ": %s request", val_to_str(opcode, opcode_vals, "Unknown opcode (%u)")); |
524 | 1.32k | col_append_fstr(pinfo->cinfo, COL_INFO, "%s request", val_to_str(opcode, opcode_vals, "Unknown opcode (%u)")); |
525 | | |
526 | 1.32k | switch (opcode) { |
527 | 79 | case SANE_NET_INIT: |
528 | 79 | dissect_sane_net_init_request(r, pinfo, tree); |
529 | 79 | break; |
530 | 8 | case SANE_NET_GET_DEVICES: |
531 | | // no additional payload here |
532 | 8 | break; |
533 | 2 | case SANE_NET_OPEN: |
534 | 2 | dissect_sane_net_open_request(r, pinfo, tree); |
535 | 2 | break; |
536 | 8 | case SANE_NET_CONTROL_OPTION: |
537 | 8 | dissect_sane_net_control_option_request(r, pinfo, tree); |
538 | 8 | break; |
539 | 8 | case SANE_NET_CLOSE: |
540 | 8 | case SANE_NET_START: |
541 | 14 | case SANE_NET_CANCEL: |
542 | 15 | case SANE_NET_GET_PARAMETERS: |
543 | 19 | case SANE_NET_GET_OPTION_DESCRIPTORS: |
544 | 19 | dissect_sane_device_handle_request(r, tree); |
545 | 19 | break; |
546 | 2 | case SANE_NET_AUTHORIZE: |
547 | 2 | dissect_sane_net_authorize_request(r, pinfo, tree); |
548 | 2 | break; |
549 | 1.32k | } |
550 | | |
551 | 1.29k | return r->bytes_read; |
552 | 1.32k | } |
553 | | |
554 | | static proto_item * |
555 | 0 | dissect_sane_status(tvb_sane_reader *r, packet_info *pinfo, proto_tree *tree, unsigned *status_ptr) { |
556 | 0 | int offset = r->offset; |
557 | 0 | unsigned status = SANE_STATUS_UNKNOWN; |
558 | | |
559 | | // Safe to ignore the return value here, we're guaranteed to have enough bytes to |
560 | | // read a word. |
561 | 0 | (void)tvb_read_sane_word(r, &status); |
562 | |
|
563 | 0 | proto_item_append_text(tree, " (%s)", val_to_str(status, status_values, "Unknown status (%u)")); |
564 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, " (%s)", val_to_str(status, status_values, "Unknown (%u)")); |
565 | |
|
566 | 0 | proto_item *status_item = proto_tree_add_item(tree, hf_sane_status, r->tvb, offset, SANE_WORD_LENGTH, ENC_BIG_ENDIAN); |
567 | 0 | proto_item_append_text(status_item, " (%s)", val_to_str(status, status_values, "Unknown (%u)")); |
568 | |
|
569 | 0 | if (status_ptr) { |
570 | 0 | *status_ptr = status; |
571 | 0 | } |
572 | |
|
573 | 0 | return status_item; |
574 | 0 | } |
575 | | |
576 | | static void |
577 | 0 | dissect_sane_net_init_response(tvb_sane_reader *r, packet_info *pinfo, proto_tree *tree) { |
578 | 0 | unsigned status; |
579 | 0 | dissect_sane_status(r, pinfo, tree, &status); |
580 | |
|
581 | 0 | int version = 0; |
582 | 0 | proto_item *version_item = dissect_sane_word(r, tree, hf_sane_version, &version); |
583 | 0 | proto_item *version_tree = proto_item_add_subtree(version_item, ett_sane_version); |
584 | |
|
585 | 0 | proto_item_append_text(version_item, " (major: %d, minor: %d, build: %d)", version >> 24, |
586 | 0 | (version >> 16) & 0xff, version & 0xffff); |
587 | |
|
588 | 0 | proto_tree_add_item(version_tree, hf_sane_version_major, r->tvb, SANE_WORD_LENGTH, 1, ENC_NA); |
589 | 0 | proto_tree_add_item(version_tree, hf_sane_version_minor, r->tvb, SANE_WORD_LENGTH + 1, 1, ENC_NA); |
590 | 0 | proto_tree_add_item(version_tree, hf_sane_version_build, r->tvb, SANE_WORD_LENGTH + 2, 2, ENC_BIG_ENDIAN); |
591 | 0 | } |
592 | | |
593 | | static void |
594 | 0 | dissect_sane_net_open_response(tvb_sane_reader *r, packet_info *pinfo, proto_tree *tree) { |
595 | 0 | unsigned status = SANE_STATUS_UNKNOWN; |
596 | 0 | dissect_sane_status(r, pinfo, tree, &status); |
597 | 0 | dissect_sane_word(r, tree, hf_sane_device_handle, NULL); |
598 | 0 | dissect_sane_string(r, pinfo, tree, hf_sane_resource_name, "Authentication resource: '%s'"); |
599 | 0 | } |
600 | | |
601 | | static void |
602 | 0 | append_option_value(proto_item *item, int value, unsigned units, unsigned type) { |
603 | 0 | switch (type) { |
604 | 0 | case SANE_TYPE_INT: |
605 | 0 | if (units) { |
606 | 0 | proto_item_append_text(item, " (%d %s)", value, |
607 | 0 | val_to_str_const(units, sane_option_unit_suffixes, "(unknown unit)")); |
608 | 0 | } else { |
609 | 0 | proto_item_append_text(item, " (%d)", value); |
610 | 0 | } |
611 | 0 | break; |
612 | 0 | case SANE_TYPE_FIXED: { |
613 | 0 | double fixed_val = ((double) value) / (1 << 16); |
614 | 0 | if (units) { |
615 | 0 | proto_item_append_text(item, " (%f %s)", fixed_val, |
616 | 0 | val_to_str_const(units, sane_option_unit_suffixes, "(unknown unit)")); |
617 | 0 | } else { |
618 | 0 | proto_item_append_text(item, " (%f)", fixed_val); |
619 | 0 | } |
620 | 0 | break; |
621 | 0 | } |
622 | 0 | case SANE_TYPE_BOOL: |
623 | 0 | proto_item_append_text(item, " (%s)", (value == 1) ? "True" : ((value == 0) ? "False" : "Invalid")); |
624 | 0 | break; |
625 | 0 | default: |
626 | 0 | break; |
627 | 0 | } |
628 | 0 | } |
629 | | |
630 | | static void |
631 | 0 | dissect_sane_net_get_option_descriptors_response(tvb_sane_reader *r, packet_info *pinfo, proto_tree *tree) { |
632 | 0 | int option_count = 0; |
633 | 0 | dissect_sane_word(r, tree, hf_sane_option_count, &option_count); |
634 | |
|
635 | 0 | for (int i = 0; i < option_count; i++) { |
636 | 0 | int unit = 0; |
637 | 0 | int type = 0; |
638 | 0 | int start_offset = r->offset; |
639 | 0 | proto_item *option_item = proto_tree_add_item(tree, hf_sane_option_descriptor, r->tvb, start_offset, 0, ENC_NA); |
640 | 0 | proto_tree *option_tree = proto_item_add_subtree(option_item, ett_sane_option); |
641 | 0 | proto_item_set_text(option_item, "Option descriptor %d", i); |
642 | |
|
643 | 0 | dissect_sane_word(r, option_tree, hf_sane_pointer_value, NULL); |
644 | 0 | char *option_name = dissect_sane_string(r, pinfo, option_tree, hf_sane_option_name, "Option name: %s"); |
645 | 0 | if (option_name && *option_name) { |
646 | 0 | proto_item_append_text(option_item, " (%s)", option_name); |
647 | 0 | } |
648 | 0 | char *option_title = dissect_sane_string(r, pinfo, option_tree, hf_sane_option_title, "Option title: %s"); |
649 | 0 | if (!(option_name && *option_name) && (option_title && *option_title)) { |
650 | 0 | proto_item_append_text(option_item, " (%s)", option_title); |
651 | 0 | } |
652 | 0 | dissect_sane_string(r, pinfo, option_tree, hf_sane_option_description, "Option description: %s"); |
653 | 0 | dissect_sane_word(r, option_tree, hf_sane_option_value_type, &type); |
654 | 0 | dissect_sane_word(r, option_tree, hf_sane_option_unit, &unit); |
655 | 0 | dissect_sane_word(r, option_tree, hf_sane_option_size, NULL); |
656 | |
|
657 | 0 | proto_tree_add_bitmask(option_tree, r->tvb, r->offset, hf_sane_option_capabilities, |
658 | 0 | ett_sane_option_capabilities, |
659 | 0 | 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 |
663 | | */ |
664 | 0 | tvb_skip_bytes(r, SANE_WORD_LENGTH); |
665 | |
|
666 | 0 | int constraint_start = r->offset; |
667 | 0 | proto_item *constraint_item = proto_tree_add_item(option_tree, hf_sane_option_constraints, r->tvb, constraint_start, 0, ENC_NA); |
668 | 0 | proto_tree *constraint_tree = proto_item_add_subtree(constraint_item, ett_sane_option_constraints); |
669 | |
|
670 | 0 | int constraint_type = SANE_NO_CONSTRAINT; |
671 | 0 | dissect_sane_word(r, constraint_tree, hf_sane_option_constraint_type, &constraint_type); |
672 | 0 | proto_item_set_text(constraint_item, "Constraint type: %s", |
673 | 0 | val_to_str(constraint_type, sane_constraint_type_names, "Unknown (%u)")); |
674 | |
|
675 | 0 | int array_length = 0; |
676 | 0 | int min = 0; |
677 | 0 | int max = 0; |
678 | 0 | int quant = 0; |
679 | 0 | switch (constraint_type) { |
680 | 0 | case SANE_CONSTRAINT_STRING_LIST: |
681 | 0 | dissect_sane_word(r, constraint_tree, hf_sane_array_length, &array_length); |
682 | |
|
683 | 0 | for (int j = 0; j < array_length; j++) { |
684 | 0 | dissect_sane_string(r, pinfo, constraint_tree, hf_sane_option_possible_string_value, "Possible value: %s"); |
685 | 0 | } |
686 | 0 | break; |
687 | 0 | case SANE_CONSTRAINT_WORD_LIST: |
688 | 0 | dissect_sane_word(r, constraint_tree, hf_sane_array_length, &array_length); |
689 | |
|
690 | 0 | for (int j = 0; j < array_length; j++) { |
691 | 0 | int value = 0; |
692 | 0 | proto_item *value_item = dissect_sane_word(r, constraint_tree, hf_sane_option_possible_word_value, |
693 | 0 | &value); |
694 | 0 | append_option_value(value_item, value, unit, type); |
695 | 0 | } |
696 | 0 | break; |
697 | 0 | case SANE_CONSTRAINT_RANGE: |
698 | 0 | dissect_sane_word(r, constraint_tree, hf_sane_pointer_value, NULL); |
699 | |
|
700 | 0 | proto_item *min_item = dissect_sane_word(r, constraint_tree, hf_sane_option_range_min, &min); |
701 | 0 | append_option_value(min_item, min, unit, type); |
702 | 0 | proto_item *max_item = dissect_sane_word(r, constraint_tree, hf_sane_option_range_max, &max); |
703 | 0 | append_option_value(max_item, max, unit, type); |
704 | 0 | proto_item *quant_item = dissect_sane_word(r, constraint_tree, hf_sane_option_range_quant, &quant); |
705 | 0 | append_option_value(quant_item, quant, unit, type); |
706 | 0 | break; |
707 | 0 | } |
708 | | |
709 | 0 | proto_item_set_len(constraint_item, r->offset - constraint_start); |
710 | 0 | proto_item_set_len(option_item, r->offset - start_offset); |
711 | 0 | } |
712 | 0 | } |
713 | | |
714 | | static void |
715 | 0 | dissect_sane_net_start_response(tvb_sane_reader *r, packet_info *pinfo, proto_tree *tree) { |
716 | 0 | dissect_sane_status(r, pinfo, tree, NULL); |
717 | 0 | dissect_sane_word(r, tree, hf_sane_data_port, NULL); |
718 | 0 | dissect_sane_word(r, tree, hf_sane_byte_order, NULL); |
719 | 0 | dissect_sane_string(r, pinfo, tree, hf_sane_resource_name, "Authentication resource: %s"); |
720 | 0 | } |
721 | | |
722 | | static void |
723 | 0 | dissect_sane_net_get_parameters_response(tvb_sane_reader *r, packet_info *pinfo, proto_tree *tree) { |
724 | 0 | dissect_sane_status(r, pinfo, tree, NULL); |
725 | 0 | dissect_sane_word(r, tree, hf_sane_frame_format, NULL); |
726 | 0 | dissect_sane_word(r, tree, hf_sane_scan_is_last_frame, NULL); |
727 | 0 | dissect_sane_word(r, tree, hf_sane_scan_bytes_per_line, NULL); |
728 | 0 | dissect_sane_word(r, tree, hf_sane_scan_pixels_per_line, NULL); |
729 | 0 | dissect_sane_word(r, tree, hf_sane_scan_line_count, NULL); |
730 | 0 | dissect_sane_word(r, tree, hf_sane_scan_pixel_depth, NULL); |
731 | 0 | } |
732 | | |
733 | | static void |
734 | 0 | dissect_sane_net_control_option_response(tvb_sane_reader *r, packet_info *pinfo, proto_tree *tree) { |
735 | 0 | dissect_sane_status(r, pinfo, tree, NULL); |
736 | 0 | proto_tree_add_bitmask(tree, r->tvb, r->offset, hf_sane_control_option_info, |
737 | 0 | ett_sane_control_option_info, |
738 | 0 | sane_control_option_info_bits, ENC_BIG_ENDIAN); |
739 | 0 | tvb_skip_bytes(r, SANE_WORD_LENGTH); |
740 | 0 | dissect_control_option_value(r, pinfo, tree); |
741 | 0 | dissect_sane_string(r, pinfo, tree, hf_sane_resource_name, "Authentication resource: %s"); |
742 | 0 | } |
743 | | |
744 | | static void |
745 | 0 | dissect_sane_dummy_response(tvb_sane_reader *r, proto_tree *tree) { |
746 | 0 | dissect_sane_word(r, tree, hf_sane_dummy_value, NULL); |
747 | 0 | } |
748 | | |
749 | | static void |
750 | 0 | dissect_sane_net_get_devices_response(tvb_sane_reader *r, packet_info *pinfo, proto_tree *tree) { |
751 | 0 | dissect_sane_status(r, pinfo, tree, NULL); |
752 | |
|
753 | 0 | int array_len = 0; |
754 | 0 | dissect_sane_word(r, tree, hf_sane_array_length, &array_len); |
755 | 0 | for (int i = 0; i < array_len - 1; i++) { |
756 | 0 | int offset = r->offset; |
757 | 0 | proto_item *device_item = proto_tree_add_item(tree, hf_sane_device_descriptor, r->tvb, r->offset, -1, ENC_NA); |
758 | 0 | proto_tree *device_tree = proto_item_add_subtree(device_item, ett_sane_device_descriptor); |
759 | 0 | proto_item_set_text(device_item, "Device[%d] descriptor", i); |
760 | |
|
761 | 0 | dissect_sane_word(r, device_tree, hf_sane_pointer_value, NULL); |
762 | 0 | dissect_sane_string(r, pinfo, device_tree, hf_sane_device_name, "Device name: %s"); |
763 | 0 | dissect_sane_string(r, pinfo, device_tree, hf_sane_device_vendor, "Device vendor: %s"); |
764 | 0 | dissect_sane_string(r, pinfo, device_tree, hf_sane_device_model, "Device model: %s"); |
765 | 0 | dissect_sane_string(r, pinfo, device_tree, hf_sane_device_type, "Device type: %s"); |
766 | 0 | proto_item_set_len(device_item, r->offset - offset); |
767 | 0 | } |
768 | |
|
769 | 0 | dissect_sane_word(r, tree, hf_sane_pointer_value, NULL); |
770 | 0 | } |
771 | | |
772 | | static void |
773 | 0 | dissect_sane_response(tvb_sane_reader *r, sane_session *sess, packet_info *pinfo, proto_tree *tree) { |
774 | 0 | sane_rpc_code opcode = get_sane_expected_response_type(sess, pinfo); |
775 | |
|
776 | 0 | proto_item_append_text(tree, ": %s response", val_to_str(opcode, opcode_vals, "Unknown opcode (%u)")); |
777 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "%s response", val_to_str(opcode, opcode_vals, "Unknown opcode (%u)")); |
778 | |
|
779 | 0 | switch (opcode) { |
780 | 0 | case SANE_NET_INIT: |
781 | 0 | dissect_sane_net_init_response(r, pinfo, tree); |
782 | 0 | break; |
783 | 0 | case SANE_NET_OPEN: |
784 | 0 | dissect_sane_net_open_response(r, pinfo, tree); |
785 | 0 | break; |
786 | 0 | case SANE_NET_GET_OPTION_DESCRIPTORS: |
787 | 0 | dissect_sane_net_get_option_descriptors_response(r, pinfo, tree); |
788 | 0 | break; |
789 | 0 | case SANE_NET_START: |
790 | 0 | dissect_sane_net_start_response(r, pinfo, tree); |
791 | 0 | break; |
792 | 0 | case SANE_NET_GET_PARAMETERS: |
793 | 0 | dissect_sane_net_get_parameters_response(r, pinfo, tree); |
794 | 0 | break; |
795 | 0 | case SANE_NET_CONTROL_OPTION: |
796 | 0 | dissect_sane_net_control_option_response(r, pinfo, tree); |
797 | 0 | break; |
798 | 0 | case SANE_NET_GET_DEVICES: |
799 | 0 | dissect_sane_net_get_devices_response(r, pinfo, tree); |
800 | 0 | break; |
801 | 0 | case SANE_NET_CLOSE: |
802 | 0 | case SANE_NET_CANCEL: |
803 | 0 | case SANE_NET_AUTHORIZE: |
804 | 0 | dissect_sane_dummy_response(r, tree); |
805 | 0 | break; |
806 | 0 | default: |
807 | 0 | break; |
808 | 0 | } |
809 | 0 | } |
810 | | |
811 | | static int |
812 | 1.32k | dissect_sane_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { |
813 | 1.32k | tvb_sane_reader r = {.tvb = tvb, .bytes_read = 0, .offset = 0}; |
814 | | |
815 | 1.32k | conversation_t *conv = find_or_create_conversation(pinfo); |
816 | 1.32k | if (!conv) { |
817 | 0 | return 0; |
818 | 0 | } |
819 | | |
820 | 1.32k | sane_session *sess = conversation_get_proto_data(conv, proto_sane); |
821 | 1.32k | DISSECTOR_ASSERT_HINT(sess, "no session found"); |
822 | | |
823 | 1.32k | col_set_str(pinfo->cinfo, COL_PROTOCOL, "SANE"); |
824 | 1.32k | col_clear(pinfo->cinfo, COL_INFO); |
825 | | |
826 | 1.32k | proto_item *sane_item = proto_tree_add_item(tree, proto_sane, r.tvb, 0, -1, ENC_NA); |
827 | 1.32k | proto_tree *sane_tree = proto_item_add_subtree(sane_item, ett_sane); |
828 | | |
829 | 1.32k | if (value_is_in_range(sane_server_ports, pinfo->destport)) { |
830 | 1.32k | dissect_sane_request(&r, pinfo, sane_tree); |
831 | 1.32k | } else { |
832 | 0 | dissect_sane_response(&r, sess, pinfo, sane_tree); |
833 | 0 | } |
834 | | |
835 | 1.32k | proto_item_set_len(sane_item, r.bytes_read); |
836 | 1.32k | return r.bytes_read; |
837 | 1.32k | } |
838 | | |
839 | | /** |
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. |
845 | | */ |
846 | | static unsigned |
847 | 1.36k | get_sane_pdu_len(packet_info *pinfo, tvbuff_t *tvb, int offset, void *data _U_) { |
848 | 1.36k | tvb_sane_reader r = {.tvb = tvb, .offset = offset, .bytes_read = 0}; |
849 | | |
850 | 1.36k | conversation_t *conv = find_or_create_conversation(pinfo); |
851 | 1.36k | if (!conv) { |
852 | 0 | return 0; |
853 | 0 | } |
854 | | |
855 | 1.36k | sane_session *sess = conversation_get_proto_data(conv, proto_sane); |
856 | | |
857 | 1.36k | if (!sess) { |
858 | 7 | sess = wmem_new0(wmem_file_scope(), sane_session); |
859 | 7 | conversation_add_proto_data(conv, proto_sane, sess); |
860 | 7 | } |
861 | | |
862 | 1.36k | if (value_is_in_range(sane_server_ports, pinfo->destport)) { |
863 | | /* REQUEST */ |
864 | 1.36k | unsigned opcode; |
865 | 1.36k | WORD_OR_RETURN(&r, &opcode); |
866 | | |
867 | 1.35k | sane_pdu pdu = { |
868 | 1.35k | .is_request = true, |
869 | 1.35k | .opcode = opcode, |
870 | 1.35k | .packet_num = pinfo->num |
871 | 1.35k | }; |
872 | | |
873 | 1.35k | if (!PINFO_FD_VISITED(pinfo)) { |
874 | 1.35k | sess->seen_request = true; |
875 | 1.35k | if (opcode == SANE_NET_AUTHORIZE) { |
876 | | /* Just set this flag, so can remember op being authorised */ |
877 | 5 | sess->auth = true; |
878 | 5 | } |
879 | 1.34k | else { |
880 | | /* Remember normal request */ |
881 | 1.34k | sess->last_request = pdu; |
882 | 1.34k | sess->auth = false; |
883 | 1.34k | } |
884 | 1.35k | } |
885 | | |
886 | 1.35k | switch (opcode) { |
887 | 96 | case SANE_NET_INIT: |
888 | 96 | WORD_OR_RETURN(&r, NULL); |
889 | 96 | STRING_OR_RETURN(&r); |
890 | 85 | break; |
891 | 85 | case SANE_NET_GET_DEVICES: |
892 | 8 | case SANE_NET_EXIT: |
893 | 8 | break; |
894 | 3 | case SANE_NET_OPEN: |
895 | 3 | STRING_OR_RETURN(&r); |
896 | 2 | break; |
897 | 8 | case SANE_NET_CLOSE: |
898 | 12 | case SANE_NET_GET_OPTION_DESCRIPTORS: |
899 | 13 | case SANE_NET_GET_PARAMETERS: |
900 | 13 | case SANE_NET_START: |
901 | 19 | case SANE_NET_CANCEL: |
902 | 19 | WORD_OR_RETURN(&r, NULL); |
903 | 19 | break; |
904 | 19 | case SANE_NET_CONTROL_OPTION: |
905 | 57 | for (int i = 0; i < 4; i++) { |
906 | 47 | WORD_OR_RETURN(&r, NULL); |
907 | 47 | } |
908 | 10 | unsigned value_size; |
909 | 10 | WORD_OR_RETURN(&r, &value_size); |
910 | | |
911 | | // Pointer to void, contains an extra word for whether the pointer is NULL |
912 | 9 | if (tvb_skip_bytes(&r, SANE_WORD_LENGTH + value_size) == 0) { |
913 | 1 | return 0; |
914 | 1 | } |
915 | | |
916 | 8 | break; |
917 | 8 | case SANE_NET_AUTHORIZE: |
918 | 5 | STRING_OR_RETURN(&r); |
919 | 4 | STRING_OR_RETURN(&r); |
920 | 3 | STRING_OR_RETURN(&r); |
921 | 2 | break; |
922 | 1.35k | } |
923 | 1.35k | } else { |
924 | | /* RESPONSE */ |
925 | 0 | sane_rpc_code opcode = get_sane_expected_response_type(sess, pinfo); |
926 | 0 | unsigned array_len; |
927 | |
|
928 | 0 | switch (opcode) { |
929 | 0 | case SANE_NET_INIT: |
930 | 0 | for (int i = 0; i < 2; i++) { |
931 | 0 | WORD_OR_RETURN(&r, NULL); |
932 | 0 | } |
933 | 0 | break; |
934 | 0 | case SANE_NET_OPEN: |
935 | | // Status word |
936 | 0 | WORD_OR_RETURN(&r, NULL); |
937 | | // Device handle |
938 | 0 | WORD_OR_RETURN(&r, NULL); |
939 | | // Authentication resource name |
940 | 0 | STRING_OR_RETURN(&r); |
941 | 0 | break; |
942 | | |
943 | 0 | case SANE_NET_GET_OPTION_DESCRIPTORS: |
944 | 0 | WORD_OR_RETURN(&r, &array_len); |
945 | | |
946 | 0 | for (unsigned i = 0; i < array_len; i++) { |
947 | 0 | WORD_OR_RETURN(&r, NULL); |
948 | | |
949 | | // read name, title and description |
950 | 0 | for (int j = 0; j < 3; j++) { |
951 | 0 | STRING_OR_RETURN(&r); |
952 | 0 | } |
953 | | |
954 | 0 | for (int j = 0; j < 4; j++) { |
955 | 0 | WORD_OR_RETURN(&r, NULL); |
956 | 0 | } |
957 | | |
958 | | // constraint type |
959 | 0 | unsigned constraint_type; |
960 | 0 | WORD_OR_RETURN(&r, &constraint_type); |
961 | | |
962 | 0 | unsigned string_count; |
963 | 0 | unsigned value_list_length; |
964 | 0 | switch (constraint_type) { |
965 | 0 | case SANE_CONSTRAINT_STRING_LIST: |
966 | 0 | WORD_OR_RETURN(&r, &string_count); |
967 | | |
968 | 0 | for (unsigned j = 0; j < string_count; j++) { |
969 | 0 | STRING_OR_RETURN(&r); |
970 | 0 | } |
971 | 0 | break; |
972 | 0 | case SANE_CONSTRAINT_WORD_LIST: |
973 | 0 | WORD_OR_RETURN(&r, &value_list_length); |
974 | | |
975 | 0 | for (unsigned j = 0; j < value_list_length; j++) { |
976 | 0 | WORD_OR_RETURN(&r, NULL); |
977 | 0 | } |
978 | 0 | break; |
979 | 0 | case SANE_CONSTRAINT_RANGE: |
980 | | // Pointer to range, then min, max, quantization |
981 | 0 | for (unsigned j = 0; j < 4; j++) { |
982 | 0 | WORD_OR_RETURN(&r, NULL); |
983 | 0 | } |
984 | 0 | break; |
985 | 0 | } |
986 | 0 | } |
987 | 0 | break; |
988 | 0 | 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 | 0 | for (int i = 0; i < 3; i++) { |
998 | 0 | WORD_OR_RETURN(&r, NULL); |
999 | 0 | } |
1000 | | |
1001 | 0 | unsigned value_len; |
1002 | 0 | WORD_OR_RETURN(&r, &value_len); |
1003 | | |
1004 | 0 | if (tvb_skip_bytes(&r, value_len + SANE_WORD_LENGTH) == 0) { |
1005 | 0 | return 0; |
1006 | 0 | } |
1007 | | |
1008 | 0 | STRING_OR_RETURN(&r); |
1009 | 0 | break; |
1010 | 0 | case SANE_NET_GET_DEVICES: |
1011 | 0 | WORD_OR_RETURN(&r, NULL); |
1012 | | |
1013 | 0 | unsigned device_count; |
1014 | 0 | WORD_OR_RETURN(&r, &device_count); |
1015 | 0 | for (unsigned i = 0; i < device_count - 1; i++) { |
1016 | 0 | WORD_OR_RETURN(&r, NULL); |
1017 | 0 | STRING_OR_RETURN(&r); |
1018 | 0 | STRING_OR_RETURN(&r); |
1019 | 0 | STRING_OR_RETURN(&r); |
1020 | 0 | STRING_OR_RETURN(&r); |
1021 | 0 | } |
1022 | 0 | WORD_OR_RETURN(&r, NULL); |
1023 | 0 | break; |
1024 | 0 | case SANE_NET_CLOSE: |
1025 | 0 | WORD_OR_RETURN(&r, NULL); |
1026 | 0 | break; |
1027 | 0 | case SANE_NET_START: |
1028 | 0 | for (int i = 0; i < 3; i++) { |
1029 | 0 | WORD_OR_RETURN(&r, NULL); |
1030 | 0 | } |
1031 | 0 | STRING_OR_RETURN(&r); |
1032 | 0 | break; |
1033 | 0 | case SANE_NET_GET_PARAMETERS: |
1034 | 0 | for (int i = 0; i < 7; i++) { |
1035 | 0 | WORD_OR_RETURN(&r, NULL); |
1036 | 0 | } |
1037 | 0 | break; |
1038 | 0 | case SANE_NET_CANCEL: |
1039 | 0 | case SANE_NET_AUTHORIZE: |
1040 | 0 | WORD_OR_RETURN(&r, NULL); |
1041 | 0 | break; |
1042 | 0 | default: |
1043 | 0 | break; |
1044 | 0 | } |
1045 | 0 | } |
1046 | | |
1047 | 1.33k | return r.bytes_read; |
1048 | 1.36k | } |
1049 | | |
1050 | | static int |
1051 | 42 | dissect_sane(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) { |
1052 | 42 | tcp_dissect_pdus(tvb, pinfo, tree, true, SANE_WORD_LENGTH, get_sane_pdu_len, dissect_sane_pdu, data); |
1053 | 42 | return (int) tvb_reported_length(tvb); |
1054 | 42 | } |
1055 | | |
1056 | | static void |
1057 | 14 | apply_sane_prefs(void) { |
1058 | 14 | sane_server_ports = prefs_get_range_value(SANE_MODULE_NAME, "tcp.port"); |
1059 | 14 | } |
1060 | | |
1061 | 14 | void proto_register_sane(void) { |
1062 | 14 | static hf_register_info hf[] = { |
1063 | 14 | {&hf_sane_opcode, |
1064 | 14 | { |
1065 | 14 | "Opcode", |
1066 | 14 | "sane.opcode", |
1067 | 14 | FT_UINT32, |
1068 | 14 | BASE_DEC, |
1069 | 14 | VALS(opcode_vals), |
1070 | 14 | 0, |
1071 | 14 | "RPC request type", |
1072 | 14 | HFILL, |
1073 | 14 | }}, |
1074 | 14 | {&hf_sane_version, |
1075 | 14 | { |
1076 | 14 | "Version", |
1077 | 14 | "sane.version", |
1078 | 14 | FT_UINT32, |
1079 | 14 | BASE_HEX, |
1080 | 14 | NULL, |
1081 | 14 | 0, |
1082 | 14 | "Protocol version", |
1083 | 14 | HFILL, |
1084 | 14 | }}, |
1085 | 14 | {&hf_sane_version_major, |
1086 | 14 | { |
1087 | 14 | "Version Major Number", |
1088 | 14 | "sane.version.major", |
1089 | 14 | FT_UINT8, |
1090 | 14 | BASE_HEX, |
1091 | 14 | NULL, |
1092 | 14 | 0, |
1093 | 14 | NULL, |
1094 | 14 | HFILL, |
1095 | 14 | }}, |
1096 | 14 | {&hf_sane_version_minor, |
1097 | 14 | { |
1098 | 14 | "Version Minor Number", |
1099 | 14 | "sane.version.minor", |
1100 | 14 | FT_UINT8, |
1101 | 14 | BASE_HEX, |
1102 | 14 | NULL, |
1103 | 14 | 0, |
1104 | 14 | NULL, |
1105 | 14 | HFILL, |
1106 | 14 | }}, |
1107 | 14 | {&hf_sane_version_build, |
1108 | 14 | { |
1109 | 14 | "Version Build Number", |
1110 | 14 | "sane.version.build", |
1111 | 14 | FT_UINT16, |
1112 | 14 | BASE_HEX, |
1113 | 14 | NULL, |
1114 | 14 | 0, |
1115 | 14 | NULL, |
1116 | 14 | HFILL, |
1117 | 14 | }}, |
1118 | 14 | {&hf_sane_username, |
1119 | 14 | { |
1120 | 14 | "Username", |
1121 | 14 | "sane.username", |
1122 | 14 | FT_STRING, |
1123 | 14 | BASE_NONE, |
1124 | 14 | NULL, |
1125 | 14 | 0, |
1126 | 14 | NULL, |
1127 | 14 | HFILL, |
1128 | 14 | }}, |
1129 | 14 | {&hf_sane_password, |
1130 | 14 | { |
1131 | 14 | "Password", |
1132 | 14 | "sane.password", |
1133 | 14 | FT_STRING, |
1134 | 14 | BASE_NONE, |
1135 | 14 | NULL, |
1136 | 14 | 0, |
1137 | 14 | NULL, |
1138 | 14 | HFILL, |
1139 | 14 | }}, |
1140 | 14 | {&hf_sane_string, |
1141 | 14 | { |
1142 | 14 | "String", |
1143 | 14 | "sane.string", |
1144 | 14 | FT_NONE, |
1145 | 14 | BASE_NONE, |
1146 | 14 | NULL, |
1147 | 14 | 0, |
1148 | 14 | NULL, |
1149 | 14 | HFILL, |
1150 | 14 | }}, |
1151 | 14 | {&hf_sane_string_length, |
1152 | 14 | { |
1153 | 14 | "String length", |
1154 | 14 | "sane.string.length", |
1155 | 14 | FT_UINT32, |
1156 | 14 | BASE_DEC, |
1157 | 14 | NULL, |
1158 | 14 | 0, |
1159 | 14 | NULL, |
1160 | 14 | HFILL, |
1161 | 14 | }}, |
1162 | 14 | {&hf_sane_array_length, |
1163 | 14 | { |
1164 | 14 | "Array length", |
1165 | 14 | "sane.array.length", |
1166 | 14 | FT_UINT32, |
1167 | 14 | BASE_DEC, |
1168 | 14 | NULL, |
1169 | 14 | 0, |
1170 | 14 | NULL, |
1171 | 14 | HFILL, |
1172 | 14 | }}, |
1173 | 14 | {&hf_sane_device_descriptor, |
1174 | 14 | { |
1175 | 14 | "Device descriptor", |
1176 | 14 | "sane.device.descriptor", |
1177 | 14 | FT_NONE, |
1178 | 14 | BASE_NONE, |
1179 | 14 | NULL, |
1180 | 14 | 0, |
1181 | 14 | NULL, |
1182 | 14 | HFILL, |
1183 | 14 | }}, |
1184 | 14 | {&hf_sane_device_name, |
1185 | 14 | { |
1186 | 14 | "Device name", |
1187 | 14 | "sane.device.name", |
1188 | 14 | FT_STRING, |
1189 | 14 | BASE_NONE, |
1190 | 14 | NULL, |
1191 | 14 | 0, |
1192 | 14 | NULL, |
1193 | 14 | HFILL, |
1194 | 14 | }}, |
1195 | 14 | {&hf_sane_device_vendor, |
1196 | 14 | { |
1197 | 14 | "Device vendor", |
1198 | 14 | "sane.device.vendor", |
1199 | 14 | FT_STRING, |
1200 | 14 | BASE_NONE, |
1201 | 14 | NULL, |
1202 | 14 | 0, |
1203 | 14 | NULL, |
1204 | 14 | HFILL, |
1205 | 14 | }}, |
1206 | 14 | {&hf_sane_device_model, |
1207 | 14 | { |
1208 | 14 | "Device model", |
1209 | 14 | "sane.device.model", |
1210 | 14 | FT_STRING, |
1211 | 14 | BASE_NONE, |
1212 | 14 | NULL, |
1213 | 14 | 0, |
1214 | 14 | NULL, |
1215 | 14 | HFILL, |
1216 | 14 | }}, |
1217 | 14 | {&hf_sane_device_type, |
1218 | 14 | { |
1219 | 14 | "Device type", |
1220 | 14 | "sane.device.type", |
1221 | 14 | FT_STRING, |
1222 | 14 | BASE_NONE, |
1223 | 14 | NULL, |
1224 | 14 | 0, |
1225 | 14 | NULL, |
1226 | 14 | HFILL, |
1227 | 14 | }}, |
1228 | 14 | {&hf_sane_resource_name, |
1229 | 14 | { |
1230 | 14 | "Resource name", |
1231 | 14 | "sane.resource.name", |
1232 | 14 | FT_STRING, |
1233 | 14 | BASE_NONE, |
1234 | 14 | NULL, |
1235 | 14 | 0, |
1236 | 14 | NULL, |
1237 | 14 | HFILL, |
1238 | 14 | }}, |
1239 | 14 | {&hf_sane_device_handle, |
1240 | 14 | { |
1241 | 14 | "Device handle", |
1242 | 14 | "sane.device.handle", |
1243 | 14 | FT_UINT32, |
1244 | 14 | BASE_DEC, |
1245 | 14 | NULL, |
1246 | 14 | 0, |
1247 | 14 | NULL, |
1248 | 14 | HFILL, |
1249 | 14 | }}, |
1250 | 14 | {&hf_sane_option_index, |
1251 | 14 | { |
1252 | 14 | "Option index", |
1253 | 14 | "sane.option", |
1254 | 14 | FT_UINT32, |
1255 | 14 | BASE_DEC, |
1256 | 14 | NULL, |
1257 | 14 | 0, |
1258 | 14 | NULL, |
1259 | 14 | HFILL, |
1260 | 14 | }}, |
1261 | 14 | {&hf_sane_option_control_action, |
1262 | 14 | { |
1263 | 14 | "Option control action", |
1264 | 14 | "sane.option.action", |
1265 | 14 | FT_UINT32, |
1266 | 14 | BASE_DEC, |
1267 | 14 | VALS(control_types), |
1268 | 14 | 0, |
1269 | 14 | NULL, |
1270 | 14 | HFILL, |
1271 | 14 | }}, |
1272 | 14 | {&hf_sane_option_length, |
1273 | 14 | { |
1274 | 14 | "Option value length", |
1275 | 14 | "sane.option.length", |
1276 | 14 | FT_UINT32, |
1277 | 14 | BASE_DEC, |
1278 | 14 | NULL, |
1279 | 14 | 0, |
1280 | 14 | NULL, |
1281 | 14 | HFILL, |
1282 | | |
1283 | 14 | }}, |
1284 | 14 | {&hf_sane_option_value_type, |
1285 | 14 | { |
1286 | 14 | "Option value type", |
1287 | 14 | "sane.option.type", |
1288 | 14 | FT_UINT32, |
1289 | 14 | BASE_DEC, |
1290 | 14 | VALS(sane_value_types), |
1291 | 14 | 0, |
1292 | 14 | NULL, |
1293 | 14 | HFILL, |
1294 | 14 | }}, |
1295 | 14 | {&hf_sane_status, |
1296 | 14 | { |
1297 | 14 | "Status", |
1298 | 14 | "sane.status", |
1299 | 14 | FT_UINT32, |
1300 | 14 | BASE_DEC, |
1301 | 14 | NULL, |
1302 | 14 | 0, |
1303 | 14 | NULL, |
1304 | 14 | HFILL, |
1305 | 14 | }}, |
1306 | 14 | {&hf_sane_option_count, |
1307 | 14 | { |
1308 | 14 | "Option count", |
1309 | 14 | "sane.option_count", |
1310 | 14 | FT_UINT32, |
1311 | 14 | BASE_DEC, |
1312 | 14 | NULL, |
1313 | 14 | 0, |
1314 | 14 | NULL, |
1315 | 14 | HFILL, |
1316 | 14 | }}, |
1317 | 14 | {&hf_sane_pointer_value, |
1318 | 14 | { |
1319 | 14 | "Pointer value", |
1320 | 14 | "sane.pointer_value", |
1321 | 14 | FT_UINT32, |
1322 | 14 | BASE_HEX, |
1323 | 14 | NULL, |
1324 | 14 | 0, |
1325 | 14 | NULL, |
1326 | 14 | HFILL, |
1327 | 14 | }}, |
1328 | 14 | {&hf_sane_option_name, |
1329 | 14 | { |
1330 | 14 | "Option name", |
1331 | 14 | "sane.option.name", |
1332 | 14 | FT_STRING, |
1333 | 14 | BASE_NONE, |
1334 | 14 | NULL, |
1335 | 14 | 0, |
1336 | 14 | NULL, |
1337 | 14 | HFILL, |
1338 | 14 | }}, |
1339 | 14 | {&hf_sane_option_title, |
1340 | 14 | { |
1341 | 14 | "Option title", |
1342 | 14 | "sane.option.title", |
1343 | 14 | FT_STRING, |
1344 | 14 | BASE_NONE, |
1345 | 14 | NULL, |
1346 | 14 | 0, |
1347 | 14 | NULL, |
1348 | 14 | HFILL, |
1349 | 14 | }}, |
1350 | 14 | {&hf_sane_option_description, |
1351 | 14 | { |
1352 | 14 | "Option description", |
1353 | 14 | "sane.option.description", |
1354 | 14 | FT_STRING, |
1355 | 14 | BASE_NONE, |
1356 | 14 | NULL, |
1357 | 14 | 0, |
1358 | 14 | NULL, |
1359 | 14 | HFILL, |
1360 | 14 | }}, |
1361 | 14 | {&hf_sane_option_descriptor, |
1362 | 14 | { |
1363 | 14 | "Option descriptor", |
1364 | 14 | "sane.option.descriptor", |
1365 | 14 | FT_BYTES, |
1366 | 14 | BASE_NONE, |
1367 | 14 | NULL, |
1368 | 14 | 0, |
1369 | 14 | NULL, |
1370 | 14 | HFILL, |
1371 | 14 | }}, |
1372 | 14 | {&hf_sane_option_unit, |
1373 | 14 | { |
1374 | 14 | "Option unit", |
1375 | 14 | "sane.option.unit", |
1376 | 14 | FT_UINT32, |
1377 | 14 | BASE_DEC, |
1378 | 14 | VALS(sane_option_units), |
1379 | 14 | 0, |
1380 | 14 | NULL, |
1381 | 14 | HFILL, |
1382 | 14 | }}, |
1383 | 14 | {&hf_sane_option_size, |
1384 | 14 | { |
1385 | 14 | "Option size", |
1386 | 14 | "sane.option.size", |
1387 | 14 | FT_UINT32, |
1388 | 14 | BASE_DEC, |
1389 | 14 | NULL, |
1390 | 14 | 0, |
1391 | 14 | NULL, |
1392 | 14 | HFILL, |
1393 | 14 | }}, |
1394 | 14 | {&hf_sane_option_capabilities, |
1395 | 14 | { |
1396 | 14 | "Option capabilities", |
1397 | 14 | "sane.option.capabilities", |
1398 | 14 | FT_UINT32, |
1399 | 14 | BASE_HEX, |
1400 | 14 | NULL, |
1401 | 14 | 0, |
1402 | 14 | NULL, |
1403 | 14 | HFILL, |
1404 | | |
1405 | 14 | }}, |
1406 | 14 | {&hf_sane_option_capability_soft_select, |
1407 | 14 | { |
1408 | 14 | "Can be changed in software", |
1409 | 14 | "sane.option.soft_select", |
1410 | 14 | FT_BOOLEAN, |
1411 | 14 | 32, |
1412 | 14 | NULL, |
1413 | 14 | SANE_CAP_SOFT_SELECT, |
1414 | 14 | NULL, |
1415 | 14 | HFILL, |
1416 | 14 | }}, |
1417 | 14 | {&hf_sane_option_capability_hard_select, |
1418 | 14 | { |
1419 | 14 | "Requires user intervention to change", |
1420 | 14 | "sane.option.hard_select", |
1421 | 14 | FT_BOOLEAN, |
1422 | 14 | 32, |
1423 | 14 | NULL, |
1424 | 14 | SANE_CAP_HARD_SELECT, |
1425 | 14 | NULL, |
1426 | 14 | HFILL, |
1427 | 14 | }}, |
1428 | 14 | {&hf_sane_option_capability_soft_detect, |
1429 | 14 | { |
1430 | 14 | "Can be detected by software", |
1431 | 14 | "sane.option.soft_detect", |
1432 | 14 | FT_BOOLEAN, |
1433 | 14 | 32, |
1434 | 14 | NULL, |
1435 | 14 | SANE_CAP_SOFT_DETECT, |
1436 | 14 | NULL, |
1437 | 14 | HFILL, |
1438 | 14 | }}, |
1439 | 14 | {&hf_sane_option_capability_emulated, |
1440 | 14 | { |
1441 | 14 | "Emulated in software", |
1442 | 14 | "sane.option.emulated", |
1443 | 14 | FT_BOOLEAN, |
1444 | 14 | 32, |
1445 | 14 | NULL, |
1446 | 14 | SANE_CAP_EMULATED, |
1447 | 14 | NULL, |
1448 | 14 | HFILL, |
1449 | 14 | }}, |
1450 | 14 | {&hf_sane_option_capability_automatic, |
1451 | 14 | { |
1452 | 14 | "Can be set automatically", |
1453 | 14 | "sane.option.automatic", |
1454 | 14 | FT_BOOLEAN, |
1455 | 14 | 32, |
1456 | 14 | NULL, |
1457 | 14 | SANE_CAP_AUTOMATIC, |
1458 | 14 | NULL, |
1459 | 14 | HFILL, |
1460 | 14 | }}, |
1461 | 14 | {&hf_sane_option_capability_inactive, |
1462 | 14 | { |
1463 | 14 | "Inactive", |
1464 | 14 | "sane.option.inactive", |
1465 | 14 | FT_BOOLEAN, |
1466 | 14 | 32, |
1467 | 14 | NULL, |
1468 | 14 | SANE_CAP_INACTIVE, |
1469 | 14 | NULL, |
1470 | 14 | HFILL, |
1471 | 14 | }}, |
1472 | 14 | {&hf_sane_option_capability_advanced, |
1473 | 14 | { |
1474 | 14 | "Advanced option", |
1475 | 14 | "sane.option.advanced", |
1476 | 14 | FT_BOOLEAN, |
1477 | 14 | 32, |
1478 | 14 | NULL, |
1479 | 14 | SANE_CAP_ADVANCED, |
1480 | 14 | NULL, |
1481 | 14 | HFILL, |
1482 | 14 | }}, |
1483 | 14 | {&hf_sane_option_value, |
1484 | 14 | { |
1485 | 14 | "Option value", |
1486 | 14 | "sane.option.value", |
1487 | 14 | FT_NONE, |
1488 | 14 | BASE_NONE, |
1489 | 14 | NULL, |
1490 | 14 | 0, |
1491 | 14 | NULL, |
1492 | 14 | HFILL, |
1493 | 14 | }}, |
1494 | 14 | {&hf_sane_option_string_value, |
1495 | 14 | { |
1496 | 14 | "Option string value", |
1497 | 14 | "sane.option.value.string", |
1498 | 14 | FT_STRING, |
1499 | 14 | BASE_NONE, |
1500 | 14 | NULL, |
1501 | 14 | 0, |
1502 | 14 | NULL, |
1503 | 14 | HFILL, |
1504 | 14 | }}, |
1505 | 14 | {&hf_sane_option_numeric_value, |
1506 | 14 | { |
1507 | 14 | "Option numeric value", |
1508 | 14 | "sane.option.value.numeric", |
1509 | 14 | FT_UINT32, |
1510 | 14 | BASE_HEX, |
1511 | 14 | NULL, |
1512 | 14 | 0, |
1513 | 14 | NULL, |
1514 | 14 | HFILL, |
1515 | 14 | }}, |
1516 | 14 | {&hf_sane_option_boolean_value, |
1517 | 14 | { |
1518 | 14 | "Option boolean value", |
1519 | 14 | "sane.option.value.boolean", |
1520 | 14 | FT_BOOLEAN, |
1521 | 14 | BASE_NONE, |
1522 | 14 | NULL, |
1523 | 14 | 0, |
1524 | 14 | NULL, |
1525 | 14 | HFILL, |
1526 | 14 | }}, |
1527 | 14 | {&hf_sane_option_constraints, |
1528 | 14 | { |
1529 | 14 | "Option constraints", |
1530 | 14 | "sane.option.constraints", |
1531 | 14 | FT_BYTES, |
1532 | 14 | BASE_NONE, |
1533 | 14 | NULL, |
1534 | 14 | 0, |
1535 | 14 | NULL, |
1536 | 14 | HFILL, |
1537 | 14 | }}, |
1538 | 14 | {&hf_sane_option_constraint_type, |
1539 | 14 | { |
1540 | 14 | "Option constraint type", |
1541 | 14 | "sane.option.constraint_type", |
1542 | 14 | FT_UINT32, |
1543 | 14 | BASE_DEC, |
1544 | 14 | VALS(sane_constraint_type_names), |
1545 | 14 | 0, |
1546 | 14 | NULL, |
1547 | 14 | HFILL, |
1548 | 14 | }}, |
1549 | 14 | {&hf_sane_option_possible_string_value, |
1550 | 14 | { |
1551 | 14 | "Possible option string value", |
1552 | 14 | "sane.option.possible_string_value", |
1553 | 14 | FT_STRING, |
1554 | 14 | BASE_NONE, |
1555 | 14 | NULL, |
1556 | 14 | 0, |
1557 | 14 | NULL, |
1558 | 14 | HFILL, |
1559 | 14 | }}, |
1560 | 14 | {&hf_sane_option_possible_word_value, |
1561 | 14 | { |
1562 | 14 | "Possible option word value", |
1563 | 14 | "sane.option.possible_word_value", |
1564 | 14 | FT_UINT32, |
1565 | 14 | BASE_HEX, |
1566 | 14 | NULL, |
1567 | 14 | 0, |
1568 | 14 | NULL, |
1569 | 14 | HFILL, |
1570 | 14 | }}, |
1571 | 14 | {&hf_sane_option_range_min, |
1572 | 14 | { |
1573 | 14 | "Option minimum value", |
1574 | 14 | "sane.option.min_value", |
1575 | 14 | FT_UINT32, |
1576 | 14 | BASE_HEX, |
1577 | 14 | NULL, |
1578 | 14 | 0, |
1579 | 14 | NULL, |
1580 | 14 | HFILL, |
1581 | 14 | }}, |
1582 | 14 | {&hf_sane_option_range_max, |
1583 | 14 | { |
1584 | 14 | "Option maximum value", |
1585 | 14 | "sane.option.max_value", |
1586 | 14 | FT_UINT32, |
1587 | 14 | BASE_HEX, |
1588 | 14 | NULL, |
1589 | 14 | 0, |
1590 | 14 | NULL, |
1591 | 14 | HFILL, |
1592 | 14 | }}, |
1593 | 14 | {&hf_sane_option_range_quant, |
1594 | 14 | { |
1595 | 14 | "Option value quantization", |
1596 | 14 | "sane.option.quant", |
1597 | 14 | FT_UINT32, |
1598 | 14 | BASE_HEX, |
1599 | 14 | NULL, |
1600 | 14 | 0, |
1601 | 14 | NULL, |
1602 | 14 | HFILL, |
1603 | 14 | }}, |
1604 | 14 | {&hf_sane_data_port, |
1605 | 14 | { |
1606 | 14 | "Image data port number", |
1607 | 14 | "sane.data_port", |
1608 | 14 | FT_UINT32, |
1609 | 14 | BASE_DEC, |
1610 | 14 | NULL, |
1611 | 14 | 0, |
1612 | 14 | NULL, |
1613 | 14 | HFILL, |
1614 | 14 | }}, |
1615 | 14 | {&hf_sane_byte_order, |
1616 | 14 | { |
1617 | 14 | "Image data byte order", |
1618 | 14 | "sane.byte_order", |
1619 | 14 | FT_UINT32, |
1620 | 14 | BASE_HEX, |
1621 | 14 | NULL, |
1622 | 14 | 0, |
1623 | 14 | NULL, |
1624 | 14 | HFILL, |
1625 | 14 | }}, |
1626 | 14 | {&hf_sane_frame_format, |
1627 | 14 | { |
1628 | 14 | "Image data frame format", |
1629 | 14 | "sane.scan.frame_format", |
1630 | 14 | FT_UINT32, |
1631 | 14 | BASE_DEC, |
1632 | 14 | VALS(sane_frame_format_names), |
1633 | 14 | 0, |
1634 | 14 | NULL, |
1635 | 14 | HFILL, |
1636 | 14 | }}, |
1637 | 14 | {&hf_sane_scan_line_count, |
1638 | 14 | { |
1639 | 14 | "Image data line count", |
1640 | 14 | "sane.scan.line_count", |
1641 | 14 | FT_UINT32, |
1642 | 14 | BASE_DEC, |
1643 | 14 | NULL, |
1644 | 14 | 0, |
1645 | 14 | NULL, |
1646 | 14 | HFILL, |
1647 | 14 | }}, |
1648 | 14 | {&hf_sane_scan_pixel_depth, |
1649 | 14 | { |
1650 | 14 | "Image data pixel depth", |
1651 | 14 | "sane.scan.pixel_depth", |
1652 | 14 | FT_UINT32, |
1653 | 14 | BASE_DEC, |
1654 | 14 | NULL, |
1655 | 14 | 0, |
1656 | 14 | NULL, |
1657 | 14 | HFILL, |
1658 | 14 | }}, |
1659 | 14 | {&hf_sane_scan_pixels_per_line, |
1660 | 14 | { |
1661 | 14 | "Image data pixels per line", |
1662 | 14 | "sane.scan.pixels_per_line", |
1663 | 14 | FT_UINT32, |
1664 | 14 | BASE_DEC, |
1665 | 14 | NULL, |
1666 | 14 | 0, |
1667 | 14 | NULL, |
1668 | 14 | HFILL, |
1669 | 14 | }}, |
1670 | 14 | {&hf_sane_scan_bytes_per_line, |
1671 | 14 | { |
1672 | 14 | "Image data bytes per line", |
1673 | 14 | "sane.scan.bytes_per_line", |
1674 | 14 | FT_UINT32, |
1675 | 14 | BASE_DEC, |
1676 | 14 | NULL, |
1677 | 14 | 0, |
1678 | 14 | NULL, |
1679 | 14 | HFILL, |
1680 | 14 | }}, |
1681 | 14 | {&hf_sane_scan_is_last_frame, |
1682 | 14 | { |
1683 | 14 | "Is last image data frame", |
1684 | 14 | "sane.scan.last_frame", |
1685 | 14 | FT_BOOLEAN, |
1686 | 14 | BASE_NONE, |
1687 | 14 | NULL, |
1688 | 14 | 0, |
1689 | 14 | NULL, |
1690 | 14 | HFILL, |
1691 | 14 | }}, |
1692 | 14 | {&hf_sane_dummy_value, |
1693 | 14 | { |
1694 | 14 | "Dummy value", |
1695 | 14 | "sane.dummy_value", |
1696 | 14 | FT_UINT32, |
1697 | 14 | BASE_DEC, |
1698 | 14 | NULL, |
1699 | 14 | 0, |
1700 | 14 | NULL, |
1701 | 14 | HFILL, |
1702 | 14 | }}, |
1703 | 14 | {&hf_sane_control_option_info, |
1704 | 14 | { |
1705 | 14 | "Control option info", |
1706 | 14 | "sane.control_option.info", |
1707 | 14 | FT_UINT32, |
1708 | 14 | BASE_HEX, |
1709 | 14 | NULL, |
1710 | 14 | 0, |
1711 | 14 | NULL, |
1712 | 14 | HFILL, |
1713 | 14 | }}, |
1714 | 14 | {&hf_sane_control_option_inexact, |
1715 | 14 | { |
1716 | 14 | "Inexact value selected", |
1717 | 14 | "sane.control_option.info.inexact", |
1718 | 14 | FT_BOOLEAN, |
1719 | 14 | 32, |
1720 | 14 | NULL, |
1721 | 14 | SANE_INFO_INEXACT, |
1722 | 14 | NULL, |
1723 | 14 | HFILL, |
1724 | 14 | }}, |
1725 | 14 | {&hf_sane_control_option_reload_options, |
1726 | 14 | { |
1727 | 14 | "Client should reload options", |
1728 | 14 | "sane.control_option.info.reload_options", |
1729 | 14 | FT_BOOLEAN, |
1730 | 14 | 32, |
1731 | 14 | NULL, |
1732 | 14 | SANE_INFO_RELOAD_OPTIONS, |
1733 | 14 | NULL, |
1734 | 14 | HFILL, |
1735 | 14 | }}, |
1736 | 14 | {&hf_sane_control_option_reload_params, |
1737 | 14 | { |
1738 | 14 | "Client should reload scan parameters", |
1739 | 14 | "sane.control_option.info.reload_params", |
1740 | 14 | FT_BOOLEAN, |
1741 | 14 | 32, |
1742 | 14 | NULL, |
1743 | 14 | SANE_INFO_RELOAD_PARAMS, |
1744 | 14 | NULL, |
1745 | 14 | HFILL, |
1746 | 14 | }}, |
1747 | 14 | }; |
1748 | | |
1749 | | |
1750 | 14 | static int *ett[] = { |
1751 | 14 | &ett_sane, |
1752 | 14 | &ett_sane_version, |
1753 | 14 | &ett_sane_string, |
1754 | 14 | &ett_sane_option, |
1755 | 14 | &ett_sane_option_value, |
1756 | 14 | &ett_sane_option_capabilities, |
1757 | 14 | &ett_sane_option_constraints, |
1758 | 14 | &ett_sane_control_option_info, |
1759 | 14 | &ett_sane_device_descriptor, |
1760 | 14 | }; |
1761 | | |
1762 | 14 | module_t *sane_module; |
1763 | | |
1764 | 14 | proto_sane = proto_register_protocol("Scanner Access Now Easy", "SANE", "sane"); |
1765 | 14 | proto_register_field_array(proto_sane, hf, array_length(hf)); |
1766 | 14 | proto_register_subtree_array(ett, array_length(ett)); |
1767 | | |
1768 | 14 | register_dissector(SANE_MODULE_NAME, dissect_sane, proto_sane); |
1769 | | |
1770 | | /* |
1771 | | * XXX - Required to be notified of server port changes, |
1772 | | * while no other preferences are registered. |
1773 | | */ |
1774 | 14 | sane_module = prefs_register_protocol(proto_sane, apply_sane_prefs); |
1775 | 14 | (void)sane_module; |
1776 | 14 | } |
1777 | | |
1778 | | void |
1779 | 14 | proto_reg_handoff_sane(void) { |
1780 | 14 | sane_handle = create_dissector_handle(dissect_sane, proto_sane); |
1781 | 14 | dissector_add_uint_range_with_preference("tcp.port", SANE_PORT, sane_handle); |
1782 | 14 | apply_sane_prefs(); |
1783 | 14 | } |
1784 | | |
1785 | | /* |
1786 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
1787 | | * |
1788 | | * Local variables: |
1789 | | * c-basic-offset: 4 |
1790 | | * tab-width: 8 |
1791 | | * indent-tabs-mode: nil |
1792 | | * End: |
1793 | | * |
1794 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
1795 | | * :indentSize=4:tabSize=8:noTabs=true: |
1796 | | */ |