File: | builds/wireshark/wireshark/epan/dissectors/packet-bt-dht.c |
Warning: | line 553, column 13 Null pointer passed to 1st parameter expecting 'nonnull' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /******************************************************************************************************/ | |||
2 | /* packet-bt-dht.c | |||
3 | * Routines for BT-DHT dissection | |||
4 | * Copyright 2011, Xiao Xiangquan <[email protected]> | |||
5 | * | |||
6 | * A plugin for BT-DHT packet: | |||
7 | * | |||
8 | * Wireshark - Network traffic analyzer | |||
9 | * By Gerald Combs <[email protected]> | |||
10 | * Copyright 1999 Gerald Combs | |||
11 | * | |||
12 | * SPDX-License-Identifier: GPL-2.0-or-later | |||
13 | */ | |||
14 | ||||
15 | #include "config.h" | |||
16 | ||||
17 | #include <stdlib.h> | |||
18 | ||||
19 | #include <epan/packet.h> | |||
20 | #include <epan/conversation.h> | |||
21 | #include <epan/prefs.h> | |||
22 | #include <epan/to_str.h> | |||
23 | #include <epan/expert.h> | |||
24 | ||||
25 | #include <wsutil/strtoi.h> | |||
26 | ||||
27 | #define DHT_MIN_LEN5 5 | |||
28 | ||||
29 | void proto_register_bt_dht(void); | |||
30 | void proto_reg_handoff_bt_dht(void); | |||
31 | ||||
32 | /* Specifications: | |||
33 | * https://www.bittorrent.org/beps/bep_0005.html BEP 5 DHT Protocol | |||
34 | * https://www.bittorrent.org/beps/bep_0042.html BEP 42 DHT Security extension | |||
35 | */ | |||
36 | ||||
37 | static int proto_bt_dht; | |||
38 | static dissector_handle_t bt_dht_handle; | |||
39 | ||||
40 | /* fields */ | |||
41 | static int hf_bencoded_int; | |||
42 | static int hf_bencoded_string; | |||
43 | static int hf_bencoded_list; | |||
44 | static int hf_bencoded_dict; | |||
45 | static int hf_bencoded_dict_entry; | |||
46 | static int hf_bencoded_list_terminator; | |||
47 | ||||
48 | static int hf_bt_dht_error; | |||
49 | static int hf_bt_dht_peers; | |||
50 | static int hf_bt_dht_peer; | |||
51 | static int hf_bt_dht_nodes; | |||
52 | static int hf_bt_dht_node; | |||
53 | static int hf_bt_dht_id; | |||
54 | ||||
55 | static int hf_ip; | |||
56 | static int hf_ip6; | |||
57 | static int hf_port; | |||
58 | static int hf_truncated_data; | |||
59 | ||||
60 | static expert_field ei_int_string; | |||
61 | static expert_field ei_invalid_len; | |||
62 | static expert_field ei_duplicate_dict_keys; | |||
63 | static expert_field ei_unsorted_dict_keys; | |||
64 | ||||
65 | /* tree types */ | |||
66 | static int ett_bt_dht; | |||
67 | static int ett_bencoded_list; | |||
68 | static int ett_bencoded_dict; | |||
69 | static int ett_bencoded_dict_entry; | |||
70 | static int ett_bt_dht_error; | |||
71 | static int ett_bt_dht_peers; | |||
72 | static int ett_bt_dht_nodes; | |||
73 | ||||
74 | /* some keys use short name in packet */ | |||
75 | static const value_string short_key_name_value_string[] = { | |||
76 | { 'a', "Request arguments" }, | |||
77 | { 'e', "Error" }, | |||
78 | { 'q', "Request type" }, | |||
79 | { 'r', "Response values" }, | |||
80 | { 't', "Transaction ID" }, | |||
81 | { 'v', "Version" }, | |||
82 | { 'y', "Message type" }, | |||
83 | { 0, NULL((void*)0) } | |||
84 | }; | |||
85 | ||||
86 | /* some values use short name in packet */ | |||
87 | static const value_string short_val_name_value_string[] = { | |||
88 | { 'e', "Error" }, | |||
89 | { 'q', "Request" }, | |||
90 | { 'r', "Response" }, | |||
91 | { 0, NULL((void*)0) } | |||
92 | }; | |||
93 | ||||
94 | static const char dict_str[] = "Dictionary..."; | |||
95 | static const char list_str[] = "List..."; | |||
96 | ||||
97 | ||||
98 | static inline bool_Bool | |||
99 | bencoded_string_length(packet_info *pinfo, tvbuff_t *tvb, unsigned *offset_ptr, unsigned *length) | |||
100 | { | |||
101 | unsigned offset, start; | |||
102 | unsigned remaining = tvb_captured_length_remaining(tvb, *offset_ptr); | |||
103 | if (remaining == 0) | |||
104 | return false0; | |||
105 | ||||
106 | offset = *offset_ptr; | |||
107 | start = offset; | |||
108 | ||||
109 | while(tvb_get_uint8(tvb, offset) != ':' && --remaining) | |||
110 | ++offset; | |||
111 | ||||
112 | if (remaining && ws_strtou32(tvb_get_string_enc(pinfo->pool, tvb, start, offset-start, ENC_ASCII0x00000000), | |||
113 | NULL((void*)0), length)) { | |||
114 | ++offset; /* skip the ':' */ | |||
115 | *offset_ptr = offset; | |||
116 | return true1; | |||
117 | } | |||
118 | return false0; | |||
119 | } | |||
120 | ||||
121 | ||||
122 | /* | |||
123 | * dissect a bencoded string from tvb, start at offset. it's like "5:abcde" | |||
124 | * *result will be the decoded value | |||
125 | */ | |||
126 | ||||
127 | static int | |||
128 | dissect_bencoded_string(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset, const char **result, bool_Bool tohex, const char *label ) | |||
129 | { | |||
130 | unsigned string_len; | |||
131 | if (!bencoded_string_length(pinfo, tvb, &offset, &string_len)) | |||
132 | return 0; | |||
133 | ||||
134 | const unsigned remaining = tvb_captured_length_remaining(tvb, offset); | |||
135 | if (remaining < string_len) | |||
136 | return 0; | |||
137 | ||||
138 | /* fill the return data */ | |||
139 | if (string_len == 0) | |||
140 | *result = ""; | |||
141 | else if (tohex
| |||
142 | *result = tvb_bytes_to_str(pinfo->pool, tvb, offset, string_len ); | |||
143 | else | |||
144 | *result = tvb_get_string_enc( pinfo->pool, tvb, offset, string_len , ENC_ASCII0x00000000); | |||
145 | ||||
146 | proto_tree_add_string_format( tree, hf_bencoded_string, tvb, offset, string_len, *result, "%s: %s", label, *result ); | |||
147 | offset += string_len; | |||
148 | return offset; | |||
149 | } | |||
150 | ||||
151 | /* | |||
152 | * dissect a bencoded integer from tvb, start at offset. it's like "i5673e" | |||
153 | * *result will be the decoded value | |||
154 | */ | |||
155 | static int | |||
156 | dissect_bencoded_int(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset, const char **result, const char *label ) | |||
157 | { | |||
158 | unsigned start_offset; | |||
159 | unsigned remaining = tvb_captured_length_remaining(tvb, offset); | |||
160 | ||||
161 | /* the shortest valid integer is i0e, so we need at least 3 bytes */ | |||
162 | if (remaining < 3) | |||
163 | return 0; | |||
164 | ||||
165 | if (tvb_get_uint8(tvb, offset) != 'i') | |||
166 | return 0; | |||
167 | ||||
168 | offset += 1; | |||
169 | remaining -= 1; | |||
170 | start_offset = offset; | |||
171 | while (tvb_get_uint8(tvb, offset) != 'e' && --remaining) | |||
172 | offset += 1; | |||
173 | ||||
174 | if (remaining == 0) | |||
175 | return 0; | |||
176 | ||||
177 | proto_tree_add_item(tree, hf_bencoded_list_terminator, tvb, offset, 1, ENC_ASCII0x00000000); | |||
178 | ||||
179 | *result = tvb_get_string_enc( pinfo->pool, tvb, start_offset, offset-start_offset, ENC_ASCII0x00000000); | |||
180 | proto_tree_add_string_format( tree, hf_bencoded_int, tvb, start_offset, offset-start_offset, *result, | |||
181 | "%s: %s", label, *result ); | |||
182 | ||||
183 | offset += 1; | |||
184 | return offset; | |||
185 | } | |||
186 | ||||
187 | /* pre definition of dissect_bencoded_dict(), which is needed by dissect_bencoded_list() */ | |||
188 | static int dissect_bencoded_dict(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset, const char *label ); | |||
189 | ||||
190 | /* dissect a bencoded list from tvb, start at offset. it's like "lXXXe", "X" is any bencoded thing */ | |||
191 | static int | |||
192 | // NOLINTNEXTLINE(misc-no-recursion) | |||
193 | dissect_bencoded_list(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset, const char *label ) | |||
194 | { | |||
195 | proto_item *ti; | |||
196 | proto_tree *sub_tree; | |||
197 | unsigned one_byte; | |||
198 | const char *result; | |||
199 | ||||
200 | /* the shortest valid list is "le", so we need at least 2 bytes */ | |||
201 | if (tvb_captured_length_remaining(tvb, offset) < 2) | |||
202 | return 0; | |||
203 | ||||
204 | ti = proto_tree_add_none_format( tree, hf_bencoded_list, tvb, offset, 0, "%s: list...", label ); | |||
205 | sub_tree = proto_item_add_subtree( ti, ett_bencoded_list); | |||
206 | ||||
207 | if (tvb_get_uint8(tvb, offset) != 'l') | |||
208 | return 0; | |||
209 | offset += 1; | |||
210 | ||||
211 | while (tvb_captured_length_remaining(tvb, offset) > 0) | |||
212 | { | |||
213 | one_byte = tvb_get_uint8(tvb, offset); | |||
214 | if (one_byte == 'e') | |||
215 | break; | |||
216 | ||||
217 | unsigned start_offset = offset; | |||
218 | switch( one_byte ) | |||
219 | { | |||
220 | /* a integer */ | |||
221 | case 'i': | |||
222 | offset = dissect_bencoded_int( tvb, pinfo, sub_tree, offset, &result, "Integer" ); | |||
223 | break; | |||
224 | /* a sub-list */ | |||
225 | case 'l': | |||
226 | offset = dissect_bencoded_list( tvb, pinfo, sub_tree, offset, "Sub-list" ); | |||
227 | break; | |||
228 | /* a dictionary */ | |||
229 | case 'd': | |||
230 | offset = dissect_bencoded_dict( tvb, pinfo, sub_tree, offset, "Sub-dict" ); | |||
231 | break; | |||
232 | /* a string */ | |||
233 | default: | |||
234 | offset = dissect_bencoded_string( tvb, pinfo, sub_tree, offset, &result, false0, "String" ); | |||
235 | break; | |||
236 | } | |||
237 | if (offset <= start_offset) | |||
238 | { | |||
239 | proto_tree_add_expert(sub_tree, pinfo, &ei_int_string, tvb, offset, -1); | |||
240 | /* if offset is not going on, there is no chance to exit the loop, then return*/ | |||
241 | return 0; | |||
242 | } | |||
243 | } | |||
244 | ||||
245 | if (tvb_captured_length_remaining(tvb, offset) == 0) | |||
246 | return 0; | |||
247 | ||||
248 | proto_tree_add_item(sub_tree, hf_bencoded_list_terminator, tvb, offset, 1, ENC_ASCII0x00000000); | |||
249 | offset += 1; | |||
250 | return offset; | |||
251 | } | |||
252 | ||||
253 | /* dissect a bt dht error from tvb, start at offset. it's like "li201e9:error msge" */ | |||
254 | static int | |||
255 | dissect_bt_dht_error(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset, const char **result, const char *label ) | |||
256 | { | |||
257 | proto_item *ti; | |||
258 | proto_tree *sub_tree; | |||
259 | const char *error_no, *error_msg; | |||
260 | ||||
261 | error_no = NULL((void*)0); | |||
262 | error_msg = NULL((void*)0); | |||
263 | ||||
264 | ti = proto_tree_add_item( tree, hf_bt_dht_error, tvb, offset, 0, ENC_NA0x00000000 ); | |||
265 | sub_tree = proto_item_add_subtree( ti, ett_bt_dht_error); | |||
266 | ||||
267 | /* we have confirmed that the first byte is 'l' */ | |||
268 | offset += 1; | |||
269 | ||||
270 | /* dissect bt-dht error number and message */ | |||
271 | offset = dissect_bencoded_int( tvb, pinfo, sub_tree, offset, &error_no, "Error ID" ); | |||
272 | if (offset == 0) { | |||
273 | return 0; | |||
274 | } | |||
275 | offset = dissect_bencoded_string( tvb, pinfo, sub_tree, offset, &error_msg, false0, "Error Message" ); | |||
276 | if (offset == 0) { | |||
277 | return 0; | |||
278 | } | |||
279 | ||||
280 | proto_item_set_text( ti, "%s: error %s, %s", label, error_no, error_msg ); | |||
281 | col_append_fstr( pinfo->cinfo, COL_INFO, " No=%s Msg=%s", error_no, error_msg ); | |||
282 | *result = wmem_strdup_printf(pinfo->pool, "error %s, %s", error_no, error_msg ); | |||
283 | ||||
284 | return offset + 1; | |||
285 | } | |||
286 | ||||
287 | /* dissect a bt dht values list from tvb, start at offset. it's like "l6:....6:....e" */ | |||
288 | static int | |||
289 | dissect_bt_dht_values(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset, const char **result, const char *label ) | |||
290 | { | |||
291 | proto_item *ti; | |||
292 | proto_tree *sub_tree; | |||
293 | proto_item *value_ti; | |||
294 | proto_tree *value_tree; | |||
295 | ||||
296 | unsigned peer_index; | |||
297 | unsigned string_len; | |||
298 | ||||
299 | ti = proto_tree_add_item( tree, hf_bt_dht_peers, tvb, offset, 0, ENC_NA0x00000000 ); | |||
300 | sub_tree = proto_item_add_subtree( ti, ett_bt_dht_peers); | |||
301 | ||||
302 | peer_index = 0; | |||
303 | /* we has confirmed that the first byte is 'l' */ | |||
304 | offset += 1; | |||
305 | ||||
306 | /* dissect bt-dht values */ | |||
307 | while( tvb_get_uint8(tvb,offset)!='e' ) | |||
308 | { | |||
309 | if (!bencoded_string_length(pinfo, tvb, &offset, &string_len)) | |||
310 | { | |||
311 | expert_add_info(pinfo, ti, &ei_invalid_len); | |||
312 | // Fail hard here rather than potentially looping excessively. | |||
313 | return 0; | |||
314 | } | |||
315 | else if (string_len == 6) | |||
316 | { | |||
317 | /* 4 bytes ip, 2 bytes port */ | |||
318 | peer_index += 1; | |||
319 | ||||
320 | value_ti = proto_tree_add_item( sub_tree, hf_bt_dht_peer, tvb, offset, 6, ENC_NA0x00000000 ); | |||
321 | proto_item_append_text(value_ti, " %d", peer_index); | |||
322 | value_tree = proto_item_add_subtree( value_ti, ett_bt_dht_peers); | |||
323 | ||||
324 | proto_tree_add_item( value_tree, hf_ip, tvb, offset, 4, ENC_BIG_ENDIAN0x00000000); | |||
325 | proto_item_append_text(value_ti, " (IP/Port: %s", tvb_ip_to_str(pinfo->pool, tvb, offset)tvb_address_to_str(pinfo->pool, tvb, AT_IPv4, offset)); | |||
326 | proto_tree_add_item( value_tree, hf_port, tvb, offset+4, 2, ENC_BIG_ENDIAN0x00000000); | |||
327 | proto_item_append_text(value_ti, ":%u)", tvb_get_ntohs( tvb, offset+4 )); | |||
328 | } | |||
329 | else if (string_len == 18) | |||
330 | { | |||
331 | /* 16 bytes ip, 2 bytes port */ | |||
332 | peer_index += 1; | |||
333 | ||||
334 | value_ti = proto_tree_add_item( sub_tree, hf_bt_dht_peer, tvb, offset, 18, ENC_NA0x00000000 ); | |||
335 | proto_item_append_text(value_ti, " %d", peer_index); | |||
336 | value_tree = proto_item_add_subtree( value_ti, ett_bt_dht_peers); | |||
337 | ||||
338 | proto_tree_add_item( value_tree, hf_ip6, tvb, offset, 16, ENC_NA0x00000000); | |||
339 | proto_item_append_text(value_ti, " (IPv6/Port: [%s]", tvb_ip6_to_str(pinfo->pool, tvb, offset)tvb_address_to_str(pinfo->pool, tvb, AT_IPv6, offset)); | |||
340 | proto_tree_add_item( value_tree, hf_port, tvb, offset+16, 2, ENC_BIG_ENDIAN0x00000000); | |||
341 | proto_item_append_text(value_ti, ":%u)", tvb_get_ntohs( tvb, offset+16 )); | |||
342 | } | |||
343 | else | |||
344 | { | |||
345 | /* truncated data */ | |||
346 | proto_tree_add_item( tree, hf_truncated_data, tvb, offset, string_len, ENC_NA0x00000000 ); | |||
347 | } | |||
348 | ||||
349 | offset += string_len; | |||
350 | } | |||
351 | ||||
352 | if (tvb_get_uint8(tvb,offset)=='e') { /* list ending delimiter */ | |||
353 | proto_tree_add_item(sub_tree, hf_bencoded_list_terminator, tvb, offset, 1, ENC_ASCII0x00000000); | |||
354 | offset++; | |||
355 | } | |||
356 | ||||
357 | proto_item_set_text( ti, "%s: %d peers", label, peer_index ); | |||
358 | col_append_fstr( pinfo->cinfo, COL_INFO, " Peers=%d", peer_index ); | |||
359 | *result = wmem_strdup_printf(pinfo->pool, "%d peers", peer_index); | |||
360 | ||||
361 | return offset; | |||
362 | } | |||
363 | ||||
364 | static int | |||
365 | dissect_bt_dht_nodes(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset, const char **result, const char *label, bool_Bool is_ipv6 ) | |||
366 | { | |||
367 | proto_item *ti; | |||
368 | proto_tree *sub_tree; | |||
369 | proto_item *node_ti; | |||
370 | proto_tree *node_tree; | |||
371 | ||||
372 | unsigned node_index; | |||
373 | unsigned string_len; | |||
374 | unsigned node_byte_length; | |||
375 | ||||
376 | if (!bencoded_string_length(pinfo, tvb, &offset, &string_len)) | |||
377 | return 0; | |||
378 | ||||
379 | ti = proto_tree_add_item( tree, hf_bt_dht_nodes, tvb, offset, string_len, ENC_NA0x00000000 ); | |||
380 | sub_tree = proto_item_add_subtree( ti, ett_bt_dht_nodes); | |||
381 | node_index = 0; | |||
382 | ||||
383 | /* 26 bytes = 20 bytes id + 4 bytes ipv4 address + 2 bytes port */ | |||
384 | node_byte_length = 26; | |||
385 | ||||
386 | if ( is_ipv6 ) | |||
387 | { | |||
388 | /* 38 bytes = 20 bytes id + 16 bytes ipv6 address + 2 bytes port */ | |||
389 | node_byte_length = 38; | |||
390 | } | |||
391 | ||||
392 | for( ; string_len>=node_byte_length; string_len-=node_byte_length, offset+=node_byte_length ) | |||
393 | { | |||
394 | node_index += 1; | |||
395 | ||||
396 | node_ti = proto_tree_add_item( sub_tree, hf_bt_dht_node, tvb, offset, node_byte_length, ENC_NA0x00000000); | |||
397 | proto_item_append_text(node_ti, " %d", node_index); | |||
398 | node_tree = proto_item_add_subtree( node_ti, ett_bt_dht_peers); | |||
399 | ||||
400 | proto_tree_add_item( node_tree, hf_bt_dht_id, tvb, offset, 20, ENC_NA0x00000000); | |||
401 | proto_item_append_text(node_ti, " (id: %s", tvb_bytes_to_str(pinfo->pool, tvb, offset, 20)); | |||
402 | ||||
403 | if ( is_ipv6 ) | |||
404 | { | |||
405 | proto_tree_add_item( node_tree, hf_ip6, tvb, offset+20, 16, ENC_NA0x00000000); | |||
406 | proto_item_append_text(node_ti, ", IPv6/Port: [%s]", tvb_ip6_to_str(pinfo->pool, tvb, offset+20)tvb_address_to_str(pinfo->pool, tvb, AT_IPv6, offset+20)); | |||
407 | ||||
408 | proto_tree_add_item( node_tree, hf_port, tvb, offset+36, 2, ENC_BIG_ENDIAN0x00000000); | |||
409 | proto_item_append_text(node_ti, ":%u)", tvb_get_ntohs( tvb, offset+36 )); | |||
410 | } | |||
411 | else | |||
412 | { | |||
413 | proto_tree_add_item( node_tree, hf_ip, tvb, offset+20, 4, ENC_BIG_ENDIAN0x00000000); | |||
414 | proto_item_append_text(node_ti, ", IPv4/Port: %s", tvb_ip_to_str(pinfo->pool, tvb, offset+20)tvb_address_to_str(pinfo->pool, tvb, AT_IPv4, offset+20)); | |||
415 | ||||
416 | proto_tree_add_item( node_tree, hf_port, tvb, offset+24, 2, ENC_BIG_ENDIAN0x00000000); | |||
417 | proto_item_append_text(node_ti, ":%u)", tvb_get_ntohs( tvb, offset+24 )); | |||
418 | } | |||
419 | } | |||
420 | ||||
421 | if( string_len>0 ) | |||
422 | { | |||
423 | proto_tree_add_item( tree, hf_truncated_data, tvb, offset, string_len, ENC_NA0x00000000 ); | |||
424 | offset += string_len; | |||
425 | } | |||
426 | proto_item_set_text( ti, "%s: %d nodes", label, node_index ); | |||
427 | col_append_fstr( pinfo->cinfo, COL_INFO, " Nodes=%d", node_index ); | |||
428 | *result = wmem_strdup_printf(pinfo->pool, "%d", node_index); | |||
429 | ||||
430 | return offset; | |||
431 | } | |||
432 | ||||
433 | static int | |||
434 | // NOLINTNEXTLINE(misc-no-recursion) | |||
435 | dissect_bencoded_dict_entry(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset, const char **key) | |||
436 | { | |||
437 | proto_item *ti; | |||
438 | proto_tree *sub_tree; | |||
439 | bool_Bool tohex; | |||
440 | const char *val; | |||
441 | unsigned orig_offset = offset; | |||
442 | ||||
443 | val = NULL((void*)0); | |||
444 | ||||
445 | ti = proto_tree_add_item( tree, hf_bencoded_dict_entry, tvb, offset, 0, ENC_NA0x00000000 ); | |||
446 | sub_tree = proto_item_add_subtree( ti, ett_bencoded_dict_entry); | |||
447 | ||||
448 | /* dissect the key, it must be a string */ | |||
449 | offset = dissect_bencoded_string( tvb, pinfo, sub_tree, offset, key, false0, "Key" ); | |||
450 | if (offset == 0) | |||
451 | { | |||
452 | proto_tree_add_expert_format(sub_tree, pinfo, &ei_int_string, tvb, offset, -1, "Invalid string for Key"); | |||
453 | return 0; | |||
454 | } | |||
455 | ||||
456 | if (tvb_captured_length_remaining(tvb, offset) == 0) | |||
457 | return 0; | |||
458 | ||||
459 | /* If it is a dict, then just do recursion */ | |||
460 | switch( tvb_get_uint8(tvb,offset) ) | |||
461 | { | |||
462 | case 'd': | |||
463 | offset = dissect_bencoded_dict( tvb, pinfo, sub_tree, offset, "Value" ); | |||
464 | val = dict_str; | |||
465 | break; | |||
466 | case 'l': | |||
467 | if( strcmp(*key,"e")==0 ) | |||
468 | offset = dissect_bt_dht_error( tvb, pinfo, sub_tree, offset, &val, "Value" ); | |||
469 | else if( strcmp(*key,"values")==0 ) | |||
470 | offset = dissect_bt_dht_values( tvb, pinfo, sub_tree, offset, &val, "Value" ); | |||
471 | /* other unfamiliar lists */ | |||
472 | else | |||
473 | { | |||
474 | offset = dissect_bencoded_list( tvb, pinfo, sub_tree, offset, "Value" ); | |||
475 | val = list_str; | |||
476 | } | |||
477 | break; | |||
478 | case 'i': | |||
479 | offset = dissect_bencoded_int( tvb, pinfo, sub_tree, offset, &val, "Value" ); | |||
480 | break; | |||
481 | /* it's a string */ | |||
482 | default: | |||
483 | /* special process */ | |||
484 | if( strcmp(*key,"nodes")==0 ) | |||
485 | { | |||
486 | offset = dissect_bt_dht_nodes( tvb, pinfo, sub_tree, offset, &val, "Value", 0 ); | |||
487 | } | |||
488 | else if( strcmp(*key,"nodes6")==0 ) | |||
489 | { | |||
490 | offset = dissect_bt_dht_nodes( tvb, pinfo, sub_tree, offset, &val, "Value", 1 ); | |||
491 | } | |||
492 | else if( strcmp(*key,"ip")==0 ) | |||
493 | { | |||
494 | /* | |||
495 | * BEP 42 DHT Security extension | |||
496 | * https://www.bittorrent.org/beps/bep_0042.html | |||
497 | * https://www.rasterbar.com/products/libtorrent/dht_sec.html | |||
498 | */ | |||
499 | ||||
500 | unsigned len; | |||
501 | int old_offset = offset; | |||
502 | if (!bencoded_string_length(pinfo, tvb, &offset, &len)) { | |||
503 | proto_tree_add_expert_format(sub_tree, pinfo, &ei_int_string, tvb, offset, -1, "Invalid string for value"); | |||
504 | return 0; | |||
505 | } | |||
506 | ||||
507 | if(len == 6) { | |||
508 | proto_tree_add_item(sub_tree, hf_ip, tvb, offset, 4, ENC_BIG_ENDIAN0x00000000); | |||
509 | val = tvb_ip_to_str(pinfo->pool, tvb, offset)tvb_address_to_str(pinfo->pool, tvb, AT_IPv4, offset); | |||
510 | offset += 4; | |||
511 | proto_tree_add_item(sub_tree, hf_port, tvb, offset, 2, ENC_BIG_ENDIAN0x00000000); | |||
512 | offset += 2; | |||
513 | } | |||
514 | else if (len == 18) | |||
515 | { | |||
516 | proto_tree_add_item(sub_tree, hf_ip6, tvb, offset, 16, ENC_NA0x00000000); | |||
517 | val = tvb_ip6_to_str(pinfo->pool, tvb, offset)tvb_address_to_str(pinfo->pool, tvb, AT_IPv6, offset); | |||
518 | offset += 16; | |||
519 | ||||
520 | proto_tree_add_item(sub_tree, hf_port, tvb, offset, 2, ENC_BIG_ENDIAN0x00000000); | |||
521 | offset += 2; | |||
522 | } | |||
523 | else { | |||
524 | offset = dissect_bencoded_string( tvb, pinfo, sub_tree, old_offset, &val, true1, "Value" ); | |||
525 | } | |||
526 | } | |||
527 | else | |||
528 | { | |||
529 | /* some need to return hex string */ | |||
530 | tohex = strcmp(*key,"id")==0 || strcmp(*key,"target")==0 | |||
531 | || strcmp(*key,"info_hash")==0 || strcmp(*key,"t")==0 | |||
532 | || strcmp(*key,"v")==0 || strcmp(*key,"token")==0; | |||
533 | offset = dissect_bencoded_string( tvb, pinfo, sub_tree, offset, &val, tohex, "Value" ); | |||
534 | } | |||
535 | } | |||
536 | ||||
537 | if (offset == 0) | |||
538 | { | |||
539 | proto_tree_add_expert_format(sub_tree, pinfo, &ei_int_string, tvb, offset, -1, "Invalid string for value"); | |||
540 | return 0; | |||
541 | } | |||
542 | ||||
543 | if(*key && strcmp(*key,"q")==0 && strlen(val)>1 ) | |||
544 | col_prepend_fstr(pinfo->cinfo, COL_INFO, "%c%s", g_ascii_toupper(val[0]), val + 1); | |||
545 | if(*key && strcmp(*key,"r")==0 ) | |||
546 | col_prepend_fstr(pinfo->cinfo, COL_INFO, "Response"); | |||
547 | if(*key && strcmp(*key,"e")==0 ) | |||
548 | col_prepend_fstr(pinfo->cinfo, COL_INFO, "Error"); | |||
549 | if(*key && (strcmp(*key,"info_hash")==0 || strcmp(*key,"target")==0) ) | |||
550 | col_append_fstr(pinfo->cinfo, COL_INFO, " %c%s=%s", g_ascii_toupper((*key)[0]), *key + 1, val); | |||
551 | ||||
552 | const char * printable_key = *key; | |||
553 | if(key
| |||
| ||||
554 | printable_key = val_to_str_const( (*key)[0], short_key_name_value_string, *key ); | |||
555 | if(val && strlen(val)==1 ) | |||
556 | val = val_to_str_const( val[0], short_val_name_value_string, val ); | |||
557 | ||||
558 | proto_item_set_text( ti, "%s: %s", printable_key, val ); | |||
559 | proto_item_set_len( ti, offset-orig_offset ); | |||
560 | ||||
561 | return offset; | |||
562 | } | |||
563 | ||||
564 | /* dict = d...e */ | |||
565 | static int | |||
566 | // NOLINTNEXTLINE(misc-no-recursion) | |||
567 | dissect_bencoded_dict(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset, const char *label ) | |||
568 | { | |||
569 | proto_item *ti; | |||
570 | proto_tree *sub_tree; | |||
571 | unsigned orig_offset = offset; | |||
572 | ||||
573 | /* the shortest valid dictionary is "de", so we need at least 2 bytes */ | |||
574 | if (tvb_captured_length_remaining(tvb, offset) < 2) | |||
575 | return 0; | |||
576 | ||||
577 | if(offset
| |||
578 | { | |||
579 | ti = proto_tree_add_item(tree, proto_bt_dht, tvb, 0, -1, ENC_NA0x00000000); | |||
580 | sub_tree = proto_item_add_subtree(ti, ett_bt_dht); | |||
581 | } | |||
582 | else | |||
583 | { | |||
584 | ti = proto_tree_add_none_format( tree, hf_bencoded_dict, tvb, offset, -1, "%s: Dictionary...", label ); | |||
585 | sub_tree = proto_item_add_subtree( ti, ett_bencoded_dict); | |||
586 | } | |||
587 | ||||
588 | if (tvb_get_uint8(tvb, offset) != 'd') | |||
589 | return 0; | |||
590 | offset += 1; | |||
591 | ||||
592 | const char * prev_key = NULL((void*)0); | |||
593 | unsigned prev_key_offset = 0; | |||
594 | while (tvb_captured_length_remaining(tvb, offset) > 0) { | |||
595 | if (tvb_get_uint8(tvb, offset) == 'e') | |||
596 | break; | |||
597 | ||||
598 | const char * key = NULL((void*)0); | |||
599 | const unsigned entry_start = offset; | |||
600 | offset = dissect_bencoded_dict_entry( tvb, pinfo, sub_tree, offset, &key ); | |||
601 | if (offset == 0) | |||
602 | { | |||
603 | proto_tree_add_expert(sub_tree, pinfo, &ei_int_string, tvb, offset, -1); | |||
604 | return 0; | |||
605 | } | |||
606 | ||||
607 | if (prev_key != NULL((void*)0) && key != NULL((void*)0)) { | |||
608 | const int ordering = strcmp(key, prev_key); | |||
609 | if (ordering < 0) { | |||
610 | proto_tree_add_expert( | |||
611 | sub_tree, pinfo, &ei_unsorted_dict_keys, tvb, prev_key_offset, offset - prev_key_offset); | |||
612 | } else if (ordering == 0) { | |||
613 | proto_tree_add_expert( | |||
614 | sub_tree, pinfo, &ei_duplicate_dict_keys, tvb, prev_key_offset, offset - prev_key_offset); | |||
615 | } | |||
616 | } | |||
617 | ||||
618 | prev_key = key; | |||
619 | prev_key_offset = entry_start; | |||
620 | } | |||
621 | ||||
622 | if (tvb_captured_length_remaining(tvb, offset) == 0) | |||
623 | return 0; | |||
624 | ||||
625 | proto_tree_add_item(sub_tree, hf_bencoded_list_terminator, tvb, offset, 1, ENC_ASCII0x00000000); | |||
626 | offset += 1; | |||
627 | proto_item_set_len( ti, offset-orig_offset ); | |||
628 | ||||
629 | return offset; | |||
630 | } | |||
631 | ||||
632 | static bool_Bool | |||
633 | test_bt_dht(packet_info *pinfo _U___attribute__((unused)), tvbuff_t *tvb, int offset, void *data _U___attribute__((unused))) | |||
634 | { | |||
635 | ||||
636 | /* The DHT KRPC protocol sends packets that are bencoded dictionaries. | |||
637 | * Bencoded dictionaries always have the keys in sorted (raw string) | |||
638 | * order. There are three possible message types, query, which has "a" and | |||
639 | * "q" keys that map to dictionaries, response, which has an "r" key | |||
640 | * that maps to a dictionary, and error, which has an "e" key that maps | |||
641 | * to a list. | |||
642 | * | |||
643 | * Conveniently, those keys appear in sort order before all other possible | |||
644 | * top level keys, with the exception of the "ip" key added in BEP-0042. | |||
645 | * | |||
646 | * Thus, there are only four possible initial sets of bytes, corresponding | |||
647 | * to beginning with an "a" dictionary, "r" dictionary, "ip" string, or an | |||
648 | * "e" list. | |||
649 | */ | |||
650 | ||||
651 | if (tvb_captured_length_remaining(tvb, offset) < DHT_MIN_LEN5) | |||
652 | return false0; | |||
653 | ||||
654 | if (tvb_memeql(tvb, offset, (const uint8_t*)"d1:ad", 5) == 0) { | |||
655 | return true1; | |||
656 | } else if (tvb_memeql(tvb, offset, (const uint8_t*)"d1:rd", 5) == 0) { | |||
657 | return true1; | |||
658 | } else if (tvb_memeql(tvb, offset, (const uint8_t*)"d2:ip", 5) == 0) { | |||
659 | return true1; | |||
660 | } else if (tvb_memeql(tvb, offset, (const uint8_t*)"d1:el", 5) == 0) { | |||
661 | return true1; | |||
662 | } | |||
663 | ||||
664 | return false0; | |||
665 | } | |||
666 | ||||
667 | static int | |||
668 | dissect_bt_dht(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) | |||
669 | { | |||
670 | /* BitTorrent clients use the same UDP connection for DHT as for uTP. | |||
671 | * So even if this has been set as the dissector for this conversation | |||
672 | * or port, test it and reject it if not BT-DHT in order to give other | |||
673 | * dissectors, especially BT-uTP, a chance. | |||
674 | */ | |||
675 | if (!test_bt_dht(pinfo, tvb, 0, data)) { | |||
676 | return 0; | |||
677 | } | |||
678 | ||||
679 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "BT-DHT"); | |||
680 | col_clear(pinfo->cinfo, COL_INFO); | |||
681 | ||||
682 | /* XXX: There is a separate "bencode" dissector. Would it be possible | |||
683 | * to use it, at least to move some functions into a shared header? | |||
684 | * DHT has some keys with special meanings, and some values that | |||
685 | * are supposed to be interpreted specially (e.g., IP/port combinations), | |||
686 | * so maybe it's more trouble than it's worth. | |||
687 | */ | |||
688 | return dissect_bencoded_dict(tvb, pinfo, tree, 0, "BitTorrent DHT Protocol"); | |||
689 | } | |||
690 | ||||
691 | static | |||
692 | bool_Bool dissect_bt_dht_heur (tvbuff_t *tvb, packet_info *pinfo, | |||
693 | proto_tree *tree, void *data) | |||
694 | { | |||
695 | conversation_t *conversation; | |||
696 | ||||
697 | if (!test_bt_dht(pinfo, tvb, 0, data)) { | |||
| ||||
698 | return false0; | |||
699 | } | |||
700 | ||||
701 | conversation = find_or_create_conversation(pinfo); | |||
702 | conversation_set_dissector_from_frame_number(conversation, pinfo->num, bt_dht_handle); | |||
703 | ||||
704 | dissect_bt_dht(tvb, pinfo, tree, NULL((void*)0)); | |||
705 | return true1; | |||
706 | } | |||
707 | ||||
708 | void | |||
709 | proto_register_bt_dht(void) | |||
710 | { | |||
711 | expert_module_t* expert_bt_dht; | |||
712 | ||||
713 | static hf_register_info hf[] = { | |||
714 | { &hf_bencoded_string, | |||
715 | { "String", "bt-dht.bencoded.string", | |||
716 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) } | |||
717 | }, | |||
718 | { &hf_bencoded_list, | |||
719 | { "List", "bt-dht.bencoded.list", | |||
720 | FT_NONE, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) } | |||
721 | }, | |||
722 | { &hf_bencoded_int, | |||
723 | { "Int", "bt-dht.bencoded.int", | |||
724 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) } | |||
725 | }, | |||
726 | { &hf_bencoded_dict, | |||
727 | { "Dictionary", "bt-dht.bencoded.dict", | |||
728 | FT_NONE, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) } | |||
729 | }, | |||
730 | { &hf_bencoded_dict_entry, | |||
731 | { "Dictionary Entry", "bt-dht.bencoded.dict_entry", | |||
732 | FT_NONE, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) } | |||
733 | }, | |||
734 | { &hf_bencoded_list_terminator, | |||
735 | { "Terminator", "bt-dht.bencoded.list.terminator", | |||
736 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) } | |||
737 | }, | |||
738 | { &hf_bt_dht_error, | |||
739 | { "Error", "bt-dht.error", | |||
740 | FT_NONE, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) } | |||
741 | }, | |||
742 | { &hf_bt_dht_peer, | |||
743 | { "Peer", "bt-dht.peer", | |||
744 | FT_NONE, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) } | |||
745 | }, | |||
746 | { &hf_bt_dht_peers, | |||
747 | { "Peers", "bt-dht.peers", | |||
748 | FT_NONE, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) } | |||
749 | }, | |||
750 | { &hf_bt_dht_node, | |||
751 | { "Node", "bt-dht.node", | |||
752 | FT_NONE, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) } | |||
753 | }, | |||
754 | { &hf_bt_dht_nodes, | |||
755 | { "Nodes", "bt-dht.nodes", | |||
756 | FT_NONE, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) } | |||
757 | }, | |||
758 | { &hf_bt_dht_id, | |||
759 | { "ID", "bt-dht.id", | |||
760 | FT_BYTES, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) } | |||
761 | }, | |||
762 | { &hf_ip, | |||
763 | { "IP", "bt-dht.ip", | |||
764 | FT_IPv4, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) } | |||
765 | }, | |||
766 | { &hf_ip6, | |||
767 | { "IP", "bt-dht.ip6", | |||
768 | FT_IPv6, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) } | |||
769 | }, | |||
770 | { &hf_port, | |||
771 | { "Port", "bt-dht.port", | |||
772 | FT_UINT16, BASE_DEC, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) } | |||
773 | }, | |||
774 | { &hf_truncated_data, | |||
775 | { "Truncated data", "bt-dht.truncated_data", | |||
776 | FT_BYTES, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) } | |||
777 | } | |||
778 | }; | |||
779 | ||||
780 | static ei_register_info ei[] = { | |||
781 | { &ei_int_string, { "bt-dht.invalid_string", PI_MALFORMED0x07000000, PI_ERROR0x00800000, | |||
782 | "String must contain an integer", EXPFILL0, ((void*)0), 0, {0, {((void*)0), ((void*)0), FT_NONE, BASE_NONE , ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE, -1, ((void *)0)}} }}, | |||
783 | { &ei_invalid_len, { "bt-dht.invalid_length", PI_MALFORMED0x07000000, PI_ERROR0x00800000, | |||
784 | "Invalid length", EXPFILL0, ((void*)0), 0, {0, {((void*)0), ((void*)0), FT_NONE, BASE_NONE , ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE, -1, ((void *)0)}} }}, | |||
785 | { &ei_duplicate_dict_keys, { "bt-dht.bencoding.dict_duplicate_key", PI_PROTOCOL0x09000000, PI_WARN0x00600000, | |||
786 | "Dictionary has duplicate keys", EXPFILL0, ((void*)0), 0, {0, {((void*)0), ((void*)0), FT_NONE, BASE_NONE , ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE, -1, ((void *)0)}} }}, | |||
787 | { &ei_unsorted_dict_keys, { "bt-dht.bencoding.dict_out_of_order", PI_PROTOCOL0x09000000, PI_CHAT0x00200000, | |||
788 | "Dictionary keys are not in sorted order", EXPFILL0, ((void*)0), 0, {0, {((void*)0), ((void*)0), FT_NONE, BASE_NONE , ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE, -1, ((void *)0)}} }}, | |||
789 | }; | |||
790 | ||||
791 | /* Setup protocol subtree array */ | |||
792 | static int *ett[] = { | |||
793 | &ett_bt_dht, | |||
794 | &ett_bencoded_list, | |||
795 | &ett_bencoded_dict, | |||
796 | &ett_bt_dht_error, | |||
797 | &ett_bt_dht_peers, | |||
798 | &ett_bt_dht_nodes, | |||
799 | &ett_bencoded_dict_entry | |||
800 | }; | |||
801 | ||||
802 | module_t *bt_dht_module; | |||
803 | ||||
804 | proto_bt_dht = proto_register_protocol ("BitTorrent DHT Protocol", "BT-DHT", "bt-dht"); | |||
805 | ||||
806 | bt_dht_module = prefs_register_protocol(proto_bt_dht, NULL((void*)0)); | |||
807 | prefs_register_obsolete_preference(bt_dht_module, "enable"); | |||
808 | ||||
809 | proto_register_field_array(proto_bt_dht, hf, array_length(hf)(sizeof (hf) / sizeof (hf)[0])); | |||
810 | proto_register_subtree_array(ett, array_length(ett)(sizeof (ett) / sizeof (ett)[0])); | |||
811 | ||||
812 | expert_bt_dht = expert_register_protocol(proto_bt_dht); | |||
813 | expert_register_field_array(expert_bt_dht, ei, array_length(ei)(sizeof (ei) / sizeof (ei)[0])); | |||
814 | ||||
815 | bt_dht_handle = register_dissector("bt-dht", dissect_bt_dht, proto_bt_dht); | |||
816 | } | |||
817 | ||||
818 | void | |||
819 | proto_reg_handoff_bt_dht(void) | |||
820 | { | |||
821 | heur_dissector_add("udp", dissect_bt_dht_heur, "BitTorrent DHT over UDP", "bittorrent_dht_udp", proto_bt_dht, HEURISTIC_ENABLE); | |||
822 | ||||
823 | // If this is ever streamed (transported over TCP) we need to add recursion checks. | |||
824 | dissector_add_for_decode_as_with_preference("udp.port", bt_dht_handle); | |||
825 | } | |||
826 | ||||
827 | /* | |||
828 | * Editor modelines | |||
829 | * | |||
830 | * Local Variables: | |||
831 | * c-basic-offset: 2 | |||
832 | * tab-width: 8 | |||
833 | * indent-tabs-mode: nil | |||
834 | * End: | |||
835 | * | |||
836 | * ex: set shiftwidth=2 tabstop=8 expandtab: | |||
837 | * :indentSize=2:tabSize=8:noTabs=true: | |||
838 | */ |