pdns.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. import json
  2. import re
  3. import requests
  4. from django.conf import settings
  5. from django.core.exceptions import SuspiciousOperation
  6. from desecapi.exceptions import PDNSException, PDNSValidationError, RequestEntityTooLarge
  7. NSLORD = object()
  8. NSMASTER = object()
  9. _config = {
  10. NSLORD: {
  11. 'base_url': settings.NSLORD_PDNS_API,
  12. 'headers': {
  13. 'Accept': 'application/json',
  14. 'User-Agent': 'desecapi',
  15. 'X-API-Key': settings.NSLORD_PDNS_API_TOKEN,
  16. }
  17. },
  18. NSMASTER: {
  19. 'base_url': settings.NSMASTER_PDNS_API,
  20. 'headers': {
  21. 'Accept': 'application/json',
  22. 'User-Agent': 'desecapi',
  23. 'X-API-Key': settings.NSMASTER_PDNS_API_TOKEN,
  24. }
  25. }
  26. }
  27. def _pdns_request(method, *, server, path, data=None):
  28. if data is not None:
  29. data = json.dumps(data)
  30. if data is not None and len(data) > settings.PDNS_MAX_BODY_SIZE:
  31. raise RequestEntityTooLarge
  32. r = requests.request(method, _config[server]['base_url'] + path, data=data, headers=_config[server]['headers'])
  33. if r.status_code == PDNSValidationError.pdns_code:
  34. raise PDNSValidationError(response=r)
  35. elif r.status_code not in range(200, 300):
  36. raise PDNSException(response=r)
  37. return r
  38. def _pdns_post(server, path, data):
  39. return _pdns_request('post', server=server, path=path, data=data)
  40. def _pdns_patch(server, path, data):
  41. return _pdns_request('patch', server=server, path=path, data=data)
  42. def _pdns_get(server, path):
  43. return _pdns_request('get', server=server, path=path)
  44. def _pdns_put(server, path):
  45. return _pdns_request('put', server=server, path=path)
  46. def _pdns_delete(server, path):
  47. return _pdns_request('delete', server=server, path=path)
  48. def pdns_id(name):
  49. # See also pdns code, apiZoneNameToId() in ws-api.cc (with the exception of forward slash)
  50. if not re.match(r'^[a-zA-Z0-9_.-]+$', name):
  51. raise SuspiciousOperation('Invalid hostname ' + name)
  52. name = name.translate(str.maketrans({'/': '=2F', '_': '=5F'}))
  53. return name.rstrip('.') + '.'
  54. def get_keys(domain):
  55. """
  56. Retrieves a dict representation of the DNSSEC key information
  57. """
  58. r = _pdns_get(NSLORD, '/zones/%s/cryptokeys' % pdns_id(domain.name))
  59. return [{k: key[k] for k in ('dnskey', 'ds', 'flags', 'keytype')}
  60. for key in r.json()
  61. if key['active'] and key['keytype'] in ['csk', 'ksk']]
  62. def get_zone(domain):
  63. """
  64. Retrieves a dict representation of the zone from pdns
  65. """
  66. r = _pdns_get(NSLORD, '/zones/' + pdns_id(domain.name))
  67. return r.json()
  68. def get_rrset_datas(domain):
  69. """
  70. Retrieves a dict representation of the RRsets in a given zone
  71. """
  72. return [{'domain': domain,
  73. 'subname': rrset['name'][:-(len(domain.name) + 2)],
  74. 'type': rrset['type'],
  75. 'records': [record['content'] for record in rrset['records']],
  76. 'ttl': rrset['ttl']}
  77. for rrset in get_zone(domain)['rrsets']]