<?xml version="1.0" encoding="UTF-8"?>

<?xml-stylesheet type="text/xsl" href="C:\Documents and Settings\Administrator\My Documents\Articles\1 - Templates\dw-document-html-2.1.xsl"?>
<dw-document xmlns:dw="http://www.ibm.com/developerWorks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="C:\Documents and Settings\Administrator\My Documents\Articles\1 - Templates\dw-document-2.1.xsd">

<!-- SKILL LEVEL 
If known, specify the (skill) level, 1 to 5 -->
<dw-article toc="auto" ratings="auto" level="1" localsite="worldwide" related_contents="auto">
<id domino-uid=""/>

<meta name="keywords" content="image, raster, imagemagick, convert, rotate, scale, thumbnail, resize, artistic"/>
<!-- Fill in or leave blank for production to fill in.  -->
<meta name="lastupdate" content="20040221mbs"/>

<zone name="linux"/>

<seriestitle></seriestitle>
<title>Implementing UDP services with shell scripts</title>
<subtitle></subtitle>
<!--  End TITLE -->

<!-- Ignore these lines. -->
<!-- <forumurl url=""/> -->
<!-- <zip url=""/> -->
<!-- <pdf size="" url=""/> -->

<author company="TOWER Software" email="mikal@stillhq.com" jobtitle="Senior Software Engineer" name="Michael Still">
<bio>Michael has been working in the image processing field for several years, including a couple of years managing and developing large image databases for an Australian government department. He currently works for TOWER Software, who manufacture a world leading EDMS and Records Management package named TRIM. Michael is also the developer of Panda, an open source PDF generation API, as well as a bunch of other Open Source code.</bio></author>
<!--  End AUTHOR -->

<!-- DATE INFO
Fill in or leave blank for production to fill in.-->
<date month="02" day="21" year="2004"/>

<abstract miniabstract="">This article discusses how to implement UDP servers using scripting languages such as shell as perl. It also contains a brief discussion of the differences between TCP and UDP, as well as connected and disconnected sockets.</abstract>

<column-info col-name="" col-icon=""/>
<docbody>

<p>
This article is about two things. The main focus of the article is to discuss how to write useful UDP servers in a common scripting language such as bash. The other, more minor, focus of the article is to give a brief tutorial on the differences between disconnected and connected sockets.
</p>

<p>
In this article I assume that you have a working knowledge of both bash scripting and C programming. If you don't then hopefully you'll still get something out of the article, but you might want to trust me about what the code snippets actually do.
</p>





















<heading refname="" type="major" toc="yes" name="" alttoc="">UDP</heading>
<p>
It strikes me as logical to start with an extremely brief introduction to UDP. At some points it makes sense to compare this with the alternative, TCP.
</p>

<p>
Both UDP and TCP sit on top of the Internet protocol, which handles all the plumbing of actually getting the data out of the back of the client machine to the server. This is where the similarity ends. UDP is the <emphasis>User Datagram Protocol</emphasis>, and is unreliable. On the other hand, TCP is the <emphasis>Transmission Control Protocol</emphasis>, and is reliable.
</p>

<p>
What is reliability? Well, TCP will hold your hand and ensure for you that every packet you send is received by the other machine. It will also ensure that the packets arrive in the right order. There are also some games played with the choice of initial sequence number to make it harder for man in the middle attacks to be successful.
</p>

<p>
UDP is unreliable, which means it does none of this for you. It is the programmer's responsibility to ensure that all the packets sent arrived, and were in the right order.
</p>

<p>
So why would you ever use UDP? This reliability in TCP comes at a price. That price is performance. Before a TCP connection is established, the following protocol sequence occurs:
</p>

<heading refname="" type="figure" toc="no" name="" alttoc="">Figure 1. The TCP handshake</heading>
<img src="tcp-handshake.png" width="191" height="155" alt="The TCP handshake"/>
</figure>

<p>
So before the two machines can even communicate, they've spent a round trip, and processing time, just setting up the connection (the ACK can be sent at the same time as the first packet, as we don't have to wait for an ACK ACK to come back).
</p>

<p>
UDP, on the other hand, does none of this. A single packet is sent, and it either arrives or it doesn't. Normally the client application will note that a reply was never received after a given timeout, and retransmit the packet.
</p>

<p>
A good example of a common network protocol that uses UDP is the Domain Name System (DNS). UDP is well suited here because the packets are short (they fit in a single datagram) and we therefore don't have to worry about our packets arriving out of order. We also want DNS to be as fast as possible. 
</p>

<sidebar>
<heading refname="" type="sidebar" toc="no" name="" alttoc="">DNS and UDP</heading>
<p>
It should be noted that RFC 1034 "Domain names &mdash; Concepts and Facilities" does specify that TCP can also be used for DNS. In fact, because UDP packets are limited to 512 bytes, TCP has to be used for zone transfers. However, RFC 1035 "Domain Names &mdash; Implementation and Specification"does recommend that normal queries occur via UDP.
</p>
</sidebar>

<p>
See the further reading section at the end of the this article for recommendations of places to read more about UDP, IP, and DNS.
</p>

