michael orlitzky

Migrating from Windows DNS to tinydns

posted 2010-01-03

Disclaimer: there is another way to do this using AXFR, but if that was feasible, you probably wouldn't be here. If you have no idea what I'm talking about, go read those pages. It is possible to enable zone transfers on a zone-by-zone basis in the Windows DNS server.

What You're Trying to Do

You are a DNS administrator. Currently, all of your zones are stored in Windows DNS because you are a bad person (mother fuck, there is no website for the Windows DNS service?). You enjoy positive change, and would like to move to a more robust authoritative DNS server—one with a goddamn website.

Since you are old, wise, and determined to avoid repeating the mistakes of your youth, you have decided to migrate all of your DNS services to D.J. Bernstein's tinydns. There's only,

One Problem

The tinydns program uses its own zone file format. D.J.B. gets mad when you so much as call them zone files, but he can't hear us right now. To be fair, the Windows DNS server also uses its own storage format—the Windows registry—so you're doublescrewed at the very least.

You can of course tell Windows to store its zone data within the filesystem, via the "Load zone data on startup…" preference. But, as you would expect, Windows completely ignores you and stores everything in the registry anyway. On more than one occasion, I have discovered recent changes in the registry that were not yet written to the filesystem.

So, we need a way to get the data out of the registry, and into tinydns-data format.

Exporting from the Registry

Like I said, configuring Windows to store the zone information within the filesystem is unreliable. There is however a way to export it from the registry completely intact. Dean Wells of MSEtechnology has written a batch file, dnsdump.cmd, which can export zones from the Windows registry to standard BIND format.

Save the script somewhere, say, C:\dnsdump\. The link has the script renamed, so fix it (to .cmd) when you save it. Now, get to the command line. You manage DNS servers, and I shouldn't have to tell you how to do that. Navigate to the new directory, and run the script:

user $ cd C:\dnsdump

user $ dnsdump.cmd export C:\dnsdump

This should dump a whole bunch of crap into the C:\dnsdump\ directory. Among that crap, you will have an InstallRoot folder, containing zone files for each of the zones contained in the registry.

Mega-caveat: when you remove old zones through the user interface, Windows doesn't actually delete them from the registry. Ha ha! You will have a bunch of files in the InstallRoot directory that represent long-gone zones. Go delete them one at a time. Remember, this is your fault.

Now, zip (or whatever) the InstallRoot directory, and figure out a way to copy it to your tinydns server (or another workstation) running Linux, BSD, Solaris, OSX, etc. I'll wait. Extract the InstallRoot directory into your home directory.

Converting the Zone Files to tinydns-data Format

Daniel Erat wrote a tool called bind-to-tinydns, and we're gonna use it. Download a copy on the tinydns server, or on another *nix workstation—wherever you have the zone files you just created. I'm going to assume you've downloaded it to your home directory.

user $ cd ~

user $ wget https://www.erat.org/files/bind-to-tinydns-0.4.3.tar.gz

user $ tar -xf bind-to-tinydns-0.4.3.tar.gz

user $ rm bind-to-tinydns-0.4.3.tar.gz

user $ cd bind-to-tinydns-0.4.3

user $ make

You should now have an executable, bind-to-tinydns, in your ~/bind-to-tinydns-0.4.3 directory. The syntax is kind of crap, so we're going to write a small script to handle it. Here's the bind-to-tinydns usage:

Usage
===================================================================
bind-to-tinydns is invoked in the following manner:

bind-to-tinydns <origin> <output file> <temp file>

The BIND zone is read from STDIN. To convert a BIND zone file named "input" containing the zone "example.com" to a tinydns-data file named "output", you would run:

bind-to-tinydns example.com output output.tmp <input

If the conversion is successful, the program exits with a return value of 0. Otherwise, it exits with a return value of 1 (after deleting the temporary file). It will abort if the temporary file already exists.

You don't want to have to run this on every single zone file you have, because the arguments change in each case. What you would do—had I not done it for you—is use a script. Save this sucker as ~/zonefile_to_tinydns and make it executable.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#!/bin/bash

#
# zonefile_to_tinydns, converts one BIND-format zone file to
# tinydns-data format.
#
# Copyright Michael Orlitzky
#
# http://michael.orlitzky.com/
#
# Released under the WTFPLv2.
#
#  http://sam.zoy.org/wtfpl/COPYING
#


# What it Does
#
# This script converts one BIND zonefile to tinydns-data format.
# Read the usage description below for input/output format.
#
# There's only one bit of configuration you need to do: enter the path
# to the bind-to-tinydns program below.

BIND_TO_TINYDNS=~/bind-to-tinydns-0.4.3/bind-to-tinydns


# Leave the rest alone unless you don't want to.

EXIT_SUCCESS=0
EXIT_NOT_ENOUGH_ARGS=1

