Ocaml/FAQ/Internet Services
Материал из Wiki.crossplatform.ru
(Различия между версиями)
ViGOur (Обсуждение | вклад)
(Новая страница: «== 18. Internet Services == === Simple DNS Lookups === <source lang="lisp">#load "unix.cma";; let () = try let addresses = Unix.gethostbyname name in le…»)
(Новая страница: «== 18. Internet Services == === Simple DNS Lookups === <source lang="lisp">#load "unix.cma";; let () = try let addresses = Unix.gethostbyname name in le…»)
Текущая версия на 20:49, 24 ноября 2010
Содержание |
[править] 18. Internet Services
[править] Simple DNS Lookups
#load "unix.cma";; let () = try let addresses = Unix.gethostbyname name in let addresses = Array.map Unix.string_of_inet_addr addresses.Unix.h_addr_list in (* addresses is an array of IP addresses *) Array.iter print_endline addresses with Not_found -> Printf.printf "Can't resolve %s\n" name (*-----------------------------*) let () = try let host = Unix.gethostbyaddr (Unix.inet_addr_of_string address) in let name = host.Unix.h_name in (* name is the hostname ("www.perl.com") *) print_endline name with Not_found -> Printf.printf "Can't resolve %s\n" address (*-----------------------------*) let () = try let host = Unix.gethostbyaddr (Unix.inet_addr_of_string address) in let name = host.Unix.h_name in try let addresses = Unix.gethostbyname name in let addresses = Array.map Unix.string_of_inet_addr addresses.Unix.h_addr_list in Array.iter print_endline addresses; let found = List.mem address (Array.to_list addresses) in print_endline (if found then "found" else "not found") with Not_found -> Printf.printf "Can't look up %s\n" name with Not_found -> Printf.printf "Can't look up %s\n" address (*-----------------------------*) #!/usr/bin/ocaml (* mxhost - find mx exchangers for a host *) (* Though there is an experimental new DNS resolver for OCaml called Netdns, it does not yet support resolving MX records. For now, we'll use Net::DNS through perl4caml until a better solution is available. *) #directory "+perl";; #load "perl4caml.cma";; let _ = Perl.eval "use Net::DNS" let host = Sys.argv.(1) let res = Perl.call_class_method "Net::DNS::Resolver" "new" [] let mx = Perl.call_array ~fn:"mx" [res; Perl.sv_of_string host] let () = if mx = [] then Printf.eprintf "Can't find MX records for %s (%s)\n" host (Perl.string_of_sv (Perl.call_method res "errorstring" [])) let () = List.iter (fun record -> let preference = Perl.call_method record "preference" [] in let exchange = Perl.call_method record "exchange" [] in Printf.printf "%s %s\n" (Perl.string_of_sv preference) (Perl.string_of_sv exchange)) mx (*-----------------------------*) #!/usr/bin/ocaml (* hostaddrs - canonize name and show addresses *) #load "unix.cma";; let name = Sys.argv.(1) let hent = Unix.gethostbyname name let () = Printf.printf "%s => %s\n" hent.Unix.h_name (* in case different *) (String.concat " " (Array.to_list (Array.map Unix.string_of_inet_addr hent.Unix.h_addr_list)))
[править] Being an FTP Client
(* The Netclient package from Ocamlnet provides an event-driven FTP client. This client does not currently support uploading. Ocamlnet is available here: http://projects.camlcity.org/projects/ocamlnet.html This recipe assumes it has been installed with findlib. *) #use "topfind";; #require "netclient";; (* Create an FTP client instance. *) let ftp = new Ftp_client.ftp_client () (* Build and execute a chain of FTP methods. *) let () = ftp#add (new Ftp_client.connect_method ~host:"127.0.0.1" ()); ftp#add (new Ftp_client.login_method ~user:"anonymous" ~get_password:(fun () -> "user@example.com") ~get_account:(fun () -> "anonymous") ()); ftp#add (new Ftp_client.walk_method (`Dir "/pub")); let ch = new Netchannels.output_channel (open_out "output.txt") in ftp#add (new Ftp_client.get_method ~file:(`Verbatim "index.txt") ~representation:`Image ~store:(fun _ -> `File_structure ch) ()); ftp#run () (*-----------------------------*) (* If an error occurs, it will be exposed by the "state" property. *) let () = match ftp#state with | `Error (Ftp_client.FTP_error (Unix.Unix_error (e, _, _))) -> Printf.eprintf "Error: %s\n%!" (Unix.error_message e) | _ -> () (*-----------------------------*) (* To determine the current working directory, send invoke the `PWD command and inspect the result in a callback. *) let () = ftp#add (new Ftp_client.invoke_method ~command:`PWD ~process_result:(fun state (code, message) -> Printf.printf "I'm in the directory %s\n%!" message) ()) (*-----------------------------*) (* Use mkdir_method and rmdir_method to make and remove directories from the remote server. Use the optional ~onerror argument to specify an error handler. *) let () = ftp#add ~onerror:(fun e -> Printf.eprintf "Can't create /ocaml: %s\n%!" (Printexc.to_string e)) (new Ftp_client.mkdir_method (`Verbatim "/pub/ocaml")) (*-----------------------------*) (* Use a list_method to get a list of files in a remote directory. *) let () = let buffer = Buffer.create 256 in let ch = new Netchannels.output_buffer buffer in ftp#add ~onsuccess:(fun () -> print_endline (Buffer.contents buffer)) ~onerror:(fun e -> Printf.eprintf "Can't get a list of files in /pub: %s\n%!" (Printexc.to_string e)) (new Ftp_client.list_method ~dir:(`Verbatim "/pub") ~representation:`Image ~store:(fun _ -> `File_structure ch) ()) (*-----------------------------*) (* Use `QUIT followed by ftp#abort to close the connection and exit the event loop. *) let () = ftp#add (new Ftp_client.invoke_method ~command:`QUIT ~process_result:(fun _ _ -> ftp#abort ()) ())
[править] Sending Mail
(* Use Netsendmail, part of the Netstring package that comes with Ocamlnet, to send mail through a command-line mailer program. *) #use "topfind";; #require "netstring";; let () = Netsendmail.sendmail ~mailer:"/usr/sbin/sendmail" (* defaults to "/usr/lib/sendmail" *) (Netsendmail.compose ~from_addr:(from_name, from_address) ~to_addrs:[(to_name, to_address)] ~subject:subject body) (*-----------------------------*) (* You can also open a pipe directly to sendmail. *) #load "unix.cma";; let () = let sendmail = Unix.open_process_out "/usr/lib/sendmail -oi -t -odq" in output_string sendmail "\ From: User Originating Mail <me@host> To: Final Destination <you@otherhost> Subject: A relevant subject line Body of the message goes here, in as many lines as you like. "; ignore (Unix.close_process_out sendmail)
[править] Reading and Posting Usenet News Messages
(* There is no NNTP library available for OCaml. With a little preparation, we can easily use the one that comes with Perl using perl4caml (http://merjis.com/developers/perl4caml) *) #directory "+perl";; #load "perl4caml.cma";; module NNTP = struct open Perl let _ = eval "use Net::NNTP" (* Returned by "list" method so that newsgroups stay sorted. *) module GroupMap = Map.Make(String) (* Wrapper for Net::NNTP class. *) class nntp host = let nntp = call_class_method "Net::NNTP" "new" [sv_of_string host] in (* Raise a Failure exception if we couldn't connect. *) let () = if sv_is_undef nntp then failwith (string_of_sv (eval "$!")) in (* Helper function to transform nullable string arrays to OCaml. *) let maybe_string_list sv = if sv_is_undef sv then raise Not_found else List.map string_of_sv (list_of_av (deref_array sv)) in object (self) val nntp = nntp method group name = match call_method_array nntp "group" [sv_of_string name] with | [narticles; first; last; name] -> (int_of_sv narticles, int_of_sv first, int_of_sv last, string_of_sv name) | _ -> raise Not_found method head msgid = maybe_string_list (call_method nntp "head" [sv_of_int msgid]) method body msgid = maybe_string_list (call_method nntp "body" [sv_of_int msgid]) method article msgid = maybe_string_list (call_method nntp "article" [sv_of_int msgid]) method postok () = bool_of_sv (call_method nntp "postok" []) method post lines = let lines = List.map sv_of_string lines in if (sv_is_undef (call_method nntp "post" lines)) then failwith (string_of_sv (eval "$!")) method list () = let hv = deref_hash (call_method nntp "list" []) in let map = ref GroupMap.empty in List.iter (fun (name, info) -> map := GroupMap.add name (match list_of_av (deref_array info) with | [last; first; flags] -> (int_of_sv last, int_of_sv first, string_of_sv flags) | _ -> assert false) !map) (assoc_of_hv hv); !map method quit () = ignore (call_method nntp "quit" []) end end (*-----------------------------*) (* Connect to an NNTP server by creating an "nntp" object. *) let server = try new NNTP.nntp "news.west.cox.net" with Failure s -> Printf.eprintf "Can't connect to news server: %s\n" s; exit 1 (*-----------------------------*) (* Select a newsgroup and retrieve its stats. *) let (narticles, first, last, name) = try server#group "misc.test" with Not_found -> Printf.eprintf "Can't select misc.test\n"; exit 1 (*-----------------------------*) (* Get the headers from the last article. *) let headers = try server#head last with Not_found -> Printf.eprintf "Can't get headers from article %d in %s\n" last name; exit 1 (*-----------------------------*) (* Get the body from the last article. *) let body = try server#head last with Not_found -> Printf.eprintf "Can't get body from article %d in %s\n" last name; exit 1 (*-----------------------------*) (* Get the headers and body from the last article. *) let article = try server#head last with Not_found -> Printf.eprintf "Can't get article from article %d in %s\n" last name; exit 1 (*-----------------------------*) (* Determine if posting is allowed with this server. *) let () = if not (server#postok ()) then Printf.eprintf "Server didn't tell me I could post.\n" (*-----------------------------*) (* Post a message. *) let () = begin try server#post lines with Failure s -> Printf.eprintf "Can't post: %s\n" s; exit 1 end (*-----------------------------*) (* Get the complete list of newsgroups. *) let () = let groupmap = server#list () in NNTP.GroupMap.iter (fun group (last, first, flags) -> if flags = "y" then (* I can post to [group] *) ()) groupmap
[править] Reading Mail with POP3
(* Use Netpop, which is part of Ocamlnet. *) #use "topfind";; #require "pop";; (* To create a Netpop client, you need to look up the server address and build a network connection first. Netpop uses wrappers called Netchannels to abstract the input and output channels. *) let inet_addr = (Unix.gethostbyname mail_server).Unix.h_addr_list.(0) let addr = Unix.ADDR_INET (inet_addr, Netpop.tcp_port) let ic, oc = Unix.open_connection addr let pop = new Netpop.client (new Netchannels.input_channel ic) (new Netchannels.output_channel oc) let () = pop#user username; pop#pass password (* Messages are retreived as a hashtable from message IDs to tuples, each tuple containing the message size in bytes and a string of server-specific extension data. *) let messages = pop#list () let () = Hashtbl.iter (fun msgid (size, ext) -> let message = pop#retr msgid in (* message is a Netchannels.in_obj_channel *) pop#dele msgid) messages (*-----------------------------*) (* Use pop#apop instead of pop#user/pop#pass to avoid sending passwords in plaintext across the network. *) let () = pop#apop username password (*-----------------------------*) (* Get a message by number and print it to the console. *) let () = Printf.printf "Retrieving %d : %!" msgnum; try let message = pop#retr msgnum in print_newline (); print_endline (Netchannels.string_of_in_obj_channel message) with Netpop.Err_status e -> Printf.printf "failed (%s)\n%!" e (*-----------------------------*) (* Gracefully tear down the connection. *) let () = pop#quit (); Unix.shutdown_connection ic; close_out oc
[править] Simulating Telnet from a Program
(* To simulate a Telnet client with OCaml, you can use the Telnet_client module from Ocamlnet's "netclient" package. This module is written in an asynchronous style, so you will need to create event handlers to process the Telnet events that occur: data, end of file, timeout, and the sending and receiving of options (also known as "do", "don't", "will", and "won't". *) #use "topfind";; #require "netclient";; open Telnet_client (* This class wraps the Telnet session for convenience in defining event handlers and chaining them together. *) class session ~host ~port ~username ~password ~prompt ~timeout = object (self) (* Telnet_client.telnet_session instance to wrap. *) val telnet = new telnet_session (* Initial on-data handler, which will be redefined later. *) val mutable process = fun _ -> () (* Initialize the Telnet session. *) initializer telnet#set_connection (Telnet_connect (host, port)); telnet#set_options {connection_timeout=timeout; verbose_connection=false; verbose_input=false; verbose_output=false}; telnet#set_callback self#on_input; telnet#set_exception_handler self#on_exception; telnet#attach (); process <- self#start (* Build an input callback that checks for a regular expression match in the input and calls a callback function if the match is positive. *) method waitfor pat cb = let rex = Pcre.regexp pat in fun data -> if Pcre.pmatch ~rex data then cb data (* Enqueue a line of data and flush the output queue. *) method write data = Queue.add (Telnet_data data) telnet#output_queue; Queue.add (Telnet_data "\n") telnet#output_queue; telnet#update () (* Handle first input: wait for a login prompt and then invoke self#send_username to send the username. *) method start = self#waitfor "ogin:" self#send_username (* Send the username and wait for the password prompt. *) method send_username data = self#write username; process <- self#waitfor "assword:" self#send_password (* Send the password and wait to see if we succeeded. *) method send_password data = self#write password; process <- self#verify_login (* Determine if the login was a success or a failure. Abort with an exception on failure; call self#logged_in on success. *) method verify_login data = if Pcre.pmatch ~pat:"incorrect" data then failwith "Login failed" else if Pcre.pmatch ~pat:"^\\s*$" data then () (* ignore blank lines *) else self#logged_in data (* Logged in successfully. Wait for a prompt if necessary and call self#run_ls to send the first command. *) method logged_in data = process <- self#waitfor prompt self#run_ls; self#waitfor prompt self#run_ls data (* Do a directory listing and wait for results. *) method run_ls data = self#write "/bin/ls -1"; process <- self#gather_files (* This variable will buffer the results of the "ls" command. *) val mutable files = "" (* Buffer the filenames printed out from the "ls" command and print them out once we get a prompt. *) method gather_files data = if Pcre.pmatch ~pat:prompt data then begin files <- Pcre.replace ~pat:"^/bin/ls -1\\s*" files; Printf.printf "Files: %s\n%!" (String.concat ", " (Pcre.split ~pat:"\\s+" files)); self#run_top data end else files <- files ^ data (* Run another command until we get a prompt and then call self#close to close the connection. *) method run_top data = self#write "top -n1 -b"; process <- self#waitfor prompt self#close (* Close the connection by sending an EOF. *) method close data = Queue.add Telnet_eof telnet#output_queue (* When we receive an EOF, exit the program. *) method on_eof () = prerr_endline "EOF"; exit 0 (* If a timeout event is received, exit with an error code. *) method on_timeout () = prerr_endline "Timeout"; exit 1 (* Print any thrown exceptions to standard error. *) method on_exception exn = prerr_endline (Printexc.to_string exn) (* This is the main error handler, which dispatches on Telnet_client events. *) method on_input got_synch = while not (Queue.is_empty telnet#input_queue) do let tc = Queue.take telnet#input_queue in match tc with | Telnet_data data -> process data | Telnet_eof -> self#on_eof () | Telnet_timeout -> self#on_timeout () | Telnet_will _ | Telnet_wont _ | Telnet_do _ | Telnet_dont _ -> (* The telnet_session handles these events. Calling this method is necessary. *) telnet#process_option_command tc | _ -> () done (* Run the Telnet session by calling the "run" method on the underling telnet_session instance. *) method run = telnet#run end (* Create an instance of our custom session class. *) let session = new session ~host:"localhost" ~port:23 ~username:"test" ~password:"pleac" ~prompt:"\\$ $" ~timeout:10. (* Start the session. *) let () = session#run ()
[править] Pinging a Machine
#!/usr/bin/ocaml (* ping - send and receive ICMP echo packets *) (* There do not appear to be any libraries available for pinging servers from OCaml, ICMP or otherwise. In this recipe, we will make a diversion from the Perl recipe, which simply determines if a host is up, and instead write a lookalike for the "ping" shell command. We might as well, if we're going to all the trouble of building ICMP packets directly. *) (* Import Unix and enable threads using findlib for convenience. *) #use "topfind";; #require "unix";; #thread;; (* The Packet module defines a data type and operations for building, parsing, and checking the integrity of ICMP packets. *) module Packet = struct exception Invalid_length of int exception Invalid_checksum of int * int (* type' and code define the ICMP message type. An echo message has type'=8, code=0, and an echo reply has type'=0, code=0. The id is a unique identifier for the current process to help distinguish between replies for other processes. seq is the sequence number, which is usually incremented with each message. data is the message body whose contents depend on the type of message. *) type t = { type' : int; code : int; id : int; seq : int; data : string } (* Define a convenience function for constructing packets. *) let make ?(type'=8) ?(code=0) ~id ~seq data = {type'=type'; code=code; id=id; seq=seq; data=data} (* Calculate a checksum for a message by adding its contents, two bytes at a time, folding the high order bits into the low order bits, and taking the logical complement. The result will be an int with 16-bit precision. *) let checksum s = let num_bytes = String.length s in let num_shorts = num_bytes / 2 in let rec sum_shorts i sum = if i < num_shorts then let short = Int32.of_int (int_of_char s.[i * 2] lsl 8 + int_of_char s.[i * 2 + 1]) in sum_shorts (i + 1) (Int32.add sum short) else sum in let sum = sum_shorts 0 0l in let sum = if num_bytes mod 2 = 1 then Int32.add sum (Int32.of_int (int_of_char s.[num_bytes - 1] lsl 8)) else sum in let sum = Int32.add (Int32.shift_right sum 16) (Int32.logand sum 0xffffl) in Int32.to_int (Int32.logand (Int32.lognot (Int32.add (Int32.shift_right sum 16) sum)) 0xffffl) (* Convert a packet to a string that can be sent over a socket. *) let to_string {type'=type'; code=code; id=id; seq=seq; data=data} = let b = Buffer.create 20 in Buffer.add_char b (char_of_int type'); Buffer.add_char b (char_of_int code); Buffer.add_char b '\000'; (* checksum hi *) Buffer.add_char b '\000'; (* checksum lo *) Buffer.add_char b (char_of_int (id lsr 8 land 0xff)); Buffer.add_char b (char_of_int (id land 0xff)); Buffer.add_char b (char_of_int (seq lsr 8 land 0xff)); Buffer.add_char b (char_of_int (seq land 0xff)); Buffer.add_string b data; let packet = Buffer.contents b in let sum = checksum packet in packet.[2] <- char_of_int (sum lsr 8 land 0xff); packet.[3] <- char_of_int (sum land 0xff); packet (* Parse a string into a packet structure. If the string is less than 8 bytes long, an Invalid_length exception will be raised. If the checksum does not match the contents, an Invalid_checksum exception will be raised. *) let of_string s = if String.length s < 8 then raise (Invalid_length (String.length s)); let s' = String.copy s in s'.[2] <- '\000'; s'.[3] <- '\000'; let sum = int_of_char s.[2] lsl 8 + int_of_char s.[3] in let sum' = checksum s' in if sum <> sum' then raise (Invalid_checksum (sum, sum')); {type'=int_of_char s.[0]; code=int_of_char s.[1]; id=int_of_char s.[4] lsl 8 + int_of_char s.[5]; seq=int_of_char s.[6] lsl 8 + int_of_char s.[7]; data=String.sub s 8 (String.length s - 8)} end (* Define a data structure for the message body of our echo requests. *) type payload = { timestamp : float; data : string } (* Send a single ICMP echo request to the given socket and address. *) let ping socket sockaddr id seq = let payload = Marshal.to_string {timestamp=Unix.gettimeofday (); data="abcdefghijklmnopqrstuvwxyz0123456"} [] in let message = Packet.to_string (Packet.make ~id ~seq payload) in ignore (Unix.sendto socket message 0 (String.length message) [] sockaddr) (* Loop forever waiting for echo replies, printing them to the console along with their hostname, IP, and round-trip time. *) let pong socket id = let buffer = String.make 256 '\000' in while true do let length, sockaddr = Unix.recvfrom socket buffer 0 (String.length buffer) [] in let response = Packet.of_string (String.sub buffer 20 (length - 20)) in match sockaddr, response with | Unix.ADDR_INET (addr, port), {Packet.type'=0; code=0; id=id'; seq=seq; data=data} when id = id' -> let host_entry = Unix.gethostbyaddr addr in let payload = Marshal.from_string data 0 in Printf.printf "%d bytes from %s (%s): icmp_seq=%d time=%.3f ms\n%!" (String.length data) host_entry.Unix.h_name (Unix.string_of_inet_addr addr) seq ((Unix.gettimeofday () -. payload.timestamp) *. 1000.) | _ -> () done (* Read hostname from command line. *) let host = if Array.length Sys.argv <> 2 then (Printf.eprintf "Usage: %s host\n" Sys.argv.(0); exit 1) else Sys.argv.(1) (* Use DNS to find the IP address and canonical name. *) let name, addr = try let h = Unix.gethostbyname host in h.Unix.h_name, h.Unix.h_addr_list.(0) with Not_found -> Printf.eprintf "%s: unknown host %s\n" Sys.argv.(0) host; exit 2 (* Make sure we are running as root, since this is required to open a socket with SOCK_RAW and send ICMP packets. *) let () = if Unix.getuid () <> 0 then (Printf.eprintf "%s: icmp ping requires root privilege\n" Sys.argv.(0); exit 3) (* Start the ping loop. *) let () = Printf.printf "PING %s (%s)\n" name (Unix.string_of_inet_addr addr); (* Build a socket and destination address. *) let proto = (Unix.getprotobyname "icmp").Unix.p_proto in let socket = Unix.socket Unix.PF_INET Unix.SOCK_RAW proto in let sockaddr = Unix.ADDR_INET (addr, 0) in (* Use the PID as the ID for packets, and create a counter for the sequence number. *) let id = Unix.getpid () in let seq = ref 0 in (* Start a background thread to print the echo replies. *) ignore (Thread.create (pong socket) id); (* Loop forever sending echo requests and sleeping. *) while true do incr seq; ping socket sockaddr id !seq; Unix.sleep 1 done
[править] Using Whois to Retrieve Information from the InterNIC
(* WHOIS servers depend on the TLD, and their output formats are informal, inconsistent, and completely different from server to server. This makes a general solution very large and ad-hoc. The Net::Whois package, on which the original Perl recipe was based, no longer works since WHOIS servers started redirecting to other servers for most of the information. Since no libraries are available for this task, we will do a WHOIS lookup manually using sockets. This example shows how to perform a WHOIS lookup for the "sourceforge.net" domain, and probably will not work without modification for domains under any other TLD. *) #load "unix.cma";; #load "str.cma";; let domain_name = "sourceforge.net" let whois_server = "whois.internic.net" let service = Unix.getservbyname "whois" "tcp" let ltrim = let re = Str.regexp "^[ \r\n\t\x00\x0B]*" in Str.global_replace re "" let () = (* Connect to the parent server to find the redirect. *) let host = Unix.gethostbyname whois_server in let socket_in, socket_out = Unix.open_connection (Unix.ADDR_INET (host.Unix.h_addr_list.(0), service.Unix.s_port)) in output_string socket_out domain_name; output_string socket_out "\n"; flush socket_out; let whois_redirect_regexp = Str.regexp "Whois Server: \\(.*\\)" in let whois_redirect = ref "" in begin try while true do let line = ltrim (input_line socket_in) in if Str.string_match whois_redirect_regexp line 0 then whois_redirect := Str.matched_group 1 line done with End_of_file -> Unix.shutdown_connection socket_in end; if !whois_redirect = "" then failwith "Couldn't find WHOIS redirect"; (* Connect to the real server and get the WHOIS data. *) let host = Unix.gethostbyname !whois_redirect in let socket_in, socket_out = Unix.open_connection (Unix.ADDR_INET (host.Unix.h_addr_list.(0), service.Unix.s_port)) in output_string socket_out domain_name; output_string socket_out "\n"; flush socket_out; let domain_name_regexp = Str.regexp "Domain name: \\(.*\\)" in let domain_name = ref "" in let registrant_regexp = Str.regexp "Registrant:" in let registrant_name = ref "" in let registrant_address = ref [] in let registrant_country = ref "" in let contact_regexp = Str.regexp "\\(.*\\) Contact:" in let contacts = ref [] in begin try while true do let line = ltrim (input_line socket_in) in if Str.string_match domain_name_regexp line 0 then domain_name := Str.matched_group 1 line else if Str.string_match registrant_regexp line 0 then begin (* Read registrant data. *) registrant_name := ltrim (input_line socket_in); let finished = ref false in while not !finished do let line = ltrim (input_line socket_in) in if String.length line > 2 then registrant_address := !registrant_address @ [line] else if String.length line = 2 then registrant_country := line else finished := true done end else if Str.string_match contact_regexp line 0 then begin (* Read contact data. *) let contact_type = Str.matched_group 1 line in let contact_info = ref [] in for i = 1 to 6 do let line = ltrim (input_line socket_in) in contact_info := !contact_info @ [line] done; contacts := (contact_type, !contact_info) :: !contacts end done with End_of_file -> Unix.shutdown_connection socket_in end; (* Display the results. *) Printf.printf "The domain is called %s\n" !domain_name; Printf.printf "Mail for %s should be sent to:\n" !registrant_name; List.iter (Printf.printf "\t%s\n") !registrant_address; Printf.printf "\t%s\n" !registrant_country; if !contacts = [] then Printf.printf "No contact information.\n" else begin Printf.printf "Contacts:\n"; List.iter (fun (contact_type, contact_info) -> Printf.printf " %s\n" contact_type; List.iter (Printf.printf " %s\n") contact_info) !contacts end
[править] Program: expn and vrfy
#!/usr/bin/ocaml (* expn -- convince smtp to divulge an alias expansion *) #use "topfind";; (* Findlib *) #require "str";; (* Stdlib *) #require "unix";; (* Stdlib *) #require "perl";; (* Perl4caml *) #require "smtp";; (* Ocamlnet *) let _ = Perl.eval "use Net::DNS" (* Net::DNS *) let selfname = Unix.gethostname () let () = if Array.length Sys.argv < 2 then (Printf.eprintf "usage: %s address@host ...\n" Sys.argv.(0); exit 1) let () = List.iter (fun combo -> let name, host = match Str.bounded_split (Str.regexp "@") combo 2 with | [] -> "", "" | [name] -> name, "localhost" | [name; host] -> name, host | _ -> assert false in let hosts = Perl.call_array ~fn:"mx" [Perl.sv_of_string host] in let hosts = List.map (fun mx -> Perl.call_method mx "exchange" []) hosts in let hosts = if hosts = [] then [Perl.sv_of_string host] else hosts in List.iter (fun host -> let host = Perl.string_of_sv host in Printf.printf "Expanding %s at %s (%s): %!" name host combo; let inet_addr = (Unix.gethostbyname host).Unix.h_addr_list.(0) in let addr = Unix.ADDR_INET (inet_addr, Netsmtp.tcp_port) in try let ic, oc = Unix.open_connection addr in let smtp = new Netsmtp.client (new Netchannels.input_channel ic) (new Netchannels.output_channel oc) in ignore (smtp#helo ~host:selfname ()); print_endline (match smtp#expn name with | None -> "None" | Some results -> String.concat ", " results); smtp#quit (); Unix.shutdown_connection ic; close_out oc with Unix.Unix_error (Unix.ECONNREFUSED, _, _) -> Printf.eprintf "cannot connect to %s\n" host) hosts) (List.tl (Array.to_list Sys.argv))