How to install a DNS cache server

1. Introduction

This column will explain what a DNS cache is and how you can set one up very quickly and easily using the dnscache program written by D.J. Bernstein.

I will start with some basic explanations about DNS and the benefits of a DNS cache. If you are already familiar with those topics you can skip ahead.

1.1. What is DNS?

As you may already know, computers that are connected to the internet are identified by two things: an IP address (which looks like this: 216.239.51.100) and a name (which looks like this: www.google.com).

In order to keep track which IP corresponds to what name, we need the DNS servers. DNS stands for Domain Name Server, but it is also used for Domain Name Service.

There are lots of such DNS servers around the world, and they all talk to each other constantly in order to keep the IP-to-name allocations consistent.

Every time you connect to the Internet you need to tell your computer the address of at least one working DNS server. You need this if you want your applications to be able to reach other computers by name. If you don’t have a working DNS server available all you can do is use IP’s, which is not very confortable.

1.2. I never knew any of this. Why does my Internet connection works fine?

Any ISP worth 2 dimes will provide its own DNS server or two to its clients. They usually tell you the IP addresses of those servers in their service specifications, brochure or whatever. Usually you edit your /etc/resolv.conf and put those IP’s there, then you forget about it. Maybe you did this, maybe a friend, relative or network administrator did it for you.

It’s possible you didn’t have to do this. If you connect to the Internet by dial-up, your dialer program (such as kppp) may ask you for these DNS servers and make them part of the connection profile. The dial-up sequence also makes it possible for the dialer to obtain the server addresses automatically, so you may not even have to do that.

1.3. What benefits does a DNS cache offer?

A DNS cache server is a server that resolves DNS requests for you and remembers the information. Whenever you need it a again, it is provided at ultra-speed, directly from your computer or from a computer on your LAN.

The benefits can be multiple, depending on your scenario. Dial-up users can use any bit of extra speed, so it will help having the information cached locally. You can also provide a DNS cache for the people in your LAN, in order to increase their connection speed and minimize DNS requests to your ISP. You could use a DNS cache as one more backup for your servers or websites.

Let’s not forget about the extra confort. If you ever decide to change ISP, or their DNS addresses change, you will only need to operate the changes in one place instead of going to every computer in your LAN.

1.4. Why not use a full DNS server?

A full-fledged DNS server is not a simple matter. Most of them are complicated to set up and maintain. Of course, they provide additional service and can be configured for more advanced features. But if you only want quick DNS service, a cache is all you need.

2. What you need

You need to download the djbdns and the daemontools packages. You will also need to have a C compiler installed on your system.

This is all. There are no other dependencies involved, since the resulting binaries are linked only with the most essential libraries, which are part of every Linux system.

3. Installation

3.1. Putting together an installation tree

You may want to follow my example and assemble your own little dnscache package somewhere. I did this in /opt/dnscache because that’s what I do with most of the things I compile from source.

So if you want to do this please create /opt/dnscache as well as /opt/dnscache/bin. The following command will do both:

mkdir -p /opt/dnscache/bin

Please make sure the directory holding the executables is in your PATH. You can do this on most systems using Bash by editing /etc/profile and adding a line such as the following:

export PATH=$PATH:/opt/dnscache/bin

3.2. Installing daemontools

Get the package I mentioned above. Unpack it somewhere. Go inside one directory at a time until you reach a place where you see these directories:

package/ src/

Without going deeper, run the following command:

package/compile

This will take a minute or two.

After it’s done you’ll find executables bundled in the command/ subdirectory. You need at least the following three:

envdir
envuidgid
softlimit

Copy them to your favorite directory (/opt/dnscache/bin if you decided to take my advice).

You may wonder what the rest of the executables are. Daemontools is actually a service management package, which means it offers a complete solution for starting, stopping and monitoring the services on a computer (such as databases, Apache, DNS etc.) We only picked stuff that is essential to make dnscache work. If you are interested, see the daemontools webpages.

3.3. Installing djbdns

