| File: | builds/wireshark/wireshark/epan/dissectors/packet-bt-dht.c |
| Warning: | line 653, column 33 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 | 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)) | |||
| 156 | return 0; | |||
| 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); | |||
| 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) | |||
| 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) | |||
| 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)) | |||
| 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) | |||
| 587 | { | |||
| 588 | offset = dissect_bt_dht_nodes(tvb, pinfo, sub_tree, offset, &val, "Value", 0); | |||
| 589 | } | |||
| 590 | else if (strcmp(*key, "nodes6") == 0) | |||
| 591 | { | |||
| 592 | offset = dissect_bt_dht_nodes(tvb, pinfo, sub_tree, offset, &val, "Value", 1); | |||
| 593 | } | |||
| 594 | else if (strcmp(*key, "ip") == 0) | |||
| 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) | |||
| 630 | { | |||
| 631 | hide_dict_entry = true1; | |||
| 632 | dissect_bencoded_string(tvb, pinfo, sub_tree, offset, &val, true1, "Value"); | |||
| 633 | if (!dissect_version(tvb, pinfo, tree, orig_offset, &offset)) { | |||
| 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) | |||
| 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) | |||
| ||||
| 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) | |||
| 689 | return 0; | |||
| 690 | ||||
| 691 | if (offset
| |||
| 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') | |||
| 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) { | |||
| 709 | if (tvb_get_uint8(tvb, offset) == 'e') | |||
| 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); | |||
| 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)) { | |||
| 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"); | |||
| 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)) { | |||
| ||||
| 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)); | |||
| 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 | */ |