patch #7305
[mldonkey.git] / src / networks / opennap / opennapServers.ml
blob43912e3560621dd286d74022657cacce7e935f6b
1 (* Copyright 2001, 2002 b8_bavard, b8_fee_carabine, INRIA *)
2 (*
3 This file is part of mldonkey.
5 mldonkey is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 mldonkey is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with mldonkey; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 open AnyEndian
21 open Printf2
22 open Md4
23 open CommonClient
24 open CommonSearch
25 open CommonServer
26 open CommonTypes
27 open CommonResult
28 open CommonComplexOptions
29 open CommonFile
30 open CommonGlobals
31 open Options
32 open BasicSocket
33 open TcpBufferedSocket
34 open OpennapTypes
35 open OpennapGlobals
36 open OpennapOptions
37 open OpennapComplexOptions
39 module DG = CommonGlobals
40 module DO = CommonOptions
41 module OP = OpennapProtocol
42 module OT = OpennapTypes
44 let end_of_search s =
45 match s.server_pending_searches with
46 [] -> ()
47 | _ :: tail ->
48 s.server_pending_searches <- tail;
49 match tail with
50 | (ss, f) :: _ -> f ss
51 | _ -> ()
53 let send_search fast s ss msg =
54 if not (List.mem_assoc ss s.server_pending_searches) then
55 let f ss =
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
63 [] ->
64 s.server_pending_searches <- [ss, f];
65 f ss
66 | first :: tail ->
67 if fast then
68 s.server_pending_searches <-
69 first :: (ss, f) :: s.server_pending_searches
70 else
71 s.server_pending_searches <- s.server_pending_searches @ [ss, f]
74 let rec remove_short list list2 =
75 match list with
76 [] -> List.rev list2
77 | s :: list ->
78 if String.length s < 5 then (* keywords should had list be 5 bytes *)
79 remove_short list list2
80 else
81 remove_short list (s :: list2)
83 let stem s =
84 let s = String.lowercase (String.copy s) in
85 for i = 0 to String.length s - 1 do
86 let c = s.[i] in
87 match c with
88 'a'..'z' | '0' .. '9' -> ()
89 | _ -> s.[i] <- ' ';
90 done;
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
97 List.iter (fun s ->
98 send_search false s ss t
99 ) !connected_servers
101 let recover_files () =
102 List.iter (fun file ->
103 let keywords =
104 match stem file.file_name with
105 [] | [_] ->
106 (* lprintf "Not enough keywords to recover %s\n" f.file_name; *)
107 [file.file_name]
108 | l -> l
110 ignore (send_query (Recover_file keywords) keywords)
111 ) !current_files;
114 let recover_files_from_server s =
115 do_if_connected s.server_sock (fun sock ->
116 List.iter (fun file ->
117 let keywords =
118 match stem file.file_name with
119 [] | [_] ->
120 (* lprintf "Not enough keywords to recover %s\n" f.file_name; *)
121 [file.file_name]
122 | l -> l
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
128 ) !current_files;
131 let new_nick s =
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 =
137 new_nick s;
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"; *)
147 List.iter (fun s ->
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";
157 }));
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;
170 with e ->
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)
178 r.result_size in
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;
182 end;
183 begin
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;
190 ) !sources;
191 end;
192 as_file file.file_file
194 let login_on_server s sock =
195 new_nick s;
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 =
208 new_nick s;
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
231 NoConnection -> ()
232 | ConnectionWaiting token ->
233 cancel_token token;
234 s.server_sock <- NoConnection
235 | Connection sock ->
237 (try close sock r with _ -> ());
238 decr nservers;
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 =
248 match event with
249 BASIC_EVENT (CLOSED r) -> disconnect_server s r
250 | _ -> ()
252 let client_to_server s t sock =
253 match t with
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;
271 try_nick s sock;
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;
288 (try
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;
301 M.freq = 0;
302 M.length = info.Mp3tag.duration;
306 ) shared_files;
308 with _ -> ());
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
323 begin
324 match s.server_searches with
325 None -> assert false
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 _) ->
333 begin
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;
343 with _ -> ()
347 | OP.BrowseUserReplyReq t ->
348 begin
349 match s.server_browse_queue with
350 c :: _ ->
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
355 None -> []
356 | Some rs -> rs in
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)
363 | _ -> ()
366 | OP.EndOfSearchReplyReq ->
367 lprintf "END OF SEARCH ON %s\n" s.server_desc;
368 begin
369 match s.server_searches with
370 None -> assert false
371 | Some (Normal_search q) ->
372 s.server_searches <- None;
373 end_of_search s
374 | Some (Recover_file _) ->
375 s.server_searches <- None;
376 end_of_search s
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
391 DR.nick = t.DA.nick;
392 DR.filename = t.DA.filename;
395 ) else (
396 lprintf "************** Can download directly *************\n";
397 let ip = t.DA.ip in
398 let port = t.DA.port in
399 c.client_addr <- Some (ip, port);
400 OpennapClients.connect_client c
403 | OP.BrowseUserReplyEndReq ->
404 begin
405 match s.server_browse_queue with
406 [] -> ()
407 | _ :: tail -> s.server_browse_queue <- tail
410 | OP.DownloadErrorReq t ->
411 begin
413 end;
414 let module DE = OP.DownloadError in
415 lprintf "?????????Download Error %s %s ???????????\n"
416 t.DE.nick t.DE.filename;
418 | _ ->
419 lprintf "################# UNUSED ###############\n";
420 OpennapProtocol.print t
422 let connect_server s =
423 if can_open_connection connection_manager then
424 let token =
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;
430 incr nservers;
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 <- [];
447 try_nick s sock;
448 (* try_login_on_server s sock; *)
449 s.server_sock <- Connection sock;
450 with e ->
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"; *)
455 decr nservers;
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
466 [] ->
467 servers_list := !current_servers;
468 if !servers_list = [] then raise Not_found;
469 connect_one_server ()
470 | s :: list ->
471 servers_list := list;
472 if DG.connection_can_try s.server_connection_control then
473 begin
474 match s.server_sock with
475 NoConnection -> connect_server s
476 | _ -> ()
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 ()
485 done
488 let ask_for_files () =
489 List.iter (fun file ->
490 List.iter (fun c ->
491 get_file_from_source c file
492 ) file.file_clients
493 ) !current_files;
497 let _ =
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
504 None -> ()
505 | Some sock ->
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;
514 server_remove s
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
541 type t = UDP | TCP
543 type cnx = {
544 ip1 : string;
545 port1 : int;
546 ip2 : string;
547 port2 : int;
548 packets_in : Buffer.t;
549 packets_out : Buffer.t;
551 let connections = Hashtbl.create 13
553 let rec iter s pos =
554 if s.[pos] = '\n' then
555 if s.[pos+1] = '\n' then pos+2
556 else
557 if s.[pos+1] = '\r' then
558 if s.[pos+2] = '\n' then pos+3
559 else iter s (pos+1)
560 else iter s (pos+1)
561 else
562 if s.[pos] = '\r' then
563 if s.[pos] = '\n' then
564 if s.[pos+1] = '\n' then pos+2
565 else
566 if s.[pos+1] = '\r' then
567 if s.[pos+2] = '\n' then pos+3
568 else iter s (pos+1)
569 else iter s (pos+1)
570 else
571 iter s (pos+1)
572 else iter s (pos+1)
574 let hescaped s =
575 String2.replace_char s '\r' ' ';s
577 let parse s_out s_in =
578 lprintf "OUTPUT:\n";
580 lprintf "INPUT:\n";
581 dump s_in
583 let commit () =
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";
596 let h1 = iter s 0 in
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";
602 let h1 = iter s 0 in
603 lprintf "Header 1: \n%s\n" (hescaped (String.sub s 0 h1));
604 end
605 else
606 lprintf "bad HTTP reply\n"*)
608 end else begin
609 parse
610 (Buffer.contents cnx.packets_out)
611 (Buffer.contents cnx.packets_in);
613 with
614 | e ->
615 lprintf "Exception %s\n" (Printexc2.to_string e)
616 ) connections
618 let new_packet (kind:t) (number:int) ip1 port1 ip2 port2 data =
619 match kind with
620 UDP ->
621 begin
624 lprintf "New packet (len=%d):\n%s\n"
625 (String.length data) (String.escaped data); *)
627 with e ->
628 (* lprintf "Could not parse UDP packet:\n"; *)
631 | TCP ->
633 lprintf "\nNew packet %s:%d -> %s:%d (len=%d):\n"
634 ip1 port1 ip2 port2
636 (String.length data);
637 dump 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;
647 with _ ->
649 let cnx = Hashtbl.find connections in_packet in
650 Buffer.add_string cnx.packets_in data
651 with _ ->
652 let cnx = {
653 ip1 = ip1;
654 port1 = port1;
655 ip2 = ip2;
656 port2 = port2;
657 packets_out = Buffer.create 100;
658 packets_in = Buffer.create 100;
659 } in
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
666 let j = len - i in
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;
670 dump data;
671 done; *)