#!/usr/local/bin/perl #------------------------------------------------------------------------- #-- proxy.pl - A simple http proxy server. -- #-- -- #-- To run, type proxy.pl [port-number] at the shell prompt. -- #-- Default port number is 5364. -- #-- -- #------------------------------------------------------------------------- #require "sys/socket.ph"; use Socket; srand (time||$$); #--- Define a friendly exit handler $SIG{'KILL'} = $SIG{QUIT} = $SIG{INT} = 'exit_handler'; sub exit_handler { print "\n\n --- Proxy server is dying ...\n\n"; close(SOCKET); exit; } #--- Setup socket $| = 1; $proxy_port = shift(@ARGV); $proxy_port = 5364 unless $proxy_port =~ /\d+/; $socket_format = 'S n a4 x8'; &listen_to_port(SOCKET, $proxy_port); $local_host = `hostname`; chop($local_host); $local_host_ip = (gethostbyname($local_host))[4]; print " --- Proxy server running on $local_host port: $proxy_port \n\n"; #--- Loop forever taking requests as they come while (1) { #--- Wait for request print " --- Waiting to be of service ...\n"; ($addr = accept(CHILD,SOCKET)) || die "accept $!"; ($port,$inetaddr) = (unpack($socket_format,$addr))[1,2]; @inetaddr = unpack('C4',$inetaddr); print "Connection from ", join(".", @inetaddr), " port: $port \n"; #--- Fork a subprocess to handle request. #--- Parent proces continues listening. if (fork) { wait; # For now we wait for the child to finish next; # We wait so that printouts don't mix } #--- Read first line of request and analyze it. #--- Return and edited version of the first line and the request method. ($first,$method) = &analyze_request; #--- Send request to remote host print URL $first; print $first; while () { print $_; next if (/Proxy-Connection:/); print URL $_; last if ($_ =~ /^[\s\x00]*$/); } if ($method eq "POST") { $data = ; print $data; print URL $data; } print URL "\n"; #--- Wait for response and transfer it to requestor. print " --- Done sending. Response: \n\n"; $header = 1; $text = 0; while () { print CHILD $_; if ($header || $text) { # Only print header & text lines to STDOUT print $_; if ($header && $_ =~ /^[\s\x00]*$/) { $header = 0; } # if ($header && $_ =~ /^Content-type: text/) { # $text = 1; # } } } close(URL); close(CHILD); exit; # Exit from child process } #------------------------------------------------------------------------- #-- analyze_request -- #-- -- #-- Analyze a new request. First read in first line of request. -- #-- Read URL from it, process URL and open connection. -- #-- Return an edited version of the first line and the request -- #-- method. -- #------------------------------------------------------------------------- sub analyze_request { #--- Read first line of HTTP request $first = ; $url = ($first =~ m|(http://\S+)|)[0]; print "Request for URL: $url \n"; #--- Check if first line is of the form GET http://host-name ... ($method, $remote_host, $remote_port) = ($first =~ m!(GET|POST|HEAD) http://([^/:]+):?(\d*)! ); #--- If not, bad request. if (!$remote_host) { print $first; while () { print $_; last if ($_ =~ /^[\s\x00]*$/); } print "Invalid HTTP request from ", join(".", @inetaddr), "\n"; # print CHILD "Content-type: text/plain","\n\n"; print CHILD "I don't understand your request.\n"; close(CHILD); exit; } #--- If requested URL is the proxy server then ignore request $remote_ip = (gethostbyname($remote_host))[4]; if (($remote_ip eq $local_host_ip) && ($remote_port eq $proxy_port)) { print $first; while () { print $_; last if ($_ =~ /^[\s\x00]*$/); } print " --- Connection to proxy server ignored.\n"; # print CHILD "Content-type: text/plain","\n\n"; print CHILD "It's not nice to make me loop on myself!.\n"; close(CHILD); exit; } #--- Setup connection to target host and send request $remote_port = "http" unless ($remote_port); &open_connection(URL, $remote_host, $remote_port); #--- Remove remote hostname from URL $first =~ s/http:\/\/[^\/]+//; ($first, $method); } #------------------------------------------------------------------------- #-- listen_to_port(SOCKET, $port) -- #-- -- #-- Create a socket that listens to a specific port -- #------------------------------------------------------------------------- sub listen_to_port { local ($port) = $_[1]; local ($socket_format, $proto, $packed_port, $cur, $max_requests); $max_requests = 3; # Max number of outstanding requests $socket_format = 'S n a4 x8'; $proto = (getprotobyname('tcp'))[2]; $packed_port = pack($socket_format, &AF_INET, $port, "\0\0\0\0"); socket($_[0], &PF_INET, &SOCK_STREAM, $proto) || die "socket: $!"; bind($_[0], $packed_port) || die "bind: $!"; listen($_[0], $max_requests) || die "listen: $!"; $cur = select($_[0]); $| = 1; # Disable buffering on socket. select($cur); } #------------------------------------------------------------------------- #-- open_connection(SOCKET, $remote_hostname, $port) -- #-- -- #-- Create a socket that connects to a certain host -- #-- $local_host_ip is assumed to be local hostname IP address -- #------------------------------------------------------------------------- sub open_connection { local ($remote_hostname, $port) = @_[1,2]; local ($socket_format, $proto, $packed_port, $cur); local ($remote_addr, @remote_ip, $remote_ip); local ($local_port, $remote_port); if ($port !~ /^\d+$/) { $port = (getservbyname($port, "tcp"))[2]; $port = 80 unless ($port); } $proto = (getprotobyname('tcp'))[2]; $remote_addr = (gethostbyname($remote_hostname))[4]; if (!$remote_addr) { die "Unknown host: $remote_hostname"; } @remote_ip = unpack("C4", $remote_addr); $remote_ip = join(".", @remote_ip); print "Connecting to $remote_ip port $port.\n\n"; $socket_format = 'S n a4 x8'; $local_port = pack($socket_format, &AF_INET, 0, $local_host_ip); $remote_port = pack($socket_format, &AF_INET, $port, $remote_addr); socket($_[0], &AF_INET, &SOCK_STREAM, $proto) || die "socket: $!"; bind($_[0], $local_port) || die "bind: $!"; connect($_[0], $remote_port) || die "socket: $!"; $cur = select($_[0]); $| = 1; # Disable buffering on socket. select($cur); }