Dusk Gopher Daemon

Serve Gopher requests under inetd(8)

This project is a Dusk package that is designed to be called by inetd(8) and serve Gopher requests.

The package isn't designed to be very configurable, but rather to have a source code simple enough that it can easily be adapted to your needs.

This code is available from Dusk OS' files folder or as a Git repository (without SSL) at:

git://git.duskos.org/dusk-gopherd.git

Build and run

To build this project, you need:

Running make will download Dusk OS and compile the package using its Usermode. The end result in a standalone dkgopherd executable with this usage:

./dkgopherd [hostname]

hostname is a string of the host name of the server and defaults to "localhost". It is used for automatic directory listings.

This program reads a selector one character at a time from standard input until CR or LF (or any control character, really...) is encountered and then spits the appropriate response to standard output. Them, we exit.

Determining the appropriate response is done by treating the selector as a path and searching for that path in its BASEDIR (by default, /var/gopher). If the selector is empty, it becomes index. If it's found, the contents of the file is yielded. If the path doesn't exist, we yield "not found".

If the selector correspond to a directory, we auto-generate an index by iterating over its contents, ignoring all elements beginning with a .. In this listing, we differentiate between the "text" type and the "bin" type through a predetermined list of file extensions for text files, by default ".txt" and ".md".

Every request is logged to syslog under the ftp facility. On many syslog.conf defaults, this will mean that the log appears in /var/log/xferlog.

Your inetd.conf line could look like this:

gopher          stream  tcp     nowait:100      root    /usr/libexec/dkgopherd  dkgopherd example.com

Being designed for inetd has this nice side effect that for testing, one can simply call the executable directly and type the request in.

Nonblocking

We read standard input in nonblocking mode so that it's possible to apply a timeout. This way, we avoid the problem of TCP connections lingering forever if the other end never terminates the request or close the connection.

The timeout is applied per character, that is, it resets as soon as we receive something. This timeout is 10 seconds. This makes interactive requests with nc possible while keeping us relatively safe from "slow requests" attacks.

Security considerations

The contents of standard input cannot be trusted and could be malicious. To protect the host against malicious requests, these mechanisms have been put in place.

  1. The program chroot(2) itself in BASEDIR to prevent a malicious path from accessing files outside it.
  2. This program is designed to be ran as root, and once it is done chrooting itself, it changes its effective user to TGTUSER (by default nobody).
  3. The selector is accumulated in a static buffer of a fixed size (by default 64 bytes). As soon as input goes over that threshold, we go in "bad request" condition.