1 /******************************************************************************************************/
3 * Routines for BT-DHT dissection
4 * Copyright 2011, Xiao Xiangquan <xiaoxiangquan@gmail.com>
8 * A plugin for BT-DHT packet:
10 * Wireshark - Network traffic analyzer
11 * By Gerald Combs <gerald@wireshark.org>
12 * Copyright 1999 Gerald Combs
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
31 #include <epan/packet.h>
32 #include <epan/conversation.h>
33 #include <epan/prefs.h>
34 #include <epan/wmem/wmem.h>
36 /* Specifications: BEP-0005
37 * http://www.bittorrent.org/beps/bep_0005.html
40 static int proto_bt_dht
= -1;
41 static dissector_handle_t bt_dht_handle
;
43 static gboolean bt_dht_enable_heuristic_dissection
= FALSE
; /* disabled by default since heuristic is weak */
46 static int hf_bencoded_int
= -1;
47 static int hf_bencoded_string
= -1;
48 static int hf_bencoded_list
= -1;
49 static int hf_bencoded_dict
= -1;
50 static int hf_bencoded_dict_entry
= -1;
52 static int hf_bt_dht_error
= -1;
53 static int hf_bt_dht_peers
= -1;
54 static int hf_bt_dht_peer
= -1;
55 static int hf_bt_dht_nodes
= -1;
56 static int hf_bt_dht_node
= -1;
57 static int hf_bt_dht_id
= -1;
59 static int hf_ip
= -1;
60 static int hf_port
= -1;
61 static int hf_truncated_data
= -1;
64 static gint ett_bt_dht
= -1;
65 static gint ett_bencoded_list
= -1;
66 static gint ett_bencoded_dict
= -1;
67 static gint ett_bencoded_dict_entry
= -1;
68 static gint ett_bt_dht_error
= -1;
69 static gint ett_bt_dht_peers
= -1;
70 static gint ett_bt_dht_nodes
= -1;
72 /* some keys use short name in packet */
73 static const value_string short_key_name_value_string
[] = {
74 { 'y', "Message type" },
75 { 'q', "Request type" },
77 { 't', "Transaction ID" },
79 { 'a', "Request arguments" },
80 { 'r', "Response values" },
84 /* some values use short name in packet */
85 static const value_string short_val_name_value_string
[] = {
92 static const char dict_str
[] = "Dictionary...";
93 static const char list_str
[] = "List...";
97 bencoded_string_length(tvbuff_t
*tvb
, guint
*offset_ptr
)
99 guint offset
, start
, len
;
101 offset
= *offset_ptr
;
104 while(tvb_get_guint8(tvb
, offset
) != ':')
107 len
= atoi(tvb_get_string(wmem_packet_scope(), tvb
, start
, offset
-start
));
108 ++offset
; /* skip the ':' */
110 *offset_ptr
= offset
;
116 * dissect a bencoded string from tvb, start at offset. it's like "5:abcde"
117 * *result will be the decoded value
121 dissect_bencoded_string(tvbuff_t
*tvb
, packet_info _U_
*pinfo
, proto_tree
*tree
, guint offset
, char **result
, gboolean tohex
, const char *label
)
124 string_len
= bencoded_string_length(tvb
, &offset
);
126 /* fill the return data */
128 *result
= tvb_bytes_to_str(tvb
, offset
, string_len
);
130 *result
= tvb_get_string( wmem_packet_scope(), tvb
, offset
, string_len
);
132 proto_tree_add_string_format( tree
, hf_bencoded_string
, tvb
, offset
, string_len
, *result
, "%s: %s", label
, *result
);
133 offset
+= string_len
;
138 * dissect a bencoded integer from tvb, start at offset. it's like "i5673e"
139 * *result will be the decoded value
142 dissect_bencoded_int(tvbuff_t
*tvb
, packet_info _U_
*pinfo
, proto_tree
*tree
, guint offset
, char **result
, const char *label
)
146 /* we have confirmed that the first byte is 'i' */
148 start_offset
= offset
;
150 while( tvb_get_guint8(tvb
,offset
)!='e' )
153 *result
= tvb_get_string( wmem_packet_scope(), tvb
, start_offset
, offset
-start_offset
);
154 proto_tree_add_string_format( tree
, hf_bencoded_int
, tvb
, start_offset
, offset
-start_offset
, *result
,
155 "%s: %s", label
, *result
);
161 /* pre definition of dissect_bencoded_dict(), which is needed by dissect_bencoded_list() */
162 static int dissect_bencoded_dict(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, guint offset
, const char *label
);
164 /* dissect a bencoded list from tvb, start at offset. it's like "lXXXe", "X" is any bencoded thing */
166 dissect_bencoded_list(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, guint offset
, const char *label
)
169 proto_tree
*sub_tree
;
173 ti
= proto_tree_add_none_format( tree
, hf_bencoded_list
, tvb
, offset
, 0, "%s: list...", label
);
174 sub_tree
= proto_item_add_subtree( ti
, ett_bencoded_list
);
179 while( (one_byte
=tvb_get_guint8(tvb
,offset
)) != 'e' )
185 offset
= dissect_bencoded_int( tvb
, pinfo
, sub_tree
, offset
, &result
, "Integer" );
189 offset
= dissect_bencoded_list( tvb
, pinfo
, sub_tree
, offset
, "Sub-list" );
193 offset
= dissect_bencoded_dict( tvb
, pinfo
, sub_tree
, offset
, "Sub-dict" );
197 offset
= dissect_bencoded_string( tvb
, pinfo
, sub_tree
, offset
, &result
, FALSE
, "String" );
205 /* dissect a bt dht error from tvb, start at offset. it's like "li201e9:error msge" */
207 dissect_bt_dht_error(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, guint offset
, char **result
, const char *label
)
210 proto_tree
*sub_tree
;
211 char *error_no
, *error_msg
;
216 ti
= proto_tree_add_item( tree
, hf_bt_dht_error
, tvb
, offset
, 0, ENC_NA
);
217 sub_tree
= proto_item_add_subtree( ti
, ett_bt_dht_error
);
219 /* we have confirmed that the first byte is 'l' */
222 /* dissect bt-dht error number and message */
223 offset
= dissect_bencoded_int( tvb
, pinfo
, sub_tree
, offset
, &error_no
, "Error ID" );
224 offset
= dissect_bencoded_string( tvb
, pinfo
, sub_tree
, offset
, &error_msg
, FALSE
, "Error Message" );
226 proto_item_set_text( ti
, "%s: error %s, %s", label
, error_no
, error_msg
);
227 col_append_fstr( pinfo
->cinfo
, COL_INFO
, "error_no=%s error_msg=%s ", error_no
, error_msg
);
228 *result
= wmem_strdup_printf(wmem_packet_scope(), "error %s, %s", error_no
, error_msg
);
233 /* dissect a bt dht values list from tvb, start at offset. it's like "l6:....6:....e" */
235 dissect_bt_dht_values(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, guint offset
, char **result
, const char *label
)
238 proto_tree
*sub_tree
;
239 proto_item
*value_ti
;
240 proto_tree
*value_tree
;
245 ti
= proto_tree_add_item( tree
, hf_bt_dht_peers
, tvb
, offset
, 0, ENC_NA
);
246 sub_tree
= proto_item_add_subtree( ti
, ett_bt_dht_peers
);
249 /* we has confirmed that the first byte is 'l' */
252 /* dissect bt-dht values */
253 while( tvb_get_guint8(tvb
,offset
)!='e' )
255 string_len
= bencoded_string_length(tvb
, &offset
);
257 /* 4 bytes ip, 2 bytes port */
258 for( ; string_len
>=6; string_len
-=6, offset
+=6 )
262 value_ti
= proto_tree_add_item( sub_tree
, hf_bt_dht_peer
, tvb
, offset
, 6, ENC_NA
);
263 proto_item_append_text(value_ti
, " %d", peer_index
);
264 value_tree
= proto_item_add_subtree( value_ti
, ett_bt_dht_peers
);
266 proto_tree_add_item( value_tree
, hf_ip
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
267 proto_item_append_text(value_ti
, " (IP/Port: %s", tvb_ip_to_str(tvb
, offset
));
268 proto_tree_add_item( value_tree
, hf_port
, tvb
, offset
+4, 2, ENC_BIG_ENDIAN
);
269 proto_item_append_text(value_ti
, ":%u)", tvb_get_ntohs( tvb
, offset
+4 ));
275 proto_tree_add_item( tree
, hf_truncated_data
, tvb
, offset
, string_len
, ENC_NA
);
276 offset
+= string_len
;
280 if (tvb_get_guint8(tvb
,offset
)=='e') /* list ending delimiter */
283 proto_item_set_text( ti
, "%s: %d peers", label
, peer_index
);
284 col_append_fstr( pinfo
->cinfo
, COL_INFO
, "reply=%d peers ", peer_index
);
285 *result
= wmem_strdup_printf(wmem_packet_scope(), "%d peers", peer_index
);
291 dissect_bt_dht_nodes(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, guint offset
, char **result
, const char *label
)
294 proto_tree
*sub_tree
;
296 proto_tree
*node_tree
;
301 string_len
= bencoded_string_length(tvb
, &offset
);
303 ti
= proto_tree_add_item( tree
, hf_bt_dht_nodes
, tvb
, offset
, string_len
, ENC_NA
);
304 sub_tree
= proto_item_add_subtree( ti
, ett_bt_dht_nodes
);
307 /* 20 bytes id, 4 bytes ip, 2 bytes port */
308 for( ; string_len
>=26; string_len
-=26, offset
+=26 )
313 node_ti
= proto_tree_add_item( sub_tree
, hf_bt_dht_node
, tvb
, offset
, 26, ENC_NA
);
314 proto_item_append_text(node_ti
, " %d", node_index
);
315 node_tree
= proto_item_add_subtree( node_ti
, ett_bt_dht_peers
);
317 proto_tree_add_item( node_tree
, hf_bt_dht_id
, tvb
, offset
, 20, ENC_NA
);
318 proto_item_append_text(node_ti
, " (id: %s", tvb_bytes_to_str(tvb
, offset
, 20));
319 proto_tree_add_item( node_tree
, hf_ip
, tvb
, offset
+20, 4, ENC_BIG_ENDIAN
);
320 proto_item_append_text(node_ti
, ", IP/Port: %s", tvb_ip_to_str(tvb
, offset
+20));
321 proto_tree_add_item( node_tree
, hf_port
, tvb
, offset
+24, 2, ENC_BIG_ENDIAN
);
322 proto_item_append_text(node_ti
, ":%u)", tvb_get_ntohs( tvb
, offset
+24 ));
326 proto_tree_add_item( tree
, hf_truncated_data
, tvb
, offset
, string_len
, ENC_NA
);
327 offset
+= string_len
;
329 proto_item_set_text( ti
, "%s: %d nodes", label
, node_index
);
330 col_append_fstr( pinfo
->cinfo
, COL_INFO
, "reply=%d nodes ", node_index
);
331 *result
= wmem_strdup_printf(wmem_packet_scope(), "%d", node_index
);
337 dissect_bencoded_dict_entry(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, guint offset
)
340 proto_tree
*sub_tree
;
343 guint orig_offset
= offset
;
348 ti
= proto_tree_add_item( tree
, hf_bencoded_dict_entry
, tvb
, offset
, 0, ENC_NA
);
349 sub_tree
= proto_item_add_subtree( ti
, ett_bencoded_dict_entry
);
351 /* dissect the key, it must be a string */
352 offset
= dissect_bencoded_string( tvb
, pinfo
, sub_tree
, offset
, &key
, FALSE
, "Key" );
354 /* If it is a dict, then just do recursion */
355 switch( tvb_get_guint8(tvb
,offset
) )
358 offset
= dissect_bencoded_dict( tvb
, pinfo
, sub_tree
, offset
, "Value" );
359 val
= (char*)dict_str
;
362 if( strcmp(key
,"e")==0 )
363 offset
= dissect_bt_dht_error( tvb
, pinfo
, sub_tree
, offset
, &val
, "Value" );
364 else if( strcmp(key
,"values")==0 )
365 offset
= dissect_bt_dht_values( tvb
, pinfo
, sub_tree
, offset
, &val
, "Value" );
366 /* other unfamiliar lists */
369 offset
= dissect_bencoded_list( tvb
, pinfo
, sub_tree
, offset
, "Value" );
370 val
= (char*)list_str
;
374 offset
= dissect_bencoded_int( tvb
, pinfo
, sub_tree
, offset
, &val
, "Value" );
378 /* special process */
379 if( strcmp(key
,"nodes")==0 )
381 offset
= dissect_bt_dht_nodes( tvb
, pinfo
, sub_tree
, offset
, &val
, "Value" );
383 else if( strcmp(key
,"ip")==0 )
386 * Not found in BEP 0005 but explained by
387 * http://www.rasterbar.com/products/libtorrent/dht_sec.html
392 len
= bencoded_string_length(tvb
, &offset
);
395 proto_tree_add_item(sub_tree
, hf_ip
, tvb
, offset
, len
, ENC_BIG_ENDIAN
);
396 val
= (char*)tvb_ip_to_str(tvb
, offset
);
400 offset
= dissect_bencoded_string( tvb
, pinfo
, sub_tree
, old_offset
, &val
, TRUE
, "Value" );
405 /* some need to return hex string */
406 tohex
= strcmp(key
,"id")==0 || strcmp(key
,"target")==0
407 || strcmp(key
,"info_hash")==0 || strcmp(key
,"t")==0
408 || strcmp(key
,"v")==0 || strcmp(key
,"token")==0;
409 offset
= dissect_bencoded_string( tvb
, pinfo
, sub_tree
, offset
, &val
, tohex
, "Value" );
414 key
= (char*)val_to_str_const( key
[0], short_key_name_value_string
, key
);
416 val
= (char*)val_to_str_const( val
[0], short_val_name_value_string
, val
);
418 proto_item_set_text( ti
, "%s: %s", key
, val
);
419 proto_item_set_len( ti
, offset
-orig_offset
);
421 if( strcmp(key
,"message_type")==0 || strcmp(key
,"request_type")==0 )
422 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "%s=%s ", key
, val
);
429 dissect_bencoded_dict(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, guint offset
, const char *label
)
432 proto_tree
*sub_tree
;
433 guint orig_offset
= offset
;
437 ti
= proto_tree_add_item(tree
, proto_bt_dht
, tvb
, 0, -1, ENC_NA
);
438 sub_tree
= proto_item_add_subtree(ti
, ett_bt_dht
);
442 ti
= proto_tree_add_none_format( tree
, hf_bencoded_dict
, tvb
, offset
, -1, "%s: Dictionary...", label
);
443 sub_tree
= proto_item_add_subtree( ti
, ett_bencoded_dict
);
446 /* skip the first char('d') */
449 while( tvb_get_guint8(tvb
,offset
)!='e' )
450 offset
= dissect_bencoded_dict_entry( tvb
, pinfo
, sub_tree
, offset
);
453 proto_item_set_len( ti
, offset
-orig_offset
);
459 dissect_bt_dht(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
461 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "BT-DHT");
462 col_clear(pinfo
->cinfo
, COL_INFO
);
464 return dissect_bencoded_dict(tvb
, pinfo
, tree
, 0, "BitTorrent DHT Protocol");
468 gboolean
dissect_bt_dht_heur (tvbuff_t
*tvb
, packet_info
*pinfo
,
469 proto_tree
*tree
, void *data _U_
)
472 /* Assume dictionary (d) is followed by a one char long (1:) key string. */
473 if(tvb_memeql(tvb
, 0, "d1:", 3) == 0)
476 guint8 key
= tvb_get_guint8(tvb
, 3);
478 /* Iterate through possible keys to improve heuristics. */
479 for(i
=0; short_key_name_value_string
[i
].value
!= 0; i
++)
481 if(short_key_name_value_string
[i
].value
== key
)
483 conversation_t
*conversation
;
485 conversation
= find_or_create_conversation(pinfo
);
486 conversation_set_dissector(conversation
, bt_dht_handle
);
488 dissect_bt_dht(tvb
, pinfo
, tree
, NULL
);
496 void proto_reg_handoff_bt_dht(void);
499 proto_register_bt_dht(void)
501 static hf_register_info hf
[] = {
502 { &hf_bencoded_string
,
503 { "String", "bt-dht.bencoded.string",
504 FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}
507 { "List", "bt-dht.bencoded.list",
508 FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}
511 { "Int", "bt-dht.bencoded.int",
512 FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}
515 { "Dictionary", "bt-dht.bencoded.dict",
516 FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}
518 { &hf_bencoded_dict_entry
,
519 { "Dictionary Entry", "bt-dht.bencoded.dict_entry",
520 FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}
523 { "Error", "bt-dht.error",
524 FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}
527 { "Peer", "bt-dht.peer",
528 FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}
531 { "Peers", "bt-dht.peers",
532 FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}
535 { "Node", "bt-dht.node",
536 FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}
539 { "Nodes", "bt-dht.nodes",
540 FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}
544 FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}
548 FT_IPv4
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}
551 { "Port", "bt-dht.port",
552 FT_UINT16
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}
554 { &hf_truncated_data
,
555 { "Truncated data", "bt-dht.truncated_data",
556 FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}
560 /* Setup protocol subtree array */
561 static gint
*ett
[] = {
568 &ett_bencoded_dict_entry
571 module_t
*bt_dht_module
;
573 proto_bt_dht
= proto_register_protocol (
574 "BitTorrent DHT Protocol", /* name */
575 "BT-DHT", /* short name */
576 "bt-dht" /* abbrev */
579 bt_dht_module
= prefs_register_protocol(proto_bt_dht
, proto_reg_handoff_bt_dht
);
580 prefs_register_bool_preference(bt_dht_module
, "enable", "Enable BT-DHT heuristic dissection",
581 "Enable BT-DHT heuristic dissection (default is disabled)",
582 &bt_dht_enable_heuristic_dissection
);
584 proto_register_field_array(proto_bt_dht
, hf
, array_length(hf
));
585 proto_register_subtree_array(ett
, array_length(ett
));
589 proto_reg_handoff_bt_dht(void)
591 static gboolean prefs_initialized
= FALSE
;
593 /* "Decode As" is always available;
594 * Heuristic dissection in disabled by default since the heuristic is quite weak.
595 * XXX - Still too weak?
597 if (!prefs_initialized
) {
598 heur_dissector_add("udp", dissect_bt_dht_heur
, proto_bt_dht
);
600 bt_dht_handle
= new_create_dissector_handle(dissect_bt_dht
, proto_bt_dht
);
601 dissector_add_handle("udp.port", bt_dht_handle
); /* for "decode_as" */
603 prefs_initialized
= TRUE
;
606 heur_dissector_set_enabled("udp", dissect_bt_dht_heur
, proto_bt_dht
, bt_dht_enable_heuristic_dissection
);
615 * indent-tabs-mode: nil
618 * ex: set shiftwidth=2 tabstop=8 expandtab:
619 * :indentSize=2:tabSize=8:noTabs=true: