[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Mldonkey-commits] Changes to mldonkey/src/networks/bittorrent/bTTracker
From: |
mldonkey-commits |
Subject: |
[Mldonkey-commits] Changes to mldonkey/src/networks/bittorrent/bTTracker.ml |
Date: |
Thu, 04 Aug 2005 18:00:29 -0400 |
Index: mldonkey/src/networks/bittorrent/bTTracker.ml
diff -u mldonkey/src/networks/bittorrent/bTTracker.ml:1.15
mldonkey/src/networks/bittorrent/bTTracker.ml:1.16
--- mldonkey/src/networks/bittorrent/bTTracker.ml:1.15 Fri Jul 22 14:32:12 2005
+++ mldonkey/src/networks/bittorrent/bTTracker.ml Thu Aug 4 22:00:23 2005
@@ -31,7 +31,8 @@
open BTTypes
open BTGlobals
open Bencode
-
+open BTTorrent
+open BTProtocol
open Gettext
let _s x = _s "BTTracker" x
@@ -69,6 +70,7 @@
mutable peer_ip : Ip.t;
mutable peer_port : int;
mutable peer_active : int;
+ mutable peer_key : string;
}
type tracker = {
@@ -77,9 +79,18 @@
mutable tracker_peers : tracker_peer Fifo.t;
mutable tracker_message_content : string;
mutable tracker_message_time : int;
+ mutable tracker_downloaded : int;
+ mutable tracker_complete : int;
+ mutable tracker_incomplete : int;
+ mutable tracker_last : int;
+ }
+
+type local_torrents_files = {
+ file_id : Sha1.t;
}
let tracker_sock = ref None
+(* let tracker_udp_sock = ref None *)
let tracked_files = Hashtbl.create 13
let ntracked_files = ref 0
@@ -95,6 +106,13 @@
"The maximal number of peers returned by the tracker"
int_option 20
+let tracker_force_local_torrents = define_option bittorrent_section
["tracker_force_local_torrents"]
+ "The tracker will check the torrent file is available if an announce request
is received"
+ bool_option true
+
+let tracker_use_key = define_option bittorrent_section ["tracker_use_key"]
+ "The tracker will check the client key to update ip if changed"
+ bool_option true
let int64_of_string v =
try
@@ -119,20 +137,31 @@
String "peers", List []
])
-let reply_has_tracker r info_hash peer_id peer_port peer_event =
+let reply_has_tracker r info_hash peer_id peer_ip peer_port peer_key peer_left
peer_event numwant no_peer_id =
- lprintf_nl () "tracker contacted for %s" (Sha1.to_string info_hash);
+ let tracker_ok = ref true in
+ if !verbose_msg_servers then
+ lprintf_nl () "tracker contacted for [%s]" (Sha1.to_hexa info_hash);
let tracker = try
Hashtbl.find tracked_files info_hash
with Not_found ->
- lprintf_nl () "Need new tracker";
- if !ntracked_files < !!max_tracked_files then
+ if !!tracker_force_local_torrents then begin
+ tracker_ok := false;
+ lprintf_nl () "Tracker rejected announce request for torrent
[%s]\n" (Sha1.to_hexa info_hash);
+ failwith "Unknown torrent"
+ end;
+ lprintf_nl () "[BT]: Need new tracker";
+ if !ntracked_files < !!max_tracked_files && !tracker_ok then
let tracker = {
tracker_id = info_hash;
tracker_table = Hashtbl.create 13;
tracker_peers = Fifo.create ();
tracker_message_time = 0;
tracker_message_content = "";
+ tracker_downloaded = 0;
+ tracker_complete = 0;
+ tracker_incomplete = 0;
+ tracker_last = (int_of_float (Unix.gettimeofday ()));
} in
incr ntracked_files;
Hashtbl.add tracked_files info_hash tracker;
@@ -146,19 +175,29 @@
let peer =
Hashtbl.find tracker.tracker_table peer_id
in
- peer.peer_ip <- fst (TcpBufferedSocket.peer_addr r.sock);
- peer.peer_port <- peer_port;
+ if !!tracker_use_key && peer.peer_ip != peer_ip && peer.peer_key !=
peer_key then
+ failwith "Invalid key";
+ if (not !!tracker_use_key || (peer.peer_key = peer_key && peer.peer_ip
!= peer_ip)) then
+ peer.peer_ip <- peer_ip;
+ if peer.peer_port != peer_port then
+ peer.peer_port <- peer_port;
peer.peer_active <- last_time ();
peer
with _ ->
let peer =
{
peer_id = peer_id;
- peer_ip = fst (TcpBufferedSocket.peer_addr r.sock);
- peer_port = peer_port;
+ peer_ip = peer_ip;
+ peer_port = peer_port;
+ peer_key = peer_key;
peer_active = last_time ();
} in
- lprintf_nl () "adding new peer";
+ if peer_left > 0 then
+ tracker.tracker_incomplete <- tracker.tracker_incomplete + 1
+ else
+ tracker.tracker_complete <- tracker.tracker_complete + 1;
+ if !verbose_msg_servers then
+ lprintf_nl () "Tracker adding new peer [%s]" (Sha1.to_string
peer_id);
Hashtbl.add tracker.tracker_table peer_id peer;
Fifo.put tracker.tracker_peers peer;
peer
@@ -166,23 +205,37 @@
let message =
match peer_event with
"completed" ->
+ tracker.tracker_incomplete <- tracker.tracker_incomplete - 1;
+ tracker.tracker_complete <- tracker.tracker_complete + 1;
void_message
-(* Reply with clients that could not connect to this tracker otherwise *)
+ (* Reply with clients that could not connect to this tracker otherwise *)
| "stopped" ->
+ if peer_left > 0 then
+ tracker.tracker_incomplete <- tracker.tracker_incomplete - 1
+ else
+ tracker.tracker_complete <- tracker.tracker_complete - 1;
void_message
-(* Don't return anything *)
+ (* Don't return anything *)
| _ ->
-(* Return the 20 best peers. In fact, we should only return peers if
- this peer is behind a firewall. *)
+ (* Return numwant peers if specified in the limit of max_tracker_reply.
+ In fact, we should only return peers if this peer is behind a firewall.
+ *)
if tracker.tracker_message_time < last_time () then
let list = ref [] in
lprintf_nl () "Tracker collecting peers:";
(try
- for i = 1 to !!max_tracker_reply do
+ let max_peer_replies =
+ if numwant > 0 then
+ min numwant !!max_tracker_reply
+ else
+ !!max_tracker_reply
+ in
+ for i = 1 to max_peer_replies do
let peer = Fifo.take tracker.tracker_peers in
- lprintf_nl () " %s:%d" (Ip.to_string
peer.peer_ip)peer.peer_port;
+ if !verbose_msg_servers then
+ lprintf_nl () " %s:%d" (Ip.to_string
peer.peer_ip)peer.peer_port;
list := peer :: !list
done
with _ -> ());
@@ -199,16 +252,26 @@
let message =
Dictionary [
String "interval", Int (Int64.of_int 600);
+ String "min interval", Int (Int64.of_int 600);
String "peers", List
(List.map (fun p ->
- Dictionary [
- String "peer id", String
- (Sha1.direct_to_string p.peer_id);
- String "ip", String (Ip.to_string p.peer_ip);
- String "port",
- Int (Int64.of_int p.peer_port);
- ]
- ) !list)
+ if no_peer_id = 1 then
+ Dictionary [
+ String "ip", String (Ip.to_string p.peer_ip);
+ String "port", Int (Int64.of_int p.peer_port);
+ ]
+ else
+ Dictionary [
+ String "peer id", String
+ (Sha1.direct_to_string p.peer_id);
+ String "ip", String (Ip.to_string p.peer_ip);
+ String "port", Int (Int64.of_int p.peer_port);
+ ]
+ ) !list);
+ String "downloaded", Int (Int64.of_int
tracker.tracker_downloaded);
+ String "complete", Int (Int64.of_int tracker.tracker_complete);
+ String "incomplete", Int (Int64.of_int
tracker.tracker_incomplete);
+ String "last", Int (Int64.of_int ((int_of_float
(Unix.gettimeofday ())) - tracker.tracker_last));
]
in
let m = Bencode.encode message in
@@ -229,42 +292,124 @@
try
add_reply_header r "Server" "MLdonkey";
add_reply_header r "Connection" "close";
- add_reply_header r "Content-Type" "application/x-bittorrent";
match r.get_url.Url.short_file with
- "/tracker" ->
-
+ "announce" ->
let args = r.get_url.Url.args in
let info_hash = ref Sha1.null in
let peer_id = ref Sha1.null in
+ let ip = ref Ip.null in
let port = ref 0 in
let uploaded = ref zero in
let downloaded = ref zero in
let left = ref zero in
let event = ref "" in
+ let compact = ref zero in
+ let hide = ref 0 in
+ let numwant = ref 0 in
+ let no_peer_id = ref 0 in
+ let key = ref "" in
+ let natmapped = ref 0 in
+ let localip = ref Ip.null in
List.iter (fun (name, arg) ->
match name with
| "info_hash" -> info_hash := Sha1.direct_of_string arg
| "peer_id" -> peer_id := Sha1.direct_of_string arg
+ | "ip" -> ip := (Ip.of_string arg)
| "port" -> port := int_of_string arg
| "uploaded" -> uploaded := int64_of_string arg
| "downloaded" -> downloaded := int64_of_string arg
| "left" -> left := int64_of_string arg
| "event" -> event := arg
- | _ -> lprintf_nl () "Unexpected [%s=%s]" name arg
+ | "compact" -> compact := int64_of_string arg
+ | "hide" -> hide := int_of_string arg
+ | "numwant" -> numwant := int_of_string arg
+ | "no_peer_id" -> no_peer_id := int_of_string arg
+ | "key" -> key := arg
+ | "natmapped" -> natmapped := int_of_string arg
+ | "localip" -> localip := (Ip.of_string arg)
+ | _ -> if !verbose_msg_servers then
+ lprintf_nl () "[BT]: Tracker: Unexpected [%s=%s]" name arg
) args;
- lprintf_nl () "Connection received by tracker:";
- lprintf_nl () " info_hash: %s" (Sha1.to_string !info_hash);
- lprintf_nl () " event: %s" !event;
- lprintf_nl () " downloaded: %d" (Int64.to_int !downloaded);
- lprintf_nl () " uploaded: %d" (Int64.to_int !uploaded);
-
- reply_has_tracker r !info_hash !peer_id !port !event
+ if !ip = Ip.null && !localip = Ip.null then
+ (* if the ip was not specified by the peer, use detected ip *)
+ ip := (fst (TcpBufferedSocket.peer_addr r.sock))
+ else if !ip = Ip.null && !natmapped = 1 then
+ (* use localip if available *)
+ ip := !localip;
+ if !verbose_msg_servers then begin
+ lprintf_nl () "Connection received by tracker from client %s:%d
with key [%s]:"
+ (Ip.to_string !ip) !port !key;
+ lprintf_nl () " info_hash: %s" (Sha1.to_hexa !info_hash);
+ lprintf_nl () " peer_id: %s" (Sha1.to_hexa !peer_id);
+ lprintf_nl () " event: %s" !event;
+ lprintf_nl () " numwant: %d" !numwant;
+ lprintf_nl () " compact: %d" (Int64.to_int !compact);
+ lprintf_nl () " downloaded: %d" (Int64.to_int !downloaded);
+ lprintf_nl () " uploaded: %d" (Int64.to_int !uploaded)
+ end;
+ (* Check hash then send reply *)
+ (try
+ let message_errors =
+ if ((List.length args) = 0) then
+ failwith "[BT] Empty request"
+ else if ((List.length args) < 4) then
+ failwith "[BT] Incomplete request"
+ else if !info_hash = Sha1.null then
+ failwith "[BT] Invalid info_hash"
+ else if !peer_id = Sha1.null then
+ failwith "[BT] Invalid peer_id"
+ else if !port = 0 then
+ failwith "[BT] Invalid port"
+ else if !!tracker_use_key && !key = "" then
+ failwith "[BT] Invalid client key"
+ else ()
+ in
+ match message_errors with _ ->
+ reply_has_tracker r !info_hash !peer_id !ip !port !key
(Int64.to_int !left) !event !numwant !no_peer_id
+ with e -> ())
+
+ | "scrape" ->
+ let files_tracked = ref [] in
+ let log_tracked_files = ref "" in
+ (* build the answer *)
+ Hashtbl.iter (fun info_hash tracker ->
+ files_tracked :=
+ (Dictionary [
+ String (Sha1.direct_to_string info_hash),
+ Dictionary [
+ String "complete", Int (Int64.of_int
tracker.tracker_complete);
+ String "downloaded", Int (Int64.of_int
tracker.tracker_downloaded);
+ String "incomplete", Int (Int64.of_int
tracker.tracker_incomplete);
+ ];
+ ]) :: !files_tracked;
+ if !verbose_msg_servers then begin
+ let next_file = (Printf.sprintf "[BT]: f: %s d: %d c: %d i:
%d\n"
+ (Sha1.to_hexa info_hash) tracker.tracker_downloaded
tracker.tracker_complete tracker.tracker_incomplete) in
+ log_tracked_files := !log_tracked_files ^ next_file
+ end
+ ) tracked_files;
+
+ if !verbose_msg_servers then begin
+ lprintf_nl () "Scrape request received by tracker";
+ lprintf_nl () "Sending scrape list:";
+ lprintf_nl () "f: (file hash) d: (downloaded) c: (complete) i:
(incomplete)";
+ lprint_string !log_tracked_files;
+ end;
+ let message = Dictionary [ String "files", List !files_tracked ] in
+ let m = Bencode.encode message in
+ r.reply_content <- m
+
+ | "favicon.ico" ->
+ if !verbose_msg_servers then
+ lprintf_nl () "favicon.ico request received by tracker";
+ add_reply_header r "Content-Type" "image/x-icon";
+ r.reply_content <- File.to_string "favicon.ico"
| filename ->
-
- lprintf_nl () "Request for .torrent [%s]" filename;
+ if !verbose_msg_servers then
+ lprintf_nl () "Tracker received a request for .torrent: [%s]"
filename;
if (Filename2.last_extension filename <> ".torrent") then
failwith "Incorrect filename 1";
for i = 1 to String.length filename - 1 do
@@ -277,7 +422,7 @@
in sub-directories in former versions. *)
let filename =
- let file_name = Filename.concat old_torrents_directory filename in
+ let file_name = Filename.concat torrents_directory filename in
(* lprintf " xx [%s]/[%s]\n" file_name filename; *)
if Sys.file_exists file_name then file_name else
let file_name = Filename.concat downloads_directory filename in
@@ -292,13 +437,78 @@
failwith
(Printf.sprintf "Tracker HTTPD: torrent [%s] not found" filename)
in
+ add_reply_header r "Content-Type" "application/x-bittorrent";
r.reply_content <- File.to_string filename
with e ->
- lprintf_nl () "for request [%s] exception %s"
- (Url.to_string r.get_url) (Printexc2.to_string e);
+ if !verbose_msg_servers then
+ lprintf_nl () "for request [%s] exception %s"
+ (Url.to_string r.get_url) (Printexc2.to_string e);
+ match e with
+ Not_found ->
+ r.reply_head <- "404 Not Found"
+ | _ ->
+ let message =
+ Dictionary [
+ String "failure reason", String (Printexc2.to_string e);
+ ]
+ in
+ let m = Bencode.encode message in
+ r.reply_content <- m
+
+(* Working zone *)
+
+
+(*
- r.reply_head <- "404 Not Found"
+let message_to_string t =
+ let buf = Buffer.create 100 in
+ begin
+ match t with
+ | _ ->
+ Buffer.add_string buf "unknown\n"
+ end;
+ Buffer.contents buf
+
+let ip_of_udp_packet p =
+ match p.UdpSocket.udp_addr with
+ Unix.ADDR_INET (inet, port) ->
+ Ip.of_inet_addr inet
+ | _ -> assert false
+
+let port_of_udp_packet p =
+ match p.UdpSocket.udp_addr with
+ Unix.ADDR_INET (inet, port) -> port
+ | _ -> assert false
+
+let udp_handler sock event =
+(* lprintf "G2: udp_handler called\n"; *)
+ match event with
+ UdpSocket.READ_DONE ->
+(* lprintf "G2: udp read_packets...\n"; *)
+ UdpSocket.read_packets sock (fun p ->
+ try
+(* lprintf "G2: udp one packet...\n"; *)
+ let pbuf = p.UdpSocket.udp_content in
+ let len = String.length pbuf in
+ let (ip,port) = match p.UdpSocket.udp_addr with
+ | Unix.ADDR_INET(ip, port) -> Ip.of_inet_addr ip, port
+ | _ -> raise Not_found
+ in
+(* lprintf "G2: calling udp_client_handler %s:%d\n"
+ (Ip.to_string ip) port; *)
+ let buf = p.UdpSocket.udp_content in
+ let len = String.length buf in
+ ()
+(* BTProtocol.udp_client_handler ip port buf *)
+ with e ->
+ lprintf "Error %s in udp_handler\n"
+ (Printexc2.to_string e);
+ ) ;
+ | _ -> ()
+
+*)
+(* *)
(* We came back to the "universal" tracker, i.e. a tracker for all files
with only a limitation on the number. So, this function is not useful anymore.
@@ -330,21 +540,28 @@
*)
let start_tracker () =
- if !!tracker_port <> 0 then
- let config = {
- bind_addr = Unix.inet_addr_any ;
- port = !!tracker_port;
- requests = [];
- addrs = [ Ip.of_string "255.255.255.255" ];
- base_ref = "";
- default = http_handler;
- } in
- let sock = TcpServerSocket.create "BT tracker"
- (Ip.to_inet_addr !!client_bind_addr)
- !!tracker_port (Http_server.handler config) in
- tracker_sock := Some sock;
- ()
-
+ if !!tracker_port <> 0 then begin
+ let config = {
+ bind_addr = if !!force_client_ip then Ip.to_inet_addr
!!set_client_ip else Unix.inet_addr_any ;
+ port = !!tracker_port;
+ requests = [];
+ addrs = [ Ip.of_string "255.255.255.255" ];
+ base_ref = "";
+ default = http_handler;
+ } in
+ let sock = TcpServerSocket.create "BT tracker"
+ (Ip.to_inet_addr !!client_bind_addr)
+ !!tracker_port (Http_server.handler config) in
+ tracker_sock := Some sock
+ end
+(*
+ if !!tracker_port <> 0 then begin
+ let sock = UdpSocket.create (Ip.to_inet_addr !!client_bind_addr)
+ !!tracker_port udp_handler in
+ tracker_udp_sock := Some sock;
+ UdpSocket.set_write_controler sock udp_write_controler;
+ end
+*)
let stop_tracker () =
match !tracker_sock with
None -> ()
@@ -352,7 +569,13 @@
(* We should also close all the sockets opened for HTTP connections, no ? *)
TcpServerSocket.close sock Closed_by_user;
tracker_sock := None
-
+(*
+ match !tracker_udp_sock with
+ None -> ()
+ | Some sock ->
+ UdpSocket.close sock Closed_by_user;
+ tracker_udp_sock := None
+ *)
(* Every 600 seconds, refresh the peers list *)
let clean_tracker_timer () =
let time_threshold = last_time () - 3600 in
@@ -386,3 +609,122 @@
let _ =
add_infinite_timer 600. clean_tracker_timer
+
+
+(*
+UDP tracker protocol
+
+Structures
+Before announcing or scraping, you have to obtain a connection ID.
+
+Choose a (random) transaction ID.
+Fill the connect input structure.
+Send the packet.
+connect input Offset Size Name Value
+0 64-bit integer connection_id 0x41727101980
+8 32-bit integer action 0
+12 32-bit integer transaction_id
+16
+
+Receive the packet.
+Check whether the packet is at least 16 bytes.
+Check whether the transaction ID is equal to the one you chose.
+Check whether the action is connect.
+Store the connection ID for future use.
+connect output Offset Size Name Value
+0 32-bit integer action 0
+4 32-bit integer transaction_id
+8 64-bit integer connection_id
+16
+
+Choose a (random) transaction ID.
+Fill the announce input structure.
+Send the packet.
+announce input Offset Size Name Value
+0 64-bit integer connection_id
+8 32-bit integer action 1
+12 32-bit integer transaction_id
+16 20-byte string info_hash
+36 20-byte string peer_id
+56 64-bit integer downloaded
+64 64-bit integer left
+72 64-bit integer uploaded
+80 32-bit integer event
+84 32-bit integer IP address 0
+88 32-bit integer key
+92 32-bit integer num_want -1
+96 16-bit integer port
+98
+
+Receive the packet.
+Check whether the packet is at least 20 bytes.
+Check whether the transaction ID is equal to the one you chose.
+Check whether the action is announce.
+Do not announce again until interval seconds have passed or an event has
happened.
+announce output Offset Size Name Value
+0 32-bit integer action 1
+4 32-bit integer transaction_id
+8 32-bit integer interval
+12 32-bit integer leechers
+16 32-bit integer seeders
+20 + 6 * n 32-bit integer IP address
+24 + 6 * n 16-bit integer TCP port
+20 + 6 * N
+
+Up to about 74 torrents can be scraped at once. A full scrape can't be done
with this protocol.
+
+Choose a (random) transaction ID.
+Fill the scrape input structure.
+Send the packet.
+scrape input Offset Size Name Value
+0 64-bit integer connection_id
+8 32-bit integer action 2
+12 32-bit integer transaction_id
+16 + 20 * n 20-byte string info_hash
+16 + 20 * N
+
+Receive the packet.
+Check whether the packet is at least 8 bytes.
+Check whether the transaction ID is equal to the one you chose.
+Check whether the action is scrape.
+scrape output Offset Size Name Value
+0 32-bit integer action 2
+4 32-bit integer transaction_id
+8 + 12 * n 32-bit integer seeders
+12 + 12 * n 32-bit integer completed
+16 + 12 * n 32-bit integer leechers
+8 + 12 * N
+
+If the tracker encounters an error, it might send an error packet.
+
+Receive the packet.
+Check whether the packet is at least 8 bytes.
+Check whether the transaction ID is equal to the one you chose.
+error output Offset Size Name Value
+0 32-bit integer action 3
+4 32-bit integer transaction_id
+8 string message
+
+If the tracker requires authentication, an authentication structure has to be
appended to every packet you send to the tracker. The hash is the first 8 bytes
of sha1(input + username + sha1(password)). authenticate input Offset Size Name
+0 8-byte zero-padded string username
+8 8-byte string hash
+16
+
+
+
+--------------------------------------------------------------------------------
+
+Actions
+0: connect
+1: announce
+2: scrape
+3: error
+
+--------------------------------------------------------------------------------
+
+Events
+0: none
+1: completed
+2: started
+3: stopped
+*)
\ No newline at end of file
- [Mldonkey-commits] Changes to mldonkey/src/networks/bittorrent/bTTracker.ml,
mldonkey-commits <=