patch #7310
[mldonkey.git] / src / networks / donkey / donkeyGlobals.ml
blob3e93648d6d06afbeba0394f691214147229a7369
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
20 open Int64ops
21 open TcpBufferedSocket
22 open Queues
23 open Printf2
24 open Md4
25 open Options
26 open BasicSocket
28 open CommonDownloads
29 open CommonSwarming
30 open CommonInteractive
31 open CommonResult
32 open CommonFile
33 open CommonServer
34 open CommonComplexOptions
35 open CommonClient
36 open CommonTypes
37 open CommonOptions
38 open CommonGlobals
39 open CommonNetwork
41 open DonkeyTypes
42 open DonkeyOptions
43 open CommonOptions
45 let log_prefix = "[EDK]"
47 let lprintf_nl fmt =
48 lprintf_nl2 log_prefix fmt
50 (*************************************************************
52 Define the instances of the plugin classes, that we be filled
53 later with functions defining the specialized methods for this
54 plugin.
56 **************************************************************)
58 let network = CommonNetwork.new_network "ED2K" "Donkey"
59 ~comment:(if Autoconf.donkey_sui_works () then "SUI" else "noSUI")
61 NetworkHasServers;
62 NetworkHasSearch;
63 NetworkHasUpload;
64 NetworkHasMultinet;
65 NetworkHasChat;
66 NetworkHasStats;
69 let connection_manager = network.network_connection_manager
70 let connections_controler = TcpServerSocket.create_connections_controler
71 "Edonkey" (fun _ _ -> true)
73 let (shared_ops : file CommonShared.shared_ops) =
74 CommonShared.new_shared_ops network
76 let (server_ops : server CommonServer.server_ops) =
77 CommonServer.new_server_ops network
79 let (room_ops : server CommonRoom.room_ops) =
80 CommonRoom.new_room_ops network
82 let (user_ops : user CommonUser.user_ops) =
83 CommonUser.new_user_ops network
85 let (file_ops : file CommonFile.file_ops) =
86 CommonFile.new_file_ops network
88 let (client_ops : client CommonClient.client_ops) =
89 CommonClient.new_client_ops network
91 let (pre_shared_ops : file_to_share CommonShared.shared_ops) =
92 CommonShared.new_shared_ops network
94 let (shared_ops : file CommonShared.shared_ops) =
95 CommonShared.new_shared_ops network
97 let client_must_update c =
98 client_must_update (as_client c.client_client)
100 let server_must_update s =
101 server_must_update (as_server s.server_server)
103 let as_client c = as_client c.client_client
104 let as_file file = as_file file.file_file
105 let file_priority file = file.file_file.impl_file_priority
106 let file_size file = file.file_file.impl_file_size
107 let file_is_largefile f = file_size f > old_max_emule_file_size
108 let file_downloaded file = file_downloaded (as_file file)
109 let file_age file = file.file_file.impl_file_age
110 let file_fd file = file_fd (as_file file)
111 let file_disk_name file = file_disk_name (as_file file)
112 let file_best_name file = file_best_name (as_file file)
114 let client_num c = client_num (as_client c)
115 let file_num c = file_num (as_file c)
116 let server_num c = server_num (as_server c.server_server)
119 (*************************************************************************)
120 (* *)
121 (* Global values *)
122 (* *)
123 (*************************************************************************)
126 let tag_client = 200
127 let tag_server = 201
128 let tag_file = 202
130 let donkey_download_counter = ref Int64.zero
131 let donkey_upload_counter = ref Int64.zero
133 let client_to_client_tags = ref ([] : tag list)
134 let client_to_server_tags = ref ([] : tag list)
135 let client_to_server_reply_tags = ref ([] : tag list)
136 let emule_info =
137 let module E = DonkeyProtoClient.EmuleClientInfo in
139 E.version = 66;
140 E.protversion = 66;
141 E.tags = [];
144 let sec_ident_enabled () = !!enable_sui && (Autoconf.donkey_sui_works ())
146 let overnet_connectreply_tags = ref ([] : tag list)
147 let overnet_connect_tags = ref ([] : tag list)
149 let overnet_md4 = Md4.random()
150 let nservers = ref 0
151 let xs_last_search = ref (-1)
153 let zone_size = Int64.of_int (180 * 1024)
154 let block_size = 9728000L
156 (* Old value: *)
157 (* let nchunks = Int64.to_int (Int64.pred file_size // block_size) + 1 in *)
159 (* New value: *)
160 (* From Emule: KnownFile.cpp
161 // File size Data parts ED2K parts ED2K part hashs
162 // ---------------------------------------------------------------
163 // 1..PARTSIZE-1 1 1 0(!)
164 // PARTSIZE 1 2(!) 2(!)
165 // PARTSIZE+1 2 2 2
166 // PARTSIZE*2 2 3(!) 3(!)
167 // PARTSIZE*2+1 3 3 3
170 let get_nchunks size = Int64.to_int (size // block_size) + 1
172 let get_nchunk_hashes size =
173 let nchunk_hashes = Int64.to_int (size // block_size) in
174 let nchunk_hashes = if nchunk_hashes <> 0
175 then nchunk_hashes + 1
176 else nchunk_hashes in
177 nchunk_hashes
179 let queue_timeout = ref (60. *. 10.) (* 10 minutes *)
181 let files_queries_per_minute = 3 (* queries for 3 files cost 3*16=48 server-credits; we did get 60 (1 each second) *)
183 let nclients = ref 0
185 let protocol_version = 62
186 let max_file_groups = 1000
187 let master_server = ref (None: DonkeyTypes.server option)
188 let udp_sock = ref (None: UdpSocket.t option)
189 let listen_sock = ref (None : TcpServerSocket.t option)
190 let porttest_sock = ref (None : TcpBufferedSocket.t option)
192 (*************************************************************************)
193 (* *)
194 (* Global tables *)
195 (* *)
196 (*************************************************************************)
198 module H = Weak.Make(struct
199 type t = client
200 let hash c = Hashtbl.hash c.client_kind
202 let equal x y = x.client_kind = y.client_kind
203 end)
205 let clients_by_kind = H.create 127
206 let clients_root = ref []
207 let servers_by_key = Hashtbl.create 127
208 let servers_list = ref ([] : server list)
209 let walker_list = ref ([] : server list)
210 let delayed_list = ref ([] : server list)
212 (* let remaining_time_for_clients = ref (60 * 15) *)
214 let current_files = ref ([] : file list)
215 let xs_servers_list = ref ([] : server list)
216 let connected_server_list = ref ([] : server list)
217 let connecting_server_list = ref ([] : server list)
219 let (banned_ips : (Ip.t, int) Hashtbl.t) = Hashtbl.create 113
220 let (old_requests : (int * int, request_record) Hashtbl.t) =
221 Hashtbl.create 13013
223 let (file_groups_fifo : Md4.t Fifo.t) = Fifo.create ()
224 let (connected_clients : (Md4.t, client) Hashtbl.t) = Hashtbl.create 130
226 let udp_servers_list = ref ([] : server list)
227 let interesting_clients = ref ([] : client list)
229 let files_by_md4 = Hashtbl.create 127
230 let find_file md4 = Hashtbl.find files_by_md4 md4
232 (* changed 2.5.24: we store directly the size and the modification time
233 in the shared_files_info *)
234 let shared_files_info = (Hashtbl.create 127
235 : (string * int64 * float, shared_file_info) Hashtbl.t)
236 let shared_files = ref ([] : file_to_share list)
238 let udp_servers_replies = (Hashtbl.create 127 : (Md4.t, server) Hashtbl.t)
240 let file_groups = (Hashtbl.create 1023 : (Md4.t, file_group) Hashtbl.t)
242 module UdpClientWHashtbl = Weak.Make(struct
243 type t = udp_client
244 let hash c = Hashtbl.hash (c.udp_client_ip, c.udp_client_port)
246 let equal x y = x.udp_client_port = y.udp_client_port
247 && x.udp_client_ip = y.udp_client_ip
248 end)
250 let udp_clients = UdpClientWHashtbl.create 1023
252 let join_queue_by_md4 = Hashtbl.create 13
253 let join_queue_by_id = Hashtbl.create 13
255 (*************************************************************************)
256 (* *)
257 (* Global functions *)
258 (* *)
259 (*************************************************************************)
261 let _ =
262 network.op_network_connected_servers <- (fun _ ->
263 List2.tail_map (fun s -> as_server s.server_server) !connected_server_list
267 let hashtbl_remove table key v =
269 let vv = Hashtbl.find table key in
270 if vv == v then
271 Hashtbl.remove table key
272 with _ -> ()
274 let add_connecting_server c =
275 connecting_server_list := c :: !connecting_server_list
277 let remove_connecting_server c =
278 connecting_server_list := List2.removeq c !connecting_server_list
280 let connecting_server_ips () =
281 List.rev_map (fun s -> s.server_ip) !connecting_server_list
283 let add_connected_server c =
284 connected_server_list := c :: !connected_server_list
286 let remove_connected_server c =
287 connected_server_list := List2.removeq c !connected_server_list
289 let connected_servers () = !connected_server_list
291 let logged_in_servers () =
292 List.filter (fun s ->
293 match server_state s with
294 | Connected _ -> true
295 | _ -> false) !connected_server_list
297 let get_udp_sock () =
298 match !udp_sock with
299 None -> failwith "No UDP socket"
300 | Some sock -> sock
302 let md4_of_array md4s =
303 let s = String.create ((Array.length md4s) * 16) in
304 Array.iteri (fun i v ->
305 String.blit (Md4.direct_to_string v) 0 s (i*16) 16
306 ) md4s;
307 Md4.string s
309 (* compute the name used to save the file *)
311 let update_best_name file =
313 let best_name = file_best_name file in
314 (* lprintf "update_best_name: %s\n" best_name; *)
315 if best_name = file_string_of_uid (Ed2k file.file_md4)
316 || best_name = string_of_uid (Ed2k file.file_md4)
317 || best_name = Md4.to_string file.file_md4
318 then
320 let file = as_file file in
321 (* lprintf "Propose filename...\n"; *)
322 CommonFile.propose_filename file;
323 let impl = as_file_impl file in
324 match impl.impl_file_probable_name with
325 None -> ()
326 | Some best_name ->
327 set_file_best_name file best_name "" 0
328 with Not_found -> ()
330 let new_file file_diskname file_state md4 file_size filename writable user group =
332 let file = find_file md4 in
333 if file.file_diskname <> file_diskname then
334 begin
335 if not (Sys.file_exists file.file_diskname)
336 && Sys.file_exists file_diskname
337 && file.file_shared = None
338 && Unix32.destroyed (file_fd file)
339 then
340 begin
341 if !verbose_share then
342 lprintf_nl "New file with changed filename %s to %s"
343 file.file_diskname file_diskname;
344 file.file_diskname <- file_diskname;
346 else
347 if !verbose_share then
348 lprintf_nl "New file with not changed different filename %s and %s"
349 file.file_diskname file_diskname;
350 end;
351 if Unix32.destroyed (file_fd file)
352 && not writable
353 && file.file_diskname = file_diskname
354 then
355 file.file_file.impl_file_fd <-
356 Some (Unix32.create_diskfile file.file_diskname true);
357 if Unix32.destroyed (file_fd file) then
358 lprintf_nl "New Edonkey file with %b && %b remaining destroyed fd %s"
359 (not writable) (file.file_diskname = file_diskname) file.file_diskname;
360 file
361 with _ ->
362 if !verbose_share then
363 lprintf_nl "New file with md4: %s" (Md4.to_string md4);
365 let t =
366 (* emulate_sparsefiles does not work, temporarily disabled
368 (* Don't use this for shared files ! *)
369 writable &&
370 (* Only if the option is set *)
371 !!emulate_sparsefiles &&
372 (* Only if the file does not already exists *)
373 not (Unix32.file_exists file_diskname)
374 then
375 Unix32.create_sparsefile file_diskname writable
376 else
379 Unix32.create_diskfile file_diskname writable
380 with e ->
381 failwith (Printf.sprintf "Error: %s" (Printexc2.to_string e))
383 let file_size =
384 if file_size = Int64.zero then
386 Unix32.getsize file_diskname
387 with _ ->
388 failwith "Zero length file ?"
389 else file_size
392 if file_size <> zero && writable then (* do not truncate if not writable *)
393 begin
395 Unix32.ftruncate64 t file_size !!create_file_sparse
396 with e ->
397 (try
398 Unix32.remove t
399 with e ->
400 lprintf_nl "Unix32.remove %s exception %s"
401 (file_diskname) (Printexc2.to_string e));
402 Unix32.destroy t;
403 failwith (Printf.sprintf "file size %s is too big, exception: %s"
404 (size_of_int64 file_size) (Printexc2.to_string e))
405 end;
407 let md4s = if file_size < block_size then [md4] else [] in
408 let rec file = {
409 file_diskname = file_diskname;
410 file_file = file_impl;
411 file_shared = None;
412 file_md4 = md4;
413 file_swarmer = None;
414 file_nchunks = get_nchunks file_size;
415 file_nchunk_hashes = get_nchunk_hashes file_size;
416 file_computed_md4s = Array.of_list md4s;
417 file_format = FormatNotComputed 0;
418 file_sources = DonkeySources.create_file_sources_manager
419 (Md4.to_string md4);
420 file_comments = [];
422 and file_impl = {
423 dummy_file_impl with
424 impl_file_owner = user;
425 impl_file_group = group;
426 impl_file_val = file;
427 impl_file_ops = file_ops;
428 impl_file_age = last_time ();
429 impl_file_size = file_size;
430 impl_file_fd = Some t;
431 impl_file_best_name = Filename.basename file_diskname;
432 impl_file_filenames = (if filename = "" then [] else [filename]);
433 impl_file_last_seen = last_time () - 100 * Date.day_in_secs;
437 file.file_sources.DonkeySources.manager_file <- (fun () -> as_file file);
439 (match file_state with
440 FileShared -> ()
441 | _ ->
442 let kernel = CommonSwarming.create_swarmer file_diskname file_size in
443 let swarmer = CommonSwarming.create kernel (as_file file) block_size
445 file.file_swarmer <- Some swarmer;
446 CommonSwarming.set_verifier swarmer
447 (if md4s = [] then VerificationNotAvailable else
448 Verification (Array.of_list (List.map (fun md4 -> Ed2k md4) md4s))
450 CommonSwarming.set_verified swarmer (fun nblocks num ->
451 if nblocks = 1 then file_must_update file)
454 update_best_name file;
455 file_add file_impl file_state;
456 Heap.set_tag file tag_file;
457 Hashtbl.add files_by_md4 md4 file;
458 file
462 for i = 0 to file.file_nchunks - 1 do
463 if client_chunks.(i) then
464 let new_n = file.file_available_chunks.(i) + 1 in
465 if new_n < 11 then file_must_update file;
466 file.file_available_chunks.(i) <- new_n;
467 done
469 let remove_client_chunks file client_chunks =
470 for i = 0 to file.file_nchunks - 1 do
471 if client_chunks.(i) then
472 let new_n = file.file_available_chunks.(i) - 1 in
473 if new_n < 11 then file_must_update file;
474 file.file_available_chunks.(i) <- new_n;
475 client_chunks.(i) <- false
476 done
479 let low_id ip =
480 match Ip.to_ints ip with
481 | _, _, _, 0 -> true
482 | _ -> false
484 let is_black_address ip port cc =
485 !!black_list && not (low_id ip) && (
486 (* lprintf "is black ="; *)
487 not (Ip.reachable ip) ||
488 (Ip_set.match_ip !server_black_list_set ip) ||
489 (List.mem port !!port_black_list) ||
490 (match !Ip.banned (ip, cc) with
491 None -> false
492 | Some reason ->
493 if !verbose_connect then
494 lprintf_nl "%s:%d blocked: %s" (Ip.to_string ip) port reason;
495 true))
497 let check_server_country_code s =
498 if Geoip.active () then
499 match s.server_country_code with
500 | None -> s.server_country_code <- Geoip.get_country_code_option s.server_ip
501 | _ -> ()
503 let new_server ip port =
504 let key = (ip) in
506 let found = Hashtbl.find servers_by_key key in
507 (* Is updating port to the most recent value the correct thing to do ?
508 PlasmaHH says they're legitimate servers switching ports :( *)
509 found.server_port <- port;
510 found
511 with Not_found ->
512 let rec s = {
513 server_server = server_impl;
514 server_next_udp = last_time ();
515 server_ip = ip;
516 server_cid = None (* client_ip None *);
517 server_port = port;
518 server_realport = None;
519 server_country_code = None;
520 server_sock = NoConnection;
521 server_search_queries = Fifo.create ();
522 server_users_queries = Fifo.create ();
523 server_connection_control = new_connection_control ();
524 server_score = 5;
525 server_tags = [];
526 server_nfiles = None;
527 server_nusers = None;
528 server_name = "";
529 server_description = "";
530 server_banner = "";
531 server_users = [];
532 server_master = false;
533 server_preferred = false;
534 server_queries_credit = 0;
535 server_waiting_queries = [];
536 server_sent_all_queries = false;
537 server_id_requests = Fifo.create ();
538 server_flags = 0;
539 server_has_zlib = false;
540 server_has_newtags = false;
541 server_has_unicode = false;
542 server_has_related_search = false;
543 server_has_tag_integer = false;
544 server_has_largefiles = false;
545 server_version = "";
546 server_lowid_users = None;
547 server_soft_limit = None;
548 server_hard_limit = None;
549 server_obfuscation_tcp = None;
550 server_obfuscation_udp = None;
551 server_sent_shared = [];
552 server_max_users = None;
553 server_last_ping = 0.;
554 server_next_ping = 0.;
555 server_descping_counter = 0;
556 server_ping = 0;
557 server_failed_count = 0;
558 server_udp_ping_challenge = None;
559 server_udp_desc_challenge = None;
560 server_has_get_sources = false;
561 server_has_get_files = false;
562 server_has_get_sources2 = false;
563 server_dynip = "";
564 server_auxportslist = "";
567 and server_impl =
569 dummy_server_impl with
570 CommonServer.impl_server_val = s;
571 CommonServer.impl_server_ops = server_ops;
574 server_add server_impl;
575 Heap.set_tag s tag_server;
576 Hashtbl.add servers_by_key key s;
577 check_server_country_code s;
578 server_must_update s;
581 let find_server ip port =
582 let key = (ip) in
583 Hashtbl.find servers_by_key key
585 let remove_server ip port =
586 let key = (ip) in
587 let s = Hashtbl.find servers_by_key key in
589 Hashtbl.remove servers_by_key key;
590 servers_list := List2.removeq s !servers_list ;
591 walker_list := List2.removeq s !walker_list;
592 delayed_list := List2.removeq s !delayed_list;
593 (match s.server_sock with
594 NoConnection -> ()
595 | ConnectionWaiting token -> cancel_token token
596 | Connection sock ->
597 TcpBufferedSocket.shutdown sock Closed_by_user)
598 with _ -> ()
600 let check_client_country_code c =
601 if Geoip.active () then
602 match c.client_country_code with
603 | None ->
604 (match c.client_kind with
605 | Direct_address (ip,port) ->
606 c.client_country_code <- Geoip.get_country_code_option ip
607 | Indirect_address (_,_,_,_,real_ip) ->
608 c.client_country_code <- Geoip.get_country_code_option real_ip
609 | _ -> ())
610 | _ -> ()
612 let dummy_client =
613 let module D = DonkeyProtoClient in
614 let rec c = {
615 client_client = client_impl;
616 client_upload = None;
617 client_kind = Direct_address (Ip.null, 0);
618 client_source = DonkeySources.dummy_source;
619 client_ip = Ip.null;
620 client_country_code = None;
621 client_md4 = Md4.null;
622 client_download = None;
623 client_file_queue = [];
624 client_tags = [];
625 client_name = "";
626 client_all_files = None;
627 client_rating = 0;
628 client_brand = Brand_unknown;
629 client_brand_mod = Brand_mod_unknown;
630 client_osinfo = None;
631 client_checked = false;
632 client_connected = false;
633 client_session_downloaded = Int64.zero;
634 client_session_uploaded = Int64.zero;
635 client_total_downloaded = Int64.zero;
636 client_total_uploaded = Int64.zero;
637 client_banned = false;
638 client_rank = 0;
639 client_connect_time = 0;
640 client_requests_sent = 0;
641 client_requests_received = 0;
642 client_slot = SlotNotAsked;
643 client_debug = false;
644 client_pending_messages = [];
645 client_emule_proto = emule_proto ();
646 client_comp = None;
647 client_connection_time = 0;
648 client_req_challenge = Int64.zero;
649 client_sent_challenge = Int64.zero;
650 client_public_key = None;
651 client_sui_verified = None;
652 client_last_file_req_md4 = None;
653 client_osinfo_sent = false;
654 } and
655 client_impl = {
656 dummy_client_impl with
657 impl_client_val = c;
658 impl_client_ops = client_ops;
659 impl_client_upload = None;
664 let create_client key cc =
665 let module D = DonkeyProtoClient in
666 let s = DonkeySources.create_source_by_uid (match key with
667 Indirect_address (server_ip, server_port, id, port, real_ip) -> Indirect_address (server_ip, server_port, id, 0, Ip.null)
668 | _ -> key) cc in
669 let rec c = {
670 client_client = client_impl;
671 client_kind = key;
672 client_upload = None;
673 client_source = s;
674 client_ip = Ip.null;
675 client_country_code = cc;
676 client_md4 = Md4.null;
677 client_download = None;
678 client_file_queue = [];
679 client_tags = [];
680 client_name = "";
681 client_all_files = None;
682 client_rating = 0;
683 client_brand = Brand_unknown;
684 client_brand_mod = Brand_mod_unknown;
685 client_osinfo = None;
686 client_checked = false;
687 client_connected = false;
688 client_total_downloaded = Int64.zero;
689 client_total_uploaded = Int64.zero;
690 client_session_downloaded = Int64.zero;
691 client_session_uploaded = Int64.zero;
692 client_banned = false;
693 client_rank = 0;
694 client_connect_time = 0;
695 client_requests_received = 0;
696 client_requests_sent = 0;
697 client_slot = SlotNotAsked;
698 client_debug = Intset.mem s.DonkeySources.source_num !debug_clients;
699 client_pending_messages = [];
700 client_emule_proto = emule_proto ();
701 client_comp = None;
702 client_connection_time = 0;
703 client_req_challenge = Int64.zero;
704 client_sent_challenge = Int64.zero;
705 client_public_key = None;
706 client_sui_verified = None;
707 client_last_file_req_md4 = None;
708 client_osinfo_sent = false;
709 } and client_impl = {
710 dummy_client_impl with
711 impl_client_val = c;
712 impl_client_ops = client_ops;
713 impl_client_upload = None;
716 Heap.set_tag c tag_client;
717 CommonClient.new_client_with_num client_impl s.DonkeySources.source_num;
718 H.add clients_by_kind c;
719 clients_root := c :: !clients_root;
720 check_client_country_code c;
723 exception ClientFound of client
724 let find_client_by_key key =
726 H.iter (fun c ->
728 (match c.client_kind with
729 | Indirect_address (server_ip, server_port, id, port, real_ip) ->
730 Indirect_address (server_ip, server_port, id, 0, Ip.null)
731 | _ -> c.client_kind) =
732 (match key with
733 | Indirect_address (server_ip, server_port, id, port, real_ip) ->
734 Indirect_address (server_ip, server_port, id, 0, Ip.null)
735 | _ -> key) then
736 raise (ClientFound c)
737 ) clients_by_kind;
738 raise Not_found
739 with ClientFound c -> c
741 let new_client key cc =
743 let c = find_client_by_key key in
744 (* An indirect client without real_ip might have been created earlier.
745 If that client connected us later we have its real ip *)
746 (match key with
747 | Indirect_address (_,_,_,_,ip_real) ->
748 let old_ip =
749 match c.client_kind with
750 | Indirect_address (_,_,_,_,old_ip) ->
751 if old_ip = Ip.null then None else Some old_ip
752 | _ -> None
754 if ip_real <> Ip.null then c.client_kind <- key;
755 (match old_ip with
756 | Some old_ip ->
757 if old_ip <> ip_real then check_client_country_code c
758 | None -> if ip_real <> Ip.null then check_client_country_code c)
759 | _ -> ());
761 with _ ->
762 create_client key cc
764 let create_client = ()
766 let client_type c =
767 client_type (as_client c)
769 let set_client_type c t=
770 set_client_type (as_client c) t
772 let friend_add c =
773 friend_add (as_client c)
775 let string_of_server s =
776 Printf.sprintf "%s:%d" (Ip.to_string s.server_ip) s.server_port
778 let set_client_name c name md4 =
779 if name <> c.client_name || c.client_md4 <> md4 then begin
780 c.client_name <- name;
781 c.client_md4 <- md4;
784 exception ClientFound of client
785 let find_client_by_name name =
787 H.iter (fun c ->
788 if c.client_name = name then raise (ClientFound c)
789 ) clients_by_kind;
790 raise Not_found
791 with ClientFound c -> c
793 let local_mem_stats level buf =
794 let client_counter = ref 0 in
795 let unconnected_unknown_clients = ref 0 in
796 let uninteresting_clients = ref 0 in
797 let aliased_clients = ref 0 in
798 let myconnected_clients = ref 0 in
799 let closed_connections = ref 0 in
800 let unlocated_client = ref 0 in
801 let bad_numbered_clients = ref 0 in
802 let disconnected_alias = ref 0 in
803 let dead_clients = ref 0 in
804 let buffers = ref 0 in
805 let waiting_msgs = ref 0 in
806 let connected_clients_by_num = Hashtbl.create 100 in
807 H.iter (fun c ->
809 if c.client_num <> num then begin
810 incr bad_numbered_clients;
812 let cc = Hashtbl.find clients_by_num c.client_num in
813 if cc.client_sock = None then incr disconnected_alias;
814 with _ -> incr dead_clients;
815 end;
817 let num = client_num c in
818 incr client_counter;
819 match c.client_source.DonkeySources.source_sock with
820 NoConnection -> begin
821 match c.client_kind with
822 Indirect_address _ -> incr unconnected_unknown_clients
823 | _ -> ()
825 | ConnectionWaiting _ -> ()
826 | Connection sock ->
827 let buf_len, nmsgs = TcpBufferedSocket.buf_size sock in
828 (try
829 Hashtbl.find connected_clients_by_num num
830 with _ ->
831 incr myconnected_clients;
832 waiting_msgs := !waiting_msgs + nmsgs;
833 buffers := !buffers + buf_len;
835 Printf.bprintf buf "%d: %6d/%6d\n" num
836 buf_len nmsgs *)
838 if TcpBufferedSocket.closed sock then
839 incr closed_connections;
840 ) clients_by_kind;
842 let bad_clients_in_files = ref 0 in
843 Hashtbl.iter (fun _ file ->
844 DonkeySources.iter_all_sources (fun s ->
845 match s.DonkeySources.source_sock with
846 NoConnection -> begin
847 match s.DonkeySources.source_uid with
848 Indirect_address _ -> incr bad_clients_in_files
849 | _ -> ()
851 | _ -> ()
852 ) file.file_sources;
853 ) files_by_md4;
855 Printf.bprintf buf "Clients: %d\n" !client_counter;
856 Printf.bprintf buf " Bad Clients: %d/%d\n" !unconnected_unknown_clients
857 !bad_clients_in_files;
858 Printf.bprintf buf " Read Buffers: %d\n" !buffers;
859 Printf.bprintf buf " Write Messages: %d\n" !waiting_msgs;
860 Printf.bprintf buf " Uninteresting clients: %d\n" !uninteresting_clients;
861 Printf.bprintf buf " Connected clients: %d\n" !myconnected_clients;
862 Printf.bprintf buf " Aliased clients: %d\n" !aliased_clients;
863 Printf.bprintf buf " Closed clients: %d\n" !closed_connections;
864 Printf.bprintf buf " Unlocated clients: %d\n" !unlocated_client;
865 Printf.bprintf buf " Bad numbered clients: %d\n" !bad_numbered_clients;
866 Printf.bprintf buf " Dead clients: %d\n" !dead_clients;
867 Printf.bprintf buf " Disconnected aliases: %d\n" !disconnected_alias;
869 Printf.bprintf buf "Number of old files: %d\n" (List.length !!old_files);
870 Printf.bprintf buf "Current files: %d\n" (List.length !current_files);
872 let counter = ref 0 in
873 UdpClientWHashtbl.iter (fun _ -> incr counter) udp_clients;
874 Printf.bprintf buf " udp_clients: %d\n" !counter;
876 Printf.bprintf buf " client_to_client_tags: %d\n" (List.length !client_to_client_tags);
877 Printf.bprintf buf " client_to_server_tags: %d\n" (List.length !client_to_server_tags);
878 Printf.bprintf buf " overnet_connectreply_tags: %d\n" (List.length !overnet_connectreply_tags);
879 Printf.bprintf buf " overnet_connect_tags: %d\n" (List.length !overnet_connect_tags);
880 Printf.bprintf buf " clients_root: %d\n" (List.length !clients_root);
881 Printf.bprintf buf " servers_list: %d\n" (List.length !servers_list);
882 Printf.bprintf buf " xs_servers_list: %d\n" (List.length !xs_servers_list);
883 Printf.bprintf buf " connected_server_list: %d\n" (List.length !connected_server_list);
884 Printf.bprintf buf " connecting_server_list: %d\n" (List.length !connecting_server_list);
885 Printf.bprintf buf " udp_servers_list: %d\n" (List.length !udp_servers_list);
886 Printf.bprintf buf " interesting_clients: %d\n" (List.length !interesting_clients);
887 Printf.bprintf buf " shared_files: %d\n" (List.length !shared_files);
888 Printf.bprintf buf " servers_by_key: %d\n" (Hashtbl.length servers_by_key);
889 Printf.bprintf buf " banned_ips: %d\n" (Hashtbl.length banned_ips);
890 Printf.bprintf buf " old_requests: %d\n" (Hashtbl.length old_requests);
891 Printf.bprintf buf " connected_clients: %d\n" (Hashtbl.length connected_clients);
892 Printf.bprintf buf " files_by_md4: %d\n" (Hashtbl.length files_by_md4);
893 Printf.bprintf buf " shared_files_info: %d\n" (Hashtbl.length shared_files_info);
894 Printf.bprintf buf " file_groups: %d\n" (Hashtbl.length file_groups);
895 Printf.bprintf buf " udp_servers_replies: %d\n" (Hashtbl.length udp_servers_replies);
896 Printf.bprintf buf " join_queue_by_md4: %d\n" (Hashtbl.length join_queue_by_md4);
897 Printf.bprintf buf " join_queue_by_id: %d\n" (Hashtbl.length join_queue_by_id);
899 (* let list = H.to_list clients_by_kind in *)
900 if level > 0 then begin
901 H.iter (fun c ->
902 Printf.bprintf buf "[%d ok: %s rating: %d]\n"
903 (client_num c)
904 (string_of_date (c.client_source.DonkeySources.source_age))
905 (* TODO: add connection state *)
906 c.client_rating;
907 ) clients_by_kind;
908 end;
911 let remove_client c =
912 client_remove (as_client c);
913 (* hashtbl_remove clients_by_kind c.client_kind c; *)
914 (* hashtbl_remove clients_by_name c.client_name c *)
918 let friend_remove c =
919 friend_remove (as_client c)
922 (* Parts stolen from update_master_servers. Maybe someone competent
923 enough reduces the redundant code produced here. *)
924 let last_connected_master () =
925 let server_list = connected_servers () in
926 let masters = ref [] in
927 List.iter (
928 fun s ->
929 if s.server_master then
930 match s.server_sock with
931 | Connection _ ->
932 masters := s :: !masters
933 | _ -> s.server_master <- false
934 ) server_list;
935 match !masters with
936 | s :: _ -> s
937 | [] -> raise Not_found
939 let last_connected_server () =
940 match !servers_list with
941 | s :: _ -> s
942 | [] ->
943 servers_list :=
944 Hashtbl.fold (fun key s l ->
945 s :: l
946 ) servers_by_key [];
947 match !servers_list with
948 [] -> raise Not_found
949 | s :: _ -> s
951 let string_of_file_state s =
952 match s with
953 | FileDownloading -> "File Downloading"
954 | FilePaused -> "File Paused"
955 | FileDownloaded -> "File Downloaded"
956 | FileShared -> "File Shared"
957 | FileCancelled -> "File Cancelled"
958 | FileNew -> "File New"
959 | FileAborted s -> Printf.sprintf "Aborted: %s" s
960 | FileQueued -> "File Queued"
962 let left_bytes = "MLDK"
964 let overnet_server_ip = ref Ip.null
965 let overnet_server_port = ref 0
967 let overnet_port_info = ref 0
968 let kademlia_port_info = ref 0
971 (*************************************************************
973 Define a function to be called when the "mem_stats" command
974 is used to display information on structure footprint.
976 **************************************************************)
978 let _ =
979 Heap.add_memstat "DonkeyGlobals" local_mem_stats
981 (*************************************************************
983 Save the state of the client positive queries for files
984 if a JoinQueue message was sent. Use this information if
985 an AvailableSlot message is received while not JoinQueue
986 message was sent (client_asked_for_slot false).
988 **************************************************************)
990 let client_id c =
991 match c.client_kind with
992 Direct_address (ip, port) -> (ip, port, zero)
993 | Indirect_address (server_ip, server_port, id, port, real_ip) ->
994 (server_ip, server_port, id)
995 | Invalid_address _ -> (Ip.null, 0, zero)
997 let save_join_queue c =
998 if c.client_file_queue <> [] then
999 let files = List.map (fun (file, chunks, _) ->
1000 file, Bitv.copy chunks
1001 ) c.client_file_queue in
1002 begin
1003 if c.client_debug then
1004 lprintf_nl "Saving %d files associated with %s"
1005 (List.length files) (Md4.to_string c.client_md4);
1006 Hashtbl.add join_queue_by_md4 c.client_md4 (files, last_time ());
1008 let id = client_id c in
1009 Hashtbl.add join_queue_by_id id (files, last_time ());
1010 with _ -> ()
1013 let clean_join_queue_tables () =
1014 let current_time = last_time () in
1016 let list = Hashtbl2.to_list2 join_queue_by_md4 in
1017 Hashtbl.clear join_queue_by_md4;
1018 List.iter (fun (key, ((v,time) as e)) ->
1019 if time + Date.half_hour_in_secs > current_time then
1020 Hashtbl.add join_queue_by_md4 key e
1021 ) list;
1023 let list = Hashtbl2.to_list2 join_queue_by_id in
1024 Hashtbl.clear join_queue_by_id;
1025 List.iter (fun (key, ((v,time) as e)) ->
1026 if time + Date.half_hour_in_secs > current_time then
1027 Hashtbl.add join_queue_by_id key e
1028 ) list
1030 let client_public_key = ref ""
1031 let key_check_started = ref false
1033 let _ =
1034 option_hook client_private_key (fun _ ->
1035 if Autoconf.donkey_sui_works () then
1036 begin
1037 if not (try String.sub !!client_private_key 0 4 = "MIIB" with e -> false) then
1038 if !key_check_started then
1039 begin
1040 let s1 =
1041 Printf.sprintf "can not create valid client_private_key, bad value found: %s"
1042 !!client_private_key in
1043 let s2 = "CryptoPP code seems not to work properly, do not use insane CFLAGS, exiting..." in
1044 Printf.eprintf "%s[EDK] %s\n%!" (log_time ()) s1;
1045 Printf.eprintf "%s[EDK] %s\n%!" (log_time ()) s2;
1046 lprintf_nl "%s" s1;
1047 lprintf_nl "%s" s2;
1048 exit 70
1050 else
1051 begin
1052 key_check_started := true;
1053 lprintf_nl "found bad client_private_key: %s, creating new key..." !!client_private_key;
1054 client_private_key =:= (DonkeySui.SUI.create_key ());
1056 else
1057 begin
1058 client_public_key := DonkeySui.SUI.load_key (!!client_private_key);
1063 let full_client_identifier c =
1064 Printf.sprintf "%s (%s%s) '%s'"
1065 (match c.client_kind with
1066 Indirect_address (server_ip, server_port, id, port, real_ip) ->
1067 Printf.sprintf "%s:%d%s[lowID %Ld, server:%s:%d]"
1068 (Ip.to_string real_ip) port
1069 (match c.client_country_code with | None -> "" | Some cc -> Printf.sprintf "(%d)" cc)
1070 id (Ip.to_string server_ip) server_port
1071 | Direct_address (ip,port) ->
1072 Printf.sprintf "%s:%d%s" (Ip.to_string ip) port
1073 (match c.client_country_code with | None -> "" | Some cc -> Printf.sprintf "(%d)" cc)
1074 | Invalid_address _ -> " invalid IP")
1075 (GuiTypes.client_software_short (brand_to_string_short c.client_brand) c.client_osinfo)
1076 (if c.client_emule_proto.emule_release = "" then "" else " " ^ c.client_emule_proto.emule_release)
1077 (String.escaped c.client_name)