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

######################################################################
# client.pl
# Tom Kelliher, CS 318
#
# This program is a demonstration of a simple client application.  The
# client merely reads a brief message from the server.  It works with
# its companion program, server.pl, as well as other simple servers,
# such as daytime.
#
# By default, the client attempts to contact localhost at port 50001.
# An alternate server and port can be specified:
#
#    client.pl [server_hostname [server_port_number]]
#
# 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_HOSTNAME = "localhost";   # Default server hostname.
my $SERVER_PORT = 50001;             # Default server port.
my $BUFFER_LEN = 1400;               # Maximum amount of data we
                                     # want to receive in a single
                                     # packet.


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

MAIN:
{
   my $server = shift || $SERVER_HOSTNAME;   # Server hostname.
   my $sport = shift || $SERVER_PORT;        # Server port number.
   my $sipaddr;                              # Server IP address.
   my $paddr;                                # Packed address
                                             # (port number and IP
                                             # address).
   my $proto;                                # TCP protocol number.
   my $sockaddr;                             # Packed address of
                                             # local end of socket.
   my $lport;                                # Local port number
   my $lipaddr;                              # Local IP address.
   my $line;                                 # Line of input.


   # If we've taken a host name from the command line, it's now
   # tainted.  If it is only letters, digits, and "dots", we'll go
   # ahead and untaint it.

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


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

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


   # If the port number has letters, assume it was given symbolically
   # and attempt to retrieve the corresponding port number.

   if ($sport =~ /\D/) { $sport = getservbyname($sport, "tcp"); }
   die "No port.\n" unless $sport;


   # Get the IP address of the server.

   $sipaddr = inet_aton($server) or die "No host: $server.\n";

   print "Server: $server\nServer Port: $sport.\n";


   # Get the TCP protocol number.

   $proto = getprotobyname("tcp");


   # 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: $!";


   # Create the packed server address and attempt to connect to the
   # server, using the just-created socket.

   $paddr = sockaddr_in($sport, $sipaddr);
   connect(SOCK, $paddr) or die "Connect: $!";


   # Get the packed address of this end of the socket and unpack it.

   $sockaddr = getsockname(SOCK);
   ($lport, $lipaddr) = sockaddr_in($sockaddr);

   print "Using Local Port: $lport.\n";
   print "Server says:\n";


   # Start receiving data from the server.  The fourth parameter to
   # recv() is option bits, usually 0.

   recv(SOCK, $line, $BUFFER_LEN, 0);


   # The message may have been fragmented into several packets.
   # We continue receiving packets until receive returns a packet
   # with no data, signifying the end of the message.

   while (length($line) > 0)
   {
      print $line;
      recv(SOCK, $line, $BUFFER_LEN, 0);
   }

   close(SOCK);

   exit 0;
}
