1 (* Copyright 2001, 2002 b8_bavard, b8_fee_carabine, INRIA *)
3 This file is part of mldonkey.
5 mldonkey is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 mldonkey is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with mldonkey; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 open TcpBufferedSocket
30 open CommonInteractive
34 open CommonComplexOptions
45 let log_prefix = "[EDK]"
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
56 **************************************************************)
58 let network = CommonNetwork.new_network
"ED2K" "Donkey"
59 ~comment
:(if Autoconf.donkey_sui_works
() then "SUI" else "noSUI")
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 (*************************************************************************)
123 (*************************************************************************)
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
)
137 let module E
= DonkeyProtoClient.EmuleClientInfo
in
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
()
151 let xs_last_search = ref (-1)
153 let zone_size = Int64.of_int
(180 * 1024)
154 let block_size = 9728000L
157 (* let nchunks = Int64.to_int (Int64.pred file_size // block_size) + 1 in *)
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(!)
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
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) *)
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 (*************************************************************************)
196 (*************************************************************************)
198 module H
= Weak.Make
(struct
200 let hash c
= Hashtbl.hash c
.client_kind
202 let equal x y
= x
.client_kind
= y
.client_kind
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
) =
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
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
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 (*************************************************************************)
257 (* Global functions *)
259 (*************************************************************************)
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
271 Hashtbl.remove table key
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 () =
299 None
-> failwith
"No UDP socket"
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
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
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
327 set_file_best_name
file best_name "" 0
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
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)
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
;
347 if !verbose_share
then
348 lprintf_nl "New file with not changed different filename %s and %s"
349 file.file_diskname file_diskname
;
351 if Unix32.destroyed
(file_fd file)
353 && file.file_diskname
= file_diskname
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
;
362 if !verbose_share
then
363 lprintf_nl "New file with md4: %s" (Md4.to_string md4
);
366 (* emulate_sparsefiles does not work, temporarily disabled
368 (* Don't use this for shared files ! *)
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
)
375 Unix32.create_sparsefile file_diskname writable
379 Unix32.create_diskfile file_diskname writable
381 failwith
(Printf.sprintf
"Error: %s" (Printexc2.to_string e
))
384 if file_size = Int64.zero
then
386 Unix32.getsize file_diskname
388 failwith
"Zero length file ?"
392 if file_size <> zero
&& writable
then (* do not truncate if not writable *)
395 Unix32.ftruncate64
t file_size !!create_file_sparse
400 lprintf_nl "Unix32.remove %s exception %s"
401 (file_diskname
) (Printexc2.to_string e
));
403 failwith
(Printf.sprintf
"file size %s is too big, exception: %s"
404 (size_of_int64
file_size) (Printexc2.to_string e
))
407 let md4s = if file_size < block_size then [md4
] else [] in
409 file_diskname
= file_diskname
;
410 file_file
= file_impl
;
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
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
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;
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;
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
480 match Ip.to_ints ip
with
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
493 if !verbose_connect
then
494 lprintf_nl "%s:%d blocked: %s" (Ip.to_string ip
) port reason
;
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
503 let new_server ip port
=
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
;
513 server_server
= server_impl
;
514 server_next_udp
= last_time
();
516 server_cid
= None
(* client_ip None *);
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
();
526 server_nfiles
= None
;
527 server_nusers
= None
;
529 server_description
= "";
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
();
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;
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;
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;
564 server_auxportslist
= "";
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
=
583 Hashtbl.find
servers_by_key key
585 let remove_server ip port
=
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
595 | ConnectionWaiting token
-> cancel_token token
597 TcpBufferedSocket.shutdown sock Closed_by_user
)
600 let check_client_country_code c
=
601 if Geoip.active
() then
602 match c
.client_country_code
with
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
613 let module D
= DonkeyProtoClient
in
615 client_client
= client_impl
;
616 client_upload
= None
;
617 client_kind
= Direct_address
(Ip.null
, 0);
618 client_source
= DonkeySources.dummy_source
;
620 client_country_code
= None
;
621 client_md4
= Md4.null
;
622 client_download
= None
;
623 client_file_queue
= [];
626 client_all_files
= None
;
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;
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
();
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;
656 dummy_client_impl
with
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
)
670 client_client
= client_impl
;
672 client_upload
= None
;
675 client_country_code
= cc
;
676 client_md4
= Md4.null
;
677 client_download
= None
;
678 client_file_queue
= [];
681 client_all_files
= None
;
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;
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
();
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
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 =
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
) =
733 | Indirect_address
(server_ip
, server_port
, id
, port
, real_ip
) ->
734 Indirect_address
(server_ip
, server_port
, id
, 0, Ip.null
)
736 raise
(ClientFound
c)
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 *)
747 | Indirect_address
(_,_,_,_,ip_real
) ->
749 match c.client_kind
with
750 | Indirect_address
(_,_,_,_,old_ip) ->
751 if old_ip = Ip.null
then None
else Some
old_ip
754 if ip_real
<> Ip.null
then c.client_kind
<- key;
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)
764 let create_client = ()
767 client_type (as_client c)
769 let set_client_type c t=
770 set_client_type (as_client c) t
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
;
784 exception ClientFound
of client
785 let find_client_by_name name
=
788 if c.client_name
= name
then raise
(ClientFound
c)
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
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;
817 let num = client_num c in
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
825 | ConnectionWaiting
_ -> ()
827 let buf_len, nmsgs
= TcpBufferedSocket.buf_size sock
in
829 Hashtbl.find
connected_clients_by_num num
831 incr
myconnected_clients;
832 waiting_msgs := !waiting_msgs + nmsgs
;
833 buffers := !buffers + buf_len;
835 Printf.bprintf buf "%d: %6d/%6d\n" num
838 if TcpBufferedSocket.closed sock
then
839 incr
closed_connections;
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
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
902 Printf.bprintf buf
"[%d ok: %s rating: %d]\n"
904 (string_of_date
(c.client_source
.DonkeySources.source_age
))
905 (* TODO: add connection state *)
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
929 if s.server_master
then
930 match s.server_sock
with
932 masters := s :: !masters
933 | _ -> s.server_master
<- false
937 | [] -> raise Not_found
939 let last_connected_server () =
940 match !servers_list with
944 Hashtbl.fold
(fun key s l
->
947 match !servers_list with
948 [] -> raise Not_found
951 let string_of_file_state s =
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 **************************************************************)
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 **************************************************************)
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
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
());
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
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
1030 let client_public_key = ref ""
1031 let key_check_started = ref false
1034 option_hook client_private_key
(fun _ ->
1035 if Autoconf.donkey_sui_works
() then
1037 if not
(try String.sub
!!client_private_key
0 4 = "MIIB" with e
-> false) then
1038 if !key_check_started then
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;
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
());
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
)