the djb way

ucspi-tcp


howdyd: a howdy daemon, part 1

Hi there!

Imagine you want to implement a server for the howdy! protocol:

The client connects on the howdy port of the server. The server emits a greeting and disconnects. The client reads the greeting and disconnects.

We made up the howdy! protocol just now, but it looks an awful lot like the hello protocol, assigned to the hello port 1789. In fact, since they are so similar, we'll go ahead and use port 1789 for our howdy port.

Here's the first cut at our howdy server:


#!/bin/sh
# howdyd.sh
# a howdy daemon
echo "Hi there!"
exit 0
### that's all, folks!

Whoa, wait a minute. A two-line script? Where's all the connection code, socket interface, system logger, stuff like that?

Answer: all that stuff is going to be handled by daemontools and ucspi-tcp, with most of the heavy lifting done by the ucspi-tcp tcpserver utility.

Let's go ahead and set up a full-blown daemontools service for the howdyd daemon to show how all this works. First, add one group and two user accounts to your system:

The "nofiles" group can be added directly to /etc/group with your favorite editor. Pick any gid number you like. This group will get a lot of use on the djb way.

Add the two users "howdyd" and "multilog" with your system's /etc/passwd maintenance utility; vipw(8) is as good as any. Pick any uid numbers you like. These accounts should have the password field asterisked to disable login, with the home directory and login shell fields both set to something like "/nonexistent".

The "multilog" user will get a lot of use on the djb way as the daemontools setuidgid user for the multilog logger. The "howdyd" user will be the unpriveleged account used to run our howdyd service.

After the users are added, make directories for the service definition:

# mkdir -p /var/svc.d/howdyd/log

Set up this daemontools run script in /var/svc.d/howdyd/run:


#!/bin/sh
# howdyd/run
# daemontools run script for howdyd service
CONLIMIT=13
exec 2>&1
exec setuidgid howdyd softlimit -m2000000 \
  tcpserver -v -rh -l0 \
  -c ${CONLIMIT} \
  -x /etc/tcprules/howdy.cdb \
  0 1789 \
    ./howdyd.sh

### that's all, folks!

The run script should be executable:

# cd /var/svc.d/howdyd
# chmod 755 run

This run script has a lot of the daemontools and ucspi-tcp bells and whistles. We'll explore them in some detail as we go on.

Now, copy/edit the howdyd.sh script from above into /var/svc.d/howdyd/howdyd.sh and make it executable:

# chmod 755 howdyd.sh

Notice that howdyd.sh is the executable tcpserver invokes in the last line of the run script. You can test howdyd.sh now if you want, to make sure it is working as expected:

# ./howdyd.sh
Hi there!

We also want a logging service for howdyd. Copy/edit this run script into /var/svc.d/howdyd/log/run:


#!/bin/sh
# howdyd/log/run
# logging service for howdyd daemon
exec setuidgid multilog multilog t /var/multilog/howdyd

Make the script executable with chmod 755. The first instance of multilog in the exec line is the first argument to setuidgid. This sets up the non-priveleged user "multilog" for the process that follows. The second instance of "multilog", then, is the daemontools executable multilog. So multilog the program will run under "multilog" the user.

Now setup the directory for the multilog log files:

# mkdir -p /var/multilog/howdyd
# chown multilog /var/multilog/howdyd

One last step now before we start the service. Our tcpserver invocation in the howdyd/run script uses the -x option, specifying access rules defined in /etc/tcprules/howdy.cdb. The access rules are in a fast hashing database, compiled from a text file in uscpi-tcp tcprules format. As an example, create this file in /etc/tcprules/howdy.rules:


# howdy.rules
127.:allow
10.0.:allow,SOMEVAR="somevalue"
:deny

This example will permit connections from localhost addresses, as well as all clients from the 10.0.0.0/16 network. In addition, for clients connecting from the 10.0.0.0/16 network, tcpserver will invoke howdyd with the environmental variable $SOMEVAR set to "somevalue". All other hosts will be denied.

When you have the howdy.rules setup as you want, compile them into the cdb database used by tcpserver:

# (cd /etc/tcprules; make howdy.cdb)

The makefile we installed in /etc/tcprules/Makefile invokes tcprules and builds the cdb database in /etc/tcprules/howdy.cdb for us. If you were to call the tcprules utility on the command line directly, it would look like this:

# cd /etc/tcprules
# tcprules howdy.cdb howdy.tmp < howdy.rules
# chmod 444 howdy

That completes the setup for the howdyd server! To activate the service, just link it into the /service directory:

# ln -s /var/svc.d/howdyd /service/howdyd

The new service should start up automatically within a few seconds. To check it out, try a telnet connection from the server itself:

$ telnet 127.0.0.1 1789
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Hi there!
Connection closed by foreign host.

Looks like it's working! Try again, this time connecting to the IP address of the server, and using the ucspi-tcp tcpcat utility instead of telnet:

$ tcpcat 10.0.1.16 1789
Hi there!

Ucks-pee, ooks-pie, look at your server, saying "hi!"

Continued in part 2...


Copyright © 2003, 2004, Wayne Marshall.
All rights reserved.

Last edit 2004.10.04, wcm.