<heading refname="" type="minor" toc="yes" name="" alttoc="">Disconnected sockets</heading>
<p>
By default when a UDP socket is created, it is disconnected. What's a disconnected socket? The short answer is that it isn't associated with any given remote machine. This means that each time we fetch data from the socket we have to use the <emphasis>recvfrom</emphasis>(2) function call:
</p>

<heading refname="" type="code" toc="no" name="" alttoc="">Listing 1. recvfrom parameters</heading>
<code type="section">
int recvfrom(int s, void *buf, 
             size_t len, int flags, 
             struct sockaddr *from, 
             socklen_t *fromlen);
</code>

<p>
The <i>struct sockaddr</i> argument in this function call is populated with enough information for the program to be able to determine where to send the reply packet. The response needs to be sent with the <i>sendto</i>(2) call:
</p>

<heading refname="" type="code" toc="no" name="" alttoc="">Listing 2. sendto parameters</heading>
<code type="section">
int sendto(int s, 
           const void *msg, 
           size_t len, 
           int flags, 
           const struct sockaddr *to, 
           socklen_t tolen);
</code>

<p>
The <i>sendto</i> function call takes another <i>struct sockaddr</i> argument which specifies where the packet should be sent.
</p>

<p>
The code listing below is an example of how to use the <i>recvfrom</i> and <i>sendto</i> functions. This example is a simple UDP echo server. The program waits on a given port, and whenever it receives data, sends it straight back.
</p>

<heading refname="" type="code" toc="no" name="" alttoc="">Listing 3. Disconnected sockets</heading>
<code type="section">
// Disconnected UDP socket example: this example 
// simply reads from clients (there can be more 
// than one), and returns what they said straight 
// back to them. You'll note that we can't use read 
// and write to get to the traffic, as this is not 
// available for disconnected UDP sockets.

#include &lt;stdio.h&gt;
#include &lt;errno.h&gt;
#include &lt;unistd.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/socket.h&gt;
#include &lt;netinet/in.h&gt;