Get the package mentioned above. Unpack it somewhere. Go into the directory it had inside (no further) and run the following command:

make

Again, it may take a minute or two.

After it’s done, you’ll find various executables bundled right in that directory. You only need this one: dnscache. Copy it to the place you put the other binaries (again, I’ll asume it’s /opt/dnscache/bin).

Why the other executables: djbdns contains a full DNS servery, called tinydns, as well as all kinds of tools related to DNS services. If you are interested, see the djbdns homepage.

3.4. How to fix compilation errors

It’s possible, on some systems, to see the following error for both packages, while compiling:

undefined reference to "errno"

If this happens, for daemontools edit src/error.h, and for djbdns edit error.h. Locate the following line at the top of both files:

extern int errno;

and replace it with this:

 #include <errno.h>

Now you can run package/compile and make, respectivelly, and they will work fine.

4. Configuring dnscache

Please note that these are the “quick-and-dirty” instructions. The online documentation offers more than this, like how to set up proper logging and how to make dnscache run with D.J. Bernstein’s daemon management system. It also has the entire manual, which will answer all kinds of questions.

4.1. The dnscache user

You will want to create a special user and group for the express purpose of running dnscache with their priviledges. The point is to restrict dnscache as much as possible, since it’s a good security practice. It’s definitely not a good idea to run stuff as root.

Don’t be alarmed. Dnscache doesn’t have any gaping security flaws, but nothing is perfect. In case someone discovers a hole in it someday, you will wish it was running as a non-privileged user.

Therefore, create a dnscache group and user with the following commands:

groupadd dnscache

useradd -g dnscache -d /etc/dnscache/root -s /bin/false -c "DNS cache" -M dnscache

The user will have /etc/dnscache/root as his home directory, /bin/false as shell (which means it won’t be able to login), will have the long name “DNS cache”, and we won’t create the home directory just yet (because it needs special preparations).

4.2. Basic configuration

Just run the following commands:

mkdir /etc/dnscache
mkdir /etc/dnscache/env
echo 1000000 > /etc/dnscache/env/CACHESIZE
echo 3000000 > /etc/dnscache/env/DATALIMIT
echo 0 > /etc/dnscache/env/FORWARDONLY
echo 0.0.0.0 > /etc/dnscache/env/IP
echo 0.0.0.0 > /etc/dnscache/env/IPSEND
echo /etc/dnscache/root > /etc/dnscache/env/ROOT
mkdir /etc/dnscache/root
mkdir /etc/dnscache/root/ip
touch /etc/dnscache/root/ip/127.0.0.1
mkdir /etc/dnscache/root/servers
cat > /etc/dnscache/root/servers/@ <<EOF
198.41.0.4
128.9.0.107
192.33.4.12
128.8.10.90
192.203.230.10
192.5.5.241
192.112.36.4
128.63.2.53
192.36.148.17
198.41.0.10
193.0.14.129
198.32.64.12
202.12.27.33 
EOF
chown dnscache.dnscache /etc/dnscache -R
find /etc/dnscache/ -type f -exec chmod 400 {} \;
find /etc/dnscache/ -type d -exec chmod 500 {} \;

This is what happens above:

4.3. Detailed configuration

Here are the details for fine-tuning your dnscache installation:

5. Running dnscache

Assuming that the configuration directory has been properly setup in /etc/dnscache, and that the binaries are in /opt/dnscache/bin, I will provide you with several scripts for starting and stopping dnscache.

5.1. The start script

I call the following script dsncache-start and I usually place it in /opt/dnscache/bin, with the other executables.

# set up vars
export BINARY="/opt/dnscache/bin/dnscache"
export CONFIG="/etc/dnscache${COPY}"
export COPY=`echo "${1:-}" | sed "s/[^0-9]//g"`
export SEED="${CONFIG}/seed"
export ENVDIR="${CONFIG}/env"
export PIDFILE="/var/run/dnscache${COPY}.pid"
export LOGFILE="/var/log/dnscache${COPY}"
echo "${CONFIG}/root" > "${ENVDIR}/ROOT"

