/src/ndpi/src/lib/protocols/bittorrent.c
Line | Count | Source |
1 | | /* |
2 | | * bittorrent.c |
3 | | * |
4 | | * Copyright (C) 2009-11 - ipoque GmbH |
5 | | * Copyright (C) 2011-26 - ntop.org |
6 | | * |
7 | | * This file is part of nDPI, an open source deep packet inspection |
8 | | * library based on the OpenDPI and PACE technology by ipoque GmbH |
9 | | * |
10 | | * nDPI is free software: you can redistribute it and/or modify |
11 | | * it under the terms of the GNU Lesser General Public License as published by |
12 | | * the Free Software Foundation, either version 3 of the License, or |
13 | | * (at your option) any later version. |
14 | | * |
15 | | * nDPI is distributed in the hope that it will be useful, |
16 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | * GNU Lesser General Public License for more details. |
19 | | * |
20 | | * You should have received a copy of the GNU Lesser General Public License |
21 | | * along with nDPI. If not, see <http://www.gnu.org/licenses/>. |
22 | | * |
23 | | */ |
24 | | |
25 | | |
26 | | #include "ndpi_protocol_ids.h" |
27 | | |
28 | | #define NDPI_CURRENT_PROTO NDPI_PROTOCOL_BITTORRENT |
29 | | |
30 | | #include "ndpi_api.h" |
31 | | #include "ndpi_private.h" |
32 | | |
33 | 2.54M | #define BITTORRENT_PROTO_STRING "BitTorrent protocol" |
34 | | |
35 | | // #define BITTORRENT_CACHE_DEBUG 1 |
36 | | |
37 | | PACK_ON |
38 | | struct ndpi_utp_hdr { |
39 | | #if defined(__BIG_ENDIAN__) |
40 | | u_int8_t h_type:4, h_version:4; |
41 | | #elif defined(__LITTLE_ENDIAN__) |
42 | | u_int8_t h_version:4, h_type:4; |
43 | | #else |
44 | | #error "Missing endian macro definitions." |
45 | | #endif |
46 | | u_int8_t next_extension; |
47 | | u_int16_t connection_id; |
48 | | u_int32_t ts_usec, tdiff_usec, window_size; |
49 | | u_int16_t sequence_nr, ack_nr; |
50 | | } PACK_OFF; |
51 | | |
52 | | |
53 | | /* Forward declaration */ |
54 | | static void ndpi_search_bittorrent(struct ndpi_detection_module_struct *ndpi_struct, |
55 | | struct ndpi_flow_struct *flow); |
56 | | static void ndpi_search_bittorrent_hash(struct ndpi_detection_module_struct *ndpi_struct, |
57 | | struct ndpi_flow_struct *flow, int bt_offset); |
58 | | |
59 | | /* *********************************************** */ |
60 | | |
61 | | static int search_bittorrent_again(struct ndpi_detection_module_struct *ndpi_struct, |
62 | 90.5k | struct ndpi_flow_struct *flow) { |
63 | 90.5k | ndpi_search_bittorrent_hash(ndpi_struct, flow, -1); |
64 | | |
65 | | /* Possibly more processing */ |
66 | 90.5k | return flow->extra_packets_func != NULL; |
67 | 90.5k | } |
68 | | |
69 | | /* *********************************************** */ |
70 | | |
71 | | static int get_utpv1_length(const u_int8_t *payload, u_int payload_len) |
72 | 1.11M | { |
73 | 1.11M | struct ndpi_utp_hdr *h = (struct ndpi_utp_hdr*)payload; |
74 | 1.11M | unsigned int off, num_ext = 0; |
75 | 1.11M | u_int8_t ext_type = h->next_extension; |
76 | | |
77 | 1.11M | off = sizeof(struct ndpi_utp_hdr); |
78 | 1.41M | while(ext_type != 0 && off + 1 < payload_len) { |
79 | 937k | ext_type = payload[off]; |
80 | 937k | if(ext_type > 2) |
81 | 632k | return -1; |
82 | | /* BEP-29 doesn't have any limits on the number of extensions |
83 | | but putting an hard limit makes sense (there are only 3 ext types) */ |
84 | 304k | if(++num_ext > 4) |
85 | 969 | return -1; |
86 | 303k | off += 2 + payload[off + 1]; |
87 | 303k | } |
88 | 478k | if(ext_type == 0) |
89 | 459k | return off; |
90 | 19.2k | return -1; |
91 | 478k | } |
92 | | |
93 | | /* *********************************************** */ |
94 | | |
95 | 1.11M | static u_int8_t is_utpv1_pkt(const u_int8_t *payload, u_int payload_len) { |
96 | 1.11M | struct ndpi_utp_hdr *h = (struct ndpi_utp_hdr*)payload; |
97 | 1.11M | int h_length; |
98 | | |
99 | 1.11M | if(payload_len < sizeof(struct ndpi_utp_hdr)) return(0); |
100 | 1.11M | h_length = get_utpv1_length(payload, payload_len); |
101 | 1.11M | if(h_length == -1) return(0); |
102 | 459k | if(h->h_version != 1) return(0); |
103 | 35.7k | if(h->h_type > 4) return(0); |
104 | 25.5k | if(h->next_extension > 2) return(0); |
105 | 16.9k | if(h->h_type == 4 /* SYN */ && (h->tdiff_usec != 0 || |
106 | 1.21k | payload_len != (u_int)h_length)) return(0); |
107 | 15.7k | if(h->h_type == 2 /* STATE */ && |
108 | 1.33k | payload_len != (u_int)h_length) return(0); |
109 | 14.8k | if(h->h_type == 0 /* DATA */ && |
110 | 13.3k | payload_len == (u_int)h_length) return(0); |
111 | 14.2k | if(h->connection_id == 0) return(0); |
112 | 9.58k | if(h->ts_usec == 0) return(0); |
113 | | |
114 | 8.94k | if((h->window_size == 0) && (payload_len != (u_int)h_length)) |
115 | 940 | return(0); |
116 | | |
117 | 8.00k | if(h->h_type == 0) |
118 | 7.39k | return (2); /* DATA */ |
119 | 603 | return(1); |
120 | 8.00k | } |
121 | | |
122 | | /* *********************************************** */ |
123 | | |
124 | | static void ndpi_search_bittorrent_hash(struct ndpi_detection_module_struct *ndpi_struct, |
125 | 99.2k | struct ndpi_flow_struct *flow, int bt_offset) { |
126 | 99.2k | const char *bt_hash = NULL; /* 20 bytes long */ |
127 | 99.2k | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
128 | | |
129 | 99.2k | if(bt_offset == -1) { |
130 | 96.6k | const char *bt_magic = ndpi_strnstr((const char *)packet->payload, |
131 | 96.6k | BITTORRENT_PROTO_STRING, packet->payload_packet_len); |
132 | | |
133 | 96.6k | if(bt_magic) { |
134 | 685 | if(bt_magic == (const char*)&packet->payload[1]) |
135 | 304 | bt_hash = (const char*)&packet->payload[28]; |
136 | 381 | else |
137 | 381 | bt_hash = &bt_magic[19]; |
138 | 685 | } |
139 | 96.6k | } else |
140 | 2.59k | bt_hash = (const char*)&packet->payload[28]; |
141 | | |
142 | 99.2k | if(bt_hash && (packet->payload_packet_len >= (20 + (bt_hash-(const char*)packet->payload)))) |
143 | 2.81k | memcpy(flow->protos.bittorrent.hash, bt_hash, 20); |
144 | 99.2k | } |
145 | | |
146 | | /* *********************************************** */ |
147 | | |
148 | 9.48M | u_int64_t make_bittorrent_host_key(struct ndpi_flow_struct *flow, int client, int offset) { |
149 | 9.48M | u_int64_t key; |
150 | | |
151 | | /* network byte order */ |
152 | 9.48M | if(flow->is_ipv6) { |
153 | 276k | if(client) |
154 | 141k | key = (ndpi_quick_hash64((const char *)flow->c_address.v6, 16) << 16) | htons(ntohs(flow->c_port) + offset); |
155 | 134k | else |
156 | 134k | key = (ndpi_quick_hash64((const char *)flow->s_address.v6, 16) << 16) | flow->s_port; |
157 | 9.20M | } else { |
158 | 9.20M | if(client) |
159 | 4.68M | key = ((u_int64_t)flow->c_address.v4 << 32) | htons(ntohs(flow->c_port) + offset); |
160 | 4.51M | else |
161 | 4.51M | key = ((u_int64_t)flow->s_address.v4 << 32) | flow->s_port; |
162 | 9.20M | } |
163 | | |
164 | 9.48M | return key; |
165 | 9.48M | } |
166 | | |
167 | | /* *********************************************** */ |
168 | | |
169 | 4.65M | u_int64_t make_bittorrent_peers_key(struct ndpi_flow_struct *flow) { |
170 | 4.65M | u_int64_t key; |
171 | | |
172 | | /* network byte order */ |
173 | 4.65M | if(flow->is_ipv6) |
174 | 134k | key = (ndpi_quick_hash64((const char *)flow->c_address.v6, 16) << 32) | (ndpi_quick_hash64((const char *)flow->s_address.v6, 16) & 0xFFFFFFFF); |
175 | 4.51M | else |
176 | 4.51M | key = ((u_int64_t)flow->c_address.v4 << 32) | flow->s_address.v4; |
177 | | |
178 | 4.65M | return key; |
179 | 4.65M | } |
180 | | |
181 | | /* *********************************************** */ |
182 | | |
183 | | static void ndpi_add_connection_as_bittorrent(struct ndpi_detection_module_struct *ndpi_struct, |
184 | | struct ndpi_flow_struct *flow, |
185 | | int bt_offset, int check_hash, |
186 | 87.2k | ndpi_confidence_t confidence) { |
187 | 87.2k | if(ndpi_struct->cfg.bittorrent_hash_enabled && |
188 | 77.1k | check_hash) |
189 | 8.72k | ndpi_search_bittorrent_hash(ndpi_struct, flow, bt_offset); |
190 | | |
191 | 87.2k | ndpi_set_detected_protocol_keeping_master(ndpi_struct, flow, NDPI_PROTOCOL_BITTORRENT, |
192 | 87.2k | confidence); |
193 | | |
194 | 87.2k | if(ndpi_struct->cfg.bittorrent_hash_enabled && |
195 | 77.1k | flow->protos.bittorrent.hash[0] == '\0') { |
196 | | /* Don't use just 1 as in TCP DNS more packets could be returned (e.g. ACK). */ |
197 | 74.6k | flow->max_extra_packets_to_check = 3; |
198 | 74.6k | flow->extra_packets_func = search_bittorrent_again; |
199 | 74.6k | } |
200 | | |
201 | 87.2k | if(ndpi_struct->bittorrent_cache) { |
202 | 86.9k | u_int64_t key, key1, key2, i; |
203 | | |
204 | 86.9k | key = make_bittorrent_peers_key(flow); |
205 | 86.9k | key1 = make_bittorrent_host_key(flow, 1, 0), key2 = make_bittorrent_host_key(flow, 0, 0); |
206 | | |
207 | 86.9k | ndpi_lru_add_to_cache(ndpi_struct->bittorrent_cache, key1, NDPI_PROTOCOL_BITTORRENT, ndpi_get_current_time(flow)); |
208 | 86.9k | ndpi_lru_add_to_cache(ndpi_struct->bittorrent_cache, key2, NDPI_PROTOCOL_BITTORRENT, ndpi_get_current_time(flow)); |
209 | | |
210 | | /* Now add hosts as twins */ |
211 | 86.9k | ndpi_lru_add_to_cache(ndpi_struct->bittorrent_cache, |
212 | 86.9k | key, |
213 | 86.9k | NDPI_PROTOCOL_BITTORRENT, |
214 | 86.9k | ndpi_get_current_time(flow)); |
215 | | |
216 | | /* Also add +2 ports of the sender in order to catch additional sockets open by the same client */ |
217 | 260k | for(i=0; i<2; i++) { |
218 | 173k | key1 = make_bittorrent_host_key(flow, 1, 1 + i); |
219 | | |
220 | 173k | ndpi_lru_add_to_cache(ndpi_struct->bittorrent_cache, key1, NDPI_PROTOCOL_BITTORRENT, ndpi_get_current_time(flow)); |
221 | 173k | } |
222 | | |
223 | | #ifdef BITTORRENT_CACHE_DEBUG |
224 | | printf("[BitTorrent] [%s] *** ADDED ports %u / %u [0x%llx][0x%llx]\n", |
225 | | flow->l4_proto == IPPROTO_TCP ? "TCP" : "UDP", |
226 | | ntohs(flow->c_port), ntohs(flow->s_port), |
227 | | (long long unsigned int)key1, (long long unsigned int)key2); |
228 | | #endif |
229 | 86.9k | } |
230 | 87.2k | } |
231 | | |
232 | | /* ************************************* */ |
233 | | |
234 | | static u_int8_t ndpi_int_search_bittorrent_tcp_zero(struct ndpi_detection_module_struct |
235 | | *ndpi_struct, struct ndpi_flow_struct *flow) |
236 | 5.91M | { |
237 | 5.91M | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
238 | 5.91M | u_int16_t a = 0; |
239 | | |
240 | 5.91M | if(packet->payload_packet_len == 1 && packet->payload[0] == 0x13) { |
241 | 1.38k | return 0; |
242 | 1.38k | } |
243 | | |
244 | 5.90M | if(flow->packet_counter == 2 && packet->payload_packet_len > 20) { |
245 | 551k | if(memcmp(&packet->payload[0], BITTORRENT_PROTO_STRING, 19) == 0) { |
246 | 84 | NDPI_LOG_INFO(ndpi_struct, "found BT: plain\n"); |
247 | 84 | ndpi_add_connection_as_bittorrent(ndpi_struct, flow, 19, 1, NDPI_CONFIDENCE_DPI); |
248 | 84 | return 1; |
249 | 84 | } |
250 | 551k | } |
251 | | |
252 | 5.90M | if(packet->payload_packet_len > 20) { |
253 | | /* test for match 0x13+BITTORRENT_PROTO_STRING */ |
254 | 3.80M | if(packet->payload[0] == 0x13) { |
255 | 9.69k | if(memcmp(&packet->payload[1], BITTORRENT_PROTO_STRING, 19) == 0) { |
256 | 2.58k | NDPI_LOG_INFO(ndpi_struct, "found BT: plain\n"); |
257 | 2.58k | ndpi_add_connection_as_bittorrent(ndpi_struct, flow, 20, 1, NDPI_CONFIDENCE_DPI); |
258 | 2.58k | return 1; |
259 | 2.58k | } |
260 | 9.69k | } |
261 | 3.80M | } |
262 | | |
263 | 5.90M | if(packet->payload_packet_len > 23 && memcmp(packet->payload, "GET /webseed?info_hash=", 23) == 0) { |
264 | 565 | NDPI_LOG_INFO(ndpi_struct, "found BT: plain webseed\n"); |
265 | 565 | ndpi_add_connection_as_bittorrent(ndpi_struct, flow, -1, 1, NDPI_CONFIDENCE_DPI); |
266 | 565 | return 1; |
267 | 565 | } |
268 | | /* seen Azureus as server for webseed, possibly other servers existing, to implement */ |
269 | | /* is Server: hypertracker Bittorrent? */ |
270 | | /* no asymmetric detection possible for answer of pattern "GET /data?fid=". */ |
271 | 5.90M | if(packet->payload_packet_len > 60 |
272 | 2.98M | && memcmp(packet->payload, "GET /data?fid=", 14) == 0 && memcmp(&packet->payload[54], "&size=", 6) == 0) { |
273 | 86 | NDPI_LOG_INFO(ndpi_struct, "found BT: plain Bitcomet persistent seed\n"); |
274 | 86 | ndpi_add_connection_as_bittorrent(ndpi_struct, flow, -1, 1, NDPI_CONFIDENCE_DPI); |
275 | 86 | return 1; |
276 | 86 | } |
277 | | |
278 | | |
279 | 5.90M | if(packet->payload_packet_len > 90 && (memcmp(packet->payload, "GET ", 4) == 0 |
280 | 1.85M | || memcmp(packet->payload, "POST ", 5) == 0)) { |
281 | 664k | const u_int8_t *ptr = &packet->payload[4]; |
282 | 664k | u_int16_t len = packet->payload_packet_len - 4; |
283 | | |
284 | | /* parse complete get packet here into line structure elements */ |
285 | 664k | ndpi_parse_packet_line_info(ndpi_struct, flow); |
286 | | /* answer to this pattern is HTTP....Server: hypertracker */ |
287 | 664k | if(packet->user_agent_line.ptr != NULL |
288 | 411k | && ((packet->user_agent_line.len > 8 && memcmp(packet->user_agent_line.ptr, "Azureus ", 8) == 0) |
289 | 410k | || (packet->user_agent_line.len >= 10 && memcmp(packet->user_agent_line.ptr, "BitTorrent", 10) == 0) |
290 | 409k | || (packet->user_agent_line.len >= 11 && memcmp(packet->user_agent_line.ptr, "BTWebClient", 11) == 0))) { |
291 | 1.54k | NDPI_LOG_INFO(ndpi_struct, "found BT: Azureus /Bittorrent user agent\n"); |
292 | 1.54k | ndpi_add_connection_as_bittorrent(ndpi_struct, flow, -1, 1, NDPI_CONFIDENCE_DPI); |
293 | 1.54k | return 1; |
294 | 1.54k | } |
295 | | |
296 | 662k | if(packet->user_agent_line.ptr != NULL |
297 | 409k | && (packet->user_agent_line.len >= 9 && memcmp(packet->user_agent_line.ptr, "Shareaza ", 9) == 0) |
298 | 1.04k | && (packet->parsed_lines > 8 && packet->line[8].ptr != 0 |
299 | 758 | && packet->line[8].len >= 9 && memcmp(packet->line[8].ptr, "X-Queue: ", 9) == 0)) { |
300 | 71 | NDPI_LOG_INFO(ndpi_struct, "found BT: Shareaza detected\n"); |
301 | 71 | ndpi_add_connection_as_bittorrent(ndpi_struct, flow, -1, 1, NDPI_CONFIDENCE_DPI); |
302 | 71 | return 1; |
303 | 71 | } |
304 | | |
305 | | /* this is a self built client, not possible to catch asymmetrically */ |
306 | 662k | if((packet->parsed_lines == 10 || (packet->parsed_lines == 11 && packet->line[10].len == 0)) |
307 | 31.6k | && packet->user_agent_line.ptr != NULL |
308 | 26.2k | && packet->user_agent_line.len > 12 |
309 | 25.4k | && memcmp(packet->user_agent_line.ptr, "Mozilla/4.0 ", |
310 | 25.4k | 12) == 0 |
311 | 2.17k | && packet->host_line.ptr != NULL |
312 | 1.83k | && packet->host_line.len >= 7 |
313 | 1.68k | && packet->line[2].ptr != NULL |
314 | 1.68k | && packet->line[2].len > 14 |
315 | 1.48k | && memcmp(packet->line[2].ptr, "Keep-Alive: 300", 15) == 0 |
316 | 1.03k | && packet->line[3].ptr != NULL |
317 | 1.03k | && packet->line[3].len > 21 |
318 | 883 | && memcmp(packet->line[3].ptr, "Connection: Keep-alive", 22) == 0 |
319 | 755 | && packet->line[4].ptr != NULL |
320 | 755 | && packet->line[4].len > 10 |
321 | 678 | && (memcmp(packet->line[4].ptr, "Accpet: */*", 11) == 0 |
322 | 262 | || memcmp(packet->line[4].ptr, "Accept: */*", 11) == 0) |
323 | | |
324 | 542 | && packet->line[5].ptr != NULL |
325 | 542 | && packet->line[5].len > 12 |
326 | 462 | && memcmp(packet->line[5].ptr, "Range: bytes=", 13) == 0 |
327 | 299 | && packet->line[7].ptr != NULL |
328 | 299 | && packet->line[7].len > 15 |
329 | 238 | && memcmp(packet->line[7].ptr, "Pragma: no-cache", 16) == 0 |
330 | 112 | && packet->line[8].ptr != NULL |
331 | 112 | && packet->line[8].len > 22 && memcmp(packet->line[8].ptr, "Cache-Control: no-cache", 23) == 0) { |
332 | | |
333 | 16 | NDPI_LOG_INFO(ndpi_struct, "found BT: Bitcomet LTS\n"); |
334 | 16 | ndpi_add_connection_as_bittorrent(ndpi_struct, flow, -1, 1, NDPI_CONFIDENCE_DPI); |
335 | 16 | return 1; |
336 | 16 | } |
337 | | |
338 | | /* FlashGet pattern */ |
339 | 662k | if(packet->parsed_lines == 8 |
340 | 30.6k | && packet->user_agent_line.ptr != NULL |
341 | 22.3k | && packet->user_agent_line.len > (sizeof("Mozilla/4.0 (compatible; MSIE 6.0;") - 1) |
342 | 19.6k | && memcmp(packet->user_agent_line.ptr, "Mozilla/4.0 (compatible; MSIE 6.0;", |
343 | 19.6k | sizeof("Mozilla/4.0 (compatible; MSIE 6.0;") - 1) == 0 |
344 | 1.64k | && packet->host_line.ptr != NULL |
345 | 1.44k | && packet->host_line.len >= 7 |
346 | 1.29k | && packet->line[2].ptr != NULL |
347 | 1.29k | && packet->line[2].len == 11 |
348 | 1.05k | && memcmp(packet->line[2].ptr, "Accept: */*", 11) == 0 |
349 | 924 | && packet->line[3].ptr != NULL && packet->line[3].len >= (sizeof("Referer: ") - 1) |
350 | 800 | && memcmp(packet->line[3].ptr, "Referer: ", sizeof("Referer: ") - 1) == 0 |
351 | 656 | && packet->line[5].ptr != NULL |
352 | 656 | && packet->line[5].len > 13 |
353 | 524 | && memcmp(packet->line[5].ptr, "Range: bytes=", 13) == 0 |
354 | 362 | && packet->line[6].ptr != NULL |
355 | 362 | && packet->line[6].len > 21 && memcmp(packet->line[6].ptr, "Connection: Keep-Alive", 22) == 0) { |
356 | | |
357 | 98 | NDPI_LOG_INFO(ndpi_struct, "found BT: FlashGet\n"); |
358 | 98 | ndpi_add_connection_as_bittorrent(ndpi_struct, flow, -1, 1, NDPI_CONFIDENCE_DPI); |
359 | 98 | return 1; |
360 | 98 | } |
361 | | |
362 | 662k | if(packet->parsed_lines == 7 |
363 | 57.5k | && packet->user_agent_line.ptr != NULL |
364 | 42.2k | && packet->user_agent_line.len > (sizeof("Mozilla/4.0 (compatible; MSIE 6.0;") - 1) |
365 | 37.4k | && memcmp(packet->user_agent_line.ptr, "Mozilla/4.0 (compatible; MSIE 6.0;", |
366 | 37.4k | sizeof("Mozilla/4.0 (compatible; MSIE 6.0;") - 1) == 0 |
367 | 1.70k | && packet->host_line.ptr != NULL |
368 | 1.47k | && packet->host_line.len >= 7 |
369 | 1.29k | && packet->line[2].ptr != NULL |
370 | 1.29k | && packet->line[2].len == 11 |
371 | 1.00k | && memcmp(packet->line[2].ptr, "Accept: */*", 11) == 0 |
372 | 803 | && packet->line[3].ptr != NULL && packet->line[3].len >= (sizeof("Referer: ") - 1) |
373 | 680 | && memcmp(packet->line[3].ptr, "Referer: ", sizeof("Referer: ") - 1) == 0 |
374 | 488 | && packet->line[5].ptr != NULL |
375 | 488 | && packet->line[5].len > 21 && memcmp(packet->line[5].ptr, "Connection: Keep-Alive", 22) == 0) { |
376 | | |
377 | 91 | NDPI_LOG_INFO(ndpi_struct, "found BT: FlashGet\n"); |
378 | 91 | ndpi_add_connection_as_bittorrent(ndpi_struct, flow, -1, 1, NDPI_CONFIDENCE_DPI); |
379 | 91 | return 1; |
380 | 91 | } |
381 | | |
382 | | /* answer to this pattern is not possible to implement asymmetrically */ |
383 | 53.9M | while (1) { |
384 | 53.9M | if(len < 50 || ptr[0] == 0x0d) { |
385 | 654k | goto ndpi_end_bt_tracker_check; |
386 | 654k | } |
387 | 53.2M | if(memcmp(ptr, "info_hash=", 10) == 0) { |
388 | 7.98k | break; |
389 | 7.98k | } |
390 | 53.2M | len--; |
391 | 53.2M | ptr++; |
392 | 53.2M | } |
393 | | |
394 | 7.98k | NDPI_LOG_DBG2(ndpi_struct, " BT stat: tracker info hash found\n"); |
395 | | |
396 | | /* len is > 50, so save operation here */ |
397 | 7.98k | len -= 10; |
398 | 7.98k | ptr += 10; |
399 | | |
400 | | /* parse bt hash */ |
401 | 99.8k | for (a = 0; a < 20; a++) { |
402 | 96.8k | if(len < 3) { |
403 | 78 | goto ndpi_end_bt_tracker_check; |
404 | 78 | } |
405 | 96.7k | if(*ptr == '%') { |
406 | 18.8k | u_int8_t x1 = 0xFF; |
407 | 18.8k | u_int8_t x2 = 0xFF; |
408 | | |
409 | | |
410 | 18.8k | if(ptr[1] >= '0' && ptr[1] <= '9') { |
411 | 7.63k | x1 = ptr[1] - '0'; |
412 | 7.63k | } |
413 | 18.8k | if(ptr[1] >= 'a' && ptr[1] <= 'f') { |
414 | 9.06k | x1 = 10 + ptr[1] - 'a'; |
415 | 9.06k | } |
416 | 18.8k | if(ptr[1] >= 'A' && ptr[1] <= 'F') { |
417 | 1.23k | x1 = 10 + ptr[1] - 'A'; |
418 | 1.23k | } |
419 | | |
420 | 18.8k | if(ptr[2] >= '0' && ptr[2] <= '9') { |
421 | 8.59k | x2 = ptr[2] - '0'; |
422 | 8.59k | } |
423 | 18.8k | if(ptr[2] >= 'a' && ptr[2] <= 'f') { |
424 | 8.11k | x2 = 10 + ptr[2] - 'a'; |
425 | 8.11k | } |
426 | 18.8k | if(ptr[2] >= 'A' && ptr[2] <= 'F') { |
427 | 959 | x2 = 10 + ptr[2] - 'A'; |
428 | 959 | } |
429 | | |
430 | 18.8k | if(x1 == 0xFF || x2 == 0xFF) { |
431 | 1.50k | goto ndpi_end_bt_tracker_check; |
432 | 1.50k | } |
433 | 17.3k | ptr += 3; |
434 | 17.3k | len -= 3; |
435 | 77.9k | } else if(*ptr >= 32 && *ptr < 127) { |
436 | 74.5k | ptr++; |
437 | 74.5k | len--; |
438 | 74.5k | } else { |
439 | 3.40k | goto ndpi_end_bt_tracker_check; |
440 | 3.40k | } |
441 | 96.7k | } |
442 | | |
443 | 2.99k | NDPI_LOG_INFO(ndpi_struct, "found BT: tracker info hash parsed\n"); |
444 | 2.99k | ndpi_add_connection_as_bittorrent(ndpi_struct, flow, -1, 1, NDPI_CONFIDENCE_DPI); |
445 | 2.99k | return 1; |
446 | 7.98k | } |
447 | | |
448 | 5.90M | ndpi_end_bt_tracker_check: |
449 | | |
450 | 5.90M | if(packet->payload_packet_len == 80) { |
451 | | /* Warez 80 Bytes Packet |
452 | | * +----------------+---------------+-----------------+-----------------+ |
453 | | * |20 BytesPattern | 32 Bytes Value| 12 BytesPattern | 16 Bytes Data | |
454 | | * +----------------+---------------+-----------------+-----------------+ |
455 | | * 20 BytesPattern : 4c 00 00 00 ff ff ff ff 57 00 00 00 00 00 00 00 20 00 00 00 |
456 | | * 12 BytesPattern : 28 23 00 00 01 00 00 00 10 00 00 00 |
457 | | * */ |
458 | 16.7k | static const u_char pattern_20_bytes[20] = { 0x4c, 0x00, 0x00, 0x00, 0xff, |
459 | 16.7k | 0xff, 0xff, 0xff, 0x57, 0x00, |
460 | 16.7k | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00 |
461 | 16.7k | }; |
462 | 16.7k | static const u_char pattern_12_bytes[12] = { 0x28, 0x23, 0x00, 0x00, 0x01, |
463 | 16.7k | 0x00, 0x00, 0x00, 0x10, 0x00, |
464 | 16.7k | 0x00, 0x00 |
465 | 16.7k | }; |
466 | | |
467 | | /* did not see this pattern anywhere */ |
468 | 16.7k | if((memcmp(&packet->payload[0], pattern_20_bytes, 20) == 0) |
469 | 335 | && (memcmp(&packet->payload[52], pattern_12_bytes, 12) == 0)) { |
470 | 27 | NDPI_LOG_INFO(ndpi_struct, "found BT: Warez - Plain\n"); |
471 | 27 | ndpi_add_connection_as_bittorrent(ndpi_struct, flow, -1, 1, NDPI_CONFIDENCE_DPI); |
472 | 27 | return 1; |
473 | 27 | } |
474 | 16.7k | } |
475 | | |
476 | 5.88M | else if(packet->payload_packet_len > 50) { |
477 | 3.12M | if(memcmp(packet->payload, "GET", 3) == 0) { |
478 | | |
479 | 672k | ndpi_parse_packet_line_info(ndpi_struct, flow); |
480 | | /* haven't fount this pattern anywhere */ |
481 | 672k | if(packet->host_line.ptr != NULL |
482 | 484k | && packet->host_line.len >= 9 && memcmp(packet->host_line.ptr, "ip2p.com:", 9) == 0) { |
483 | 878 | NDPI_LOG_INFO(ndpi_struct, "found BT: Warez - Plain Host: ip2p.com: pattern\n"); |
484 | 878 | ndpi_add_connection_as_bittorrent(ndpi_struct, flow, -1, 1, NDPI_CONFIDENCE_DPI); |
485 | 878 | return 1; |
486 | 878 | } |
487 | 672k | } |
488 | 3.12M | } |
489 | 5.90M | return 0; |
490 | 5.90M | } |
491 | | |
492 | | /* ************************************* */ |
493 | | |
494 | | /* Search for BitTorrent commands */ |
495 | | static void ndpi_int_search_bittorrent_tcp(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) |
496 | 5.91M | { |
497 | 5.91M | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
498 | | |
499 | 5.91M | if(packet->payload_packet_len == 0) { |
500 | 0 | return; |
501 | 0 | } |
502 | | |
503 | 5.91M | ndpi_int_search_bittorrent_tcp_zero(ndpi_struct, flow); |
504 | 5.91M | } |
505 | | |
506 | | /* ************************************* */ |
507 | | |
508 | 4.50M | static u_int8_t is_port(u_int16_t a, u_int16_t b, u_int16_t what) { |
509 | 4.50M | return(((what == a) || (what == b)) ? 1 : 0); |
510 | 4.50M | } |
511 | | |
512 | | /* ************************************* */ |
513 | | |
514 | | static void ndpi_skip_bittorrent(struct ndpi_detection_module_struct *ndpi_struct, |
515 | 358k | struct ndpi_flow_struct *flow) { |
516 | 358k | if(flow->detected_protocol_stack[0] == NDPI_PROTOCOL_BITTORRENT) |
517 | 223 | return; |
518 | 358k | if(search_into_bittorrent_cache(ndpi_struct, flow)) |
519 | 74.3k | ndpi_add_connection_as_bittorrent(ndpi_struct, flow, -1, 0, NDPI_CONFIDENCE_DPI_CACHE); |
520 | 284k | else |
521 | 284k | NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); |
522 | 358k | } |
523 | | |
524 | | /* ************************************* */ |
525 | | |
526 | | static void ndpi_search_bittorrent(struct ndpi_detection_module_struct *ndpi_struct, |
527 | 7.51M | struct ndpi_flow_struct *flow) { |
528 | 7.51M | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
529 | 7.51M | char *bt_proto = NULL; |
530 | | |
531 | 7.51M | NDPI_LOG_DBG(ndpi_struct, "Search bittorrent\n"); |
532 | | |
533 | | /* This is broadcast */ |
534 | 7.51M | if(packet->iph) { |
535 | 7.29M | if((packet->iph->saddr == 0xFFFFFFFF) || (packet->iph->daddr == 0xFFFFFFFF)) |
536 | 17.8k | goto exclude_bt; |
537 | | |
538 | 7.27M | if(packet->udp) { |
539 | 1.50M | u_int16_t sport = ntohs(packet->udp->source), dport = ntohs(packet->udp->dest); |
540 | | |
541 | 1.50M | if(is_port(sport, dport, 3544) /* teredo */ |
542 | 1.50M | || is_port(sport, dport, 5246) || is_port(sport, dport, 5247) /* CAPWAP */) { |
543 | 23.9k | exclude_bt: |
544 | 23.9k | NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); |
545 | 23.9k | return; |
546 | 6.18k | } |
547 | 1.50M | } |
548 | 7.27M | } |
549 | | |
550 | 7.49M | if(flow->detected_protocol_stack[0] != NDPI_PROTOCOL_BITTORRENT) { |
551 | 7.49M | if(packet->tcp != NULL) { |
552 | 5.91M | ndpi_int_search_bittorrent_tcp(ndpi_struct, flow); |
553 | 5.91M | } else if(packet->udp != NULL) { |
554 | | /* UDP */ |
555 | 1.58M | const char *bt_search = "BT-SEARCH * HTTP/1.1\r\n"; |
556 | 1.58M | const char *bt_search1 = "d1:ad2:id20:"; |
557 | | |
558 | 1.58M | if((ntohs(packet->udp->source) < 1024) |
559 | 1.47M | || (ntohs(packet->udp->dest) < 1024) /* High ports only */) { |
560 | 204k | ndpi_skip_bittorrent(ndpi_struct, flow); |
561 | 204k | return; |
562 | 204k | } |
563 | | |
564 | | /* |
565 | | Check for uTP http://www.bittorrent.org/beps/bep_0029.html |
566 | | |
567 | | wireshark/epan/dissectors/packet-bt-utp.c |
568 | | */ |
569 | | |
570 | 1.38M | if( |
571 | 1.38M | (packet->payload_packet_len > 22 && strncmp((const char*)packet->payload, bt_search, strlen(bt_search)) == 0) || |
572 | 1.38M | (packet->payload_packet_len > 12 && strncmp((const char*)packet->payload, bt_search1, strlen(bt_search1)) == 0) |
573 | 1.38M | ) { |
574 | 295 | ndpi_add_connection_as_bittorrent(ndpi_struct, flow, -1, 1, NDPI_CONFIDENCE_DPI); |
575 | 295 | return; |
576 | 1.38M | } else if(packet->payload_packet_len >= 20) { |
577 | | /* Check if this is protocol v0 */ |
578 | 1.11M | u_int8_t v0_extension = packet->payload[17]; |
579 | 1.11M | u_int8_t v0_flags = packet->payload[18]; |
580 | 1.11M | int rc; |
581 | | |
582 | 1.11M | if((rc = is_utpv1_pkt(packet->payload, packet->payload_packet_len)) > 0) { |
583 | 8.00k | bt_proto = ndpi_strnstr((const char *)&packet->payload[20], BITTORRENT_PROTO_STRING, packet->payload_packet_len-20); |
584 | | /* DATA check is quite weak so in that case wait for multiple packets/confirmations */ |
585 | 8.00k | if(rc == 1 || bt_proto != NULL || (rc == 2 && flow->packet_counter > 2)) { |
586 | 1.84k | goto bittorrent_found; |
587 | 6.15k | } else { |
588 | 6.15k | return; |
589 | 6.15k | } |
590 | 1.10M | } else if((packet->payload[0]== 0x60) |
591 | 4.77k | && (packet->payload[1]== 0x0) |
592 | 1.36k | && (packet->payload[2]== 0x0) |
593 | 915 | && (packet->payload[3]== 0x0) |
594 | 532 | && (packet->payload[4]== 0x0)) { |
595 | | /* Heuristic */ |
596 | 217 | bt_proto = ndpi_strnstr((const char *)&packet->payload[20], BITTORRENT_PROTO_STRING, packet->payload_packet_len-20); |
597 | 217 | goto bittorrent_found; |
598 | | /* CSGO/DOTA conflict */ |
599 | 1.10M | } else if((v0_flags < 6 /* ST_NUM_STATES */) && (v0_extension < 3 /* EXT_NUM_EXT */)) { |
600 | 280k | u_int32_t ts = ntohl(*((u_int32_t*)&(packet->payload[4]))); |
601 | 280k | u_int32_t now; |
602 | | |
603 | 280k | now = (u_int32_t)(packet->current_time_ms / 1000); |
604 | | |
605 | 280k | if((ts < (now+86400)) && (ts > (now-86400))) { |
606 | 42 | bt_proto = ndpi_strnstr((const char *)&packet->payload[20], BITTORRENT_PROTO_STRING, packet->payload_packet_len-20); |
607 | 42 | goto bittorrent_found; |
608 | 42 | } |
609 | 823k | } else if(ndpi_strnstr((const char *)&packet->payload[20], BITTORRENT_PROTO_STRING, packet->payload_packet_len-20) |
610 | 823k | ) { |
611 | 223 | goto bittorrent_found; |
612 | 223 | } |
613 | | |
614 | 1.11M | } |
615 | | |
616 | 1.37M | flow->bittorrent_stage++; |
617 | | |
618 | 1.37M | if(flow->bittorrent_stage < 5) { |
619 | | /* We have detected bittorrent but we need to wait until we get a hash */ |
620 | | |
621 | 1.30M | if(packet->payload_packet_len > 19 /* min size */) { |
622 | 1.05M | if(ndpi_strnstr((const char *)packet->payload, ":target20:", packet->payload_packet_len) |
623 | 1.05M | || ndpi_strnstr((const char *)packet->payload, ":find_node1:", packet->payload_packet_len) |
624 | 1.05M | || ndpi_strnstr((const char *)packet->payload, "d1:ad2:id20:", packet->payload_packet_len) |
625 | 1.05M | || ndpi_strnstr((const char *)packet->payload, ":info_hash20:", packet->payload_packet_len) |
626 | 1.05M | || ndpi_strnstr((const char *)packet->payload, ":filter64", packet->payload_packet_len) |
627 | 1.05M | || ndpi_strnstr((const char *)packet->payload, "d1:rd2:id20:", packet->payload_packet_len) |
628 | 1.05M | || (bt_proto = ndpi_strnstr((const char *)packet->payload, BITTORRENT_PROTO_STRING, packet->payload_packet_len)) |
629 | 1.05M | ) { |
630 | 3.58k | bittorrent_found: |
631 | 3.58k | if(bt_proto != NULL && ((u_int8_t *)&bt_proto[27] - packet->payload + |
632 | 341 | sizeof(flow->protos.bittorrent.hash)) < packet->payload_packet_len) { |
633 | 139 | memcpy(flow->protos.bittorrent.hash, &bt_proto[27], sizeof(flow->protos.bittorrent.hash)); |
634 | 139 | flow->extra_packets_func = NULL; /* Nothing else to do */ |
635 | 139 | } |
636 | | |
637 | 3.58k | NDPI_LOG_INFO(ndpi_struct, "found BT: plain\n"); |
638 | 3.58k | ndpi_add_connection_as_bittorrent(ndpi_struct, flow, -1, 0, NDPI_CONFIDENCE_DPI); |
639 | 3.58k | return; |
640 | 1.25k | } |
641 | 1.05M | } |
642 | | |
643 | 1.30M | return; |
644 | 1.30M | } |
645 | | |
646 | 64.2k | ndpi_skip_bittorrent(ndpi_struct, flow); |
647 | 64.2k | } |
648 | 7.49M | } |
649 | | |
650 | 5.97M | if(flow->packet_counter > 5) |
651 | 90.1k | ndpi_skip_bittorrent(ndpi_struct, flow); |
652 | 5.97M | } |
653 | | |
654 | | /* ************************************* */ |
655 | | |
656 | | void init_bittorrent_dissector(struct ndpi_detection_module_struct *ndpi_struct) |
657 | 20.4k | { |
658 | 20.4k | ndpi_register_dissector("BitTorrent", ndpi_struct, |
659 | 20.4k | ndpi_search_bittorrent, |
660 | 20.4k | NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_OR_UDP_WITH_PAYLOAD_WITHOUT_RETRANSMISSION, |
661 | 20.4k | 1, NDPI_PROTOCOL_BITTORRENT); |
662 | 20.4k | } |