I can't exactly remember when it was I stumbled over dig.jsondns.org, but it was a while back. What I saw made a bit of an impression, and it's been in the back of my mind ever since. If you look at one of the samples, say, an A record, you see basically a DNS response wrapped up in a JSON object. This is quite easy to do, and as far as returning queries only, rather boring as well, so why talk about it?

It occurred to me that there may be a case for providing an API to DNS updates via HTTP for scenarios in which DDNS is not allowed or possible (e.g. due to firewall restrictions). I discussed updating your DNS via RFC 2136 Dynamic updates recently: dynamic updates on your DNS and securing ddns updates with SIG(0). Now there's little wrong with nsupdate(1), but it requires DNS "connectivity" to a server. If I'm sitting behind a firewall which, say, allows HTTP only, I'd be stuck.

Back to the JSON object. Why not use a JSON object to update the DNS? I rather liked the (crazy) idea and went in search of something Perl'y with which I could easily implement a REST service. (I wanted to use Perl because the the only decent scripting API with DNS support I know of is Net::DNS in Perl, and it has support for dynamic updates -- I've showed you that already.)

For the REST part of the deal I chose Perl Dancer. Look at the example on that page, and you'll see why: It is extremely easy to get started, Dancer has excellent documentation and it just works.

Within a few hours I had a workable RESTful prototype that supports the following methods on the DNS:

  • GET requests lead to DNS queries, results returned as JSON objects. Or XML. Or YAML. (All that, thanks to Dancer's Mutable serializer.)
  • A GET request on a particular URL performs a DNS zone transfer (AXFR) for the zone.
  • POST request leads to DNS updates being performed on a particular zone.
  • DELETE requests lead to DNS updates for deletion of a DNS resource record (RR) or RR set.

(RESTfully speaking, the POST methods should ADD new records, whereas the PUT method should replace records.)

Let me show you snippets of what that look likes so far:

# Let me do a query for the MX of the domain
    
    $ curl -X GET -D /tmp/hdr http://127.0.0.1:3000/q/b.aa/mx
    {
       "msg" : "NXDOMAIN",
       "status" : "400",
       "err" : "EBADQUERY"
    }
    
    $ cat /tmp/hdr
    HTTP/1.0 404 Not found
    
    # How about an NS RRset?
    
    $ curl -X GET http://127.0.0.1:3000/q/b.aa/ns
    {
       "question" : [
          {
             "qclass" : "IN",
             "qname" : "b.aa",
             "qtype" : "ns"
          }
       ],
       "domain" : "b.aa",
       "answer" : [
          {
             "ttl" : 60,
             "name" : "b.aa",
             "class" : "IN",
             "type" : "NS",
             "rdata" : "localhost."
          }
       ]
    }
    
    # or in XML?
    
    $ curl -X GET -H "Accept-type: text/xml" http://127.0.0.1:3000/q/b.aa/ns
    <data domain="b.aa">
      <answer name="b.aa" class="IN" rdata="localhost." ttl="60" type="NS" />
      <question qclass="IN" qname="b.aa" qtype="ns" />
    </data>
    
    # And now, I want some YAML
    
    $ curl -X GET -H "Accept-type: text/x-yaml" http://127.0.0.1:3000/q/b.aa/soa
    
    ---
    answer:
      - class: IN
        name: b.aa
        rdata:
          expire: 3600000
          minimum: 86400
          mname: localhost
          refresh: 28800
          retry: 14400
          rname: root.localhost
          serial: 39
        ttl: 60
        type: SOA
    domain: b.aa
    question:
      - qclass: IN
        qname: b.aa
        qtype: soa
    
    # Let me add a record or two
    #  : adding an RR at 'xa.b.aa' A
    
    $ cat a.json
    {
        "zone": "b.aa",
        "records":[
           {
            "name":"xa.b.aa",
            "ttl":90,
            "type":"a",
            "rdata":"127.0.0.14"
           },
           {
            "name":"xa.b.aa",
            "ttl":90,
            "type":"a",
            "rdata":"127.0.0.2"
           }
         ]
    }
    
    $ curl -X POST http://127.0.0.1:3000/ddns -d@a.json
    {
       "msg" : "ok",
       "status" : 200,
       "err" : "Update succeeded",
       "nupdates" : 2
    }
    
    # And delete the 127.0.0.14 A RR 
    #  : deleting an RR at xa.b.aa A
    
    $ curl -X DELETE http://127.0.0.1:3000/ddns/xa.b.aa/a/127.0.0.14
    {
       "msg" : "ok",
       "status" : 200,
       "err" : "Update succeeded",
       "delete" : "xa.b.aa A 127.0.0.14"
    }
    
    # Delete all A RR
    
    $ curl -X DELETE http://127.0.0.1:3000/ddns/xa.b.aa/a
    
    # Shall we try that again? ;-)
    #  : update unsuccessful: xa.b.aa/A: 'rrset exists (value independent)' prerequisite not satisfied (NXRRSET)
    
    $ curl -X DELETE http://127.0.0.1:3000/ddns/xa.b.aa/a
    {
       "msg" : "NXRRSET",
       "status" : 400,
       "err" : "Update failed",
       "delete" : "xa.b.aa A"
    }

As I said above: just foolin' around, but I did this mainly to satisfy my curiosity.

Flattr this
DNS, CLI, and REST :: 21 Mar 2011 :: e-mail

Comments

blog comments powered by Disqus