By Stew Benedict
In a previous article, I discussed the TCP protocol, its purpose, and a number of UNIX system commands you can use to look at TCP operations and control TCP transactions. Now, I'll present a simple programming example that shows you how to use TCP, or “socket programming,” to implement a client/server application with Perl.
Beware of buffer overflows
A buffer overflow is a common security flaw that's often exploited. It has led to many security updates for various applications. What can happen in this scenario is that your server program may be written to accept a string from the client application. If you're not careful in how you accept it into your input buffer, and if the buffer is of fixed length, an unfriendly client could overflow the buffer, possibly gain undesired access to the server system, and run its own code. Other higher-level languages have built-in protection against buffer overflows by dynamically resizing variables or other methods.
Creating a server
To create a server program, we need to:
- · Create a socket.
- · Bind the socket to a port.
- · Listen to the port and accept client connections.
We'll make a simple server that responds to requests by printing the output of the w command, which shows who is logged in to a system and what they are doing, as well as the system time, uptime, and load average. This could be handy if you are administering a number of servers, as you could set up a cron job to access each one and report back the system stats. The code does not do any checking of the calling parameters, expecting only the port number to watch, defaulting to 9876 if not specified. Listing A shows the server code.
To run the program:
[stew@moe stew]$ ./socket-serv.pl
Using port: 9876
The server just sits, waiting for a client connection. Hit [Ctrl]C to kill the server. You can quickly test the server without even writing a client using Telnet, as shown in Listing B.
Let's take a closer look at what the code in Listing A is doing. The first four lines determine whether a port is specified; if one isn't, the code assigns 9876. In general, you want to use port numbers greater than 1024 for your own servers, as 1024 and lower have defined uses by common services. The AF_INET and SOCK_STREAM assignments are the values defined in /usr/include/bits/socket.h (Linux 2.4 source tree) for the address and socket type.
The $sockaddr variable defines the format that will be used for the address and port number by the bind call. It consists of an unsigned short (S), a short in network order (n), four ASCII characters (a4), and eight null bytes (x8). Next, we retrieve the protocol number to be used, print a status line, and define the server address.
The select(SOCK) line specifies SOCK as the default output file handle, with the buffers set to be flushed at every write or print.
The next three lines create the socket, SERVER, bind to it, and start listening to it, with a maximum client queue of five.
The remaining endless loop just waits for accesses to the socket, responding with the output of the w command passed to the client.
Creating a client
Although we easily demonstrated the client side using Telnet, we can also write a proper client program in Perl. To do this, we need to:
- · Create a socket.
- · Connect the socket to the remote machine running the server.
- · Grab the server output and print to stdout and then exit.
Listing C shows the client code, and Listing D shows sample output when the client is executed.
The client code is almost identical to the server. Variables are defined, the server port address is retrieved, and a connection to the socket is established. Again, we use the SOCK file handle as stdout and set the buffers to flush on each print or write. We read server input from the socket connection, print the output on stdout, and exit.
You can significantly simplify the code by using the Perl IO::Socket module. This reduces the socket setup code significantly. You may already have this module on your system or you can retrieve it from CPAN:
perl -MCPAN -e 'install IO::Socket'
Using the socket module, the server code changes to something resembling Listing E, and the client code looks like Listing F.
As you can see, IO::Socket takes care of a lot of the details of setting up the socket connection for you.
That's it. You now have a working pair of TCP client-server applications. You can readily change the output from the server by changing a couple of lines in the server loop, and there are a number of things you can do to enhance the functionality of either the client or server. For instance, on the server side, you could:
- · Add logging capabilities to record accesses in the system logs.
- · Add a parsing engine to allow the server to respond to different types of client requests.
- · Rework the server into a forked, parent/child process so that it can respond to multiple client requests.
I hope these simple examples have given you some ideas for projects where you can use UNIX sockets for communications between systems. Thanks to the foresight of the designers of TCP/IP, it is capable of handling quite powerful interactions, with very little code, when using a high-level language such as Perl.