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 192.168.1.1
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():
tokens=entry.strip().split()
ip=tokens[0]
name=tokens[3]
hostname=name.split(".")[0]
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'
>>> ['1', 'IN', 'PTR', 'router.nso'][3]
'router.nso'
>>> ['1', 'IN', 'PTR', 'router.nso'][3].split('.')[0]
'router'
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 192.168.20.236
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+\)\s-*\(IN\)\s-*\(A\)\s-*\(\S-+\)
- \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^I^I\2^I\3^I\4
\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.