Android: Add ADB dissector
[metze/wireshark/wip.git] / epan / dissectors / packet-adb_cs.c
1 /* packet-adb_cs.c
2  * Routines for Android Debug Bridge Client-Server 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  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See thehf_class
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24
25 #include "config.h"
26
27 #include <epan/packet.h>
28 #include <epan/prefs.h>
29 #include <epan/expert.h>
30 #include <epan/wmem/wmem.h>
31 #include <wiretap/wtap.h>
32
33 #include "packet-adb_service.h"
34
35 static int proto_adb_cs                                                    = -1;
36
37 static int hf_role                                                         = -1;
38 static int hf_hex_ascii_length                                             = -1;
39 static int hf_length                                                       = -1;
40 static int hf_service                                                      = -1;
41 static int hf_status                                                       = -1;
42 static int hf_data                                                         = -1;
43 static int hf_fail_reason                                                  = -1;
44
45 static gint ett_adb_cs                                                     = -1;
46 static gint ett_length                                                     = -1;
47
48 static expert_field ei_incomplete_message                             = EI_INIT;
49
50 static dissector_handle_t  adb_cs_handle;
51 static dissector_handle_t  adb_service_handle;
52 static dissector_handle_t  data_handle;
53
54 static wmem_tree_t *client_requests = NULL;
55
56 static guint server_port = 5037;
57
58 typedef struct _client_request_t {
59     gint64    service_length;
60     guint8   *service;
61     guint32   first_in;
62     gint64    service_in;
63     gint64    response_frame;
64
65     guint8    status;
66     gint64    data_length;
67 } client_request_t;
68
69 static const value_string role_vals[] = {
70     { 0x00,   "Unknown" },
71     { 0x01,   "Server" },
72     { 0x02,   "Client" },
73     { 0, NULL }
74 };
75
76 #define SERVICE_NONE  NULL
77
78 #define STATUS_UNKNOWN  0
79 #define STATUS_OKAY     1
80 #define STATUS_FAIL     2
81
82 void proto_register_adb_cs(void);
83 void proto_reg_handoff_adb_cs(void);
84
85 static gint
86 dissect_adb_cs(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
87 {
88     proto_item  *main_item;
89     proto_tree  *main_tree;
90     proto_item  *sub_item;
91     proto_item  *p_item;
92     gint         offset = 0;
93     gint64       length = -1;
94     gint         direction;
95     gboolean     client_request_service = FALSE;
96     tvbuff_t           *next_tvb;
97     adb_service_data_t  adb_service_data;
98     guint32             wireshark_interface_id = 0;
99
100     col_set_str(pinfo->cinfo, COL_PROTOCOL, "ADB CS");
101     col_clear(pinfo->cinfo, COL_INFO);
102
103     main_item = proto_tree_add_item(tree, proto_adb_cs, tvb, offset, -1, ENC_NA);
104     main_tree = proto_item_add_subtree(main_item, ett_adb_cs);
105
106     if (pinfo->phdr->presence_flags & WTAP_HAS_INTERFACE_ID)
107         wireshark_interface_id = pinfo->phdr->interface_id;
108
109     if (pinfo->destport == server_port) { /* Client sent to Server */
110         client_request_t  *client_request;
111         guint8            *service = SERVICE_NONE;
112         wmem_tree_t       *subtree;
113         wmem_tree_key_t    key[5];
114
115         direction = P2P_DIR_SENT;
116
117         p_item = proto_tree_add_uint(main_tree, hf_role, tvb, offset, 0, 0x02);
118         PROTO_ITEM_SET_GENERATED(p_item);
119
120         col_add_fstr(pinfo->cinfo, COL_INFO, "Client");
121
122         if (pinfo->phdr->presence_flags & WTAP_HAS_INTERFACE_ID)
123             wireshark_interface_id = pinfo->phdr->interface_id;
124
125         key[0].length = 1;
126         key[0].key = &wireshark_interface_id;
127         key[1].length = 1;
128         key[1].key = &pinfo->srcport;
129         key[2].length = 1;
130         key[2].key = &pinfo->destport;
131         key[3].length = 0;
132         key[3].key = NULL;
133
134         subtree = (wmem_tree_t *) wmem_tree_lookup32_array(client_requests, key);
135         client_request = (subtree) ? (client_request_t *) wmem_tree_lookup32_le(subtree, pinfo->fd->num) : NULL;
136         if (client_request && client_request->service_in > -1 && client_request->service_in < pinfo->fd->num) {
137             p_item = proto_tree_add_string(main_tree, hf_service, tvb, offset, 0, client_request->service);
138             PROTO_ITEM_SET_GENERATED(p_item);
139             service = client_request->service;
140             client_request_service = TRUE;
141         } else {
142             if (client_request && client_request->service_in > -1 && client_request->service_in <= pinfo->fd->num)
143                client_request_service = TRUE;
144             client_request = NULL;
145         }
146
147         /* heuristic to recognize type of (partial) packet */
148         if (tvb_reported_length_remaining(tvb, offset) >= 4) {
149             guint8  hex_ascii_length[5];
150             guint32 ulength;
151
152             hex_ascii_length[4] = 0;
153
154             tvb_memcpy(tvb, hex_ascii_length, offset, 4);
155             if (g_ascii_xdigit_value(hex_ascii_length[0]) >= 0 &&
156                     g_ascii_xdigit_value(hex_ascii_length[1]) >= 0 &&
157                     g_ascii_xdigit_value(hex_ascii_length[2]) >= 0 &&
158                     g_ascii_xdigit_value(hex_ascii_length[3]) >= 0) {
159                 /* probably 4 bytes ascii hex length field */
160                 offset = dissect_ascii_uint32(main_tree, hf_hex_ascii_length, ett_length, hf_length, tvb, offset, &ulength);
161                 length = (gint64) ulength;
162                 col_append_fstr(pinfo->cinfo, COL_INFO, " Length=%u", ulength);
163             }
164         }
165
166
167         if (length == -1 && service) {
168             col_append_fstr(pinfo->cinfo, COL_INFO, " Service=<%s>", service);
169             length = tvb_captured_length(tvb);
170
171             /* Decode services */
172             adb_service_data.service = service;
173             adb_service_data.direction = direction;
174
175             adb_service_data.session_key_length = 3;
176             adb_service_data.session_key = (guint32 *) wmem_alloc(wmem_packet_scope(), adb_service_data.session_key_length * sizeof(guint32));
177             adb_service_data.session_key[0] = wireshark_interface_id;
178             adb_service_data.session_key[1] = pinfo->destport;
179             adb_service_data.session_key[2] = pinfo->srcport;
180
181             next_tvb = tvb_new_subset(tvb, offset, tvb_captured_length_remaining(tvb, offset), tvb_captured_length_remaining(tvb, offset));
182             call_dissector_with_data(adb_service_handle, next_tvb, pinfo, tree, &adb_service_data);
183
184             return tvb_captured_length(tvb);
185         }
186
187         if (!pinfo->fd->flags.visited && length > 0) { /* save Length to client_requests */
188             if (pinfo->phdr->presence_flags & WTAP_HAS_INTERFACE_ID)
189                 wireshark_interface_id = pinfo->phdr->interface_id;
190
191             key[0].length = 1;
192             key[0].key = &wireshark_interface_id;
193             key[1].length = 1;
194             key[1].key = &pinfo->srcport;
195             key[2].length = 1;
196             key[2].key = &pinfo->destport;
197             key[3].length = 1;
198             key[3].key = &pinfo->fd->num;
199             key[4].length = 0;
200             key[4].key = NULL;
201
202             client_request = wmem_new(wmem_file_scope(), client_request_t);
203
204             client_request->service_length = length;
205             client_request->service = SERVICE_NONE;
206             client_request->response_frame = -1;
207             client_request->first_in = pinfo->fd->num;
208             client_request->service_in = -1;
209             client_request->data_length = -1;
210             wmem_tree_insert32_array(client_requests, key, client_request);
211         }
212
213         if (!pinfo->fd->flags.visited && (length == -1 || (client_request && client_request->service_in == -1 && tvb_reported_length_remaining(tvb, offset) > 0))) { /* save Service to client_requests */
214             if (!client_request) {
215                 if (pinfo->phdr->presence_flags & WTAP_HAS_INTERFACE_ID)
216                     wireshark_interface_id = pinfo->phdr->interface_id;
217
218                 key[0].length = 1;
219                 key[0].key = &wireshark_interface_id;
220                 key[1].length = 1;
221                 key[1].key = &pinfo->srcport;
222                 key[2].length = 1;
223                 key[2].key = &pinfo->destport;
224                 key[3].length = 0;
225                 key[3].key = NULL;
226
227                 subtree = (wmem_tree_t *) wmem_tree_lookup32_array(client_requests, key);
228                 client_request = (subtree) ? (client_request_t *) wmem_tree_lookup32_le(subtree, pinfo->fd->num - 1) : NULL;
229             }
230
231             if (client_request) {
232                 client_request->service = (guint8 *) wmem_alloc(wmem_file_scope(), (const size_t)(client_request->service_length + 1));
233                 tvb_memcpy(tvb, client_request->service, offset, (size_t) client_request->service_length);
234                 client_request->service[client_request->service_length] = '\0';
235                 client_request->service_in = pinfo->fd->num;
236             }
237         }
238
239         if (!client_request_service && tvb_reported_length_remaining(tvb, offset) > 0) {
240             col_append_fstr(pinfo->cinfo, COL_INFO, " Unknown service");
241             proto_tree_add_item(main_tree, hf_data, tvb, offset, -1, ENC_NA);
242         } else if (tvb_reported_length_remaining(tvb, offset) > 0) {
243             proto_tree_add_item(main_tree, hf_service, tvb, offset, -1, ENC_NA | ENC_ASCII);
244
245             service = (guint8 *) wmem_alloc(wmem_packet_scope(), tvb_reported_length_remaining(tvb, offset) + 1);
246             tvb_memcpy(tvb, service, offset, tvb_reported_length_remaining(tvb, offset));
247             service[tvb_reported_length_remaining(tvb, offset)] = '\0';
248             col_append_fstr(pinfo->cinfo, COL_INFO, " Service=<%s>", service);
249         }
250
251         offset = tvb_captured_length(tvb);
252
253     } else if (pinfo->srcport == server_port) { /* Server sent to Client */
254         guint8             *service = SERVICE_NONE;
255         wmem_tree_t        *subtree;
256         wmem_tree_key_t     key[5];
257         client_request_t   *client_request;
258         gint64              response_frame = -1;
259         guint8              status = STATUS_UNKNOWN;
260
261         direction = P2P_DIR_RECV;
262
263         key[0].length = 1;
264         key[0].key = &wireshark_interface_id;
265         key[1].length = 1;
266         key[1].key = &pinfo->destport;
267         key[2].length = 1;
268         key[2].key = &pinfo->srcport;
269         key[3].length = 0;
270         key[3].key = NULL;
271
272         subtree = (wmem_tree_t *) wmem_tree_lookup32_array(client_requests, key);
273         client_request = (subtree) ? (client_request_t *) wmem_tree_lookup32_le(subtree, pinfo->fd->num - 1) : NULL;
274         if (client_request) {
275             service = client_request->service;
276             status = client_request->status;
277             length = client_request->data_length;
278             response_frame = client_request->response_frame;
279         }
280
281         p_item = proto_tree_add_uint(main_tree, hf_role, tvb, offset, 0, 0x01);
282         PROTO_ITEM_SET_GENERATED(p_item);
283
284         p_item = proto_tree_add_string(main_tree, hf_service, tvb, offset, 0, service);
285         PROTO_ITEM_SET_GENERATED(p_item);
286
287         col_add_fstr(pinfo->cinfo, COL_INFO, "Server");
288
289         if (!service) {
290             col_append_fstr(pinfo->cinfo, COL_INFO, " Unknown service");
291             proto_tree_add_item(main_tree, hf_data, tvb, offset, -1, ENC_NA);
292
293             return tvb_captured_length(tvb);
294         }
295
296         if (response_frame == -1 || response_frame == (gint64) pinfo->fd->num) {
297             proto_tree_add_item(main_tree, hf_status, tvb, offset, 4, ENC_NA | ENC_ASCII);
298             col_append_fstr(pinfo->cinfo, COL_INFO, " Status=%c%c%c%c", tvb_get_guint8(tvb, offset),
299             tvb_get_guint8(tvb, offset + 1), tvb_get_guint8(tvb, offset + 2), tvb_get_guint8(tvb, offset + 3));
300             offset += 4;
301
302             if (tvb_memeql(tvb, offset - 4, "FAIL", 4) == 0) {
303                 guint32 ulength;
304
305                 offset = dissect_ascii_uint32(main_tree, hf_hex_ascii_length, ett_length, hf_length, tvb, offset, &ulength);
306                 length = (gint64) ulength;
307
308                 status = STATUS_FAIL;
309             } else if (tvb_memeql(tvb, offset - 4, "OKAY", 4) == 0) {
310                 status = STATUS_OKAY;
311                 length = -1;
312             }
313
314             if (!pinfo->fd->flags.visited && client_request) {
315                 client_request->response_frame = pinfo->fd->num;
316                 client_request->status = status;
317                 client_request->data_length = length;
318             }
319         }
320
321         col_append_fstr(pinfo->cinfo, COL_INFO, " Service=<%s>", service);
322
323         if (tvb_reported_length_remaining(tvb, offset) <= 0) return offset;
324
325         if (status == STATUS_FAIL) {
326             sub_item = proto_tree_add_item(main_tree, hf_fail_reason, tvb, offset, -1, ENC_NA | ENC_ASCII);
327             if (length < tvb_reported_length_remaining(tvb, offset)) {
328                 expert_add_info(pinfo, sub_item, &ei_incomplete_message);
329             }
330
331             col_append_fstr(pinfo->cinfo, COL_INFO, " Fail=<%s>", tvb_get_string_enc(wmem_packet_scope(), tvb, offset, tvb_reported_length_remaining(tvb, offset), ENC_ASCII));
332             return tvb_captured_length(tvb);
333         }
334
335         /* Decode services */
336         adb_service_data.service = service;
337         adb_service_data.direction = direction;
338
339         adb_service_data.session_key_length = 3;
340         adb_service_data.session_key = (guint32 *) wmem_alloc(wmem_packet_scope(), adb_service_data.session_key_length * sizeof(guint32));
341         adb_service_data.session_key[0] = wireshark_interface_id;
342         adb_service_data.session_key[1] = pinfo->destport;
343         adb_service_data.session_key[2] = pinfo->srcport;
344
345         next_tvb = tvb_new_subset(tvb, offset, tvb_captured_length_remaining(tvb, offset), tvb_captured_length_remaining(tvb, offset));
346         call_dissector_with_data(adb_service_handle, next_tvb, pinfo, tree, &adb_service_data);
347         offset = tvb_captured_length(tvb);
348     } else {
349         col_add_fstr(pinfo->cinfo, COL_INFO, "Unknown role");
350
351         p_item = proto_tree_add_uint(main_tree, hf_role, tvb, offset, 0, 0x00);
352         PROTO_ITEM_SET_GENERATED(p_item);
353
354         next_tvb = tvb_new_subset_remaining(tvb, offset);
355         call_dissector(data_handle, next_tvb, pinfo, main_tree);
356         offset += tvb_captured_length_remaining(tvb, offset);
357     }
358
359     return offset;
360 }
361
362 void
363 proto_register_adb_cs(void)
364 {
365     module_t         *module;
366     expert_module_t  *expert_module;
367
368     static hf_register_info hf[] = {
369         { &hf_role,
370             { "Role",                            "adb_cs.role",
371             FT_UINT8, BASE_HEX, VALS(role_vals), 0x00,
372             NULL, HFILL }
373         },
374         { &hf_hex_ascii_length,
375             { "Hex ASCII Length",                "adb_cs.hex_ascii_length",
376             FT_STRING, STR_ASCII, NULL, 0x00,
377             NULL, HFILL }
378         },
379         { &hf_length,
380             { "Length",                          "adb_cs.length",
381             FT_UINT32, BASE_DEC_HEX, NULL, 0x00,
382             NULL, HFILL }
383         },
384         { &hf_service,
385             { "Service",                         "adb_cs.service",
386             FT_STRING, STR_ASCII, NULL, 0x00,
387             NULL, HFILL }
388         },
389         { &hf_fail_reason,
390             { "Fail Reason",                     "adb_cs.fail_reason",
391             FT_STRING, STR_ASCII, NULL, 0x00,
392             NULL, HFILL }
393         },
394         { &hf_status,
395             { "Status",                          "adb_cs.status",
396             FT_STRING, STR_ASCII, NULL, 0x00,
397             NULL, HFILL }
398         },
399         { &hf_data,
400             { "Data",                            "adb_cs.data",
401             FT_BYTES, BASE_NONE, NULL, 0x00,
402             NULL, HFILL }
403         },
404     };
405
406     static gint *ett[] = {
407         &ett_adb_cs,
408         &ett_length
409     };
410
411     static ei_register_info ei[] = {
412         { &ei_incomplete_message,         { "adb_cs.expert.incomplete_message", PI_PROTOCOL, PI_WARN, "Incomplete message", EXPFILL }},
413     };
414
415     client_requests = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
416
417     proto_adb_cs = proto_register_protocol("Android Debug Bridge Client-Server", "ADB CS", "adb_cs");
418     adb_cs_handle = new_register_dissector("adb_cs", dissect_adb_cs, proto_adb_cs);
419
420     proto_register_field_array(proto_adb_cs, hf, array_length(hf));
421     proto_register_subtree_array(ett, array_length(ett));
422     expert_module = expert_register_protocol(proto_adb_cs);
423     expert_register_field_array(expert_module, ei, array_length(ei));
424
425     module = prefs_register_protocol(proto_adb_cs, NULL);
426     prefs_register_static_text_preference(module, "version",
427             "ADB CS protocol version is compatibile pior to: adb 1.0.31",
428             "Version of protocol supported by this dissector.");
429
430     prefs_register_uint_preference(module, "server_port",
431             "Server Port",
432             "Server Port",
433             10, &server_port);
434 }
435
436 void
437 proto_reg_handoff_adb_cs(void)
438 {
439     data_handle   = find_dissector("data");
440     adb_service_handle = find_dissector("adb_service");
441
442     dissector_add_for_decode_as("tcp.port", adb_cs_handle);
443 }
444
445 /*
446  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
447  *
448  * Local variables:
449  * c-basic-offset: 4
450  * tab-width: 8
451  * indent-tabs-mode: nil
452  * End:
453  *
454  * vi: set shiftwidth=4 tabstop=8 expandtab:
455  * :indentSize=4:tabSize=8:noTabs=true:
456  */