asn-dns.py 3.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. #!/usr/bin/env python
  2. # Maintained at https://code.ffdn.org/zorun/dn42-scripts
  3. """This script generates DNS TXT records containing ASN information.
  4. The idea comes from asn.cymru.com. Example:
  5. dig +short AS1.asn.cymru.com TXT
  6. The output looks like this:
  7. "1 | US | arin | 2001-09-20 | LVLT-1 - Level 3 Communications, Inc."
  8. In dn42, we try to respect this format. Fields 2 and 3 are set to "DN42"
  9. and "dn42", respectively. The date field is left empty, as we currently
  10. do not record this information in the registry. For the last field, we use
  11. "[as-name] [descr]" from the registry.
  12. """
  13. import sys
  14. import time
  15. import argparse
  16. from registry import AutNum
  17. def gen_zone(args):
  18. out = args.output
  19. # SOA and stuff
  20. out.write("$ORIGIN {}\n".format(args.zone))
  21. out.write("$TTL {}\n".format(args.ttl))
  22. serial = int(time.time())
  23. out.write("@ IN SOA {0.ns[0]} {0.rname} "
  24. "({1} {0.refresh} {0.retry} {0.expire} {0.negative})\n".format(args,
  25. serial))
  26. # NS
  27. for ns in args.ns:
  28. out.write("@ NS {}\n".format(ns))
  29. # TXT data
  30. autnum = AutNum(args.registry)
  31. for (asn, data) in autnum.data.items():
  32. comment = ""
  33. if "as-name" in data:
  34. comment = data["as-name"][0]
  35. if "descr" in data:
  36. comment += " " + data["descr"][0]
  37. comment.replace('"', '')
  38. # Bind and NSD are quite happy with UTF-8 in TXT records, but
  39. # KnotDNS isn't (at least Knot 1.4.7). This is a hack to work around this.
  40. comment = comment.encode('ascii', errors='xmlcharrefreplace').decode()
  41. out.write('{} TXT "{} | DN42 | dn42 | | {}"\n'.format(asn, asn[2:], comment))
  42. def cleanup(args):
  43. def final_dot(s):
  44. if not s.endswith('.'):
  45. return s + "."
  46. else:
  47. return s
  48. args.zone = final_dot(args.zone)
  49. args.rname = args.rname.replace("@", ".")
  50. args.rname = final_dot(args.rname)
  51. for (i, ns) in enumerate(args.ns):
  52. args.ns[i] = final_dot(ns)
  53. if __name__ == "__main__":
  54. parser = argparse.ArgumentParser(description=__doc__)
  55. parser.add_argument("-n", "--ns", action="append", required=True,
  56. help="name server, can be passed multiple times (the first one will be listed as master in the SOA)")
  57. parser.add_argument("-r", "--rname", required=True,
  58. help="contact email of the administrator of the zone")
  59. parser.add_argument("-o", "--output", default=sys.stdout, type=argparse.FileType('w'),
  60. help="output file (default: stdout)")
  61. parser.add_argument("registry", help="path to the monotone registry")
  62. opt = parser.add_argument_group("Zone options")
  63. opt.add_argument("-z", "--zone", default="asn.dn42.",
  64. help="zone to generate data for (default: %(default)s)")
  65. opt.add_argument("-t", "--ttl", default="1h",
  66. help="TTL for all records (default: %(default)s)")
  67. opt.add_argument("--refresh", default="1d",
  68. help="refresh timer for slaves (default: %(default)s)")
  69. opt.add_argument("--retry", default="1h",
  70. help="retry timer for slaves (default: %(default)s)")
  71. opt.add_argument("--expire", default="4w",
  72. help="expire time for slaves (default: %(default)s)")
  73. opt.add_argument("--negative", default="5m",
  74. help="negative TTL (default: %(default)s)")
  75. args = parser.parse_args()
  76. # Sanity checks
  77. cleanup(args)
  78. gen_zone(args)