# check config dir
if [ ! -d "$CONFIG" ]; then
  echo "Config dir $CONFIG not found."
  exit 1
fi

# check for another server
[ -r "$PIDFILE" ] && {
  kill -0 `cat "$PIDFILE"` &>/dev/null && {
    echo "Another instance is already running (pid=`cat \"$PIDFILE\"`)."
    exit 1
  } || {
    echo "Stale pid file, erasing..."
    rm -f "$PIDFILE"
  }
}

# set up log
if [ "$LOGFILE" != "/dev/null" ]; then
  touch $LOGFILE
  chown dnscache.dnscache $LOGFILE
  chmod 600 $LOGFILE
fi

# generate random seed
rm -f "$SEED"
touch "$SEED"
chmod 400 "$SEED"
chown dnscache.dnscache "$SEED"
dd if=/dev/urandom of="$SEED" bs=128 count=1 2>/dev/null

# record time
echo "--- START: `date`" >> "$LOGFILE"

# start the server
exec 2>&1
exec < "$SEED"
exec envdir "$ENVDIR" sh -c '
  exec envuidgid dnscache softlimit -o250 -d "$DATALIMIT" "$BINARY"
' 1 >> "$LOGFILE" 2>&1 & echo $! > "$PIDFILE"

You will need to run it as root in order to start dnscache. It will check and refuse to start if another instance is already running.

It will also setup a log file in which it will report what it’s doing. It’s interesting and useful when you are first setting it up, but it will not be of much use later and it can grow quite big. So when you’re done setting it up, change the logfile location at the top of the script from /var/log/dnscache to /dev/null.

What the script does:

5.2. The stop script

You can put the following in a script called dnscache-stop, for convenience.

export COPY=`echo "${1:-}" | sed "s/[^0-9]//g"`
export PIDFILE="/var/run/dnscache${COPY}.pid"

kill `cat "$PIDFILE"` && rm -f "$PIDFILE"

5.3. Integrating dnscache with the system

You will probably need an elegant way to start (and stop) dnscache with your system.

A lot of systems use the SysV scripts, otherwise known as the /etc/init.d directory. If your system is one of them, you can use the following script:

# dnscache  This starts and stops dnscache
# chkconfig: 2345 60 60
#
# This script should stay in /etc/init.d

COPY=`echo "${2:-}" | sed "s/[^0-9]//g"`
START="/opt/dnscache/bin/dnscache-start \"$COPY\""
STOP="/opt/dnscache/bin/dnscache-stop \"$COPY\""
PIDFILE="/var/run/dnscache${COPY}.pid"
RETVAL=0

start() {
  echo -n $"Starting DNS cache: "
  (eval "$START") && echo OK || echo FAILED
  RETVAL=$?
  return $RETVAL
}

stop() {
  echo -n $"Stopping DNS cache: "
  (eval "$STOP") && echo OK || echo FAILED
  RETVAL=$?
  return $RETVAL
}

restart() {
  stop
  sleep 1
  start
  RETVAL=$?
  return $RETVAL
}

status() {
  if [ -r "$PIDFILE" ] ; then
    PID=`cat "$PIDFILE"`
    if `kill -CHLD "$PID" &>/dev/null` ; then
      echo "The server is running [pid=${PID}]."

      return 0
    fi
    echo "The server is NOT running."
    echo "Stale $PIDFILE file, erasing..."
    rm -f $PIDFILE
  else
    echo "The server is NOT running."
  fi
}

case "$1" in
  start)
    start
  ;;
  stop)
    stop
  ;;
  restart)
    restart
  ;;
  status)
    status
  ;;
  *)
    echo $"Usage: $0 {start|stop|restart|status} [copy]"
    RETVAL=1
  ;;
esac

exit $RETVAL

So, if you’re using /etc/init.d, you can copy this script there and name it dnscache.

