#!/usr/bin/perl # Written by Adam P. Jenkins # # This program lets clients connect to a server indirectly through # this proxy server. The server address and port that the proxy # connects to is configurable on the command line. By default it will # connect to the the nntp port on the host stored in the NNTPSERVER # environment variable. # # The port that the proxy server listens for connections from clients # on can be specified on the commandline. If not specified, a free # port is selected at startup, and printed to stdout. # # Run with the -help option to get a usage message. use strict; use IO::Socket; use Sys::Hostname; use Getopt::Long; use Socket; STDOUT->autoflush(); STDERR->autoflush(); # All global variables must be declared here separated by spaces use vars qw($defaultServer $defaultServerPort); $defaultServer = 'news.netway.com'; $defaultServerPort = 'nntp'; if (exists $ENV{'NNTPSERVER'}) { $defaultServer = $ENV{'NNTPSERVER'}; } # handleConnection(clientSock, serverHost, serverPort) # # clientSock is a connection to a client. Opens a connection to # (serverHost, serverPort), and then forwards all input from # clientSock to the server, and all input from the server to the # client. Closes the connection and returns when either the server or # client connection closes. serverPort can be a port number or # service name. sub handleConnection { die "handleConnection called with wrong # of args\n" unless (@_ == 3); my ($clientSock, $serverHost, $serverPort) = @_; my ($serverSock, $err) = IO::Socket::INET->new(PeerAddr => $serverHost, PeerPort => $serverPort, Proto => 'tcp'); die "$err" unless $serverSock; my $rin = ''; vec($rin, $serverSock->fileno(), 1) = 1; vec($rin, $clientSock->fileno(), 1) = 1; while (1) { my $rout = $rin; my $nfound = select($rout, undef, undef, 600); if ($nfound <= 0) { print STDERR "select failed\n"; last; } if (vec($rout, $serverSock->fileno(), 1)) { # read from server, write to client my $inbuf; $serverSock->recv($inbuf, 80); if (length($inbuf) == 0) { print STDERR "serverSock->recv failed\n"; last; } if (!$clientSock->send($inbuf)) { last; } } if (vec($rout, $clientSock->fileno(), 1)) { # read from client, write to server my $inbuf; $clientSock->recv($inbuf, 80); if (length($inbuf) == 0) { print STDERR "clientSock->recv failed\n"; last; } if (!$serverSock->send($inbuf)) { last; } } } # while $serverSock->close(); $clientSock->close(); } sub REAPER { my $waitedpid = wait(); $SIG{CHLD} = \&REAPER; } $SIG{CHLD} = \&REAPER; sub main { my ($proxyPort, $serverPort, $serverHost); my $res = GetOptions("proxy-port=i" => \$proxyPort, "server-port=s" => \$serverPort, "server-host=s" => \$serverHost); if (!$res) { my $myname = $0; $myname =~ s,.*/,,; die "usage: $myname [-proxy-port=port] [-server-port=port]" . " [-server-host=host]\n"; } $proxyPort = 0 unless $proxyPort; $serverHost = $defaultServer unless $serverHost; $serverPort = $defaultServerPort unless $serverPort; my $proxySock = IO::Socket::INET->new(LocalAddr => hostname(), LocalPort => $proxyPort, Listen => 4, Proto => 'tcp'); die "$!" unless $proxySock; $proxyPort = $proxySock->sockport(); print "listening on port $proxyPort\n"; while (1) { my $clientSock = $proxySock->accept(); die "$!" unless $clientSock; my $peerhost = gethostbyaddr($clientSock->peeraddr(), AF_INET); if ("$peerhost" eq "") { $peerhost = inet_ntoa($clientSock->peeraddr()); } my $peerport = $clientSock->peerport(); print STDERR "Accepted connection from ($peerhost, $peerport)\n"; my $pid = fork(); if (!defined($pid)) { die "$!"; } elsif ($pid == 0) { # child process handleConnection($clientSock, $serverHost, $serverPort); print STDERR "Connection with ($peerhost, $peerport) closed\n"; exit(0); } } } main();