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
28 open CommonComplexOptions
33 open TcpBufferedSocket
37 open OpennapComplexOptions
39 module DG
= CommonGlobals
40 module DO
= CommonOptions
41 module OP
= OpennapProtocol
42 module OT
= OpennapTypes
45 match s
.server_pending_searches
with
48 s
.server_pending_searches
<- tail
;
50 | (ss
, f
) :: _
-> f ss
53 let send_search fast s ss msg
=
54 if not
(List.mem_assoc ss s
.server_pending_searches
) then
56 do_if_connected s
.server_sock
(fun sock
->
57 lprintf
"SENDING SEARCH TO %s\n" s
.server_desc
;
58 s
.server_searches
<- Some ss
;
59 OP.debug_server_send sock
(OP.SearchReq msg
)
62 match s
.server_pending_searches
with
64 s
.server_pending_searches
<- [ss
, f];
68 s
.server_pending_searches
<-
69 first
:: (ss
, f) :: s
.server_pending_searches
71 s
.server_pending_searches
<- s
.server_pending_searches
@ [ss
, f]
74 let rec remove_short list list2
=
78 if String.length s
< 5 then (* keywords should had list be 5 bytes *)
79 remove_short list list2
81 remove_short list
(s
:: list2
)
84 let s = String.lowercase
(String.copy
s) in
85 for i
= 0 to String.length
s - 1 do
88 'a'
..'z'
| '
0'
.. '
9'
-> ()
91 remove_short (String2.split
s ' '
) []
93 let send_query ss words
=
94 let module S
= OP.Search
in
95 let t = { S.dummy_search
with
96 S.artist
= Some
(String2.unsplit words ' '
) } in
98 send_search false s ss
t
101 let recover_files () =
102 List.iter
(fun file
->
104 match stem file
.file_name
with
106 (* lprintf "Not enough keywords to recover %s\n" f.file_name; *)
110 ignore
(send_query (Recover_file
keywords) keywords)
114 let recover_files_from_server s =
115 do_if_connected
s.server_sock
(fun sock
->
116 List.iter
(fun file
->
118 match stem file
.file_name
with
120 (* lprintf "Not enough keywords to recover %s\n" f.file_name; *)
124 let module S
= OP.Search
in
125 let t = { S.dummy_search
with
126 S.artist
= Some
(String2.unsplit
keywords ' '
) } in
127 send_search false s (Recover_file
keywords) t
132 s.server_nick_num
<- s.server_nick_num
+ 1;
133 s.server_last_nick
<- if s.server_nick_num
= 0 then !!DO.global_login
else
134 Printf.sprintf
"%s[%d]" !!DO.global_login
s.server_nick_num
136 let try_nick s sock
=
138 OP.server_send sock
(OP.NickCheckReq
s.server_last_nick
)
141 let get_file_from_source c file
=
142 (* lprintf "GET FILE FROM SOURCE !!!!!!!!!!!!!!!!!!!!\n"; *)
144 if connection_can_try
c.client_connection_control
then begin
145 connection_try
c.client_connection_control
;
146 (* lprintf "Opennap.get_file_from_source not implemented\n"; *)
148 do_if_connected
s.server_sock
(fun sock
->
149 connection_failed
c.client_connection_control
;
151 (* emulate WinMX behavior *)
152 OP.debug_server_send sock
(OP.PrivateMessageReq
(
153 let module PM
= OP.PrivateMessage
in
155 PM.nick
= c.client_name
;
156 PM.message
= "//WantQueue";
159 OP.debug_server_send sock
(OP.DownloadRequestReq
(
160 let module DR
= OP.DownloadRequest
in
162 DR.nick
= c.client_name
;
163 DR.filename
= List.assq file
c.client_files
;
167 ) c.client_user
.user_servers
;
171 lprintf
"Exception %s in get_file_from_source\n"
172 (Printexc2.to_string e
)
175 let download_file hash
(r
: CommonTypes.result_info
) =
176 let file = new_file
(Md4.random
())
177 (List.hd r
.result_names
)
179 (* lprintf "DOWNLOAD FILE %s\n" f.file_name; *)
180 if not
(List.memq
file !current_files
) then begin
181 current_files
:= file :: !current_files
;
184 let sources = Hashtbl.find result_sources r
.result_num
in
185 List.iter
(fun (user
,filename
) ->
186 lprintf
"Adding source %s (%d servers)\n" user
.user_nick
187 (List.length user
.user_servers
);
188 let c = add_file_client
file user filename
in
189 get_file_from_source c file;
192 as_file
file.file_file
194 let login_on_server s sock
=
196 OP.server_send sock
(OP.NewUserLoginReq
(
197 let module NUL
= OP.NewUserLogin
in
199 NUL.nick
= s.server_last_nick
;
200 NUL.password
= !!client_password
;
201 NUL.port
= !!client_port
;
202 NUL.client_info
= !!client_info
;
203 NUL.link_type
= OT.LinkUnknown
;
204 NUL.email
= "nomail";
207 let try_login_on_server s sock
=
209 OP.server_send sock
(OP.LoginReq
(
210 let module NUL
= OP.Login
in
212 NUL.nick
= s.server_last_nick
;
213 NUL.password
= !!client_password
;
214 NUL.port
= !!client_port
;
215 NUL.client_info
= !!client_info
;
216 NUL.link_type
= OT.LinkCable
;
220 let update_source s t =
221 let module Q = OP.SearchReply in
222 let c = new_source s t.Q.nick t.Q.ip in
224 c.client_link <- t.Q.link_type;
228 let disconnect_server s r
=
230 match s.server_sock
with
232 | ConnectionWaiting token
->
234 s.server_sock
<- NoConnection
237 (try close sock r
with _
-> ());
239 (* lprintf "%s:%d CLOSED received by server\n"
240 (Ip.to_string s.server_ip) s.server_port;
242 DG.connection_failed
(s.server_connection_control
);
243 s.server_sock
<- NoConnection
;
244 set_server_state
s (NotConnected
(r
, -1));
245 connected_servers
:= List2.removeq
s !connected_servers
247 let server_handler s sock event
=
249 BASIC_EVENT
(CLOSED r
) -> disconnect_server s r
252 let client_to_server s t sock
=
255 | OP.ErrorReq error
->
256 lprintf
"SERVER %s:%d %s\n" (Ip.to_string
s.server_ip
)
257 s.server_port
s.server_net
;
258 lprintf
"ERROR FROM SERVER: %s\n" error
;
260 | OP.MessageReq error
->
261 let msg = Printf.sprintf
"From server %s [%s:%d]: %s\n"
262 s.server_desc
(Ip.to_string
s.server_ip
) s.server_port error
in
263 CommonEvent.add_event
(Console_message_event
msg)
266 | OP.NickAlreadyUsedReq
->
267 (* lprintf "NICK NAME ALREADY USED %d\n" s.server_nick; *)
268 try_login_on_server s sock
;
270 s.server_nick <- s.server_nick + 1;
274 | OP.NickInvalidReq
->
275 (* lprintf "NICK NAME IS INVALID %s\n" !!DO.client_name; *)
278 | OP.NickUnusedReq
->
279 lprintf
"NICK NAME ACCEPTED\n";
280 login_on_server s sock
282 | OP.LoginAckReq mail
->
283 set_rtimeout sock
Date.half_day_in_secs
;
284 lprintf
"***** CONNECTED %s ******\n" mail
;
285 set_server_state
s (Connected
(-1));
286 connected_servers
:= s :: !connected_servers
;
289 let nshared_files = ref 0 in
290 Hashtbl.iter
(fun _ sh
->
291 if !nshared_files > !!max_shared_files
then raise Exit
;
293 let (tag
,info
) = sh
.shared_format
in
294 OP.debug_server_send sock
(OP.AddFileReq
(
295 let module M
= OP.AddFile
in
297 M.filename
= sh
.shared_codedname
;
298 M.md5
= Md5.to_string
Md5.null
;
299 M.size
= Int64.of_int info
.Mp3tag.filesize
;
300 M.bitrate
= info
.Mp3tag.bitrate
;
302 M.length
= info
.Mp3tag.duration
;
310 recover_files_from_server s
312 | OP.ServerStatsReq
t ->
313 DG.connection_ok
s.server_connection_control
;
314 let module SS
= OP.ServerStats
in
315 s.server_nfiles
<- Int64.of_int
t.SS.files
;
316 s.server_nusers
<- Int64.of_int
t.SS.users
;
317 s.server_size
<- t.SS.size
;
318 server_must_update
(as_server
s.server_server
)
320 | OP.SearchReplyReq
t ->
321 lprintf
"*** SearchReplyReq ***\n";
322 let module SR
= OP.SearchReply
in
324 match s.server_searches
with
326 | Some
(Normal_search q
) ->
327 let user = new_user
(Some
s) t.SR.nick
in
328 user.user_link
<- t.SR.link_type
;
329 let result = new_result
(basename
t.SR.filename
) t.SR.size
in
330 add_source
result user t.SR.filename
;
331 CommonInteractive.search_add_result
true q
result;
332 | Some
(Recover_file _
) ->
335 let file = find_file
(basename
t.SR.filename
) t.SR.size
in
336 lprintf
"++++++++++ RECOVER %s ++++++++\n" t.SR.filename
;
338 let result = new_result
(basename
t.SR.filename
) t.SR.size
in
339 let user = new_user
(Some
s) t.SR.nick
in
340 let c = add_file_client
file user t.SR.filename
in
341 add_source
result user t.SR.filename
;
342 get_file_from_source c file;
347 | OP.BrowseUserReplyReq
t ->
349 match s.server_browse_queue
with
351 let module BU
= OP.BrowseUserReply
in
352 let r = new_result
(basename
t.BU.filename
) t.BU.size
in
353 add_source
r c.client_user
t.BU.filename
;
354 let rs = match c.client_all_files
with
357 if not
(List.memq
r rs) then begin
358 c.client_all_files
<- Some
(r :: rs);
359 client_new_file
(as_client
c.client_client
)
360 (Filename.dirname
t.BU.filename
)
366 | OP.EndOfSearchReplyReq
->
367 lprintf
"END OF SEARCH ON %s\n" s.server_desc
;
369 match s.server_searches
with
371 | Some
(Normal_search q
) ->
372 s.server_searches
<- None
;
374 | Some
(Recover_file _
) ->
375 s.server_searches
<- None
;
379 | OP.DownloadAckReq
t ->
381 let module DA
= OP.DownloadAck
in
382 lprintf
"DownloadAckReq %s !!!!!!!!!!!!!!!!!!!!!!!!\n" t.DA.nick
;
384 let c = new_client
t.DA.nick
in
386 if t.DA.port
= 0 then (
387 lprintf
"************** Must download indirectly *************\n";
388 OP.debug_server_send sock
(OP.AlternateDownloadRequestReq
(
389 let module DR
= OP.DownloadRequest
in
392 DR.filename
= t.DA.filename
;
396 lprintf
"************** Can download directly *************\n";
398 let port = t.DA.port in
399 c.client_addr
<- Some
(ip, port);
400 OpennapClients.connect_client
c
403 | OP.BrowseUserReplyEndReq
->
405 match s.server_browse_queue
with
407 | _
:: tail
-> s.server_browse_queue
<- tail
410 | OP.DownloadErrorReq
t ->
414 let module DE
= OP.DownloadError
in
415 lprintf
"?????????Download Error %s %s ???????????\n"
416 t.DE.nick
t.DE.filename
;
419 lprintf
"################# UNUSED ###############\n";
420 OpennapProtocol.print
t
422 let connect_server s =
423 if can_open_connection connection_manager
then
425 add_pending_connection connection_manager
(fun token ->
426 s.server_sock
<- NoConnection
;
428 (* lprintf "CONNECTING ONE SERVER\n"; *)
429 DG.connection_try
s.server_connection_control
;
431 let sock = TcpBufferedSocket.connect
token "opennap to server"
432 (Ip.to_inet_addr
s.server_ip
) s.server_port
433 (server_handler s) (* Mftp_comm.server_msg_to_string*) in
434 set_server_state
s Connecting
;
435 set_read_controler
sock DG.download_control
;
436 set_write_controler
sock DG.upload_control
;
438 set_reader
sock (OpennapProtocol.opennap_handler
(client_to_server s));
439 set_rtimeout
sock !!server_connection_timeout
;
440 set_handler
sock (BASIC_EVENT RTIMEOUT
) (fun s ->
441 close
s Closed_for_timeout
443 s.server_nick_num
<- 0;
444 s.server_searches
<- None
;
445 s.server_pending_searches
<- [];
446 s.server_browse_queue
<- [];
448 (* try_login_on_server s sock; *)
449 s.server_sock
<- Connection
sock;
451 lprintf
"%s:%d IMMEDIAT DISCONNECT %s"
452 (Ip.to_string
s.server_ip
) s.server_port
453 (Printexc2.to_string e
);
454 (* lprintf "DISCONNECTED IMMEDIATLY\n"; *)
456 s.server_sock
<- NoConnection
;
457 set_server_state
s (NotConnected
(Closed_connect_failed
, -1));
458 DG.connection_failed
s.server_connection_control
461 s.server_sock
<- ConnectionWaiting
token
463 let rec connect_one_server () =
464 if can_open_connection connection_manager
then
465 match !servers_list
with
467 servers_list
:= !current_servers
;
468 if !servers_list
= [] then raise Not_found
;
469 connect_one_server ()
471 servers_list
:= list
;
472 if DG.connection_can_try
s.server_connection_control
then
474 match s.server_sock
with
475 NoConnection
-> connect_server s
480 let connect_servers () =
481 (* lprintf "CONNECT SERVERS\n"; *)
482 if !nservers
< !!max_connected_servers
then
483 for i
= !nservers
to !!max_connected_servers
do
484 connect_one_server ()
488 let ask_for_files () =
489 List.iter
(fun file ->
491 get_file_from_source c file
498 server_ops
.op_server_connect
<- connect_server;
499 server_ops
.op_server_disconnect
<- (fun s ->
500 disconnect_server s Closed_by_user
);
502 (* server_ops.op_server_query_users <- (fun s -> *)
503 match s.server_sock
with
506 server_send
sock (GetNickListReq
)
508 (* server_ops.op_server_users <- (fun s -> *)
509 List2.tail_map
(fun u
-> as_user u
.user_user
) s.server_users
512 server_ops
.op_server_remove
<- (fun s ->
513 disconnect_server s Closed_by_user
;
517 network
.op_network_connected
<- (fun _ -> !connected_servers
!= []);
518 network
.op_network_save_complex_options
<- (fun _ -> ());
519 network
.op_network_update_options
<- (fun _ -> ());
520 network
.op_network_save_sources
<- (fun _ -> ())
523 If you run a packet sniffer on WPNP (WinMX Peer Network Protocol) packets,
524 it will soon become apparent that these packets are not transmitted in cleartext.
525 For instance, search terms cannot directly be found in the packet stream.
527 The reason for this is that the packets are encoded using a simple XOR based algorithm.
528 It cannot really be called encryption, since there is no key except packet length.
530 The encoding algorithm in question first xors the first byte with the last, then repeatedly
531 xors a byte with its preceding byte, moving from the next-to-first byte to the last one,
532 one byte at a time. This is done five times. The procedure varies slightly the first time,
533 where the first byte is not XORed with the last byte, but rather with the packet[1] length.
536 external winmx_encode
: string -> int -> unit = "winmx_encode_ml"
537 external winmx_decode
: string -> int -> unit = "winmx_decode_ml"
539 module Pandora
= struct
548 packets_in
: Buffer.t;
549 packets_out
: Buffer.t;
551 let connections = Hashtbl.create
13
554 if s.[pos
] = '
\n'
then
555 if s.[pos
+1] = '
\n'
then pos
+2
557 if s.[pos
+1] = '
\r'
then
558 if s.[pos
+2] = '
\n'
then pos
+3
562 if s.[pos
] = '
\r'
then
563 if s.[pos
] = '
\n'
then
564 if s.[pos
+1] = '
\n'
then pos
+2
566 if s.[pos
+1] = '
\r'
then
567 if s.[pos
+2] = '
\n'
then pos
+3
575 String2.replace_char
s '
\r' ' '
;s
577 let parse s_out s_in
=
584 Hashtbl.iter (fun _ cnx
->
586 lprintf
"CONNECTION %s:%d --> %s:%d\n"
587 cnx
.ip1 cnx
.port1 cnx
.ip2 cnx
.port2
;
589 let s = Buffer.contents cnx
.packets_out
in
590 let len = String.length
s in
592 if String2.starts_with
s "GET" ||
593 String2.starts_with
s "POST" then begin
595 lprintf "Http connect to\n";
597 lprintf "Header 1: \n%s\n" (hescaped (String.sub s 0 h1));
599 let s = Buffer.contents cnx.packets_in in
600 if String2.starts_with s "HTTP" then begin
601 lprintf "Http connected from\n";
603 lprintf "Header 1: \n%s\n" (hescaped (String.sub s 0 h1));
606 lprintf "bad HTTP reply\n"*)
610 (Buffer.contents cnx
.packets_out
)
611 (Buffer.contents cnx
.packets_in
);
615 lprintf
"Exception %s\n" (Printexc2.to_string e
)
618 let new_packet (kind
:t) (number
:int) ip1 port1 ip2 port2 data
=
624 lprintf "New packet (len=%d):\n%s\n"
625 (String.length data) (String.escaped data); *)
628 (* lprintf "Could not parse UDP packet:\n"; *)
633 lprintf "\nNew packet %s:%d -> %s:%d (len=%d):\n"
636 (String.length data);
640 let out_packet = (ip1
, port1
, ip2
, port2
) in
641 let in_packet = (ip2
, port2
, ip1
, port1
) in
644 let cnx = Hashtbl.find
connections out_packet in
645 Buffer.add_string
cnx.packets_out data
;
649 let cnx = Hashtbl.find
connections in_packet in
650 Buffer.add_string
cnx.packets_in data
657 packets_out
= Buffer.create
100;
658 packets_in
= Buffer.create
100;
660 Hashtbl.add
connections out_packet cnx;
661 Buffer.add_string
cnx.packets_out data
);
664 let len = String.length data in
665 for i = 0 to len -1 do
667 let data = String.sub data i j in
668 winmx_decode data (String.length data);
669 lprintf "DECODED DATA [%d,%d]:\n" i j;