1 (* Copyright 2001, 2002 b8_bavard, b8_fee_carabine, INRIA *)
3 This file is part of mldonkey.
5 mldonkey is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 mldonkey is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with mldonkey; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 1. Download the .torrent file
23 *****************************
25 "announce" = "http://sucs.org:6969/announce";
30 "path" = [ "Mandrake91-cd1-inst.i586.iso"; ]
36 "Mandrake91-cd2-ext.i586.iso";
44 "Mandrake91-cd3-i18n.i586.iso";
51 "name" = "mandrake9.1";
52 "piece length" = 262144;
53 "pieces" = "[EAd\155ã´gÛ ÓþËf\134Ê«\025\016ô͵,1U\150À
54 \132\147îª\n%ù\\é,\012ÿC\008GÈÓd!æ¾öuL!\134Ô\016\152&\017¾\008³¢d\029Ë3\031Ï\134
55 #»×\025\137¡=¢.®\019§´\138î.ñ\151O\137Ùÿ,£ç&\019Àۢç\156.ù\150<Eªª\153\018\145\
56 149d\147[+J=º\155l\139Î\028¡dVÉ\000-\017°Å¤\013\154¼>A¹Ã5ïIt\007\020©ãÚÀÈÈ\014O®
57 ô1\152UÄ\026K\021^ãúì5Í¿ü \026\149\131q\024\015¸]Òþ£\027&\148\\ã-©\028WMÂ5...";
62 2. Extract BitTorrent information needed:
63 *****************************************
65 Metainfo files are bencoded dictionaries with the following keys -
68 The url of the tracker.
71 This maps to a dictionary, with keys described below.
73 The 'name' key maps to a string which is the suggested name to save
74 the file (or directory) as. It is purely advisory.
76 'piece length' maps to the number of bytes in each piece the file is
77 split into. For the purposes of transfer, files are split into
78 fixed-size pieces which are all the same length except for possibly
79 the last one which may be truncated. Piece length is almost always a
80 power of two, most commonly 2^20 .
82 'pieces' maps to a string whose length is a multiple of 20. It is to
83 be subdivided into strings of length 20, each of which is the sha1
84 hash of the piece at the corresponding index.
86 There is also a key 'length' or a key 'files', but not both or
87 neither. If 'length' is present then the download represents a
88 single file, otherwise it represents a set of files which go in a
91 In the single file case, 'length' maps to the length of the file in
94 For the purposes of the other keys, the multi-file case is treated
95 as only having a single file by concatenating the files in the order
96 they appear in the files list. The files list is the value 'files'
97 maps to, and is a list of dictionaries containing the following keys -
100 The length of the file, in bytes. 'path'
101 A list of strings corresponding to subdirectory names, the last of
102 which is the actual file name (a zero length list is an error case).
104 In the single file case, the 'name' key is the name of a file, in the
105 muliple file case, it's the name of a directory.
108 3. Contact the tracker regularly to update file information
109 ***********************************************************
111 Tracker GET requests have the following keys by HTTP:
114 The 20 byte sha1 hash of the bencoded form of the 'info' value from
115 the metainfo file. Note that this is a substring of the metainfo
116 file. This value will almost certainly have to be escaped.
119 A string of length 20 which this downloader uses as its id. Each
120 downloader generates its own id at random at the start of a new
121 download. This value will also almost certainly have to be escaped.
124 An optional parameter giving the ip (or dns name) which this peer is
125 at. Generally used for the origin if it's on the same machine as the
129 The port number this peer is listening on. Common behavior is for a
130 downloader to try to listen on port 6881 and if that port is taken
131 try 6882, then 6883, etc. and give up after 6889.
134 The total amount uploaded so far, encoded in base ten ascii.
137 The total amount downloaded so far, encoded in base ten ascii.
140 The number of bytes this peer still has to download, encoded in base
141 ten ascii. Note that this can't be computed from downloaded and the
142 file length since it might be a resume, and there's a chance that
143 some of the downloaded data failed an integrity check and had to be
147 This is an optional key which maps to 'started', 'completed', or
148 'stopped' (or '', which is the same as not being present).
151 { 'failure reason' = ... }
154 'interval' = ....; (* before next request to tracker *)
165 4. Contact every peer regularly
166 *******************************
170 type int = BigEndian.int32
173 string8 (prefixed by length): "BitTorrent protocol"
174 int8[8]: reserved(zeros)
175 int8[20 bytes]: Sha1.string (Bencode.encode file.file_info)
176 int8[20 bytes]: peer id
179 string8 (prefixed by length): "BitTorrent protocol"
180 int8[8]: reserved(zeros)
181 int8[20 bytes]: Sha1.string (Bencode.encode file.file_info)
182 int8[20 bytes]: peer id
184 ----> disconnect if sha1 don't match, or if peer id is unexpected
187 int: len of message (byte+payload) 0 -> keepalive sent every 2 minutes
188 byte8: opcode of message
192 Connections start out choked and not interested.
195 * 0 - choke: you have been blocked
196 * 1 - unchoke: you have been unblocked
197 * 2 - interested: I'm interested in downloading this file now
198 * 3 - not interested: I'm not interested in downloading this file now
199 With bencoded payload:
201 int : index of new completed chunk
203 string: a bitfield of bit 1 for downloaded chunks
204 byte: bits are inverted 0....7 ---> 7 .... 0
208 int: length (power of 2, 2 ^ 15)
213 * 8 - cancel: cancel a requesu
216 int: length (power of 2, 2 ^ 15)
218 Choke/unchoke every 10 seconds
229 open TcpBufferedSocket
233 let log_prefix = "[BT]"
236 lprintf_nl2 log_prefix fmt
238 let azureus_porttest_random = ref 0
241 BTHeader of (gconn -> TcpBufferedSocket.t ->
242 (string * string * Sha1.t) -> unit)
243 | Reader of (gconn -> TcpBufferedSocket.t -> unit)
246 mutable gconn_handler : ghandler;
247 mutable gconn_refill : (TcpBufferedSocket.t -> unit) list;
248 mutable gconn_close_on_write : bool;
251 module TcpMessages = struct
260 | Request of int * int64 * int64
261 | Piece of int * int64 * string * int * int
262 | Cancel of int * int64 * int64
269 | Unchoke -> "Unchoke"
270 | Interested -> "Interested"
271 | NotInterested -> "NotInterested"
272 | Have n -> Printf.sprintf "Have %Ld" n
273 | BitField s -> Printf.sprintf "BitField %s" (String.escaped s)
274 | Request (index, offset, len) ->
275 Printf.sprintf "Request %d %Ld[%Ld]" index offset len
276 | Piece (index, offset, s, pos, len) ->
277 Printf.sprintf "Piece %d %Ld[%d]" index offset len
278 | Cancel (index, offset, len) ->
279 Printf.sprintf "Cancel %d %Ld[%Ld]" index offset len
281 | PeerID s -> Printf.sprintf "PeerID [%s]" (String.escaped s)
283 let parsing opcode m =
289 | 4 -> Have (get_uint64_32 m 0)
291 | 6 -> Request (get_int m 0, get_uint64_32 m 4, get_uint64_32 m 8)
292 | 7 -> Piece (get_int m 0, get_uint64_32 m 4, m, 8, String.length m - 8)
293 | 8 -> Cancel (get_int m 0, get_uint64_32 m 4, get_uint64_32 m 8)
295 | _ -> raise Not_found
297 let buf = Buffer.create 100
304 | Choke -> buf_int8 buf 0
305 | Unchoke -> buf_int8 buf 1
306 | Interested -> buf_int8 buf 2
307 | NotInterested -> buf_int8 buf 3
308 | Have i -> buf_int8 buf 4; buf_int64_32 buf i
309 | BitField string -> buf_int8 buf 5; Buffer.add_string buf string
310 | Request (index, pos, len) ->
312 buf_int buf index; buf_int64_32 buf pos; buf_int64_32 buf len
313 | Piece (num, index, s, pos, len) ->
316 buf_int64_32 buf index;
317 Buffer.add_substring buf s pos len
323 let s = Buffer.contents buf in
324 str_int s 0 (String.length s - 4);
328 (*************************************************************************)
332 (*************************************************************************)
338 module UdpMessages = struct
341 PingReq of int * string * string
342 | SupernodePongReq of int * string * string
343 | NodePongReq of int * string
344 | UnknownReq of int * string
345 let extract_string s pos =
346 let end_pos = String.index_from s pos '\000' in
347 String.sub s pos (end_pos - pos), pos + 1
350 match int_of_char p.[0] with
352 let min_enc_type = get_int p 1 in
353 let unknown = String.sub p 5 1 in
354 let netname, pos = extract_string p 6 in
356 PingReq (min_enc_type, unknown, netname)
359 let min_enc_type = get_int p 1 in
360 let unknown = String.sub p 5 6 in
361 let netname, pos = extract_string p 11 in
362 SupernodePongReq (min_enc_type, unknown, netname)
365 let min_enc_type = get_int p 1 in
366 let unknown = String.sub p 5 (String.length p - 5) in
367 NodePongReq (min_enc_type, unknown)
368 | n -> UnknownReq (n, p)
371 let b = Buffer.create 100 in
374 | PingReq (min_enc_type, unknown, netname) ->
376 buf_int b min_enc_type;
377 Buffer.add_string b unknown;
378 Buffer.add_string b netname;
380 | SupernodePongReq (min_enc_type, unknown, netname) ->
382 buf_int b min_enc_type;
383 Buffer.add_string b unknown;
384 Buffer.add_string b netname;
386 | NodePongReq (min_enc_type, unknown) ->
388 buf_int b min_enc_type;
389 Buffer.add_string b unknown
390 | UnknownReq (opcode, unknown) ->
391 Buffer.add_string b unknown;
396 let b = Buffer.create 100 in
399 | PingReq (min_enc_type, unknown, netname) ->
400 Printf.bprintf b "Ping (%d, " min_enc_type;
401 bprint_ints b unknown;
402 Printf.bprintf b ", %s)" netname
403 | SupernodePongReq (min_enc_type, unknown, netname) ->
404 Printf.bprintf b "SupernodePong (%d, " min_enc_type;
405 bprint_ints b unknown;
406 Printf.bprintf b ", %s)" netname
407 | NodePongReq (min_enc_type, unknown) ->
408 Printf.bprintf b "NodePong (%d, " min_enc_type;
409 bprint_ints b unknown;
411 | UnknownReq (opcode, unknown) ->
412 Printf.bprintf b "Unknown \n ";
413 bprint_ints b unknown;
414 Printf.bprintf b "\n ";
415 bprint_chars b unknown;
416 Printf.bprintf b "\n"
420 let udp_send t ip port ping msg =
422 if !verbose_udp then begin
423 lprintf "Message UDP to %s:%d\n%s\n" (Ip.to_string ip) port
429 UdpSocket.write t ping s ip port
431 lprintf "FT: Exception %s in udp_send\n" (Printexc2.to_string e)
437 exception Wait_for_more of string
439 let bt_handler parse_fun handler c sock =
441 let b = TcpBufferedSocket.buf sock in
442 if not c.client_received_peer_id then
444 (* we get and parse the peer_id here because it may
445 not be sent from trackers that test us for NAT
446 (they just wait for our handshake response and
447 then drop the connection) *)
450 let payload = String.sub b.buf b.pos 20 in
451 let p = parse_fun (-1) payload in
453 c.client_received_peer_id <- true;
457 lprintf_nl "Exception %s in BTProtocol.parse_fun while handling peer_id"
458 (Printexc2.to_string e);
461 close sock Closed_by_user
463 else raise (Wait_for_more "peer_id");
464 (* must break the loop even if there is data, because the socket
465 could be closed beneath our feet and then b.buf seems to be zero length
466 regardless of what b.len tells (this is a bug somewhere in
467 tcpBufferedSocket i think) *)
468 raise (Wait_for_more "after_peer_id");
471 let msg_len = get_int b.buf b.pos in
474 let (ip,port) = (TcpBufferedSocket.peer_addr sock) in
475 lprintf_nl "BT: Unknown message from %s:%d dropped!! peerid:%b data_len:%i msg_len:%i software: %s"
476 (Ip.to_string ip) port c.client_received_peer_id b.len msg_len (brand_to_string c.client_brand);
477 dump (String.sub b.buf b.pos (min b.len 30));
479 close sock Closed_by_user;
481 else if msg_len > 20000 then
482 (* We NEVER request pieces greater than size 20000, this client is
483 trying to waste our bandwidth ? *)
485 let (ip,port) = (TcpBufferedSocket.peer_addr sock) in
486 lprintf_nl "btprotocol.bt_handler: closed connection from %s:%d because of too much data!! data_len:%i msg_len:%i software: %s"
487 (Ip.to_string ip) port b.len msg_len (brand_to_string c.client_brand);
488 dump (String.sub b.buf b.pos (min b.len 30));
490 close sock Closed_by_user
492 else if b.len >= 4 + msg_len then
495 (* lprintf "Message complete: %d\n" msg_len; *)
497 let opcode = get_int8 b.buf b.pos in
498 let payload = String.sub b.buf (b.pos+1) (msg_len-1) in
500 (* lprintf "Opcode %d\n" opcode; *)
502 (* We use opcodes < 0 internaly and
503 they don't occur in the spec
505 if opcode < 0 then raise Not_found;
506 let p = parse_fun opcode payload in
507 (* lprintf "Parsed, calling handler\n"; *)
510 lprintf_nl "Exception %s in BTProtocol.parse_fun while handling message with opcode: %d"
511 (Printexc2.to_string e) opcode;
515 set_lifetime sock 130.
517 else raise (Wait_for_more "message")
519 if b.len != 0 then raise (Wait_for_more "loop")
522 if closed sock && s <> "after_peer_id" then
523 lprintf_nl "bt_handler: Socket was closed while waiting for more data in %s" s
525 lprintf_nl "Exception %s in bt_handler"
526 (Printexc2.to_string e)
528 let handlers info gconn =
529 let rec iter_read sock nread =
530 (* lprintf "iter_read %d\n" nread; *)
531 let b = TcpBufferedSocket.buf sock in
533 match gconn.gconn_handler with
535 (* dump (String.sub b.buf b.pos (min b.len 100)); *)
536 let slen = get_int8 b.buf b.pos in
537 if slen + 29 <= b.len then
539 (* get proto and file_id from handshake,
540 peer_id is not fetched here because
541 it might be late or not present
543 (* let proto = String.sub b.buf (b.pos+1) slen in *)
544 let file_id = Sha1.direct_of_string
545 (String.sub b.buf (b.pos+9+slen) 20) in
546 let proto,pos = get_string8 b.buf b.pos in
547 let rbits = (String.sub b.buf (b.pos+pos) 8) in
548 buf_used b (slen+29);
549 h gconn sock (proto, rbits, file_id);
552 if (String.sub b.buf b.pos (min b.len 100)) = "NATCHECK_HANDSHAKE" then
553 write_string sock (Printf.sprintf "azureus_rand_%d" !azureus_porttest_random)
554 else if (TcpBufferedSocket.closed sock) then
555 let (ip,port) = (TcpBufferedSocket.peer_addr sock) in
556 lprintf_nl "bt-handshake: closed sock from %s:%d b.len:%i slen:%i"
557 (Ip.to_string ip) port b.len slen;
564 let set_bt_sock sock info ghandler =
566 gconn_handler = ghandler;
568 gconn_close_on_write = false;
570 TcpBufferedSocket.set_reader sock (handlers info gconn);
571 TcpBufferedSocket.set_refill sock (fun sock ->
572 match gconn.gconn_refill with
574 | refill :: _ -> refill sock
576 TcpBufferedSocket.set_handler sock TcpBufferedSocket.WRITE_DONE (
578 match gconn.gconn_refill with
581 gconn.gconn_refill <- tail;
584 if gconn.gconn_close_on_write then
585 set_lifetime sock 30.
586 (* TcpBufferedSocket.close sock "write done" *)
587 | refill :: _ -> refill sock)
591 * 0 - choke: you have been blocked
592 * 1 - unchoke: you have been unblocked
593 * 2 - interested: I'm interested in downloading this file now
594 * 3 - not interested: I'm not interested in downloading this file now
595 With bencoded payload:
597 int : index of new completed chunk
599 string: a bitfield of bit 1 for downloaded chunks
600 byte: bits are inverted 0....7 ---> 7 .... 0
604 int: length (power of 2, 2 ^ 15)
609 * 8 - cancel: cancel a requesu
612 int: length (power of 2, 2 ^ 15)
615 let send_client client_sock msg =
616 do_if_connected client_sock (fun sock ->
618 let s = TcpMessages.write msg in
619 if !verbose_msg_clients then begin
620 lprintf_nl "send message: %s" (TcpMessages.to_string msg);
625 lprintf_nl "CLIENT : Error %s in send_client"
626 (Printexc2.to_string e)
629 let zero8 = String.make 8 '\000'
631 let send_init client_uid file_id sock =
632 let buf = Buffer.create 100 in
633 buf_string8 buf "BitTorrent protocol";
634 Buffer.add_string buf zero8;
635 Buffer.add_string buf (Sha1.direct_to_string file_id);
636 Buffer.add_string buf (Sha1.direct_to_string client_uid);
637 let s = Buffer.contents buf in
641 http://www.bittorrent.org/beps/bep_0015.html *)
642 module UdpTracker = struct
646 let of_bits = string_of_bitstring
647 let bits = bitstring_of_string
650 Choose a random transaction ID.
651 Fill the connect request structure.
654 let connect_request txn =
655 of_bits ( BITSTRING { 0x41727101980L : 64 ; 0l : 32 ; txn : 32 } )
657 exception Error of string
659 let fail fmt = Printf.ksprintf (fun s -> raise (Error s)) fmt
663 Check whether the packet is at least 16 bytes.
664 Check whether the transaction ID is equal to the one you chose.
665 Check whether the action is connect.
666 Store the connection ID for future use.
668 let connect_response s exp_txn =
670 | { 0l : 32 ; txn : 32 ; conn_id : 64 } ->
671 if txn = exp_txn then conn_id else fail "error connect_response txn %ld expected %ld" txn exp_txn
672 | { 3l : 32 ; txn : 32 ; msg : -1 : string } -> fail "error connect_response txn %ld : %s" txn msg
673 | { _ } -> fail "error connect_response"
676 Choose a random transaction ID.
677 Fill the announce request structure.
680 let announce_request conn txn ~info_hash ~peer_id (downloaded,left,uploaded) event ?(key=0l) ~numwant port =
685 info_hash : 20 * 8 : string;
686 peer_id : 20 * 8 : string;
693 numwant : 32 ; (* key *)
698 Check whether the packet is at least 20 bytes.
699 Check whether the transaction ID is equal to the one you chose.
700 Check whether the action is announce.
701 Do not announce again until interval seconds have passed or an event has occurred.
703 let announce_response s exp_txn =
704 let rec clients rest l =
706 | { ip : 32 ; port : 16 ; rest : -1 : bitstring } -> clients rest ((ip,port)::l)
710 | { 1l : 32 ; txn : 32 ; interval : 32 ; leechers : 32 ; seeders : 32 ;
711 rest : -1 : bitstring } ->
712 if txn = exp_txn then
713 (interval,clients rest [])
715 fail "error announce_response txn %ld expected %ld" txn exp_txn
716 | { 3l : 32 ; txn : 32 ; msg : -1 : string } -> fail "error announce_response txn %ld : %s" txn msg
717 | { _ } -> fail "error announce_response"
720 If the tracker encounters an error, it might send an error packet.
722 Check whether the packet is at least 8 bytes.
723 Check whether the transaction ID is equal to the one you chose.
725 let error_response s =
727 | { 3l : 32 ; txn : 32 ; msg : -1 : string } -> Some (txn, msg)