if [ $# -lt 1 ]; then
    echo 'Usage:'
    echo ''
    echo "  $0 <input file> [extension]"
    echo ''
    echo 'The input file should be of the form <zone_name>.something, where'
    echo 'the extension is arbitrary but required. The output file will be'
    echo 'named <zone_name>.extension if the optional extension argument is'
    echo 'supplied. The default extension is "tinydns".'
    echo ''
    echo 'Example:'
    echo ''
    echo "  $0 example.org.dns"
    echo ''
    echo 'would output a tinydns-data file named example.org.tinydns.'
    echo ''
    exit $EXIT_NOT_ENOUGH_ARGS
fi

# Set the default extension.
OUT_EXTENSION='tinydns'

if [ $# -gt 1 ]; then
    # The user supplied an extension. Use it.
    OUT_EXTENSION=$2
fi

# If the input file name does not contain both a zone and an
# extension, terrible things are going to happen here.
INFILE=`basename $1`
ZONE="${INFILE%.*}"
OUTFILE="${ZONE}.${OUT_EXTENSION}"
TMPFILE="${ZONE}.tmp"

$BIND_TO_TINYDNS $ZONE $OUTFILE $TMPFILE < $1

exit $EXIT_SUCCESS

Now, we just have to run the script on all of our zone (*.dns) files.

user $ cd ~/InstallRoot

user $ for x in *.dns; do ~/zonefile_to_tinydns $x; done;

You might see a bunch of warnings scroll by,

warning: line 19: ignoring out-of-zone data
warning: line 18: ignoring out-of-zone data
warning: line 20: ignoring out-of-zone data
warning: line 18: ignoring out-of-zone data
warning: line 20: ignoring out-of-zone data
warning: line 22: ignoring out-of-zone data
warning: line 24: ignoring out-of-zone data
warning: line 18: ignoring out-of-zone data
warning: line 20: ignoring out-of-zone data
warning: line 18: ignoring out-of-zone data
warning: line 20: ignoring out-of-zone data
warning: line 19: ignoring out-of-zone data
warning: line 18: ignoring out-of-zone data
warning: line 20: ignoring out-of-zone data
warning: line 18: ignoring out-of-zone data
warning: line 20: ignoring out-of-zone data
warning: line 18: ignoring out-of-zone data

Ignore them. Warnings are for girls.

At this point, you should have a directory full of *.tinydns files, ready to be fed into tinydns-data.

Cleanup

I guess the first step here would be to remove all of the old *.dns files that you don't need anymore. Have faith:

user $ rm *.dns

user $ rm *.DNS

Yes, there is one file, CACHE.DNS with an uppercase extension.

We've got one more thing to download. Valtz is a validation tool for tinydns-data zone files. Grab it, and extract it in your home directory.

user $ cd ~

user $ wget https://x42.com/software/valtz/valtz-0.7.tgz

user $ tar -xf valtz-0.7.tgz

Run it on each of the *.tinydns files in your InstallRoot directory.

user $ cd ~/InstallRoot

user $ ~/valtz-0.7/valtz *.tinydns

You should fix any errors that appear. Note that in valtz-0.7 there is a small bug with respect to SPF records. Valtz doesn't like the default output of bind-to-tinydns, even though that output is valid. For example,

user $ valtz orlitzky.com.tinydns

File orlitzky.com.tinydns

line 6; err 524288 :orlitzky.com.:16:\036v=spf1 include\072viabit.com -all:86400

expected: fqdn:n:rdata:ttl:timestamp:lo

pos 1; n; must not 2(NS), 5(CNAME), 6(SOA), 12(PTR), 15(MX) or 252(AXFR)

You can feel free to ignore these errors, or replace the generic record syntax with a standard TXT record, e.g.

1
'orlitzky.com.:v=spf1 include\072viabit.com -all:86400

I have a script that will do this file-by-file, but you should be very careful using it. Just in case you missed that; be very careful using this script. I am unsure whether it is safe to use on all tinydns-data files. It works on mine. There are no refunds.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#!/bin/bash
#
# generic_to_txt, takes a tinydns zone file as an argument, and
# converts all generic (TXT) records contained therein to standard TXT
# record format.
#
# Copyright Michael Orlitzky
#
# http://michael.orlitzky.com/
#
# Released under the WTFPLv2.
#
#  http://sam.zoy.org/wtfpl/COPYING
#

EXIT_SUCCESS=0
EXIT_NOT_ENOUGH_ARGS=1

if [ $# -lt 1 ]; then
    echo 'Usage:'
    echo ''
    echo "  $0 <input file>"
    echo ''
    echo 'Takes a tinydns-data file as an argument, and replaces all'
    echo 'generic-syntax TXT records with standard-format ones.'
    echo ''
    exit $EXIT_NOT_ENOUGH_ARGS
fi

INFILE=$1
TMPFILE=${1}.tmp

mv $INFILE $TMPFILE
sed -r "s/^:(.*?):16:\\\036/'\1:/g" $TMPFILE > $INFILE

if [ $? -eq $EXIT_SUCCESS ]; then
    rm $TMPFILE
    exit $EXIT_SUCCESSS
fi

If you have ignored my warnings or have an irrational faith in my abilities, go ahead and run it on your *.tinydns files.

user $ cd ~/

user $ wget http://michael.orlitzky.com/code/releases/generic_to_txt

user $ chmod 700 generic_to_txt

user $ cd ~/InstallRoot

user $ for x in *.tinydns; do ~/generic_to_txt $x; done;

At this point, your *.tinydns files should all pass validation, making you feel warm and/or fuzzy. Go install tinydns and feed it your zone files.