├── .gitignore ├── README.md └── main.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.json 3 | *.jpg 4 | auth.py 5 | __pycache__ 6 | *.log 7 | .idea 8 | *.db 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | DHT 公网嗅探器 2 | =========== 3 | 4 | 这是根据 DHT 协议写的嗅探器,下载种子的 infohash。另外我还写了 [Go 版的 DHT 嗅探器](https://github.com/lyyyuna/godht)。 5 | 6 | 其实暂时大部分代码来自了 [simDHT](https://github.com/Fuck-You-GFW/simDHT/blob/master/simDHT.py),我只是改成了 gevent 版本。因为原来的版本虽然是多线程,但是在 udp.recv 的时候还是会阻塞的。 7 | 8 | ## 简单说明 9 | 10 | ### 依赖 11 | 12 | * Python 2.7 13 | * pip install gevent 14 | * pip install bencode -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from gevent import monkey 2 | monkey.patch_all() 3 | 4 | from gevent.server import DatagramServer 5 | import gevent 6 | import socket 7 | from hashlib import sha1 8 | from random import randint 9 | from struct import unpack 10 | from socket import inet_ntoa 11 | from threading import Timer, Thread 12 | from gevent import sleep 13 | from collections import deque 14 | from bencode import bencode, bdecode 15 | 16 | 17 | BOOTSTRAP_NODES = ( 18 | ("router.bittorrent.com", 6881), 19 | ("dht.transmissionbt.com", 6881), 20 | ("router.utorrent.com", 6881) 21 | ) 22 | TID_LENGTH = 2 23 | RE_JOIN_DHT_INTERVAL = 3 24 | MONITOR_INTERVAL = 10 25 | TOKEN_LENGTH = 2 26 | 27 | def entropy(length): 28 | return "".join(chr(randint(0, 255)) for _ in xrange(length)) 29 | 30 | 31 | def random_id(): 32 | h = sha1() 33 | h.update(entropy(20)) 34 | return h.digest() 35 | 36 | 37 | def decode_nodes(nodes): 38 | n = [] 39 | length = len(nodes) 40 | if (length % 26) != 0: 41 | return n 42 | 43 | for i in range(0, length, 26): 44 | nid = nodes[i:i+20] 45 | ip = inet_ntoa(nodes[i+20:i+24]) 46 | port = unpack("!H", nodes[i+24:i+26])[0] 47 | n.append((nid, ip, port)) 48 | 49 | return n 50 | 51 | def get_neighbor(target, nid, end=10): 52 | return target[:end]+nid[end:] 53 | 54 | 55 | 56 | class KNode(object): 57 | 58 | def __init__(self, nid, ip, port): 59 | self.nid = nid 60 | self.ip = ip 61 | self.port = port 62 | 63 | 64 | 65 | 66 | 67 | class DHTServer(DatagramServer): 68 | def __init__(self, max_node_qsize, bind_ip): 69 | s = ':' + str(bind_ip) 70 | self.bind_ip = bind_ip 71 | DatagramServer.__init__(self, s) 72 | 73 | self.process_request_actions = { 74 | "get_peers": self.on_get_peers_request, 75 | "announce_peer": self.on_announce_peer_request, 76 | } 77 | self.max_node_qsize = max_node_qsize 78 | self.nid = random_id() 79 | self.nodes = deque(maxlen=max_node_qsize) 80 | 81 | def handle(self, data, address): # 82 | try: 83 | msg = bdecode(data) 84 | self.on_message(msg, address) 85 | except Exception: 86 | pass 87 | 88 | def monitor(self): 89 | while True: 90 | print 'len: ', len(self.nodes) 91 | sleep(MONITOR_INTERVAL) 92 | 93 | def send_krpc(self, msg, address): 94 | try: 95 | self.socket.sendto(bencode(msg), address) 96 | except Exception: 97 | pass 98 | 99 | def send_find_node(self, address, nid=None): 100 | nid = get_neighbor(nid, self.nid) if nid else self.nid 101 | tid = entropy(TID_LENGTH) 102 | msg = { 103 | "t": tid, 104 | "y": "q", 105 | "q": "find_node", 106 | "a": { 107 | "id": nid, 108 | "target": random_id() 109 | } 110 | } 111 | self.send_krpc(msg, address) 112 | 113 | def join_DHT(self): 114 | for address in BOOTSTRAP_NODES: 115 | self.send_find_node(address) 116 | 117 | def re_join_DHT(self): 118 | 119 | while True: 120 | if len(self.nodes) == 0: 121 | self.join_DHT() 122 | sleep(RE_JOIN_DHT_INTERVAL) 123 | 124 | 125 | def auto_send_find_node(self): 126 | 127 | wait = 1.0 / self.max_node_qsize / 5.0 128 | while True: 129 | try: 130 | node = self.nodes.popleft() 131 | self.send_find_node((node.ip, node.port), node.nid) 132 | except IndexError: 133 | pass 134 | sleep(wait) 135 | 136 | def process_find_node_response(self, msg, address): 137 | # print 'find node' + str(msg) 138 | nodes = decode_nodes(msg["r"]["nodes"]) 139 | for node in nodes: 140 | (nid, ip, port) = node 141 | if len(nid) != 20: continue 142 | if ip == self.bind_ip: continue 143 | if port < 1 or port > 65535: continue 144 | n = KNode(nid, ip, port) 145 | self.nodes.append(n) 146 | 147 | 148 | def on_message(self, msg, address): 149 | try: 150 | if msg["y"] == "r": 151 | if msg["r"].has_key("nodes"): 152 | self.process_find_node_response(msg, address) 153 | elif msg["y"] == "q": 154 | try: 155 | self.process_request_actions[msg["q"]](msg, address) 156 | except KeyError: 157 | self.play_dead(msg, address) 158 | except KeyError: 159 | pass 160 | 161 | def on_get_peers_request(self, msg, address): 162 | try: 163 | infohash = msg["a"]["info_hash"] 164 | tid = msg["t"] 165 | nid = msg["a"]["id"] 166 | token = infohash[:TOKEN_LENGTH] 167 | print 'get peers: ' + infohash.encode("hex"), address[0], address[1] 168 | msg = { 169 | "t": tid, 170 | "y": "r", 171 | "r": { 172 | "id": get_neighbor(infohash, self.nid), 173 | "nodes": "", 174 | "token": token 175 | } 176 | } 177 | self.send_krpc(msg, address) 178 | except KeyError: 179 | pass 180 | 181 | def on_announce_peer_request(self, msg, address): 182 | try: 183 | print 'announce peer' 184 | infohash = msg["a"]["info_hash"] 185 | token = msg["a"]["token"] 186 | nid = msg["a"]["id"] 187 | tid = msg["t"] 188 | 189 | if infohash[:TOKEN_LENGTH] == token: 190 | if msg["a"].has_key("implied_port") and msg["a"]["implied_port"] != 0: 191 | port = address[1] 192 | else: 193 | port = msg["a"]["port"] 194 | if port < 1 or port > 65535: return 195 | print 'announce peer: ' + infohash.encode("hex"), address[0], port 196 | except Exception: 197 | pass 198 | finally: 199 | self.ok(msg, address) 200 | 201 | def play_dead(self, msg, address): 202 | try: 203 | tid = msg["t"] 204 | msg = { 205 | "t": tid, 206 | "y": "e", 207 | "e": [202, "Server Error"] 208 | } 209 | self.send_krpc(msg, address) 210 | except KeyError: 211 | pass 212 | 213 | def ok(self, msg, address): 214 | try: 215 | tid = msg["t"] 216 | nid = msg["a"]["id"] 217 | msg = { 218 | "t": tid, 219 | "y": "r", 220 | "r": { 221 | "id": get_neighbor(nid, self.nid) 222 | } 223 | } 224 | self.send_krpc(msg, address) 225 | except KeyError: 226 | pass 227 | 228 | if __name__ == '__main__': 229 | sniffer = DHTServer(500, 6882) 230 | gevent.spawn(sniffer.auto_send_find_node) 231 | gevent.spawn(sniffer.re_join_DHT) 232 | gevent.spawn(sniffer.monitor) 233 | 234 | print('Receiving datagrams on :6882') 235 | sniffer.serve_forever() 236 | --------------------------------------------------------------------------------