int
main (int argc, char *argv[])
{
  int lfd;
  struct sockaddr_in servaddr;
  struct sockaddr clientaddr;
  char buf[1024];
  size_t len;
  socklen_t clen;

  // We will listen with this file descriptor
  if ((lfd = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
    {
      fprintf (stderr, "Error whilst starting to listen\n");
      exit (42);
    }

  // Define what we are listening for
  bzero (&amp;servaddr, sizeof (servaddr));
  servaddr.sin_family = AF_INET;
  servaddr.sin_addr.s_addr = htonl (INADDR_ANY);
  servaddr.sin_port = htons (1234);

  // Bind to the address
  if (bind (lfd, (struct sockaddr *) &amp;servaddr, sizeof (servaddr)) < 0)
    {
      perror ("Couldn't bind");
      exit (42);
    }

  // Do stuff
  while (1)
    {
      len = 1024;
      printf ("Reading...\n");
      clen = sizeof (clientaddr);
      if ((len = recvfrom (lfd, buf, len, 0,
                           (struct sockaddr *) &amp;clientaddr, &amp;clen)) < 0)
        {
          perror ("Socket read error");
          exit (42);
        }
      if (len == 0)
        break;

      // The buffer is not null terminated
      buf[len] = '\0';
      printf ("Read: %s\n", buf);

      // And send it straight back
      if (sendto (lfd, buf, len, 0, &amp;clientaddr, clen) < 0)
        {
          perror ("Socket write error");
          exit (42);
        }
    }
}
</code>

<p>
Let's have a look at this program running. As we can see from the source code, the program listens on UDP port 1234 (very imaginative). We can use <b>netcat</b> to test the program. First, we need to start the server in a different terminal, this is as simple as running it.
</p>

<sidebar>
<heading refname="" type="sidebar" toc="no" name="" alttoc="">netcat rocks</heading>
<p>
<b>netcat</b> rocks. Its a little application which lets you push data to arbitrary port numbers using both UDP and TCP. It can also be used to create very simple servers, because <b>netcat</b> can do all the listening for you. For more information, and download details, checkout http://www.atstake.com/research/tools/network_utilities/.
</p>
</sidebar>

<p>
A client interaction with the server looks like this:
</p>

<heading refname="" type="code" toc="no" name="" alttoc="">Listing 4. Testing the UDP echo server</heading>
<code type="section">
[mikal@localhost article]# nc -u localhost 1234
hello
hello
out
out
there
there
 punt!
[mikal@localhost article]#
</code>

<p>
Note that the "punt!" is me hitting control c on <b>netcat</b>. The server output for this session is:
</p>

<heading refname="" type="code" toc="no" name="" alttoc="">Listing 5. UDP echo server output</heading>
<code type="section">
[root@localhost sockets]# ./disconnected 
Reading...
Read: hello

Reading...
Read: out

Reading...
Read: there

Reading...
</code>

<p>
There are a couple of things worth noting here. Firstly, the UDP server can handle multiple clients at once in the single process. This is because each packet comes in, and is then responded to &mdash; there is no assumption made that packets come from the same machine. This model works well for the single packet communication paradigm. Secondly, since the server simply waits for a packet, responds, and then starts waiting again, the server survives disconnects from the client, it simply waits until a new packet comes along.
</p>


<heading refname="" type="minor" toc="yes" name="" alttoc="">Disconnected sockets and scripting</heading>
<p>
This article is really about writing useful UDP servers using scripting languages &mdash; specifically <b>bash</b>. In this instance, the disconnected nature of these default UDP sockets causes great pain. This is because there is no trivial way in bash to call <i>recvfrom</i> and <i>sendto</i>. Shell scripts want to be able to use the standard <i>read</i> and <i>write</i> calls, because these languages are intended for file and pipe input output, as opposed to socket traffic.
</p>

<p>
We can demonstrate this by writing a very simple, and probably quite impractical <b>inetd</b> server.
</p>

<sidebar>
<heading refname="" type="sidebar" toc="no" name="" alttoc="">What is inetd?</heading>
<p>
<b>inetd</b>, and its fairly common <b>xinetd</b> alternative are super daemons. Their role in the networking food chain is as a way of running programs to process network traffic as demand requires. For example, many <b>rsync</b> servers operate from <b>inetd</b>. What this means is that <b>inetd</b> runs in the background as a daemon waiting for connections from clients to the <b>rsync</b> port. It then starts a new copy of <b>rsync</b> for each of these connections, and <b>rsync</b> processes the traffic (and perhaps responds).
</p>

<p>
Many developers write network servers which are intended to be run by <b>inetd</b> because it simplifies development of the application. <b>inetd</b> handles most of the plumbing for the application.
</p>

<p>
Please bear in mind that <b>inetd</b> and <b>xinetd</b> are very configurable, and this is only a general description. There is a brief discussion on how to configure <b>inetd</b> and <b>xinetd</b> later in this article. You should refer to the relevant documentation for more information.
</p>
</sidebar>

<p>
This is a particularly useful example, because our shell scripts are eventually going to run from <b>inetd</b>, so having an understanding of how it works is opportune.
</p>

<heading refname="" type="code" toc="no" name="" alttoc="">Listing 6. A disconnected inetd</heading>
<code type="section">
// Disconnected UDP socket example: this example 
// simply waits for traffic, and the starts a 
// process to deal with the results. One process 
// per packet, one packet per process. This version 
// wont work, because the socket is not connected. 
// In fact, cat is smart enough to warn us about 
// this:
//
//      cat: write error: Transport endpoint is not 
//      connected

#include &lt;stdio.h&gt;
#include &lt;errno.h&gt;
#include &lt;unistd.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/socket.h&gt;
#include &lt;sys/poll.h&gt;
#include &lt;netinet/in.h&gt;

int
main (int argc, char *argv[])
{
  int lfd;
  struct sockaddr_in servaddr;
  struct pollfd pfd;

  // We will listen with this file descriptor
  if ((lfd = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
    {
      fprintf (stderr, "Error whilst starting to listen\n");
      exit (42);
    }

  // Define what we are listening for
  bzero (&amp;servaddr, sizeof (servaddr));
  servaddr.sin_family = AF_INET;
  servaddr.sin_addr.s_addr = htonl (INADDR_ANY);
  servaddr.sin_port = htons (1234);

  // Bind to the address
  if (bind (lfd, (struct sockaddr *) &amp;servaddr, sizeof (servaddr)) < 0)
    {
      perror ("Couldn't bind");
      exit (42);
    }

  // Setup the list of file descriptors we want to 
  // wait for events on
  pfd.fd = lfd;
  pfd.events = POLLIN | POLLPRI;

  // Do stuff
  while (1)
    {
      if (poll (&amp;pfd, 1, -1) < 0)
        {
          perror ("Waiting for new data failed");
          exit (42);
        }

      printf ("Data arrived\n");

      // Spawn a child to handle this packet
      switch (fork ())
        {
        case -1:
          perror ("Couldn't spawn child to handle connection");
          exit (42);

        case 0:
          // Child process -- setup the file 
          // descriptors, and the run the helper 
          // application
          dup2 (lfd, 0);
          dup2 (lfd, 1);

          execl ("/bin/cat", "cat", NULL);
          perror ("Exec failed");
          exit (42);
          break;

        default:
          // Parent process
          break;
        }
    }
}
</code>

<p>
All this program does is wait on port 1234 until a connection comes in, forks, and the new child process starts executing the server program (in this case <b>cat</b>). This technique relies on the fact that the child process will share the environment of the parent process, including its file handles. These are duplicated to stdin and stdout before the server program starts executing, so that network input goes into stdin, and server output is sent back over the network.
</p>

<p>
This of course doesn't work because the socket is disconnected, so <b>cat</b>'s <i>read</i> call will work, but its <i>write</i> call fails because the socket layer doesn't know where to send the response to.
</p>










<heading refname="" type="minor" toc="yes" name="" alttoc="">Connected sockets</heading>
<p>
The answer to our need to use <i>read</i> and <i>write</i> for our shell scripts is a connected socket. A connected socket is simply a socket which has had the <i>connect</i>(2) function called upon it.
</p>

<p>
When the <i>connect</i> function is called, it simply records which machine the network packet came from, so that the socket layer knows where to send the reply packet when it is output with the <i>write</i> function call.
</p>

<p>
Here's a simple example of a connected socket version of the disconnected echo server above. Note that we now use <i>read</i> and <i>write</i> function calls for all the network input output.
</p>

<heading refname="" type="code" toc="no" name="" alttoc="">Listing 7. Connected sockets</heading>
<code type="section">
// Connected UDP socket example: this example 
// simply reads from clients (there can be more 
// than one), and returns what they said straight 
// back to them. You'll note that we can now use 
// read and write to get to the traffic...

#include &lt;stdio.h&gt;
#include &lt;errno.h&gt;
#include &lt;unistd.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/socket.h&gt;
#include &lt;netinet/in.h&gt;

int
main (int argc, char *argv[])
{
  int lfd;
  struct sockaddr_in servaddr;
  struct sockaddr clientaddr;
  char buf[1024];
  size_t len;
  socklen_t clen;

  // We will listen with this file descriptor
  if ((lfd = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
    {
      fprintf (stderr, "Error whilst starting to listen\n");
      exit (42);
    }

  // Define what we are listening for
  bzero (&amp;servaddr, sizeof (servaddr));
  servaddr.sin_family = AF_INET;
  servaddr.sin_addr.s_addr = htonl (INADDR_ANY);
  servaddr.sin_port = htons (1234);

  // Bind to the address
  if (bind (lfd, (struct sockaddr *) &amp;servaddr, sizeof (servaddr)) < 0)
    {
      perror ("Couldn't bind");
      exit (42);
    }

  // Do stuff
  while (1)
    {
      // We need to peek at the first part of the 
      // packet to determine who to connect to
      len = 1;
      printf ("Reading...\n");
      clen = sizeof (clientaddr);
      if ((len = recvfrom (lfd, buf, len, MSG_PEEK,
                           (struct sockaddr *) &amp;clientaddr, &amp;clen)) < 0)
        {
          perror ("Socket peek error");
          exit (42);
        }
      if (len == 0)
        break;

      // Connect
      if (connect (lfd, &amp;clientaddr, clen) < 0)
        {
          perror ("Could not connect");
          exit (42);
        }

      // And now we can just use the normal read 
      // and write
      len = 1024;
      if ((len = read (lfd, buf, len)) < 0)
        {
          perror ("Socket read error");
          exit (42);
        }
      if (write (lfd, buf, len) < 0)
        {
          perror ("Socket write error");
          exit (42);
        }
    }
}
</code>

<p>
We do cheat a little in this example, there is one <i>recvfrom</i> call. This is used to "peek" at the data which is waiting on the socket to determine the address to which we will connect. The <i>MSG_PEEK</i> means that the data is not actually removed from the queue of data to be processed by this call.
</p>

<p>
I won't include an example of what this program looks like when it runs, because it looks exactly the same as the disconnected echo server above.
</p>


<heading refname="" type="minor" toc="yes" name="" alttoc="">Back to our simple inetd server</heading>
<p>
You'll recall that a couple of examples ago I showed you the code for a simple <b>inetd</b> server. This example below expands on that so it uses a connected socket. This example will behave just like the hard coded echo server in the first and third examples in this article (except that the server debugging output doesn't happen any more).
</p>

<heading refname="" type="code" toc="no" name="" alttoc="">Listing 8. A connected inetd</heading>
<code type="section">
// Connected UDP socket example: this example this 
// example simply waits for traffic, and the starts 
// a process to deal with the results. One process 
// per packet, one packet per process. You'll note 
// that we can now use read and write to get to the 
// traffic, and that this all works...

#include &lt;stdio.h&gt;
#include &lt;errno.h&gt;
#include &lt;unistd.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/socket.h&gt;
#include &lt;netinet/in.h&gt;

int
main (int argc, char *argv[])
{
  int lfd;
  struct sockaddr_in servaddr;
  struct sockaddr clientaddr;
  char buf[1024];
  size_t len;
  socklen_t clen;

  // We will listen with this file descriptor
  if ((lfd = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
    {
      fprintf (stderr, "Error whilst starting to listen\n");
      exit (42);
    }

  // Define what we are listening for
  bzero (&amp;servaddr, sizeof (servaddr));
  servaddr.sin_family = AF_INET;
  servaddr.sin_addr.s_addr = htonl (INADDR_ANY);
  servaddr.sin_port = htons (1234);

  // Bind to the address
  if (bind (lfd, (struct sockaddr *) &amp;servaddr, sizeof (servaddr)) < 0)
    {
      perror ("Couldn't bind");
      exit (42);
    }

  // Do stuff
  while (1)
    {
      // We need to peek at the first part of the 
      // packet to determine who to connect to
      len = 1;
      printf ("Reading...\n");
      clen = sizeof (clientaddr);
      if ((len = recvfrom (lfd, buf, len, MSG_PEEK,
                           (struct sockaddr *) &amp;clientaddr, &amp;clen)) < 0)
        {
          perror ("Socket peek error");
          exit (42);
        }
      if (len == 0)
        break;

      // Connect
      if (connect (lfd, &amp;clientaddr, clen) < 0)
        {
          perror ("Could not connect");
          exit (42);
        }

      printf ("Data arrived\n");

      // Spawn a child to handle this packet
      switch (fork ())
        {
        case -1:
          perror ("Couldn't spawn child to handle connection");
          exit (42);

        case 0:
          // Child process -- setup the file 
          // descriptors, and the run the helper 
          // application
          dup2 (lfd, 0);
          dup2 (lfd, 1);

          execl ("/bin/cat", "cat", NULL);
          perror ("Exec failed");
          exit (42);
          break;

        default:
          // Parent process
          break;
        }
    }
}
</code>

<heading refname="" type="minor" toc="yes" name="" alttoc="">inetd and xinetd configuration</heading>
<p>
The two standard inetd implementations that most people use are called <b>inetd</b> and <b>xinetd</b>. <b>inetd</b> doesn't implement a connected socket for the server before starting it, which means that it is effectively impossible to write a standard shell script which processes UDP network traffic using a stock <b>inetd</b>. <b>xinetd</b> however does connect the socket before calling the script.
</p>

<p>
It seems logical to me to include a patch to make <b>inetd</b> behave in the manner that I would like it to. After all, if our goal is as noble as implementing a server in shell script, then surely the operating system should be modified to accommodate us?
</p>

<heading refname="" type="minor" toc="yes" name="" alttoc="">inetd patch</heading>
<p>
The package which contains <b>inetd</b> as run on Debian is called <i>netkit</i>. The following is a patch to use connected sockets for UDP and TCP servers.
</p>

<heading refname="" type="code" toc="no" name="" alttoc="">Listing 9. netkit patch</heading>
<code type="section">
diff -ur netkit-base-0.16/inetd/inetd.c netkit-base-0.16-hacked/inetd/inetd.c
--- netkit-base-0.16/inetd/inetd.c	Wed Nov 24 06:31:53 1999
+++ netkit-base-0.16-hacked/inetd/inetd.c	Thu Jan 23 13:42:59 2003
@@ -470,7 +470,30 @@
 			return;
 		}
 		if (pid==0) {
+		        char buf[2];
+			size_t len = 1;
+			struct sockaddr clientaddr;
+			socklen_t clen;
+			
 			/* child */
+			len = sizeof(clientaddr);
+			if ((len = recvfrom(ctrl, buf, len, MSG_PEEK, 
+					   (struct sockaddr *) &amp;clientaddr, 
+					   &amp;clen)) < 0) {
+			        syslog(LOG_WARNING, "failed to peek for (for %s): %m",
+				       sep->se_service);
+			}
+			if (len==0) {
+			        syslog(LOG_WARNING, "no data (for %s): %m",
+				       sep->se_service);
+			}
+
+			// Connect
+			if (connect(ctrl, &amp;clientaddr, clen) < 0) {
+			        syslog(LOG_WARNING, "connect failed (for %s): %m",
+				       sep->se_service);
+			}
+
 			dup2(ctrl, 0);
 			close(ctrl);
 			dup2(0, 1);
</code>


<heading refname="" type="minor" toc="yes" name="" alttoc="">Configuring inetd</heading>
<p>
A simple example of how to configure <b>inetd</b> to handle our UDP echo server is shown below &mdash; the configuration file this line is added to is /etc/inetd.conf. It should be noted that <i>myecho</i> is the name of a service I had to add to /etc/services because <b>inetd</b> insists on all services being listed there...
</p>

<heading refname="" type="code" toc="no" name="" alttoc="">Listing 10. netkit inetd configuration example</heading>
<code type="section">
# An example UDP echo server
myecho    dgram   udp     nowait    root    /bin/cat cat -
</code>

<p>
This service has the following characteristics:
</p>

<ul>The service name is <i>myecho</i>. The listing in /etc/services is:

<heading refname="" type="code" toc="no" name="" alttoc="">Listing 11. /etc/services entry</heading>
<code type="section">
myecho		1234/udp			# Simple echo example for LCA 2004 paper
</code>

<li>The socket type is <i>dgram</i>, which is short for datagram, the type of packet that the UDP protocol uses.

<li>The protocol is UDP.

<li><i>nowait</i> is used to indicate that one server should be started per packet, instead of waiting for the previous server to exit. This argument should always be nowait for TCP servers.

<li>The server runs as root.

<li>The server is located at /bin/cat

<li>The server should be started with these arguments. Note that the name of the executable is always the first argument. Checkout <i>execve</i>(2) for more details.
</ul>

<sidebar>
<heading refname="" type="sidebar" toc="no" name="" alttoc="">Why not echo?</heading>
<p>
Note that we can't use the name <i>echo</i> for our service for a couple of reasons: it is already defined in /etc/services; it is an internal service which <b>inetd</b> already offers using the normal port number; and we wanted to define a different port number for the service, without breaking things which might use the old /etc/services entry.
</p>
</sidebar>

<heading refname="" type="minor" toc="yes" name="" alttoc="">Configuring xinetd</heading>
<p>
The same echo service configured with <b>xinetd</b> looks like this:
</p>

<heading refname="" type="code" toc="no" name="" alttoc="">Listing 12. xinetd configuration</heading>
<code type="section">
service myecho
{
        socket_type     = dgram
        protocol        = udp
        wait            = yes
        user            = root
        server          = /bin/cat
        server_args     = -
}
</code>

<p>
Again, the service is a dgram using the UDP protocol. <b>xinetd</b> will wait for previous requests to be processed before moving onto the next request. The echo service also runs as root, using cat. Note the difference in the way the server arguments are configured.
</p>

<p>
You should also note that on Red Hat systems, services are often configured within their own configuration files, located in /etc/xinetd.d &mdash; this is done to make it easier to add new services during package installation.
</p>
















<heading refname="" type="major" toc="yes" name="" alttoc="">shdns</heading>
<p>
This article wouldn't be complete without an example of a UDP shell script to run from <b>inetd</b> or <b>xinetd</b>. Something non-trivial is required here, so I present for your enjoyment <b>shdns</b>, a DNS server implemented in bash shell script.
</p>

<heading refname="" type="minor" toc="yes" name="" alttoc="">The code</heading>
<p>
The code for <b>shdns</b> is broken into two components. The first is a simple script which redirects the input packet to a file, so that <b>shdns</b> can move backwards and forwards within the input packet to grab the bits it needs as required.
</p>

<heading refname="" type="code" toc="no" name="" alttoc="">Listing 13. shdns-server</heading>
<code type="section">
#!/bin/bash

logger "shdns $$ Started listening"
/home/mikal/opensource/shdns/shdns /tmp/shdns-$$ &
cat - > /tmp/shdns-$$
logger "shdns $$ Waiting for processor to end"
wait
logger "shdns $$ Stopped listening"
</code>

<p>
The other part of the code is the script which actually parses the input DNS UDP packet and responds. Note that this is merely a proof of concept at the moment, it doesn't attempt to implement the entire DNS protocol &mdash; there is only enough here to perform a name lookup.
</p>

<heading refname="" type="code" toc="no" name="" alttoc="">Listing 14. shdns</heading>
<code type="section">
#!/bin/bash

#############
# shdns: take a query and build a response

# Set this to the location of the lookup
# file
execpath="/home/mikal/opensource/shdns/"

# The state of a given bit in the byte: 
# (byte, poweroftwo)
dumpbit(){
  local temp
  temp=$1

  if [ $1 -gt $(( $2 - 1 )) ]
  then
    echo -n "1"
    temp=$(( $1 - $2 ))
  else
    echo -n "0"
  fi

  return $temp
}

# Is a given bit on? (byte, poweroftwo)
testbit(){
  return `dumpbit $1 $2`
}

# Turn on a given bit in the byte: (initial byte 
# state, poweroftwo, desiredstate)
# Returns a decimal version of the byte
twiddlebit(){
  local temp
  temp=$1

  testbit $1 $2
  if [ $? = 1 ]
  then
    if [ $3 = 0 ]
    then
      temp=$(( $temp - $2 ))
    fi
  else 
    if [ $3 = 1 ]
    then
      temp=$(( $temp + $2 ))
    fi
  fi

  return $temp
}

# Spin until a byte exists: (filename, bytenumber, wait)
spinfor(){
  local len

  len=`cat $1 | wc -c | tr -d " "`
  while [ $len -lt $2 ]
  do
    logger "shdns $$ Spin on byte $2"
    usleep $3
    len=`cat $1 | wc -c | tr -d " "`
  done
}

# Read a single byte from a file: 
# (filename, bytenumber)
readbyte(){
  spinfor $1 $2 10 
  cat $1 | cut -b $2
}

# Read a range of bytes from a file: 
# (filename, startbyte, length)
readstring(){
  spinfor $1 $(( $2 + $3 - 1 )) 10
  logger "shdns $$ Getting byte range $1:$2-"$(( $2 + $3 - 1 ))":$3"
  cat $1 | cut -b $2-$(( $2 + $3 - 1 ))
}

# Read a single binary byte as decimal from a file: 
# (filename, bytenumber)
readbytebinary(){
  local temp

  spinfor $1 $2 1000
  return `cat $1 | cut -b $2 | od -Ad -t u1 | head -1 | tr -s " " | cut -f 2 -d " "`
}

# Output the bit for this value: 
# (inputvalue, byteoffset)
writebinarybit(){
  if [ $1 -gt $(( $2 - 1 )) ]
  then
    echo -n "1"
    return $(( $1 - $2 ))
  else
    echo -n "0"
    return $1
  fi
}

# Turn a number into a binary byte: (inputvalue)
writebinarybyte(){
  writebinarybit $1 128
  writebinarybit $? 64
  writebinarybit $? 32
  writebinarybit $? 16
  writebinarybit $? 8
  writebinarybit $? 4
  writebinarybit $? 2
  writebinarybit $? 1
}

# Output the byte which is represented by a decimal 
# number
tobyte(){
  local octal
  local quotient
  local remainder

  # Echo only takes octal numbers, so we convert
  quotient=$1
  if [ $quotient -eq 0 ]
  then
    octal="0"
  fi

  while [ $quotient -ne 0 ]
  do
    remainder=$(( $quotient % 8 ))
    octal="$remainder$octal"
    quotient=$(( $quotient / 8 ))
  done

  echo -n -e \\$octal
}

####################################################

process(){
  logger "shdns $$ Started parsing $1 at $2"
  inset=$2

  # Identification: 2 bytes
  readbytebinary "$1" $inset; idtopbyte=$?
    inset=$(( $inset + 1 ))
  readbytebinary "$1" $inset; idbotbyte=$? 
    inset=$(( $inset + 1 ))
  logger "shdns $$ Packet id portions: $idtopbyte $idbotbyte"
  id=$(( ($idtopbyte * 128) + $idbotbyte ))
  logger "shdns $$ Packet id: $id"

  # Flags: 2 bytes
  temp=`cat $1 | cut -b $inset |  od -Ad -c | head -1 | cut -f 2 -d " "`
    inset=$(( $inset + 1 ))
  testbit $temp 128; qr=$?
  testbit $temp 8; op=$?
  testbit $temp 4; aa=$?
  testbit $temp 2; trun=$?
  testbit $temp 1; rd=$?

  logger "shdns $$ Query / response: $qr"
  logger "shdns $$ Opcode: $op"
  logger "shdns $$ Authoritative answer: $aa"
  logger "shdns $$ Packet truncated: $trun"
  logger "shdns $$ Recursion desired: $rd"

  readbytebinary "$1" $inset; temp=$?
    inset=$(( $inset + 1 ))
  testbit $temp 128; ra=$?
  
  logger "shdns $$ Recursion available: $ra"
  
  # The number of questions is the next two bytes
  readbytebinary "$1" $inset; topbyte=$?
    inset=$(( $inset + 1 ))
  readbytebinary "$1" $inset; botbyte=$?
    inset=$(( $inset + 1 ))
  qcount=$(( ($topbyte * 128) + $botbyte ))
  logger "shdns $$ Number of questions: $qcount"
  
  # The number of answers is the next two bytes
  readbytebinary "$1" $inset; topbyte=$?
    inset=$(( $inset + 1 ))
  readbytebinary "$1" $inset; botbyte=$?
    inset=$(( $inset + 1 ))
  acount=$(( ($topbyte * 128) + $botbyte ))
  logger "shdns $$ Number of answers: $acount"
  
  # The number of authority RRs is the next two 
  # bytes
  readbytebinary "$1" $inset; topbyte=$?
    inset=$(( $inset + 1 ))
  readbytebinary "$1" $inset; botbyte=$?
    inset=$(( $inset + 1 ))
  authcount=$(( ($topbyte * 128) + $botbyte ))
  logger "shdns $$ Number of authorities: $authcount"
  
  # The number of additional RRs is the next two 
  # bytes
  readbytebinary "$1" $inset; topbyte=$?
    inset=$(( $inset + 1 ))
  readbytebinary "$1" $inset; botbyte=$?
    inset=$(( $inset + 1 ))
  addcount=$(( ($topbyte * 128) + $botbyte ))
  logger "shdns $$ Number of additionals: $addcount"
  
  ######################
  # For each question
  ######################
  
  len=42
  questioncount=0
  
  while [ $questioncount -lt $qcount ]
  do
    questionstart=$inset
    logger "shdns $$ Question"
    name=""
  
    namelength=0
    readbytebinary "$1" $inset; len=$?
    while [ $len -gt 0 ]
    do
      inset=$(( $inset + 1 ))
      name="$name"`readstring "$1" $inset $len`"."
      inset=$(( $inset + $len ))
      
      namelength=$(( $namelength + $len + 1 ))
      readbytebinary "$1" $inset; len=$?
    done
    inset=$(( $inset + 1 ))
    logger "shdns $$ Lookup: $name"
  
    # Type of question -- assume we only want the 
    # last byte for now...
    inset=$(( $inset + 1 ))
    readbytebinary "$1" $inset; type=$?
    error="none"
    temp="shdns $$ Determine the query type"
    case $type in
    1 ) temp="$temp A";;
    2 ) temp="$temp NS";;
    5 ) temp="$temp CNAME";;
    12 ) temp="$temp PTR";;
    13 ) temp="$temp HINFO";;
    15 ) temp="$temp MX";;
    * ) temp="Error: Unknown query type"; error="yes";;
    esac
  
    logger "$temp (error = $error)"
    inset=$(( $inset + 1 ))  

    # The class should always be 1 -- assume it's 
    # all in the last byte as well
    inset=$(( $inset + 1 ))
    readbytebinary "$1" $inset; class=$?
      inset=$(( $inset + 1 ))
    logger "shdns $$ Query class: $class"
  
    if [ "%$error%" = "%none%" ]
    then
      # Dodgy bug fix
      name=`echo $name | sed 's/\.$//'`
    
      # Lookup the name in the db file
      result=`grep "$name" "$execpath" lookup | tr -s "\t" | cut -f 2`
      logger "shdns $$ Result: $result"

      ##############################################
      # Now we need to build a response to the query

      # The id number we were handed gets handed 
      # straight back
      tobyte $idtopbyte > /tmp/shdns-response-$$
      tobyte $idbotbyte >> /tmp/shdns-response-$$

      # Flag this packet as being a reply 
      # (we currently never claim to be authoritive)
      tobyte 128 >> /tmp/shdns-response-$$
      tobyte 0 >> /tmp/shdns-response-$$

      # Number of questions (we have to return the 
      # question we are answering)
      tobyte 0 >> /tmp/shdns-response-$$
      tobyte 1 >> /tmp/shdns-response-$$
 
      # Number of answers
      tobyte 0 >> /tmp/shdns-response-$$
      tobyte 1 >> /tmp/shdns-response-$$

      # Number of authorities
      tobyte 0 >> /tmp/shdns-response-$$
      tobyte 0 >> /tmp/shdns-response-$$

      # Number of additionals
      tobyte 0 >> /tmp/shdns-response-$$
      tobyte 0 >> /tmp/shdns-response-$$

      # It's easy to return the question, we just 
      # copy it...
      echo -n `readstring $1 $questionstart $namelength` >> /tmp/shdns-response-$$
      tobyte 0 >> /tmp/shdns-response-$$
      tobyte 0 >> /tmp/shdns-response-$$
      tobyte $type >> /tmp/shdns-response-$$
      tobyte 0 >> /tmp/shdns-response-$$
      tobyte $class >> /tmp/shdns-response-$$

      # The domain name we are answering for in 
      # this answer
      echo -n `readstring $1 $questionstart $namelength` >> /tmp/shdns-response-$$
      tobyte 0 >> /tmp/shdns-response-$$

      # The type is the same as in the question
      tobyte 0 >> /tmp/shdns-response-$$
      tobyte $type >> /tmp/shdns-response-$$

      # The class is the same as well
      tobyte 0 >> /tmp/shdns-response-$$
      tobyte $class >> /tmp/shdns-response-$$

      # The time to live is always low, because we 
      # are dodgy
      tobyte 0 >> /tmp/shdns-response-$$
      tobyte 0 >> /tmp/shdns-response-$$
      tobyte 0 >> /tmp/shdns-response-$$
      tobyte 4 >> /tmp/shdns-response-$$

      # The length of the returned data is always 
      # an IP (32 bits)
      tobyte 0 >> /tmp/shdns-response-$$
      tobyte 4 >> /tmp/shdns-response-$$

      # And now the answer as a number
      # For the result in the answer, we are going 
      # to need this in a binary form of decimal
      while [ "%$result%" != "%%" ]
      do
        temp=`echo $result | cut -f 1 -d "."`
        result=`echo $result | sed 's/^[0-9]*\.*//'`
        tobyte $temp >> /tmp/shdns-response-$$
        logger "shdns $$ Processing result segment: $temp ($result)"
      done

      # And now we can send the result (done this 
      # way to make sure it is all in one packet)
      cat /tmp/shdns-response-$$
      logger "shdns $$ Finished extracting result"
    else
      # Work out the erroneous type
      logger "shdns $$ Erroneous type was $type ("`dumpbyte $type`")"
    fi

    logger "shdns $$ Finished processing question"
    questioncount=$(( $questioncount + 1 ))
  done

  return $inset`twiddlebit 0 128 1`
}

