### 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

Show analyzer invocation
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

Keyboard shortcuts:

- Use 'j/k' keys for keyboard navigation
- Use 'Shift+S' to show/hide relevant lines
- Use '?' to toggle this window

Close
Show only relevant lines Show control flow arrows

1 | /******************************************************************************************************/
2 | /* packet-bt-dht.c
3 | * Routines for BT-DHT dissection
4 | * Copyright 2011, Xiao Xiangquan <xiaoxiangquan@gmail.com>
5 | *
6 | * A plugin for BT-DHT packet:
7 | *
8 | * Wireshark - Network traffic analyzer
9 | * By Gerald Combs <gerald@wireshark.org>
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 | static int hf_version;
55 | static int hf_version_client;
56 | static int hf_version_number;
57 | static int hf_version_raw;
58 |
59 | static int hf_ip;
60 | static int hf_ip6;
61 | static int hf_port;
62 | static int hf_truncated_data;
63 |
64 | static expert_field ei_int_string;
65 | static expert_field ei_invalid_len;
66 | static expert_field ei_duplicate_dict_keys;
67 | static expert_field ei_unsorted_dict_keys;
68 |
69 | /* tree types */
70 | static int ett_bt_dht;
71 | static int ett_bencoded_list;
72 | static int ett_bencoded_dict;
73 | static int ett_bencoded_dict_entry;
74 | static int ett_bt_dht_error;
75 | static int ett_bt_dht_peers;
76 | static int ett_bt_dht_nodes;
77 | static int ett_bt_dht_version;
78 |
79 | /* some keys use short name in packet */
80 | static 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 */
92 | static const value_string short_val_name_value_string[] = {
93 | { 'e', "Error" },
94 | { 'q', "Request" },
95 | { 'r', "Response" },
96 | { 0, NULL((void*)0) }
97 | };
98 |
99 | struct 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 |
106 | static const char *
107 | dissect_client_version_libtorrent(packet_info *pinfo, tvbuff_t *tvb, const unsigned int offset);
108 |
109 | static 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 |
118 | static const char dict_str[] = "Dictionary...";
119 | static const char list_str[] = "List...";
120 |
121 |
122 | static inline bool_Bool
123 | bencoded_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 |
151 | static int
152 | dissect_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 | */
179 | static int
180 | dissect_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() */
212 | static 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 */
215 | static int
216 | // NOLINTNEXTLINE(misc-no-recursion)
217 | dissect_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" */
278 | static int
279 | dissect_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" */
312 | static int
313 | dissect_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 |
388 | static int
389 | 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)
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 |
457 | static const char *
458 | dissect_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 |
466 | static void
467 | dissect_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 |
495 | static bool_Bool
496 | dissect_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 |
534 | static int
535 | // NOLINTNEXTLINE(misc-no-recursion)
536 | dissect_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 */
679 | static int
680 | // NOLINTNEXTLINE(misc-no-recursion)
681 | dissect_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 |
746 | static bool_Bool
747 | test_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 |
781 | static int
782 | dissect_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 |
805 | static
806 | bool_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 |
822 | void
823 | proto_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 |
949 | void
950 | proto_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 | */