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
19 (* The function handling the cooperation between two clients. Most used
20 functions are defined in downloadOneFile.ml *)
32 open CommonComplexOptions
39 open TcpBufferedSocket
42 open DonkeyComplexOptions
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
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
)
75 if !!update_server_list_client
then
77 ignore
(check_add_server serverIP serverPort
);
78 Indirect_address
(serverIP
, serverPort
, id_of_ip ip
, 0, Ip.null
)
83 if Ip.usable ip
then begin
84 let uid = Direct_address
(ip
, tcp_port
) in
86 cc := (DonkeySources.find_source_by_uid
uid).DonkeySources.source_country_code
;
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
100 let s = DonkeySources.create_source_by_uid
uid !cc in
101 DonkeySources.set_request_result
s file
.file_sources File_new_source
;
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
=
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
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
122 lprintf_nl
"banned: %s %s" msg
(full_client_identifier 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
(
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
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";
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"))
163 record.last_request
<- last_time
();
166 Hashtbl.add old_requests
(client_num c
, file_num file
)
167 { last_request
= last_time
(); nwarnings
= 0; }
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
178 Hashtbl.remove banned_ips
ip;
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
();
188 let module M
= DonkeyProtoClient
in
189 let module Q
= M.AvailableSlot
in
190 M.AvailableSlotReq
Q.t
);
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
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 "
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
225 (nwritten sock
) (nread sock
)
226 (if c
.client_banned
then "banned" else "")
227 c
.client_requests_received
228 c
.client_requests_sent
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
241 | ConnectionWaiting token
->
243 c
.client_source
.DonkeySources.source_sock
<- NoConnection
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
;
261 c
.client_slot
<- SlotNotAsked
;
263 (* clean_client_zones: clean all structures related to downloads when
264 a client disconnects *)
266 match c
.client_download
with
269 CommonSwarming.unregister_uploader up
;
270 c
.client_download
<- None
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
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
296 let new_udp_client c group
=
297 match c
.client_kind
with
298 Indirect_address
_ | Invalid_address
_ -> ()
299 | Direct_address
(ip, port
) ->
301 udp_client_last_conn
= last_time
();
303 udp_client_port
= port
;
304 udp_client_can_receive
= client_can_receive c
309 let uc = UdpClientWHashtbl.find udp_clients
uc in
310 uc.udp_client_last_conn
<- last_time
();
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
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
331 DonkeyProtoCom.udp_send
(get_udp_sock
())
336 let find_sources_in_groups c md4
=
337 if !!propagate_sources
&&
338 (match c
.client_brand
with
339 Brand_mldonkey1
| Brand_overnet
-> false
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 *)
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 *)
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
_ -> ()
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);
363 let module Q
= DonkeyProtoClient.Sources
in
364 DonkeyProtoClient.SourcesReq
{
369 client_send_if_possible c sock
msg
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";
384 Udp.QueryLocationReplyUdpReq
(
385 let module Q
= DonkeyProtoServer.QueryLocationReply
in
388 Q.locs
= [{ Q.ip = ip; Q.port
= port
}];
392 new_udp_client c
group
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
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
431 up
.up_pos
<- begin_pos
;
432 up
.up_end_chunk
<- end_pos
;
433 up
.up_chunks
<- [chunk];
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
442 if md4.[5] = Char.chr
14 && md4.[14] = Char.chr
111 then
444 else if md4.[5] = 'M'
&& md4.[14] = 'L'
then
447 if DonkeySources.source_brand c
.client_source
then
448 Brand_overnet
else Brand_edonkey
)
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
=
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
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" ->
589 let sub = fst
mod_array.(i
) in
590 if (String2.subcontains
s sub) then
591 c
.client_brand_mod
<- snd
mod_array.(i
)
594 iter 0 (Array.length
mod_array)
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))
618 Printf.sprintf
"%d.%d.%d" maj min up
621 let parse_compatible_client num old_brand
=
628 | 40 -> Brand_shareaza
629 | 5 -> Brand_emuleplus
630 | 6 -> Brand_hydranode
631 | 10 -> Brand_mldonkey3
634 | 240 -> Brand_verycd
637 let parse_mod_version s c
=
640 let sub = fst
mod_array.(i
) in
641 if (String2.subcontains
s sub) then
642 c
.client_brand_mod
<- snd
mod_array.(i
)
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
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
)
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
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
)
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
767 DonkeyProtoClient.EmuleClientInfo.protversion
= 255;
768 DonkeyProtoClient.EmuleClientInfo.tags
= [
769 string_tag
(Field_KNOWN
"os_info") (String2.upp_initial
Autoconf.system
);
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 ())
794 (DonkeyProtoUdp.QueryCallUdpReq {
796 Q.port = !!donkey_port;
801 let module M
= DonkeyProtoServer
in
802 let module C
= M.QueryID
in
807 if !!update_server_list_client
then
809 ignore
(check_add_server
ip port
);
813 let shared_of_file file
=
814 match file
.file_shared
with
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
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
)
854 (* ask for more sources *)
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
874 Check if the bitmap returned by a client contains a chunk that has not
877 let is_useful_client file chunks
=
878 match file
.file_swarmer
with
881 let bitmap = CommonSwarming.chunks_verified_bitmap swarmer
in
882 VB.existsi
(fun i
s ->
885 | VB.State_missing
| VB.State_partial
-> true
886 | VB.State_complete
| VB.State_verified
-> false)
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
897 lprintf_nl
"Compared to: %s" (VB.to_string
(CommonSwarming.chunks_verified_bitmap swarmer
));
901 if file_size file
<= block_size
902 then Bitv.create
1 true
904 if Bitv.length
chunks = 0
905 then Bitv.create file
.file_nchunks
true
907 if Bitv.length
chunks <> file
.file_nchunks
then begin
909 lprintf_nl
"number of chunks is different %d/%d for %s(%s), size %Ld on %s"
912 (file_best_name file
)
913 (Md4.to_string file
.file_md4
)
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 ?
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
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
949 if Ip.matches c
.client_ip !!nolimit_ips
then t lor client_nolimit_tag
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
;
963 client_must_update c
;
964 c
.client_checked
<- true;
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
975 if !result = Int64.zero
then
976 (match s.server_cid
with
978 | Some i
-> if not
(low_id i
) then
979 result := int64_of_rip i
;
981 ) (connected_servers
());
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
994 ip := int64_of_rip (peer_ip
s);
1000 match c
.client_public_key
with
1005 match c
.client_public_key
with
1009 let send_signature c
=
1010 if has_pubkey c
then
1013 let ip = ref Int64.zero
in
1014 let ip_type = ref 0 in
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
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;
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;
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;
1050 let module M
= DonkeyProtoClient
in
1051 let module E
= M.EmuleSecIdentStateReq
in
1052 client_send c
(M.EmuleSecIdentStateReq
{
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
);
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
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
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)
1086 if !verbose_msg_clients
then
1087 lprintf_nl
"%s [process_mule_info] [verify_ident]" (full_client_identifier 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
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
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
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
))
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
);
1142 M.ConnectReplyReq
t ->
1143 if !verbose_msg_clients
then begin
1144 lprintf_nl
"[HELLOANSWER] %s" (full_client_identifier c
);
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
1157 TcpBufferedSocket.close sock
(Closed_for_error
"Reply of Invalid Client");
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
->
1169 { tag_name
= Field_KNOWN
"name"; tag_value
= String
s } ->
1170 set_client_name c
s t.CR.md4
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
1187 match t.CR.server_info
with
1188 Some
(ip, port) -> if !!update_server_list_client
then safe_add_server
ip port
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
1208 DonkeySources.set_request_result
s m File_found
;
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
);
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"
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
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
;
1276 ) file.file_sources
;
1277 if !sources <> [] then
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);
1283 M.EmuleRequestSourcesReplyReq
{
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
1296 if !verbose_msg_clients
then
1297 lprintf_nl
"Received ViewFilesReply";
1299 let list = ref [] in
1301 match result_of_file f
.f_md4 f
.f_tags
with
1304 (* TODO let r = DonkeyIndexer.index_result_no_filter r in *)
1305 client_new_file c
r;
1308 c
.client_all_files
<- Some
!list;
1309 client_must_update c
1312 lprintf_nl ~exn
"ViewFilesReply"
1315 | M.AvailableSlotReq
_ ->
1316 set_lifetime sock
active_lifetime;
1317 set_rtimeout sock
!!queued_timeout
;
1318 (* how long should we wait for a block ? *)
1320 match c.client_block with
1323 lprintf "[QUEUED WITH BLOCK]\n";
1324 DonkeyOneFile.clean_client_zones c;
1327 match c
.client_download
with
1329 if !verbose_download
then
1330 lprintf_nl
"Clear download";
1331 CommonSwarming.clear_uploader_ranges
up;
1332 c
.client_download
<- None
1334 match c
.client_file_queue
with
1337 if c
.client_slot
= SlotNotAsked
then
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";
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 *)
1356 if c
.client_debug
then
1357 lprintf_nl
"AvailableSlot received, but not file to download!";
1358 (* TODO: ask for the files now *)
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))
1374 match c
.client_brand
with
1375 | Brand_mldonkey3
->
1376 if Fifo.length upload_clients
>= !!max_upload_slots
then
1378 if c
.client_source
.source_sock
<> None
&&
1379 c
.client_brand
= Brand_mldonkey3
then raise Exit
)
1382 if Fifo.length upload_clients
>= !!max_upload_slots
then
1386 (* set_rtimeout sock !!upload_timeout; *)
1387 set_lifetime sock
(float_of_int
Date.day_in_secs
);
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
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
1411 | Some f
-> CommonFile.file_best_name f
);
1414 | M.OutOfPartsReq
_ ->
1415 set_client_state c
(Connected
0);
1417 match c
.client_download
with
1420 if !verbose_download
then
1421 lprintf_nl
"Slot closed during download";
1422 CommonSwarming.clear_uploader_ranges
up
1424 c
.client_session_downloaded
<- 0L;
1425 c
.client_slot
<- SlotNotAsked
;
1426 (* OK, the slot is closed, but what should we do now ????? *)
1428 match c
.client_file_queue
with
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
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)
1458 if file.file_computed_md4s
= [||] then begin
1460 let module M
= DonkeyProtoClient
in
1461 let module C
= M.QueryChunkMd4
in
1462 M.QueryChunkMd4Req
file.file_md4
);
1468 | M.EmuleFileDescReq
t ->
1470 match c
.client_last_file_req_md4
with
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;
1490 | M.QueryChunksReplyReq
t ->
1491 let module Q
= M.QueryChunksReply
in
1494 let file = find_file
t.Q.md4 in
1495 received_client_bitmap c
file t.Q.chunks
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 ->
1506 let module Q
= M.QueryChunkMd4Reply
in
1507 let file = find_file
t.Q.md4 in
1509 let module Q
= M.QueryChunkMd4Reply
in
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
1522 CommonSwarming.set_verifier swarmer
1523 (Verification
[| Ed2k
file.file_md4
|])
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
)
1529 if Array.length
t.Q.chunks <> file.file_nchunk_hashes
then begin
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)
1534 (file_best_name
file)
1535 (Md4.to_string
file.file_md4
)
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.
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
);
1560 file.file_computed_md4s
<- md4s;
1561 match file.file_swarmer
with
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");
1584 let module Q
= M.EmuleCompressedPart
in
1585 let comp = match c
.client_comp
with
1589 comp_pos
= t.Q.statpos
;
1590 comp_total
= Int64.to_int
t.Q.newsize
;
1594 c
.client_comp
<- Some
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
1611 let pos = iter tail
in
1612 let len = String.length b
in
1613 String.blit b
0 s 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
;
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
;
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");
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
);
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
1665 let files = DonkeyShare.all_shared
() in
1666 let published_files = ref [] in
1668 let filename = file_best_name f
in
1669 if not
(String2.starts_with
filename "hidden.") then
1670 published_files := f
:: !published_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
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
1697 let files = DonkeyShare.all_shared
() in
1698 let published_files = ref [] in
1700 let filename = file_best_name f
in
1701 if not
(String2.starts_with
filename "hidden.") then
1702 published_files := f
:: !published_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
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
1728 M.SayReq
"[AUTOMATED WARNING] Please, update your MLdonkey client to at least version 2.7.0!");
1732 count_filerequest c
;
1733 let file = find_file
md4 in
1734 (match file.file_shared
with
1735 None
-> raise Not_found
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);
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 *)
1754 match t.M.QueryFile.emule_extension
with
1756 | Some
(chunks, _) ->
1757 received_client_bitmap c
file chunks
1759 if file_state
file = FileDownloading
then
1760 DonkeySources.query_files c
.client_source
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)
1768 lprintf_nl ~exn
"QueryFileReq"
1771 | M.EmuleSignatureReq
t ->
1772 if sec_ident_enabled
() then
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;
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
1791 ip_type := t.Q.ip_type;
1792 if (!ip_type == 20) (* || isLowid *) then
1793 id := int64_of_rip (peer_ip sock
)
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
);
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;
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");
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
1829 let module Q
= M.EmulePublicKeyReq
in
1830 (match c
.client_public_key
with
1831 Some
s -> if s <> t then
1833 if !verbose_msg_clients
then begin
1834 lprintf_nl
"%s [EPubKeyReq] [Key is different!]" (full_client_identifier c
);
1836 c
.client_public_key
<- None
;
1839 if !verbose_msg_clients
then begin
1840 lprintf_nl
"%s [EPubKeyReq] [Key matches]" (full_client_identifier c
);
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
;
1848 if (c
.client_req_challenge
<> Int64.zero
) then send_signature c
;
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
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
);
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
;
1874 then send_public_key c
;
1876 then send_signature c
;
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
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);
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
1907 let module Q
= M.Sources
in
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);
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
1926 | M.SayReq
s when (!is_not_spam
) s ->
1927 (* FIXME: add logging *)
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
1952 match file.file_computed_md4s
with
1953 [||] -> () (* should not happen *)
1956 let module Q
= M.QueryChunkMd4Reply
in
1957 M.QueryChunkMd4ReplyReq
{
1958 Q.md4 = file.file_md4
;
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
1975 let file = find_file
t in
1977 match file.file_swarmer
with
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
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;
1999 let module Q
= M.QueryChunksReply
in
2000 M.QueryChunksReplyReq
{
2001 Q.md4 = file.file_md4
;
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
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
;
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
2043 client_upload_lifetime :=
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)
2052 last_time
() > c
.client_connect_time
+ !client_upload_lifetime + 5 * prio
2056 if client_received_enough c
then
2057 if Intmap.length
!CommonUploads.pending_slots_map
= 0 then
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)
2064 DonkeyOneFile.remove_client_slot c
;
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
2078 | _ :: q
-> up.up_flying_chunks
<- q
);
2083 up_pos
= Int64.zero
;
2084 up_end_chunk
= Int64.zero
;
2086 up_flying_chunks
= [];
2087 up_current
= Int64.zero
;
2089 up_waiting
= old_up
.up_waiting
;
2090 }, old_up
.up_waiting
2094 up_pos
= Int64.zero
;
2095 up_end_chunk
= Int64.zero
;
2097 up_flying_chunks
= [];
2098 up_current
= ((t.Q.start_pos1
++ t.Q.end_pos1
) // (2L ** block_size
));
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
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
2112 DonkeyOneFile.remove_client_slot c
;
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
2123 if !verbose_upload
then lprintf_nl
"QueryBloc treated"
2125 | M.NoSuchFileReq
t ->
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
;
2138 if !verbose_unknown_messages
then begin
2139 lprintf_nl
"Unused client message %s:" (full_client_identifier c
);
2143 let client_handler c sock event
=
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;
2160 let client_handler2 c sock event
=
2162 BASIC_EVENT
(CLOSED
s) -> decr
DonkeySources.indirect_connections
2165 Some c
-> client_handler c sock event
2168 BASIC_EVENT
(LTIMEOUT
| RTIMEOUT
) ->
2169 close sock Closed_for_timeout
2173 let init_connection sock
ip =
2174 TcpBufferedSocket.setsock_iptos_throughput sock
;
2177 Ip.matches
ip !!nolimit_ips
2179 if not
nolimit then begin
2180 TcpBufferedSocket.set_read_controler sock download_control
;
2181 TcpBufferedSocket.set_write_controler sock upload_control
;
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
)
2205 set_handler sock (BASIC_EVENT RTIMEOUT) (fun s ->
2206 connection_delay c.client_connection_control;
2207 printf_string "-!C";
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
;
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)
2228 (match cc with | None
-> "" | Some
cc -> Printf.sprintf
"(%d)" cc);
2235 if !verbose_msg_clients
then begin
2236 lprintf_nl
"[HELLO] %s" (Ip.to_string
real_ip);
2239 let module CR
= M.Connect
in
2241 if not
(is_valid_client t.CR.md4 ) then
2243 TcpBufferedSocket.close sock
(Closed_for_error
"Connect of Invalid Client");
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
->
2252 { tag_name
= Field_KNOWN
"name"; tag_value
= String
s } -> name := s
2257 if low_id
t.CR.ip then
2258 match t.CR.server_info
with
2260 Invalid_address
(!name, Md4.to_string
t.CR.md4)
2262 if Ip.usable
ip then
2263 Indirect_address
(ip, port, id_of_ip
t.CR.ip, t.CR.port, real_ip)
2265 Invalid_address
(!name, Md4.to_string
t.CR.md4)
2267 if Ip.usable
t.CR.ip then
2268 Direct_address
(t.CR.ip, t.CR.port)
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
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;
2287 match c.client_source
.DonkeySources.source_sock
with
2289 c.client_source
.DonkeySources.source_sock
<- Connection sock
;
2290 c.client_connected
<- true;
2292 init_client_after_first_message sock
c
2294 | ConnectionWaiting token
->
2296 c.client_source
.DonkeySources.source_sock
<- Connection sock
;
2297 c.client_connected
<- true;
2299 init_client_after_first_message sock
c
2302 close sock
(Closed_for_error
"already connected");
2303 c.client_connected
<- false;
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
;
2316 match t.CR.server_info
with
2317 Some
(ip, port) -> if !!update_server_list_client
then safe_add_server
ip port
2319 if overnet
then begin
2320 lprintf_nl
"incoming Overnet client";
2321 DonkeySources.set_source_brand
c.client_source overnet
;
2325 (* Lugdunum servers are not interested in our EmuleClientInfo *)
2326 if supports_eep c.client_brand
&& not server
then
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
)
2336 let module M
= DonkeyProtoClient
in
2337 let module C
= M.Connect
in
2338 if DonkeySources.source_brand
c.client_source
then
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
;
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
;
2363 incr_activity_indirect_connections c;
2365 check_stolen_hash c sock
t.CR.md4;
2367 finish_client_handshake c sock
;
2370 | M.NewUserIDReq
_ ->
2371 lprintf_nl
"NewUserIDReq: "; M.print
m;
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);
2382 if !verbose_unknown_messages
then
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
();
2388 close sock
(Closed_for_error
"bad connecting message");
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
2402 match c.client_source
.DonkeySources.source_sock
with
2403 ConnectionWaiting
_ | Connection
_ ->
2404 (* Already connected ! *)
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)
2416 (client_handler c) (*client_msg_to_string*) in
2419 incr_activity_connections c;
2421 init_connection sock ip;
2423 (* The lifetime of the client socket is now half an hour, and
2424 can be increased by AvailableSlotReq, BlocReq, QueryBlocReq
2426 set_lifetime
sock active_lifetime;
2428 c.client_checked
<- false;
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
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
2452 if not
(!!force_high_id
|| !!force_client_high_id
)
2453 && low_id server_cid
2454 && Ip.any
!= server_cid
2461 let module M
= DonkeyProtoClient
in
2462 let module C
= M.Connect
in
2463 if DonkeySources.source_brand
c.client_source
then
2465 C.md4 = overnet_md4
;
2466 C.ip = client_ip None
;
2467 C.port = !!overnet_port
;
2468 C.tags
= !overnet_connect_tags
;
2470 C.server_info
= Some
(!overnet_server_ip
,
2471 !overnet_server_port
);
2472 C.left_bytes
= left_bytes
;
2476 C.md4 = !!client_md4
;
2477 C.ip = send_this_id;
2478 C.port = !!donkey_port
;
2479 C.tags
= !client_to_client_tags
;
2481 C.server_info
= Some
(server_ip, server_port
);
2482 C.left_bytes
= left_bytes
;
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
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,
2527 add_source file l
.Q.ip l
.Q.port s.server_ip s.server_port
2529 with Not_found
-> ()
2531 let matches_3 l
ip =
2532 let rec iter l
(a
,b
,c,d
) =
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
=
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"
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
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)
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"
2583 if accept_connection then
2588 incr
DonkeySources.indirect_connections
;
2589 let token = create_token connection_manager
in
2591 TcpBufferedSocket.create
token "donkey client connection" s
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. *)
2601 if can_open_connection connection_manager
then
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"
2614 lprintf_nl ~exn
"client_connection_handler";
2623 (*************************************************************************)
2625 (* Stubs for CommonSources *)
2627 (*************************************************************************)
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
2644 let bitmap = CommonSwarming.chunks_verified_bitmap swarmer
in
2646 Bitv.init
(VB.length
bitmap)
2647 (fun i
-> VB.get
bitmap i
= VB.State_verified
)
2649 let ncompletesources = if extendedrequest > 1 then
2651 Some
(chunks, ncompletesources)
2655 DonkeyProtoCom.client_send
c (
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
()))
2672 lprintf_nl ~exn
"query_source"
2675 DonkeySources.functions
.DonkeySources.function_connect
<-
2680 let c = new_client s_uid s_cc
in
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;
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
<-
2703 let file = find_file
(Md4.of_string file_uid
) in
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
);
2726 lprintf_nl ~exn
"add_location"
2729 DonkeySources.functions
.DonkeySources.function_remove_location
<- (fun
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
);
2741 lprintf_nl ~exn
"remove_location for file_md4 %s"