test_dyndns12update.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. import random
  2. from rest_framework import status
  3. from desecapi.tests.base import DynDomainOwnerTestCase
  4. class DynDNS12UpdateTest(DynDomainOwnerTestCase):
  5. def assertIP(self, ipv4=None, ipv6=None, name=None):
  6. name = name or self.my_domain.name.lower()
  7. for type_, value in [('A', ipv4), ('AAAA', ipv6)]:
  8. response = self.client_token_authorized.get(self.reverse('v1:rrset', name=name, subname='', type=type_))
  9. if value:
  10. self.assertStatus(response, status.HTTP_200_OK)
  11. self.assertEqual(response.data['records'][0], value)
  12. self.assertEqual(response.data['ttl'], 60)
  13. else:
  14. self.assertStatus(response, status.HTTP_404_NOT_FOUND)
  15. def test_identification_by_domain_name(self):
  16. self.client.set_credentials_basic_auth(self.my_domain.name + '.invalid', self.token.plain)
  17. response = self.assertDynDNS12NoUpdate(mock_remote_addr='10.5.5.6')
  18. self.assertStatus(response, status.HTTP_401_UNAUTHORIZED)
  19. def test_identification_by_query_params(self):
  20. # /update?username=foobar.dedyn.io&password=secret
  21. self.client.set_credentials_basic_auth(None, None)
  22. response = self.assertDynDNS12Update(username=self.my_domain.name, password=self.token.plain)
  23. self.assertStatus(response, status.HTTP_200_OK)
  24. self.assertEqual(response.data, 'good')
  25. self.assertIP(ipv4='127.0.0.1')
  26. def test_deviant_ttl(self):
  27. """
  28. The dynamic update will try to set the TTL to 60. Here, we create
  29. a record with a different TTL beforehand and then make sure that
  30. updates still work properly.
  31. """
  32. with self.assertPdnsRequests(
  33. self.request_pdns_zone_update(self.my_domain.name),
  34. self.request_pdns_zone_axfr(self.my_domain.name),
  35. ):
  36. response = self.client_token_authorized.patch_rr_set(self.my_domain.name.lower(), '', 'A', {'ttl': 3600})
  37. self.assertStatus(response, status.HTTP_200_OK)
  38. response = self.assertDynDNS12Update(self.my_domain.name)
  39. self.assertStatus(response, status.HTTP_200_OK)
  40. self.assertEqual(response.data, 'good')
  41. self.assertIP(ipv4='127.0.0.1')
  42. def test_ddclient_dyndns1_v4_success(self):
  43. # /nic/dyndns?action=edit&started=1&hostname=YES&host_id=foobar.dedyn.io&myip=10.1.2.3
  44. with self.assertPdnsRequests(
  45. self.request_pdns_zone_update(self.my_domain.name),
  46. self.request_pdns_zone_axfr(self.my_domain.name),
  47. ):
  48. response = self.client.get(
  49. self.reverse('v1:dyndns12update'),
  50. {
  51. 'action': 'edit',
  52. 'started': 1,
  53. 'hostname': 'YES',
  54. 'host_id': self.my_domain.name,
  55. 'myip': '10.1.2.3'
  56. }
  57. )
  58. self.assertStatus(response, status.HTTP_200_OK)
  59. self.assertEqual(response.data, 'good')
  60. self.assertIP(ipv4='10.1.2.3')
  61. # Repeat and make sure that no pdns request is made (not even for the empty AAAA record)
  62. response = self.client.get(
  63. self.reverse('v1:dyndns12update'),
  64. {
  65. 'action': 'edit',
  66. 'started': 1,
  67. 'hostname': 'YES',
  68. 'host_id': self.my_domain.name,
  69. 'myip': '10.1.2.3'
  70. }
  71. )
  72. self.assertStatus(response, status.HTTP_200_OK)
  73. def test_ddclient_dyndns1_v6_success(self):
  74. # /nic/dyndns?action=edit&started=1&hostname=YES&host_id=foobar.dedyn.io&myipv6=::1337
  75. response = self.assertDynDNS12Update(
  76. domain_name=self.my_domain.name,
  77. action='edit',
  78. started=1,
  79. hostname='YES',
  80. host_id=self.my_domain.name,
  81. myipv6='::1337'
  82. )
  83. self.assertStatus(response, status.HTTP_200_OK)
  84. self.assertEqual(response.data, 'good')
  85. self.assertIP(ipv4='127.0.0.1', ipv6='::1337')
  86. # Repeat and make sure that no pdns request is made (not even for the empty A record)
  87. response = self.client.get(
  88. self.reverse('v1:dyndns12update'),
  89. {
  90. 'domain_name': self.my_domain.name,
  91. 'action': 'edit',
  92. 'started': 1,
  93. 'hostname': 'YES',
  94. 'host_id': self.my_domain.name,
  95. 'myipv6': '::1337'
  96. }
  97. )
  98. self.assertStatus(response, status.HTTP_200_OK)
  99. def test_ddclient_dyndns2_v4_success(self):
  100. # /nic/update?system=dyndns&hostname=foobar.dedyn.io&myip=10.2.3.4
  101. response = self.assertDynDNS12Update(
  102. domain_name=self.my_domain.name,
  103. system='dyndns',
  104. hostname=self.my_domain.name,
  105. myip='10.2.3.4',
  106. )
  107. self.assertStatus(response, status.HTTP_200_OK)
  108. self.assertEqual(response.data, 'good')
  109. self.assertIP(ipv4='10.2.3.4')
  110. def test_ddclient_dyndns2_v4_invalid(self):
  111. # /nic/update?system=dyndns&hostname=foobar.dedyn.io&myip=10.2.3.4asdf
  112. params = {
  113. 'domain_name': self.my_domain.name,
  114. 'system': 'dyndns',
  115. 'hostname': self.my_domain.name,
  116. 'myip': '10.2.3.4asdf',
  117. }
  118. response = self.client.get(self.reverse('v1:dyndns12update'), params)
  119. self.assertStatus(response, status.HTTP_400_BAD_REQUEST)
  120. self.assertIn('Record content malformed', str(response.data))
  121. def test_ddclient_dyndns2_v6_success(self):
  122. # /nic/update?system=dyndns&hostname=foobar.dedyn.io&myipv6=::1338
  123. response = self.assertDynDNS12Update(
  124. domain_name=self.my_domain.name,
  125. system='dyndns',
  126. hostname=self.my_domain.name,
  127. myipv6='::666',
  128. )
  129. self.assertStatus(response, status.HTTP_200_OK)
  130. self.assertEqual(response.data, 'good')
  131. self.assertIP(ipv4='127.0.0.1', ipv6='::666')
  132. def test_fritz_box(self):
  133. # /
  134. response = self.assertDynDNS12Update(self.my_domain.name)
  135. self.assertStatus(response, status.HTTP_200_OK)
  136. self.assertEqual(response.data, 'good')
  137. self.assertIP(ipv4='127.0.0.1')
  138. def test_unset_ip(self):
  139. for (v4, v6) in [
  140. ('127.0.0.1', '::1'),
  141. ('127.0.0.1', ''),
  142. ('', '::1'),
  143. ('', ''),
  144. ]:
  145. response = self.assertDynDNS12Update(self.my_domain.name, ip=v4, ipv6=v6)
  146. self.assertStatus(response, status.HTTP_200_OK)
  147. self.assertEqual(response.data, 'good')
  148. self.assertIP(ipv4=v4, ipv6=v6)
  149. class SingleDomainDynDNS12UpdateTest(DynDNS12UpdateTest):
  150. NUM_OWNED_DOMAINS = 1
  151. def test_identification_by_token(self):
  152. self.client.set_credentials_basic_auth('', self.token.plain)
  153. response = self.assertDynDNS12Update(self.my_domain.name, mock_remote_addr='10.5.5.6')
  154. self.assertStatus(response, status.HTTP_200_OK)
  155. self.assertEqual(response.data, 'good')
  156. self.assertIP(ipv4='10.5.5.6')
  157. def test_identification_by_email(self):
  158. self.client.set_credentials_basic_auth(self.owner.email, self.token.plain)
  159. response = self.assertDynDNS12Update(self.my_domain.name, mock_remote_addr='10.5.5.6')
  160. self.assertStatus(response, status.HTTP_200_OK)
  161. self.assertEqual(response.data, 'good')
  162. self.assertIP(ipv4='10.5.5.6')
  163. class MultipleDomainDynDNS12UpdateTest(DynDNS12UpdateTest):
  164. NUM_OWNED_DOMAINS = 4
  165. def test_honor_minimum_ttl(self):
  166. self.my_domain.minimum_ttl = 61
  167. self.my_domain.save()
  168. response = self.assertDynDNS12NoUpdate(self.my_domain.name)
  169. self.assertStatus(response, status.HTTP_403_FORBIDDEN)
  170. self.assertEqual(response.data['detail'], 'Domain not eligible for dynamic updates, please contact support.')
  171. def test_identification_by_token(self):
  172. """
  173. Test if the conflict of having multiple domains, but not specifying which to update is correctly recognized.
  174. """
  175. self.client.set_credentials_basic_auth('', self.token.plain)
  176. response = self.client.get(self.reverse('v1:dyndns12update'), REMOTE_ADDR='10.5.5.7')
  177. self.assertStatus(response, status.HTTP_409_CONFLICT)
  178. class MixedCaseDynDNS12UpdateTestCase(DynDNS12UpdateTest):
  179. @staticmethod
  180. def random_casing(s):
  181. return ''.join([c.lower() if random.choice([True, False]) else c.upper() for c in s])
  182. def setUp(self):
  183. super().setUp()
  184. self.my_domain.name = self.random_casing(self.my_domain.name)
  185. class UppercaseDynDNS12UpdateTestCase(DynDNS12UpdateTest):
  186. def setUp(self):
  187. super().setUp()
  188. self.my_domain.name = self.my_domain.name.upper()