How to get the CNAME for a domain?

Grant Taylor gtaylor at
Thu Jan 12 03:13:18 UTC 2017

On 01/10/2017 03:40 AM, Michelle Konzack wrote:
> Hello experts,

/me looks over his shoulders wondering who's being addressed.

> I do not want to querry the world, but only my own Name Server for CNAME
> configured (or not).

Okay.  ONLY use local data.  Check.

> Currently I am updating my web administration interface and I have  lost
> over the time a script, which queried my Name Server for CNAMES.

Been there, done that.  Here, have a drink (of your choice) to wash the 
bitter taste of lost tools down.

> E.g. if I have a physical server <> I  like  to
> know, which CNAMEs (on MY Name Server) pointing to it as

If I'm understanding you correctly, you are wanting to know the name of 
the RR that has your server as the target of a CNAME.  (Correct me if 
I'm wrong.)

> OK, I can grep the whole /etc/bind/master/ directory, but since my  Name
> Server is responsable for several 1000 (sub)domains,  the  execution  of
> the script takes ages!

Something seems wrong here.  Unix is EXTREMELY good at text processing, 
which is what I think would be best here.  (I see no reason to involve 
DNS at all, besides the obvious data subject.)

A few things jump out at me as I read your script, some of which could 
have significant impact on performance.

> ----[tdgetincname]------------------------------------------------------
> #!/bin/bash
> VSERVER="$1"
> LIST=`cd /etc/bind/master/ && find -type f |grep -v -E "(.conf|.signed|.private|.key)$" |sed 's|^\./||' |sort`

It looks like you're looking to exclude files that end in .conf or 
.signed, or .private, or .key.

However, that's not what you're actually asking (e)grep to do.  You're 
really asking (e)grep for any occurrence of conf or signed or private or 
key with any character before them.

I suspect you meant to escape the periods so that you are looking for 
the literal strings ".conf" or ".signed" or ".private" or ",key".

grep -v -E "(\.conf|\.signed|\.private|\.key)$"

I'm guessing this will speed things up more than a little bit.

I question why you cd to the /etc/bind/master directory rather than just 
passing the path to find.  (Perhaps it's because find's output then 
returns the full path, instead of just file names.  I think there are 
other ways to deal with this.)

Why are you excluding the files types that you don't want?  I would 
think that it would be better to include just the files types that you 
do want.  (I'm going to assume that you want .zone files for the rest of 
this email.)

It looks like you're removing the leading "./" from find's output.

Curious, is sorting the output strictly necessary?  Or just a would be 
nice to have.  (I ask because this may be a small performance hit.)

Putting all of that I'd be tempted to try to accomplish the goal of 
building the list of files differently.

    find /etc/bind/master/ -type f -name '*.zone' | sed 's|.*\/||'

Let find generate a raw list (/etc/bind/master/ and 
then remove all of the head with sed so that you end up with 

Q:  Do you have all your zone files in one directory, /etc/bind/master, 
or are they separated in something like /etc/bind/master/$client/$zone? 
-  This would change the RE.

(Sorting the output if you want to.)

> for FILE in ${LIST}
> do
>   RET=`cd /etc/bind/master/ && grep -E "IN CNAME.*${VSERVER}" ${FILE} |sed "s|\.[ \t]*IN CNAME.*||"`

I suspect that the first ".*" is desired to deal with various types of 
white space.

I'm guessing that you are wanting to take grep's output, something like 
"    IN CNAME", and turn it into just the owning RR name, 
"".  (Correct me if I'm wrong.)

>   if [ -n "${RET}" ]
>   then
>     echo "${RET}"
>   fi

It looks like you are printing the owning RR name /if/ there is a value.

I'm guessing your RET variable will either be zero bytes or a list of 
one or more owning RR names.  (Correct me if I'm wrong.)

> done
> ------------------------------------------------------------------------
> Note: If I do not the "cd /etc/bind/master/ &&",
>       I exceed the maximum lenght of the commandline.

I'm guessing that you have enough files that your sub-command that 
populates the LIST variable is too long.

> Any ideas how to do this better?

Maybe ...

I think I'd be tempted to do something like the following:

for ZONE in $(find /etc/bind -type f -name '*.zone'); do
	awk '($2 == "CNAME" && $3 == ""){print $1}' 

To me this has a number of advantages:

  - There are no Regular Expressions, which can be slow.
  - awk will deal with the different fields for you.
     - Match *JUST* CNAME records.
     - Match the CNAME target
     - Print the RR name
  - You can pass a fully qualified file to awk.
     - No need to for text manipulation.

This is all very simple text manipulation (filtering), which unix excels at.

I have tested the above code and it "Works for Me" (TM).

However ... there is a potential problem.  If you are using abbreviated 
zone files, you may end up missing the ORIGIN in the output since it's 
not on the line.  I.e. the following zone file:


Similarly you may miss some CNAME's if the target is abbreviated.  I.e.:

www,electronica	IN	CNAME	vserver04

About the only (trivial) way that I can see to get around this (type of) 
limitation is to actually parse the zone data, ...or use a tool that 
does so for you.

I think it's easiest to cheat and parse the output of a "dig axfr".

for ZONE in $(find /etc/bind -type f -name '*.zone'); do
	dig axfr ${ZONE} | awk '($4 == "CNAME" && $5 == 
""){print $1}'

Note that the field numbers change because dig includes the TTL and the 
protocol type, IN.

> Thanks in avance

You're welcome.

I hope I helped.

Good luck.

(Please share what you end up doing.)

Grant. . . .
unix || die

-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/pkcs7-signature
Size: 3717 bytes
Desc: S/MIME Cryptographic Signature
URL: <>

More information about the bind-users mailing list