#!/usr/bin/env python3

"""Simple Python program to implement IETF draft
draft-ietf-dnsop-rrserial (hereafter "the draft")

If you don't know Python:
pip install dnspython
./test-rrserial.py 200.1.122.30 dateserial.example.com

"""

import struct
import sys

# DNSpython https://www.dnspython.org/ We probably require 2.0 or higher.
import dns.message
import dns.query
import dns.edns
import dns.rdatatype

# Temporary value in the draft
dns.edns.RRSERIAL = 65024

def print_soa(response):
  found = False
  for rr in response.authority:
    if rr.rdtype == dns.rdatatype.SOA:
      found = True
      print("Serial found from SOA record: %s" % rr[0].serial)
  if not found:
    print("Negative answer without a SOA record :-(")

def print_rrserial(response):
  found = False
  for opt in response.options:
    if opt.otype == dns.edns.RRSERIAL:
      found = True
      print("Serial of the answer is %s" % struct.unpack(">I", opt.data)[0])
  if not found:
    print("No EDNS serial option in answer")

# TODO: it would probably be better (more pythonic) to inherit from
# dns.edns.GenericOption and provide a parser.
opts = [dns.edns.GenericOption(dns.edns.RRSERIAL, b'')]

if len(sys.argv) != 3 and len(sys.argv) != 4:
  raise Exception("Usage: %s server qname qtype [for instance '200.1.122.30 dateserial.example.com AAAA')" % sys.argv[0])
server = sys.argv[1]
qname = sys.argv[2]
if len(sys.argv) == 4:
  qtype = dns.rdatatype.from_text(sys.argv[3])
else:
  qtype = dns.rdatatype.AAAA
message = dns.message.make_query(qname, qtype, options=opts)
response = dns.query.udp(message, server)
if response.rcode() == dns.rcode.NOERROR:
  data = False
  for rr in response.answer:
    if rr.rdtype == qtype:
      data = True
      print(rr)
  if not data:
    print("No value for %s/%s" % (qname, dns.rdatatype.to_text(qtype)))
    print_soa(response)
  else:
    print_rrserial(response)
elif response.rcode() == dns.rcode.NXDOMAIN:
  print("%s not found" % qname)
  print_soa(response)
elif response.rcode() == dns.rcode.SERVFAIL:
  print("%s failed" % qname) 
  print_rrserial(response)
else:
  print("Unknown return code %s" % response.rcode())


