DNS Zone File Fun with Python and Emacs

Sometimes we're faced with a boring, manual, labourious job which really needs to be done, will take a fairly long time, and be pretty unpleasant.

Whenever I'm faced with something like this, especially if it involves text, I try to make it interesting by setting myself the challenge of writing a script and/or using my editor to do the job faster than had I done it manually.

In this case, I had two problems. Firstly. I had just made about 50 entries into a reverse DNS zone.

These look like this:

1 IN PTR router.nso

Having done this manually, and rather slowly, I then had to make correpsonding forward entries, of the form:

router in A

My first thought was to do it in emacs with re-search-forward and replace-match, but this wasn't easy to do, so my next plan was to write a python script that does it.

for entry in open("reverselist","r").readlines():
  print "%s IN A 192.168.1.%s"%(ip, hostname)

This takes an input file of a list of reverse zone entries, and spits out forward entries. It's very simple - first we tokenise the input file:

'1  IN PTR router.nso'.strip().split()
['1', 'IN', 'PTR', 'router.nso']

We then take the first and fourth (python counts from 0) tokens - the last part of the IP, and the hostname, and take off only the short name:

>>> ['1', 'IN', 'PTR', 'router.nso'][0]
>>> ['1', 'IN', 'PTR', 'router.nso'][3]
>>> ['1', 'IN', 'PTR', 'router.nso'][3].split('.')[0]

Then we just print them out again in the order we want.

I was then faced with my second problem. The (large) forward zone file is horribly and inconsistently formatted. Adding entries is always a pain, because if you just hit tab a few times you'll never line up with the rest of the entries. Tidying it up has been on my todo list for a while, so I decided now really was the time to use the power of emacs!

So my task was how to convert various lines such as:

nst-xp-ie8	IN	A
svn		IN	CNAME	cap-svn

with their various spacing issues into a consistently spaced file.

In order to do this, we make use of the emacs regexp-replace function. This enables us to describe a pattern to match, and then defines how to match it.

Emacs regular expressions are a lot like sed's, so I got there pretty quickly.

The first pattern to match is:


  • \w+ means: match any word-constituent character
  • \s- means: match any white space
  • \S- means: match anything that is NOT whitespace
  • \(...\) is a construct which stores the match inside the brackets, so we can use it again later.

So this says:

Match the hostname, IN, A and the IP, and all the space inbetween; store the hostname, IN, A and the IP in variables.

The replacement is much simpler:


\1, \2 etc are the variables, holding the matched hostname etc, and ^I is the tab character.

Simple really, but more fun, and done in less time than doing it manually.

Show Comments