Previous Page Next Page

20.7. The Socket.pm Module

Although sockets were originally an idea started at Berkeley for UNIX systems, they are now supported on many other operating systems. Perl 5 introduced a special module, called Socket.pm, to deal with sockets. This makes it much easier to port programs from one machine to another, because the necessary functions and constants needed for your machine are handled in the module, thus allowing you to get away from hardcoding values into the program as seen in the previousl examples. However, a caveat: You must understand the way the Socket module works before using it. The names of constants are not intuitive, and the later versions of Perl 5 have introduced more functionality to the module.

The following examples demonstrate how to write the previous TCP/IP server and client programs by taking advantage of Socket.pm and some of the other pragmas offered in Perl 5 to better secure the programs when used on a network.

Figure 20.4. Server/client and Socket.pm, discussed in the following examples.


The Server

Example 20.21.

(The Server Script)
1   #!/bin/perl  -Tw
2   require 5.6;
3   use strict;
4   use Socket;
5   use FileHandle;

    # timeserver --  a Time Server program, opens a rendezvous
    # socket on port 29688 and waits for a client to connect.
    # When each client connects, this server determines the machine
    # time on its host and writes the value on the communication
    # socket to the client.
    #
    #                     Usage: timeserver
    #
6   my($this, $now);
7   my $port = shift || 29688;

8   $this = pack('Sna4x8', AF_INET, $port, "\0\0\0\0");
    print "Port = $port\n";
9   my $prototype = getprotobyname('tcp');
10  socket(SOCKET, PF_INET, SOCK_STREAM, $prototype) ||
                          die "socket: $!\n";
    print "Socket ok.\n";

11  bind(SOCKET, $this) || die "bind: $!\n";
    print "Bind ok.\n";

12  listen(SOCKET, SOMAXCONN) || die "connect: $!\n";
    print "Listen ok.\n";

13  COMM_SOCKET->autoflush;
    SOCKET->autoflush;

    # Infinite loop -- wait until client connects,
    # then serve the client
14  while(1){
        print "In loop.\n";
15      accept(COMM_SOCKET, SOCKET) || die "$!\n";
        print "Accept ok.\n";
16      $now = time;
17      print COMM_SOCKET $now;
    }

					  

Explanation

  1. The -w switch sends diagnostics to STDERR if an identifier is mentioned only once, a scalar is used before being set, a non-number is used as a number, etc. The -T switch turns on taint checking to prevent data coming into your program from affecting something inside your program. If using a directory path, the taint mode checks to see if the directory is writeable by others, and it checks arguments coming into the program. Once tainted, the data (or any variable that references the tainted data) cannot be used in any command that invokes a subshell. It is suggested that taint checking be turned on for server programs and CGI scripts; that is, programs that are run by someone else.

  2. The require function uses the version number as an argument. This ensures that Perl versions prior to 5.6 will abort.

  3. The strict pragma ensures that your program does not use unsafe constructs. It disallows symbolic references and barewords, and variables must be declared using the my function.

  4. The Socket module will be used in this program. The module is designed to make the use of sockets more portable.

  5. The FileHandle module is used here to take advantage of its method autoflush, which will force the proper flushing of buffers when writing to the socket.

  6. These variables will be used later in the program. They must be declared with my because the strict pragma enforces this as a safety feature.

  7. A hostname may be passed as a command-line argument and shifted into the $port variable. If the ARGV array is empty, the scalar $port is assigned the value 9688, a number well outside the range of reserved port numbers.

  8. AF_INET is a constant defined in the Socket module to represent the Internet domain. The pack function packs the IP address and port number for the server socket into $this.

  9. The getprotobyname function returns the official protocol name, any aliases, and the protocol number, using the tcp protocol as the name.

  10. The socket function creates the rendezvous socket filehandle, SOCKET.

  11. The bind function binds the socket filehandle to the correct address for the server.

  12. The listen function sets the queue limit to SOMAXCONN, the maximum for pending requests from the client (usually 5).

  13. The autoflush method from the FileHandle class forces buffers to be flushed as soon as something is written to the socket.

  14. An infinite loop is started. The server is now waiting for a client request.

  15. The accept function waits for a client request, and when it gets one, accepts it by creating a new socket filehandle called COMM_SOCK, with all the same attributes as SOCKET.

  16. The server calls the time function to get the current time and assigns that value to $now.

  17. The server sends the time to COMM_SOCK. The client will get it at its end.

 

The Client

Example 20.22.

     #!/usr/local/bin/perl -Tw
     require 5.6.0;
1    use Socket;
     use FileHandle;
2    use strict;
3    my($remote, $port, @thataddr, $that,$them, $proto,@now,
         $hertime);

     # timeclient --  a client for the Time Server program,
     # creates a socket and connects it to the server on
     # port 29688.
     # The client then expects the server to write server's
     # host time onto the socket, so the client simply does
     # a read on its socket, SOCK, to get the server's time
     #
     #
     #                 Usage:  timeclient [server_host_name]
     #
     print "Hi, I'm in perl program \'client\' \n";
4    $remote = shift || 'localhost' ;
5    $port = 29688 ;  # timeserver is at this port number
6    @thataddr=gethostbyname($remote);

7    $that = pack('Sna4x8', AF_INET, $port, $thataddr[4]);

8    $proto = getprotobyname('tcp');

     # Make the socket filehandle

9    if ( socket(SOCK, PF_INET, SOCK_STREAM, $proto ) ){
         print "Socket ok.\n";
     }
     else { die $!; }

     # Call up the server
10   if (connect(SOCK, $that)) {
         print "Connect ok.\n";
     }
     else { die $!;}

     # Set socket to be command buffered
11   SOCK->autoflush;

     # Now we're connected to the server, let's read her host time
12   $hertime = <SOCK>;
13   close(SOCK);

14   print "Server machine time is: $hertime\n";
15   @now = localtime($hertime);
16   print "\tTime-$now[2]:$now[1] ",
            "Date-",$now[4]+1,"/$now[3]/$now[5]\n";

(Output)
$ perl timeserver
Port = 29688
Socket ok.
Bind ok.
Listen ok.
In loop.

$ perl timeclient
Hi, I'm in perl program 'client'
Socket ok.
Connect ok.
Server machine time is: 981415699
        Time-15:28 Date-2/5/07

					  

Explanation

  1. The Socket module will be used in this program.

  2. The strict pragma is used to ensure that variables used in this program are "safe."

  3. These variables will be used later in the program. They must be declared with my because the strict pragma enforces this as a safety feature.

  4. A server's hostname may be passed as a command-line argument and shifted into the $port variable. If the ARGV array is empty, the scalar $port is assigned localhost.

  5. The client gets the server's port number if it was assigned a value.

  6. Now the client gets the server's official address. The raw network address information is obtained by gethostbyname.

  7. The address for the server's Internet domain and port number is packed into a binary structure consisting of an unsigned short, a short in "network" order, four ASCII characters, and 8 null bytes.

  8. The tcp protocol information is returned.

  9. The socket function creates an Internet domain, connection-oriented socket filehandle, SOCK.

  10. The connect function connects the client's socket to the server's address.

  11. The autoflush method forces the socket's buffers to be flushed after prints and writes.

  12. Perl reads from the SOCK filehandle. The server's time is retrieved via the socket.

  13. The socket is closed.

  14. The time value (number of non-leap-year seconds since 1/1/1970, UTC) retrieved from the server is printed.

  15. The time is converted to local time and assigned to array @now.

  16. The converted time is printed.

 

Previous Page Next Page