Various Linux distributions have various methods for telling the system to start using it. For Red Hat or Fedora, you need to do this:

chkconfig --add dnscache

For Debian, you need to either run sysv-rc-conf and visually check dnscache for runlevels 2-5, or run this:

sysv-rc-conf --level 2345 dnscache on

For other systems, you can either manually add symlinks for the relevant /etc/rc?.d directories, or just add /opt/dnscache/bin/dnscache-start to your /etc/rc.local.

5.4. Running several copies of dnscache

For whatever reason, you may decide you need two separate copies of dnscache running. It can happen, if you need separate configurations.

It’s very easy to do this. First, you need to make a copy of /etc/dnscache and name it /etc/dnscache2. Customize the second config dir as you see fit.

Second, all the scripts I offered above take an extra parameter which indicates the copy of dnscache. By default, there is no copy number. But if you want to start up a second dnscache, you just pass 2 to them. Examples:

/opt/dnscache/bin/dnscache-start 2
/opt/dnscache/bin/dnscache-stop 2
/etc/init.d/dnscache start 2
/etc/init.d/dnscache stop 2
/etc/init.d/dnscache status 2

However, please note that the system will run /etc/init.d/dnscache script without the extra parameter, so only the first copy will start automatically. You may need to use /etc/rc.local or other tricks for copies 2 and up.

6. Using the DNS cache

6.1. Check if it’s working

You can verify that your new DNS cache is working by going to a machine which is allowed to connect to it and running this:

host www.google.com DNS.CACHE.IP.ADDRESS

Replace DNS.CACHE.IP.ADDRESS with whatever IP address the DNS cache machine is located at. If you set up a local server, use 127.0.0.1. If you set up a LAN server on a router, use the router’s LAN address.

The program host will try to use the DNS service at the address given to resolve www.google.com to an IP address. If you get back something like the following output, everything is fine:

Using domain server:
Name: 127.0.0.1
Address: 127.0.0.1#53
Aliases: 

www.google.com is an alias for www.l.google.com.
www.l.google.com has address 66.249.93.99
www.l.google.com has address 66.249.93.104
Using domain server:
Name: 127.0.0.1
Address: 127.0.0.1#53
Aliases: 

www.google.com is an alias for www.l.google.com.
Using domain server:
Name: 127.0.0.1
Address: 127.0.0.1#53
Aliases: 

www.google.com is an alias for www.l.google.com.

However, if it takes a long while and you get the following, it didn’t work:

;; connection timed out; no servers could be reached

In such a case, you will need to double check your settings, check the logfile, and make sure dnscache is running and listening to calls. The following commands should NOT come out empty:

ps awux | grep dnscache | grep -v grep

netstat -vtlnep | grep dnscache

6.2. Making use of the DNS cache

Next, you need to go to the machines that need the DNS cache and set them up to use it.

For Linux and generally *n*x systems, you edit /etc/resolv.conf, comment out other nameservers, and add the following:

nameserver DNS.CACHE.IP.ADDRESS

After you did this, the host command should work without the extra argument:

host www.google.com

Please note that some dialer programs may rewrite /etc/resolv.conf with the nameservers they receive from the ISP upon every connection. If this is the case, identify the culprit and fix it, otherwise the entire dnscache setup goes to waste.

On Windows machines in your LAN, you need to go to the Internet connection properties, locate the section about DNS servers and tell them to use the IP address of the DNS cache.

7. Security considerations

dnscache is a pretty secure program. If used properly, using the setup I described, it will run as a non-privileged user, which is always a good idea for a server that provides any kind of service.

It may be interesting to know that it runs chrooted inside its home directory, so it never gets access to any other part of your system. The logfile and pidfile are set up by the startup script, they have nothing to do with dnscache, and the data cache is kept in RAM, not on disk.

There are security issues concerning the DNS protocol itself. dnscache tries to tackle some of them, for example by using a random seed for its inner workings, which I’ve made even more random by regenerating it every time dnscache starts.

For more details please see the online documentation.