|
Ocaml/FAQ/Sockets
Материал из Wiki.crossplatform.ru
[править] Introduction
open Unix
(* Convert human readable form to 32 bit value *)
let packed_ip = inet_addr_of_string "208.146.240.1" in
let host = gethostbyname "www.oreilly.com" in
let packed_ip = host.h_addr_list.(0) in
(* Convert 32 bit value to ip adress *)
let ip_address = string_of_inet_addr (packed_ip) in
(* Create socket object *)
let sock = socket PF_INET SOCK_STREAM 0 in
(* Get socketname *)
let saddr = getsockname sock ;;
[править] Writing a TCP Client
(* For real applications you should the SMTP module in Ocamlnet. *)
open Unix
let sock_send sock str =
let len = String.length str in
send sock str 0 len []
let sock_recv sock maxlen =
let str = String.create maxlen in
let recvlen = recv sock str 0 maxlen [] in
String.sub str 0 recvlen
let client_sock = socket PF_INET SOCK_STREAM 0 in
let hentry = gethostbyname "coltrane" in
connect client_sock (ADDR_INET (hentry.h_addr_list.(0), 25)) ; (* SMTP *)
sock_recv client_sock 1024 ;
sock_send client_sock "mail from: <pleac@localhost>\n" ;
sock_recv client_sock 1024 ;
sock_send client_sock "rcpt to: <erikd@localhost>\n" ;
sock_recv client_sock 1024;
sock_send client_sock "data\n" ;
sock_recv client_sock 1024 ;
sock_send client_sock "From: Ocaml whiz\nSubject: Ocaml rulez!\n\nYES!\n.\n" ;
sock_recv client_sock 1024 ;
close client_sock ;;
[править] Writing a TCP Server
(* Writing a TCP Server *)
(* Run this and then telnet <machinename> 1027 *)
#load "unix.cma" ;;
open Unix ;;
let server_sock = socket PF_INET SOCK_STREAM 0 in
(* so we can restart our server quickly *)
setsockopt server_sock SO_REUSEADDR true ;
(* build up my socket address *)
let address = (gethostbyname(gethostname())).h_addr_list.(0) in
bind server_sock (ADDR_INET (address, 1029)) ;
(* Listen on the socket. Max of 10 incoming connections. *)
listen server_sock 10 ;
(* accept and process connections *)
while true do
let (client_sock, client_addr) = accept server_sock in
let str = "Hello\n" in
let len = String.length str in
let x = send client_sock str 0 len [] in
shutdown client_sock SHUTDOWN_ALL
done ;;
[править] Communicating over TCP
#load "unix.cma";;
let () =
let server_in = Unix.in_channel_of_descr server in
let server_out = Unix.out_channel_of_descr server in
output_string server_out "What is your name?\n";
flush server_out;
let response = input_line server_in in
print_endline response
(*-----------------------------*)
let () =
try
ignore
(Unix.send server data_to_send 0 (String.length data_to_send) flags)
with Unix.Unix_error (e, _, _) ->
Printf.eprintf "Can't send: %s\n%!"
(Unix.error_message e);
exit 1
let data_read =
let data_read = String.create maxlen in
let data_length =
try
Unix.recv server data_read 0 maxlen flags
with Unix.Unix_error (e, _, _) ->
Printf.eprintf "Can't receive: %s\n%!"
(Unix.error_message e);
exit 1 in
String.sub data_read 0 data_length
(*-----------------------------*)
let () =
let read_from, _, _ =
Unix.select [from_server; to_client] [] [] timeout in
List.iter
(fun socket ->
(* read the pending data from socket *)
())
read_from
(*-----------------------------*)
(* Requires OCaml 3.11 or newer. *)
let () =
try Unix.setsockopt server Unix.TCP_NODELAY true
with Unix.Unix_error (e, _, _) ->
Printf.eprintf "Couldn't disable Nagle's algorithm: %s\n%!"
(Unix.error_message e)
(*-----------------------------*)
(* Requires OCaml 3.11 or newer. *)
let () =
try Unix.setsockopt server Unix.TCP_NODELAY false
with Unix.Unix_error (e, _, _) ->
Printf.eprintf "Couldn't enable Nagle's algorithm: %s\n%!"
(Unix.error_message e)
[править] Setting Up a UDP Client
#load "unix.cma";;
(* Create a UDP socket. *)
let socket =
Unix.socket Unix.PF_INET Unix.SOCK_DGRAM
(Unix.getprotobyname "udp").Unix.p_proto
(*-----------------------------*)
(* Send a UDP message. *)
let ipaddr = (Unix.gethostbyname hostname).Unix.h_addr_list.(0)
let portaddr = Unix.ADDR_INET (ipaddr, portno)
let len = Unix.sendto socket msg 0 (String.length msg) [] portaddr
(*-----------------------------*)
(* Receive a UDP message. *)
let msg = String.create maxlen
let len, portaddr = Unix.recvfrom socket msg 0 maxlen []
(*-----------------------------*)
#!/usr/bin/ocaml
(* clockdrift - compare another system's clock with this one *)
#load "unix.cma";;
let secs_of_70_years = 2_208_988_800L
let msgbox =
Unix.socket Unix.PF_INET Unix.SOCK_DGRAM
(Unix.getprotobyname "udp").Unix.p_proto
let him =
Unix.ADDR_INET ((Unix.gethostbyname
(if Array.length Sys.argv > 1
then Sys.argv.(1)
else "127.1")).Unix.h_addr_list.(0),
(Unix.getservbyname "time" "udp").Unix.s_port)
let () = ignore (Unix.sendto msgbox "" 0 0 [] him)
let ptime = String.create 4
let host =
match Unix.recvfrom msgbox ptime 0 4 [] with
| _, Unix.ADDR_INET (addr, port) ->
(Unix.gethostbyaddr addr).Unix.h_name
| _ -> assert false
let delta =
Int64.to_float
(Int64.sub
(Int64.of_string (Printf.sprintf "0x%02x%02x%02x%02x"
(int_of_char ptime.[0])
(int_of_char ptime.[1])
(int_of_char ptime.[2])
(int_of_char ptime.[3])))
secs_of_70_years)
-. (Unix.time ())
let () =
Printf.printf "Clock on %s is %d seconds ahead of this one.\n"
host (int_of_float delta)
[править] Setting Up a UDP Server
#load "unix.cma";;
let () =
begin
try
Unix.bind socket (Unix.ADDR_INET (Unix.inet_addr_any, server_port));
with Unix.Unix_error (e, _, _) ->
Printf.eprintf "Couldn't be a udp server on port %d: %s\n"
server_port (Unix.error_message e);
exit 1
end;
let him = String.create max_to_read in
while true do
ignore (Unix.recvfrom socket him 0 max_to_read []);
(* do something *)
done
(*-----------------------------*)
#!/usr/bin/ocaml
(* udpqotd - UDP message server *)
#load "unix.cma";;
let maxlen = 1024
let portno = 5151
let sock =
Unix.socket Unix.PF_INET Unix.SOCK_DGRAM
(Unix.getprotobyname "udp").Unix.p_proto
let () =
Unix.bind sock (Unix.ADDR_INET (Unix.inet_addr_any, portno));
Printf.printf "Awaiting UDP messages on port %d\n%!" portno
let oldmsg = ref "This is the starting message."
let () =
let newmsg = String.create maxlen in
while true do
let newmsg, hishost, sockaddr =
match Unix.recvfrom sock newmsg 0 maxlen [] with
| len, (Unix.ADDR_INET (addr, port) as sockaddr) ->
String.sub newmsg 0 len,
(Unix.gethostbyaddr addr).Unix.h_name,
sockaddr
| _ -> assert false in
Printf.printf "Client %s said ``%s''\n%!" hishost newmsg;
ignore
(Unix.sendto sock !oldmsg 0 (String.length !oldmsg) [] sockaddr);
oldmsg := Printf.sprintf "[%s] %s" hishost newmsg
done
(*-----------------------------*)
#!/usr/bin/ocaml
(* udpmsg - send a message to the udpqotd server *)
#load "unix.cma";;
let maxlen = 1024
let portno = 5151
let timeout = 5
let server_host, msg =
match Array.to_list Sys.argv with
| _ :: head :: tail -> head, String.concat " " tail
| _ ->
Printf.eprintf "Usage: %s server_host msg ...\n" Sys.argv.(0);
exit 1
let sock =
Unix.socket Unix.PF_INET Unix.SOCK_DGRAM
(Unix.getprotobyname "udp").Unix.p_proto
let sockaddr =
let addr = (Unix.gethostbyname server_host).Unix.h_addr_list.(0) in
Unix.ADDR_INET (addr, portno)
let handle_alarm signal =
Printf.eprintf "recv from %s timed out after %d seconds.\n"
server_host timeout;
exit 1
let () =
ignore (Unix.sendto sock msg 0 (String.length msg) [] sockaddr);
Sys.set_signal Sys.sigalrm (Sys.Signal_handle handle_alarm);
ignore (Unix.alarm timeout);
let msg = String.create maxlen in
let msg, hishost =
match Unix.recvfrom sock msg 0 maxlen [] with
| len, Unix.ADDR_INET (addr, port) ->
String.sub msg 0 len,
(Unix.gethostbyaddr addr).Unix.h_name
| _ -> assert false in
ignore (Unix.alarm 0);
Printf.printf "Server %s responded ``%s''\n" hishost msg
[править] Using UNIX Domain Sockets
#load "unix.cma";;
(* Create a Unix domain socket server - you can also use SOCK_STREAM. *)
let server = Unix.socket Unix.PF_UNIX Unix.SOCK_DGRAM 0
let () = try Unix.unlink "/tmp/mysock" with Unix.Unix_error _ -> ()
let () = Unix.bind server (Unix.ADDR_UNIX "/tmp/mysock")
(* Create a Unix domain socket client - you can also use SOCK_STREAM. *)
let client = Unix.socket Unix.PF_UNIX Unix.SOCK_DGRAM 0
let () = Unix.connect client (Unix.ADDR_UNIX "/tmp/mysock")
[править] Identifying the Other End of a Socket
#load "unix.cma";;
(* Get the remote IP address. *)
let () =
let other_end = Unix.getpeername socket in
let name_info = Unix.getnameinfo other_end [Unix.NI_NUMERICHOST] in
let ip_address = name_info.Unix.ni_hostname in
(* ... *)
()
(*-----------------------------*)
(* Attempt to determine the remote host name, with forward and reverse
DNS lookups to detect spoofing. *)
let () =
let other_end = Unix.getpeername socket in
let name_info = Unix.getnameinfo other_end [Unix.NI_NUMERICHOST] in
let actual_ip = name_info.Unix.ni_hostname in
let claimed_hostname =
(Unix.gethostbyaddr (Unix.inet_addr_of_string actual_ip))
.Unix.h_name in
let name_lookup = Unix.gethostbyname claimed_hostname in
let resolved_ips =
Array.to_list (Array.map
Unix.string_of_inet_addr
name_lookup.Unix.h_addr_list) in
(* ... *)
()
[править] Finding Your Own Name and Address
(*-----------------------------*)
(*
** Finding Your Own Name and Address.
** The Unix module to the rescue again.
*)
#load "unix.cma" ;;
open Unix ;;
let hostname = gethostname () in
Printf.printf "hostname : %s\n" hostname ;;
(*-----------------------------*)
(*
** Unfortunately there is no easy way of retreiving the
** uname without using Unix.open_process_in.
*)
(*-----------------------------*)
let hentry = gethostbyname hostname in
let address = hentry.h_addr_list.(0) in
Printf.printf "address : %s\n" (string_of_inet_addr address) ;;
let hentry = gethostbyaddr address in
Printf.printf "hostname : %s\n" hentry.h_name ;;
[править] Closing a Socket After Forking
(* Closing a Socket After Forking *)
(*-----------------------------*)
shutdown sock SHUTDOWN_RECEIVE ; (* I/we have stopped reading data *)
shutdown sock SHUTDOWN_SEND ; (* I/we have stopped writing data *)
shutdown sock SHUTDOWN_ALL ;; (* I/we have stopped using this socket *)
(*-----------------------------*)
(* Using the sock_send and sock_recv functions from above. *)
sock_send sock "my request\n" ; (* send some data *)
shutdown sock SHUTDOWN_SEND ; (* send eof; no more writing *)
let answer = sock_recv sock 4096 ;; (* but you can still read *)
[править] Writing Bidirectional Clients
#!/usr/bin/ocaml
(* biclient - bidirectional forking client *)
#load "unix.cma";;
let host, port =
match Array.to_list Sys.argv with
| [_; host; port] -> host, int_of_string port
| _ -> Printf.eprintf "usage: %s host port\n" Sys.argv.(0); exit 1
let sockaddr =
let addr = (Unix.gethostbyname host).Unix.h_addr_list.(0) in
Unix.ADDR_INET (addr, port)
let () =
let socket = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
Unix.connect socket sockaddr;
Printf.eprintf "[Connected to %s:%d]\n%!" host port;
(* split the program into two processes, identical twins *)
match Unix.fork () with
| 0 ->
(* child copies standard input to the socket *)
let output = Unix.out_channel_of_descr socket in
while true do
let line = input_line stdin in
output_string output line;
output_string output "\n";
flush output
done
| kidpid ->
(* parent copies the socket to standard output *)
let input = Unix.in_channel_of_descr socket in
try
while true do
let line = input_line input in
output_string stdout line;
output_string stdout "\n";
flush stdout
done
with End_of_file ->
Unix.kill kidpid Sys.sigterm
let () = exit 0
[править] Forking Servers
(* set up the socket SERVER, bind and listen ... *)
#load "unix.cma";;
let rec reaper signal =
try while true do ignore (Unix.waitpid [Unix.WNOHANG] (-1)) done
with Unix.Unix_error (Unix.ECHILD, _, _) -> ();
Sys.set_signal Sys.sigchld (Sys.Signal_handle reaper)
let () =
Sys.set_signal Sys.sigchld (Sys.Signal_handle reaper)
let () =
while true do
try
let (client, addr) = Unix.accept server in
let pid = Unix.fork () in
if pid = 0 then (* parent *)
begin
Unix.close server; (* no use to child *)
(* ... do something *)
exit 0 (* child leaves *)
end
else
begin
Unix.close client (* no use to parent *)
end
with Unix.Unix_error (Unix.EINTR, _, _) -> ()
done
[править] Pre-Forking Servers
#!/usr/bin/ocaml
(* preforker - server who forks first *)
#load "unix.cma";;
(* global variables *)
let prefork = 5
let max_clients_per_child = 5
module PidSet = Set.Make(struct type t = int let compare = compare end)
let children = ref PidSet.empty
(* takes care of dead children *)
let rec reaper _ =
Sys.set_signal Sys.sigchld (Sys.Signal_handle reaper);
match Unix.wait ()
with (pid, _) -> children := PidSet.remove pid !children
(* signal handler for SIGINT *)
let rec huntsman _ =
(* we're going to kill our children *)
Sys.set_signal Sys.sigchld Sys.Signal_ignore;
PidSet.iter
(fun pid ->
try Unix.kill Sys.sigint pid with Unix.Unix_error _ -> ())
!children;
(* clean up with dignity *)
exit 0
let make_new_child server =
(* block signal for fork *)
let sigset = [Sys.sigint] in
ignore (Unix.sigprocmask Unix.SIG_BLOCK sigset);
match Unix.fork () with
| 0 ->
(* Child can *not* return from this subroutine. *)
(* make SIGINT kill us as it did before *)
Sys.set_signal Sys.sigint Sys.Signal_default;
(* unblock signals *)
ignore (Unix.sigprocmask Unix.SIG_UNBLOCK sigset);
(* handle connections until we've reached max_clients_per_child *)
for i = 1 to max_clients_per_child do
let (client, _) = Unix.accept server in
(* do something with the connection *)
()
done;
(* tidy up gracefully and finish *)
(* this exit is VERY important, otherwise the child will become
a producer of more and more children, forking yourself into
process death. *)
exit 0
| pid ->
(* Parent records the child's birth and returns. *)
ignore (Unix.sigprocmask Unix.SIG_UNBLOCK sigset);
children := PidSet.add pid !children
let () =
(* establish SERVER socket, bind and listen. *)
let server = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
Unix.setsockopt server Unix.SO_REUSEADDR true;
Unix.bind server (Unix.ADDR_INET (Unix.inet_addr_any, 6969));
Unix.listen server 10;
(* Fork off our children. *)
for i = 1 to prefork do
make_new_child server
done;
(* Install signal handlers. *)
Sys.set_signal Sys.sigchld (Sys.Signal_handle reaper);
Sys.set_signal Sys.sigint (Sys.Signal_handle huntsman);
(* And maintain the population. *)
while true do
(* wait for a signal (i.e., child's death) *)
Unix.pause ();
for i = (PidSet.cardinal !children) to (prefork - 1) do
(* top up the child pool *)
make_new_child server
done
done
[править] Non-Forking Servers
#!/usr/bin/ocaml
(* nonforker - server who multiplexes without forking *)
#load "unix.cma";;
let port = 1685 (* change this at will *)
(* Listen to port. *)
let server = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0
let () =
Unix.setsockopt server Unix.SO_REUSEADDR true;
Unix.bind server (Unix.ADDR_INET (Unix.inet_addr_any, port));
Unix.listen server 10;
Unix.set_nonblock server
module FDSet =
Set.Make(struct type t = Unix.file_descr let compare = compare end)
let clients = ref (FDSet.singleton server)
(* begin with empty buffers *)
let inbuffer = Hashtbl.create 0
let outbuffer = Hashtbl.create 0
let ready = Hashtbl.create 0
let buffer_size = 8192
let buffer = String.make buffer_size '\000'
(* handle deals with all pending requests for client *)
let handle client requests =
(* requests are in ready[client] *)
(* send output to outbuffer[client] *)
List.iter
(fun request ->
(* request is the text of the request *)
let data = Printf.sprintf "You said: %s\n" request in
(* put text of reply into outbuffer[client] *)
Hashtbl.replace outbuffer client
(try Hashtbl.find outbuffer client ^ data
with Not_found -> data))
requests
(* Main loop: check reads/accepts, check writes, check ready to process *)
let () =
while true do
(* check for new information on the connections we have *)
let (can_read, _, _) =
Unix.select (FDSet.elements !clients) [] [] 1.0 in
List.iter
(fun client ->
if client = server
then
begin
(* accept a new connection *)
let (client, addr) = Unix.accept server in
clients := FDSet.add client !clients;
Unix.set_nonblock client
end
else
begin
(* read data *)
let chars_read =
try
Some (Unix.read client buffer 0 buffer_size)
with Unix.Unix_error (error, _, _) ->
prerr_endline (Unix.error_message error);
None in
match chars_read with
| None | Some 0 ->
(* This would be the end of file, so close the client *)
Hashtbl.remove inbuffer client;
Hashtbl.remove outbuffer client;
Hashtbl.remove ready client;
clients := FDSet.remove client !clients;
Unix.close client
| Some chars_read ->
let data = String.sub buffer 0 chars_read in
Hashtbl.replace inbuffer client
(try Hashtbl.find inbuffer client ^ data
with Not_found -> data);
(* test whether the data in the buffer or the data we *)
(* just read means there is a complete request waiting *)
(* to be fulfilled. If there is, set ready[client] *)
(* to the requests waiting to be fulfilled. *)
try
while true do
let data = Hashtbl.find inbuffer client in
let index = String.index data '\n' in
Hashtbl.replace inbuffer client
(String.sub data
(index + 1)
(String.length data - index - 1));
Hashtbl.replace ready client
((try Hashtbl.find ready client
with Not_found -> [])
@ [String.sub data 0 index])
done
with Not_found -> ()
end)
can_read;
(* Any complete requests to process? *)
Hashtbl.iter handle ready;
Hashtbl.clear ready;
(* Buffers to flush? *)
let (_, can_write, _) =
Unix.select [] (FDSet.elements !clients) [] 1.0 in
(* Skip client if we have nothing to say *)
let can_write =
List.filter (Hashtbl.mem outbuffer) can_write in
List.iter
(fun client ->
let data = Hashtbl.find outbuffer client in
let chars_written =
try
Some (Unix.single_write client data 0 (String.length data))
with
| Unix.Unix_error (Unix.EAGAIN, _, _)
| Unix.Unix_error (Unix.EWOULDBLOCK, _, _) ->
prerr_endline "I was told I could write, but I can't.";
Some 0
| Unix.Unix_error (error, _, _) ->
prerr_endline (Unix.error_message error);
None in
match chars_written with
| Some chars_written ->
if chars_written = String.length data
then Hashtbl.remove outbuffer client
else Hashtbl.replace outbuffer client
(String.sub data chars_written
(String.length data - chars_written))
| None ->
(* Couldn't write all the data, and it wasn't because *)
(* it would have blocked. Shutdown and move on. *)
Hashtbl.remove inbuffer client;
Hashtbl.remove outbuffer client;
Hashtbl.remove ready client;
clients := FDSet.remove client !clients;
Unix.close client)
can_write;
let (_, _, has_exception) =
Unix.select [] [] (FDSet.elements !clients) 0.0 in
List.iter
(fun client ->
(* Deal with out-of-band data here, if you want to. *)
())
has_exception;
done
[править] Writing a Multi-Homed Server
#load "unix.cma";;
let server =
Unix.socket Unix.PF_INET Unix.SOCK_STREAM
(Unix.getprotobyname "tcp").Unix.p_proto
let () =
Unix.setsockopt server Unix.SO_REUSEADDR true;
Unix.bind server (Unix.ADDR_INET (Unix.inet_addr_any, server_port));
Unix.listen server 10;
(* accept loop *)
while true do
let client, sockaddr = Unix.accept server in
match Unix.getsockname client with
| Unix.ADDR_INET (addr, port) ->
print_endline (Unix.string_of_inet_addr addr)
| _ -> assert false
done
(*-----------------------------*)
#load "unix.cma";;
let port = 4269 (* port to bind to *)
let host = "specific.host.com" (* virtual host to listen on *)
let server =
Unix.socket Unix.PF_INET Unix.SOCK_STREAM
(Unix.getprotobyname "tcp").Unix.p_proto
let () =
let addr = (Unix.gethostbyname host).Unix.h_addr_list.(0) in
Unix.bind server (Unix.ADDR_INET (addr, port));
Unix.listen server 10;
while true do
let client, sockaddr = Unix.accept server in
(* ... *)
()
done
[править] Making a Daemon Server
#load "unix.cma";;
let () =
(* for the paranoid *)
(* Unix.handle_unix_error Unix.chroot "/var/daemon"; *)
(* fork and let parent exit *)
let pid = Unix.fork () in
if pid > 0 then exit 0;
(* create a new session and abandon the controlling process *)
ignore (Unix.setsid ())
(* flag indicating it is time to exit *)
let time_to_die = ref false
(* trap fatal signals *)
let () =
let signal_handler _ = time_to_die := true in
List.iter
(fun signal ->
Sys.set_signal signal (Sys.Signal_handle signal_handler))
[Sys.sigint; Sys.sigterm; Sys.sighup]
(* trap or ignore Sys.sigpipe *)
(* server loop *)
let () =
while not !time_to_die do
(* ... *)
()
done
[править] Restarting a Server on Demand
#load "unix.cma";;
let self = "/usr/bin/ocaml"
let args = self :: Array.to_list Sys.argv
let phoenix _ =
(* close all your connections, kill your children, and *)
(* generally prepare to be reincarnated with dignity. *)
try
ignore (Unix.sigprocmask Unix.SIG_UNBLOCK [Sys.sighup]);
Unix.execv self (Array.of_list args)
with Unix.Unix_error (e, _, _) ->
Printf.eprintf "Couldn't restart: %s\n%!"
(Unix.error_message e)
let () =
Sys.set_signal Sys.sighup (Sys.Signal_handle phoenix)
(*-----------------------------*)
(* This recipe uses the Ocaml-Syck YAML parser available at:
http://ocaml-syck.sourceforge.net/ *)
#directory "+yaml";;
#load "yaml.cma";;
#load "unix.cma";;
let yaml_parser = YamlParser.make ()
let config_file = "/usr/local/etc/myprog/server_conf.yaml"
let config = ref (YamlNode.SCALAR ("", ""))
let read_config _ =
let in_channel = open_in config_file in
let lines = ref [] in
try
while true do
let line = input_line in_channel in
lines := line :: !lines
done
with End_of_file ->
close_in in_channel;
config :=
YamlParser.parse_string yaml_parser
(String.concat "\n" (List.rev !lines))
let () =
read_config ();
Sys.set_signal Sys.sighup (Sys.Signal_handle read_config)
[править] Program: backsniff
Oct 4 11:01:16 pedro sniffer: Connection from 10.0.0.4 to 10.0.0.1:echo
(*-----------------------------*)
echo stream tcp nowait nobody /usr/bin/ocaml ocaml /path/to/backsniff.ml
(*-----------------------------*)
(* backsniff - log attempts to connect to particular ports *)
#load "unix.cma";;
(* This recipe uses syslog-ocaml, which is available at:
http://www.cs.cmu.edu/~ecc/software.html *)
#directory "+syslog";;
#load "syslog.cma";;
(* identify my port and address *)
let sockname =
try Unix.getsockname Unix.stdin
with Unix.Unix_error (e, _, _) ->
Printf.eprintf "Couldn't identify myself: %s\n%!"
(Unix.error_message e);
exit 1
let iaddr, port =
match sockname with
| Unix.ADDR_INET (iaddr, port) -> iaddr, port
| _ -> assert false
let my_address = Unix.string_of_inet_addr iaddr
(* get a name for the service *)
let service =
try (Unix.getservbyport port "tcp").Unix.s_name
with Not_found -> string_of_int port
(* now identify remote address *)
let sockname =
try Unix.getpeername Unix.stdin
with Unix.Unix_error (e, _, _) ->
Printf.eprintf "Couldn't identify other end: %s\n%!"
(Unix.error_message e);
exit 1
let iaddr, port =
match sockname with
| Unix.ADDR_INET (iaddr, port) -> iaddr, port
| _ -> assert false
let ex_address = Unix.string_of_inet_addr iaddr
(* and log the information *)
let () =
let log = Syslog.openlog ~flags:[] ~facility:`LOG_DAEMON "sniffer" in
Syslog.syslog log `LOG_NOTICE
(Printf.sprintf "Connection from %s to %s:%s\n"
ex_address my_address service);
Syslog.closelog log;
exit 0
[править] Program: fwdport
#!/usr/bin/ocaml
(* fwdport -- act as proxy forwarder for dedicated services *)
#load "str.cma";;
#load "unix.cma";;
let children = Hashtbl.create 0 (* hash of outstanding child processes *)
let remote = ref "" (* whom we connect to on the outside *)
let local = ref "" (* where we listen to on the inside *)
let service = ref "" (* our service name or port number *)
let proxy_server = ref Unix.stdin (* the socket we accept() from *)
(* process command line switches *)
let check_args () =
Arg.parse
[
"-r", Arg.Set_string remote, "Remote host";
"-remote", Arg.Set_string remote, "Remote host";
"-l", Arg.Set_string local, "Local interface";
"-local", Arg.Set_string local, "Local interface";
"-s", Arg.Set_string service, "Service";
"-service", Arg.Set_string service, "Service";
]
(fun s ->
raise (Arg.Bad (Printf.sprintf "unexpected argument `%s'" s)))
(Printf.sprintf "usage: %s [ -remote host ] [ -local interface ] [ -service service ]" Sys.argv.(0));
if !remote = ""
then (prerr_endline "Need remote"; exit 1);
if !local = "" && !service = ""
then (prerr_endline "Need local or service"; exit 1);
if !local = ""
then local := "localhost"
let parse_host host =
match Str.split (Str.regexp ":") host with
| [] -> "", ""
| host :: [] -> host, ""
| host :: service :: _ -> host, service
let resolve_host host =
try (Unix.gethostbyname host).Unix.h_addr_list.(0)
with Not_found ->
Printf.eprintf "Host not found: %s\n" host;
exit 1
let resolve_service service =
try int_of_string service
with Failure _ ->
try (Unix.getservbyname service "tcp").Unix.s_port
with Not_found ->
Printf.eprintf "Service not found: %s\n" service;
exit 1
(* begin our server *)
let start_proxy () =
try
let proto = (Unix.getprotobyname "tcp").Unix.p_proto in
let addr, port =
match parse_host (!local ^ ":" ^ !service) with
| host, service ->
(resolve_host host,
resolve_service service) in
proxy_server := Unix.socket Unix.PF_INET Unix.SOCK_STREAM proto;
Unix.setsockopt !proxy_server Unix.SO_REUSEADDR true;
Unix.bind !proxy_server (Unix.ADDR_INET (addr, port));
Unix.listen !proxy_server 128;
Printf.printf "[Proxy server on %s initialized.]\n%!"
(if !local <> "" then !local else !service)
with Unix.Unix_error (e, _, _) ->
Printf.eprintf "Can't create proxy server: %s\n%!"
(Unix.error_message e);
exit 1
(* helper function to produce a nice string in the form HOST:PORT *)
let peerinfo sock =
match Unix.getpeername sock with
| Unix.ADDR_INET (addr, port) ->
let hostinfo = Unix.gethostbyaddr addr in
Printf.sprintf "%s:%d" hostinfo.Unix.h_name port
| _ -> assert false
(* somebody just died. keep harvesting the dead until *)
(* we run out of them. check how long they ran. *)
let rec reaper signal =
begin
let result =
try Some (Unix.waitpid [Unix.WNOHANG] (-1))
with Unix.Unix_error (Unix.ECHILD, _, _) -> None in
match result with
| Some (child, status) when Hashtbl.mem children child ->
let start = Hashtbl.find children child in
let runtime = Unix.time () -. start in
Printf.printf "Child %d ran %dm%fs\n%!"
child
(int_of_float (runtime /. 60.))
(mod_float runtime 60.);
Hashtbl.remove children child;
reaper signal
| Some (child, status) ->
Printf.printf "Bizarre kid %d exited with %s\n%!"
child
(match status with
| Unix.WEXITED code ->
"code " ^ string_of_int code
| Unix.WSTOPPED signal
| Unix.WSIGNALED signal ->
"signal " ^ string_of_int signal);
reaper signal
| None -> ()
end;
(* If I had to choose between System V and 4.2, I'd resign. *)
(* --Peter Honeyman *)
Sys.set_signal Sys.sigchld (Sys.Signal_handle reaper)
let service_clients () =
(* harvest the moribund *)
Sys.set_signal Sys.sigchld (Sys.Signal_handle reaper);
(* an accepted connection here means someone inside wants out *)
while true do
try
begin
let local_client = fst (Unix.accept !proxy_server) in
let lc_info = peerinfo local_client in
Printf.printf "[Connect from %s]\n%!" lc_info;
let proto = (Unix.getprotobyname "tcp").Unix.p_proto in
let addr, port =
match parse_host (!remote ^ ":" ^ !service) with
| host, service ->
(resolve_host host,
resolve_service service) in
Printf.printf "[Connecting to %s...%!" !remote;
let remote_server = Unix.socket Unix.PF_INET Unix.SOCK_STREAM proto in
Unix.connect remote_server (Unix.ADDR_INET (addr, port));
Printf.printf "done]\n%!";
let local_in = Unix.in_channel_of_descr local_client in
let local_out = Unix.out_channel_of_descr local_client in
let remote_in = Unix.in_channel_of_descr remote_server in
let remote_out = Unix.out_channel_of_descr remote_server in
match Unix.fork () with
| 0 ->
(* at this point, we are the forked child process dedicated *)
(* to the incoming client. but we want a twin to make i/o *)
(* easier. *)
Unix.close !proxy_server; (* no use to slave *)
(* now each twin sits around and ferries lines of data. *)
(* see how simple the algorithm is when you can have *)
(* multiple threads of control? *)
(match Unix.fork () with
| 0 ->
(* this is the fork's child, the master's grandchild *)
(try
while true do
let line = input_line local_in in
Printf.fprintf remote_out "%s\n%!" line
done
with End_of_file ->
(* kill my twin cause we're done *)
Unix.kill (Unix.getppid ()) Sys.sigterm)
| kidpid ->
(* this is the fork's parent, the master's child *)
(try
while true do
let line = input_line remote_in in
Printf.fprintf local_out "%s\n%!" line
done
with End_of_file ->
(* kill my twin cause we're done *)
Unix.kill kidpid Sys.sigterm));
exit 0 (* whoever's still alive bites it *)
| kidpid ->
(* remember his start time *)
Hashtbl.replace children kidpid (Unix.time ());
Unix.close remote_server; (* no use to master *)
Unix.close local_client; (* likewise *)
end
with Unix.Unix_error (Unix.EINTR, "accept", _) -> ()
done
let () =
check_args (); (* processing switches *)
start_proxy (); (* launch our own server *)
service_clients (); (* wait for incoming *)
prerr_endline "NOT REACHED"; (* you can't get here from there *)
exit 1
|