Bug Summary

File:builds/wireshark/wireshark/epan/dissectors/packet-bt-dht.c
Warning:line 653, column 33
Null pointer passed to 1st parameter expecting 'nonnull'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name packet-bt-dht.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -fno-delete-null-pointer-checks -mframe-pointer=all -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -ffloat16-excess-precision=fast -fbfloat16-excess-precision=fast -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/builds/wireshark/wireshark/build -fcoverage-compilation-dir=/builds/wireshark/wireshark/build -resource-dir /usr/lib/llvm-21/lib/clang/21 -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -isystem /builds/wireshark/wireshark/epan/dissectors -isystem /builds/wireshark/wireshark/build/epan/dissectors -isystem /usr/include/mit-krb5 -isystem /usr/include/libxml2 -isystem /builds/wireshark/wireshark/epan -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D WS_BUILD_DLL -D WS_DEBUG -D WS_DEBUG_UTF_8 -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -D _GLIBCXX_ASSERTIONS -internal-isystem /usr/lib/llvm-21/lib/clang/21/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/builds/wireshark/wireshark/= -fmacro-prefix-map=/builds/wireshark/wireshark/build/= -fmacro-prefix-map=../= -Wno-format-nonliteral -std=gnu11 -ferror-limit 19 -fvisibility=hidden -fwrapv -fwrapv-pointer -fstrict-flex-arrays=3 -stack-protector 2 -fstack-clash-protection -fcf-protection=full -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fexceptions -fcolor-diagnostics -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /builds/wireshark/wireshark/sbout/2026-01-25-100340-3605-1 -x c /builds/wireshark/wireshark/epan/dissectors/packet-bt-dht.c
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
29void proto_register_bt_dht(void);
30void 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
37static int proto_bt_dht;
38static dissector_handle_t bt_dht_handle;
39
40/* fields */
41static int hf_bencoded_int;
42static int hf_bencoded_string;
43static int hf_bencoded_list;
44static int hf_bencoded_dict;
45static int hf_bencoded_dict_entry;
46static int hf_bencoded_list_terminator;
47
48static int hf_bt_dht_error;
49static int hf_bt_dht_peers;
50static int hf_bt_dht_peer;
51static int hf_bt_dht_nodes;
52static int hf_bt_dht_node;
53static int hf_bt_dht_id;
54static int hf_version;
55static int hf_version_client;
56static int hf_version_number;
57static int hf_version_raw;
58
59static int hf_ip;
60static int hf_ip6;
61static int hf_port;
62static int hf_truncated_data;
63
64static expert_field ei_int_string;
65static expert_field ei_invalid_len;
66static expert_field ei_duplicate_dict_keys;
67static expert_field ei_unsorted_dict_keys;
68
69/* tree types */
70static int ett_bt_dht;
71static int ett_bencoded_list;
72static int ett_bencoded_dict;
73static int ett_bencoded_dict_entry;
74static int ett_bt_dht_error;
75static int ett_bt_dht_peers;
76static int ett_bt_dht_nodes;
77static int ett_bt_dht_version;
78
79/* some keys use short name in packet */
80static const value_string short_key_name_value_string[] = {
81 { 'a', "Request arguments" },
82 { 'e', "Error" },
83 { 'q', "Request type" },
84 { 'r', "Response values" },
85 { 't', "Transaction ID" },
86 { 'v', "Version" },
87 { 'y', "Message type" },
88 { 0, NULL((void*)0) }
89};
90
91/* some values use short name in packet */
92static const value_string short_val_name_value_string[] = {
93 { 'e', "Error" },
94 { 'q', "Request" },
95 { 'r', "Response" },
96 { 0, NULL((void*)0) }
97};
98
99struct client_data {
100 const size_t version_length;
101 const char *encoded_name;
102 const char *client_name;
103 const char * (*client_version)(packet_info *pinfo, tvbuff_t *tvb, unsigned offset);
104};
105
106static const char *
107dissect_client_version_libtorrent(packet_info *pinfo, tvbuff_t *tvb, const unsigned int offset);
108
109static const struct client_data client_data[] = {
110 { 4, "LT", "libtorrent", dissect_client_version_libtorrent },
111 { 4, "UT", "uTorrent", NULL((void*)0) },
112 { 4, "lt", "rTorrent", NULL((void*)0) },
113 { 6, "MO", "Monotorrent", NULL((void*)0) },
114 { 4, "XDHT", "XDHT", NULL((void*)0) },
115 { 0, NULL((void*)0), NULL((void*)0), NULL((void*)0) }
116};
117
118static const char dict_str[] = "Dictionary...";
119static const char list_str[] = "List...";
120
121
122static inline bool_Bool
123bencoded_string_length(packet_info *pinfo, tvbuff_t *tvb, unsigned *offset_ptr, unsigned *length)
124{
125 unsigned offset, start;
126 unsigned remaining = tvb_captured_length_remaining(tvb, *offset_ptr);
127 if (remaining == 0)
128 return false0;
129
130 offset = *offset_ptr;
131 start = offset;
132
133 while (tvb_get_uint8(tvb, offset) != ':' && --remaining)
134 ++offset;
135
136 if (remaining && ws_strtou32((char*)tvb_get_string_enc(pinfo->pool, tvb, start, offset-start, ENC_ASCII0x00000000),
137 NULL((void*)0), length)) {
138 ++offset; /* skip the ':' */
139 *offset_ptr = offset;
140 return true1;
141 }
142 return false0;
143}
144
145
146/*
147 * dissect a bencoded string from tvb, start at offset. it's like "5:abcde"
148 * *result will be the decoded value
149 */
150
151static int
152dissect_bencoded_string(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset, const char **result, bool_Bool tohex, const char *label)
153{
154 unsigned string_len;
155 if (!bencoded_string_length(pinfo, tvb, &offset, &string_len))
30
Assuming the condition is true
31
Taking true branch
156 return 0;
32
Returning without writing to '*result'
157
158 const unsigned remaining = tvb_captured_length_remaining(tvb, offset);
159 if (remaining < string_len)
160 return 0;
161
162 /* fill the return data */
163 if (string_len == 0)
164 *result = "";
165 else if (tohex)
166 *result = tvb_bytes_to_str(pinfo->pool, tvb, offset, string_len);
167 else
168 *result = (char*)tvb_get_string_enc(pinfo->pool, tvb, offset, string_len, ENC_ASCII0x00000000);
169
170 proto_tree_add_string_format(tree, hf_bencoded_string, tvb, offset, string_len, *result, "%s: %s", label, *result);
171 offset += string_len;
172 return offset;
173}
174
175/*
176 * dissect a bencoded integer from tvb, start at offset. it's like "i5673e"
177 * *result will be the decoded value
178 */
179static int
180dissect_bencoded_int(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset, const char **result, const char *label)
181{
182 unsigned start_offset;
183 unsigned remaining = tvb_captured_length_remaining(tvb, offset);
184
185 /* the shortest valid integer is i0e, so we need at least 3 bytes */
186 if (remaining < 3)
187 return 0;
188
189 if (tvb_get_uint8(tvb, offset) != 'i')
190 return 0;
191
192 offset += 1;
193 remaining -= 1;
194 start_offset = offset;
195 while (tvb_get_uint8(tvb, offset) != 'e' && --remaining)
196 offset += 1;
197
198 if (remaining == 0)
199 return 0;
200
201 proto_tree_add_item(tree, hf_bencoded_list_terminator, tvb, offset, 1, ENC_ASCII0x00000000);
202
203 *result = (char*)tvb_get_string_enc(pinfo->pool, tvb, start_offset, offset - start_offset, ENC_ASCII0x00000000);
204 proto_tree_add_string_format(tree, hf_bencoded_int, tvb, start_offset, offset - start_offset, *result,
205 "%s: %s", label, *result);
206
207 offset += 1;
208 return offset;
209}
210
211/* pre definition of dissect_bencoded_dict(), which is needed by dissect_bencoded_list() */
212static int dissect_bencoded_dict(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset, const char *label);
213
214/* dissect a bencoded list from tvb, start at offset. it's like "lXXXe", "X" is any bencoded thing */
215static int
216// NOLINTNEXTLINE(misc-no-recursion)
217dissect_bencoded_list(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset, const char *label)
218{
219 proto_item *ti;
220 proto_tree *sub_tree;
221 unsigned one_byte;
222 const char *result;
223
224 /* the shortest valid list is "le", so we need at least 2 bytes */
225 if (tvb_captured_length_remaining(tvb, offset) < 2)
226 return 0;
227
228 ti = proto_tree_add_none_format(tree, hf_bencoded_list, tvb, offset, 0, "%s: list...", label);
229 sub_tree = proto_item_add_subtree(ti, ett_bencoded_list);
230
231 if (tvb_get_uint8(tvb, offset) != 'l')
232 return 0;
233 offset += 1;
234
235 while (tvb_captured_length_remaining(tvb, offset) > 0)
236 {
237 one_byte = tvb_get_uint8(tvb, offset);
238 if (one_byte == 'e')
239 break;
240
241 unsigned start_offset = offset;
242 switch (one_byte)
243 {
244 /* a integer */
245 case 'i':
246 offset = dissect_bencoded_int(tvb, pinfo, sub_tree, offset, &result, "Integer");
247 break;
248 /* a sub-list */
249 case 'l':
250 offset = dissect_bencoded_list(tvb, pinfo, sub_tree, offset, "Sub-list");
251 break;
252 /* a dictionary */
253 case 'd':
254 offset = dissect_bencoded_dict(tvb, pinfo, sub_tree, offset, "Sub-dict");
255 break;
256 /* a string */
257 default:
258 offset = dissect_bencoded_string(tvb, pinfo, sub_tree, offset, &result, false0, "String");
259 break;
260 }
261 if (offset <= start_offset)
262 {
263 proto_tree_add_expert_remaining(sub_tree, pinfo, &ei_int_string, tvb, offset);
264 /* if offset is not going on, there is no chance to exit the loop, then return*/
265 return 0;
266 }
267 }
268
269 if (tvb_captured_length_remaining(tvb, offset) == 0)
270 return 0;
271
272 proto_tree_add_item(sub_tree, hf_bencoded_list_terminator, tvb, offset, 1, ENC_ASCII0x00000000);
273 offset += 1;
274 return offset;
275}
276
277/* dissect a bt dht error from tvb, start at offset. it's like "li201e9:error msge" */
278static int
279dissect_bt_dht_error(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset, const char **result, const char *label)
280{
281 proto_item *ti;
282 proto_tree *sub_tree;
283 const char *error_no, *error_msg;
284
285 error_no = NULL((void*)0);
286 error_msg = NULL((void*)0);
287
288 ti = proto_tree_add_item(tree, hf_bt_dht_error, tvb, offset, 0, ENC_NA0x00000000);
289 sub_tree = proto_item_add_subtree(ti, ett_bt_dht_error);
290
291 /* we have confirmed that the first byte is 'l' */
292 offset += 1;
293
294 /* dissect bt-dht error number and message */
295 offset = dissect_bencoded_int(tvb, pinfo, sub_tree, offset, &error_no, "Error ID");
296 if (offset == 0) {
297 return 0;
298 }
299 offset = dissect_bencoded_string(tvb, pinfo, sub_tree, offset, &error_msg, false0, "Error Message");
300 if (offset == 0) {
301 return 0;
302 }
303
304 proto_item_set_text(ti, "%s: error %s, %s", label, error_no, error_msg);
305 col_append_fstr(pinfo->cinfo, COL_INFO, " No=%s Msg=%s", error_no, error_msg);
306 *result = wmem_strdup_printf(pinfo->pool, "error %s, %s", error_no, error_msg);
307
308 return offset + 1;
309}
310
311/* dissect a bt dht values list from tvb, start at offset. it's like "l6:....6:....e" */
312static int
313dissect_bt_dht_values(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset, const char **result, const char *label)
314{
315 proto_item *ti;
316 proto_tree *sub_tree;
317 proto_item *value_ti;
318 proto_tree *value_tree;
319
320 unsigned peer_index;
321 unsigned string_len;
322
323 ti = proto_tree_add_item(tree, hf_bt_dht_peers, tvb, offset, 0, ENC_NA0x00000000);
324 sub_tree = proto_item_add_subtree(ti, ett_bt_dht_peers);
325
326 peer_index = 0;
327 /* we has confirmed that the first byte is 'l' */
328 offset += 1;
329
330 /* dissect bt-dht values */
331 while (tvb_get_uint8(tvb, offset) != 'e')
332 {
333 if (!bencoded_string_length(pinfo, tvb, &offset, &string_len))
334 {
335 expert_add_info(pinfo, ti, &ei_invalid_len);
336 // Fail hard here rather than potentially looping excessively.
337 return 0;
338 }
339 else if (string_len == 6)
340 {
341 /* 4 bytes ip, 2 bytes port */
342 peer_index += 1;
343
344 value_ti = proto_tree_add_item(sub_tree, hf_bt_dht_peer, tvb, offset, 6, ENC_NA0x00000000);
345 proto_item_append_text(value_ti, " %d", peer_index);
346 value_tree = proto_item_add_subtree(value_ti, ett_bt_dht_peers);
347
348 proto_tree_add_item(value_tree, hf_ip, tvb, offset, 4, ENC_BIG_ENDIAN0x00000000);
349 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));
350 proto_tree_add_item(value_tree, hf_port, tvb, offset + 4, 2, ENC_BIG_ENDIAN0x00000000);
351 proto_item_append_text(value_ti, ":%u)", tvb_get_ntohs(tvb, offset + 4));
352 }
353 else if (string_len == 18)
354 {
355 /* 16 bytes ip, 2 bytes port */
356 peer_index += 1;
357
358 value_ti = proto_tree_add_item(sub_tree, hf_bt_dht_peer, tvb, offset, 18, ENC_NA0x00000000);
359 proto_item_append_text(value_ti, " %d", peer_index);
360 value_tree = proto_item_add_subtree(value_ti, ett_bt_dht_peers);
361
362 proto_tree_add_item(value_tree, hf_ip6, tvb, offset, 16, ENC_NA0x00000000);
363 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));
364 proto_tree_add_item(value_tree, hf_port, tvb, offset + 16, 2, ENC_BIG_ENDIAN0x00000000);
365 proto_item_append_text(value_ti, ":%u)", tvb_get_ntohs(tvb, offset + 16));
366 }
367 else
368 {
369 /* truncated data */
370 proto_tree_add_item(tree, hf_truncated_data, tvb, offset, string_len, ENC_NA0x00000000);
371 }
372
373 offset += string_len;
374 }
375
376 if (tvb_get_uint8(tvb, offset) == 'e') { /* list ending delimiter */
377 proto_tree_add_item(sub_tree, hf_bencoded_list_terminator, tvb, offset, 1, ENC_ASCII0x00000000);
378 offset++;
379 }
380
381 proto_item_set_text(ti, "%s: %d peers", label, peer_index);
382 col_append_fstr(pinfo->cinfo, COL_INFO, " Peers=%d", peer_index);
383 *result = wmem_strdup_printf(pinfo->pool, "%d peers", peer_index);
384
385 return offset;
386}
387
388static int
389dissect_bt_dht_nodes(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset, const char **result, const char *label, bool_Bool is_ipv6)
390{
391 proto_item *ti;
392 proto_tree *sub_tree;
393 proto_item *node_ti;
394 proto_tree *node_tree;
395
396 unsigned node_index;
397 unsigned string_len;
398 unsigned node_byte_length;
399
400 if (!bencoded_string_length(pinfo, tvb, &offset, &string_len))
401 return 0;
402
403 ti = proto_tree_add_item(tree, hf_bt_dht_nodes, tvb, offset, string_len, ENC_NA0x00000000);
404 sub_tree = proto_item_add_subtree(ti, ett_bt_dht_nodes);
405 node_index = 0;
406
407 /* 26 bytes = 20 bytes id + 4 bytes ipv4 address + 2 bytes port */
408 node_byte_length = 26;
409
410 if (is_ipv6)
411 {
412 /* 38 bytes = 20 bytes id + 16 bytes ipv6 address + 2 bytes port */
413 node_byte_length = 38;
414 }
415
416 for (; string_len >= node_byte_length; string_len -= node_byte_length, offset += node_byte_length)
417 {
418 node_index += 1;
419
420 node_ti = proto_tree_add_item(sub_tree, hf_bt_dht_node, tvb, offset, node_byte_length, ENC_NA0x00000000);
421 proto_item_append_text(node_ti, " %d", node_index);
422 node_tree = proto_item_add_subtree(node_ti, ett_bt_dht_peers);
423
424 proto_tree_add_item(node_tree, hf_bt_dht_id, tvb, offset, 20, ENC_NA0x00000000);
425 proto_item_append_text(node_ti, " (id: %s", tvb_bytes_to_str(pinfo->pool, tvb, offset, 20));
426
427 if (is_ipv6)
428 {
429 proto_tree_add_item(node_tree, hf_ip6, tvb, offset + 20, 16, ENC_NA0x00000000);
430 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));
431
432 proto_tree_add_item(node_tree, hf_port, tvb, offset + 36, 2, ENC_BIG_ENDIAN0x00000000);
433 proto_item_append_text(node_ti, ":%u)", tvb_get_ntohs(tvb, offset + 36));
434 }
435 else
436 {
437 proto_tree_add_item(node_tree, hf_ip, tvb, offset + 20, 4, ENC_BIG_ENDIAN0x00000000);
438 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));
439
440 proto_tree_add_item(node_tree, hf_port, tvb, offset + 24, 2, ENC_BIG_ENDIAN0x00000000);
441 proto_item_append_text(node_ti, ":%u)", tvb_get_ntohs(tvb, offset + 24));
442 }
443 }
444
445 if (string_len > 0)
446 {
447 proto_tree_add_item(tree, hf_truncated_data, tvb, offset, string_len, ENC_NA0x00000000);
448 offset += string_len;
449 }
450 proto_item_set_text(ti, "%s: %d nodes", label, node_index);
451 col_append_fstr(pinfo->cinfo, COL_INFO, " Nodes=%d", node_index);
452 *result = wmem_strdup_printf(pinfo->pool, "%d", node_index);
453
454 return offset;
455}
456
457static const char *
458dissect_client_version_libtorrent(packet_info *pinfo, tvbuff_t *tvb, const unsigned int offset)
459{
460 const uint8_t version_major = tvb_get_uint8(tvb, offset + 2);
461 const uint8_t version_minor = tvb_get_uint8(tvb, offset + 3) >> 4;
462 const uint8_t version_tiny = tvb_get_uint8(tvb, offset + 3) & 0x0F;
463 return wmem_strdup_printf(pinfo->pool, "%d.%d.%d", version_major, version_minor, version_tiny);
464}
465
466static void
467dissect_client_version(packet_info *pinfo, tvbuff_t *tvb, const unsigned int offset,
468 const unsigned int version_length, int *name_bytes,
469 const char **client_name, const char **client_version)
470{
471 *name_bytes = 0;
472 *client_name = NULL((void*)0);
473 *client_version = NULL((void*)0);
474
475 for (const struct client_data *client = client_data; client->encoded_name; client++) {
476 if (version_length != client->version_length) {
477 continue;
478 }
479
480 const int name_length = (int)strlen(client->encoded_name);
481 if (tvb_strneql(tvb, offset, client->encoded_name, name_length) != 0) {
482 continue;
483 }
484
485 *name_bytes = name_length;
486 *client_name = client->client_name;
487 if (client->client_version != NULL((void*)0)) {
488 *client_version = client->client_version(pinfo, tvb, offset);
489 }
490
491 return;
492 }
493}
494
495static bool_Bool
496dissect_version(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, const unsigned int key_offset,
497 unsigned int *offset)
498{
499 unsigned int version_length;
500 if (!bencoded_string_length(pinfo, tvb, offset, &version_length)) {
501 return false0;
502 }
503 const unsigned int version_start = *offset;
504
505 int name_bytes = 0;
506 const char * client_name = NULL((void*)0);
507 const char * client_version = NULL((void*)0);
508 dissect_client_version(pinfo, tvb, version_start, version_length, &name_bytes, &client_name, &client_version);
509
510 proto_item *ti = proto_tree_add_none_format(tree, hf_version, tvb, key_offset,
511 version_start - key_offset + version_length,
512 "Client version");
513 if (client_name != NULL((void*)0)) {
514 proto_item_append_text(ti, ": %s", client_name);
515 }
516 if (client_version != NULL((void*)0)) {
517 proto_item_append_text(ti, " %s", client_version);
518 }
519
520 proto_tree *sub_tree = proto_item_add_subtree(ti, ett_bt_dht_version);
521 if (client_name != NULL((void*)0)) {
522 proto_tree_add_string(sub_tree, hf_version_client, tvb, version_start, name_bytes, client_name);
523 }
524 if (client_version != NULL((void*)0)) {
525 proto_tree_add_string(sub_tree, hf_version_number, tvb, version_start + name_bytes,
526 version_length - name_bytes, client_version);
527 }
528 proto_tree_add_item(sub_tree, hf_version_raw, tvb, version_start, version_length, ENC_NA0x00000000);
529
530 *offset += version_length;
531 return true1;
532}
533
534static int
535// NOLINTNEXTLINE(misc-no-recursion)
536dissect_bencoded_dict_entry(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset, const char **key)
537{
538 proto_item *ti;
539 proto_tree *sub_tree;
540 bool_Bool tohex;
541 const char *val;
542 unsigned orig_offset = offset;
543
544 val = NULL((void*)0);
15
Null pointer value stored to 'val'
545
546 ti = proto_tree_add_item(tree, hf_bencoded_dict_entry, tvb, offset, 0, ENC_NA0x00000000);
547 sub_tree = proto_item_add_subtree(ti, ett_bencoded_dict_entry);
548
549 /* dissect the key, it must be a string */
550 offset = dissect_bencoded_string(tvb, pinfo, sub_tree, offset, key, false0, "Key");
551 if (offset == 0 || !*key)
16
Assuming 'offset' is not equal to 0
17
Assuming the condition is false
18
Taking false branch
552 {
553 proto_tree_add_expert_format_remaining(sub_tree, pinfo, &ei_int_string, tvb, offset, "Invalid string for Key");
554 return 0;
555 }
556
557 if (tvb_captured_length_remaining(tvb, offset) == 0)
19
Assuming the condition is false
20
Taking false branch
558 return 0;
559
560 bool_Bool hide_dict_entry = false0;
561 /* If it is a dict, then just do recursion */
562 switch (tvb_get_uint8(tvb, offset))
21
Control jumps to the 'default' case at line 584
563 {
564 case 'd':
565 offset = dissect_bencoded_dict(tvb, pinfo, sub_tree, offset, "Value");
566 val = dict_str;
567 break;
568 case 'l':
569 if (strcmp(*key, "e") == 0)
570 offset = dissect_bt_dht_error(tvb, pinfo, sub_tree, offset, &val, "Value");
571 else if (strcmp(*key, "values") == 0)
572 offset = dissect_bt_dht_values(tvb, pinfo, sub_tree, offset, &val, "Value");
573 /* other unfamiliar lists */
574 else
575 {
576 offset = dissect_bencoded_list(tvb, pinfo, sub_tree, offset, "Value");
577 val = list_str;
578 }
579 break;
580 case 'i':
581 offset = dissect_bencoded_int(tvb, pinfo, sub_tree, offset, &val, "Value");
582 break;
583 /* it's a string */
584 default:
585 /* special process */
586 if (strcmp(*key, "nodes") == 0)
22
Assuming the condition is false
23
Taking false branch
587 {
588 offset = dissect_bt_dht_nodes(tvb, pinfo, sub_tree, offset, &val, "Value", 0);
589 }
590 else if (strcmp(*key, "nodes6") == 0)
24
Assuming the condition is false
25
Taking false branch
591 {
592 offset = dissect_bt_dht_nodes(tvb, pinfo, sub_tree, offset, &val, "Value", 1);
593 }
594 else if (strcmp(*key, "ip") == 0)
26
Assuming the condition is false
27
Taking false branch
595 {
596 /*
597 * BEP 42 DHT Security extension
598 * https://www.bittorrent.org/beps/bep_0042.html
599 * https://www.rasterbar.com/products/libtorrent/dht_sec.html
600 */
601
602 unsigned len;
603 int old_offset = offset;
604 if (!bencoded_string_length(pinfo, tvb, &offset, &len)) {
605 proto_tree_add_expert_format_remaining(sub_tree, pinfo, &ei_int_string, tvb, offset, "Invalid string for value");
606 return 0;
607 }
608
609 if (len == 6) {
610 proto_tree_add_item(sub_tree, hf_ip, tvb, offset, 4, ENC_BIG_ENDIAN0x00000000);
611 val = tvb_ip_to_str(pinfo->pool, tvb, offset)tvb_address_to_str(pinfo->pool, tvb, AT_IPv4, offset);
612 offset += 4;
613 proto_tree_add_item(sub_tree, hf_port, tvb, offset, 2, ENC_BIG_ENDIAN0x00000000);
614 offset += 2;
615 }
616 else if (len == 18)
617 {
618 proto_tree_add_item(sub_tree, hf_ip6, tvb, offset, 16, ENC_NA0x00000000);
619 val = tvb_ip6_to_str(pinfo->pool, tvb, offset)tvb_address_to_str(pinfo->pool, tvb, AT_IPv6, offset);
620 offset += 16;
621
622 proto_tree_add_item(sub_tree, hf_port, tvb, offset, 2, ENC_BIG_ENDIAN0x00000000);
623 offset += 2;
624 }
625 else {
626 offset = dissect_bencoded_string(tvb, pinfo, sub_tree, old_offset, &val, true1, "Value");
627 }
628 }
629 else if (strcmp(*key, "v") == 0)
28
Taking true branch
630 {
631 hide_dict_entry = true1;
632 dissect_bencoded_string(tvb, pinfo, sub_tree, offset, &val, true1, "Value");
29
Calling 'dissect_bencoded_string'
33
Returning from 'dissect_bencoded_string'
633 if (!dissect_version(tvb, pinfo, tree, orig_offset, &offset)) {
34
Taking false branch
634 return 0;
635 }
636 }
637 else
638 {
639 /* some need to return hex string */
640 tohex = strcmp(*key, "id") == 0 || strcmp(*key, "target") == 0
641 || strcmp(*key, "info_hash") == 0 || strcmp(*key, "t") == 0
642 || strcmp(*key, "token") == 0;
643 offset = dissect_bencoded_string(tvb, pinfo, sub_tree, offset, &val, tohex, "Value");
644 }
645 }
646
647 if (offset == 0)
35
Assuming 'offset' is not equal to 0
648 {
649 proto_tree_add_expert_format_remaining(sub_tree, pinfo, &ei_int_string, tvb, offset, "Invalid string for value");
650 return 0;
651 }
652
653 if (strcmp(*key, "q") == 0 && strlen(val) > 1)
36
Null pointer passed to 1st parameter expecting 'nonnull'
654 col_prepend_fstr(pinfo->cinfo, COL_INFO, "%c%s", g_ascii_toupper(val[0]), val + 1);
655 if (strcmp(*key, "r") == 0)
656 col_prepend_fstr(pinfo->cinfo, COL_INFO, "Response");
657 if (strcmp(*key, "e") == 0)
658 col_prepend_fstr(pinfo->cinfo, COL_INFO, "Error");
659 if ((strcmp(*key, "info_hash") == 0 || strcmp(*key, "target") == 0))
660 col_append_fstr(pinfo->cinfo, COL_INFO, " %c%s=%s", g_ascii_toupper((*key)[0]), *key + 1, val);
661
662 const char * printable_key = *key;
663 if (strlen(*key) == 1)
664 printable_key = val_to_str_const((*key)[0], short_key_name_value_string, *key);
665 if (val && strlen(val) == 1)
666 val = val_to_str_const(val[0], short_val_name_value_string, val);
667
668 proto_item_set_text(ti, "%s: %s", printable_key, val);
669 proto_item_set_len(ti, offset - orig_offset);
670
671 if (hide_dict_entry) {
672 proto_item_set_hidden(ti);
673 }
674
675 return offset;
676}
677
678/* dict = d...e */
679static int
680// NOLINTNEXTLINE(misc-no-recursion)
681dissect_bencoded_dict(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset, const char *label)
682{
683 proto_item *ti;
684 proto_tree *sub_tree;
685 unsigned orig_offset = offset;
686
687 /* the shortest valid dictionary is "de", so we need at least 2 bytes */
688 if (tvb_captured_length_remaining(tvb, offset) < 2)
5
Assuming the condition is false
6
Taking false branch
689 return 0;
690
691 if (offset
6.1
'offset' is equal to 0
== 0)
7
Taking true branch
692 {
693 ti = proto_tree_add_item(tree, proto_bt_dht, tvb, 0, -1, ENC_NA0x00000000);
694 sub_tree = proto_item_add_subtree(ti, ett_bt_dht);
695 }
696 else
697 {
698 ti = proto_tree_add_none_format(tree, hf_bencoded_dict, tvb, offset, -1, "%s: Dictionary...", label);
699 sub_tree = proto_item_add_subtree(ti, ett_bencoded_dict);
700 }
701
702 if (tvb_get_uint8(tvb, offset) != 'd')
8
Assuming the condition is false
9
Taking false branch
703 return 0;
704 offset += 1;
705
706 const char * prev_key = NULL((void*)0);
707 unsigned prev_key_offset = 0;
708 while (tvb_captured_length_remaining(tvb, offset) > 0) {
10
Assuming the condition is true
11
Loop condition is true. Entering loop body
709 if (tvb_get_uint8(tvb, offset) == 'e')
12
Assuming the condition is false
13
Taking false branch
710 break;
711
712 const char * key = NULL((void*)0);
713 const unsigned entry_start = offset;
714 offset = dissect_bencoded_dict_entry(tvb, pinfo, sub_tree, offset, &key);
14
Calling 'dissect_bencoded_dict_entry'
715 if (offset == 0)
716 {
717 proto_tree_add_expert_remaining(sub_tree, pinfo, &ei_int_string, tvb, offset);
718 return 0;
719 }
720
721 if (prev_key != NULL((void*)0) && key != NULL((void*)0)) {
722 const int ordering = strcmp(key, prev_key);
723 if (ordering < 0) {
724 proto_tree_add_expert(
725 sub_tree, pinfo, &ei_unsorted_dict_keys, tvb, prev_key_offset, offset - prev_key_offset);
726 } else if (ordering == 0) {
727 proto_tree_add_expert(
728 sub_tree, pinfo, &ei_duplicate_dict_keys, tvb, prev_key_offset, offset - prev_key_offset);
729 }
730 }
731
732 prev_key = key;
733 prev_key_offset = entry_start;
734 }
735
736 if (tvb_captured_length_remaining(tvb, offset) == 0)
737 return 0;
738
739 proto_tree_add_item(sub_tree, hf_bencoded_list_terminator, tvb, offset, 1, ENC_ASCII0x00000000);
740 offset += 1;
741 proto_item_set_len(ti, offset - orig_offset);
742
743 return offset;
744}
745
746static bool_Bool
747test_bt_dht(packet_info *pinfo _U___attribute__((unused)), tvbuff_t *tvb, int offset, void *data _U___attribute__((unused)))
748{
749
750 /* The DHT KRPC protocol sends packets that are bencoded dictionaries.
751 * Bencoded dictionaries always have the keys in sorted (raw string)
752 * order. There are three possible message types, query, which has "a" and
753 * "q" keys that map to dictionaries, response, which has an "r" key
754 * that maps to a dictionary, and error, which has an "e" key that maps
755 * to a list.
756 *
757 * Conveniently, those keys appear in sort order before all other possible
758 * top level keys, with the exception of the "ip" key added in BEP-0042.
759 *
760 * Thus, there are only four possible initial sets of bytes, corresponding
761 * to beginning with an "a" dictionary, "r" dictionary, "ip" string, or an
762 * "e" list.
763 */
764
765 if (tvb_captured_length_remaining(tvb, offset) < DHT_MIN_LEN5)
766 return false0;
767
768 if (tvb_memeql(tvb, offset, (const uint8_t*)"d1:ad", 5) == 0) {
769 return true1;
770 } else if (tvb_memeql(tvb, offset, (const uint8_t*)"d1:rd", 5) == 0) {
771 return true1;
772 } else if (tvb_memeql(tvb, offset, (const uint8_t*)"d2:ip", 5) == 0) {
773 return true1;
774 } else if (tvb_memeql(tvb, offset, (const uint8_t*)"d1:el", 5) == 0) {
775 return true1;
776 }
777
778 return false0;
779}
780
781static int
782dissect_bt_dht(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
783{
784 /* BitTorrent clients use the same UDP connection for DHT as for uTP.
785 * So even if this has been set as the dissector for this conversation
786 * or port, test it and reject it if not BT-DHT in order to give other
787 * dissectors, especially BT-uTP, a chance.
788 */
789 if (!test_bt_dht(pinfo, tvb, 0, data)) {
3
Taking false branch
790 return 0;
791 }
792
793 col_set_str(pinfo->cinfo, COL_PROTOCOL, "BT-DHT");
794 col_clear(pinfo->cinfo, COL_INFO);
795
796 /* XXX: There is a separate "bencode" dissector. Would it be possible
797 * to use it, at least to move some functions into a shared header?
798 * DHT has some keys with special meanings, and some values that
799 * are supposed to be interpreted specially (e.g., IP/port combinations),
800 * so maybe it's more trouble than it's worth.
801 */
802 return dissect_bencoded_dict(tvb, pinfo, tree, 0, "BitTorrent DHT Protocol");
4
Calling 'dissect_bencoded_dict'
803}
804
805static
806bool_Bool dissect_bt_dht_heur (tvbuff_t *tvb, packet_info *pinfo,
807 proto_tree *tree, void *data)
808{
809 conversation_t *conversation;
810
811 if (!test_bt_dht(pinfo, tvb, 0, data)) {
1
Taking false branch
812 return false0;
813 }
814
815 conversation = find_or_create_conversation(pinfo);
816 conversation_set_dissector_from_frame_number(conversation, pinfo->num, bt_dht_handle);
817
818 dissect_bt_dht(tvb, pinfo, tree, NULL((void*)0));
2
Calling 'dissect_bt_dht'
819 return true1;
820}
821
822void
823proto_register_bt_dht(void)
824{
825 expert_module_t* expert_bt_dht;
826
827 static hf_register_info hf[] = {
828 { &hf_bencoded_string,
829 { "String", "bt-dht.bencoded.string",
830 FT_STRING, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
831 },
832 { &hf_bencoded_list,
833 { "List", "bt-dht.bencoded.list",
834 FT_NONE, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
835 },
836 { &hf_bencoded_int,
837 { "Int", "bt-dht.bencoded.int",
838 FT_STRING, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
839 },
840 { &hf_bencoded_dict,
841 { "Dictionary", "bt-dht.bencoded.dict",
842 FT_NONE, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
843 },
844 { &hf_bencoded_dict_entry,
845 { "Dictionary Entry", "bt-dht.bencoded.dict_entry",
846 FT_NONE, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
847 },
848 { &hf_bencoded_list_terminator,
849 { "Terminator", "bt-dht.bencoded.list.terminator",
850 FT_STRING, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
851 },
852 { &hf_bt_dht_error,
853 { "Error", "bt-dht.error",
854 FT_NONE, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
855 },
856 { &hf_bt_dht_peer,
857 { "Peer", "bt-dht.peer",
858 FT_NONE, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
859 },
860 { &hf_bt_dht_peers,
861 { "Peers", "bt-dht.peers",
862 FT_NONE, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
863 },
864 { &hf_bt_dht_node,
865 { "Node", "bt-dht.node",
866 FT_NONE, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
867 },
868 { &hf_bt_dht_nodes,
869 { "Nodes", "bt-dht.nodes",
870 FT_NONE, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
871 },
872 { &hf_bt_dht_id,
873 { "ID", "bt-dht.id",
874 FT_BYTES, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
875 },
876 { &hf_version,
877 { "Client version", "bt-dht.version",
878 FT_NONE, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
879 },
880 { &hf_version_client,
881 { "Client name", "bt-dht.version.client",
882 FT_STRING, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
883 },
884 { &hf_version_number,
885 { "Client version", "bt-dht.version.version",
886 FT_STRING, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
887 },
888 { &hf_version_raw,
889 { "Raw version bytes", "bt-dht.version.raw",
890 FT_BYTES, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
891 },
892 { &hf_ip,
893 { "IP", "bt-dht.ip",
894 FT_IPv4, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
895 },
896 { &hf_ip6,
897 { "IP", "bt-dht.ip6",
898 FT_IPv6, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
899 },
900 { &hf_port,
901 { "Port", "bt-dht.port",
902 FT_UINT16, BASE_DEC, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
903 },
904 { &hf_truncated_data,
905 { "Truncated data", "bt-dht.truncated_data",
906 FT_BYTES, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
907 }
908 };
909
910 static ei_register_info ei[] = {
911 { &ei_int_string, { "bt-dht.invalid_string", PI_MALFORMED0x07000000, PI_ERROR0x00800000,
912 "String must contain an integer", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE
, BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE
, -1, ((void*)0)}}
}},
913 { &ei_invalid_len, { "bt-dht.invalid_length", PI_MALFORMED0x07000000, PI_ERROR0x00800000,
914 "Invalid length", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE
, BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE
, -1, ((void*)0)}}
}},
915 { &ei_duplicate_dict_keys, { "bt-dht.bencoding.dict_duplicate_key", PI_PROTOCOL0x09000000, PI_WARN0x00600000,
916 "Dictionary has duplicate keys", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE
, BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE
, -1, ((void*)0)}}
}},
917 { &ei_unsorted_dict_keys, { "bt-dht.bencoding.dict_out_of_order", PI_PROTOCOL0x09000000, PI_CHAT0x00200000,
918 "Dictionary keys are not in sorted order", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE
, BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE
, -1, ((void*)0)}}
}},
919 };
920
921 /* Setup protocol subtree array */
922 static int *ett[] = {
923 &ett_bt_dht,
924 &ett_bencoded_list,
925 &ett_bencoded_dict,
926 &ett_bt_dht_error,
927 &ett_bt_dht_peers,
928 &ett_bt_dht_nodes,
929 &ett_bt_dht_version,
930 &ett_bencoded_dict_entry
931 };
932
933 module_t *bt_dht_module;
934
935 proto_bt_dht = proto_register_protocol("BitTorrent DHT Protocol", "BT-DHT", "bt-dht");
936
937 bt_dht_module = prefs_register_protocol(proto_bt_dht, NULL((void*)0));
938 prefs_register_obsolete_preference(bt_dht_module, "enable");
939
940 proto_register_field_array(proto_bt_dht, hf, array_length(hf)(sizeof (hf) / sizeof (hf)[0]));
941 proto_register_subtree_array(ett, array_length(ett)(sizeof (ett) / sizeof (ett)[0]));
942
943 expert_bt_dht = expert_register_protocol(proto_bt_dht);
944 expert_register_field_array(expert_bt_dht, ei, array_length(ei)(sizeof (ei) / sizeof (ei)[0]));
945
946 bt_dht_handle = register_dissector("bt-dht", dissect_bt_dht, proto_bt_dht);
947}
948
949void
950proto_reg_handoff_bt_dht(void)
951{
952 heur_dissector_add("udp", dissect_bt_dht_heur, "BitTorrent DHT over UDP", "bittorrent_dht_udp", proto_bt_dht, HEURISTIC_DISABLE);
953
954 // If this is ever streamed (transported over TCP) we need to add recursion checks.
955 dissector_add_for_decode_as_with_preference("udp.port", bt_dht_handle);
956}
957
958/*
959 * Editor modelines
960 *
961 * Local Variables:
962 * c-basic-offset: 2
963 * tab-width: 8
964 * indent-tabs-mode: nil
965 * End:
966 *
967 * ex: set shiftwidth=2 tabstop=8 expandtab:
968 * :indentSize=2:tabSize=8:noTabs=true:
969 */