#!/usr/local/bin/perl -Tw

######################################################################
# server.pl
# Tom Kelliher, CS 318
#
# This program is a demonstration of a simple server application.  The
# server merely returns a brief message reporting the number of times
# it has been contacted.  It works with its companion program,
# client.pl, as well as telnet.
#
# By default, the server binds to port 50001.  This can be changed by
# providing a port number on the command line.
#
# After binding to the port, the server enters an infinite loop.  Use
# ^C to terminate the server.
#
# Note that taint checking (-T) has been enabled in addition to the
# standard warnings (-w) on the shebang line.
######################################################################


# Initial declarations.

require 5.002;
use strict;
use sigtrap;   # Use some basic signal traps.
use Socket;    # Use the socket library.


# Globals.

my $SERVER_PORT = 50001;   # Default server port.


# Prototypes.

sub TERM_handler();


$SIG{'INT'} = 'TERM_handler';   # Install a ^C handler.


######################################################################
# MAIN
######################################################################

MAIN:
{
   my $sport = shift || $SERVER_PORT;   # Server port.
   my $proto;                           # TCP protocol number.
   my $paddr;                           # Packed address (port # and
                                        # IP address).
   my $cport;                           # Client port.
   my $cipaddr;                         # Client IP address.
   my $chostname;                       # Client hostname;
   my $visits = 0;                      # Number of client visits;


   # If we've taken a port number from the command line, it's now
   # tainted.  If it is only digits, we'll go ahead and untaint it.

   if ($sport =~ /^(\d+)$/)
   {
      $sport = $1;
   }
   else
   {
      die "Bad data in server port: $sport.\n";
   }

   $proto = getprotobyname("tcp");   # Get the TCP protocol number.


   # Attempt to create a socket.  SOCK is the handle.  This socket
   # uses a protocol family of Internet, it's a stream socket, and it's
   # a TCP socket.

   socket(SOCK, PF_INET, SOCK_STREAM, $proto) or die "Socket: $!";


   # Set the option bit allowing us to reuse the port number.

   setsockopt(SOCK, SOL_SOCKET, SO_REUSEADDR, 1)
      or die "Setsockopt: $!";


   # Attempt to bind the socket to the port, using any available
   # ethernet interface (IP address).

   bind(SOCK, sockaddr_in($sport, INADDR_ANY)) or die "Bind: $!";


   # Set the length of the request queue to the maximum value (64).

   listen(SOCK, SOMAXCONN);

   print "Server started on port $sport.\n\n";

   while (1)   # Enter the server loop.
   {

      # Wait for, and accept, a connection from a client.  accept()
      # returns the packed address (port # and IP address) of the
      # client.  SOCK is the socket we listen on.  CLIENT is the socket
      # we use to communicate with the client.

      $paddr = accept(CLIENT, SOCK);
      

      # Unpack the address.

      ($cport, $cipaddr) = sockaddr_in($paddr);


      # Attempt to get the client's hostname by reverse DNS mapping
      # its IP address.

      $chostname = gethostbyaddr($cipaddr, AF_INET)
         || "Could not resolve hostname";

      ++$visits;

      print "Client Hostname: $chostname \[", inet_ntoa($cipaddr), 
            "\]\n";
      print "Client Port: $cport\n";
      print "Visit: $visits.\n\n";


      # Send a message to the client.  The third parameter to send()
      # is option bits, ordinarily 0.

      send(CLIENT, "This server has been contacted $visits times.\n", 0);

      close(CLIENT);
   }

   exit 0;
}


######################################################################
# TERM_handler
# 
# This subroutine provides a very basic signal handler for the 
# TERM signal, ^C.  All it does is close any open sockets and exit.
######################################################################

sub TERM_handler()
{
   print "\nTerminating.\n";
   close CLIENT;
   close SOCK;
   exit 0;
}
