Hello,
Attached is the patch for udp trackers support (BEP 15).
It may cause (easily solvable) merge conflicts with my previous patches.
It is intended for testing and is not 100% polished, some issues are outlined
below :
- depends on bitstring ocaml library
- the above dependency is hardcoded into Makefile and relies on ocamlfind (see
BITSTRING_DIR)
- http trackers are disabled (to simplify testing)
- uses ugly resume_clients_hook to solve circular dependency
- tracker address resolution is synchronous (blocking)
- verbose logging (grep udpt)
--
ygrek
http://ygrek.org.ua/
diff --git a/Makefile b/Makefile
index c9e8f78..c5adb09 100644
--- a/Makefile
+++ b/Makefile
@@ -28,8 +28,11 @@ NO_LIBS_opt=
NO_STATIC_LIBS_opt=
NO_CMXA=
-LIBS_byte=-custom bigarray.cma unix.cma str.cma
-LIBS_opt= bigarray.cmxa unix.cmxa str.cmxa
+BITSTRING_DIR="$(shell ocamlfind query bitstring)"
+#BITSTRING_DIR=/usr/lib/ocaml/bitstring
+
+LIBS_byte=-custom bigarray.cma unix.cma str.cma -I $(BITSTRING_DIR) bitstring.cma
+LIBS_opt= bigarray.cmxa unix.cmxa str.cmxa -I $(BITSTRING_DIR) bitstring.cmxa
BIGARRAY_LIBS_opt=bigarray.cmxa
BIGARRAY_LIBS_byte=bigarray.cma
@@ -91,7 +94,7 @@ SRC_FILETP=src/networks/fileTP
SUBDIRS=$(CDK) $(LIB) $(RSS) $(XML) $(NET) tools \
$(COMMON) $(DRIVER) $(MP3) src/config/$(OS_FILES)
-INCLUDES += $(foreach file, $(SUBDIRS), -I $(file))
+INCLUDES += $(foreach file, $(SUBDIRS), -I $(file)) -I $(BITSTRING_DIR)
CFLAGS:=$(CFLAGS) $(CONFIG_INCLUDES) $(GTKCFLAGS) $(GD_CFLAGS)
@@ -426,6 +429,7 @@ BITTORRENT_SRCS= \
$(SRC_BITTORRENT)/bTRate.ml \
$(SRC_BITTORRENT)/bTTypes.ml \
$(SRC_BITTORRENT)/bTOptions.ml \
+ $(SRC_BITTORRENT)/bTUdpTracker.ml \
$(SRC_BITTORRENT)/bTProtocol.ml \
$(SRC_BITTORRENT)/bTTorrent.ml \
$(SRC_BITTORRENT)/bTGlobals.ml \
@@ -1482,11 +1486,14 @@ ZOGSOURCES += $(BITTORRENT_ZOG:.zog=.ml)
MLTSOURCES += $(BITTORRENT_MLT:.mlt=.ml)
MLPSOURCES += $(BITTORRENT_MLP:.mlcpp=.ml)
+$(SRC_BITTORRENT)/bTUdpTracker.ml: $(SRC_BITTORRENT)/bTUdpTracker.mlp
+ camlp4of -I $(BITSTRING_DIR) bitstring.cma bitstring_persistent.cma pa_bitstring.cmo -impl $< -o $@
+
build/mlbt.cmxa: $(BITTORRENT_OBJS) $(BITTORRENT_CMXS)
- $(OCAMLOPT) -a -o $@ $(BITTORRENT_OBJS) $(LIBS_flags) $(_LIBS_flags) $(BITTORRENT_CMXS)
+ $(OCAMLOPT) -a -o $@ -I $(BITSTRING_DIR) $(BITTORRENT_OBJS) $(LIBS_flags) $(_LIBS_flags) $(BITTORRENT_CMXS)
build/mlbt.cma: $(BITTORRENT_OBJS) $(BITTORRENT_CMOS)
- $(OCAMLC) -a -o $@ $(BITTORRENT_OBJS) $(LIBS_flags) $(_LIBS_flags) $(BITTORRENT_CMOS)
+ $(OCAMLC) -a -o $@ -I $(BITSTRING_DIR) $(BITTORRENT_OBJS) $(LIBS_flags) $(_LIBS_flags) $(BITTORRENT_CMOS)
diff --git a/src/networks/bittorrent/bTClients.ml b/src/networks/bittorrent/bTClients.ml
index ff27686..d58e3d4 100644
--- a/src/networks/bittorrent/bTClients.ml
+++ b/src/networks/bittorrent/bTClients.ml
@@ -18,7 +18,8 @@
*)
-(** Functions used in client<->client communication
+(** Functions used in client<->client communication
+ and also client<->tracker
*)
(** A peer (or client) is always a remote peer in this file.
@@ -73,6 +74,125 @@ let http11_ok = "HTTP/1.1 200 OK"
let next_uploaders = ref ([] : BTTypes.client list)
let current_uploaders = ref ([] : BTTypes.client list)
+(** Check that client is valid and record it *)
+let maybe_new_client file id ip port =
+ let cc = Geoip.get_country_code_option ip in
+ if id <> !!client_uid
+ && ip != Ip.null
+ && port <> 0
+ && (match !Ip.banned (ip, cc) with
+ | None -> true
+ | Some reason ->
+ if !verbose_connect then
+ lprintf_file_nl (as_file file) "%s:%d blocked: %s" (Ip.to_string ip) port reason;
+ false)
+ then
+ ignore (new_client file id (ip,port) cc);
+ if !verbose_sources > 1 then
+ lprintf_file_nl (as_file file) "Received %s:%d" (Ip.to_string ip) port
+
+
+let resume_clients_hook = ref (fun _ -> assert false)
+
+include struct
+
+(* open modules locally *)
+open BTUdpTracker
+open UdpSocket
+
+let string_of_event = function
+ | READ_DONE -> "READ_DONE"
+ | WRITE_DONE -> "WRITE_DONE"
+ | CAN_REFILL -> "CAN_REFILL"
+ | BASIC_EVENT e -> match e with
+ | CLOSED reason -> "CLOSED " ^ (string_of_reason reason)
+ | RTIMEOUT -> "RTIMEOUT"
+ | WTIMEOUT -> "WTIMEOUT"
+ | LTIMEOUT -> "LTIMEOUT"
+ | CAN_READ -> "CAN_READ"
+ | CAN_WRITE -> "CAN_WRITE"
+
+(** talk to udp tracker and parse response
+ except of parsing should perform everything that
+ talk_to_tracker's inner function does FIXME refactor both
+
+ Better create single global udp socket and use it for all
+ tracker requests and distinguish trackers by txn? FIXME?
+ *)
+let talk_to_udp_tracker host port args file t need_sources =
+ try
+ lprintf_nl "udpt start with %s:%d" host port;
+ let addr = try (Unix.gethostbyname host).Unix.h_addr_list.(0) with exn -> failwith ("failed to resolve " ^ host) in
+ let ip = Ip.of_inet_addr addr in
+ lprintf_nl "udpt resolved to ip %s" (Ip.to_string ip);
+ let socket = create Unix.inet_addr_any 0 (fun sock event ->
+(* lprintf_nl "udpt got event %s for %s" (string_of_event event) host *)
+ match event with
+ | WRITE_DONE | CAN_REFILL -> ()
+ | READ_DONE -> assert false (* set_reader prevents this *)
+ | BASIC_EVENT x -> match x with
+ | CLOSED _ -> ()
+ | CAN_READ | CAN_WRITE -> assert false (* udpSocket implementation prevents this *)
+ | LTIMEOUT | WTIMEOUT | RTIMEOUT -> close sock (Closed_for_error "udpt timeout"))
+ in
+ BasicSocket.set_wtimeout (sock socket) 120.;
+ BasicSocket.set_rtimeout (sock socket) 120.;
+ let txn = Random.int32 Int32.max_int in
+ lprintf_nl "udpt txn %ld for %s" txn host;
+ write socket false (connect_request txn) ip port;
+ set_reader socket (fun _ ->
+ let p = read socket in
+ let conn = connect_response p.udp_content txn in
+ lprintf_nl "udpt connection_id %Ld for %s" conn host;
+ let txn = Random.int32 Int32.max_int in
+ lprintf_nl "udpt txn' %ld for host %s" txn host;
+ let int s = Int64.of_string (List.assoc s args) in
+ let req = announce_request conn txn
+ ~info_hash:(List.assoc "info_hash" args)
+ ~peer_id:(List.assoc "peer_id" args)
+ (int "downloaded",int "left",int "uploaded")
+ (match List.assoc "event" args with
+ | "completed" -> 1l
+ | "started" -> 2l
+ | "stopped" -> 3l
+ | "" -> 0l
+ | s -> lprintf_nl "udpt event %s? for %s" s host; 0l)
+ ~numwant:(try Int32.of_string (List.assoc "numwant" args) with _ -> -1l)
+ (int_of_string (List.assoc "port" args))
+ in
+ write socket false req ip port;
+ set_reader socket (fun _ ->
+ let p = read socket in
+
+ t.tracker_last_conn <- last_time ();
+ file.file_tracker_connected <- true;
+ t.tracker_interval <- 600;
+ t.tracker_min_interval <- 600;
+ if need_sources then t.tracker_last_clients_num <- 0;
+
+ let (interval,clients) = announce_response p.udp_content txn in
+ lprintf_nl "udpt got interval %ld clients %d for host %s" interval (List.length clients) host;
+ if interval > 0l then
+ begin
+ t.tracker_interval <- Int32.to_int interval;
+ if t.tracker_min_interval > t.tracker_interval then
+ t.tracker_min_interval <- t.tracker_interval
+ end;
+ List.iter (fun (ip',port) ->
+ let ip = Ip.of_int64 (Int64.logand 0xFFFFFFFFL (Int64.of_int32 ip')) in
+ lprintf_nl "udpt got %s:%d" (Ip.to_string ip) port;
+ t.tracker_last_clients_num <- t.tracker_last_clients_num + 1;
+ maybe_new_client file Sha1.null ip port
+ ) clients;
+ close socket Closed_by_user;
+ lprintf_nl "udpt interact done for %s" host;
+ if need_sources then !resume_clients_hook file
+ ))
+ with
+ exn ->
+ lprintf_nl "udpt interact exn %s" (Printexc2.to_string exn)
+
+end (* include *)
(**
In this function we connect to a tracker.
@@ -173,28 +293,30 @@ let connect_trackers file event f =
then
begin
(* if we already tried to connect but failed, disable tracker, but allow re-enabling *)
- if file.file_tracker_connected && t.tracker_last_clients_num = 0 &&
- t.tracker_last_conn < 1 then begin
+ (* FIXME t.tracker_last_conn < 1 only at first connect, so later failures will stay undetected! *)
+ if file.file_tracker_connected && t.tracker_last_clients_num = 0 && t.tracker_last_conn < 1 then
+ begin
if !verbose_msg_servers then
- lprintf_nl "Request error from tracker: disabling %s" t.tracker_url;
- t.tracker_status <- Disabled (intern "MLDonkey: Request error from tracker")
+ lprintf_nl "Request error from tracker: disabling %s" (show_tracker_url t.tracker_url);
+ t.tracker_status <- Disabled (intern "MLDonkey: Request error from tracker")
end
(* Send request to tracker *)
- else begin
- let args = if String.length t.tracker_id > 0 then
- ("trackerid", t.tracker_id) :: args else args
- in
- let args = if String.length t.tracker_key > 0 then
- ("key", t.tracker_key) :: args else args
- in
- if !verbose_msg_servers then
- lprintf_nl "get_sources_from_tracker: tracker_connected:%s id:%s key:%s last_clients:%i last_conn-last_time:%i file: %s"
- (string_of_bool file.file_tracker_connected)
- t.tracker_id t.tracker_key t.tracker_last_clients_num
- (t.tracker_last_conn - last_time()) file.file_name;
-
+ else
+ let args = if String.length t.tracker_id > 0 then
+ ("trackerid", t.tracker_id) :: args else args
+ in
+ let args = if String.length t.tracker_key > 0 then
+ ("key", t.tracker_key) :: args else args
+ in
+ if !verbose_msg_servers then
+ lprintf_nl "connect_trackers: connected:%s id:%s key:%s last_clients:%i last_conn-last_time:%i numwant:%s file: %s"
+ (string_of_bool file.file_tracker_connected)
+ t.tracker_id t.tracker_key t.tracker_last_clients_num
+ (t.tracker_last_conn - last_time()) (try List.assoc "numwant" args with _ -> "_") file.file_name;
+
+ match t.tracker_url with
+ | `Http url ->
let module H = Http_client in
- let url = t.tracker_url in
let r = {
H.basic_request with
H.req_url = Url.of_string ~args: args url;
@@ -206,19 +328,20 @@ let connect_trackers file event f =
if !verbose_msg_servers then
lprintf_nl "Request sent to tracker %s for file: %s"
- t.tracker_url file.file_name;
+ url file.file_name;
H.wget r
(fun fileres ->
t.tracker_last_conn <- last_time ();
file.file_tracker_connected <- true;
f t fileres)
- end
+ | `Other url -> assert false (* should have been disabled *)
+ | `Udp (host,port) -> talk_to_udp_tracker host port args file t need_sources
end
else
if !verbose_msg_servers then
lprintf_nl "Request NOT sent to tracker %s - next request in %ds for file: %s"
- t.tracker_url (t.tracker_interval - (last_time () - t.tracker_last_conn)) file.file_name
+ (show_tracker_url t.tracker_url) (t.tracker_interval - (last_time () - t.tracker_last_conn)) file.file_name
) enabled_trackers
let start_upload c =
@@ -354,7 +477,9 @@ let disconnect_clients file =
let download_finished file =
if List.memq file !current_files then
begin
- connect_trackers file "completed" (fun _ _ -> ()); (*must be called before swarmer gets removed from file*)
+ connect_trackers file "completed" (fun _ _ ->
+ lprintf_file_nl (as_file file) "Tracker return: completed %s" file.file_name;
+ ()); (*must be called before swarmer gets removed from file*)
(*CommonComplexOptions.file_completed*)
file_completed (as_file file);
(* Remove the swarmer for this file as it is not useful anymore... *)
@@ -1304,6 +1429,9 @@ let resume_clients file =
lprintf_file_nl (as_file file) "Exception %s in resume_clients" (Printexc2.to_string e)
) file.file_clients
+let () =
+ resume_clients_hook := resume_clients
+
(** Check if the value replied by the tracker is correct.
@param key the name of the key
@param n the value to check
@@ -1313,17 +1441,15 @@ let resume_clients file =
let chk_keyval key n url name =
let int_n = (Int64.to_int n) in
if !verbose_msg_clients then
- lprintf_nl "Reply from %s in file: %s has %s: %d" url name key int_n;
+ lprintf_nl "Reply from %s in file: %s has %s: %d" (show_tracker_url url) name key int_n;
if int_n > -1 then
int_n
else begin
- lprintf_nl "Reply from %s in file: %s has an invalid %s value: %d" url name key int_n;
+ lprintf_nl "Reply from %s in file: %s has an invalid %s value: %d" (show_tracker_url url) name key int_n;
0
end
-
-(** In this function we initiate a connection to the file tracker
- to get sources.
+(** In this function we interact with the tracker
@param file The file for which we want some sources
@param url Url of the tracker
If we have less than !!ask_tracker_threshold sources
@@ -1335,6 +1461,7 @@ let get_sources_from_tracker file =
(*This is the function which will be called by the http client
for parsing the response
*)
+ let tracker_url = show_tracker_url t.tracker_url in
let tracker_reply =
try
File.to_string filename
@@ -1344,7 +1471,7 @@ let get_sources_from_tracker file =
match tracker_reply with
| "" ->
if !verbose_connect then
- lprintf_file_nl (as_file file) "Empty reply from tracker";
+ lprintf_file_nl (as_file file) "Empty reply from tracker %s" tracker_url;
Bencode.decode ""
| _ -> Bencode.decode tracker_reply
in
@@ -1359,7 +1486,7 @@ let get_sources_from_tracker file =
| _ -> (match t.tracker_status with
| Disabled_failure (i, _) ->
lprintf_file_nl (as_file file) "Received good message from Tracker %s in file: %s after %d bad attempts"
- t.tracker_url file.file_name i
+ (show_tracker_url t.tracker_url) file.file_name i
| _ -> ());
(* Received good message from tracker after failures, re-enable tracker *)
t.tracker_status <- Enabled);
@@ -1373,9 +1500,9 @@ let get_sources_from_tracker file =
lprintf_file_nl (as_file file) "Failure no. %d%s from Tracker %s in file: %s Reason: %s"
(match t.tracker_status with | Disabled_failure (i,_) -> i | _ -> 1)
(if !!tracker_retries = 0 then "" else Printf.sprintf "/%d" !!tracker_retries)
- t.tracker_url file.file_name (Charset.to_utf8 failure)
+ tracker_url file.file_name (Charset.to_utf8 failure)
| String "warning message", String warning ->
- lprintf_file_nl (as_file file) "Warning from Tracker %s in file: %s Reason: %s" t.tracker_url file.file_name warning
+ lprintf_file_nl (as_file file) "Warning from Tracker %s in file: %s Reason: %s" tracker_url file.file_name warning
| String "interval", Int n ->
t.tracker_interval <- chk_keyval (Bencode.print key) n t.tracker_url file.file_name;
(* in case we don't receive "min interval" *)
@@ -1406,11 +1533,11 @@ let get_sources_from_tracker file =
| String "key", String n ->
t.tracker_key <- n;
if !verbose_msg_clients then
- lprintf_file_nl (as_file file) "%s in file: %s has key: %s" t.tracker_url file.file_name n
+ lprintf_file_nl (as_file file) "%s in file: %s has key: %s" tracker_url file.file_name n
| String "tracker id", String n ->
t.tracker_id <- n;
if !verbose_msg_clients then
- lprintf_file_nl (as_file file) "%s in file: %s has tracker id %s" t.tracker_url file.file_name n
+ lprintf_file_nl (as_file file) "%s in file: %s has tracker id %s" tracker_url file.file_name n
| String "peers", List list ->
List.iter (fun v ->
@@ -1478,6 +1605,11 @@ let get_sources_from_tracker file =
lprintf_file_nl (as_file file) "get_sources_from_tracker: got %i source(s) for file %s"
t.tracker_last_clients_num file.file_name;
resume_clients file
+ (*
+ lprintf_file_nl (as_file file) "talk_to_tracker: got %i source(s) from %s"
+ t.tracker_last_clients_num tracker_url;
+ if need_sources then resume_clients file
+ *)
| _ -> assert false
in
diff --git a/src/networks/bittorrent/bTComplexOptions.ml b/src/networks/bittorrent/bTComplexOptions.ml
index 8ce1a77..566729d 100644
--- a/src/networks/bittorrent/bTComplexOptions.ml
+++ b/src/networks/bittorrent/bTComplexOptions.ml
@@ -219,7 +219,7 @@ let file_to_value file =
"file_uploaded", int64_to_value (file.file_uploaded);
"file_id", string_to_value (Sha1.to_string file.file_id);
"file_trackers", (list_to_value string_to_value)
- (List.map (fun t -> t.tracker_url) file.file_trackers);
+ (List.map (fun t -> show_tracker_url t.tracker_url) file.file_trackers);
(* OK, but I still don't like the idea of forgetting all the clients.
We should have a better strategy, ie rating the clients and connecting
to them depending on the results of our last connections. And then,
diff --git a/src/networks/bittorrent/bTGlobals.ml b/src/networks/bittorrent/bTGlobals.ml
index 1f23a51..dd92e0f 100644
--- a/src/networks/bittorrent/bTGlobals.ml
+++ b/src/networks/bittorrent/bTGlobals.ml
@@ -230,17 +230,31 @@ let create_temp_file file_temp file_files file_state =
file_temp);
file_fd
-let can_handle_tracker t =
- String2.check_prefix (String.lowercase t.tracker_url) "http://"
+let make_tracker_url url =
+ let url = String.lowercase url in
+ if String2.check_prefix url "http://" then
+ `Http url
+ else
+ try Scanf.sscanf url "udp://%s@:%d" (fun host port -> `Udp (host,port))
+ with _ -> `Other url
+
+(** invariant: [make_tracker_url (show_tracker_url url) = url] *)
+let show_tracker_url : tracker_url -> string = function
+ | `Http url | `Other url -> url
+ | `Udp (host,port) -> Printf.sprintf "udp://%s:%d" host port
+
+let can_handle_tracker = function
+ | `Http _ -> false
+ | `Udp _ -> true
+ | `Other _ -> false
let rec set_trackers file file_trackers =
match file_trackers with
| [] -> ()
| url :: q ->
- if not (List.exists (fun tracker ->
- tracker.tracker_url = url
- ) file.file_trackers) then
- let t = {
+ let url = make_tracker_url url in
+ if not (List.exists (fun tracker -> tracker.tracker_url = url) file.file_trackers) then
+ let t = {
tracker_url = url;
tracker_interval = 600;
tracker_min_interval = 600;
@@ -253,12 +267,11 @@ let rec set_trackers file file_trackers =
tracker_torrent_last_dl_req = 0;
tracker_id = "";
tracker_key = "";
- tracker_status = Enabled
- } in
- if not (can_handle_tracker t) then
- t.tracker_status <- Disabled_mld (intern "Tracker type not supported");
- file.file_trackers <- t :: file.file_trackers;
- set_trackers file q
+ tracker_status = if can_handle_tracker url then Enabled
+ else Disabled_mld (intern "Tracker type not supported")
+ } in
+ file.file_trackers <- t :: file.file_trackers;
+ set_trackers file q
let new_file file_id t torrent_diskname file_temp file_state user group =
try
@@ -867,7 +880,7 @@ let remove_client c =
let remove_tracker url file =
if !verbose_msg_servers then
List.iter (fun tracker ->
- lprintf_nl "Old tracker list :%s" tracker.tracker_url
+ lprintf_nl "Old tracker list: %s" (show_tracker_url tracker.tracker_url)
) file.file_trackers;
List.iter (fun bad_tracker ->
if bad_tracker.tracker_url = url then
@@ -875,7 +888,7 @@ let remove_tracker url file =
) file.file_trackers;
if !verbose_msg_servers then
List.iter (fun tracker ->
- lprintf_nl "New tracker list :%s" tracker.tracker_url
+ lprintf_nl "New tracker list: %s" (show_tracker_url tracker.tracker_url)
) file.file_trackers
let tracker_is_enabled t =
diff --git a/src/networks/bittorrent/bTInteractive.ml b/src/networks/bittorrent/bTInteractive.ml
index d7930e2..70c02e8 100644
--- a/src/networks/bittorrent/bTInteractive.ml
+++ b/src/networks/bittorrent/bTInteractive.ml
@@ -193,14 +193,15 @@ let op_file_print file o =
Printf.bprintf buf "\\</tr\\>\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr ());
let tracker_header_printed = ref false in
List.iter (fun tracker ->
+ let tracker_url = show_tracker_url tracker.tracker_url in
let tracker_text =
match tracker.tracker_status with
| Disabled s | Disabled_mld s ->
- Printf.sprintf "\\<font color=\\\"red\\\"\\>disabled: %s\\<br\\>\\--error: %s\\</font\\>" tracker.tracker_url s
+ Printf.sprintf "\\<font color=\\\"red\\\"\\>disabled: %s\\<br\\>\\--error: %s\\</font\\>" tracker_url s
| Disabled_failure (i,s) ->
- Printf.sprintf "\\<font color=\\\"red\\\"\\>disabled: %s\\<br\\>\\--error: %s (try %d)\\</font\\>" tracker.tracker_url s i
+ Printf.sprintf "\\<font color=\\\"red\\\"\\>disabled: %s\\<br\\>\\--error: %s (try %d)\\</font\\>" tracker_url s i
| _ ->
- Printf.sprintf "enabled: %s" tracker.tracker_url
+ Printf.sprintf "enabled: %s" tracker_url
in
html_mods_td buf [
@@ -209,7 +210,7 @@ let op_file_print file o =
else
("", "sr br", "")
);
- (tracker.tracker_url, "sr", tracker_text)];
+ (tracker_url, "sr", tracker_text)];
Printf.bprintf buf "\\</tr\\>\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr ());
tracker_header_printed := true;
) file.file_trackers;
@@ -370,12 +371,13 @@ let op_file_print file o =
Printf.bprintf buf "Trackers:\n";
List.iter (fun tracker ->
+ let tracker_url = show_tracker_url tracker.tracker_url in
match tracker.tracker_status with
| Disabled s | Disabled_mld s ->
- Printf.bprintf buf "%s, disabled: %s\n" tracker.tracker_url s
+ Printf.bprintf buf "%s, disabled: %s\n" tracker_url s
| Disabled_failure (i,s) ->
- Printf.bprintf buf "%s, disabled (try %d): %s\n" tracker.tracker_url i s
- | _ -> Printf.bprintf buf "%s\n" tracker.tracker_url
+ Printf.bprintf buf "%s, disabled (try %d): %s\n" tracker_url i s
+ | _ -> Printf.bprintf buf "%s\n" tracker_url
) file.file_trackers;
if file.file_torrent_diskname <> "" then
Printf.bprintf buf "Torrent diskname: %s\n" file.file_torrent_diskname;
@@ -646,9 +648,7 @@ let load_torrent_string s user group =
(* Save the torrent, because we later want to put
it in the seeded directory. *)
let torrent_is_usable = ref false in
- let can_handle_tracker url =
- String2.check_prefix url "http://" in
- List.iter (fun url -> if can_handle_tracker url then torrent_is_usable := true)
+ List.iter (fun url -> if can_handle_tracker (make_tracker_url url) then torrent_is_usable := true)
(if torrent.torrent_announce_list <> [] then torrent.torrent_announce_list else [torrent.torrent_announce]);
if not !torrent_is_usable then raise (Torrent_can_not_be_used torrent.torrent_name);
diff --git a/src/networks/bittorrent/bTProtocol.ml b/src/networks/bittorrent/bTProtocol.ml
index 8d24926..ce66d7b 100644
--- a/src/networks/bittorrent/bTProtocol.ml
+++ b/src/networks/bittorrent/bTProtocol.ml
@@ -218,7 +218,6 @@ With bencoded payload:
Choke/unchoke every 10 seconds
*)
-
open BasicSocket
open CommonTypes
open Printf2
@@ -637,3 +636,4 @@ let send_init client_uid file_id sock =
Buffer.add_string buf (Sha1.direct_to_string client_uid);
let s = Buffer.contents buf in
write_string sock s
+
diff --git a/src/networks/bittorrent/bTTorrent.ml b/src/networks/bittorrent/bTTorrent.ml
index fc13385..451b243 100644
--- a/src/networks/bittorrent/bTTorrent.ml
+++ b/src/networks/bittorrent/bTTorrent.ml
@@ -112,12 +112,12 @@ let decode_torrent s =
match key, value with
String "path", List path ->
current_file := path_list_to_string path;
- if !verbose_msg_servers then
- lprintf_nl "[BT] Parsed a new path: [%s]" !current_file
+ (*if !verbose_msg_servers then
+ lprintf_nl "[BT] Parsed a new path: [%s]" !current_file*)
| String "path.utf-8", List path_utf8 ->
current_file_utf8 := path_list_to_string path_utf8;
- if !verbose_msg_servers then
- lprintf_nl "[BT] Parsed path.utf-8: [%s]" !current_file
+ (*if !verbose_msg_servers then
+ lprintf_nl "[BT] Parsed path.utf-8: [%s]" !current_file*)
| String "length", Int n ->
length := !length ++ n;
current_length := n;
@@ -147,7 +147,7 @@ let decode_torrent s =
match key, value with
String "announce", String tracker_url ->
if !verbose_msg_servers then
- lprintf_nl "[BT] New tracker added :%s" tracker_url;
+ lprintf_nl "[BT] New tracker added: %s" tracker_url;
announce := tracker_url
| String "announce-list", List list ->
List.iter (fun url_list ->
diff --git a/src/networks/bittorrent/bTTypes.ml b/src/networks/bittorrent/bTTypes.ml
index aa5af21..474d26f 100644
--- a/src/networks/bittorrent/bTTypes.ml
+++ b/src/networks/bittorrent/bTTypes.ml
@@ -222,6 +222,11 @@ type tracker_status =
| Disabled_mld of string
| Disabled_failure of (int * string)
+type tracker_url =
+[ `Http of string (* url *)
+| `Udp of string * int (* host and port *)
+| `Other of string ]
+
type client = {
client_client : client CommonClient.client_impl;
mutable client_file : file;
@@ -277,7 +282,7 @@ type client = {
}
and tracker_info = {
- tracker_url : string;
+ tracker_url : tracker_url;
mutable tracker_interval : int;
mutable tracker_min_interval : int;
mutable tracker_last_conn : int;
diff --git a/src/networks/bittorrent/bTUdpTracker.mlp b/src/networks/bittorrent/bTUdpTracker.mlp
new file mode 100644
index 0000000..c086bf1
--- /dev/null
+++ b/src/networks/bittorrent/bTUdpTracker.mlp
@@ -0,0 +1,104 @@
+
+(*
+open BasicSocket
+open CommonTypes
+open Printf2
+open CommonOptions
+open Options
+open Md4
+open CommonGlobals
+open BigEndian
+open TcpBufferedSocket
+open AnyEndian
+open BTTypes
+*)
+
+(** UDP trackers
+ http://www.bittorrent.org/beps/bep_0015.html *)
+
+open Bitstring
+
+let of_bits = string_of_bitstring
+let bits = bitstring_of_string
+
+(*
+Choose a random transaction ID.
+Fill the connect request structure.
+Send the packet.
+*)
+let connect_request txn =
+ of_bits ( BITSTRING { 0x41727101980L : 64 ; 0l : 32 ; txn : 32 } )
+
+exception Error of string
+
+let fail fmt = Printf.ksprintf (fun s -> raise (Error s)) fmt
+
+(*
+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.
+*)
+let connect_response s exp_txn =
+ bitmatch bits s with
+ | { 0l : 32 ; txn : 32 ; conn_id : 64 } ->
+ if txn = exp_txn then conn_id else fail "error connect_response txn %ld expected %ld" txn exp_txn
+ | { 3l : 32 ; txn : 32 ; msg : -1 : string } -> fail "error connect_response txn %ld : %s" txn msg
+ | { _ } -> fail "error connect_response"
+
+(*
+Choose a random transaction ID.
+Fill the announce request structure.
+Send the packet.
+*)
+let announce_request conn txn ~info_hash ~peer_id (downloaded,left,uploaded) event ?(key=0l) ~numwant port =
+ of_bits (BITSTRING {
+ conn : 64 ;
+ 1l : 32 ;
+ txn : 32 ;
+ info_hash : 20 * 8 : string;
+ peer_id : 20 * 8 : string;
+ downloaded : 64 ;
+ left : 64 ;
+ uploaded : 64 ;
+ event : 32 ;
+ 0l : 32 ; (* ip *)
+ key : 32 ; (* key *)
+ numwant : 32 ; (* key *)
+ port : 16 })
+
+(*
+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 occurred.
+*)
+let announce_response s exp_txn =
+ let rec clients rest l =
+ bitmatch rest with
+ | { ip : 32 ; port : 16 ; rest : -1 : bitstring } -> clients rest ((ip,port)::l)
+ | { _ } -> l
+ in
+ bitmatch bits s with
+ | { 1l : 32 ; txn : 32 ; interval : 32 ; leechers : 32 ; seeders : 32 ;
+ rest : -1 : bitstring } ->
+ if txn = exp_txn then
+ (interval,clients rest [])
+ else
+ fail "error announce_response txn %ld expected %ld" txn exp_txn
+ | { 3l : 32 ; txn : 32 ; msg : -1 : string } -> fail "error announce_response txn %ld : %s" txn msg
+ | { _ } -> fail "error announce_response"
+
+(*
+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.
+*)
+let error_response s =
+ bitmatch bits s with
+ | { 3l : 32 ; txn : 32 ; msg : -1 : string } -> Some (txn, msg)
+ | { _ } -> None
+
diff --git a/src/utils/lib/options.ml4 b/src/utils/lib/options.ml4
index 43f0deb..38e3379 100644
--- a/src/utils/lib/options.ml4
+++ b/src/utils/lib/options.ml4
@@ -256,10 +256,10 @@ let really_load filename sections =
let temp_file = filename ^ ".tmp" in
if Sys.file_exists temp_file then
begin
- Printf.eprintf "File %s exists\n" temp_file;
- Printf.eprintf "An error may have occurred during previous configuration save.\n";
- Printf.eprintf "Please, check your configurations files, and rename/remove this file\n";
- Printf.eprintf "before restarting\n";
+ lprintf "File %s exists\n" temp_file;
+ lprintf "An error may have occurred during previous configuration save.\n";
+ lprintf "Please, check your configurations files, and rename/remove this file\n";
+ lprintf "before restarting\n";
exit 70
end;
Unix2.tryopen_read filename (fun ic ->
@@ -271,10 +271,10 @@ let really_load filename sections =
try
parse_gwmlrc stream
with e ->
- Printf.eprintf "Syntax error while parsing file %s at pos %d:(%s)\n"
+ lprintf "Syntax error while parsing file %s at pos %d:(%s)\n"
filename (Stream.count s) (Printexc2.to_string e);
- Printf.eprintf "it seems that %s is corrupt,\n" filename;
- Printf.eprintf "try to use a backup from %s\n"
+ lprintf "it seems that %s is corrupt,\n" filename;
+ lprintf "try to use a backup from %s\n"
(Filename.concat (Sys.getcwd ()) "old_config");
exit 70 in
Hashtbl.clear once_values;
_______________________________________________
Mldonkey-users mailing list
[email protected]
http://lists.nongnu.org/mailman/listinfo/mldonkey-users