####################################################
# The entry point for the script
offset=1
spinfor $1 $offset
process $1 $offset
offset=$?
logger "shdns $$ Ended at $offset"
killall -9 cat
exit
</code>

<p>
The <b>shdns</b> implementation strategy is summarized by the following diagram.
</p>

<heading refname="" type="figure" toc="no" name="" alttoc="">Figure 2. The design of shdns</heading>
<img src="shdns-design.png" width="298" height="384" alt="The design of shdns"/>
</figure>

<p>
You'll note that the reply to the DNS request is constructed in a temporary file and then sent back to the client. This is because <b>inetd</b> will send the output of each <i>write</i> as its own packet. <b>shdns</b> therefore needs to force all of the reply packet to be written at one time.
</p>


<heading refname="" type="minor" toc="yes" name="" alttoc="">Setting up shdns</heading>
<p>
The only configuration required to run <b>shdns</b>, apart from configuring you <b>inetd</b> or <b>xinetd</b> daemon, is to put a file named lookup in the path pointed at by the variable <i>execpath</i> in the <b>shdns</b> source file.
</p>

<p>
Here's a sample lookup file:
</p>

<heading refname="" type="code" toc="no" name="" alttoc="">Listing 15. The lookup file</heading>
<code type="section">
www.stillhq.com	127.0.0.1
</code>

<p>
You also need a <b>usleep</b> on your path. I couldn't find a Debian package containing this, so I wrote my own. If you need to install a <b>usleep</b>, then just compile usleep.c with a <i>gcc -o usleep usleep.c</i>, and copy it somewhere on your path.
</p>



</docbody>

<relatedlist>
TODO
</relatedlist>

<resourcelist>
<ul>
<ul>A list of DNS related Internet RFCs -- <a href="http://www.faqs.org/rfcs/dns-rfcs.html">http://www.faqs.org/rfcs/dns-rfcs.html</a>
<ul>An excellent introduction to DNS, as well as many other protocols -- TCP/IP Illustrated, Volume One: The protocols, by W Richard Stevens, published by Addison-Wesley
<ul>The Freshmeat page for NetKit -- <a href="http://freshmeat.net/projects/netkit/?topic_id=150">http://freshmeat.net/projects/netkit/?topic_id=150</a>
<ul>The xinetd home page -- <a href="http://www.xinetd.org/">http://www.xinetd.org</a>
<ul>The source code for shdns -- <a href="http://www.stillhq.com/extracted/shdns/">http://www.stillhq.com/extracted/shdns/</a>
</ul>
</resourcelist>
</dw-article>
</dw-document>