/src/wireshark/epan/dissectors/packet-adb.c
Line | Count | Source |
1 | | /* packet-adb.c |
2 | | * Routines for Android Debug Bridge Transport Protocol |
3 | | * |
4 | | * Copyright 2014 Michal Labedzki for Tieto Corporation |
5 | | * |
6 | | * Wireshark - Network traffic analyzer |
7 | | * By Gerald Combs <gerald@wireshark.org> |
8 | | * Copyright 1998 Gerald Combs |
9 | | * |
10 | | * SPDX-License-Identifier: GPL-2.0-or-later |
11 | | */ |
12 | | |
13 | | #include "config.h" |
14 | | |
15 | | #include <epan/packet.h> |
16 | | #include <epan/prefs.h> |
17 | | #include <epan/expert.h> |
18 | | #include <epan/tfs.h> |
19 | | |
20 | | #include <wiretap/wtap.h> |
21 | | |
22 | | #include "packet-adb_service.h" |
23 | | #include "packet-usb.h" |
24 | | |
25 | | static int proto_adb; |
26 | | static int hf_command; |
27 | | static int hf_argument_0; |
28 | | static int hf_argument_1; |
29 | | static int hf_data_length; |
30 | | static int hf_data_crc32; |
31 | | static int hf_magic; |
32 | | static int hf_local_id; |
33 | | static int hf_remote_id; |
34 | | static int hf_version; |
35 | | static int hf_max_data; |
36 | | static int hf_zero; |
37 | | static int hf_sequence; |
38 | | static int hf_online; |
39 | | static int hf_auth_type; |
40 | | static int hf_data; |
41 | | static int hf_service; |
42 | | static int hf_data_fragment; |
43 | | static int hf_command_in_frame; |
44 | | static int hf_completed_in_frame; |
45 | | static int hf_service_start_in_frame; |
46 | | static int hf_close_local_in_frame; |
47 | | static int hf_close_remote_in_frame; |
48 | | static int hf_connection_info; |
49 | | |
50 | | static int ett_adb; |
51 | | static int ett_adb_arg0; |
52 | | static int ett_adb_arg1; |
53 | | static int ett_adb_crc; |
54 | | static int ett_adb_magic; |
55 | | |
56 | | static expert_field ei_invalid_magic; |
57 | | static expert_field ei_invalid_crc; |
58 | | static expert_field ei_invalid_data; |
59 | | |
60 | | static dissector_handle_t adb_handle; |
61 | | static dissector_handle_t adb_service_handle; |
62 | | |
63 | | static int proto_tcp; |
64 | | static int proto_usb; |
65 | | |
66 | | static wmem_tree_t *command_info; |
67 | | static wmem_tree_t *service_info; |
68 | | |
69 | | typedef struct service_data_t { |
70 | | uint32_t start_in_frame; |
71 | | |
72 | | uint32_t close_local_in_frame; |
73 | | uint32_t close_remote_in_frame; |
74 | | |
75 | | uint32_t local_id; |
76 | | uint32_t remote_id; |
77 | | |
78 | | const char *service; |
79 | | } service_data_t; |
80 | | |
81 | | typedef struct command_data_t { |
82 | | uint32_t command; |
83 | | |
84 | | uint32_t command_in_frame; |
85 | | uint32_t response_in_frame; |
86 | | |
87 | | uint32_t arg0; |
88 | | uint32_t arg1; |
89 | | |
90 | | uint32_t data_length; |
91 | | uint32_t crc32; |
92 | | |
93 | | uint32_t completed_in_frame; |
94 | | uint32_t reassemble_data_length; |
95 | | uint8_t *reassemble_data; |
96 | | uint32_t reassemble_error_in_frame; |
97 | | } command_data_t; |
98 | | |
99 | | static uint32_t max_in_frame = UINT32_MAX; |
100 | | |
101 | | static const value_string command_vals[] = { |
102 | | { 0x434e5953, "Synchronize" }, |
103 | | { 0x45534c43, "Close" }, |
104 | | { 0x45545257, "Write" }, |
105 | | { 0x48545541, "Authenticate" }, |
106 | | { 0x4e584e43, "Connect" }, |
107 | | { 0x4e45504f, "Open" }, |
108 | | { 0x59414b4f, "Okay" }, |
109 | | { 0, NULL } |
110 | | }; |
111 | | |
112 | | static const value_string magic_vals[] = { |
113 | | { 0xFFFFFFFF ^ 0x434e5953, "Synchronize" }, |
114 | | { 0xFFFFFFFF ^ 0x45534c43, "Close" }, |
115 | | { 0xFFFFFFFF ^ 0x45545257, "Write" }, |
116 | | { 0xFFFFFFFF ^ 0x48545541, "Authenticate" }, |
117 | | { 0xFFFFFFFF ^ 0x4e584e43, "Connect" }, |
118 | | { 0xFFFFFFFF ^ 0x4e45504f, "Open" }, |
119 | | { 0xFFFFFFFF ^ 0x59414b4f, "Okay" }, |
120 | | { 0, NULL } |
121 | | }; |
122 | | |
123 | | static const value_string auth_type_vals[] = { |
124 | | { 1, "Token" }, |
125 | | { 2, "Signature" }, |
126 | | { 3, "RSA Public Key" }, |
127 | | { 0, NULL } |
128 | | }; |
129 | | |
130 | 0 | #define A_SYNC 0x434e5953 |
131 | 0 | #define A_CLSE 0x45534c43 |
132 | 0 | #define A_WRTE 0x45545257 |
133 | 0 | #define A_AUTH 0x48545541 |
134 | 0 | #define A_CNXN 0x4e584e43 |
135 | 0 | #define A_OPEN 0x4e45504f |
136 | 0 | #define A_OKAY 0x59414b4f |
137 | | |
138 | 0 | #define ADB_TCP_PORT 5555 |
139 | | |
140 | | void proto_register_adb(void); |
141 | | void proto_reg_handoff_adb(void); |
142 | | |
143 | | static void |
144 | | save_command(uint32_t cmd, uint32_t arg0, uint32_t arg1, uint32_t data_length, |
145 | | uint32_t crc32, service_data_t *service_data, int proto, void *data, |
146 | | packet_info *pinfo, service_data_t **returned_service_data, |
147 | | command_data_t **returned_command_data) |
148 | 0 | { |
149 | 0 | wmem_tree_key_t key[6]; |
150 | 0 | uint32_t interface_id; |
151 | 0 | uint32_t bus_id; |
152 | 0 | uint32_t device_address; |
153 | 0 | uint32_t side_id; |
154 | 0 | uint32_t frame_number; |
155 | 0 | command_data_t *command_data; |
156 | 0 | wmem_tree_t *wmem_tree; |
157 | 0 | int direction = P2P_DIR_UNKNOWN; |
158 | 0 | urb_info_t *urb = (urb_info_t *) data; |
159 | |
|
160 | 0 | frame_number = pinfo->num; |
161 | |
|
162 | 0 | if (pinfo->rec->presence_flags & WTAP_HAS_INTERFACE_ID) |
163 | 0 | interface_id = pinfo->rec->rec_header.packet_header.interface_id; |
164 | 0 | else |
165 | 0 | interface_id = 0; |
166 | |
|
167 | 0 | if (proto == proto_usb) { |
168 | 0 | DISSECTOR_ASSERT(urb); |
169 | |
|
170 | 0 | direction = urb->direction; |
171 | |
|
172 | 0 | bus_id = urb->bus_id; |
173 | 0 | device_address = urb->device_address; |
174 | |
|
175 | 0 | key[0].length = 1; |
176 | 0 | key[0].key = &interface_id; |
177 | 0 | key[1].length = 1; |
178 | 0 | key[1].key = &bus_id; |
179 | 0 | key[2].length = 1; |
180 | 0 | key[2].key = &device_address; |
181 | 0 | key[3].length = 1; |
182 | 0 | key[3].key = &side_id; |
183 | 0 | key[4].length = 1; |
184 | 0 | key[4].key = &frame_number; |
185 | 0 | key[5].length = 0; |
186 | 0 | key[5].key = NULL; |
187 | 0 | } else { /* tcp */ |
188 | 0 | if (pinfo->destport == ADB_TCP_PORT) |
189 | 0 | direction = P2P_DIR_SENT; |
190 | 0 | else |
191 | 0 | direction = P2P_DIR_RECV; |
192 | |
|
193 | 0 | key[0].length = 1; |
194 | 0 | key[0].key = &interface_id; |
195 | 0 | key[1].length = 1; |
196 | 0 | key[2].length = 1; |
197 | 0 | if (direction == P2P_DIR_SENT) { |
198 | 0 | key[1].key = &pinfo->srcport; |
199 | 0 | key[2].key = &pinfo->destport; |
200 | 0 | } else { |
201 | 0 | key[1].key = &pinfo->destport; |
202 | 0 | key[2].key = &pinfo->srcport; |
203 | 0 | } |
204 | 0 | key[3].length = 1; |
205 | 0 | key[3].key = &side_id; |
206 | 0 | key[4].length = 1; |
207 | 0 | key[4].key = &frame_number; |
208 | 0 | key[5].length = 0; |
209 | 0 | key[5].key = NULL; |
210 | 0 | } |
211 | |
|
212 | 0 | if (direction == P2P_DIR_SENT) |
213 | 0 | if (cmd == A_CLSE) |
214 | 0 | side_id = arg1; /* OUT: local id */ |
215 | 0 | else |
216 | 0 | side_id = arg0; /* OUT: local id */ |
217 | 0 | else |
218 | 0 | side_id = arg1; /* IN: remote id */ |
219 | |
|
220 | 0 | if (cmd == A_OPEN) { |
221 | 0 | service_data = wmem_new(wmem_file_scope(), service_data_t); |
222 | |
|
223 | 0 | service_data->start_in_frame = pinfo->num; |
224 | 0 | service_data->close_local_in_frame = max_in_frame; |
225 | 0 | service_data->close_remote_in_frame = max_in_frame; |
226 | |
|
227 | 0 | service_data->local_id = arg0; |
228 | 0 | service_data->remote_id = arg1; |
229 | |
|
230 | 0 | service_data->service = "unknown"; |
231 | |
|
232 | 0 | wmem_tree_insert32_array(service_info, key, service_data); |
233 | 0 | } |
234 | |
|
235 | 0 | command_data = wmem_new(wmem_file_scope(), command_data_t); |
236 | |
|
237 | 0 | command_data->command = cmd; |
238 | 0 | command_data->arg0 = arg0; |
239 | 0 | command_data->arg1 = arg1; |
240 | |
|
241 | 0 | command_data->command_in_frame = pinfo->num; |
242 | 0 | command_data->response_in_frame = max_in_frame; |
243 | |
|
244 | 0 | command_data->crc32 = crc32; |
245 | 0 | command_data->data_length = data_length; |
246 | 0 | if (data_length == 0) |
247 | 0 | command_data->completed_in_frame = pinfo->num; |
248 | 0 | else |
249 | 0 | command_data->completed_in_frame = max_in_frame; |
250 | 0 | command_data->reassemble_data_length = 0; |
251 | | /* Lazy allocation: allocate only when payload bytes are actually copied. */ |
252 | 0 | command_data->reassemble_data = NULL; |
253 | 0 | command_data->reassemble_error_in_frame = 0; |
254 | |
|
255 | 0 | key[3].length = 1; |
256 | 0 | key[3].key = &frame_number; |
257 | 0 | key[4].length = 0; |
258 | 0 | key[4].key = NULL; |
259 | 0 | wmem_tree_insert32_array(command_info, key, command_data); |
260 | | |
261 | |
|
262 | 0 | if (direction == P2P_DIR_SENT) |
263 | 0 | if (command_data->command == A_CLSE) |
264 | 0 | side_id = command_data->arg1; /* OUT: local id */ |
265 | 0 | else |
266 | 0 | side_id = command_data->arg0; /* OUT: local id */ |
267 | 0 | else |
268 | 0 | side_id = command_data->arg1; /* IN: remote id */ |
269 | |
|
270 | 0 | key[3].length = 1; |
271 | 0 | key[3].key = &side_id; |
272 | 0 | key[4].length = 0; |
273 | 0 | key[4].key = NULL; |
274 | |
|
275 | 0 | wmem_tree = (wmem_tree_t *) wmem_tree_lookup32_array(service_info, key); |
276 | 0 | if (wmem_tree) { |
277 | 0 | service_data = (service_data_t *) wmem_tree_lookup32_le(wmem_tree, frame_number); |
278 | 0 | } |
279 | |
|
280 | 0 | if (cmd == A_OKAY) { |
281 | 0 | if (!service_data) { |
282 | 0 | if (direction == P2P_DIR_SENT) |
283 | 0 | side_id = command_data->arg0; /* OUT: local id */ |
284 | 0 | else |
285 | 0 | side_id = command_data->arg1; /* IN: remote id */ |
286 | |
|
287 | 0 | wmem_tree = (wmem_tree_t *) wmem_tree_lookup32_array(service_info, key); |
288 | 0 | if (wmem_tree) { |
289 | 0 | service_data = (service_data_t *) wmem_tree_lookup32_le(wmem_tree, frame_number); |
290 | 0 | } |
291 | 0 | } |
292 | |
|
293 | 0 | if (service_data && service_data->remote_id == 0 && direction == P2P_DIR_RECV) { |
294 | 0 | if (direction == P2P_DIR_SENT) { |
295 | 0 | service_data->remote_id = arg1; |
296 | 0 | } else { |
297 | 0 | service_data->remote_id = arg0; |
298 | 0 | } |
299 | |
|
300 | 0 | side_id = service_data->remote_id; |
301 | |
|
302 | 0 | key[4].length = 1; |
303 | 0 | key[4].key = &frame_number; |
304 | 0 | key[5].length = 0; |
305 | 0 | key[5].key = NULL; |
306 | |
|
307 | 0 | wmem_tree_insert32_array(service_info, key, service_data); |
308 | 0 | } |
309 | 0 | } else if (cmd == A_CLSE) { |
310 | 0 | if (service_data) { |
311 | 0 | if (direction == P2P_DIR_RECV && service_data->local_id == arg1) |
312 | 0 | service_data->close_local_in_frame = pinfo->num; |
313 | 0 | else if (direction == P2P_DIR_SENT && service_data->remote_id == arg1) |
314 | 0 | service_data->close_remote_in_frame = pinfo->num; |
315 | 0 | } |
316 | 0 | } |
317 | |
|
318 | 0 | DISSECTOR_ASSERT(returned_service_data && returned_command_data); |
319 | 0 | *returned_service_data = service_data; |
320 | 0 | *returned_command_data = command_data; |
321 | 0 | } |
322 | | |
323 | | static int |
324 | | dissect_adb(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
325 | 0 | { |
326 | 0 | proto_item *main_item; |
327 | 0 | proto_tree *main_tree; |
328 | 0 | proto_item *arg0_item; |
329 | 0 | proto_tree *arg0_tree; |
330 | 0 | proto_item *arg1_item; |
331 | 0 | proto_tree *arg1_tree; |
332 | 0 | proto_item *magic_item; |
333 | 0 | proto_item *crc_item; |
334 | 0 | proto_tree *crc_tree = NULL; |
335 | 0 | proto_item *sub_item; |
336 | 0 | unsigned offset = 0; |
337 | 0 | uint32_t command; |
338 | 0 | uint32_t arg0; |
339 | 0 | uint32_t arg1; |
340 | 0 | uint32_t data_length = 0; |
341 | 0 | uint32_t crc32 = 0; |
342 | 0 | urb_info_t *urb = NULL; |
343 | 0 | wmem_tree_key_t key[5]; |
344 | 0 | uint32_t interface_id; |
345 | 0 | uint32_t bus_id; |
346 | 0 | uint32_t device_address; |
347 | 0 | uint32_t side_id; |
348 | 0 | uint32_t frame_number; |
349 | 0 | bool is_command = true; |
350 | 0 | bool is_next_fragment = false; |
351 | 0 | bool is_service = false; |
352 | 0 | int proto; |
353 | 0 | int direction = P2P_DIR_UNKNOWN; |
354 | 0 | wmem_tree_t *wmem_tree; |
355 | 0 | command_data_t *command_data = NULL; |
356 | 0 | service_data_t *service_data = NULL; |
357 | |
|
358 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "ADB"); |
359 | 0 | col_clear(pinfo->cinfo, COL_INFO); |
360 | |
|
361 | 0 | main_item = proto_tree_add_item(tree, proto_adb, tvb, offset, -1, ENC_NA); |
362 | 0 | main_tree = proto_item_add_subtree(main_item, ett_adb); |
363 | |
|
364 | 0 | frame_number = pinfo->num; |
365 | | |
366 | | /* XXX: Why? If interface is USB only first try is correct |
367 | | * (and seems strange...), in other cases standard check for |
368 | | * previous protocol is correct */ |
369 | 0 | proto = (int) GPOINTER_TO_INT(wmem_list_frame_data(/*wmem_list_frame_prev*/(wmem_list_tail(pinfo->layers)))); |
370 | 0 | if (proto != proto_usb) { |
371 | 0 | proto = (int) GPOINTER_TO_INT(wmem_list_frame_data(wmem_list_frame_prev(wmem_list_tail(pinfo->layers)))); |
372 | 0 | } |
373 | |
|
374 | 0 | if (proto == proto_usb) { |
375 | 0 | urb = (urb_info_t *) data; |
376 | 0 | DISSECTOR_ASSERT(urb); |
377 | |
|
378 | 0 | direction = urb->direction; |
379 | 0 | } else if (proto == proto_tcp) { |
380 | 0 | if (pinfo->destport == ADB_TCP_PORT) |
381 | 0 | direction = P2P_DIR_SENT; |
382 | 0 | else |
383 | 0 | direction = P2P_DIR_RECV; |
384 | 0 | } else { |
385 | 0 | return offset; |
386 | 0 | } |
387 | | |
388 | 0 | if (pinfo->rec->presence_flags & WTAP_HAS_INTERFACE_ID) |
389 | 0 | interface_id = pinfo->rec->rec_header.packet_header.interface_id; |
390 | 0 | else |
391 | 0 | interface_id = 0; |
392 | |
|
393 | 0 | if (proto == proto_usb) { |
394 | 0 | bus_id = urb->bus_id; |
395 | 0 | device_address = urb->device_address; |
396 | |
|
397 | 0 | key[0].length = 1; |
398 | 0 | key[0].key = &interface_id; |
399 | 0 | key[1].length = 1; |
400 | 0 | key[1].key = &bus_id; |
401 | 0 | key[2].length = 1; |
402 | 0 | key[2].key = &device_address; |
403 | 0 | key[3].length = 0; |
404 | 0 | key[3].key = NULL; |
405 | 0 | } else { /* tcp */ |
406 | 0 | key[0].length = 1; |
407 | 0 | key[0].key = &interface_id; |
408 | 0 | key[1].length = 1; |
409 | 0 | key[2].length = 1; |
410 | 0 | if (direction == P2P_DIR_SENT) { |
411 | 0 | key[1].key = &pinfo->srcport; |
412 | 0 | key[2].key = &pinfo->destport; |
413 | 0 | } else { |
414 | 0 | key[1].key = &pinfo->destport; |
415 | 0 | key[2].key = &pinfo->srcport; |
416 | 0 | } |
417 | 0 | key[3].length = 0; |
418 | 0 | key[3].key = NULL; |
419 | 0 | } |
420 | |
|
421 | 0 | wmem_tree = (wmem_tree_t *) wmem_tree_lookup32_array(command_info, key); |
422 | 0 | if (wmem_tree) { |
423 | 0 | command_data = (command_data_t *) wmem_tree_lookup32_le(wmem_tree, frame_number); |
424 | 0 | if (command_data && command_data->completed_in_frame >= frame_number && |
425 | 0 | command_data->command_in_frame <= frame_number) { |
426 | |
|
427 | 0 | if (command_data->command_in_frame != frame_number) { |
428 | 0 | is_command = false; |
429 | 0 | is_next_fragment = true; |
430 | 0 | } |
431 | |
|
432 | 0 | data_length = command_data->data_length; |
433 | 0 | crc32 = command_data->crc32; |
434 | |
|
435 | 0 | if (direction == P2P_DIR_SENT) { |
436 | 0 | if (command_data->command == A_CLSE) |
437 | 0 | side_id = command_data->arg1; /* OUT: local id */ |
438 | 0 | else |
439 | 0 | side_id = command_data->arg0; /* OUT: local id */ |
440 | 0 | } else { |
441 | 0 | side_id = command_data->arg1; /* IN: remote id */ |
442 | 0 | } |
443 | |
|
444 | 0 | key[3].length = 1; |
445 | 0 | key[3].key = &side_id; |
446 | 0 | key[4].length = 0; |
447 | 0 | key[4].key = NULL; |
448 | |
|
449 | 0 | wmem_tree = (wmem_tree_t *) wmem_tree_lookup32_array(service_info, key); |
450 | 0 | if (wmem_tree) { |
451 | 0 | service_data = (service_data_t *) wmem_tree_lookup32_le(wmem_tree, frame_number); |
452 | 0 | if (service_data && command_data->command == A_OPEN) { |
453 | 0 | is_service = true; |
454 | 0 | } |
455 | 0 | } |
456 | 0 | } |
457 | 0 | } |
458 | | |
459 | | /* Simple heuristics to check if packet is command or data */ |
460 | 0 | if ((command_data && command_data->completed_in_frame <= frame_number) || !command_data) { |
461 | 0 | if (tvb_reported_length(tvb) < 24) { |
462 | 0 | is_command = false; |
463 | 0 | } else if (tvb_reported_length(tvb) >= 24) { |
464 | 0 | command = tvb_get_letohl(tvb, offset); |
465 | |
|
466 | 0 | if (command != A_SYNC && command != A_CLSE && command != A_WRTE && |
467 | 0 | command != A_AUTH && command != A_CNXN && command != A_OPEN && command != A_OKAY) |
468 | 0 | is_command = false; |
469 | 0 | else if (command != (0xFFFFFFFF ^ tvb_get_letohl(tvb, offset + 20))) |
470 | 0 | is_command = false; |
471 | |
|
472 | 0 | if (is_command) { |
473 | 0 | data_length = tvb_get_letohl(tvb, offset + 12); |
474 | 0 | crc32 = tvb_get_letohl(tvb, offset + 16); |
475 | 0 | } |
476 | 0 | if (command == A_OPEN) is_service = true; |
477 | 0 | } |
478 | 0 | } |
479 | |
|
480 | 0 | if (service_data && !(command_data->command == A_OPEN && is_next_fragment)) { |
481 | 0 | sub_item = proto_tree_add_string(main_tree, hf_service, tvb, offset, 0, service_data->service); |
482 | 0 | proto_item_set_generated(sub_item); |
483 | 0 | } |
484 | |
|
485 | 0 | if (service_data) { |
486 | 0 | sub_item = proto_tree_add_uint(main_tree, hf_service_start_in_frame, tvb, offset, 0, service_data->start_in_frame); |
487 | 0 | proto_item_set_generated(sub_item); |
488 | |
|
489 | 0 | if (service_data->close_local_in_frame < max_in_frame) { |
490 | 0 | sub_item = proto_tree_add_uint(main_tree, hf_close_local_in_frame, tvb, offset, 0, service_data->close_local_in_frame); |
491 | 0 | proto_item_set_generated(sub_item); |
492 | 0 | } |
493 | |
|
494 | 0 | if (service_data->close_remote_in_frame < max_in_frame) { |
495 | 0 | sub_item = proto_tree_add_uint(main_tree, hf_close_remote_in_frame, tvb, offset, 0, service_data->close_remote_in_frame); |
496 | 0 | proto_item_set_generated(sub_item); |
497 | 0 | } |
498 | 0 | } |
499 | |
|
500 | 0 | if (is_command) { |
501 | 0 | proto_tree_add_item_ret_uint(main_tree, hf_command, tvb, offset, 4, ENC_LITTLE_ENDIAN, &command); |
502 | 0 | offset += 4; |
503 | |
|
504 | 0 | col_append_str(pinfo->cinfo, COL_INFO, val_to_str_const(command, command_vals, "Unknown command")); |
505 | |
|
506 | 0 | arg0_item = proto_tree_add_item(main_tree, hf_argument_0, tvb, offset, 4, ENC_LITTLE_ENDIAN); |
507 | 0 | arg0_tree = proto_item_add_subtree(arg0_item, ett_adb_arg0); |
508 | 0 | arg0 = tvb_get_letohl(tvb, offset); |
509 | 0 | offset += 4; |
510 | |
|
511 | 0 | arg1_item = proto_tree_add_item(main_tree, hf_argument_1, tvb, offset, 4, ENC_LITTLE_ENDIAN); |
512 | 0 | arg1_tree = proto_item_add_subtree(arg1_item, ett_adb_arg1); |
513 | 0 | arg1 = tvb_get_letohl(tvb, offset); |
514 | 0 | offset += 4; |
515 | |
|
516 | 0 | switch (command) { |
517 | 0 | case A_CNXN: |
518 | 0 | proto_tree_add_item(arg0_tree, hf_version, tvb, offset - 8, 4, ENC_LITTLE_ENDIAN); |
519 | 0 | proto_tree_add_item(arg1_tree, hf_max_data, tvb, offset - 4, 4, ENC_LITTLE_ENDIAN); |
520 | |
|
521 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "(version=%u.%u.%u, max_data=%u)", tvb_get_uint8(tvb, offset - 5), tvb_get_uint8(tvb, offset - 6), tvb_get_letohs(tvb, offset - 7), tvb_get_letohl(tvb, offset - 4)); |
522 | 0 | break; |
523 | 0 | case A_AUTH: |
524 | 0 | proto_tree_add_item(arg0_tree, hf_auth_type, tvb, offset - 8, 4, ENC_LITTLE_ENDIAN); |
525 | 0 | proto_tree_add_item(arg1_tree, hf_zero, tvb, offset - 4, 4, ENC_LITTLE_ENDIAN); |
526 | |
|
527 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "(type=%s, 0)", val_to_str_const(tvb_get_letohl(tvb, offset - 8), auth_type_vals, "Unknown")); |
528 | 0 | break; |
529 | 0 | case A_OPEN: |
530 | 0 | proto_tree_add_item(arg0_tree, hf_local_id, tvb, offset - 8, 4, ENC_LITTLE_ENDIAN); |
531 | 0 | proto_tree_add_item(arg1_tree, hf_zero, tvb, offset - 4, 4, ENC_LITTLE_ENDIAN); |
532 | |
|
533 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "(local=%u, 0)", tvb_get_letohl(tvb, offset - 8)); |
534 | 0 | break; |
535 | 0 | case A_WRTE: |
536 | 0 | proto_tree_add_item(arg0_tree, hf_local_id, tvb, offset - 8, 4, ENC_LITTLE_ENDIAN); |
537 | 0 | proto_tree_add_item(arg1_tree, hf_remote_id, tvb, offset - 4, 4, ENC_LITTLE_ENDIAN); |
538 | |
|
539 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "(local=%u, remote=%u)", arg0, arg1); |
540 | 0 | break; |
541 | 0 | case A_CLSE: |
542 | 0 | case A_OKAY: |
543 | 0 | proto_tree_add_item(arg0_tree, hf_local_id, tvb, offset - 8, 4, ENC_LITTLE_ENDIAN); |
544 | 0 | proto_tree_add_item(arg1_tree, hf_remote_id, tvb, offset - 4, 4, ENC_LITTLE_ENDIAN); |
545 | |
|
546 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "(local=%u, remote=%u)", tvb_get_letohl(tvb, offset - 8), tvb_get_letohl(tvb, offset - 4)); |
547 | 0 | break; |
548 | 0 | case A_SYNC: |
549 | 0 | proto_tree_add_item(arg0_tree, hf_online, tvb, offset - 8, 4, ENC_LITTLE_ENDIAN); |
550 | 0 | proto_tree_add_item(arg1_tree, hf_sequence, tvb, offset - 4, 4, ENC_LITTLE_ENDIAN); |
551 | |
|
552 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "(online=%s, sequence=%u)", tvb_get_letohl(tvb, offset - 8) ? "Yes": "No", tvb_get_letohl(tvb, offset - 4)); |
553 | 0 | break; |
554 | 0 | } |
555 | | |
556 | 0 | proto_tree_add_item(main_tree, hf_data_length, tvb, offset, 4, ENC_LITTLE_ENDIAN); |
557 | 0 | offset += 4; |
558 | |
|
559 | 0 | if (data_length > 0) |
560 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, " length=%u ", data_length); |
561 | |
|
562 | 0 | crc_item = proto_tree_add_item(main_tree, hf_data_crc32, tvb, offset, 4, ENC_LITTLE_ENDIAN); |
563 | 0 | crc_tree = proto_item_add_subtree(crc_item, ett_adb_crc); |
564 | 0 | crc32 = tvb_get_letohl(tvb, offset); |
565 | 0 | offset += 4; |
566 | |
|
567 | 0 | magic_item = proto_tree_add_item(main_tree, hf_magic, tvb, offset, 4, ENC_LITTLE_ENDIAN); |
568 | 0 | if ((tvb_get_letohl(tvb, offset) ^ 0xFFFFFFFF) != command) { |
569 | 0 | proto_tree *expert_tree; |
570 | |
|
571 | 0 | expert_tree = proto_item_add_subtree(magic_item, ett_adb_magic); |
572 | 0 | proto_tree_add_expert(expert_tree, pinfo, &ei_invalid_magic, tvb, offset, 4); |
573 | 0 | } |
574 | |
|
575 | 0 | if (!pinfo->fd->visited) |
576 | 0 | save_command(command, arg0, arg1, data_length, crc32, service_data, proto, data, pinfo, &service_data, &command_data); |
577 | 0 | offset += 4; |
578 | 0 | } |
579 | 0 | if (!pinfo->fd->visited && command_data) { |
580 | 0 | if (command_data->command_in_frame != frame_number) { |
581 | 0 | is_command = false; |
582 | 0 | is_next_fragment = true; |
583 | 0 | } |
584 | |
|
585 | 0 | data_length = command_data->data_length; |
586 | 0 | crc32 = command_data->crc32; |
587 | |
|
588 | 0 | if ((command_data->command_in_frame != frame_number && tvb_captured_length(tvb) == data_length) || |
589 | 0 | (command_data->command_in_frame == frame_number && tvb_captured_length(tvb) == data_length + 24) |
590 | 0 | ) { |
591 | 0 | command_data->reassemble_data_length = command_data->data_length; |
592 | 0 | command_data->completed_in_frame = frame_number; |
593 | 0 | } |
594 | 0 | } |
595 | |
|
596 | 0 | if (is_next_fragment && command_data) { |
597 | 0 | sub_item = proto_tree_add_uint(main_tree, hf_command_in_frame, tvb, offset, 0, command_data->command_in_frame); |
598 | 0 | proto_item_set_generated(sub_item); |
599 | |
|
600 | 0 | sub_item = proto_tree_add_uint(main_tree, hf_command, tvb, offset, 0, command_data->command); |
601 | 0 | proto_item_set_generated(sub_item); |
602 | |
|
603 | 0 | sub_item = proto_tree_add_uint(main_tree, hf_data_length, tvb, offset, 0, command_data->data_length); |
604 | 0 | proto_item_set_generated(sub_item); |
605 | |
|
606 | 0 | crc_item = proto_tree_add_uint(main_tree, hf_data_crc32, tvb, offset, 0, command_data->crc32); |
607 | 0 | crc_tree = proto_item_add_subtree(crc_item, ett_adb_crc); |
608 | 0 | proto_item_set_generated(crc_item); |
609 | 0 | } |
610 | |
|
611 | 0 | if (command_data && command_data->completed_in_frame != frame_number) { |
612 | 0 | sub_item = proto_tree_add_uint(main_tree, hf_completed_in_frame, tvb, offset, 0, command_data->completed_in_frame); |
613 | 0 | proto_item_set_generated(sub_item); |
614 | 0 | } |
615 | | |
616 | | |
617 | |
|
618 | 0 | if (tvb_captured_length_remaining(tvb, offset) > 0 && (!is_command || data_length > 0)) { |
619 | 0 | uint32_t crc = 0; |
620 | 0 | uint32_t i_offset; |
621 | 0 | uint32_t reported_payload_remaining = (uint32_t)tvb_reported_length_remaining(tvb, offset); |
622 | | |
623 | | /* First pass: store message payload (usually a single packet, but |
624 | | * potentially multiple fragments). */ |
625 | 0 | if (!pinfo->fd->visited && command_data && command_data->reassemble_data_length < command_data->data_length) { |
626 | 0 | unsigned chunklen = tvb_captured_length_remaining(tvb, offset); |
627 | 0 | uint32_t remaining = command_data->data_length - command_data->reassemble_data_length; |
628 | |
|
629 | 0 | if (chunklen > remaining) { |
630 | 0 | chunklen = remaining; |
631 | | /* Payload mismatch detected - mark reassembly error */ |
632 | 0 | command_data->reassemble_error_in_frame = frame_number; |
633 | 0 | } |
634 | |
|
635 | 0 | command_data->reassemble_data = (uint8_t *) wmem_realloc(wmem_file_scope(), |
636 | 0 | command_data->reassemble_data, |
637 | 0 | command_data->reassemble_data_length + chunklen); |
638 | |
|
639 | 0 | tvb_memcpy(tvb, command_data->reassemble_data + command_data->reassemble_data_length, offset, chunklen); |
640 | 0 | command_data->reassemble_data_length += chunklen; |
641 | |
|
642 | 0 | if (command_data->reassemble_data_length >= command_data->data_length) |
643 | 0 | command_data->completed_in_frame = frame_number; |
644 | 0 | } |
645 | |
|
646 | 0 | if (command_data && frame_number == command_data->reassemble_error_in_frame) { |
647 | | /* data reassembly error was detected in the first pass. */ |
648 | 0 | proto_tree_add_expert_remaining(main_tree, pinfo, &ei_invalid_data, tvb, offset); |
649 | 0 | } |
650 | |
|
651 | 0 | if ((!pinfo->fd->visited && command_data && command_data->reassemble_data_length < command_data->data_length) || data_length > (uint32_t) tvb_captured_length_remaining(tvb, offset) || data_length > reported_payload_remaining) { /* need reassemble */ |
652 | 0 | proto_tree_add_item(main_tree, hf_data_fragment, tvb, offset, -1, ENC_NA); |
653 | 0 | col_append_str(pinfo->cinfo, COL_INFO, "Data Fragment"); |
654 | 0 | offset = tvb_captured_length(tvb); |
655 | |
|
656 | 0 | if (service_data && command_data && command_data->reassemble_data_length >= command_data->data_length && frame_number == command_data->completed_in_frame) { |
657 | 0 | tvbuff_t *next_tvb; |
658 | 0 | adb_service_data_t adb_service_data; |
659 | |
|
660 | 0 | next_tvb = tvb_new_child_real_data(tvb, command_data->reassemble_data, command_data->reassemble_data_length, command_data->reassemble_data_length); |
661 | 0 | add_new_data_source(pinfo, next_tvb, "ADB Reassembled Data"); |
662 | |
|
663 | 0 | adb_service_data.service = service_data->service; |
664 | 0 | adb_service_data.direction = direction; |
665 | |
|
666 | 0 | adb_service_data.session_key_length = 3; |
667 | 0 | adb_service_data.session_key = (uint32_t *) wmem_alloc(pinfo->pool, adb_service_data.session_key_length * sizeof(uint32_t)); |
668 | 0 | adb_service_data.session_key[0] = interface_id; |
669 | |
|
670 | 0 | if (proto == proto_usb) { |
671 | 0 | adb_service_data.session_key[1] = urb->bus_id; |
672 | 0 | adb_service_data.session_key[2] = urb->device_address; |
673 | 0 | } else { /* tcp */ |
674 | 0 | if (direction == P2P_DIR_SENT) { |
675 | 0 | adb_service_data.session_key[1] = pinfo->srcport; |
676 | 0 | adb_service_data.session_key[2] = pinfo->destport; |
677 | 0 | } else { |
678 | 0 | adb_service_data.session_key[1] = pinfo->destport; |
679 | 0 | adb_service_data.session_key[2] = pinfo->srcport; |
680 | 0 | } |
681 | 0 | } |
682 | |
|
683 | 0 | call_dissector_with_data(adb_service_handle, next_tvb, pinfo, tree, &adb_service_data); |
684 | 0 | } |
685 | 0 | } else { /* full message */ |
686 | 0 | for (i_offset = 0; i_offset < data_length; ++i_offset) |
687 | 0 | crc += tvb_get_uint8(tvb, offset + i_offset); |
688 | |
|
689 | 0 | if (crc32 > 0 && crc32 != crc) |
690 | 0 | proto_tree_add_expert_remaining(crc_tree, pinfo, &ei_invalid_crc, tvb, offset); |
691 | |
|
692 | 0 | if (is_service) { |
693 | 0 | proto_tree_add_item(main_tree, hf_service, tvb, offset, -1, ENC_ASCII); |
694 | 0 | if (!pinfo->fd->visited && service_data) { |
695 | 0 | service_data->service = (char *) tvb_get_stringz_enc(wmem_file_scope(), tvb, offset, NULL, ENC_ASCII); |
696 | 0 | } |
697 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "Service: %s", tvb_get_stringz_enc(pinfo->pool, tvb, offset, NULL, ENC_ASCII)); |
698 | 0 | offset = tvb_captured_length(tvb); |
699 | 0 | } else if (command_data && command_data->command == A_CNXN) { |
700 | 0 | const uint8_t *info; |
701 | | |
702 | | /* |
703 | | * Format: "<systemtype>:<serialno>:<banner>". |
704 | | * Previously adb used "device::ro.product.name=...;...;\0" as |
705 | | * human-readable banner, but since platform/system/core commit |
706 | | * 1792c23cb8 (2015-05-18) it is a ";"-separated feature list. |
707 | | */ |
708 | |
|
709 | 0 | proto_tree_add_item_ret_string(main_tree, hf_connection_info, tvb, offset, -1, ENC_ASCII | ENC_NA, pinfo->pool, &info); |
710 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "Connection Info: %s", info); |
711 | 0 | offset = tvb_captured_length(tvb); |
712 | 0 | } else { |
713 | 0 | col_append_str(pinfo->cinfo, COL_INFO, "Data"); |
714 | | |
715 | | /* Decode service payload */ |
716 | 0 | if (service_data) { |
717 | 0 | tvbuff_t *next_tvb; |
718 | 0 | adb_service_data_t adb_service_data; |
719 | |
|
720 | 0 | adb_service_data.service = service_data->service; |
721 | 0 | adb_service_data.direction = direction; |
722 | |
|
723 | 0 | adb_service_data.session_key_length = 3; |
724 | 0 | adb_service_data.session_key = (uint32_t *) wmem_alloc(pinfo->pool, adb_service_data.session_key_length * sizeof(uint32_t)); |
725 | 0 | adb_service_data.session_key[0] = interface_id; |
726 | |
|
727 | 0 | if (proto == proto_usb) { |
728 | 0 | adb_service_data.session_key[1] = urb->bus_id; |
729 | 0 | adb_service_data.session_key[2] = urb->device_address; |
730 | 0 | } else { /* tcp */ |
731 | 0 | if (direction == P2P_DIR_SENT) { |
732 | 0 | adb_service_data.session_key[1] = pinfo->srcport; |
733 | 0 | adb_service_data.session_key[2] = pinfo->destport; |
734 | 0 | } else { |
735 | 0 | adb_service_data.session_key[1] = pinfo->destport; |
736 | 0 | adb_service_data.session_key[2] = pinfo->srcport; |
737 | 0 | } |
738 | 0 | } |
739 | |
|
740 | 0 | next_tvb = tvb_new_subset_remaining(tvb, offset); |
741 | 0 | call_dissector_with_data(adb_service_handle, next_tvb, pinfo, tree, &adb_service_data); |
742 | |
|
743 | 0 | } else { |
744 | 0 | proto_item *data_item; |
745 | 0 | char *data_str; |
746 | |
|
747 | 0 | data_item = proto_tree_add_item(main_tree, hf_data, tvb, offset, data_length, ENC_NA); |
748 | 0 | data_str = tvb_format_text(pinfo->pool, tvb, offset, data_length); |
749 | 0 | proto_item_append_text(data_item, ": %s", data_str); |
750 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, " Raw: %s", data_str); |
751 | 0 | } |
752 | |
|
753 | 0 | offset = tvb_captured_length(tvb); |
754 | 0 | } |
755 | 0 | } |
756 | 0 | } |
757 | |
|
758 | 0 | return offset; |
759 | 0 | } |
760 | | |
761 | | void |
762 | | proto_register_adb(void) |
763 | 15 | { |
764 | 15 | module_t *module; |
765 | 15 | expert_module_t *expert_module; |
766 | | |
767 | 15 | static hf_register_info hf[] = { |
768 | 15 | { &hf_command, |
769 | 15 | { "Command", "adb.command", |
770 | 15 | FT_UINT32, BASE_HEX, VALS(command_vals), 0x00, |
771 | 15 | NULL, HFILL } |
772 | 15 | }, |
773 | 15 | { &hf_argument_0, |
774 | 15 | { "Argument 0", "adb.argument.0", |
775 | 15 | FT_UINT32, BASE_HEX, NULL, 0x00, |
776 | 15 | NULL, HFILL } |
777 | 15 | }, |
778 | 15 | { &hf_argument_1, |
779 | 15 | { "Argument 1", "adb.argument.1", |
780 | 15 | FT_UINT32, BASE_HEX, NULL, 0x00, |
781 | 15 | NULL, HFILL } |
782 | 15 | }, |
783 | 15 | { &hf_data_length, |
784 | 15 | { "Data Length", "adb.data_length", |
785 | 15 | FT_UINT32, BASE_DEC, NULL, 0x00, |
786 | 15 | NULL, HFILL } |
787 | 15 | }, |
788 | 15 | { &hf_data_crc32, |
789 | 15 | { "Data CRC32", "adb.data_crc32", |
790 | 15 | FT_UINT32, BASE_HEX, NULL, 0x00, |
791 | 15 | NULL, HFILL } |
792 | 15 | }, |
793 | 15 | { &hf_magic, |
794 | 15 | { "Magic", "adb.magic", |
795 | 15 | FT_UINT32, BASE_HEX, VALS(magic_vals), 0x00, |
796 | 15 | NULL, HFILL } |
797 | 15 | }, |
798 | 15 | { &hf_version, |
799 | 15 | { "Version", "adb.version", |
800 | 15 | FT_UINT32, BASE_HEX, NULL, 0x00, |
801 | 15 | NULL, HFILL } |
802 | 15 | }, |
803 | 15 | { &hf_max_data, |
804 | 15 | { "Max Data", "adb.max_data", |
805 | 15 | FT_UINT32, BASE_DEC, NULL, 0x00, |
806 | 15 | NULL, HFILL } |
807 | 15 | }, |
808 | 15 | { &hf_auth_type, |
809 | 15 | { "Type", "adb.auth_type", |
810 | 15 | FT_UINT32, BASE_HEX, VALS(auth_type_vals), 0x00, |
811 | 15 | NULL, HFILL } |
812 | 15 | }, |
813 | 15 | { &hf_online, |
814 | 15 | { "Online", "adb.online", |
815 | 15 | FT_BOOLEAN, BASE_NONE, TFS(&tfs_no_yes), 0x00, |
816 | 15 | NULL, HFILL } |
817 | 15 | }, |
818 | 15 | { &hf_sequence, |
819 | 15 | { "Sequence", "adb.sequence", |
820 | 15 | FT_UINT32, BASE_DEC, NULL, 0x00, |
821 | 15 | NULL, HFILL } |
822 | 15 | }, |
823 | 15 | { &hf_zero, |
824 | 15 | { "Zero", "adb.zero", |
825 | 15 | FT_UINT32, BASE_HEX, NULL, 0x00, |
826 | 15 | NULL, HFILL } |
827 | 15 | }, |
828 | 15 | { &hf_local_id, |
829 | 15 | { "Local ID", "adb.local_id", |
830 | 15 | FT_UINT32, BASE_DEC, NULL, 0x00, |
831 | 15 | NULL, HFILL } |
832 | 15 | }, |
833 | 15 | { &hf_remote_id, |
834 | 15 | { "Remote ID", "adb.remote_id", |
835 | 15 | FT_UINT32, BASE_DEC, NULL, 0x00, |
836 | 15 | NULL, HFILL } |
837 | 15 | }, |
838 | 15 | { &hf_data, |
839 | 15 | { "Data", "adb.data", |
840 | 15 | FT_NONE, BASE_NONE, NULL, 0x00, |
841 | 15 | NULL, HFILL } |
842 | 15 | }, |
843 | 15 | { &hf_service, |
844 | 15 | { "Service", "adb.service", |
845 | 15 | FT_STRING, BASE_NONE, NULL, 0x00, |
846 | 15 | NULL, HFILL } |
847 | 15 | }, |
848 | 15 | { &hf_data_fragment, |
849 | 15 | { "Data Fragment", "adb.data_fragment", |
850 | 15 | FT_NONE, BASE_NONE, NULL, 0x00, |
851 | 15 | NULL, HFILL } |
852 | 15 | }, |
853 | 15 | { &hf_service_start_in_frame, |
854 | 15 | { "Service Start in Frame", "adb.service_start_in_frame", |
855 | 15 | FT_FRAMENUM, BASE_NONE, NULL, 0x00, |
856 | 15 | NULL, HFILL } |
857 | 15 | }, |
858 | 15 | { &hf_close_local_in_frame, |
859 | 15 | { "Local Service Close in Frame", "adb.close_local_in_frame", |
860 | 15 | FT_FRAMENUM, BASE_NONE, NULL, 0x00, |
861 | 15 | NULL, HFILL } |
862 | 15 | }, |
863 | 15 | { &hf_close_remote_in_frame, |
864 | 15 | { "Remote Service Close in Frame", "adb.close_remote_in_frame", |
865 | 15 | FT_FRAMENUM, BASE_NONE, NULL, 0x00, |
866 | 15 | NULL, HFILL } |
867 | 15 | }, |
868 | 15 | { &hf_command_in_frame, |
869 | 15 | { "Command in Frame", "adb.command_in_frame", |
870 | 15 | FT_FRAMENUM, BASE_NONE, NULL, 0x00, |
871 | 15 | NULL, HFILL } |
872 | 15 | }, |
873 | 15 | { &hf_completed_in_frame, |
874 | 15 | { "Completed in Frame", "adb.completed_in_frame", |
875 | 15 | FT_FRAMENUM, BASE_NONE, NULL, 0x00, |
876 | 15 | NULL, HFILL } |
877 | 15 | }, |
878 | 15 | { &hf_connection_info, |
879 | 15 | { "Info", "adb.connection_info", |
880 | 15 | FT_STRING, BASE_NONE, NULL, 0x00, |
881 | 15 | NULL, HFILL } |
882 | 15 | } |
883 | 15 | }; |
884 | | |
885 | 15 | static int *ett[] = { |
886 | 15 | &ett_adb, |
887 | 15 | &ett_adb_arg0, |
888 | 15 | &ett_adb_arg1, |
889 | 15 | &ett_adb_crc, |
890 | 15 | &ett_adb_magic |
891 | 15 | }; |
892 | | |
893 | 15 | static ei_register_info ei[] = { |
894 | 15 | { &ei_invalid_magic, { "adb.expert.invalid_magic", PI_PROTOCOL, PI_WARN, "Invalid Magic", EXPFILL }}, |
895 | 15 | { &ei_invalid_crc, { "adb.expert.crc_error", PI_PROTOCOL, PI_ERROR, "CRC32 Error", EXPFILL }}, |
896 | 15 | { &ei_invalid_data, { "adb.expert.data_error", PI_PROTOCOL, PI_ERROR, "Mismatch between message payload size and data length", EXPFILL }}, |
897 | 15 | }; |
898 | | |
899 | 15 | command_info = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope()); |
900 | 15 | service_info = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope()); |
901 | | |
902 | 15 | proto_adb = proto_register_protocol("Android Debug Bridge", "ADB", "adb"); |
903 | 15 | adb_handle = register_dissector("adb", dissect_adb, proto_adb); |
904 | | |
905 | 15 | proto_register_field_array(proto_adb, hf, array_length(hf)); |
906 | 15 | proto_register_subtree_array(ett, array_length(ett)); |
907 | 15 | expert_module = expert_register_protocol(proto_adb); |
908 | 15 | expert_register_field_array(expert_module, ei, array_length(ei)); |
909 | | |
910 | 15 | module = prefs_register_protocol(proto_adb, NULL); |
911 | 15 | prefs_register_static_text_preference(module, "version", |
912 | 15 | "ADB protocol version is compatible prior to: adb 1.0.31", |
913 | 15 | "Version of protocol supported by this dissector."); |
914 | 15 | } |
915 | | |
916 | | void |
917 | | proto_reg_handoff_adb(void) |
918 | 15 | { |
919 | 15 | adb_service_handle = find_dissector_add_dependency("adb_service", proto_adb); |
920 | | |
921 | 15 | dissector_add_for_decode_as_with_preference("tcp.port", adb_handle); |
922 | 15 | dissector_add_for_decode_as("usb.device", adb_handle); |
923 | 15 | dissector_add_for_decode_as("usb.product", adb_handle); |
924 | 15 | dissector_add_for_decode_as("usb.protocol", adb_handle); |
925 | | |
926 | 15 | proto_tcp = proto_get_id_by_filter_name("tcp"); |
927 | 15 | proto_usb = proto_get_id_by_filter_name("usb"); |
928 | 15 | } |
929 | | |
930 | | /* |
931 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
932 | | * |
933 | | * Local variables: |
934 | | * c-basic-offset: 4 |
935 | | * tab-width: 8 |
936 | | * indent-tabs-mode: nil |
937 | | * End: |
938 | | * |
939 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
940 | | * :indentSize=4:tabSize=8:noTabs=true: |
941 | | */ |