CryptoPP: drop unnecessary caml/config.h include, fix #86
[mldonkey.git] / src / networks / donkey / donkeyClient.ml
blob8d439a9b950f868713bb7f783e98e2cececf2d88
1 (* Copyright 2001, 2002 b8_bavard, b8_fee_carabine, INRIA *)
2 (*
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
19 (* The function handling the cooperation between two clients. Most used
20 functions are defined in downloadOneFile.ml *)
22 open Int64ops
23 open Printf2
24 open Md4
26 open CommonSources
27 open CommonRoom
28 open CommonShared
29 open CommonGlobals
30 open CommonFile
31 open CommonClient
32 open CommonComplexOptions
33 open CommonSwarming
35 open CommonTypes
36 open Options
37 open BasicSocket
38 open DonkeyProtoCom
39 open TcpBufferedSocket
40 open DonkeyOptions
41 open CommonOptions
42 open DonkeyComplexOptions
43 open DonkeyGlobals
44 open DonkeyStats
45 open DonkeyTypes
46 open DonkeyReliability
48 module VB = VerificationBitmap
50 module Udp = DonkeyProtoUdp
52 (* Lifetime of a socket after sending interesting messages *)
53 let active_lifetime = 1200.
56 (*************************************************************************)
57 (* adding a source to the source-management *)
58 (*************************************************************************)
59 let add_source file ip tcp_port serverIP serverPort =
60 (* man, we are receiving sources from some clients even when we release *)
61 if (file_state file) = FileDownloading then
62 try
63 let cc = ref None in
64 let uid =
65 if low_id ip then
66 begin
67 try
68 (* without server, we can't request a callback *)
69 let s = Hashtbl.find servers_by_key serverIP in
70 if serverPort = s.server_port then
71 Indirect_address (serverIP, serverPort, id_of_ip ip, 0, Ip.null)
72 else
73 raise Not_found
74 with Not_found ->
75 if !!update_server_list_client then
76 begin
77 ignore (check_add_server serverIP serverPort);
78 Indirect_address (serverIP, serverPort, id_of_ip ip, 0, Ip.null)
79 end
80 else raise Not_found
81 end
82 else
83 if Ip.usable ip then begin
84 let uid = Direct_address (ip, tcp_port) in
85 (try
86 cc := (DonkeySources.find_source_by_uid uid).DonkeySources.source_country_code;
87 with Not_found ->
88 cc := Geoip.get_country_code_option ip);
89 if not (is_black_address ip tcp_port !cc) then
90 if not ( Hashtbl.mem banned_ips ip) then
91 uid
92 else
93 raise Not_found
94 else
95 raise Not_found
96 end
97 else
98 raise Not_found
100 let s = DonkeySources.create_source_by_uid uid !cc in
101 DonkeySources.set_request_result s file.file_sources File_new_source;
102 with Not_found -> ()
104 let is_banned c sock =
105 c.client_banned <- Hashtbl.mem banned_ips (fst (peer_addr sock))
108 (* Supports Emule Extended Protocol *)
109 let supports_eep cb =
110 match cb with
111 Brand_lmule | Brand_newemule | Brand_cdonkey |
112 Brand_emuleplus | Brand_hydranode | Brand_mldonkey3 |
113 Brand_shareaza | Brand_amule | Brand_lphant | Brand_verycd | Brand_imp -> true
114 | _ -> false
116 let ban_client c sock msg =
117 let ip = fst (peer_addr sock) in
118 if not (Hashtbl.mem banned_ips ip) then
119 let module M = DonkeyProtoClient in
121 if !verbose then
122 lprintf_nl "banned: %s %s" msg (full_client_identifier c);
124 count_banned c;
125 c.client_banned <- true;
126 Hashtbl.add banned_ips ip (last_time ());
128 if !!send_warning_messages then
129 client_send c ( M.SayReq (
130 Printf.sprintf
131 "[AUTOMATED ERROR] Your client %s has been banned" msg))
133 let corruption_warning c =
134 if !!send_warning_messages then
135 let module M = DonkeyProtoClient in
136 client_send c (
137 M.SayReq "
138 [AUTOMATED WARNING] It has been detected that your client
139 is sending corrupted data. Please double-check your hardware
140 (disk, memory, cpu) and software (latest version ?)")
142 let request_for c file sock =
143 if !!ban_queue_jumpers then
145 let record = Hashtbl.find old_requests (client_num c, file_num file) in
146 if record.last_request + 540 > last_time () then begin
147 let old_time = last_time () - record.last_request in
148 record.nwarnings <- record.nwarnings + 1;
149 record.last_request <- last_time ();
150 if record.nwarnings > 3 then raise Exit;
151 let module M = DonkeyProtoClient in
152 if record.nwarnings = 3 then begin
153 ban_client c sock "is connecting too fast";
154 raise Exit;
155 end;
156 if !verbose then
157 lprintf_nl "warning no. %d, connecting too fast (last connect %d sec. ago): %s"
158 record.nwarnings old_time (full_client_identifier c);
159 if !!send_warning_messages then
160 client_send c ( M.SayReq (
161 "[AUTOMATED WARNING] Your client is connecting too fast, it will get banned"))
162 end else
163 record.last_request <- last_time ();
164 with
165 Not_found ->
166 Hashtbl.add old_requests (client_num c, file_num file)
167 { last_request = last_time (); nwarnings = 0; }
168 | Exit -> ()
170 let clean_requests () = (* to be called every hour *)
171 Hashtbl.clear old_requests;
172 let remove_ips = ref [] in
173 Hashtbl.iter (fun ip time ->
174 if time + 3600 * !!ban_period < last_time () then
175 remove_ips := ip :: !remove_ips
176 ) banned_ips;
177 List.iter (fun ip ->
178 Hashtbl.remove banned_ips ip;
179 ) !remove_ips
181 let _ =
183 let client_enter_upload_queue c =
184 do_if_connected c.client_source.DonkeySources.source_sock (fun sock ->
185 set_rtimeout sock !!upload_timeout;
186 c.client_connect_time <- last_time ();
187 client_send c (
188 let module M = DonkeyProtoClient in
189 let module Q = M.AvailableSlot in
190 M.AvailableSlotReq Q.t);
192 if !verbose then
193 lprintf_nl "New uploader %s%s%s"
194 (full_client_identifier c)
195 (let slot_text = string_of_slot_kind (client_slot (as_client c)) true in
196 if slot_text = "" then "" else Printf.sprintf "(%s)" slot_text)
197 (match client_upload (as_client c) with
198 None -> ""
199 | Some f -> Printf.sprintf " for file %s" (CommonFile.file_best_name f))
202 client_ops.op_client_enter_upload_queue <- client_enter_upload_queue
205 let log_client_info c sock =
206 let buf = Buffer.create 100 in
207 let date = BasicSocket.date_of_int (last_time ()) in
208 Printf.bprintf buf "%-12s(%d):%d -> %-30s[%-14s %-20s] connected for %5d secs %-10s bw %5d/%-5d %-6s %2d/%-2d reqs "
209 (Date.simple date)
210 (nb_sockets ())
211 (client_num c)
213 let s = c.client_name in
214 let len = String.length s in
215 if len > 30 then String.sub s 0 30 else s)
217 (brand_to_string c.client_brand)
218 (match c.client_kind with Indirect_address _ | Invalid_address _ -> "LowID"
219 | Direct_address (ip,port) -> Printf.sprintf "%s:%d"
220 (Ip.to_string ip) port)
221 (last_time () - c.client_connect_time)
222 (if c.client_rank > 0 then
223 Printf.sprintf "rank %d" c.client_rank
224 else "")
225 (nwritten sock) (nread sock)
226 (if c.client_banned then "banned" else "")
227 c.client_requests_received
228 c.client_requests_sent
231 List.iter (fun r ->
232 Printf.bprintf buf "(%d)" r.DonkeySources.request_score;
233 ) c.client_source.DonkeySources.source_files;
234 Buffer.add_char buf '\n';
235 let m = Buffer.contents buf in
236 CommonEvent.add_event (Console_message_event m)
238 let disconnect_client c reason =
239 match c.client_source.DonkeySources.source_sock with
240 NoConnection -> ()
241 | ConnectionWaiting token ->
242 cancel_token token;
243 c.client_source.DonkeySources.source_sock <- NoConnection
244 | Connection sock ->
245 (try
246 DonkeyOneFile.remove_client_slot c;
247 c.client_comp <- None;
248 (try if c.client_checked then count_seen c with _ -> ());
249 (try if !!log_clients_on_console && c.client_name <> "" then
250 log_client_info c sock with _ -> ());
251 c.client_connect_time <- 0;
252 (try Hashtbl.remove connected_clients c.client_md4 with _ -> ());
253 (try CommonUploads.remove_pending_slot (as_client c) with _ -> ());
254 (try TcpBufferedSocket.close sock reason with _ -> ());
256 (* Remove the Connected and NoLimit tags *)
257 set_client_type c (client_type c
258 land (lnot (client_initialized_tag lor client_nolimit_tag)));
259 c.client_source.DonkeySources.source_sock <- NoConnection;
260 save_join_queue c;
261 c.client_slot <- SlotNotAsked;
263 (* clean_client_zones: clean all structures related to downloads when
264 a client disconnects *)
265 (try
266 match c.client_download with
267 | None -> ()
268 | Some (file, up) ->
269 CommonSwarming.unregister_uploader up;
270 c.client_download <- None
271 with _ -> ());
273 List.iter (fun (file, chunks, up) ->
274 try CommonSwarming.unregister_uploader up with _ -> ()
275 ) c.client_file_queue;
277 c.client_file_queue <- [];
278 c.client_session_downloaded <- 0L;
280 with exn -> lprintf_nl ~exn "disconnect_client");
281 set_client_disconnected c reason;
282 DonkeySources.source_disconnected c.client_source
284 let client_send_if_possible c sock msg =
285 if can_write_len sock (!!client_buffer_size/2) then
286 client_send c msg
288 let tag_udp_client = 203
290 let client_can_receive c =
291 match c.client_brand with
292 | Brand_mldonkey2 -> true
293 | Brand_mldonkey3 -> true
294 | _ -> false
296 let new_udp_client c group =
297 match c.client_kind with
298 Indirect_address _ | Invalid_address _ -> ()
299 | Direct_address (ip, port) ->
300 let uc = {
301 udp_client_last_conn = last_time ();
302 udp_client_ip = ip;
303 udp_client_port = port;
304 udp_client_can_receive = client_can_receive c
307 let uc =
309 let uc = UdpClientWHashtbl.find udp_clients uc in
310 uc.udp_client_last_conn <- last_time ();
312 with _ ->
313 Heap.set_tag uc tag_udp_client;
314 UdpClientWHashtbl.add udp_clients uc;
317 group.group <- UdpClientMap.add c.client_kind uc group.group
320 let udp_client_send uc t =
321 if not (is_black_address uc.udp_client_ip (uc.udp_client_port+4) None) then
322 begin
323 DonkeyProtoCom.udp_send (get_udp_sock ())
324 uc.udp_client_ip (uc.udp_client_port+4)
328 let client_udp_send ip port t =
329 if not (is_black_address ip (port+4) None) then
330 begin
331 DonkeyProtoCom.udp_send (get_udp_sock ())
332 ip (port+4)
336 let find_sources_in_groups c md4 =
337 if !!propagate_sources &&
338 (match c.client_brand with
339 Brand_mldonkey1 | Brand_overnet -> false
340 | _ -> true) then
342 let group = Hashtbl.find file_groups md4 in
344 let uc = UdpClientMap.find c.client_kind group.group in
345 uc.udp_client_last_conn <- last_time ()
346 (* the client is already known *)
347 with _ ->
348 (* a new client for this group *)
349 if client_can_receive c then begin
350 do_if_connected c.client_source.DonkeySources.source_sock (fun sock ->
351 (* send the list of members of the group to the client *)
352 let list = ref [] in
353 UdpClientMap.iter (fun _ uc ->
354 match ip_reliability uc.udp_client_ip with
355 Reliability_reliable | Reliability_neutral ->
356 list := (uc.udp_client_ip, uc.udp_client_port, uc.udp_client_ip) :: !list
357 | Reliability_suspicious _ -> ()
358 ) group.group;
359 if !list <> [] then begin
360 if !verbose_sources > 2 then
361 lprintf_nl "Send %d sources from file groups to mldonkey peer" (List.length !list);
362 let msg =
363 let module Q = DonkeyProtoClient.Sources in
364 DonkeyProtoClient.SourcesReq {
365 Q.md4 = md4;
366 Q.sources = !list;
369 client_send_if_possible c sock msg
372 end;
374 match c.client_kind with
375 Indirect_address _ | Invalid_address _ -> ()
376 | Direct_address (ip, port) ->
377 (* send this client as a source for the file to all mldonkey clients in the group. add client to group *)
379 UdpClientMap.iter (fun _ uc ->
380 if uc.udp_client_can_receive then begin
381 if !verbose_sources > 2 then
382 lprintf_nl "Send new source to file groups UDP peers";
383 udp_client_send uc (
384 Udp.QueryLocationReplyUdpReq (
385 let module Q = DonkeyProtoServer.QueryLocationReply in
387 Q.md4 = md4;
388 Q.locs = [{ Q.ip = ip; Q.port = port }];
389 }]))
391 ) group.group;
392 new_udp_client c group
393 with _ ->
394 if Fifo.length DonkeyGlobals.file_groups_fifo >= max_file_groups then
395 Hashtbl.remove file_groups (Fifo.take file_groups_fifo);
396 let group = { group = UdpClientMap.empty } in
397 Hashtbl.add file_groups md4 group;
398 Fifo.put DonkeyGlobals.file_groups_fifo md4;
399 new_udp_client c group
401 let clean_groups () =
402 let one_day_before = last_time () - Date.day_in_secs in
403 Hashtbl.iter (fun file group ->
404 let map = group.group in
405 group.group <- UdpClientMap.empty;
406 UdpClientMap.iter (fun v uc ->
407 if uc.udp_client_last_conn > one_day_before then
408 group.group <- UdpClientMap.add v uc group.group
409 ) map
410 ) file_groups
412 let client_wants_file c md4 =
413 if md4 <> Md4.null && md4 <> Md4.one && md4 <> Md4.two then begin
414 find_sources_in_groups c md4;
418 let new_chunk up begin_pos end_pos =
419 let req_size = end_pos -- begin_pos in
420 let req_location = (begin_pos ++ end_pos) // (2L ** block_size) in
421 if !verbose_upload then
422 lprintf_nl "new block: (%Ld,%Ld) size %Ld chunk #%Ld" begin_pos end_pos req_size req_location;
423 if (req_size < Int64.zero) || (req_size > zone_size) || ((up.up_current <> req_location) && (req_size <> Int64.zero)) then
424 up.up_finish <- true;
425 if ((not up.up_finish) || (not !!upload_complete_chunks)) && (req_size > Int64.zero) && (req_size <= zone_size) then
426 let chunk = (begin_pos, end_pos) in
427 (* the zone requested is already "in the pipe" *)
428 if not (List.mem chunk up.up_flying_chunks) then
429 match up.up_chunks with
430 | [] ->
431 up.up_pos <- begin_pos;
432 up.up_end_chunk <- end_pos;
433 up.up_chunks <- [chunk];
434 | up_chunks ->
435 if not (List.mem chunk up_chunks) then
436 up.up_chunks <- up_chunks @ [chunk]
438 let identify_client_brand c =
439 if c.client_brand = Brand_unknown then
440 let md4 = Md4.direct_to_string c.client_md4 in
441 c.client_brand <- (
442 if md4.[5] = Char.chr 14 && md4.[14] = Char.chr 111 then
443 Brand_newemule
444 else if md4.[5] = 'M' && md4.[14] = 'L' then
445 Brand_mldonkey2
446 else
447 if DonkeySources.source_brand c.client_source then
448 Brand_overnet else Brand_edonkey)
450 let mod_array =
452 ("extasy", Brand_mod_extasy);
453 ("hunter", Brand_mod_hunter);
454 ("mortimer", Brand_mod_mortimer);
455 ("sivka", Brand_mod_sivka);
456 ("plus", Brand_mod_plus);
457 ("lsd", Brand_mod_lsd);
458 ("maella", Brand_mod_maella);
459 ("pille", Brand_mod_pille);
460 ("morphkad", Brand_mod_morphkad);
461 ("ef-mod", Brand_mod_efmod);
462 ("efmod", Brand_mod_efmod);
463 ("xtreme", Brand_mod_xtreme);
464 ("bionic", Brand_mod_bionic);
465 ("pawcio", Brand_mod_pawcio);
466 ("gammaoh", Brand_mod_gammaoh);
467 ("zzul", Brand_mod_zzul);
468 ("black hand", Brand_mod_blackhand);
469 ("lovelace", Brand_mod_lovelace);
470 ("morphnext", Brand_mod_morphnext);
471 ("fincan", Brand_mod_fincan);
472 ("ewombat", Brand_mod_ewombat);
473 ("mortillo", Brand_mod_mortillo);
474 ("emulespa\241a", Brand_mod_emulespana);
475 ("blackrat", Brand_mod_blackrat);
476 ("enkeydev", Brand_mod_enkeydev);
477 ("gnaddelwarz", Brand_mod_gnaddelwarz);
478 ("phoenix-kad", Brand_mod_phoenixkad);
479 ("phoenix", Brand_mod_phoenix);
480 ("koizo", Brand_mod_koizo);
481 ("ed2kfiles", Brand_mod_ed2kfiles);
482 ("athlazan", Brand_mod_athlazan);
483 ("goldi cryptum", Brand_mod_goldicryptum);
484 ("cryptum", Brand_mod_cryptum);
485 ("lamerzchoice", Brand_mod_lamerzchoice);
486 ("notdead", Brand_mod_notdead);
487 ("peace", Brand_mod_peace);
488 ("eastshare", Brand_mod_eastshare);
489 ("[mfck]", Brand_mod_mfck);
490 ("echanblard", Brand_mod_echanblard);
491 ("sp4rk", Brand_mod_sp4rk);
492 ("bloodymad", Brand_mod_bloodymad);
493 ("roman2k", Brand_mod_roman2k);
494 ("elfenwombat", Brand_mod_elfenwombat);
495 ("o\178", Brand_mod_o2);
496 ("sf-iom", Brand_mod_sfiom);
497 ("magic-elseve", Brand_mod_magic_elseve);
498 ("schlumpmule", Brand_mod_schlumpmule);
499 ("noamson", Brand_mod_noamson);
500 ("stormit", Brand_mod_stormit);
501 ("omax", Brand_mod_omax);
502 ("spiders", Brand_mod_spiders);
503 ("ib\233ricaxt", Brand_mod_ibericaxt);
504 ("ib\233rica", Brand_mod_iberica);
505 ("stonehenge", Brand_mod_stonehenge);
506 ("mison", Brand_mod_mison);
507 ("xlillo", Brand_mod_xlillo);
508 ("imperator", Brand_mod_imperator);
509 ("raziboom", Brand_mod_raziboom);
510 ("khaos", Brand_mod_khaos);
511 ("hardmule", Brand_mod_hardmule);
512 ("sc", Brand_mod_sc);
513 ("cy4n1d", Brand_mod_cy4n1d);
514 ("dmx", Brand_mod_dmx);
515 ("ketamine", Brand_mod_ketamine);
516 ("blackmule", Brand_mod_blackmule);
517 ("morphxt", Brand_mod_morphxt);
518 ("ngdonkey", Brand_mod_ngdonkey);
519 ("morph", Brand_mod_morph);
520 ("emule.de", Brand_mod_emulede);
521 ("aldo", Brand_mod_aldo);
522 ("dm", Brand_mod_dm);
523 ("lc", Brand_mod_lc);
524 ("lh", Brand_mod_lh);
525 ("l!onetwork", Brand_mod_lh);
526 ("lionetwork", Brand_mod_lh);
527 ("hawkstar", Brand_mod_hawkstar);
528 ("neo mule", Brand_mod_neomule);
529 ("cyrex", Brand_mod_cyrex);
530 ("zx", Brand_mod_zx);
531 ("ackronic", Brand_mod_ackronic);
532 ("rappis", Brand_mod_rappis);
533 ("overdose", Brand_mod_overdose);
534 ("hebmule", Brand_mod_hebmule);
535 ("senfei", Brand_mod_senfei);
536 ("spoofmod", Brand_mod_spoofmod);
537 ("fusspilz", Brand_mod_fusspilz);
538 ("rocket", Brand_mod_rocket);
539 ("warezfaw", Brand_mod_warezfaw);
540 ("emusicmule", Brand_mod_emusicmule);
541 ("aideadsl", Brand_mod_aideadsl);
542 ("a i d e a d s l", Brand_mod_aideadsl);
543 ("epo", Brand_mod_epo);
544 ("kalitsch", Brand_mod_kalitsch);
545 ("raynz", Brand_mod_raynz);
546 ("serverclient", Brand_mod_serverclient);
547 ("bl4ckbird", Brand_mod_bl4ckbird);
548 ("bl4ckf0x", Brand_mod_bl4ckf0x);
549 ("candy-mule", Brand_mod_candymule);
550 ("rt", Brand_mod_rt);
551 ("ice", Brand_mod_ice);
552 ("air-ionix", Brand_mod_airionix);
553 ("ionix", Brand_mod_ionix);
554 ("tornado", Brand_mod_tornado);
555 ("anti-faker", Brand_mod_antifaker);
556 ("netf", Brand_mod_netf);
557 ("nextemf", Brand_mod_nextemf);
558 ("proemule", Brand_mod_proemule);
559 ("szemule", Brand_mod_szemule);
560 ("darkmule", Brand_mod_darkmule);
561 ("miragemod", Brand_mod_miragemod);
562 ("nextevolution", Brand_mod_nextevolution);
563 ("pootzgrila", Brand_mod_pootzgrila);
564 ("freeangel", Brand_mod_freeangel);
565 ("enos", Brand_mod_enos);
566 ("webys", Brand_mod_webys)
569 let to_lowercase s = String.lowercase s
571 let string_of_tags_list tags =
572 let s = ref "" in
573 List.iter (fun tag ->
574 let st = to_lowercase (string_of_tag_value tag.tag_value) in
575 let str = (escaped_string_of_field tag) ^ " : " ^ st ^ " ; " in
576 s := !s ^ str
577 ) tags;
580 let identify_client_brand_mod c tags =
581 if c.client_brand_mod = Brand_mod_unknown then begin
582 List.iter (fun tag ->
583 let s = to_lowercase (string_of_tag_value tag.tag_value) in
584 match tag.tag_name with
585 Field_KNOWN "mod_version" ->
586 begin
587 let rec iter i len =
588 if i < len then
589 let sub = fst mod_array.(i) in
590 if (String2.subcontains s sub) then
591 c.client_brand_mod <- snd mod_array.(i)
592 else iter (i+1) len
594 iter 0 (Array.length mod_array)
596 | _ -> ()
598 ) tags;
599 if String2.subcontains c.client_name "@PowerMule" then begin
600 c.client_brand_mod <- Brand_mod_powermule
604 let update_emule_release c =
605 let client_version = c.client_emule_proto.emule_version land 0x00ffffff in
606 let brand = c.client_brand in
608 let maj = (client_version lsr 17) land 0x7f in
609 let min = (client_version lsr 10) land 0x7f in
610 let up = (client_version lsr 7) land 0x07 in
612 c.client_emule_proto.emule_release <- (
613 if maj = 0 && min = 0 && up = 0 then
615 else if brand = Brand_newemule || brand = Brand_emuleplus then
616 Printf.sprintf "%d.%d%c" maj min (Char.chr ((int_of_char 'a') + up))
617 else
618 Printf.sprintf "%d.%d.%d" maj min up
621 let parse_compatible_client num old_brand =
622 match num with
623 0 -> old_brand
624 | 1 -> Brand_cdonkey
625 | 2 -> Brand_lmule
626 | 3 -> Brand_amule
628 | 40 -> Brand_shareaza
629 | 5 -> Brand_emuleplus
630 | 6 -> Brand_hydranode
631 | 10 -> Brand_mldonkey3
632 | 20 -> Brand_lphant
633 | 60 -> Brand_imp
634 | 240 -> Brand_verycd
635 | _ -> Brand_unknown
637 let parse_mod_version s c =
638 let rec iter i len =
639 if i < len then
640 let sub = fst mod_array.(i) in
641 if (String2.subcontains s sub) then
642 c.client_brand_mod <- snd mod_array.(i)
643 else iter (i+1) len
645 iter 0 (Array.length mod_array)
647 let update_client_from_tags c tags =
648 let module M = DonkeyProtoClient in
649 List.iter (fun tag ->
650 match tag.tag_name with
651 | Field_KNOWN "emule_udpports" ->
652 for_two_int16_tag tag (fun ed2k_port kad_port ->
653 (* Kademlia: we should use this client to bootstrap Kademlia *)
654 if kad_port <> 0 && !!enable_kademlia then
655 DonkeyProtoKademlia.Kademlia.bootstrap
656 c.client_ip kad_port
658 | Field_KNOWN "emule_miscoptions1" ->
659 c.client_emule_proto.received_miscoptions1 <- true;
660 for_int64_tag tag (fun i ->
661 M.update_emule_proto_from_miscoptions1
662 c.client_emule_proto i;
663 if !verbose_msg_clients || c.client_debug then
664 lprintf_nl "miscoptions1 from client %s\n%s"
665 (full_client_identifier c)
666 (M.print_emule_proto_miscoptions1 c.client_emule_proto)
668 | Field_KNOWN "emule_miscoptions2" ->
669 c.client_emule_proto.received_miscoptions2 <- true;
670 for_int64_tag tag (fun i ->
671 M.update_emule_proto_from_miscoptions2
672 c.client_emule_proto i;
673 if !verbose_msg_clients || c.client_debug then
674 lprintf_nl "miscoptions2 from client %s\n%s"
675 (full_client_identifier c)
676 (M.print_emule_proto_miscoptions2 c.client_emule_proto)
678 | Field_KNOWN "emule_compatoptions" ->
679 for_int_tag tag (fun i ->
680 M.update_emule_proto_from_compatoptions
681 c.client_emule_proto i
683 | Field_KNOWN "emule_version" ->
684 for_int_tag tag (fun i ->
685 c.client_emule_proto.emule_version <- i;
686 let compatibleclient = (i lsr 24) in
687 c.client_brand <- parse_compatible_client compatibleclient c.client_brand;
688 update_emule_release c;
690 if c.client_brand = Brand_unknown then
691 lprintf_nl "[emule_version] Brand_unknown %s" (full_client_identifier c);
693 | Field_KNOWN "mod_version" ->
694 let s = to_lowercase (string_of_tag_value tag.tag_value) in
695 parse_mod_version s c
696 | Field_KNOWN _ -> if !verbose_unknown_messages then
697 lprintf_nl "update_client_from_tags, known tag: [%s] (%s)" (string_of_tag tag) (full_client_identifier c)
698 | _ -> if not (DonkeySources.source_brand c.client_source) && !verbose_unknown_messages then
699 lprintf_nl "update_client_from_tags, unknown tag: [%s] (%s) %s"
700 (hexstring_of_tag tag) (full_client_identifier c) (string_of_tags_list tags)
701 ) tags
703 let update_emule_proto_from_tags c tags =
704 List.iter (fun tag ->
705 match tag.tag_name with
706 Field_KNOWN "compatibleclient" ->
707 for_int_tag tag (fun i ->
708 c.client_brand <- parse_compatible_client i c.client_brand;
709 if c.client_brand = Brand_unknown then
710 lprintf_nl "unknown compatibleclient %d (%s) (please report to dev team)" i (full_client_identifier c)
712 | Field_KNOWN "compression" ->
713 for_int_tag tag (fun i ->
714 c.client_emule_proto.emule_compression <- i
716 | Field_KNOWN "udpver" ->
717 for_int_tag tag (fun i ->
718 c.client_emule_proto.emule_udpver <- i
720 | Field_KNOWN "sourceexchange" ->
721 for_int_tag tag (fun i ->
722 c.client_emule_proto.emule_sourceexchange <- i
724 | Field_KNOWN "comments" ->
725 for_int_tag tag (fun i ->
726 c.client_emule_proto.emule_comments <- i
728 | Field_KNOWN "extendedrequest" ->
729 for_int_tag tag (fun i ->
730 c.client_emule_proto.emule_extendedrequest <- i
732 | Field_KNOWN "features" ->
733 for_int_tag tag (fun i ->
734 c.client_emule_proto.emule_secident <- i land 0x3
736 | Field_KNOWN "mod_version" ->
737 parse_mod_version (to_lowercase (string_of_tag_value tag.tag_value)) c;
739 | Field_KNOWN "os_info" ->
740 let s = to_lowercase (string_of_tag_value tag.tag_value) in
741 (match c.client_osinfo with
742 Some _ -> ()
743 | _ -> if s <> "" then c.client_osinfo <- Some s)
744 | Field_KNOWN _ -> if !verbose_unknown_messages then
745 lprintf_nl "update_emule_proto_from_tags, known tag: [%s] (%s)" (string_of_tag tag) (full_client_identifier c)
746 | _ -> if not (DonkeySources.source_brand c.client_source) && !verbose_unknown_messages then
747 lprintf_nl "update_emule_proto_from_tags, unknown tag: [%s] (%s) %s"
748 (hexstring_of_tag tag) (full_client_identifier c) (string_of_tags_list tags)
749 ) tags
751 let fight_disguised_mods c =
752 if (c.client_brand = Brand_mldonkey2 || c.client_brand = Brand_mldonkey3)
753 && (c.client_brand_mod = Brand_mod_morphxt || c.client_brand_mod = Brand_mod_ionix) then
754 c.client_brand <- Brand_newemule;
755 if c.client_emule_proto.emule_release <> "" && c.client_brand = Brand_mldonkey2 then
756 c.client_brand <- Brand_newemule;
757 if c.client_brand = Brand_edonkey && c.client_brand_mod = Brand_mod_plus then
758 c.client_brand <- Brand_emuleplus;
759 if c.client_brand = Brand_emuleplus && c.client_brand_mod = Brand_mod_plus then
760 c.client_brand_mod <- Brand_mod_unknown
762 let request_osinfo c =
763 if c.client_emule_proto.emule_osinfosupport = 1 && not c.client_osinfo_sent then
764 begin
765 let emule_osinfo = {
766 emule_info with
767 DonkeyProtoClient.EmuleClientInfo.protversion = 255;
768 DonkeyProtoClient.EmuleClientInfo.tags = [
769 string_tag (Field_KNOWN "os_info") (String2.upp_initial Autoconf.system);
770 ]} in
771 client_send c (DonkeyProtoClient.EmuleClientInfoReq emule_osinfo);
772 c.client_osinfo_sent <- true
775 let rec query_id ip port id =
776 let client_ip = client_ip None in
778 (* TODO: check if we are connected to this server. If yes, issue a
779 query_id instead of a UDP packet *)
780 if Ip.reachable client_ip then
781 let module Q = DonkeyProtoUdp.QueryCallUdp in
782 (* lprintf "Ask connection from indirect client\n"; *)
785 let s = DonkeyGlobals.find_server ip port in
786 match s.server_sock with
787 NoConnection | ConnectionWaiting _ -> ()
789 (* OK, this fixes the problem with Lugdunum servers, but there should be
790 another better way, since this functionnality is still useful...
792 DonkeyProtoCom.udp_send (get_udp_sock ())
793 ip (port+4)
794 (DonkeyProtoUdp.QueryCallUdpReq {
795 Q.ip = client_ip;
796 Q.port = !!donkey_port;
797 Q.id = id;
798 }) *)
799 | Connection sock ->
800 server_send sock (
801 let module M = DonkeyProtoServer in
802 let module C = M.QueryID in
803 M.QueryIDReq id
806 with _ ->
807 if !!update_server_list_client then
808 begin
809 ignore(check_add_server ip port);
810 query_id ip port id
813 let shared_of_file file =
814 match file.file_shared with
815 | None -> None
816 | Some sh -> Some (as_shared sh)
818 let query_view_files c =
819 if CommonClient.is_must_browse (as_client c) then begin
820 CommonClient.set_not_must_browse (as_client c);
821 if c.client_emule_proto.emule_noviewshared <> 1 then client_send c (
822 let module M = DonkeyProtoClient in
823 let module C = M.ViewFiles in
824 M.ViewFilesReq C.t);
827 (* client is valid if it's not us or if it's not yet connected *)
828 let is_valid_client md4 =
829 md4 <> !!client_md4 &&
830 md4 <> overnet_md4 &&
831 not (Hashtbl.mem connected_clients md4)
833 (*Do what's need to be done when client has a file we want:
834 - register it in sources
835 - do *not* ask for sources, we can't be sure, the client is still downloading the file!
837 let client_has_file c file =
838 DonkeySources.set_request_result c.client_source file.file_sources File_found
841 Do what's need to be done when client asked for a file we want:
842 - register it in sources
843 - ask for sources if necessary
844 - do not ask sources from mldonkey-clients, they are supposed to automatically send sources after an QueryFileReq
846 let client_queried_file c file =
847 client_has_file c file;
848 let module M = DonkeyProtoClient in
849 if file_state file = FileDownloading
850 && M.sourceexchange c.client_emule_proto > 0
851 && DonkeySources.need_new_sources file.file_sources
852 && not (client_can_receive c)
853 then
854 (* ask for more sources *)
855 begin
856 if !verbose_location then
857 lprintf_nl "donkeyClient: Requesting sources from client %s that queried file %s"
858 (full_client_identifier c) (file_best_name file);
859 let module E = M.EmuleRequestSources in
860 client_send c (M.EmuleRequestSourcesReq file.file_md4)
863 (*Do what's need to be done when client has file chunks we want:
864 - register it in sources
865 - ask for sources if necessary Edit: errr, where is this done?
867 let client_is_useful c file chunks =
868 DonkeySources.set_request_result c.client_source file.file_sources File_chunk;
869 DonkeyOneFile.add_client_chunks c file chunks;
870 if file_state file = FileDownloading then
871 DonkeyOneFile.request_slot c
873 (* added in 2.5.25
874 Check if the bitmap returned by a client contains a chunk that has not
875 yet been downloaded.
877 let is_useful_client file chunks =
878 match file.file_swarmer with
879 None -> false
880 | Some swarmer ->
881 let bitmap = CommonSwarming.chunks_verified_bitmap swarmer in
882 VB.existsi (fun i s ->
883 Bitv.get chunks i &&
884 (match s with
885 | VB.State_missing | VB.State_partial -> true
886 | VB.State_complete | VB.State_verified -> false)
887 ) bitmap
889 let received_client_bitmap c file chunks =
891 let module M = DonkeyProtoClient in
893 if !verbose_msg_clients then begin
894 match file.file_swarmer with
895 None -> ()
896 | Some swarmer ->
897 lprintf_nl "Compared to: %s" (VB.to_string (CommonSwarming.chunks_verified_bitmap swarmer));
898 end;
900 let chunks =
901 if file_size file <= block_size
902 then Bitv.create 1 true
903 else
904 if Bitv.length chunks = 0
905 then Bitv.create file.file_nchunks true
906 else
907 if Bitv.length chunks <> file.file_nchunks then begin
908 if !verbose then
909 lprintf_nl "number of chunks is different %d/%d for %s(%s), size %Ld on %s"
910 (Bitv.length chunks)
911 (file.file_nchunks)
912 (file_best_name file)
913 (Md4.to_string file.file_md4)
914 (file_size file)
915 (full_client_identifier c);
916 Bitv.create file.file_nchunks false
917 (* What should we do ?
919 1) Try to recover the correct size of the file: we can use
920 ViewFilesReq on all clients having the file to test what is
921 the most widely used size for this file. Maybe create
922 different instances of the file for each proposed size ?
926 end else chunks
929 if is_useful_client file chunks then client_is_useful c file chunks
931 let send_pending_messages c sock =
932 let module M = DonkeyProtoClient in
934 List.iter (fun m ->
935 client_send c (M.SayReq m)
936 ) c.client_pending_messages;
937 c.client_pending_messages <- []
939 let init_client_after_first_message sock c =
940 (* we read something on socket so ip is now known for socket *)
941 let old_ip = c.client_ip in
942 c.client_ip <- peer_ip sock;
943 if old_ip <> Ip.null && old_ip <> c.client_ip &&
944 c.client_country_code = None then
945 check_client_country_code c;
946 (* Add the Connected tag and when needed the NoLimit tag *)
947 let t = client_type c lor client_initialized_tag in
948 let t = try
949 if Ip.matches c.client_ip !!nolimit_ips then t lor client_nolimit_tag
950 else t
951 with _ -> t in
952 set_client_type c t;
956 let finish_client_handshake c sock =
957 c.client_connect_time <- last_time ();
958 send_pending_messages c sock;
959 set_client_state c (Connected (-1));
960 (* query_files c sock; see comment at implementation*)
961 DonkeySources.source_connected c.client_source;
962 query_view_files c;
963 client_must_update c;
964 c.client_checked <- true;
965 is_banned c sock
968 (* reverse ip bytes? *)
969 let int64_of_rip ip =
970 Ip.to_int64 (Ip.rev ip)
972 let get_high_id_int64 () =
973 let result = ref Int64.zero in
974 List.iter (fun s ->
975 if !result = Int64.zero then
976 (match s.server_cid with
977 None -> ()
978 | Some i -> if not (low_id i) then
979 result := int64_of_rip i;
981 ) (connected_servers());
982 !result
984 (* If we know our own IP (donkey high id), use type 20 and our ip
985 If we do not know our IP (could be NAT'd), use type 10 and their ip *)
986 let get_ip_and_type sock =
987 let ip = ref (get_high_id_int64 ()) in
988 let ip_type = ref (if !ip == Int64.zero then 0 else 20) in
990 if (!ip_type == 0) then begin
991 match sock with
992 Connection s ->
993 ip_type := 10;
994 ip := int64_of_rip (peer_ip s);
995 | _ -> ()
996 end;
997 (!ip,!ip_type)
999 let has_pubkey c =
1000 match c.client_public_key with
1001 None -> false
1002 | _ -> true
1004 let get_pubkey c =
1005 match c.client_public_key with
1006 None -> ""
1007 | Some s -> s
1009 let send_signature c =
1010 if has_pubkey c then
1011 begin
1013 let ip = ref Int64.zero in
1014 let ip_type = ref 0 in
1015 (* check low id? *)
1016 if (c.client_emule_proto.emule_secident == 2) then begin (* Use v1 as default, except if only v2 is supported (same as emule) *)
1017 let (x,y) = get_ip_and_type c.client_source.DonkeySources.source_sock in
1018 ip := x;
1019 ip_type := y;
1020 end;
1022 let pubkey = get_pubkey c in
1023 let signature = DonkeySui.SUI.create_signature pubkey (String.length pubkey) c.client_req_challenge !ip_type !ip in
1025 if !verbose_msg_clients then begin
1026 lprintf_nl "%s [send_signature] [sigLen: %d] [keyLen: %d] [reqChall: %Ld] [ipType: %d] [ip: %Ld]" (full_client_identifier c) (String.length signature) (String.length pubkey) c.client_req_challenge !ip_type !ip;
1027 end;
1029 let module M = DonkeyProtoClient in
1030 let module E = M.EmuleSignatureReq in
1031 client_send c (M.EmuleSignatureReq {
1032 E.signature = signature;
1033 E.ip_type = !ip_type;
1036 else
1037 if !verbose_msg_clients then begin
1038 lprintf_nl "%s [send_signature] Can't send without a key" (full_client_identifier c)
1041 let verify_ident c =
1042 let challenge = Random.int64 (Int64.of_int32 Int32.max_int) in
1043 let state, state_string = if has_pubkey c then (1,"SIGNEEDED") else (2,"KEYANDSIGNEEDED") in
1044 c.client_sent_challenge <- challenge;
1046 if !verbose_msg_clients then begin
1047 lprintf_nl "%s [verify_ident] [state: %d (%s)] [sentChall: %Ld]" (full_client_identifier c) state state_string challenge;
1048 end;
1050 let module M = DonkeyProtoClient in
1051 let module E = M.EmuleSecIdentStateReq in
1052 client_send c (M.EmuleSecIdentStateReq {
1053 E.state = state;
1054 E.challenge = challenge;
1057 let send_public_key c =
1058 if !verbose_msg_clients then begin
1059 lprintf_nl "%s [send_public_key] [keyLen: %d]" (full_client_identifier c) (String.length !client_public_key);
1060 end;
1062 let module M = DonkeyProtoClient in
1063 client_send c (M.EmulePublicKeyReq !client_public_key)
1065 let get_server_ip_port () =
1066 match !DonkeyGlobals.master_server with
1067 | None ->
1068 Ip.null, 0
1069 | Some s ->
1070 let port =
1071 match s.server_realport with
1072 None -> (*lprintf "%d\n" s.server_port;*) s.server_port
1073 | Some p -> (*lprintf "%d\n" p;*) p
1075 s.server_ip, port
1077 let process_mule_info c t =
1078 update_emule_proto_from_tags c t;
1079 update_emule_release c;
1080 client_must_update c;
1081 if sec_ident_enabled ()
1082 && (c.client_md4 <> Md4.null)
1083 && (c.client_sent_challenge == Int64.zero)
1084 && (c.client_emule_proto.emule_secident > 0)
1085 then begin
1086 if !verbose_msg_clients then
1087 lprintf_nl "%s [process_mule_info] [verify_ident]" (full_client_identifier c);
1088 verify_ident c
1092 let incr_activity_successful_connections c =
1093 if DonkeySources.source_brand c.client_source then
1094 !activity.activity_client_overnet_successful_connections <-
1095 !activity.activity_client_overnet_successful_connections +1
1096 else
1097 !activity.activity_client_edonkey_successful_connections <-
1098 !activity.activity_client_edonkey_successful_connections +1
1100 let incr_activity_indirect_connections c =
1101 if DonkeySources.source_brand c.client_source then
1102 !activity.activity_client_overnet_indirect_connections <-
1103 !activity.activity_client_overnet_indirect_connections +1
1104 else
1105 !activity.activity_client_edonkey_indirect_connections <-
1106 !activity.activity_client_edonkey_indirect_connections +1
1108 let incr_activity_connections c =
1109 if DonkeySources.source_brand c.client_source then
1110 !activity.activity_client_overnet_connections <-
1111 !activity.activity_client_overnet_connections +1
1112 else
1113 !activity.activity_client_edonkey_connections <-
1114 !activity.activity_client_edonkey_connections +1
1116 let check_stolen_hash c sock md4 =
1117 if not (register_client_hash (peer_ip sock) md4) then
1118 if !!ban_identity_thieves then
1119 ban_client c sock "is probably using stolen client hashes"
1121 let string_of_client_addr c =
1123 match c.client_source.DonkeySources.source_sock with
1124 | Connection sock ->
1125 (Ip.to_string (peer_ip sock) ^ ":" ^ string_of_int (peer_port sock))
1126 | _ ->
1127 raise Not_found
1128 with _ ->
1129 match c.client_kind with
1130 | Direct_address (ip,port) -> ((Ip.to_string ip) ^ ":" ^ string_of_int port)
1131 | Indirect_address _ | Invalid_address _ -> "Indirect"
1133 let client_to_client for_files c t sock =
1134 let module M = DonkeyProtoClient in
1136 if !verbose_msg_clients || c.client_debug then begin
1137 lprintf_nl "Message from %s" (full_client_identifier c);
1138 M.print t;
1139 end;
1141 match t with
1142 M.ConnectReplyReq t ->
1143 if !verbose_msg_clients then begin
1144 lprintf_nl "[HELLOANSWER] %s" (full_client_identifier c);
1145 end;
1147 incr_activity_successful_connections c;
1149 init_client_after_first_message sock c;
1151 set_client_has_a_slot (as_client c) NoSlot;
1153 let module CR = M.Connect in
1155 if not (is_valid_client t.CR.md4) then
1156 begin
1157 TcpBufferedSocket.close sock (Closed_for_error "Reply of Invalid Client");
1158 raise Exit
1159 end;
1161 if (is_black_address t.CR.ip t.CR.port c.client_country_code) then raise Exit;
1163 check_stolen_hash c sock t.CR.md4;
1165 c.client_tags <- t.CR.tags;
1167 List.iter (fun tag ->
1168 match tag with
1169 { tag_name = Field_KNOWN "name"; tag_value = String s } ->
1170 set_client_name c s t.CR.md4
1171 | _ -> ()
1172 ) c.client_tags;
1174 identify_client_brand c;
1175 update_client_from_tags c t.CR.tags;
1176 fight_disguised_mods c;
1177 update_emule_release c;
1178 Hashtbl.add connected_clients t.CR.md4 c;
1180 (* connection_ok c.client_connection_control; *)
1182 if c.client_debug || !verbose_msg_clients || !verbose_msg_clienttags then begin
1183 M.Connect.print t;
1184 end;
1186 begin
1187 match t.CR.server_info with
1188 Some (ip, port) -> if !!update_server_list_client then safe_add_server ip port
1189 | _ -> ()
1190 end;
1192 check_stolen_hash c sock t.CR.md4;
1194 finish_client_handshake c sock;
1195 (* We initiated the connection so we know which files to ask *)
1196 DonkeySources.query_files c.client_source
1198 | M.EmuleQueueRankingReq rank
1199 | M.QueueRankReq rank ->
1200 c.client_rank <- rank;
1201 set_client_state c (Connected rank);
1202 if rank > !!good_client_rank then
1203 List.iter (fun (file, _, _) ->
1204 let s = c.client_source in
1205 let m = file.file_sources in
1206 match DonkeySources.find_request_result s m with
1207 File_chunk ->
1208 DonkeySources.set_request_result s m File_found;
1209 | _ -> ()
1210 ) c.client_file_queue
1212 | M.EmuleClientInfoReq t ->
1214 let old_ip = c.client_ip in
1215 c.client_ip <- peer_ip sock;
1216 if old_ip <> Ip.null && old_ip <> c.client_ip &&
1217 c.client_country_code = None then
1218 check_client_country_code c;
1219 (* lprintf "Emule Extended Protocol asked\n"; *)
1220 let module CI = M.EmuleClientInfo in
1221 process_mule_info c t.CI.tags;
1222 if !!emule_mods_count then
1223 identify_client_brand_mod c t.CI.tags;
1225 let module E = M.EmuleClientInfo in
1226 client_send c (M.EmuleClientInfoReplyReq emule_info);
1227 request_osinfo c;
1230 | M.EmuleClientInfoReplyReq t ->
1232 let module CI = M.EmuleClientInfo in
1234 process_mule_info c t.CI.tags;
1236 if !verbose_msg_clienttags then
1237 lprintf_nl "Message from client[%d] %s %s tags: %s"
1238 (client_num c)
1239 (match c.client_kind with
1240 Indirect_address _ | Invalid_address _ -> ""
1241 | Direct_address (ip,port) ->
1242 Printf.sprintf " [%s:%d]" (Ip.to_string ip) port;
1244 (full_client_identifier c)
1245 (string_of_tags_list t.CI.tags)
1247 (* lprintf "Emule Extended Protocol activated\n"; *)
1250 | M.EmuleRequestSourcesReq t ->
1251 let module E = M.EmuleRequestSourcesReply in
1253 (* lprintf "Emule requested sources\n"; *)
1254 let file = find_file t in
1255 let sources = ref [] in
1256 DonkeySources.iter_qualified_sources (fun s ->
1257 match s.DonkeySources.source_uid with
1258 Indirect_address _ | Invalid_address _ -> () (* not yet supported *)
1259 | Direct_address (ip, port) ->
1260 if s.DonkeySources.source_age > last_time () - 600 &&
1261 (match ip_reliability ip with
1262 Reliability_reliable | Reliability_neutral -> true
1263 | Reliability_suspicious _ -> false) &&
1264 List.exists (fun r ->
1265 r.DonkeySources.request_score >= CommonSources.possible_score
1266 ) s.DonkeySources.source_files then
1267 sources := {
1268 E.src_ip = ip;
1269 E.src_port = port;
1270 E.src_cc = None;
1271 E.src_server_ip = Ip.null;
1272 E.src_server_port = 0;
1273 (* this is not very good, but what can we do ? we don't keep sources UIDs *)
1274 E.src_md4 = Md4.null;
1275 } :: !sources
1276 ) file.file_sources;
1277 if !sources <> [] then
1278 begin
1279 if !verbose_location then
1280 lprintf_nl "donkeyClient: EmuleRequestSourcesReq: Sending %d Sources to %s for file %s"
1281 (List.length !sources) (full_client_identifier c) (file_best_name file);
1282 client_send c (
1283 M.EmuleRequestSourcesReplyReq {
1284 E.md4 = t;
1285 E.sources = Array.of_list !sources;
1289 | M.ViewFilesReplyReq t ->
1291 lprintf "****************************************\n";
1292 lprintf " VIEW FILES REPLY \n";
1294 let module Q = M.ViewFilesReply in
1295 begin
1296 if !verbose_msg_clients then
1297 lprintf_nl "Received ViewFilesReply";
1299 let list = ref [] in
1300 List.iter (fun f ->
1301 match result_of_file f.f_md4 f.f_tags with
1302 None -> ()
1303 | Some r ->
1304 (* TODO let r = DonkeyIndexer.index_result_no_filter r in *)
1305 client_new_file c r;
1306 list := r :: !list
1307 ) t;
1308 c.client_all_files <- Some !list;
1309 client_must_update c
1311 with exn ->
1312 lprintf_nl ~exn "ViewFilesReply"
1313 end;
1315 | M.AvailableSlotReq _ ->
1316 set_lifetime sock active_lifetime;
1317 set_rtimeout sock !!queued_timeout;
1318 (* how long should we wait for a block ? *)
1319 (* begin
1320 match c.client_block with
1321 None -> ()
1322 | Some b ->
1323 lprintf "[QUEUED WITH BLOCK]\n";
1324 DonkeyOneFile.clean_client_zones c;
1325 end; *)
1326 begin
1327 match c.client_download with
1328 | Some (file,up) ->
1329 if !verbose_download then
1330 lprintf_nl "Clear download";
1331 CommonSwarming.clear_uploader_ranges up;
1332 c.client_download <- None
1333 | None ->
1334 match c.client_file_queue with
1335 _ :: _ -> ()
1336 | [] ->
1337 if c.client_slot = SlotNotAsked then
1339 let files, _ = try
1340 let v = Hashtbl.find join_queue_by_md4 c.client_md4 in
1341 if c.client_debug then
1342 lprintf_nl "Recovered file queue by md4";
1344 with _ ->
1345 let id = client_id c in
1346 let v = Hashtbl.find join_queue_by_id id in
1347 if c.client_debug then
1348 lprintf_nl "Recovered file queue by md4";
1351 List.iter (fun (file, chunks) ->
1352 let chunks = Bitv.copy chunks in
1353 DonkeyOneFile.add_client_chunks c file chunks) files;
1354 (* DonkeyOneFile.restart_download c *)
1355 with _ ->
1356 if c.client_debug then
1357 lprintf_nl "AvailableSlot received, but not file to download!";
1358 (* TODO: ask for the files now *)
1359 end;
1360 (* now, we can forget we have asked for a slot *)
1361 c.client_slot <- SlotReceived;
1362 DonkeyOneFile.get_from_client c
1364 | M.JoinQueueReq _ when not (!!ban_queue_jumpers && c.client_banned) ->
1367 if !!ban_queue_jumpers && c.client_banned then
1368 direct_client_send sock (M.EmuleQueueRankingReq
1369 (900 + Random.int 100))
1370 else *)
1371 begin try
1373 begin
1374 match c.client_brand with
1375 | Brand_mldonkey3 ->
1376 if Fifo.length upload_clients >= !!max_upload_slots then
1377 Fifo.iter (fun c ->
1378 if c.client_source.source_sock <> None &&
1379 c.client_brand = Brand_mldonkey3 then raise Exit)
1380 upload_clients
1381 | _ ->
1382 if Fifo.length upload_clients >= !!max_upload_slots then
1383 raise Exit;
1384 end;
1386 (* set_rtimeout sock !!upload_timeout; *)
1387 set_lifetime sock (float_of_int Date.day_in_secs);
1388 add_pending_slot c
1390 with _ -> *)
1392 (* If the client is in the nolimit_ips list, he doesn't need a slot, so put
1393 it immediatly in the upload queue... but what will happen in the queue
1394 since the client upload should not be taken into account !
1396 What we need: put the upload and download engines inside the bandwidth
1397 controler, and use two bandwidth controlers, one for limited sockets, the
1398 other one for unlimited sockets. *)
1400 (* NOT IMPLEMENTED YET
1401 if is_nolimit cc then begin
1402 set_client_has_a_slot cc true;
1403 client_enter_upload_queue cc
1404 end else *)
1405 CommonUploads.add_pending_slot (as_client c);
1406 if !verbose_upload then
1407 lprintf_nl "added to pending slots: %s %s"
1408 (full_client_identifier c)
1409 (match client_upload (as_client c) with
1410 None -> ""
1411 | Some f -> CommonFile.file_best_name f);
1412 (* end *)
1414 | M.OutOfPartsReq _ ->
1415 set_client_state c (Connected 0);
1416 begin
1417 match c.client_download with
1418 None -> ()
1419 | Some (file,up) ->
1420 if !verbose_download then
1421 lprintf_nl "Slot closed during download";
1422 CommonSwarming.clear_uploader_ranges up
1423 end;
1424 c.client_session_downloaded <- 0L;
1425 c.client_slot <- SlotNotAsked;
1426 (* OK, the slot is closed, but what should we do now ????? *)
1427 begin
1428 match c.client_file_queue with
1429 [] -> ()
1430 | _ ->
1431 if !verbose_download then
1432 lprintf_nl "OutOfPartsReq";
1433 DonkeyOneFile.request_slot c;
1434 set_rtimeout sock !!queued_timeout;
1437 | M.ReleaseSlotReq _ ->
1438 DonkeyOneFile.remove_client_slot c;
1439 if c.client_file_queue = [] then set_rtimeout sock 120.;
1440 CommonUploads.refill_upload_slots ()
1442 | M.QueryFileReplyReq t ->
1443 let module Q = M.QueryFileReply in
1445 begin
1447 let file = find_file t.Q.md4 in
1448 c.client_rating <- c.client_rating + 1;
1450 client_has_file c file;
1451 add_file_filenames (as_file file) t.Q.name;
1453 update_best_name file;
1454 if file_size file <= block_size then begin
1455 client_is_useful c file (Bitv.create 1 true)
1456 end else begin
1458 if file.file_computed_md4s = [||] then begin
1459 client_send c (
1460 let module M = DonkeyProtoClient in
1461 let module C = M.QueryChunkMd4 in
1462 M.QueryChunkMd4Req file.file_md4);
1465 with _ -> ()
1466 end
1468 | M.EmuleFileDescReq t ->
1469 begin
1470 match c.client_last_file_req_md4 with
1471 Some md4 ->
1472 begin
1473 try
1474 let file = find_file md4 in
1475 let module Q = M.EmuleFileDesc in
1476 let slen = String.length t.Q.comment in
1477 if slen > 0 && slen <= !!max_comment_length && (!is_not_comment_spam) t.Q.comment then begin
1478 (* Disallow dups from single IP, but allow comment updates *)
1479 file.file_comments <- List.filter (fun (i,_,_,_) -> i <> c.client_ip) file.file_comments;
1480 if List.length file.file_comments < !!max_comments_per_file then begin
1481 file.file_comments <- (c.client_ip, c.client_name, t.Q.rating, (intern t.Q.comment)) :: file.file_comments;
1482 file_must_update file;
1483 end;
1485 with _ -> ()
1487 | None -> ()
1490 | M.QueryChunksReplyReq t ->
1491 let module Q = M.QueryChunksReply in
1492 begin
1494 let file = find_file t.Q.md4 in
1495 received_client_bitmap c file t.Q.chunks
1496 with exn ->
1497 client_send c (M.NoSuchFileReq t.Q.md4);
1498 if !verbose then lprintf_nl ~exn
1499 "QueryChunksReply: Client (%s) asked for file_md4 %s"
1500 (full_client_identifier c)
1501 (Md4.to_string t.Q.md4)
1504 | M.QueryChunkMd4ReplyReq t ->
1505 begin
1506 let module Q = M.QueryChunkMd4Reply in
1507 let file = find_file t.Q.md4 in
1509 let module Q = M.QueryChunkMd4Reply in
1510 if !verbose then
1511 lprintf_nl "Received chunks md4 for %s from %s"
1512 (file_best_name file) (full_client_identifier c);
1514 if file.file_computed_md4s = [||] then begin
1515 if file.file_nchunk_hashes = 0 then begin
1516 lprintf_nl "[ERROR] file %s has only one chunk, ignoring QueryChunkMd4ReplyReq"
1517 (file_best_name file);
1518 file.file_computed_md4s <- [|file.file_md4|];
1519 match file.file_swarmer with
1520 None -> ()
1521 | Some swarmer ->
1522 CommonSwarming.set_verifier swarmer
1523 (Verification [| Ed2k file.file_md4 |])
1524 end else
1525 if t.Q.chunks = [||] then
1526 lprintf_nl "[ERROR] received empty chunks md4 message for %s from %s"
1527 (file_best_name file) (full_client_identifier c)
1528 else
1529 if Array.length t.Q.chunks <> file.file_nchunk_hashes then begin
1530 if !verbose then
1531 lprintf_nl "[ERROR] number of chunks does not match, received md4s %d/should be %d, for %s(%s):%Ld bytes from %s"
1532 (Array.length t.Q.chunks)
1533 (file.file_nchunks)
1534 (file_best_name file)
1535 (Md4.to_string file.file_md4)
1536 (file_size file)
1537 (full_client_identifier c)
1538 (* What should we do ?
1540 1) Try to recover the correct size of the file: we can use
1541 ViewFilesReq on all clients having the file to test what is
1542 the most widely used size for this file. Maybe create
1543 different instances of the file for each proposed size ?
1545 Maybe we should allow a degraded mode of download, where each client
1546 is checked for the file.
1550 end else begin
1551 (* We should check the correctness of the Md4 array *)
1552 let md4s = t.Q.chunks in
1553 let md4 = md4_of_array md4s in
1554 if md4 <> file.file_md4 then begin
1555 lprintf_nl "[ERROR] Chunks md4s do not match file_md4 for %s(%s) from %s"
1556 (file_best_name file)
1557 (Md4.to_string file.file_md4)
1558 (full_client_identifier c);
1559 end else begin
1560 file.file_computed_md4s <- md4s;
1561 match file.file_swarmer with
1562 None -> ()
1563 | Some swarmer ->
1564 CommonSwarming.set_verifier swarmer
1565 (Verification (Array.map (fun m -> Ed2k m) md4s))
1570 (* if file.file_exists then verify_chunks file *)
1573 | M.EmuleCompressedPart t ->
1575 set_lifetime sock active_lifetime;
1576 if !!reliable_sources &&
1577 client_reliability c = Reliability_suspicious 0 then begin
1578 lprintf_nl "Receiving data from unreliable client, disconnect";
1579 corruption_warning c;
1580 disconnect_client c (Closed_for_error "Unreliable Source");
1581 raise Not_found
1582 end;
1584 let module Q = M.EmuleCompressedPart in
1585 let comp = match c.client_comp with
1586 None ->
1587 let comp = {
1588 comp_md4 = t.Q.md4;
1589 comp_pos = t.Q.statpos;
1590 comp_total = Int64.to_int t.Q.newsize;
1591 comp_len = 0;
1592 comp_blocs = [];
1593 } in
1594 c.client_comp <- Some comp;
1595 comp
1596 | Some comp -> comp
1598 comp.comp_blocs <- t.Q.bloc :: comp.comp_blocs;
1599 comp.comp_len <- comp.comp_len + String.length t.Q.bloc;
1601 (* lprintf "Comp bloc: %d/%d\n" comp.comp_len comp.comp_total; *)
1602 if comp.comp_len = comp.comp_total then begin
1603 if !verbose_download then
1604 lprintf_nl "Complete compressed block received!";
1606 let s = String.create comp.comp_len in
1607 let rec iter list =
1608 match list with
1609 [] -> 0
1610 | b :: tail ->
1611 let pos = iter tail in
1612 let len = String.length b in
1613 String.blit b 0 s pos len;
1614 pos + len
1616 let pos = iter comp.comp_blocs in
1617 assert (pos = comp.comp_len);
1618 let s = Zlib2.uncompress_string2 s in
1619 if !verbose_download then
1620 lprintf_nl "Decompressed: %d/%d" (String.length s) comp.comp_len;
1622 DonkeyOneFile.block_received c comp.comp_md4
1623 comp.comp_pos s 0 (String.length s);
1625 c.client_comp <- None;
1626 end else
1627 if comp.comp_len > comp.comp_total then begin
1628 if !verbose_unknown_messages then
1629 lprintf_nl "eMule compressed data, ignoring, more data (%d) than compressed (%d) from %s for %s"
1630 comp.comp_len comp.comp_total (full_client_identifier c) (Md4.to_string comp.comp_md4);
1631 c.client_comp <- None;
1634 | M.BlocReq t ->
1636 set_lifetime sock active_lifetime;
1637 if !!reliable_sources &&
1638 client_reliability c = Reliability_suspicious 0 then begin
1639 lprintf_nl "Receiving data from unreliable client, disconnect";
1640 corruption_warning c;
1641 disconnect_client c (Closed_for_error "Unreliable Source");
1642 raise Not_found
1643 end;
1645 let module M = DonkeyProtoClient in
1646 let module Q = M.Bloc in
1648 let begin_pos = t.Q.start_pos in
1649 let end_pos = t.Q.end_pos in
1650 let len = end_pos -- begin_pos in
1651 if Int64.to_int len <> t.Q.bloc_len then begin
1652 lprintf_nl "%d: inconsistent packet sizes" (client_num c);
1653 raise Not_found
1654 end;
1656 DonkeyOneFile.block_received c t.Q.md4
1657 t.Q.start_pos t.Q.bloc_str t.Q.bloc_begin t.Q.bloc_len
1659 (* Upload requests *)
1660 | M.ViewFilesReq t when !CommonGlobals.has_upload = 0 &&
1661 (match !!allow_browse_share with
1662 1 -> client_friend_tag land client_type c <> 0
1663 | 2 -> true
1664 | _ -> false) ->
1665 let files = DonkeyShare.all_shared () in
1666 let published_files = ref [] in
1667 List.iter (fun f ->
1668 let filename = file_best_name f in
1669 if not (String2.starts_with filename "hidden.") then
1670 published_files := f :: !published_files
1671 ) files;
1673 lprintf "ASK VIEW FILES\n";
1675 if !verbose_msg_clients then
1676 lprintf_nl "Sending %d Files in ViewFilesReqReply" (List.length !published_files);
1677 client_send_files sock !published_files
1679 (*TODO: real directory support*)
1680 | M.ViewDirsReq t when !CommonGlobals.has_upload = 0 &&
1681 (match !!allow_browse_share with
1682 1 -> client_friend_tag land client_type c <> 0
1683 | 2 -> true
1684 | _ -> false) ->
1685 let published_dirs = ["FIXME"] in
1686 if !verbose_msg_clients then
1687 lprintf_nl "Sending %d Dirs in ViewDirsReplyReq" (List.length published_dirs);
1688 client_send c (M.ViewDirsReplyReq published_dirs)
1690 (*TODO: real directory support*)
1691 (*TODO: "!Incomplete Files" support*)
1692 | M.ViewFilesDirReq t when !CommonGlobals.has_upload = 0 &&
1693 (match !!allow_browse_share with
1694 1 -> client_friend_tag land client_type c <> 0
1695 | 2 -> true
1696 | _ -> false) ->
1697 let files = DonkeyShare.all_shared () in
1698 let published_files = ref [] in
1699 List.iter (fun f ->
1700 let filename = file_best_name f in
1701 if not (String2.starts_with filename "hidden.") then
1702 published_files := f :: !published_files
1703 ) files;
1705 lprintf "ASK VIEW FILES\n";
1707 if !verbose_msg_clients then
1708 lprintf_nl "Sending %d Files in ViewFilesReqReply" (List.length !published_files);
1709 client_send_dir sock t !published_files
1711 | M.QueryFileReq t ->
1712 let md4 = t.M.QueryFile.md4 in
1713 c.client_requests_received <- c.client_requests_received + 1;
1715 if !CommonGlobals.has_upload = 0 &&
1716 not (!!ban_queue_jumpers && c.client_banned) then
1718 (try client_wants_file c md4 with _ -> ());
1720 if md4 = Md4.null && c.client_brand = Brand_edonkey then
1721 c.client_brand <- Brand_mldonkey1;
1722 if c.client_brand = Brand_mldonkey1 || c.client_brand = Brand_mldonkey2 then begin
1723 if !verbose then
1724 lprintf_nl "donkeyClient:QueryFileReq: Client %s is really old mldonkey1/2 and queried file %s"
1725 (full_client_identifier c) (Md4.to_string md4);
1726 if Random.int 100 < 3 && !!send_warning_messages then
1727 client_send c (
1728 M.SayReq "[AUTOMATED WARNING] Please, update your MLdonkey client to at least version 2.7.0!");
1729 end;
1731 begin try
1732 count_filerequest c;
1733 let file = find_file md4 in
1734 (match file.file_shared with
1735 None -> raise Not_found
1736 | Some impl ->
1737 shared_must_update_downloaded (as_shared impl);
1738 impl.impl_shared_requests <- impl.impl_shared_requests + 1);
1739 request_for c file sock;
1740 set_client_upload (as_client c) (as_file file);
1741 client_send c (
1742 let module Q = M.QueryFileReply in
1743 let filename = file_best_name file in
1744 let published_filename = if String.length filename < 7 ||
1745 String.sub filename 0 7 <> "hidden." then filename
1746 else String.sub filename 7 (String.length filename - 7) in
1747 M.QueryFileReplyReq {
1748 Q.md4 = file.file_md4;
1749 Q.name = published_filename
1751 client_queried_file c file;
1752 (* Here's the correct place to check for emule_extension *)
1753 begin
1754 match t.M.QueryFile.emule_extension with
1755 None -> ()
1756 | Some (chunks, _) ->
1757 received_client_bitmap c file chunks
1758 end;
1759 if file_state file = FileDownloading then
1760 DonkeySources.query_files c.client_source
1762 with Not_found ->
1763 client_send c (M.NoSuchFileReq md4);
1764 if !verbose_unexpected_messages then
1765 lprintf_nl "donkeyClient: QueryFileReq: Client %s queried unpublished file %s"
1766 (full_client_identifier c) (Md4.to_string md4)
1767 | exn ->
1768 lprintf_nl ~exn "QueryFileReq"
1771 | M.EmuleSignatureReq t ->
1772 if sec_ident_enabled () then
1773 begin
1774 let module Q = M.EmuleSignatureReq in
1776 if !verbose_msg_clients then begin
1777 let lipType,lipTypeString =
1778 (match t.Q.ip_type with
1779 10 -> (10, "IpLocal")
1780 | 20 -> (20, "IpRemote")
1781 | e -> (e, "Unknown")) in
1782 let lkeyString = if (has_pubkey c) then "" else "[NO KEY!!]" in
1783 lprintf_nl "%s [ESigReq] [sentChall: %Ld] [ipType: %d (%s)] %s" (full_client_identifier c) c.client_sent_challenge lipType lipTypeString lkeyString;
1784 end;
1786 let ip_type = ref 0 in
1787 let id = ref Int64.zero in
1789 if (c.client_emule_proto.emule_secident > 1 && t.Q.ip_type <> 0) then
1790 begin
1791 ip_type := t.Q.ip_type;
1792 if (!ip_type == 20) (* || isLowid *) then
1793 id := int64_of_rip (peer_ip sock)
1794 else
1795 begin
1796 id := get_high_id_int64 ();
1797 if !id = Int64.zero then begin
1798 id := int64_of_rip (my_ip sock);
1799 if !verbose_msg_clients then begin
1800 lprintf_nl "%s [ESigReq] Warning: Local IP unknown (signature might fail)" (full_client_identifier c);
1801 end;
1802 end;
1803 end;
1804 end;
1806 let pubKey = get_pubkey c in
1808 if !verbose_msg_clients then begin
1809 lprintf_nl "%s [ESigReq] [verify_signature] [keyLen: %d] [sigLen: %d] [sentChall: %Ld] [ipType %d] [ip: %Ld]" (full_client_identifier c) (String.length pubKey) (String.length t.Q.signature) c.client_sent_challenge !ip_type !id;
1810 end;
1812 let verified = DonkeySui.SUI.verify_signature pubKey (String.length pubKey) t.Q.signature (String.length t.Q.signature) c.client_sent_challenge !ip_type !id in
1813 c.client_sui_verified <- Some verified;
1814 c.client_sent_challenge <- Int64.zero;
1815 client_must_update c;
1817 if !verbose_msg_clients then begin
1818 lprintf_nl "%s [ESigReq] [verify_signature: %s]" (full_client_identifier c) (if verified then "passed" else "failed");
1819 end;
1821 end else
1822 if !verbose_msg_clients then begin
1823 lprintf_nl "%s [ESigReq] [DISABLED]" (full_client_identifier c) ;
1826 | M.EmulePublicKeyReq t ->
1827 if sec_ident_enabled () then
1828 begin
1829 let module Q = M.EmulePublicKeyReq in
1830 (match c.client_public_key with
1831 Some s -> if s <> t then
1832 begin
1833 if !verbose_msg_clients then begin
1834 lprintf_nl "%s [EPubKeyReq] [Key is different!]" (full_client_identifier c);
1835 end;
1836 c.client_public_key <- None;
1837 end
1838 else
1839 if !verbose_msg_clients then begin
1840 lprintf_nl "%s [EPubKeyReq] [Key matches]" (full_client_identifier c);
1841 end;
1842 | _ ->
1843 c.client_public_key <- Some t;
1844 if !verbose_msg_clients then begin
1845 lprintf_nl "%s [EPubKeyReq] [New Key] [keyLen: %d] [reqChall: %Ld]" (full_client_identifier c) (String.length t) c.client_req_challenge;
1846 end;
1848 if (c.client_req_challenge <> Int64.zero) then send_signature c;
1851 else
1852 if !verbose_msg_clients then
1853 lprintf_nl "%s [EPubKeyReq] [DISABLED]" (full_client_identifier c);
1855 | M.EmuleSecIdentStateReq t ->
1856 if sec_ident_enabled () then
1857 begin
1858 let module Q = M.EmuleSecIdentStateReq in
1860 if !verbose_msg_clients then begin
1861 let lstate,lstateString =
1862 (match t.Q.state with
1863 1 -> (1,"SIGNNEEDED")
1864 | 2 -> (2,"KEYANDSIGNNEEDED")
1865 | e -> (e,"UNKNOWN")) in
1866 lprintf_nl "%s [ESecIdentStateReq] [type: %d (%s)] [reqChall: %Ld] [sendChall: %Ld] [hasKey: %b]"
1867 (full_client_identifier c) lstate lstateString t.Q.challenge c.client_sent_challenge (has_pubkey c);
1868 end;
1870 c.client_req_challenge <- t.Q.challenge;
1871 if (not (has_pubkey c)) && (c.client_sent_challenge = Int64.zero)
1872 then verify_ident c;
1873 if (t.Q.state == 2)
1874 then send_public_key c;
1875 if (has_pubkey c)
1876 then send_signature c;
1878 end else
1879 if !verbose_msg_clients then
1880 lprintf_nl "%s [ESecIdentStateReq] [DISABLED]" (full_client_identifier c);
1882 | M.EmuleRequestSourcesReplyReq t ->
1883 (* lprintf "Emule sent sources\n"; *)
1884 let module Q = M.EmuleRequestSourcesReply in
1885 begin
1887 let file = find_file t.Q.md4 in
1888 (* Always accept sources when already received !
1890 if file.file_enough_sources then begin
1891 lprintf "** Dropped %d sources for %s **\n" (List.length t.Q.sources) (file_best_name file);
1893 end else *)
1894 if !verbose_location then
1895 lprintf_nl "donkeyClient: EmuleRequestSourcesReply: Received %d sources from %s for %s"
1896 (Array.length t.Q.sources) (full_client_identifier c) (file_best_name file);
1898 Array.iter (fun s ->
1899 add_source file s.Q.src_ip s.Q.src_port s.Q.src_server_ip s.Q.src_server_port
1900 ) t.Q.sources;
1901 with _ -> ()
1905 | M.SourcesReq t ->
1907 let module Q = M.Sources in
1908 begin
1910 let file = find_file t.Q.md4 in
1911 (* Always accept sources when already received !
1913 if file.file_enough_sources then begin
1914 lprintf "** Dropped %d sources for %s **\n" (List.length t.Q.sources) (file_best_name file);
1916 end else *)
1917 if !verbose_location then
1918 lprintf_nl "donkeyClient: SourcesReq: Received %d sources from %s for %s"
1919 (List.length t.Q.sources) (full_client_identifier c) (file_best_name file);
1920 List.iter (fun (ip1, port, ip2) ->
1921 add_source file ip1 port Ip.null 0
1922 ) t.Q.sources;
1923 with _ -> ()
1926 | M.SayReq s when (!is_not_spam) s ->
1927 (* FIXME: add logging *)
1928 (* !say_hook c s *)
1929 private_message_from (as_client c) s;
1930 let cip = string_of_client_addr c in
1931 log_chat_message cip (client_num c) c.client_name s;
1933 | M.EmuleCaptchaReq t ->
1934 let b64data = Base64.encode t in
1935 let cip = string_of_client_addr c in
1936 log_chat_message cip (client_num c) c.client_name ("data:image/bmp;base64," ^ b64data)
1938 | M.EmuleCaptchaRes t ->
1939 let msg = match t with
1940 | 0 -> _s "You have correctly solved the captcha and your message was sent."
1941 | 1 -> _s "Wrong answer to the captcha, so your message was not sent. You will only be sent 3 captchas. Try sending another message to receive another captcha challenge."
1942 | 2 -> _s "3 captchas have already been sent to you. Fail."
1943 | _ -> _s "Unknown captcha state!?"
1945 let cip = string_of_client_addr c in
1946 log_chat_message cip (client_num c) c.client_name msg
1948 | M.QueryChunkMd4Req t when !CommonGlobals.has_upload = 0 ->
1950 let file = find_file t in
1951 begin
1952 match file.file_computed_md4s with
1953 [||] -> () (* should not happen *)
1954 | md4s ->
1955 client_send c (
1956 let module Q = M.QueryChunkMd4Reply in
1957 M.QueryChunkMd4ReplyReq {
1958 Q.md4 = file.file_md4;
1959 Q.chunks = md4s
1964 | M.QueryChunksReq t ->
1965 c.client_requests_received <- c.client_requests_received + 1;
1967 (* All clients query chunks during download! This is legitimate!
1968 !CommonGlobals.has_upload = 0 && *)
1969 (* banned is banned, do we need to check ban_queue_jumpers
1970 here? besides that ... we shouldn't be connected with
1971 a banned client! Waste of resources! Or? *)
1972 if not (!!ban_queue_jumpers && c.client_banned) then
1973 begin
1975 let file = find_file t in
1976 let chunks =
1977 match file.file_swarmer with
1978 None ->
1979 (* file was found, if we have no swarmer, we have
1980 the file complete and share it! it's safe to
1981 assume that we have all chunks! *)
1982 Bitv.create file.file_nchunks true
1983 | Some swarmer ->
1984 let bitmap = CommonSwarming.chunks_verified_bitmap swarmer in
1985 Bitv.init (VB.length bitmap)
1986 (fun i -> VB.get bitmap i = VB.State_verified)
1987 (* This is not very smart, as we might get banned for this request.
1988 TODO We should probably check if we don't know already this source...
1990 NONSENSE! We don't need to query_file! A peer requesting
1991 chunks will always have (part of) that file!
1992 We would just have to add it as source ... but I think it was already done!
1994 DonkeySources.query_file c.client_source file.file_sources;
1995 chunks
1998 client_send c (
1999 let module Q = M.QueryChunksReply in
2000 M.QueryChunksReplyReq {
2001 Q.md4 = file.file_md4;
2002 Q.chunks = chunks;
2004 with
2005 | _ ->
2006 if !verbose_unexpected_messages then
2007 lprintf_nl "donkeyClient:QueryChunksReq: chunks of unpublished file %s queried from %s"
2008 (Md4.to_string t) (full_client_identifier c);
2009 client_send c ( M.NoSuchFileReq t );
2012 | M.QueryBlocReq t when !CommonGlobals.has_upload = 0 &&
2013 client_has_a_slot (as_client c) ->
2015 let module Q = M.QueryBloc in
2016 let file = find_file t.Q.md4 in
2018 let check_file_size size =
2019 if size > file_size file && size <> 0L then
2020 begin
2021 lprintf_nl "client requested filesize %Ld > real filesize %Ld, %s %s, upload slot revoked"
2022 size (file_size file) (file_best_name file) (full_client_identifier c);
2023 DonkeyOneFile.remove_client_slot c;
2024 raise Not_found
2028 (* ignore block requests outside file boundaries *)
2029 check_file_size t.Q.start_pos1;
2030 check_file_size t.Q.start_pos2;
2031 check_file_size t.Q.start_pos3;
2033 if !verbose_upload then lprintf_nl "donkeyClient: uploader %s asks for %s"
2034 (full_client_identifier c) (file_best_name file);
2036 let prio = (file_priority file) in
2037 let client_upload_lifetime = ref ((max 0 !!upload_lifetime) * Date.minute_in_secs) in
2039 if !!dynamic_upload_lifetime && not !!upload_complete_chunks
2040 && c.client_session_uploaded > c.client_session_downloaded
2041 && c.client_session_uploaded > Int64.of_int !!dynamic_upload_threshold ** zone_size
2042 then
2043 client_upload_lifetime :=
2044 Int64.to_int
2045 (Int64.of_int !client_upload_lifetime
2046 ** c.client_session_downloaded // c.client_session_uploaded);
2048 let client_received_enough c =
2049 if !!upload_full_chunks && not !!upload_complete_chunks then
2050 c.client_session_uploaded > (block_size ++ 20L ** 1024L)
2051 else
2052 last_time() > c.client_connect_time + !client_upload_lifetime + 5 * prio
2055 begin
2056 if client_received_enough c then
2057 if Intmap.length !CommonUploads.pending_slots_map = 0 then
2058 begin
2059 if !verbose_upload then lprintf_nl
2060 "donkeyClient: not closing upload slot of %s (%s), pending slots empty, sending next block..."
2061 (full_client_identifier c) (file_best_name file)
2063 else begin
2064 DonkeyOneFile.remove_client_slot c;
2065 raise Not_found
2066 end;
2068 set_lifetime sock active_lifetime;
2069 set_rtimeout sock !!upload_timeout;
2071 let up, waiting = match c.client_upload with
2072 | Some ({ up_file = f; _ } as up) when f == file ->
2073 (* zones are received in the order they're sent, so we
2074 know that the oldest of the zones "in fly" must have
2075 been received when this QueryBlockReq was sent *)
2076 (match up.up_flying_chunks with
2077 | [] -> ()
2078 | _ :: q -> up.up_flying_chunks <- q);
2079 up, up.up_waiting
2080 | Some old_up ->
2082 up_file = file;
2083 up_pos = Int64.zero;
2084 up_end_chunk = Int64.zero;
2085 up_chunks = [];
2086 up_flying_chunks = [];
2087 up_current = Int64.zero;
2088 up_finish = true;
2089 up_waiting = old_up.up_waiting;
2090 }, old_up.up_waiting
2091 | _ ->
2093 up_file = file;
2094 up_pos = Int64.zero;
2095 up_end_chunk = Int64.zero;
2096 up_chunks = [];
2097 up_flying_chunks = [];
2098 up_current = ((t.Q.start_pos1 ++ t.Q.end_pos1) // (2L ** block_size));
2099 up_finish = false;
2100 up_waiting = false;
2101 }, false
2103 new_chunk up t.Q.start_pos1 t.Q.end_pos1;
2104 new_chunk up t.Q.start_pos2 t.Q.end_pos2;
2105 new_chunk up t.Q.start_pos3 t.Q.end_pos3;
2106 (match up.up_chunks with
2107 [] ->
2108 (* it should never happen here, that a client with up.up_finish = false
2109 has an empty block queue *)
2110 if up.up_finish && !!upload_complete_chunks then
2111 begin
2112 DonkeyOneFile.remove_client_slot c;
2113 raise Not_found
2114 end;
2115 | chunks ->
2116 c.client_upload <- Some up;
2117 set_client_upload (as_client c) (as_file file);
2118 if not waiting && !CommonGlobals.has_upload = 0 then begin
2119 CommonUploads.ready_for_upload (as_client c);
2120 up.up_waiting <- true
2121 end)
2122 end;
2123 if !verbose_upload then lprintf_nl "QueryBloc treated"
2125 | M.NoSuchFileReq t ->
2126 begin
2128 let file = find_file t in
2129 if !verbose_location then
2130 lprintf_nl "donkeyClient: NoSuchFileReq: from %s for file %s"
2131 (full_client_identifier c) (file_best_name file);
2132 DonkeySources.set_request_result c.client_source
2133 file.file_sources File_not_found;
2134 with _ -> ()
2137 | _ ->
2138 if !verbose_unknown_messages then begin
2139 lprintf_nl "Unused client message %s:" (full_client_identifier c);
2140 M.print t;
2143 let client_handler c sock event =
2144 match event with
2145 BASIC_EVENT (CLOSED s) ->
2146 disconnect_client c s;
2148 | BASIC_EVENT (LTIMEOUT | RTIMEOUT) ->
2149 close sock Closed_for_timeout;
2152 if c.client_name <> "" then begin
2153 lprintf "client %s(%s) disconnected: reason %s\n"
2154 c.client_name (brand_to_string c.client_brand) s;
2158 | _ -> ()
2160 let client_handler2 c sock event =
2161 (match event with
2162 BASIC_EVENT (CLOSED s) -> decr DonkeySources.indirect_connections
2163 | _ -> ());
2164 match !c with
2165 Some c -> client_handler c sock event
2166 | None ->
2167 match event with
2168 BASIC_EVENT (LTIMEOUT | RTIMEOUT) ->
2169 close sock Closed_for_timeout
2171 | _ -> ()
2173 let init_connection sock ip =
2174 TcpBufferedSocket.setsock_iptos_throughput sock;
2176 let nolimit = try
2177 Ip.matches ip !!nolimit_ips
2178 with _ -> false in
2179 if not nolimit then begin
2180 TcpBufferedSocket.set_read_controler sock download_control;
2181 TcpBufferedSocket.set_write_controler sock upload_control;
2182 end;
2183 set_rtimeout sock !!client_timeout;
2185 (* Fix a lifetime for the connection. If we are not able to connect and
2186 query file within this delay, the connection is aborted.
2188 With 150 connections of 1 minute, it means we can at most make
2189 make 1500 connections/10 minutes. *)
2191 (* set_lifetime sock 60.; *)
2194 let init_client sock c =
2195 set_handler sock WRITE_DONE (fun s ->
2196 match c.client_upload with
2197 | Some ({ up_chunks = _ :: _; _ } as up) ->
2198 if not up.up_waiting && !CommonGlobals.has_upload = 0 then begin
2199 up.up_waiting <- true;
2200 CommonUploads.ready_for_upload (as_client c)
2202 | _ -> ()
2205 set_handler sock (BASIC_EVENT RTIMEOUT) (fun s ->
2206 connection_delay c.client_connection_control;
2207 printf_string "-!C";
2208 close s "timeout"
2209 ); *)
2210 (* c.client_block <- None; *)
2211 (* c.client_zones <- []; *)
2212 c.client_file_queue <- [];
2213 set_client_has_a_slot (as_client c) NoSlot;
2214 c.client_upload <- None;
2215 c.client_rank <- 0;
2216 c.client_requests_received <- 0;
2217 c.client_requests_sent <- 0;
2218 c.client_slot <- SlotNotAsked
2220 let read_first_message overnet server cc m sock =
2221 let module M = DonkeyProtoClient in
2222 let real_ip = peer_ip sock in
2223 if (not server && !verbose_msg_clients) || (server && !verbose_msg_servers) then begin
2224 lprintf_nl "Message from incoming %s %s:%d%s"
2225 (if server then "server" else "client")
2226 (Ip.to_string real_ip)
2227 (peer_port sock)
2228 (match cc with | None -> "" | Some cc -> Printf.sprintf "(%d)" cc);
2229 M.print m;
2230 end;
2232 match m with
2234 | M.ConnectReq t ->
2235 if !verbose_msg_clients then begin
2236 lprintf_nl "[HELLO] %s" (Ip.to_string real_ip);
2237 end;
2239 let module CR = M.Connect in
2241 if not (is_valid_client t.CR.md4 ) then
2242 begin
2243 TcpBufferedSocket.close sock (Closed_for_error "Connect of Invalid Client");
2244 raise Exit
2245 end;
2247 if (is_black_address t.CR.ip t.CR.port cc) then raise Exit;
2249 let name = ref "" in
2250 List.iter (fun tag ->
2251 match tag with
2252 { tag_name = Field_KNOWN "name"; tag_value = String s } -> name := s
2253 | _ -> ()
2254 ) t.CR.tags;
2256 let kind =
2257 if low_id t.CR.ip then
2258 match t.CR.server_info with
2259 | None ->
2260 Invalid_address (!name, Md4.to_string t.CR.md4)
2261 | Some (ip,port) ->
2262 if Ip.usable ip then
2263 Indirect_address (ip, port, id_of_ip t.CR.ip, t.CR.port, real_ip)
2264 else
2265 Invalid_address (!name, Md4.to_string t.CR.md4)
2266 else
2267 if Ip.usable t.CR.ip then
2268 Direct_address (t.CR.ip, t.CR.port)
2269 else
2270 Invalid_address (!name, Md4.to_string t.CR.md4)
2273 let c = new_client kind cc in
2274 if c.client_debug || !verbose_msg_clients || !verbose_msg_clienttags then begin
2275 M.print m;
2276 end;
2278 Hashtbl.add connected_clients t.CR.md4 c;
2280 set_client_name c !name t.CR.md4;
2281 c.client_tags <- t.CR.tags;
2282 identify_client_brand c;
2283 update_client_from_tags c t.CR.tags;
2284 fight_disguised_mods c;
2285 update_emule_release c;
2286 begin
2287 match c.client_source.DonkeySources.source_sock with
2288 | NoConnection ->
2289 c.client_source.DonkeySources.source_sock <- Connection sock;
2290 c.client_connected <- true;
2291 init_client sock c;
2292 init_client_after_first_message sock c
2294 | ConnectionWaiting token ->
2295 cancel_token token;
2296 c.client_source.DonkeySources.source_sock <- Connection sock;
2297 c.client_connected <- true;
2298 init_client sock c;
2299 init_client_after_first_message sock c
2301 | _ ->
2302 close sock (Closed_for_error "already connected");
2303 c.client_connected <- false;
2304 raise Not_found
2305 end;
2307 check_stolen_hash c sock t.CR.md4;
2309 if !!reliable_sources &&
2310 ip_reliability real_ip = Reliability_suspicious 0 then begin
2311 set_client_state c BlackListedHost;
2312 raise Not_found
2313 end;
2315 begin
2316 match t.CR.server_info with
2317 Some (ip, port) -> if !!update_server_list_client then safe_add_server ip port
2318 | None ->
2319 if overnet then begin
2320 lprintf_nl "incoming Overnet client";
2321 DonkeySources.set_source_brand c.client_source overnet;
2323 end;
2325 (* Lugdunum servers are not interested in our EmuleClientInfo *)
2326 if supports_eep c.client_brand && not server then
2327 begin
2328 (* lprintf "Emule Extended Protocol query\n"; *)
2329 let module M = DonkeyProtoClient in
2330 let module E = M.EmuleClientInfo in
2331 client_send c (M.EmuleClientInfoReq emule_info)
2332 end;
2334 request_osinfo c;
2335 client_send c (
2336 let module M = DonkeyProtoClient in
2337 let module C = M.Connect in
2338 if DonkeySources.source_brand c.client_source then
2339 M.ConnectReplyReq {
2340 C.md4 = overnet_md4;
2341 C.ip = client_ip (Some sock);
2342 C.port = !!overnet_port;
2343 C.tags = !overnet_connectreply_tags;
2344 C.server_info = Some (!overnet_server_ip, !overnet_server_port);
2345 C.left_bytes = left_bytes;
2346 C.hash_len = 16;
2348 else
2349 begin
2350 M.ConnectReplyReq {
2351 C.md4 = !!client_md4;
2352 C.ip = client_ip (Some sock);
2353 C.port = !!donkey_port;
2354 (* Lugdunum servers need fewer infos than clients *)
2355 C.tags = if server then !client_to_server_reply_tags else !client_to_client_tags;
2356 C.server_info = Some (get_server_ip_port ());
2357 C.left_bytes = left_bytes;
2358 C.hash_len = 16;
2360 end;
2363 incr_activity_indirect_connections c;
2365 check_stolen_hash c sock t.CR.md4;
2367 finish_client_handshake c sock;
2368 Some c
2370 | M.NewUserIDReq _ ->
2371 lprintf_nl "NewUserIDReq: "; M.print m;
2372 None
2374 | M.EmulePortTestReq t ->
2375 porttest_sock := Some sock;
2376 set_closer sock (fun _ _ -> porttest_sock := None);
2377 set_lifetime sock 30.;
2378 write_string sock (client_msg_to_string (emule_proto ()) m);
2379 None
2381 | _ ->
2382 if !verbose_unknown_messages then
2383 begin
2384 lprintf_nl "BAD MESSAGE FROM CONNECTING CLIENT with ip:%s port:%i overnet:%b"
2385 (Ip.to_string real_ip) (peer_port sock) overnet;
2386 M.print m; lprint_newline ();
2387 end;
2388 close sock (Closed_for_error "bad connecting message");
2389 raise Not_found
2392 let reconnect_client c =
2393 if can_open_connection connection_manager then
2394 match c.client_kind with
2395 Indirect_address _ | Invalid_address _ -> ()
2396 | Direct_address (ip, port) ->
2397 if client_state c <> BlackListedHost then
2398 if !!black_list && is_black_address ip port c.client_country_code ||
2399 (!!reliable_sources && ip_reliability ip = Reliability_suspicious 0) then
2400 set_client_state c BlackListedHost
2401 else
2402 match c.client_source.DonkeySources.source_sock with
2403 ConnectionWaiting _ | Connection _ ->
2404 (* Already connected ! *)
2406 | NoConnection ->
2407 let token =
2408 add_pending_connection connection_manager (fun token ->
2410 set_client_state c Connecting;
2411 (* connection_try c.client_connection_control; *)
2413 let sock = TcpBufferedSocket.connect token "donkey to client"
2414 (Ip.to_inet_addr ip)
2415 port
2416 (client_handler c) (*client_msg_to_string*) in
2419 incr_activity_connections c;
2421 init_connection sock ip;
2422 init_client sock c;
2423 (* The lifetime of the client socket is now half an hour, and
2424 can be increased by AvailableSlotReq, BlocReq, QueryBlocReq
2425 messages *)
2426 set_lifetime sock active_lifetime;
2428 c.client_checked <- false;
2430 set_reader sock (
2431 DonkeyProtoCom.cut_messages
2432 (DonkeyProtoClient.parse c.client_emule_proto)
2433 (client_to_client files c));
2435 c.client_source.DonkeySources.source_sock <- Connection sock;
2437 let old_ip = c.client_ip in
2438 c.client_ip <- ip;
2439 if old_ip <> Ip.null && old_ip <> c.client_ip &&
2440 c.client_country_code = None then
2441 check_client_country_code c;
2442 c.client_connected <- true;
2443 let server_ip, server_port, server_cid =
2445 let s = DonkeyGlobals.last_connected_master () in
2446 match s.server_cid with
2447 None -> s.server_ip, s.server_port, Ip.any
2448 | Some cid -> s.server_ip, s.server_port, cid
2449 with _ -> Ip.localhost, 4665, Ip.any
2451 let send_this_id =
2452 if not (!!force_high_id || !!force_client_high_id)
2453 && low_id server_cid
2454 && Ip.any != server_cid
2455 then
2456 server_cid
2457 else
2458 client_ip None
2460 client_send c (
2461 let module M = DonkeyProtoClient in
2462 let module C = M.Connect in
2463 if DonkeySources.source_brand c.client_source then
2464 M.ConnectReq {
2465 C.md4 = overnet_md4;
2466 C.ip = client_ip None;
2467 C.port = !!overnet_port;
2468 C.tags = !overnet_connect_tags;
2469 C.hash_len = 16;
2470 C.server_info = Some (!overnet_server_ip,
2471 !overnet_server_port);
2472 C.left_bytes = left_bytes;
2474 else
2475 M.ConnectReq {
2476 C.md4 = !!client_md4;
2477 C.ip = send_this_id;
2478 C.port = !!donkey_port;
2479 C.tags = !client_to_client_tags;
2480 C.hash_len = 16;
2481 C.server_info = Some (server_ip, server_port);
2482 C.left_bytes = left_bytes;
2486 with
2487 Unix.Unix_error (Unix.ENETUNREACH,_,_) ->
2488 if !verbose then lprintf_nl "Network unreachable for IP %s:%d"
2489 (Ip.to_string ip) port;
2490 set_client_disconnected c (Closed_connect_failed);
2491 DonkeySources.source_disconnected c.client_source
2492 | exn ->
2493 lprintf_nl ~exn "client connection to IP %s:%d"
2494 (Ip.to_string ip) port;
2495 (* connection_failed c.client_connection_control; *)
2496 set_client_disconnected c (Closed_for_exception exn);
2497 DonkeySources.source_disconnected c.client_source
2500 c.client_source.DonkeySources.source_sock <- ConnectionWaiting token
2503 let query_locations_reply s t =
2504 let module M = DonkeyProtoServer in
2505 let module Q = M.QueryLocationReply in
2508 let file = find_file t.Q.md4 in
2509 let nlocs = List.length t.Q.locs in
2511 if !verbose_location then
2512 lprintf_nl "Received %d sources from server %s:%s for %s"
2513 nlocs (Ip.to_string s.server_ip) (string_of_int s.server_port) (file_best_name file);
2515 s.server_score <- s.server_score + 3;
2517 (* TODO: verify that new sources are queried as soon as
2518 possible. Maybe we should check how many new sources
2519 this client has, and query a connection immediatly if
2520 they are too many. No need to care about in this
2521 place ... make need_new_sources based on ready
2522 sources, then the next refill_file will query them,
2523 that's soon enough!
2526 List.iter (fun l ->
2527 add_source file l.Q.ip l.Q.port s.server_ip s.server_port
2528 ) t.Q.locs;
2529 with Not_found -> ()
2531 let matches_3 l ip =
2532 let rec iter l (a,b,c,d) =
2533 match l with
2534 [] -> Ip.null
2535 | ip :: _ when
2536 let (w,x,y,z) = Ip.to_ints ip in
2537 w=a && x=b && y=c -> ip
2538 | _ :: t -> iter t (a,b,c,d)
2540 iter l (Ip.to_ints ip)
2542 let client_connection_handler overnet t event =
2543 match event with
2544 TcpServerSocket.CONNECTION (s, Unix.ADDR_INET (from_ip, from_port)) ->
2545 let from_ip = Ip.of_inet_addr from_ip in
2546 let s_from_ip = Ip.to_string from_ip in
2547 let cc = Geoip.get_country_code_option from_ip in
2548 let is_ip_blocked = !Ip.banned (from_ip, cc) <> None in
2549 let too_many_indirect_connections =
2550 !DonkeySources.indirect_connections >
2551 !real_max_indirect_connections
2554 let connecting_server = matches_3 (connecting_server_ips()) from_ip in
2555 let is_connecting_server = connecting_server <> Ip.null in
2557 let accept_connection = not is_ip_blocked
2558 && (not too_many_indirect_connections || is_connecting_server)
2561 if !verbose_connect || (!verbose && (too_many_indirect_connections || is_connecting_server)) then
2562 lprintf_nl "incoming connection from %s:%d %s: (%d/%d)%s"
2563 s_from_ip from_port
2564 (if accept_connection then "accepted" else
2565 if is_ip_blocked then "blocked" else "denied")
2566 !DonkeySources.indirect_connections
2567 !real_max_indirect_connections
2568 (if is_connecting_server then
2569 ( try
2570 let s = Hashtbl.find servers_by_key from_ip in
2571 set_server_state s Connected_initiating;
2572 Printf.sprintf " %s (%s)" s.server_name (string_of_server s)
2573 with _ ->
2574 try
2575 let s = Hashtbl.find servers_by_key connecting_server in
2576 set_server_state s Connected_initiating;
2577 Printf.sprintf " %s (%s)" s.server_name (string_of_server s)
2578 with _ -> "Unknown server"
2580 else ""
2583 if accept_connection then
2585 begin
2586 (try
2587 let c = ref None in
2588 incr DonkeySources.indirect_connections;
2589 let token = create_token connection_manager in
2590 let sock =
2591 TcpBufferedSocket.create token "donkey client connection" s
2592 (client_handler2 c)
2594 init_connection sock from_ip;
2595 accept_connection_bandwidth sock;
2597 (* Normal connections have 20 minutes to live (AvailableSlot, QueryBloc
2598 and Bloc messages extend this lifetime), whereas exceeding connections
2599 have only 1 minute 30 seconds to live. *)
2600 set_lifetime sock (
2601 if can_open_connection connection_manager then
2602 active_lifetime
2603 else
2606 (try
2607 set_reader sock
2608 (DonkeyProtoCom.client_handler2 c (read_first_message overnet is_connecting_server cc)
2609 (client_to_client []));
2611 with exn -> lprintf_nl ~exn "init_connection"
2613 with exn ->
2614 lprintf_nl ~exn "client_connection_handler";
2615 Unix.close s)
2616 end
2617 else
2618 Unix.close s
2619 | _ ->
2623 (*************************************************************************)
2624 (* *)
2625 (* Stubs for CommonSources *)
2626 (* *)
2627 (*************************************************************************)
2629 let _ =
2630 DonkeySources.functions.DonkeySources.function_query <-
2631 (fun s_uid file_uid ->
2633 let c = find_client_by_key s_uid in
2634 let file = find_file (Md4.of_string file_uid) in
2635 c.client_requests_sent <- c.client_requests_sent + 1;
2636 let module M = DonkeyProtoClient in
2638 let emule_extension =
2639 let extendedrequest = M.extendedrequest c.client_emule_proto in
2640 if extendedrequest > 0 then
2641 match file.file_swarmer with
2642 None -> None
2643 | Some swarmer ->
2644 let bitmap = CommonSwarming.chunks_verified_bitmap swarmer in
2645 let chunks =
2646 Bitv.init (VB.length bitmap)
2647 (fun i -> VB.get bitmap i = VB.State_verified)
2649 let ncompletesources = if extendedrequest > 1 then
2650 0 else -1 in
2651 Some (chunks, ncompletesources)
2652 else
2653 None
2655 DonkeyProtoCom.client_send c (
2656 M.QueryFileReq {
2657 M.QueryFile.md4 = file.file_md4;
2659 (* TODO build the extension if needed *)
2660 M.QueryFile.emule_extension = emule_extension;
2662 c.client_last_file_req_md4 <- Some file.file_md4;
2663 let know_file_chunks = List.exists (fun (f,_,_) -> f == file) c.client_file_queue in
2664 if not know_file_chunks then
2665 DonkeyProtoCom.client_send c (
2666 let module M = DonkeyProtoClient in
2667 M.QueryChunksReq file.file_md4);
2668 ignore (DonkeySources.add_request c.client_source
2669 file.file_sources (last_time ()))
2670 with exn ->
2671 if !verbose then
2672 lprintf_nl ~exn "query_source"
2675 DonkeySources.functions.DonkeySources.function_connect <-
2676 (fun s_uid s_cc ->
2678 match s_uid with
2679 Direct_address _ ->
2680 let c = new_client s_uid s_cc in
2681 reconnect_client c
2682 | Invalid_address _ -> ()
2683 | Indirect_address (server_ip, server_port, id, port, real_ip) ->
2685 if Ip.reachable server_ip then
2686 query_id server_ip server_port id;
2688 with exn ->
2689 if !verbose then begin
2690 lprintf_nl "connect_source"
2695 DonkeySources.functions.DonkeySources.function_max_connections_per_second <-
2696 (fun () -> !!max_connections_per_second);
2698 DonkeySources.functions.DonkeySources.function_max_sources_per_file <-
2699 (fun () -> !!max_sources_per_file);
2701 DonkeySources.functions.DonkeySources.function_string_to_manager <-
2702 (fun file_uid ->
2703 let file = find_file (Md4.of_string file_uid) in
2704 file.file_sources
2708 (* TODO: verify that the client is destroyed very early. We should also find
2709 a way to keep the client allocated after the first successful connection,
2710 for a given time. For example, we could put successful clients in
2711 a FIFO from where they are removed after 30 minutes. What about using
2712 file.file_clients for this purpose !! *)
2713 DonkeySources.functions.DonkeySources.function_add_location <- (fun
2714 s_uid file_uid s_cc ->
2716 let file = find_file (Md4.of_string file_uid) in
2717 let c = new_client s_uid s_cc in
2719 CommonFile.file_add_source (CommonFile.as_file file.file_file)
2720 (CommonClient.as_client c.client_client);
2722 with
2723 | Not_found -> ()
2724 | exn ->
2725 if !verbose then
2726 lprintf_nl ~exn "add_location"
2729 DonkeySources.functions.DonkeySources.function_remove_location <- (fun
2730 s_uid file_uid ->
2732 let file = find_file (Md4.of_string file_uid) in
2733 let c = new_client s_uid None in
2734 CommonFile.file_remove_source (CommonFile.as_file file.file_file)
2735 (CommonClient.as_client c.client_client);
2737 with
2738 | Not_found -> ()
2739 | exn ->
2740 if !verbose then
2741 lprintf_nl ~exn "remove_location for file_md4 %s"
2742 file_uid