summaryrefslogtreecommitdiffstats
path: root/ansible_collections/community/dns/plugins
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:04:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:04:41 +0000
commit975f66f2eebe9dadba04f275774d4ab83f74cf25 (patch)
tree89bd26a93aaae6a25749145b7e4bca4a1e75b2be /ansible_collections/community/dns/plugins
parentInitial commit. (diff)
downloadansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.tar.xz
ansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.zip
Adding upstream version 7.7.0+dfsg.upstream/7.7.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/community/dns/plugins')
-rw-r--r--ansible_collections/community/dns/plugins/doc_fragments/attributes.py105
-rw-r--r--ansible_collections/community/dns/plugins/doc_fragments/filters.py71
-rw-r--r--ansible_collections/community/dns/plugins/doc_fragments/hetzner.py176
-rw-r--r--ansible_collections/community/dns/plugins/doc_fragments/hosttech.py189
-rw-r--r--ansible_collections/community/dns/plugins/doc_fragments/inventory_records.py47
-rw-r--r--ansible_collections/community/dns/plugins/doc_fragments/module_record.py69
-rw-r--r--ansible_collections/community/dns/plugins/doc_fragments/module_record_info.py56
-rw-r--r--ansible_collections/community/dns/plugins/doc_fragments/module_record_set.py90
-rw-r--r--ansible_collections/community/dns/plugins/doc_fragments/module_record_set_info.py58
-rw-r--r--ansible_collections/community/dns/plugins/doc_fragments/module_record_sets.py88
-rw-r--r--ansible_collections/community/dns/plugins/doc_fragments/module_zone_info.py28
-rw-r--r--ansible_collections/community/dns/plugins/doc_fragments/options.py65
-rw-r--r--ansible_collections/community/dns/plugins/filter/domain_suffix.py91
-rw-r--r--ansible_collections/community/dns/plugins/filter/get_public_suffix.yml39
-rw-r--r--ansible_collections/community/dns/plugins/filter/get_registrable_domain.yml34
-rw-r--r--ansible_collections/community/dns/plugins/filter/remove_public_suffix.yml39
-rw-r--r--ansible_collections/community/dns/plugins/filter/remove_registrable_domain.yml39
-rw-r--r--ansible_collections/community/dns/plugins/inventory/hetzner_dns_records.py94
-rw-r--r--ansible_collections/community/dns/plugins/inventory/hosttech_dns_records.py101
-rw-r--r--ansible_collections/community/dns/plugins/module_utils/argspec.py52
-rw-r--r--ansible_collections/community/dns/plugins/module_utils/conversion/base.py14
-rw-r--r--ansible_collections/community/dns/plugins/module_utils/conversion/converter.py246
-rw-r--r--ansible_collections/community/dns/plugins/module_utils/conversion/txt.py228
-rw-r--r--ansible_collections/community/dns/plugins/module_utils/hetzner/api.py419
-rw-r--r--ansible_collections/community/dns/plugins/module_utils/hosttech/api.py115
-rw-r--r--ansible_collections/community/dns/plugins/module_utils/hosttech/json_api.py340
-rw-r--r--ansible_collections/community/dns/plugins/module_utils/hosttech/wsdl_api.py279
-rw-r--r--ansible_collections/community/dns/plugins/module_utils/http.py76
-rw-r--r--ansible_collections/community/dns/plugins/module_utils/json_api_helper.py193
-rw-r--r--ansible_collections/community/dns/plugins/module_utils/module/_utils.py43
-rw-r--r--ansible_collections/community/dns/plugins/module_utils/module/record.py195
-rw-r--r--ansible_collections/community/dns/plugins/module_utils/module/record_info.py149
-rw-r--r--ansible_collections/community/dns/plugins/module_utils/module/record_set.py272
-rw-r--r--ansible_collections/community/dns/plugins/module_utils/module/record_set_info.py175
-rw-r--r--ansible_collections/community/dns/plugins/module_utils/module/record_sets.py264
-rw-r--r--ansible_collections/community/dns/plugins/module_utils/module/zone_info.py71
-rw-r--r--ansible_collections/community/dns/plugins/module_utils/names.py73
-rw-r--r--ansible_collections/community/dns/plugins/module_utils/options.py37
-rw-r--r--ansible_collections/community/dns/plugins/module_utils/provider.py109
-rw-r--r--ansible_collections/community/dns/plugins/module_utils/record.py100
-rw-r--r--ansible_collections/community/dns/plugins/module_utils/resolver.py213
-rw-r--r--ansible_collections/community/dns/plugins/module_utils/wsdl.py308
-rw-r--r--ansible_collections/community/dns/plugins/module_utils/zone.py38
-rw-r--r--ansible_collections/community/dns/plugins/module_utils/zone_record_api.py221
-rw-r--r--ansible_collections/community/dns/plugins/module_utils/zone_record_helpers.py132
-rw-r--r--ansible_collections/community/dns/plugins/modules/hetzner_dns_record.py118
-rw-r--r--ansible_collections/community/dns/plugins/modules/hetzner_dns_record_info.py136
-rw-r--r--ansible_collections/community/dns/plugins/modules/hetzner_dns_record_set.py234
-rw-r--r--ansible_collections/community/dns/plugins/modules/hetzner_dns_record_set_info.py199
-rw-r--r--ansible_collections/community/dns/plugins/modules/hetzner_dns_record_sets.py129
-rw-r--r--ansible_collections/community/dns/plugins/modules/hetzner_dns_zone_info.py199
-rw-r--r--ansible_collections/community/dns/plugins/modules/hosttech_dns_record.py111
-rw-r--r--ansible_collections/community/dns/plugins/modules/hosttech_dns_record_info.py133
-rw-r--r--ansible_collections/community/dns/plugins/modules/hosttech_dns_record_set.py233
-rw-r--r--ansible_collections/community/dns/plugins/modules/hosttech_dns_record_set_info.py198
-rw-r--r--ansible_collections/community/dns/plugins/modules/hosttech_dns_record_sets.py128
-rw-r--r--ansible_collections/community/dns/plugins/modules/hosttech_dns_records.py128
-rw-r--r--ansible_collections/community/dns/plugins/modules/hosttech_dns_zone_info.py185
-rw-r--r--ansible_collections/community/dns/plugins/modules/wait_for_txt.py335
-rw-r--r--ansible_collections/community/dns/plugins/plugin_utils/inventory/records.py124
-rw-r--r--ansible_collections/community/dns/plugins/plugin_utils/public_suffix.py214
-rw-r--r--ansible_collections/community/dns/plugins/plugin_utils/templated_options.py20
-rw-r--r--ansible_collections/community/dns/plugins/public_suffix_list.dat13806
-rw-r--r--ansible_collections/community/dns/plugins/public_suffix_list.dat.license2
64 files changed, 22473 insertions, 0 deletions
diff --git a/ansible_collections/community/dns/plugins/doc_fragments/attributes.py b/ansible_collections/community/dns/plugins/doc_fragments/attributes.py
new file mode 100644
index 000000000..1fff4ab70
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/doc_fragments/attributes.py
@@ -0,0 +1,105 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+class ModuleDocFragment(object):
+
+ # Standard documentation fragment
+ DOCUMENTATION = r'''
+options: {}
+attributes:
+ check_mode:
+ description: Can run in C(check_mode) and return changed status prediction without modifying target.
+ diff_mode:
+ description: Will return details on what has changed (or possibly needs changing in C(check_mode)), when in diff mode.
+'''
+
+ # Should be used together with the standard fragment
+ INFO_MODULE = r'''
+options: {}
+attributes:
+ check_mode:
+ support: full
+ details:
+ - This action does not modify state.
+ diff_mode:
+ support: N/A
+ details:
+ - This action does not modify state.
+'''
+
+ ACTIONGROUP_HETZNER = r'''
+options: {}
+attributes:
+ action_group:
+ description: Use C(group/community.dns.hetzner) in C(module_defaults) to set defaults for this module.
+ support: full
+ membership:
+ - community.dns.hetzner
+'''
+
+ ACTIONGROUP_HOSTTECH = r'''
+options: {}
+attributes:
+ action_group:
+ description: Use C(group/community.dns.hosttech) in C(module_defaults) to set defaults for this module.
+ support: full
+ membership:
+ - community.dns.hosttech
+'''
+
+ CONN = r'''
+options: {}
+attributes:
+ become:
+ description: Is usable alongside C(become) keywords.
+ connection:
+ description: Uses the target's configured connection information to execute code on it.
+ delegation:
+ description: Can be used in conjunction with C(delegate_to) and related keywords.
+'''
+
+ FACTS = r'''
+options: {}
+attributes:
+ facts:
+ description: Action returns an C(ansible_facts) dictionary that will update existing host facts.
+'''
+
+ # Should be used together with the standard fragment and the FACTS fragment
+ FACTS_MODULE = r'''
+options: {}
+attributes:
+ check_mode:
+ support: full
+ details:
+ - This action does not modify state.
+ diff_mode:
+ support: N/A
+ details:
+ - This action does not modify state.
+ facts:
+ support: full
+'''
+
+ FILES = r'''
+options: {}
+attributes:
+ safe_file_operations:
+ description: Uses Ansible's strict file operation functions to ensure proper permissions and avoid data corruption.
+'''
+
+ FLOW = r'''
+options: {}
+attributes:
+ action:
+ description: Indicates this has a corresponding action plugin so some parts of the options can be executed on the controller.
+ async:
+ description: Supports being used with the C(async) keyword.
+'''
diff --git a/ansible_collections/community/dns/plugins/doc_fragments/filters.py b/ansible_collections/community/dns/plugins/doc_fragments/filters.py
new file mode 100644
index 000000000..e153d4bf9
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/doc_fragments/filters.py
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2022 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+class ModuleDocFragment(object):
+
+ DOCUMENTATION = r'''
+options:
+ icann_only:
+ description:
+ - This controls whether only entries from the ICANN section of the Public Suffix List are used,
+ or also entries from the Private section. For example, C(.co.uk) is in the ICANN section,
+ but C(github.io) is in the Private section.
+ type: boolean
+ default: false
+'''
+
+ PUBLIC_SUFFIX = r'''
+options:
+ keep_unknown_suffix:
+ description:
+ - This treats unknown TLDs as valid public suffixes. So for example the public suffix
+ of C(example.tlddoesnotexist) is C(.tlddoesnotexist) if this is C(true). If set to
+ C(false), it will return an empty string in this case.
+ - This option corresponds to whether the global wildcard rule C(*) in the Public
+ Suffix List is used or not.
+ type: boolean
+ default: true
+'''
+
+ REGISTERABLE_DOMAIN = r'''
+options:
+ only_if_registerable:
+ description:
+ - This controls the behavior in case there is no label in front of the public suffix.
+ This is the case if the DNS name itself is a public suffix.
+ - If set to C(false), in this case the public suffix is treated as a registrable domain.
+ - If set to C(true) (default), the registrable domain of a public suffix is interpreted as an
+ empty string.
+ type: boolean
+ default: true
+ keep_unknown_suffix:
+ description:
+ - This treats unknown TLDs as valid public suffixes. So for example the public suffix of
+ C(example.tlddoesnotexist) is C(.tlddoesnotexist) if this is C(true), and hence the
+ registrable domain of C(www.example.tlddoesnotexist) is C(example.tlddoesnotexist).
+ If set to C(false), the registrable domain of C(www.example.tlddoesnotexist) is
+ C(tlddoesnotexist).
+ - This option corresponds to whether the global wildcard rule C(*) in the Public Suffix List
+ is used or not.
+ type: boolean
+ default: true
+'''
+
+ GET = r'''
+options:
+ normalize_result:
+ description:
+ - This controls whether the result is reconstructed from the normalized name used during lookup.
+ During normalization, ulabels are converted to alabels, and every label is converted to lowercase.
+ For example, the ulabel C(ëçãmplê) is converted to C(xn--mpl-llatwb) (puny-code), and
+ C(Example.COM) is converted to C(example.com).
+ type: boolean
+ default: false
+'''
diff --git a/ansible_collections/community/dns/plugins/doc_fragments/hetzner.py b/ansible_collections/community/dns/plugins/doc_fragments/hetzner.py
new file mode 100644
index 000000000..3b79e5da0
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/doc_fragments/hetzner.py
@@ -0,0 +1,176 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+class ModuleDocFragment(object):
+
+ # Standard files documentation fragment
+ DOCUMENTATION = r'''
+options:
+ hetzner_token:
+ description:
+ - The token for the Hetzner API.
+ - If not provided, will be read from the environment variable C(HETZNER_DNS_TOKEN).
+ aliases:
+ - api_token
+ type: str
+ required: true
+'''
+
+ # NOTE: This document fragment augments the above standard DOCUMENTATION document fragment
+ # by providing alternative ways to provide configuration for plugins. (The above
+ # documentation fragment is tailored for modules.)
+ PLUGIN = r'''
+options:
+ hetzner_token:
+ env:
+ - name: HETZNER_DNS_TOKEN
+'''
+
+ # WARNING: This section is automatically generated by update-docs-fragments.py.
+ # It is used to augment the docs fragments module_record, module_record_set.
+ # DO NOT EDIT MANUALLY!
+ RECORD_DEFAULT_TTL = r'''
+options:
+ ttl:
+ default: null
+'''
+
+ # WARNING: This section is automatically generated by update-docs-fragments.py.
+ # It is used to augment the docs fragments module_record, module_record_info,
+ # module_record_set, module_record_set_info.
+ # DO NOT EDIT MANUALLY!
+ RECORD_TYPE_CHOICES = r'''
+options:
+ type:
+ choices:
+ - A
+ - AAAA
+ - CAA
+ - CNAME
+ - DANE
+ - DS
+ - HINFO
+ - MX
+ - NS
+ - RP
+ - SOA
+ - SRV
+ - TLSA
+ - TXT
+'''
+
+ # WARNING: This section is automatically generated by update-docs-fragments.py.
+ # It is used to augment the docs fragment module_record_sets.
+ # DO NOT EDIT MANUALLY!
+ RECORD_TYPE_CHOICES_RECORD_SETS_MODULE = r'''
+options:
+ record_sets:
+ suboptions:
+ record:
+ description:
+ - The full DNS record to create or delete.
+ - Exactly one of I(record) and I(prefix) must be specified.
+ type: str
+ prefix:
+ description:
+ - The prefix of the DNS record.
+ - This is the part of I(record) before I(zone_name). For example,
+ if the record to be modified is C(www.example.com) for the zone
+ C(example.com), the prefix is C(www). If the record in this
+ example would be C(example.com), the prefix would be C('') (empty
+ string).
+ - Exactly one of I(record) and I(prefix) must be specified.
+ type: str
+ ttl:
+ description:
+ - The TTL to give the new record, in seconds.
+ type: int
+ default: null
+ type:
+ description:
+ - The type of DNS record to create or delete.
+ required: true
+ type: str
+ choices:
+ - A
+ - AAAA
+ - CAA
+ - CNAME
+ - DANE
+ - DS
+ - HINFO
+ - MX
+ - NS
+ - RP
+ - SOA
+ - SRV
+ - TLSA
+ - TXT
+ value:
+ description:
+ - The new value when creating a DNS record.
+ - YAML lists or multiple comma-spaced values are allowed.
+ - When deleting a record all values for the record must be specified
+ or it will not be deleted.
+ - Must be specified if I(ignore=false).
+ type: list
+ elements: str
+ ignore:
+ description:
+ - If set to C(true), I(value) will be ignored.
+ - This is useful when I(prune=true), but you do not want certain
+ entries to be removed without having to know their current value.
+ type: bool
+ default: false
+'''
+
+ # WARNING: This section is automatically generated by update-docs-fragments.py.
+ # It is used to augment the docs fragment inventory_records.
+ # DO NOT EDIT MANUALLY!
+ RECORD_TYPE_CHOICES_RECORDS_INVENTORY = r'''
+options:
+ filters:
+ suboptions:
+ type:
+ description:
+ - Record types whose values to use.
+ type: list
+ elements: string
+ default:
+ - A
+ - AAAA
+ - CNAME
+ choices:
+ - A
+ - AAAA
+ - CAA
+ - CNAME
+ - DANE
+ - DS
+ - HINFO
+ - MX
+ - NS
+ - RP
+ - SOA
+ - SRV
+ - TLSA
+ - TXT
+'''
+
+ # WARNING: This section is automatically generated by update-docs-fragments.py.
+ # It is used to augment the docs fragments inventory_records, module_record,
+ # module_record_info, module_record_set, module_record_set_info,
+ # module_record_sets, module_zone_info.
+ # DO NOT EDIT MANUALLY!
+ ZONE_ID_TYPE = r'''
+options:
+ zone_id:
+ type: str
+'''
diff --git a/ansible_collections/community/dns/plugins/doc_fragments/hosttech.py b/ansible_collections/community/dns/plugins/doc_fragments/hosttech.py
new file mode 100644
index 000000000..937983f50
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/doc_fragments/hosttech.py
@@ -0,0 +1,189 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017-2020 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+class ModuleDocFragment(object):
+
+ # Standard files documentation fragment
+ DOCUMENTATION = r'''
+requirements:
+ - lxml
+
+options:
+ hosttech_username:
+ description:
+ - The username for the Hosttech API user.
+ - If provided, I(hosttech_password) must also be provided.
+ - Mutually exclusive with I(hosttech_token).
+ type: str
+ hosttech_password:
+ description:
+ - The password for the Hosttech API user.
+ - If provided, I(hosttech_username) must also be provided.
+ - Mutually exclusive with I(hosttech_token).
+ type: str
+ hosttech_token:
+ description:
+ - The password for the Hosttech API user.
+ - Mutually exclusive with I(hosttech_username) and I(hosttech_password).
+ - Since community.dns 1.2.0, the alias I(api_token) can be used.
+ aliases:
+ - api_token
+ type: str
+ version_added: 0.2.0
+'''
+
+ # NOTE: This document fragment augments the above standard DOCUMENTATION document fragment
+ # by providing alternative ways to provide configuration for plugins. (The above
+ # documentation fragment is tailored for modules.)
+ PLUGIN = r'''
+options:
+ hosttech_username:
+ env:
+ - name: ANSIBLE_HOSTTECH_API_USERNAME
+ version_added: 2.5.0
+ hosttech_password:
+ env:
+ - name: ANSIBLE_HOSTTECH_API_PASSWORD
+ version_added: 2.5.0
+ hosttech_token:
+ env:
+ - name: ANSIBLE_HOSTTECH_DNS_TOKEN
+ version_added: 2.5.0
+'''
+
+ # WARNING: This section is automatically generated by update-docs-fragments.py.
+ # It is used to augment the docs fragments module_record, module_record_set.
+ # DO NOT EDIT MANUALLY!
+ RECORD_DEFAULT_TTL = r'''
+options:
+ ttl:
+ default: 3600
+'''
+
+ # WARNING: This section is automatically generated by update-docs-fragments.py.
+ # It is used to augment the docs fragments module_record, module_record_info,
+ # module_record_set, module_record_set_info.
+ # DO NOT EDIT MANUALLY!
+ RECORD_TYPE_CHOICES = r'''
+options:
+ type:
+ choices:
+ - A
+ - AAAA
+ - CAA
+ - CNAME
+ - MX
+ - NS
+ - PTR
+ - SPF
+ - SRV
+ - TXT
+'''
+
+ # WARNING: This section is automatically generated by update-docs-fragments.py.
+ # It is used to augment the docs fragment module_record_sets.
+ # DO NOT EDIT MANUALLY!
+ RECORD_TYPE_CHOICES_RECORD_SETS_MODULE = r'''
+options:
+ record_sets:
+ suboptions:
+ record:
+ description:
+ - The full DNS record to create or delete.
+ - Exactly one of I(record) and I(prefix) must be specified.
+ type: str
+ prefix:
+ description:
+ - The prefix of the DNS record.
+ - This is the part of I(record) before I(zone_name). For example,
+ if the record to be modified is C(www.example.com) for the zone
+ C(example.com), the prefix is C(www). If the record in this
+ example would be C(example.com), the prefix would be C('') (empty
+ string).
+ - Exactly one of I(record) and I(prefix) must be specified.
+ type: str
+ ttl:
+ description:
+ - The TTL to give the new record, in seconds.
+ type: int
+ default: 3600
+ type:
+ description:
+ - The type of DNS record to create or delete.
+ required: true
+ type: str
+ choices:
+ - A
+ - AAAA
+ - CAA
+ - CNAME
+ - MX
+ - NS
+ - PTR
+ - SPF
+ - SRV
+ - TXT
+ value:
+ description:
+ - The new value when creating a DNS record.
+ - YAML lists or multiple comma-spaced values are allowed.
+ - When deleting a record all values for the record must be specified
+ or it will not be deleted.
+ - Must be specified if I(ignore=false).
+ type: list
+ elements: str
+ ignore:
+ description:
+ - If set to C(true), I(value) will be ignored.
+ - This is useful when I(prune=true), but you do not want certain
+ entries to be removed without having to know their current value.
+ type: bool
+ default: false
+'''
+
+ # WARNING: This section is automatically generated by update-docs-fragments.py.
+ # It is used to augment the docs fragment inventory_records.
+ # DO NOT EDIT MANUALLY!
+ RECORD_TYPE_CHOICES_RECORDS_INVENTORY = r'''
+options:
+ filters:
+ suboptions:
+ type:
+ description:
+ - Record types whose values to use.
+ type: list
+ elements: string
+ default:
+ - A
+ - AAAA
+ - CNAME
+ choices:
+ - A
+ - AAAA
+ - CAA
+ - CNAME
+ - MX
+ - NS
+ - PTR
+ - SPF
+ - SRV
+ - TXT
+'''
+
+ # WARNING: This section is automatically generated by update-docs-fragments.py.
+ # It is used to augment the docs fragments inventory_records, module_record,
+ # module_record_info, module_record_set, module_record_set_info,
+ # module_record_sets, module_zone_info.
+ # DO NOT EDIT MANUALLY!
+ ZONE_ID_TYPE = r'''
+options:
+ zone_id:
+ type: int
+'''
diff --git a/ansible_collections/community/dns/plugins/doc_fragments/inventory_records.py b/ansible_collections/community/dns/plugins/doc_fragments/inventory_records.py
new file mode 100644
index 000000000..fcdc55acb
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/doc_fragments/inventory_records.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# Copyright (c) 2020 Markus Bergholz <markuman+spambelongstogoogle@gmail.com>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+class ModuleDocFragment(object):
+
+ # Standard files documentation fragment
+ DOCUMENTATION = r'''
+description:
+ - Records are matched by prefix / record name and value.
+
+notes:
+ - The I(zone_name) and I(zone_id) options can be templated.
+
+options:
+ zone_name:
+ description:
+ - The DNS zone to modify.
+ - Exactly one of I(zone_name) and I(zone_id) must be specified.
+ type: str
+ aliases:
+ - zone
+ zone_id:
+ description:
+ - The ID of the DNS zone to modify.
+ - Exactly one of I(zone_name) and I(zone_id) must be specified.
+ filters:
+ description:
+ - A dictionary of filter value pairs.
+ type: dict
+ default: {}
+ suboptions:
+ # (The following must be kept in sync with the equivalent lines in <provider_name>.py!)
+ type:
+ description:
+ - Record types whose values to use.
+ type: list
+ elements: string
+ default: [A, AAAA, CNAME]
+'''
diff --git a/ansible_collections/community/dns/plugins/doc_fragments/module_record.py b/ansible_collections/community/dns/plugins/doc_fragments/module_record.py
new file mode 100644
index 000000000..4eaa956aa
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/doc_fragments/module_record.py
@@ -0,0 +1,69 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+class ModuleDocFragment(object):
+
+ # Standard files documentation fragment
+
+ # NOTE: This document fragment needs to be augmented by ZONE_ID_TYPE in a provider document fragment.
+ # The ZONE_ID_TYPE fragment will provide `choices` for the options.type entry.
+ DOCUMENTATION = r'''
+description:
+ - Records are matched by prefix / record name and value.
+
+options:
+ state:
+ description:
+ - Specifies the state of the resource record.
+ required: true
+ choices: ['present', 'absent']
+ type: str
+ zone_name:
+ description:
+ - The DNS zone to modify.
+ - Exactly one of I(zone_name) and I(zone_id) must be specified.
+ type: str
+ aliases:
+ - zone
+ zone_id:
+ description:
+ - The ID of the DNS zone to modify.
+ - Exactly one of I(zone_name) and I(zone_id) must be specified.
+ record:
+ description:
+ - The full DNS record to create or delete.
+ - Exactly one of I(record) and I(prefix) must be specified.
+ type: str
+ prefix:
+ description:
+ - The prefix of the DNS record.
+ - This is the part of I(record) before I(zone_name). For example, if the record to be modified is C(www.example.com)
+ for the zone C(example.com), the prefix is C(www). If the record in this example would be C(example.com), the
+ prefix would be C('') (empty string).
+ - Exactly one of I(record) and I(prefix) must be specified.
+ type: str
+ ttl:
+ description:
+ - The TTL to give the new record, in seconds.
+ - This is not used for record deletion.
+ type: int
+ type:
+ description:
+ - The type of DNS record to create or delete.
+ required: true
+ type: str
+ value:
+ description:
+ - The new value when creating a DNS record.
+ - When deleting a record all values for the record must be specified or it will
+ not be deleted.
+ required: true
+ type: str
+'''
diff --git a/ansible_collections/community/dns/plugins/doc_fragments/module_record_info.py b/ansible_collections/community/dns/plugins/doc_fragments/module_record_info.py
new file mode 100644
index 000000000..e039ec6aa
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/doc_fragments/module_record_info.py
@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+class ModuleDocFragment(object):
+
+ # Standard files documentation fragment
+
+ # NOTE: This document fragment needs to be augmented by ZONE_ID_TYPE in a provider document fragment.
+ # The ZONE_ID_TYPE fragment will provide `choices` for the options.type entry.
+ DOCUMENTATION = r'''
+options:
+ what:
+ description:
+ - Describes whether to fetch a single record and type combination, all types for a
+ record, or all records. By default, a single record and type combination is fetched.
+ - Note that the return value structure depends on this option.
+ choices: ['single_record', 'all_types_for_record', 'all_records']
+ default: single_record
+ type: str
+ zone_name:
+ description:
+ - The DNS zone to modify.
+ - Exactly one of I(zone) and I(zone_id) must be specified.
+ type: str
+ aliases:
+ - zone
+ zone_id:
+ description:
+ - The ID of the DNS zone to modify.
+ - Exactly one of I(zone_name) and I(zone_id) must be specified.
+ record:
+ description:
+ - The full DNS record to retrieve.
+ - If I(what) is C(single_record) or C(all_types_for_record), exactly one of I(record) and I(prefix) is required.
+ type: str
+ prefix:
+ description:
+ - The prefix of the DNS record.
+ - This is the part of I(record) before I(zone_name). For example, if the record to be modified is C(www.example.com)
+ for the zone C(example.com), the prefix is C(www). If the record in this example would be C(example.com), the
+ prefix would be C('') (empty string).
+ - If I(what) is C(single_record) or C(all_types_for_record), exactly one of I(record) and I(prefix) is required.
+ type: str
+ type:
+ description:
+ - The type of DNS record to retrieve.
+ - Required if I(what) is C(single_record).
+ type: str
+'''
diff --git a/ansible_collections/community/dns/plugins/doc_fragments/module_record_set.py b/ansible_collections/community/dns/plugins/doc_fragments/module_record_set.py
new file mode 100644
index 000000000..2b4004422
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/doc_fragments/module_record_set.py
@@ -0,0 +1,90 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+class ModuleDocFragment(object):
+
+ # Standard files documentation fragment
+
+ # NOTE: This document fragment needs to be augmented by ZONE_ID_TYPE in a provider document fragment.
+ # The ZONE_ID_TYPE fragment will provide `choices` for the options.type entry.
+ DOCUMENTATION = r'''
+options:
+ state:
+ description:
+ - Specifies the state of the resource record.
+ required: true
+ choices: ['present', 'absent']
+ type: str
+ zone_name:
+ description:
+ - The DNS zone to modify.
+ - Exactly one of I(zone_name) and I(zone_id) must be specified.
+ type: str
+ aliases:
+ - zone
+ zone_id:
+ description:
+ - The ID of the DNS zone to modify.
+ - Exactly one of I(zone_name) and I(zone_id) must be specified.
+ version_added: 0.2.0
+ record:
+ description:
+ - The full DNS record to create or delete.
+ - Exactly one of I(record) and I(prefix) must be specified.
+ type: str
+ prefix:
+ description:
+ - The prefix of the DNS record.
+ - This is the part of I(record) before I(zone_name). For example, if the record to be modified is C(www.example.com)
+ for the zone C(example.com), the prefix is C(www). If the record in this example would be C(example.com), the
+ prefix would be C('') (empty string).
+ - Exactly one of I(record) and I(prefix) must be specified.
+ type: str
+ version_added: 0.2.0
+ ttl:
+ description:
+ - The TTL to give the new record, in seconds.
+ - Will be ignored if I(state=absent) and I(on_existing=replace).
+ type: int
+ type:
+ description:
+ - The type of DNS record to create or delete.
+ required: true
+ type: str
+ value:
+ description:
+ - The new value when creating a DNS record.
+ - YAML lists or multiple comma-spaced values are allowed.
+ - When deleting a record all values for the record must be specified or it will
+ not be deleted.
+ - Must be specified if I(state=present) or when I(on_existing) is not C(replace).
+ - Will be ignored if I(state=absent) and I(on_existing=replace).
+ type: list
+ elements: str
+ on_existing:
+ description:
+ - This option defines the behavior if the record set already exists, but differs from the specified record set.
+ For this comparison, I(value) and I(ttl) are used for all records of type I(type) matching the I(prefix) resp. I(record).
+ - If set to C(replace), the record will be updated (I(state=present)) or removed (I(state=absent)).
+ This is the old I(overwrite=true) behavior.
+ - If set to C(keep_and_fail), the module will fail and not modify the records.
+ This is the old I(overwrite=false) behavior if I(state=present).
+ - If set to C(keep_and_warn), the module will warn and not modify the records.
+ - If set to C(keep), the module will not modify the records.
+ This is the old I(overwrite=false) behavior if I(state=absent).
+ - If I(state=absent) and the value is not C(replace), I(value) must be specified.
+ default: replace
+ type: str
+ choices:
+ - replace
+ - keep_and_fail
+ - keep_and_warn
+ - keep
+'''
diff --git a/ansible_collections/community/dns/plugins/doc_fragments/module_record_set_info.py b/ansible_collections/community/dns/plugins/doc_fragments/module_record_set_info.py
new file mode 100644
index 000000000..857e9036e
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/doc_fragments/module_record_set_info.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+class ModuleDocFragment(object):
+
+ # Standard files documentation fragment
+
+ # NOTE: This document fragment needs to be augmented by ZONE_ID_TYPE in a provider document fragment.
+ # The ZONE_ID_TYPE fragment will provide `choices` for the options.type entry.
+ DOCUMENTATION = r'''
+options:
+ what:
+ description:
+ - Describes whether to fetch a single record and type combination, all types for a
+ record, or all records. By default, a single record and type combination is fetched.
+ - Note that the return value structure depends on this option.
+ choices: ['single_record', 'all_types_for_record', 'all_records']
+ default: single_record
+ type: str
+ zone_name:
+ description:
+ - The DNS zone to modify.
+ - Exactly one of I(zone) and I(zone_id) must be specified.
+ type: str
+ aliases:
+ - zone
+ zone_id:
+ description:
+ - The ID of the DNS zone to modify.
+ - Exactly one of I(zone_name) and I(zone_id) must be specified.
+ version_added: 0.2.0
+ record:
+ description:
+ - The full DNS record to retrieve.
+ - If I(what) is C(single_record) or C(all_types_for_record), exactly one of I(record) and I(prefix) is required.
+ type: str
+ prefix:
+ description:
+ - The prefix of the DNS record.
+ - This is the part of I(record) before I(zone_name). For example, if the record to be modified is C(www.example.com)
+ for the zone C(example.com), the prefix is C(www). If the record in this example would be C(example.com), the
+ prefix would be C('') (empty string).
+ - If I(what) is C(single_record) or C(all_types_for_record), exactly one of I(record) and I(prefix) is required.
+ type: str
+ version_added: 0.2.0
+ type:
+ description:
+ - The type of DNS record to retrieve.
+ - Required if I(what) is C(single_record).
+ type: str
+'''
diff --git a/ansible_collections/community/dns/plugins/doc_fragments/module_record_sets.py b/ansible_collections/community/dns/plugins/doc_fragments/module_record_sets.py
new file mode 100644
index 000000000..1b393b444
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/doc_fragments/module_record_sets.py
@@ -0,0 +1,88 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+class ModuleDocFragment(object):
+
+ # Standard files documentation fragment
+ DOCUMENTATION = r'''
+description:
+ - The module allows to set, modify and delete multiple DNS record sets at once.
+ - With the I(purge) option, it is also possible to delete existing record sets
+ that are not mentioned in the module parameters. With this, it is possible
+ to synchronize the expected state of a DNS zone with the expected state.
+ - "It is possible to ignore certain record sets by specifying I(ignore: true) for
+ that record set."
+
+options:
+ zone_name:
+ description:
+ - The DNS zone to modify.
+ - Exactly one of I(zone_name) and I(zone_id) must be specified.
+ type: str
+ aliases:
+ - zone
+ zone_id:
+ description:
+ - The ID of the DNS zone to modify.
+ - Exactly one of I(zone_name) and I(zone_id) must be specified.
+ prune:
+ description:
+ - If set to C(true), will remove all existing records in the zone that are not listed in I(records).
+ type: bool
+ default: false
+ record_sets:
+ description:
+ - The records that should be present in the zone.
+ required: true
+ type: list
+ elements: dict
+ aliases:
+ - records
+ suboptions:
+ # (The following must be kept in sync with the equivalent lines in <provider_name>.py!)
+ record:
+ description:
+ - The full DNS record to create or delete.
+ - Exactly one of I(record) and I(prefix) must be specified.
+ type: str
+ prefix:
+ description:
+ - The prefix of the DNS record.
+ - This is the part of I(record) before I(zone_name). For example, if the record to be modified is C(www.example.com)
+ for the zone C(example.com), the prefix is C(www). If the record in this example would be C(example.com), the
+ prefix would be C('') (empty string).
+ - Exactly one of I(record) and I(prefix) must be specified.
+ type: str
+ ttl:
+ description:
+ - The TTL to give the new record, in seconds.
+ type: int
+ type:
+ description:
+ - The type of DNS record to create or delete.
+ required: true
+ type: str
+ value:
+ description:
+ - The new value when creating a DNS record.
+ - YAML lists or multiple comma-spaced values are allowed.
+ - When deleting a record all values for the record must be specified or it will
+ not be deleted.
+ - Must be specified if I(ignore=false).
+ type: list
+ elements: str
+ ignore:
+ description:
+ - If set to C(true), I(value) will be ignored.
+ - This is useful when I(prune=true), but you do not want certain entries to be removed
+ without having to know their current value.
+ type: bool
+ default: false
+'''
diff --git a/ansible_collections/community/dns/plugins/doc_fragments/module_zone_info.py b/ansible_collections/community/dns/plugins/doc_fragments/module_zone_info.py
new file mode 100644
index 000000000..b218051e5
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/doc_fragments/module_zone_info.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+class ModuleDocFragment(object):
+
+ # Standard files documentation fragment
+ DOCUMENTATION = r'''
+options:
+ zone_name:
+ description:
+ - The DNS zone to query.
+ - Exactly one of I(zone_name) and I(zone_id) must be specified.
+ type: str
+ aliases:
+ - zone
+ zone_id:
+ description:
+ - The ID of the DNS zone to query.
+ - Exactly one of I(zone_name) and I(zone_id) must be specified.
+ version_added: 0.2.0
+'''
diff --git a/ansible_collections/community/dns/plugins/doc_fragments/options.py b/ansible_collections/community/dns/plugins/doc_fragments/options.py
new file mode 100644
index 000000000..a9e92e8b5
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/doc_fragments/options.py
@@ -0,0 +1,65 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+class ModuleDocFragment(object):
+
+ BULK_OPERATIONS = r'''
+options:
+ bulk_operation_threshold:
+ description:
+ - Determines the threshold from when on bulk operations are used.
+ - The default value 2 means that if 2 or more operations of a kind are planned,
+ and the API supports bulk operations for this kind of operation, they will
+ be used.
+ type: int
+ default: 2
+'''
+
+ RECORD_TRANSFORMATION = r'''
+options:
+ txt_transformation:
+ description:
+ - Determines how TXT entry values are converted between the API and this module's
+ input and output.
+ - The value C(api) means that values are returned from this module as they are returned
+ from the API, and pushed to the API as they have been passed to this module. For
+ idempotency checks, the input string will be compared to the strings returned by the
+ API. The API might automatically transform some values, like splitting long values or
+ adding quotes, which can cause problems with idempotency.
+ - The value C(unquoted) automatically transforms values so that you can pass in unquoted
+ values, and the module will return unquoted values. If you pass in quoted values, they
+ will be double-quoted.
+ - The value C(quoted) automatically transforms values so that you must use quoting for values
+ that contain spaces, characters such as quotation marks and backslashes, and that are
+ longer than 255 bytes. It also makes sure to return values from the API in a normalized
+ encoding.
+ - The default value, C(unquoted), ensures that you can work with values without having
+ to care about how to correctly quote for DNS. Most users should use one of C(unquoted)
+ or C(quoted), but not C(api).
+ - B(Note:) the conversion code assumes UTF-8 encoding for values. If you need another
+ encoding use I(txt_transformation=api) and handle the encoding yourself.
+ type: str
+ choices:
+ - api
+ - quoted
+ - unquoted
+ default: unquoted
+ txt_character_encoding:
+ description:
+ - Whether to treat numeric escape sequences (C(\xyz)) as octal or decimal numbers.
+ This is only used when I(txt_transformation=quoted).
+ - The current default is C(octal) which is deprecated. It will change to C(decimal) in
+ community.dns 3.0.0. The value C(decimal) is compatible to L(RFC 1035, https://www.ietf.org/rfc/rfc1035.txt).
+ type: str
+ choices:
+ - decimal
+ - octal
+ version_added: 2.5.0
+'''
diff --git a/ansible_collections/community/dns/plugins/filter/domain_suffix.py b/ansible_collections/community/dns/plugins/filter/domain_suffix.py
new file mode 100644
index 000000000..80e86a096
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/filter/domain_suffix.py
@@ -0,0 +1,91 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2020-2021, Felix Fontein <felix@fontein.de>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+from ansible_collections.community.dns.plugins.plugin_utils.public_suffix import PUBLIC_SUFFIX_LIST
+
+
+def _remove_suffix(dns_name, suffix, keep_trailing_period):
+ suffix_len = len(suffix)
+ if suffix_len and suffix_len < len(dns_name) and not keep_trailing_period:
+ suffix_len += 1
+ return dns_name[:-suffix_len] if suffix_len else dns_name
+
+
+def get_registrable_domain(dns_name,
+ keep_unknown_suffix=True,
+ only_if_registerable=True,
+ normalize_result=False,
+ icann_only=False):
+ '''Given DNS name, returns the registrable domain.'''
+ return PUBLIC_SUFFIX_LIST.get_registrable_domain(
+ dns_name,
+ keep_unknown_suffix=keep_unknown_suffix,
+ only_if_registerable=only_if_registerable,
+ normalize_result=normalize_result,
+ icann_only=icann_only,
+ )
+
+
+def get_public_suffix(dns_name,
+ keep_leading_period=True,
+ keep_unknown_suffix=True,
+ normalize_result=False,
+ icann_only=False):
+ '''Given DNS name, returns the public suffix.'''
+ suffix = PUBLIC_SUFFIX_LIST.get_suffix(
+ dns_name,
+ keep_unknown_suffix=keep_unknown_suffix,
+ normalize_result=normalize_result,
+ icann_only=icann_only,
+ )
+ if suffix and len(suffix) < len(dns_name) and keep_leading_period:
+ suffix = '.' + suffix
+ return suffix
+
+
+def remove_registrable_domain(dns_name,
+ keep_trailing_period=False,
+ keep_unknown_suffix=True,
+ only_if_registerable=True,
+ icann_only=False):
+ '''Given DNS name, returns the part before the registrable_domain.'''
+ suffix = PUBLIC_SUFFIX_LIST.get_registrable_domain(
+ dns_name,
+ keep_unknown_suffix=keep_unknown_suffix,
+ only_if_registerable=only_if_registerable,
+ normalize_result=False,
+ icann_only=icann_only,
+ )
+ return _remove_suffix(dns_name, suffix, keep_trailing_period)
+
+
+def remove_public_suffix(dns_name,
+ keep_trailing_period=False,
+ keep_unknown_suffix=True,
+ icann_only=False):
+ '''Given DNS name, returns the part before the public suffix.'''
+ suffix = PUBLIC_SUFFIX_LIST.get_suffix(
+ dns_name,
+ keep_unknown_suffix=keep_unknown_suffix,
+ normalize_result=False,
+ icann_only=icann_only,
+ )
+ return _remove_suffix(dns_name, suffix, keep_trailing_period)
+
+
+class FilterModule(object):
+ '''Ansible jinja2 filters'''
+
+ def filters(self):
+ return {
+ 'get_public_suffix': get_public_suffix,
+ 'get_registrable_domain': get_registrable_domain,
+ 'remove_public_suffix': remove_public_suffix,
+ 'remove_registrable_domain': remove_registrable_domain,
+ }
diff --git a/ansible_collections/community/dns/plugins/filter/get_public_suffix.yml b/ansible_collections/community/dns/plugins/filter/get_public_suffix.yml
new file mode 100644
index 000000000..3bdec71af
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/filter/get_public_suffix.yml
@@ -0,0 +1,39 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+DOCUMENTATION:
+ name: get_public_suffix
+ short_description: Returns the public suffix of a DNS name
+ version_added: 0.1.0
+ description:
+ - Returns the public suffix of a DNS name.
+ options:
+ _input:
+ description:
+ - A DNS name.
+ type: string
+ required: true
+ keep_leading_period:
+ description:
+ - This controls whether the leading period of a public suffix is preserved or not.
+ type: boolean
+ default: true
+ extends_documentation_fragment:
+ - community.dns.filters
+ - community.dns.filters.public_suffix
+ - community.dns.filters.get
+ author:
+ - Felix Fontein (@felixfontein)
+
+EXAMPLES: |
+ - name: Extract the public suffix from a DNS name
+ ansible.builtin.set_fact:
+ public_suffix: "{{ 'www.ansible.co.uk' | community.dns.get_public_suffix }}"
+ # Should result in '.co.uk'
+
+RETURN:
+ _value:
+ description: The public suffix.
+ type: string
diff --git a/ansible_collections/community/dns/plugins/filter/get_registrable_domain.yml b/ansible_collections/community/dns/plugins/filter/get_registrable_domain.yml
new file mode 100644
index 000000000..8ce0a08ae
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/filter/get_registrable_domain.yml
@@ -0,0 +1,34 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+DOCUMENTATION:
+ name: get_registrable_domain
+ short_description: Returns the registrable domain name of a DNS name
+ version_added: 0.1.0
+ description:
+ - Returns the registrable domain name of a DNS name.
+ options:
+ _input:
+ description:
+ - A DNS name.
+ type: string
+ required: true
+ extends_documentation_fragment:
+ - community.dns.filters
+ - community.dns.filters.registerable_domain
+ - community.dns.filters.get
+ author:
+ - Felix Fontein (@felixfontein)
+
+EXAMPLES: |
+ - name: Extract the registrable domain from a DNS name
+ ansible.builtin.set_fact:
+ public_suffix: "{{ 'www.ansible.co.uk' | community.dns.get_registrable_domain }}"
+ # Should result in 'ansible.co.uk'
+
+RETURN:
+ _value:
+ description: The registrable domain.
+ type: string
diff --git a/ansible_collections/community/dns/plugins/filter/remove_public_suffix.yml b/ansible_collections/community/dns/plugins/filter/remove_public_suffix.yml
new file mode 100644
index 000000000..dbb3317f9
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/filter/remove_public_suffix.yml
@@ -0,0 +1,39 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+DOCUMENTATION:
+ name: remove_public_suffix
+ short_description: Removes the public suffix from a DNS name
+ version_added: 0.1.0
+ description:
+ - Removes the public suffix from a DNS name.
+ options:
+ _input:
+ description:
+ - A DNS name.
+ type: string
+ required: true
+ keep_trailing_period:
+ description:
+ - This controls whether the trailing period of the prefix (that is, the part before the
+ public suffix) is preserved or not.
+ type: boolean
+ default: false
+ extends_documentation_fragment:
+ - community.dns.filters
+ - community.dns.filters.public_suffix
+ author:
+ - Felix Fontein (@felixfontein)
+
+EXAMPLES: |
+ - name: Remove the public suffix from a DNS name
+ ansible.builtin.set_fact:
+ public_suffix: "{{ 'www.ansible.co.uk' | community.dns.remove_public_suffix }}"
+ # Should result in 'www.ansible'
+
+RETURN:
+ _value:
+ description: The part of the DNS name before the public suffix.
+ type: string
diff --git a/ansible_collections/community/dns/plugins/filter/remove_registrable_domain.yml b/ansible_collections/community/dns/plugins/filter/remove_registrable_domain.yml
new file mode 100644
index 000000000..2e8a37118
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/filter/remove_registrable_domain.yml
@@ -0,0 +1,39 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+DOCUMENTATION:
+ name: remove_registrable_domain
+ short_description: Removes the registrable domain name from a DNS name
+ version_added: 0.1.0
+ description:
+ - Removes the registrable domain name from a DNS name.
+ options:
+ _input:
+ description:
+ - A DNS name.
+ type: string
+ required: true
+ keep_trailing_period:
+ description:
+ - This controls whether the trailing period of the prefix (that is, the part before the
+ registrable domain) is preserved or not.
+ type: boolean
+ default: false
+ extends_documentation_fragment:
+ - community.dns.filters
+ - community.dns.filters.registerable_domain
+ author:
+ - Felix Fontein (@felixfontein)
+
+EXAMPLES: |
+ - name: Remove the registrable domain from a DNS name
+ ansible.builtin.set_fact:
+ public_suffix: "{{ 'www.ansible.co.uk' | community.dns.remove_registrable_domain }}"
+ # Should result in 'www'
+
+RETURN:
+ _value:
+ description: The part of the DNS name before the registrable domain.
+ type: string
diff --git a/ansible_collections/community/dns/plugins/inventory/hetzner_dns_records.py b/ansible_collections/community/dns/plugins/inventory/hetzner_dns_records.py
new file mode 100644
index 000000000..970cd8631
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/inventory/hetzner_dns_records.py
@@ -0,0 +1,94 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# Copyright (c) 2020 Markus Bergholz <markuman+spambelongstogoogle@gmail.com>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = '''
+name: hetzner_dns_records
+
+short_description: Create inventory from Hetzner DNS records
+
+version_added: 2.0.0
+
+description:
+ - This plugin allows to create an inventory from Hetzner DNS records.
+ - >-
+ For Ansible to be able to identify a YAML file as an inventory for this plugin, the inventory file must contain
+ C(plugin: community.dns.hetzner_dns_records) and its filename must end with C(hetzner_dns.yaml) or C(hetzner_dns.yml)
+
+options:
+ plugin:
+ description: The name of this plugin. Should always be set to C(community.dns.hetzner_dns_records) for this plugin to recognize it as its own.
+ # TODO: add `required: true` in 3.0.0
+ # required: true
+ choices:
+ - community.dns.hetzner_dns_records
+ type: str
+
+extends_documentation_fragment:
+ - community.dns.hetzner
+ - community.dns.hetzner.plugin
+ - community.dns.hetzner.record_type_choices_records_inventory
+ - community.dns.hetzner.zone_id_type
+ - community.dns.inventory_records
+ - community.dns.options.record_transformation
+
+notes:
+ - The provider-specific I(hetzner_token) option can be templated.
+
+author:
+ - Markus Bergholz (@markuman) <markuman+spambelongstogoogle@gmail.com>
+ - Felix Fontein (@felixfontein)
+
+seealso:
+ - module: community.dns.hetzner_dns_record_set_info
+ - module: community.dns.hetzner_dns_record_info
+'''
+
+EXAMPLES = '''
+# filename must end with hetzner_dns.yaml or hetzner_dns.yml
+
+plugin: community.dns.hetzner_dns_records
+zone_name: domain.de
+filters:
+ type:
+ - TXT
+txt_transformation: unquoted
+
+# You can also configure the token by putting secret value into this file,
+# but this is discouraged. Use a lookup like below, or leave it away and
+# set it with the HETZNER_DNS_TOKEN environment variable.
+hetzner_token: >-
+ {{ (lookup('community.sops.sops', 'keys/hetzner.sops.yml') | from_yaml).hetzner_dns_token }}
+'''
+
+from ansible_collections.community.dns.plugins.module_utils.http import (
+ OpenURLHelper,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.hetzner.api import (
+ create_hetzner_api,
+ create_hetzner_provider_information,
+)
+
+from ansible_collections.community.dns.plugins.plugin_utils.templated_options import (
+ TemplatedOptionProvider,
+)
+
+from ansible_collections.community.dns.plugins.plugin_utils.inventory.records import (
+ RecordsInventoryModule,
+)
+
+
+class InventoryModule(RecordsInventoryModule):
+ NAME = 'community.dns.hetzner_dns_records'
+ VALID_ENDINGS = ('hetzner_dns.yaml', 'hetzner_dns.yml')
+
+ def setup_api(self):
+ self.provider_information = create_hetzner_provider_information()
+ self.api = create_hetzner_api(TemplatedOptionProvider(self, self.templar), OpenURLHelper())
diff --git a/ansible_collections/community/dns/plugins/inventory/hosttech_dns_records.py b/ansible_collections/community/dns/plugins/inventory/hosttech_dns_records.py
new file mode 100644
index 000000000..aa840510d
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/inventory/hosttech_dns_records.py
@@ -0,0 +1,101 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# Copyright (c) 2020 Markus Bergholz <markuman+spambelongstogoogle@gmail.com>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = '''
+name: hosttech_dns_records
+
+short_description: Create inventory from Hosttech DNS records
+
+version_added: 2.0.0
+
+description:
+ - This plugin allows to create an inventory from Hosttech DNS records.
+ - >-
+ For Ansible to be able to identify a YAML file as an inventory for this plugin, the inventory file must contain
+ C(plugin: community.dns.hosttech_dns_records) and its filename must end with C(hosttech_dns.yaml) or C(hosttech_dns.yml)
+
+options:
+ plugin:
+ description: The name of this plugin. Should always be set to C(community.dns.hosttech_dns_records) for this plugin to recognize it as its own.
+ # TODO: add `required: true` in 3.0.0
+ # required: true
+ choices:
+ - community.dns.hosttech_dns_records
+ type: str
+
+ # We need to overwrite zone_id to be of type string, otherwise templating cannot be passed in
+ zone_id:
+ type: raw
+ # If there wouldn't be ansible-base 2.10, this should be string instead. ansible-base will
+ # not accept an integer for type=string options, whence type=string breaks backwards
+ # compatibility with previous type=int...
+ # type: string
+
+extends_documentation_fragment:
+ - community.dns.hosttech
+ - community.dns.hosttech.plugin
+ - community.dns.hosttech.record_type_choices_records_inventory
+ - community.dns.hosttech.zone_id_type
+ - community.dns.inventory_records
+ - community.dns.options.record_transformation
+
+notes:
+ - The provider-specific I(hosttech_username), I(hosttech_password), and I(hosttech_token) options can be templated.
+
+author:
+ - Markus Bergholz (@markuman) <markuman+spambelongstogoogle@gmail.com>
+ - Felix Fontein (@felixfontein)
+
+seealso:
+ - module: community.dns.hosttech_dns_record_set_info
+ - module: community.dns.hosttech_dns_record_info
+'''
+
+EXAMPLES = '''
+# filename must end with hosttech_dns.yaml or hosttech_dns.yml
+
+plugin: community.dns.hosttech_dns_records
+zone_name: domain.ch
+filters:
+ type:
+ - AAAA
+
+# You can also configure the token by putting secret value into this file,
+# but this is discouraged. Use a lookup like below, or leave it away and
+# set it with the ANSIBLE_HOSTTECH_DNS_TOKEN environment variable.
+hosttech_token: >-
+ {{ (lookup('community.sops.sops', 'keys/hosttech.sops.yml') | from_yaml).hosttech_dns_token }}
+'''
+
+from ansible_collections.community.dns.plugins.module_utils.http import (
+ OpenURLHelper,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.hosttech.api import (
+ create_hosttech_api,
+ create_hosttech_provider_information,
+)
+
+from ansible_collections.community.dns.plugins.plugin_utils.templated_options import (
+ TemplatedOptionProvider,
+)
+
+from ansible_collections.community.dns.plugins.plugin_utils.inventory.records import (
+ RecordsInventoryModule,
+)
+
+
+class InventoryModule(RecordsInventoryModule):
+ NAME = 'community.dns.hosttech_dns_records'
+ VALID_ENDINGS = ('hosttech_dns.yaml', 'hosttech_dns.yml')
+
+ def setup_api(self):
+ self.provider_information = create_hosttech_provider_information()
+ self.api = create_hosttech_api(TemplatedOptionProvider(self, self.templar), OpenURLHelper())
diff --git a/ansible_collections/community/dns/plugins/module_utils/argspec.py b/ansible_collections/community/dns/plugins/module_utils/argspec.py
new file mode 100644
index 000000000..3b5104595
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/module_utils/argspec.py
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+class ArgumentSpec(object):
+ def __init__(self, argument_spec=None, required_together=None, required_if=None, required_one_of=None, mutually_exclusive=None):
+ self.argument_spec = {}
+ self.required_together = []
+ self.required_if = []
+ self.required_one_of = []
+ self.mutually_exclusive = []
+ if argument_spec:
+ self.argument_spec.update(argument_spec)
+ if required_together:
+ self.required_together.extend(required_together)
+ if required_if:
+ self.required_if.extend(required_if)
+ if required_one_of:
+ self.required_one_of.extend(required_one_of)
+ if mutually_exclusive:
+ self.mutually_exclusive.extend(mutually_exclusive)
+
+ def merge(self, other):
+ self.argument_spec.update(other.argument_spec)
+ self.required_together.extend(other.required_together)
+ self.required_if.extend(other.required_if)
+ self.required_one_of.extend(other.required_one_of)
+ self.mutually_exclusive.extend(other.mutually_exclusive)
+ return self
+
+ def to_kwargs(self):
+ return {
+ 'argument_spec': self.argument_spec,
+ 'required_together': self.required_together,
+ 'required_if': self.required_if,
+ 'required_one_of': self.required_one_of,
+ 'mutually_exclusive': self.mutually_exclusive,
+ }
+
+
+class ModuleOptionProvider(object):
+ def __init__(self, module):
+ self.module = module
+
+ def get_option(self, option_name):
+ return self.module.params[option_name]
diff --git a/ansible_collections/community/dns/plugins/module_utils/conversion/base.py b/ansible_collections/community/dns/plugins/module_utils/conversion/base.py
new file mode 100644
index 000000000..4c2970af1
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/module_utils/conversion/base.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+class DNSConversionError(Exception):
+ def __init__(self, message):
+ super(DNSConversionError, self).__init__(message)
+ self.error_message = message
diff --git a/ansible_collections/community/dns/plugins/module_utils/conversion/converter.py b/ansible_collections/community/dns/plugins/module_utils/conversion/converter.py
new file mode 100644
index 000000000..b8a9f4a8d
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/module_utils/conversion/converter.py
@@ -0,0 +1,246 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+import warnings
+
+from ansible.module_utils.common.text.converters import to_text
+from ansible.module_utils.six import raise_from
+
+from ansible_collections.community.dns.plugins.module_utils.record import (
+ DNSRecord,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.conversion.base import (
+ DNSConversionError,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.conversion.txt import (
+ decode_txt_value,
+ encode_txt_value,
+)
+
+
+class RecordConverter(object):
+ def __init__(self, provider_information, option_provider):
+ """
+ Create a record converter.
+ """
+ self._provider_information = provider_information
+ self._option_provider = option_provider
+
+ # Valid values: 'decoded', 'encoded', 'encoded-no-octal' (deprecated), 'encoded-no-char-encoding'
+ self._txt_api_handling = self._provider_information.txt_record_handling()
+ if self._txt_api_handling == 'encoded-no-octal':
+ warnings.warn('provider_information.txt_record_handling() returned deprecated value "encoded-no-octal"')
+ self._txt_api_character_encoding = self._provider_information.txt_character_encoding()
+ # Valid values: 'api', 'quoted', 'unquoted'
+ self._txt_transformation = self._option_provider.get_option('txt_transformation')
+ # Valid values: 'decimal', 'octal'
+ self._txt_character_encoding = self._option_provider.get_option('txt_character_encoding')
+ self._txt_character_encoding_deprecation = False
+ if self._txt_character_encoding is None:
+ # TODO: remove implicit default in community.dns 3.0.0
+ self._txt_character_encoding = 'octal'
+ if self._txt_transformation == 'quoted':
+ self._txt_character_encoding_deprecation = True
+
+ def emit_deprecations(self, deprecator):
+ if self._txt_character_encoding_deprecation:
+ deprecator(
+ 'The default of the txt_character_encoding option will change from "octal" to "decimal" in community.dns 3.0.0.'
+ ' This potentially affects you since you use txt_transformation=quoted. You can explicitly set txt_character_encoding'
+ ' to "octal" to keep the current behavior, or "decimal" to already now switch to the new behavior. We recommend'
+ ' switching to the new behavior, and using check/diff mode to figure out potential changes',
+ version='3.0.0',
+ collection_name='community.dns',
+ )
+
+ def _handle_txt_api(self, to_api, record):
+ """
+ Handle TXT records for sending to/from the API.
+ """
+ if self._txt_transformation == 'api':
+ # Do not touch record values
+ return
+
+ # We assume that records internally use decoded values
+ if self._txt_api_handling in ('encoded', 'encoded-no-octal', 'encoded-no-char-encoding'):
+ if to_api:
+ record.target = encode_txt_value(
+ record.target,
+ use_character_encoding=self._txt_api_handling == 'encoded',
+ character_encoding=self._txt_api_character_encoding)
+ else:
+ record.target = decode_txt_value(record.target, character_encoding=self._txt_api_character_encoding)
+
+ def _handle_txt_user(self, to_user, record):
+ """
+ Handle TXT records for sending to/from the user.
+ """
+ if self._txt_transformation == 'api':
+ # Do not touch record values
+ return
+
+ # We assume that records internally use decoded values
+ if self._txt_transformation == 'quoted':
+ if to_user:
+ record.target = encode_txt_value(record.target, character_encoding=self._txt_character_encoding)
+ else:
+ record.target = decode_txt_value(record.target, character_encoding=self._txt_character_encoding)
+
+ def process_from_api(self, record):
+ """
+ Process a record object (DNSRecord) after receiving from API.
+ Modifies the record in-place.
+ """
+ try:
+ record.target = to_text(record.target)
+ if record.type == 'TXT':
+ self._handle_txt_api(False, record)
+ return record
+ except DNSConversionError as e:
+ raise_from(DNSConversionError(u'While processing record from API: {0}'.format(e.error_message)), e)
+
+ def process_to_api(self, record):
+ """
+ Process a record object (DNSRecord) for sending to API.
+ Modifies the record in-place.
+ """
+ try:
+ if record.type == 'TXT':
+ self._handle_txt_api(True, record)
+ return record
+ except DNSConversionError as e:
+ raise_from(DNSConversionError(u'While processing record for the API: {0}'.format(e.error_message)), e)
+
+ def process_from_user(self, record):
+ """
+ Process a record object (DNSRecord) after receiving from the user.
+ Modifies the record in-place.
+ """
+ try:
+ record.target = to_text(record.target)
+ if record.type == 'TXT':
+ self._handle_txt_user(False, record)
+ return record
+ except DNSConversionError as e:
+ raise_from(DNSConversionError(u'While processing record from the user: {0}'.format(e.error_message)), e)
+
+ def process_to_user(self, record):
+ """
+ Process a record object (DNSRecord) for sending to the user.
+ Modifies the record in-place.
+ """
+ try:
+ if record.type == 'TXT':
+ self._handle_txt_user(True, record)
+ return record
+ except DNSConversionError as e:
+ raise_from(DNSConversionError(u'While processing record for the user: {0}'.format(e.error_message)), e)
+
+ def clone_from_api(self, record):
+ """
+ Process a record object (DNSRecord) after receiving from API.
+ Return a modified clone of the record; the original will not be modified.
+ """
+ record = record.clone()
+ self.process_from_api(record)
+ return record
+
+ def clone_to_api(self, record):
+ """
+ Process a record object (DNSRecord) for sending to API.
+ Return a modified clone of the record; the original will not be modified.
+ """
+ record = record.clone()
+ self.process_to_api(record)
+ return record
+
+ def clone_multiple_from_api(self, records):
+ """
+ Process a list of record object (DNSRecord) after receiving from API.
+ Return a list of modified clones of the records; the originals will not be modified.
+ """
+ return [self.clone_from_api(record) for record in records]
+
+ def clone_multiple_to_api(self, records):
+ """
+ Process a list of record objects (DNSRecord) for sending to API.
+ Return a list of modified clones of the records; the originals will not be modified.
+ """
+ return [self.clone_to_api(record) for record in records]
+
+ def process_multiple_from_api(self, records):
+ """
+ Process a list of record object (DNSRecord) after receiving from API.
+ Modifies the records in-place.
+ """
+ for record in records:
+ self.process_from_api(record)
+ return records
+
+ def process_multiple_to_api(self, records):
+ """
+ Process a list of record objects (DNSRecord) for sending to API.
+ Modifies the records in-place.
+ """
+ for record in records:
+ self.process_to_api(record)
+ return records
+
+ def process_multiple_from_user(self, records):
+ """
+ Process a list of record object (DNSRecord) after receiving from the user.
+ Modifies the records in-place.
+ """
+ for record in records:
+ self.process_from_user(record)
+ return records
+
+ def process_multiple_to_user(self, records):
+ """
+ Process a list of record objects (DNSRecord) for sending to the user.
+ Modifies the records in-place.
+ """
+ for record in records:
+ self.process_to_user(record)
+ return records
+
+ def process_value_from_user(self, record_type, value):
+ """
+ Process a record value (string) after receiving from the user.
+ """
+ record = DNSRecord()
+ record.type = record_type
+ record.target = value
+ self.process_from_user(record)
+ return record.target
+
+ def process_values_from_user(self, record_type, values):
+ """
+ Process a list of record values (strings) after receiving from the user.
+ """
+ return [self.process_value_from_user(record_type, value) for value in values]
+
+ def process_value_to_user(self, record_type, value):
+ """
+ Process a record value (string) for sending to the user.
+ """
+ record = DNSRecord()
+ record.type = record_type
+ record.target = value
+ self.process_to_user(record)
+ return record.target
+
+ def process_values_to_user(self, record_type, values):
+ """
+ Process a list of record values (strings) for sending to the user.
+ """
+ return [self.process_value_to_user(record_type, value) for value in values]
diff --git a/ansible_collections/community/dns/plugins/module_utils/conversion/txt.py b/ansible_collections/community/dns/plugins/module_utils/conversion/txt.py
new file mode 100644
index 000000000..a4521c7fa
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/module_utils/conversion/txt.py
@@ -0,0 +1,228 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+import sys
+import warnings
+
+from ansible.module_utils.common.text.converters import to_bytes, to_text
+
+from ansible_collections.community.dns.plugins.module_utils.conversion.base import (
+ DNSConversionError,
+)
+
+
+_DECIMAL_DIGITS = b'0123456789'
+
+_STATE_OUTSIDE = 0
+_STATE_QUOTED_STRING = 1
+_STATE_UNQUOTED_STRING = 3
+
+
+if sys.version_info[0] < 3:
+ _int_to_byte = chr
+else:
+ def _int_to_byte(value):
+ return bytes((value, ))
+
+
+def _parse_quoted(value, index, use_octal):
+ if index == len(value):
+ raise DNSConversionError(u'Unexpected backslash at end of string')
+ letter = value[index:index + 1]
+ index += 1
+ if letter in (b'\\', b'"'):
+ return letter, index
+ # This must be a decimal sequence
+ v2 = _DECIMAL_DIGITS.find(letter)
+ if v2 < 0 or (use_octal and v2 >= 8):
+ # It is apparently not - error out
+ raise DNSConversionError(
+ u'A backslash must not be followed by "{letter}" (index {index})'.format(letter=to_text(letter), index=index))
+ if index + 1 >= len(value):
+ # We need more letters for a three-digit decimal sequence
+ raise DNSConversionError(
+ u'The {type} sequence at the end requires {missing} more digit(s)'.format(
+ type='octal' if use_octal else 'decimal', missing=index + 2 - len(value)))
+ letter = value[index:index + 1]
+ index += 1
+ v1 = _DECIMAL_DIGITS.find(letter)
+ if v1 < 0 or (use_octal and v1 >= 8):
+ raise DNSConversionError(
+ u'The second letter of the {type} sequence at index {index} is not a {type} digit, but "{letter}"'.format(
+ type='octal' if use_octal else 'decimal', letter=to_text(letter), index=index))
+ letter = value[index:index + 1]
+ index += 1
+ v0 = _DECIMAL_DIGITS.find(letter)
+ if v0 < 0 or (use_octal and v0 >= 8):
+ raise DNSConversionError(
+ u'The third letter of the {type} sequence at index {index} is not a {type} digit, but "{letter}"'.format(
+ type='octal' if use_octal else 'decimal', letter=to_text(letter), index=index))
+ if use_octal:
+ return _int_to_byte(v2 * 64 + v1 * 8 + v0), index
+ return _int_to_byte(v2 * 100 + v1 * 10 + v0), index
+
+
+_SENTINEL = object()
+
+
+def decode_txt_value(value, character_encoding=_SENTINEL):
+ """
+ Given an encoded TXT value, decodes it.
+
+ Raises DNSConversionError in case of errors.
+ """
+ if character_encoding is _SENTINEL:
+ warnings.warn(
+ 'The default value of the decode_txt_value parameter character_encoding is deprecated.'
+ ' Set explicitly to "octal" for the old behavior, or set to "decimal" for the new and correct behavior.',
+ DeprecationWarning,
+ )
+ character_encoding = 'octal'
+ if character_encoding not in ('octal', 'decimal'):
+ raise ValueError('character_encoding must be set to "octal" or "decimal"')
+ value = to_bytes(value)
+ state = _STATE_OUTSIDE
+ index = 0
+ length = len(value)
+ result = []
+ while index < length:
+ letter = value[index:index + 1]
+ index += 1
+ if letter == b' ':
+ if state == _STATE_QUOTED_STRING:
+ result.append(letter)
+ else:
+ state = _STATE_OUTSIDE
+ elif letter == b'\\':
+ if state != _STATE_QUOTED_STRING:
+ state = _STATE_UNQUOTED_STRING
+ letter, index = _parse_quoted(value, index, character_encoding == 'octal')
+ result.append(letter)
+ elif letter == b'"':
+ if state == _STATE_QUOTED_STRING:
+ state = _STATE_OUTSIDE
+ elif state == _STATE_OUTSIDE:
+ state = _STATE_QUOTED_STRING
+ else:
+ raise DNSConversionError(
+ u'Unexpected double quotation mark inside an unquoted block at position {index}'.format(index=index))
+ else:
+ if state != _STATE_QUOTED_STRING:
+ state = _STATE_UNQUOTED_STRING
+ result.append(letter)
+
+ if state == _STATE_QUOTED_STRING:
+ raise DNSConversionError(u'Missing double quotation mark at the end of value')
+
+ return to_text(b''.join(result))
+
+
+def _get_utf8_length(first_byte_value):
+ """
+ Given the byte value of a UTF-8 letter, returns the length of the UTF-8 character.
+ """
+ if first_byte_value & 0xE0 == 0xC0:
+ return 2
+ if first_byte_value & 0xF0 == 0xE0:
+ return 3
+ if first_byte_value & 0xF8 == 0xF0:
+ return 4
+ # Shouldn't happen
+ return 1
+
+
+def encode_txt_value(value, always_quote=False, use_character_encoding=_SENTINEL, use_octal=_SENTINEL, character_encoding=_SENTINEL):
+ """
+ Given a decoded TXT value, encodes it.
+
+ If always_quote is set to True, always use double quotes for all strings.
+ If use_character_encoding (default: True) is set to False, do not use octal encoding.
+ """
+ if use_octal is not _SENTINEL:
+ warnings.warn(
+ 'The encode_txt_value parameter use_octal is deprecated. Use use_character_encoding instead.',
+ DeprecationWarning,
+ )
+ if use_character_encoding is not _SENTINEL:
+ raise ValueError('Cannot use both use_character_encoding and use_octal. Use only use_character_encoding!')
+ use_character_encoding = use_octal
+ if use_character_encoding is _SENTINEL:
+ use_character_encoding = True
+ if character_encoding is _SENTINEL:
+ warnings.warn(
+ 'The default value of the encode_txt_value parameter character_encoding is deprecated.'
+ ' Set explicitly to "octal" for the old behavior, or set to "decimal" for the new and correct behavior.',
+ DeprecationWarning,
+ )
+ character_encoding = 'octal'
+ if character_encoding not in ('octal', 'decimal'):
+ raise ValueError('character_encoding must be set to "octal" or "decimal"')
+
+ value = to_bytes(value)
+ buffer = []
+ output = []
+
+ def append(buffer):
+ value = b''.join(buffer)
+ if b' ' in value or not value or always_quote:
+ value = b'"%s"' % value
+ output.append(value)
+
+ index = 0
+ length = len(value)
+ while index < length:
+ letter = value[index:index + 1]
+ index += 1
+
+ # Add letter
+ if letter in (b'"', b'\\'):
+ buffer.append(b'\\')
+ buffer.append(letter)
+ elif use_character_encoding and not (0x20 <= ord(letter) < 0x7F):
+ # Make sure that we don't split up a decimal sequence over multiple TXT strings
+ if len(buffer) + 4 > 255:
+ append(buffer[:255])
+ buffer = buffer[255:]
+ letter_value = ord(letter)
+ buffer.append(b'\\')
+ if character_encoding == 'octal':
+ v2 = (letter_value >> 6) & 7
+ v1 = (letter_value >> 3) & 7
+ v0 = letter_value & 7
+ else:
+ v2 = (letter_value // 100) % 10
+ v1 = (letter_value // 10) % 10
+ v0 = letter_value % 10
+ buffer.append(_DECIMAL_DIGITS[v2:v2 + 1])
+ buffer.append(_DECIMAL_DIGITS[v1:v1 + 1])
+ buffer.append(_DECIMAL_DIGITS[v0:v0 + 1])
+ elif not use_character_encoding and (ord(letter) & 0x80) != 0:
+ utf8_length = min(_get_utf8_length(ord(letter)), length - index + 1)
+ # Make sure that we don't split up a UTF-8 letter over multiple TXT strings
+ if len(buffer) + utf8_length > 255:
+ append(buffer[:255])
+ buffer = buffer[255:]
+ buffer.append(letter)
+ while utf8_length > 1:
+ buffer.append(value[index:index + 1])
+ index += 1
+ utf8_length -= 1
+ else:
+ buffer.append(letter)
+
+ # Split if too long
+ if len(buffer) >= 255:
+ append(buffer[:255])
+ buffer = buffer[255:]
+
+ if buffer or not output:
+ append(buffer)
+
+ return to_text(b' '.join(output))
diff --git a/ansible_collections/community/dns/plugins/module_utils/hetzner/api.py b/ansible_collections/community/dns/plugins/module_utils/hetzner/api.py
new file mode 100644
index 000000000..dee1ff13c
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/module_utils/hetzner/api.py
@@ -0,0 +1,419 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# Copyright (c) 2020 Markus Bergholz <markuman+spambelongstogoogle@gmail.com>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+from ansible.module_utils.basic import env_fallback
+
+from ansible_collections.community.dns.plugins.module_utils.argspec import (
+ ArgumentSpec,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.json_api_helper import (
+ JSONAPIHelper,
+ ERROR_CODES,
+ UNKNOWN_ERROR,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.provider import (
+ ProviderInformation,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.record import (
+ DNSRecord,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.zone import (
+ DNSZone,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.zone_record_api import (
+ DNSAPIError,
+ NOT_PROVIDED,
+ ZoneRecordAPI,
+ filter_records,
+)
+
+
+def _create_zone_from_json(source):
+ zone = DNSZone(source['name'])
+ zone.id = source['id']
+ info = source.copy()
+ info.pop('name')
+ info.pop('id')
+ if 'legacy_ns' in info:
+ info['legacy_ns'] = sorted(info['legacy_ns'])
+ zone.info = info
+ return zone
+
+
+def _create_record_from_json(source, type=None, has_id=True):
+ source = dict(source)
+ result = DNSRecord()
+ if has_id:
+ result.id = source.pop('id')
+ result.type = source.pop('type', type)
+ result.ttl = source.pop('ttl', None)
+ name = source.pop('name', None)
+ if name == '@':
+ name = None
+ result.prefix = name
+ result.target = source.pop('value')
+ source.pop('zone_id', None)
+ result.extra.update(source)
+ return result
+
+
+def _record_to_json(record, zone_id):
+ result = {
+ 'name': record.prefix or '@',
+ 'value': record.target,
+ 'type': record.type,
+ 'zone_id': zone_id,
+ }
+ if record.ttl is not None:
+ result['ttl'] = record.ttl
+ return result
+
+
+class HetznerAPI(ZoneRecordAPI, JSONAPIHelper):
+ def __init__(self, http_helper, token, api='https://dns.hetzner.com/api/', debug=False):
+ JSONAPIHelper.__init__(self, http_helper, token, api=api, debug=debug)
+
+ def _create_headers(self):
+ return {
+ 'Accept': 'application/json',
+ 'Auth-API-Token': self._token,
+ }
+
+ def _extract_only_error_message(self, result):
+ # These errors are not documented, but are what I experienced the API seems to return:
+ res = ''
+ if isinstance(result.get('error'), dict):
+ if 'message' in result['error']:
+ res = '{0} with error message "{1}"'.format(res, result['error']['message'])
+ if 'code' in result['error']:
+ res = '{0} (error code {1})'.format(res, result['error']['code'])
+ if result.get('message'):
+ res = '{0} with message "{1}"'.format(res, result['message'])
+ return res
+
+ def _extract_error_message(self, result):
+ if result is None:
+ return ''
+ if isinstance(result, dict):
+ res = self._extract_only_error_message(result)
+ if res:
+ return res
+ return ' with data: {0}'.format(result)
+
+ def _validate(self, result=None, info=None, expected=None, method='GET'):
+ super(HetznerAPI, self)._validate(result=result, info=info, expected=expected, method=method)
+ if isinstance(result, dict):
+ error = result.get('error')
+ if isinstance(error, dict):
+ status = error.get('code')
+ if status is None:
+ return
+ url = info['url']
+ if expected is not None and status in expected:
+ return
+ error_code = ERROR_CODES.get(status, UNKNOWN_ERROR)
+ more = self._extract_error_message(result)
+ raise DNSAPIError(
+ '{0} {1} resulted in API error {2} ({3}){4}'.format(method, url, status, error_code, more))
+
+ def _list_pagination(self, url, data_key, query=None, block_size=100, accept_404=False):
+ result = []
+ page = 1
+ while True:
+ query_ = query.copy() if query else dict()
+ query_['per_page'] = block_size
+ query_['page'] = page
+ res, info = self._get(url, query_, must_have_content=[200], expected=[200, 404] if accept_404 and page == 1 else [200])
+ if accept_404 and page == 1 and info['status'] == 404:
+ return None
+ result.extend(res[data_key])
+ if 'meta' not in res and page == 1:
+ return result
+ if page >= res['meta']['pagination']['last_page']:
+ return result
+ page += 1
+
+ def get_zone_by_name(self, name):
+ """
+ Given a zone name, return the zone contents if found.
+
+ @param name: The zone name (string)
+ @return The zone information (DNSZone), or None if not found
+ """
+ result, info = self._get('v1/zones', expected=[200, 404], query=dict(name=name))
+ for zone in result['zones']:
+ if zone.get('name') == name:
+ return _create_zone_from_json(zone)
+ return None
+
+ def get_zone_by_id(self, id):
+ """
+ Given a zone ID, return the zone contents if found.
+
+ @param id: The zone ID
+ @return The zone information (DNSZone), or None if not found
+ """
+ result, info = self._get('v1/zones/{id}'.format(id=id), expected=[200, 404], must_have_content=[200])
+ if info['status'] == 404:
+ return None
+ return _create_zone_from_json(result['zone'])
+
+ def get_zone_records(self, zone_id, prefix=NOT_PROVIDED, record_type=NOT_PROVIDED):
+ """
+ Given a zone ID, return a list of records, optionally filtered by the provided criteria.
+
+ @param zone_id: The zone ID
+ @param prefix: The prefix to filter for, if provided. Since None is a valid value,
+ the special constant NOT_PROVIDED indicates that we are not filtering.
+ @param record_type: The record type to filter for, if provided
+ @return A list of DNSrecord objects, or None if zone was not found
+ """
+ result = self._list_pagination('v1/records', data_key='records', query=dict(zone_id=zone_id), accept_404=True)
+ if result is None:
+ return None
+ return filter_records(
+ [_create_record_from_json(record) for record in result],
+ prefix=prefix,
+ record_type=record_type,
+ )
+
+ def add_record(self, zone_id, record):
+ """
+ Adds a new record to an existing zone.
+
+ @param zone_id: The zone ID
+ @param record: The DNS record (DNSRecord)
+ @return The created DNS record (DNSRecord)
+ """
+ data = _record_to_json(record, zone_id=zone_id)
+ result, info = self._post('v1/records', data=data, expected=[200, 422])
+ if info['status'] == 422:
+ raise DNSAPIError(
+ 'The new {type} record with value "{target}" and TTL {ttl} has not been accepted by the server{message}'.format(
+ type=record.type,
+ target=record.target,
+ ttl=record.ttl,
+ message=self._extract_only_error_message(result),
+ )
+ )
+ return _create_record_from_json(result['record'])
+
+ def update_record(self, zone_id, record):
+ """
+ Update a record.
+
+ @param zone_id: The zone ID
+ @param record: The DNS record (DNSRecord)
+ @return The DNS record (DNSRecord)
+ """
+ if record.id is None:
+ raise DNSAPIError('Need record ID to update record!')
+ data = _record_to_json(record, zone_id=zone_id)
+ result, info = self._put('v1/records/{id}'.format(id=record.id), data=data, expected=[200, 422])
+ if info['status'] == 422:
+ raise DNSAPIError(
+ 'The updated {type} record with value "{target}" and TTL {ttl} has not been accepted by the server{message}'.format(
+ type=record.type,
+ target=record.target,
+ ttl=record.ttl,
+ message=self._extract_only_error_message(result),
+ )
+ )
+ return _create_record_from_json(result['record'])
+
+ def delete_record(self, zone_id, record):
+ """
+ Delete a record.
+
+ @param zone_id: The zone ID
+ @param record: The DNS record (DNSRecord)
+ @return True in case of success (boolean)
+ """
+ if record.id is None:
+ raise DNSAPIError('Need record ID to delete record!')
+ dummy, info = self._delete('v1/records/{id}'.format(id=record.id), must_have_content=False, expected=[200, 404])
+ return info['status'] == 200
+
+ @staticmethod
+ def _append(results_per_zone_id, zone_id, result):
+ if zone_id not in results_per_zone_id:
+ results_per_zone_id[zone_id] = []
+ results_per_zone_id[zone_id].append(result)
+
+ def add_records(self, records_per_zone_id, stop_early_on_errors=True):
+ """
+ Add new records to an existing zone.
+
+ @param records_per_zone_id: Maps a zone ID to a list of DNS records (DNSRecord)
+ @param stop_early_on_errors: If set to ``True``, try to stop changes after the first error happens.
+ This might only work on some APIs.
+ @return A dictionary mapping zone IDs to lists of tuples ``(record, created, failed)``.
+ Here ``created`` indicates whether the record was created (``True``) or not (``False``).
+ If it was created, ``record`` contains the record ID and ``failed`` is ``None``.
+ If it was not created, ``failed`` should be a ``DNSAPIError`` instance indicating why
+ it was not created. It is possible that the API only creates records if all succeed,
+ in that case ``failed`` can be ``None`` even though ``created`` is ``False``.
+ """
+ json_records = []
+ for zone_id, records in records_per_zone_id.items():
+ for record in records:
+ json_records.append(_record_to_json(record, zone_id=zone_id))
+ data = {'records': json_records}
+ # Error 422 means that at least one of the records was not valid
+ result, info = self._post('v1/records/bulk', data=data, expected=[200, 422])
+ results_per_zone_id = {}
+ # This is the list of invalid records that was detected before accepting the whole set
+ for json_record in result.get('invalid_records') or []:
+ record = _create_record_from_json(json_record, has_id=False)
+ zone_id = json_record['zone_id']
+ self._append(results_per_zone_id, zone_id, (record, False, DNSAPIError(
+ 'Creating {type} record "{target}" with TTL {ttl} for zone {zoneID} failed with unknown reason'.format(
+ type=record.type,
+ target=record.target,
+ ttl=record.ttl,
+ zoneID=zone_id))))
+ # This is the list of valid records that were not processed
+ for json_record in result.get('valid_records') or []:
+ record = _create_record_from_json(json_record, has_id=False)
+ zone_id = json_record['zone_id']
+ self._append(results_per_zone_id, zone_id, (record, False, None))
+ # This is the list of correctly processed records
+ for json_record in result.get('records') or []:
+ record = _create_record_from_json(json_record)
+ zone_id = json_record['zone_id']
+ self._append(results_per_zone_id, zone_id, (record, True, None))
+ return results_per_zone_id
+
+ def update_records(self, records_per_zone_id, stop_early_on_errors=True):
+ """
+ Update multiple records.
+
+ @param records_per_zone_id: Maps a zone ID to a list of DNS records (DNSRecord)
+ @param stop_early_on_errors: If set to ``True``, try to stop changes after the first error happens.
+ This might only work on some APIs.
+ @return A dictionary mapping zone IDs to lists of tuples ``(record, updated, failed)``.
+ Here ``updated`` indicates whether the record was updated (``True``) or not (``False``).
+ If it was not updated, ``failed`` should be a ``DNSAPIError`` instance. If it was
+ updated, ``failed`` should be ``None``. It is possible that the API only updates
+ records if all succeed, in that case ``failed`` can be ``None`` even though
+ ``updated`` is ``False``.
+ """
+ # Currently Hetzner's bulk update API seems to be broken, it always returns the error message
+ # "An invalid response was received from the upstream server". That's why for now, we always
+ # fall back to the default implementation.
+ if True: # pylint: disable=using-constant-test
+ return super(HetznerAPI, self).update_records(records_per_zone_id, stop_early_on_errors=stop_early_on_errors)
+
+ json_records = []
+ for zone_id, records in records_per_zone_id.items():
+ for record in records:
+ json_records.append(_record_to_json(record, zone_id=zone_id))
+ data = {'records': json_records}
+ result, dummy = self._put('v1/records/bulk', data=data, expected=[200])
+ results_per_zone_id = {}
+ for json_record in result.get('failed_records') or []:
+ record = _create_record_from_json(json_record)
+ zone_id = json_record['zone_id']
+ self._append(results_per_zone_id, zone_id, (record, False, DNSAPIError(
+ 'Updating {type} record #{id} "{target}" with TTL {ttl} for zone {zoneID} failed with unknown reason'.format(
+ type=record.type,
+ id=record.id,
+ target=record.target,
+ ttl=record.ttl,
+ zoneID=zone_id))))
+ for json_record in result.get('records') or []:
+ record = _create_record_from_json(json_record)
+ zone_id = json_record['zone_id']
+ self._append(results_per_zone_id, zone_id, (record, True, None))
+ return results_per_zone_id
+
+
+class HetznerProviderInformation(ProviderInformation):
+ def get_supported_record_types(self):
+ """
+ Return a list of supported record types.
+ """
+ return ['A', 'AAAA', 'NS', 'MX', 'CNAME', 'RP', 'TXT', 'SOA', 'HINFO', 'SRV', 'DANE', 'TLSA', 'DS', 'CAA']
+
+ def get_zone_id_type(self):
+ """
+ Return the (short) type for zone IDs, like ``'int'`` or ``'str'``.
+ """
+ return 'str'
+
+ def get_record_id_type(self):
+ """
+ Return the (short) type for record IDs, like ``'int'`` or ``'str'``.
+ """
+ return 'str'
+
+ def get_record_default_ttl(self):
+ """
+ Return the default TTL for records, like 300, 3600 or None.
+ None means that some other TTL (usually from the zone) will be used.
+ """
+ return None
+
+ def normalize_prefix(self, prefix):
+ """
+ Given a prefix (string or None), return its normalized form.
+
+ The result should always be None for the trivial prefix, and a non-zero length DNS name
+ for a non-trivial prefix.
+
+ If a provider supports other identifiers for the trivial prefix, such as '@', this
+ function needs to convert them to None as well.
+ """
+ return None if prefix in ('@', '') else prefix
+
+ def supports_bulk_actions(self):
+ """
+ Return whether the API supports some kind of bulk actions.
+ """
+ return True
+
+ def txt_record_handling(self):
+ """
+ Return how the API handles TXT records.
+
+ Returns one of the following strings:
+ * 'decoded' - the API works with unencoded values
+ * 'encoded' - the API works with encoded values
+ * 'encoded-no-char-encoding' - the API works with encoded values, but without character encoding
+ """
+ return 'encoded-no-char-encoding'
+
+
+def create_hetzner_provider_information():
+ return HetznerProviderInformation()
+
+
+def create_hetzner_argument_spec():
+ return ArgumentSpec(
+ argument_spec=dict(
+ hetzner_token=dict(
+ type='str',
+ required=True,
+ no_log=True,
+ aliases=['api_token'],
+ fallback=(env_fallback, ['HETZNER_DNS_TOKEN']),
+ ),
+ ),
+ )
+
+
+def create_hetzner_api(option_provider, http_helper):
+ return HetznerAPI(http_helper, option_provider.get_option('hetzner_token'))
diff --git a/ansible_collections/community/dns/plugins/module_utils/hosttech/api.py b/ansible_collections/community/dns/plugins/module_utils/hosttech/api.py
new file mode 100644
index 000000000..adf2dd15c
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/module_utils/hosttech/api.py
@@ -0,0 +1,115 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017-2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+from ansible_collections.community.dns.plugins.module_utils.argspec import (
+ ArgumentSpec,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.provider import (
+ ProviderInformation,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.wsdl import (
+ HAS_LXML_ETREE,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.zone_record_api import (
+ DNSAPIError,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.hosttech.wsdl_api import (
+ HostTechWSDLAPI,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.hosttech.json_api import (
+ HostTechJSONAPI,
+)
+
+
+class HosttechProviderInformation(ProviderInformation):
+ def get_supported_record_types(self):
+ """
+ Return a list of supported record types.
+ """
+ return ['A', 'CNAME', 'MX', 'AAAA', 'TXT', 'PTR', 'SRV', 'SPF', 'NS', 'CAA']
+
+ def get_zone_id_type(self):
+ """
+ Return the (short) type for zone IDs, like ``'int'`` or ``'str'``.
+ """
+ return 'int'
+
+ def get_record_id_type(self):
+ """
+ Return the (short) type for record IDs, like ``'int'`` or ``'str'``.
+ """
+ return 'int'
+
+ def get_record_default_ttl(self):
+ """
+ Return the default TTL for records, like 300, 3600 or None.
+ None means that some other TTL (usually from the zone) will be used.
+ """
+ return 3600
+
+ def normalize_prefix(self, prefix):
+ """
+ Given a prefix (string or None), return its normalized form.
+
+ The result should always be None for the trivial prefix, and a non-zero length DNS name
+ for a non-trivial prefix.
+
+ If a provider supports other identifiers for the trivial prefix, such as '@', this
+ function needs to convert them to None as well.
+ """
+ return prefix or None
+
+ def txt_record_handling(self):
+ """
+ Return how the API handles TXT records.
+
+ Returns one of the following strings:
+ * 'decoded' - the API works with unencoded values
+ * 'encoded' - the API works with encoded values
+ * 'encoded-no-char-encoding' - the API works with encoded values, but without character encoding
+ """
+ return 'decoded'
+
+
+def create_hosttech_provider_information():
+ return HosttechProviderInformation()
+
+
+def create_hosttech_argument_spec():
+ return ArgumentSpec(
+ argument_spec=dict(
+ hosttech_username=dict(type='str'),
+ hosttech_password=dict(type='str', no_log=True),
+ hosttech_token=dict(type='str', no_log=True, aliases=['api_token']),
+ ),
+ required_together=[('hosttech_username', 'hosttech_password')],
+ mutually_exclusive=[('hosttech_username', 'hosttech_token')],
+ )
+
+
+def create_hosttech_api(option_provider, http_helper):
+ username = option_provider.get_option('hosttech_username')
+ password = option_provider.get_option('hosttech_password')
+ if username is not None and password is not None:
+ if not HAS_LXML_ETREE:
+ raise DNSAPIError('Needs lxml Python module (pip install lxml)')
+
+ return HostTechWSDLAPI(http_helper, username, password, debug=False)
+
+ token = option_provider.get_option('hosttech_token')
+ if token is not None:
+ return HostTechJSONAPI(http_helper, token)
+
+ raise DNSAPIError('One of hosttech_token or both hosttech_username and hosttech_password must be provided!')
diff --git a/ansible_collections/community/dns/plugins/module_utils/hosttech/json_api.py b/ansible_collections/community/dns/plugins/module_utils/hosttech/json_api.py
new file mode 100644
index 000000000..f5f5ee78d
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/module_utils/hosttech/json_api.py
@@ -0,0 +1,340 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017-2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# The API documentation can be found here: https://api.ns1.hosttech.eu/api/documentation/
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+from ansible_collections.community.dns.plugins.module_utils.json_api_helper import (
+ JSONAPIHelper,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.record import (
+ DNSRecord,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.zone import (
+ DNSZone,
+ DNSZoneWithRecords,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.zone_record_api import (
+ DNSAPIError,
+ NOT_PROVIDED,
+ ZoneRecordAPI,
+ filter_records,
+)
+
+
+def _create_record_from_json(source, type=None):
+ source = dict(source)
+ result = DNSRecord()
+ result.id = source.pop('id')
+ result.type = source.pop('type', type)
+ ttl = source.pop('ttl')
+ result.ttl = int(ttl) if ttl is not None else None
+ result.extra['comment'] = source.pop('comment')
+
+ name = source.pop('name', None)
+ target = None
+ if result.type == 'A':
+ target = source.pop('ipv4')
+ elif result.type == 'AAAA':
+ target = source.pop('ipv6')
+ elif result.type == 'CAA':
+ target = '{0} {1} "{2}"'.format(source.pop('flag'), source.pop('tag'), source.pop('value'))
+ elif result.type == 'CNAME':
+ target = source.pop('cname')
+ elif result.type == 'MX':
+ mx_name, name = name, source.pop('ownername')
+ target = '{0} {1}'.format(source.pop('pref'), mx_name)
+ elif result.type == 'NS':
+ name = source.pop('ownername')
+ target = source.pop('targetname')
+ elif result.type == 'PTR':
+ ptr_name, name = name, ''
+ target = '{0} {1}'.format(source.pop('origin'), ptr_name)
+ elif result.type == 'SRV':
+ name = source.pop('service')
+ target = '{0} {1} {2} {3}'.format(source.pop('priority'), source.pop('weight'), source.pop('port'), source.pop('target'))
+ elif result.type == 'TXT':
+ target = source.pop('text')
+ elif result.type == 'TLSA':
+ target = source.pop('text')
+ else:
+ raise DNSAPIError('Cannot parse unknown record type: {0}'.format(result.type))
+
+ result.prefix = name or None # API returns '', we want None
+ result.target = target
+ result.extra.update(source)
+ return result
+
+
+def _create_zone_from_json(source):
+ zone = DNSZone(source['name'])
+ zone.id = source['id']
+ zone.info = dict(
+ dnssec=source['dnssec'],
+ dnssec_email=source.get('dnssec_email'),
+ ds_records=source.get('ds_records'),
+ email=source.get('email'),
+ ttl=source['ttl'],
+ )
+ return zone
+
+
+def _create_zone_with_records_from_json(source, prefix=NOT_PROVIDED, record_type=NOT_PROVIDED):
+ return DNSZoneWithRecords(
+ _create_zone_from_json(source),
+ filter_records(
+ [_create_record_from_json(record) for record in source['records']],
+ prefix=prefix,
+ record_type=record_type,
+ ),
+ )
+
+
+def _record_to_json(record, include_id=False, include_type=True):
+ result = {
+ 'ttl': record.ttl,
+ 'comment': record.extra.get('comment') or '',
+ }
+ if include_type:
+ result['type'] = record.type
+ if include_id:
+ result['id'] = record.id
+
+ if record.type == 'A':
+ result['name'] = record.prefix or ''
+ result['ipv4'] = record.target
+ elif record.type == 'AAAA':
+ result['name'] = record.prefix or ''
+ result['ipv6'] = record.target
+ elif record.type == 'CAA':
+ result['name'] = record.prefix or ''
+ try:
+ flag, tag, value = record.target.split(' ', 2)
+ if value.startswith('"') and value.endswith('"'):
+ value = value[1:-1]
+ result['flag'] = flag
+ result['tag'] = tag
+ result['value'] = value
+ except Exception as e:
+ raise DNSAPIError(
+ 'Cannot split {0} record "{1}" into flag, tag and value: {2}'.format(
+ record.type, record.target, e))
+ elif record.type == 'CNAME':
+ result['name'] = record.prefix or ''
+ result['cname'] = record.target
+ elif record.type == 'MX':
+ result['ownername'] = record.prefix or ''
+ try:
+ pref, name = record.target.split(' ', 1)
+ result['pref'] = int(pref)
+ result['name'] = name
+ except Exception as e:
+ raise DNSAPIError(
+ 'Cannot split {0} record "{1}" into integer preference and name: {2}'.format(
+ record.type, record.target, e))
+ elif record.type == 'NS':
+ result['ownername'] = record.prefix or ''
+ result['targetname'] = record.target
+ elif record.type == 'PTR':
+ try:
+ origin, name = record.target.split(' ', 1)
+ result['origin'] = origin
+ result['name'] = name
+ except Exception as e:
+ raise DNSAPIError(
+ 'Cannot split {0} record "{1}" into origin and name: {2}'.format(
+ record.type, record.target, e))
+ elif record.type == 'SRV':
+ result['service'] = record.prefix or ''
+ try:
+ priority, weight, port, target = record.target.split(' ', 3)
+ result['priority'] = int(priority)
+ result['weight'] = int(weight)
+ result['port'] = int(port)
+ result['target'] = target
+ except Exception as e:
+ raise DNSAPIError(
+ 'Cannot split {0} record "{1}" into integer priority, integer weight, integer port and target: {2}'.format(
+ record.type, record.target, e))
+ elif record.type == 'TXT':
+ result['name'] = record.prefix or ''
+ result['text'] = record.target
+ elif record.type == 'TLSA':
+ result['name'] = record.prefix or ''
+ result['text'] = record.target
+ else:
+ raise DNSAPIError('Cannot serialize unknown record type: {0}'.format(record.type))
+
+ return result
+
+
+class HostTechJSONAPI(ZoneRecordAPI, JSONAPIHelper):
+ def __init__(self, http_helper, token, api='https://api.ns1.hosttech.eu/api/', debug=False):
+ """
+ Create a new HostTech API instance with given API token.
+ """
+ JSONAPIHelper.__init__(self, http_helper, token, api=api, debug=debug)
+
+ def _extract_error_message(self, result):
+ if result is None:
+ return ''
+ if isinstance(result, dict):
+ res = ''
+ if result.get('message'):
+ res = '{0} with message "{1}"'.format(res, result['message'])
+ if 'errors' in result:
+ if isinstance(result['errors'], dict):
+ for k, v in sorted(result['errors'].items()):
+ if isinstance(v, list):
+ v = '; '.join(v)
+ res = '{0} (field "{1}": {2})'.format(res, k, v)
+ if res:
+ return res
+ return ' with data: {0}'.format(result)
+
+ def _create_headers(self):
+ return dict(
+ accept='application/json',
+ authorization='Bearer {token}'.format(token=self._token),
+ )
+
+ def _list_pagination(self, url, query=None, block_size=100):
+ result = []
+ offset = 0
+ while True:
+ query_ = query.copy() if query else dict()
+ query_['limit'] = block_size
+ query_['offset'] = offset
+ res, info = self._get(url, query_, must_have_content=True, expected=[200])
+ result.extend(res['data'])
+ if len(res['data']) < block_size:
+ return result
+ offset += block_size
+
+ def get_zone_with_records_by_id(self, id, prefix=NOT_PROVIDED, record_type=NOT_PROVIDED):
+ """
+ Given a zone ID, return the zone contents with records if found.
+
+ @param id: The zone ID
+ @param prefix: The prefix to filter for, if provided. Since None is a valid value,
+ the special constant NOT_PROVIDED indicates that we are not filtering.
+ @param record_type: The record type to filter for, if provided
+ @return The zone information with records (DNSZoneWithRecords), or None if not found
+ """
+ result, info = self._get('user/v1/zones/{0}'.format(id), expected=[200, 404], must_have_content=[200])
+ if info['status'] == 404:
+ return None
+ return _create_zone_with_records_from_json(result['data'], prefix=prefix, record_type=record_type)
+
+ def get_zone_with_records_by_name(self, name, prefix=NOT_PROVIDED, record_type=NOT_PROVIDED):
+ """
+ Given a zone name, return the zone contents with records if found.
+
+ @param name: The zone name (string)
+ @param prefix: The prefix to filter for, if provided. Since None is a valid value,
+ the special constant NOT_PROVIDED indicates that we are not filtering.
+ @param record_type: The record type to filter for, if provided
+ @return The zone information with records (DNSZoneWithRecords), or None if not found
+ """
+ result = self._list_pagination('user/v1/zones', query=dict(query=name))
+ for zone in result:
+ if zone['name'] == name:
+ result, info = self._get('user/v1/zones/{0}'.format(zone['id']), expected=[200])
+ return _create_zone_with_records_from_json(result['data'], prefix=prefix, record_type=record_type)
+ return None
+
+ def get_zone_records(self, zone_id, prefix=NOT_PROVIDED, record_type=NOT_PROVIDED):
+ """
+ Given a zone ID, return a list of records, optionally filtered by the provided criteria.
+
+ @param zone_id: The zone ID
+ @param prefix: The prefix to filter for, if provided. Since None is a valid value,
+ the special constant NOT_PROVIDED indicates that we are not filtering.
+ @param record_type: The record type to filter for, if provided
+ @return A list of DNSrecord objects, or None if zone was not found
+ """
+ query = dict()
+ if record_type is not NOT_PROVIDED:
+ query['type'] = record_type.upper()
+ result, info = self._get('user/v1/zones/{0}/records'.format(zone_id), query=query, expected=[200, 404], must_have_content=[200])
+ if info['status'] == 404:
+ return None
+ return filter_records(
+ [_create_record_from_json(record) for record in result['data']],
+ prefix=prefix,
+ record_type=record_type,
+ )
+
+ def get_zone_by_name(self, name):
+ """
+ Given a zone name, return the zone contents if found.
+
+ @param name: The zone name (string)
+ @return The zone information (DNSZone), or None if not found
+ """
+ result = self._list_pagination('user/v1/zones', query=dict(query=name))
+ for zone in result:
+ if zone['name'] == name:
+ # We cannot simply return `_create_zone_from_json(zone)`, since this contains less information!
+ return self.get_zone_by_id(zone['id'])
+ return None
+
+ def get_zone_by_id(self, id):
+ """
+ Given a zone ID, return the zone contents if found.
+
+ @param id: The zone ID
+ @return The zone information (DNSZone), or None if not found
+ """
+ result, info = self._get('user/v1/zones/{0}'.format(id), expected=[200, 404], must_have_content=[200])
+ if info['status'] == 404:
+ return None
+ return _create_zone_from_json(result['data'])
+
+ def add_record(self, zone_id, record):
+ """
+ Adds a new record to an existing zone.
+
+ @param zone_id: The zone ID
+ @param record: The DNS record (DNSRecord)
+ @return The created DNS record (DNSRecord)
+ """
+ data = _record_to_json(record, include_id=False, include_type=True)
+ result, dummy = self._post('user/v1/zones/{0}/records'.format(zone_id), data=data, expected=[201])
+ return _create_record_from_json(result['data'])
+
+ def update_record(self, zone_id, record):
+ """
+ Update a record.
+
+ @param zone_id: The zone ID
+ @param record: The DNS record (DNSRecord)
+ @return The DNS record (DNSRecord)
+ """
+ if record.id is None:
+ raise DNSAPIError('Need record ID to update record!')
+ data = _record_to_json(record, include_id=False, include_type=False)
+ result, dummy = self._put('user/v1/zones/{0}/records/{1}'.format(zone_id, record.id), data=data, expected=[200])
+ return _create_record_from_json(result['data'])
+
+ def delete_record(self, zone_id, record):
+ """
+ Delete a record.
+
+ @param zone_id: The zone ID
+ @param record: The DNS record (DNSRecord)
+ @return True in case of success (boolean)
+ """
+ if record.id is None:
+ raise DNSAPIError('Need record ID to delete record!')
+ dummy, info = self._delete('user/v1/zones/{0}/records/{1}'.format(zone_id, record.id), must_have_content=False, expected=[204, 404])
+ return info['status'] == 204
diff --git a/ansible_collections/community/dns/plugins/module_utils/hosttech/wsdl_api.py b/ansible_collections/community/dns/plugins/module_utils/hosttech/wsdl_api.py
new file mode 100644
index 000000000..0e36e1ffa
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/module_utils/hosttech/wsdl_api.py
@@ -0,0 +1,279 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017-2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+from ansible.module_utils.six import raise_from
+from ansible.module_utils.common.text.converters import to_native
+
+from ansible_collections.community.dns.plugins.module_utils.record import (
+ DNSRecord,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.wsdl import (
+ WSDLError,
+ WSDLNetworkError,
+ Composer,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.zone import (
+ DNSZone,
+ DNSZoneWithRecords,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.zone_record_api import (
+ DNSAPIError,
+ DNSAPIAuthenticationError,
+ NOT_PROVIDED,
+ ZoneRecordAPI,
+ filter_records,
+)
+
+
+def _create_record_from_encoding(source, type=None):
+ source = dict(source)
+ result = DNSRecord()
+ result.id = source.pop('id')
+ result.type = source.pop('type', type)
+ result.prefix = source.pop('prefix', None)
+ ttl = source.pop('ttl')
+ result.ttl = int(ttl) if ttl is not None else None
+ priority = source.pop('priority')
+ target = source.pop('target')
+ if result.type in ('PTR', 'MX'):
+ result.target = '{0} {1}'.format(priority, target)
+ else:
+ result.target = target
+ source.pop('zone', None)
+ result.extra['comment'] = source.pop('comment') or ''
+ result.extra.update(source)
+ return result
+
+
+def _create_zone_from_encoding(source, prefix=NOT_PROVIDED, record_type=NOT_PROVIDED):
+ zone = DNSZone(source['name'])
+ zone.id = source['id']
+ zone.info = dict(
+ email=source.get('email'),
+ ttl=source['ttl'],
+ )
+ return DNSZoneWithRecords(
+ zone,
+ filter_records(
+ [_create_record_from_encoding(record) for record in source['records']],
+ prefix=prefix,
+ record_type=record_type,
+ ),
+ )
+
+
+def _encode_record(record, include_id=False):
+ result = {
+ 'type': record.type,
+ 'prefix': record.prefix,
+ 'target': record.target,
+ 'ttl': record.ttl,
+ }
+ if record.type in ('PTR', 'MX'):
+ try:
+ priority, target = record.target.split(' ', 1)
+ result['priority'] = int(priority)
+ result['target'] = target
+ except Exception as e:
+ raise DNSAPIError(
+ 'Cannot split {0} record "{1}" into integer priority and target: {2}'.format(
+ record.type, record.target, e))
+ else:
+ result['priority'] = None
+ if include_id:
+ result['id'] = record.id
+ return result
+
+
+def _encode_zone(zone):
+ return {
+ 'id': zone.id,
+ 'name': zone.name,
+ # 'email': zone.email,
+ # 'ttl': zone.ttl,
+ # 'nameserver': zone.nameserver,
+ # 'serial': zone.serial,
+ # 'template': zone.template,
+ 'records': [_encode_record(record, include_id=True) for record in zone.records],
+ }
+
+
+class HostTechWSDLAPI(ZoneRecordAPI):
+ def __init__(self, http_helper, username, password, api='https://ns1.hosttech.eu/public/api', debug=False):
+ """
+ Create a new HostTech API instance with given username and password.
+ """
+ self._http_helper = http_helper
+ self._api = api
+ self._namespaces = {
+ 'ns1': 'https://ns1.hosttech.eu/soap',
+ }
+ self._username = username
+ self._password = password
+ self._debug = debug
+
+ def _prepare(self):
+ command = Composer(self._http_helper, self._api, self._namespaces)
+ command.add_auth(self._username, self._password)
+ return command
+
+ def _announce(self, msg):
+ if self._debug:
+ pass
+ # q.q('{0} {1} {2}'.format('=' * 4, msg, '=' * 40))
+
+ def _execute(self, command, result_name, acceptable_types):
+ if self._debug:
+ pass
+ # q.q('Request: {0}'.format(command))
+ try:
+ result = command.execute(debug=self._debug)
+ except WSDLError as e:
+ if e.error_code == '998':
+ raise DNSAPIAuthenticationError('Error on authentication ({0})'.format(e.error_message))
+ raise
+ res = result.get_result(result_name)
+ if isinstance(res, acceptable_types):
+ if self._debug:
+ pass
+ # q.q('Extracted result: {0} (type {1})'.format(res, type(res)))
+ return res
+ if self._debug:
+ pass
+ # q.q('Result: {0}; extracted type {1}'.format(result, type(res)))
+ raise DNSAPIError('Result has unexpected type {0} (expecting {1})!'.format(type(res), acceptable_types))
+
+ def get_zone_with_records_by_name(self, name, prefix=NOT_PROVIDED, record_type=NOT_PROVIDED):
+ """
+ Given a zone name, return the zone contents with records if found.
+
+ @param name: The zone name (string)
+ @param prefix: The prefix to filter for, if provided. Since None is a valid value,
+ the special constant NOT_PROVIDED indicates that we are not filtering.
+ @param record_type: The record type to filter for, if provided
+ @return The zone information with records (DNSZoneWithRecords), or None if not found
+ """
+ self._announce('get zone')
+ command = self._prepare()
+ command.add_simple_command('getZone', sZoneName=name)
+ try:
+ return _create_zone_from_encoding(self._execute(command, 'getZoneResponse', dict), prefix=prefix, record_type=record_type)
+ except WSDLError as exc:
+ if exc.error_origin == 'server' and exc.error_message == 'zone not found':
+ return None
+ raise_from(DNSAPIError('Error while getting zone: {0}'.format(to_native(exc))), exc)
+ except WSDLNetworkError as exc:
+ raise_from(DNSAPIError('Network error while getting zone: {0}'.format(to_native(exc))), exc)
+
+ def get_zone_with_records_by_id(self, id, prefix=NOT_PROVIDED, record_type=NOT_PROVIDED):
+ """
+ Given a zone ID, return the zone contents with records if found.
+
+ @param id: The zone ID
+ @param prefix: The prefix to filter for, if provided. Since None is a valid value,
+ the special constant NOT_PROVIDED indicates that we are not filtering.
+ @param record_type: The record type to filter for, if provided
+ @return The zone information with records (DNSZoneWithRecords), or None if not found
+ """
+ return self.get_zone_with_records_by_name(str(id), prefix=prefix, record_type=record_type)
+
+ def get_zone_records(self, zone_id, prefix=NOT_PROVIDED, record_type=NOT_PROVIDED):
+ """
+ Given a zone ID, return a list of records, optionally filtered by the provided criteria.
+
+ @param zone_id: The zone ID
+ @param prefix: The prefix to filter for, if provided. Since None is a valid value,
+ the special constant NOT_PROVIDED indicates that we are not filtering.
+ @param record_type: The record type to filter for, if provided
+ @return A list of DNSrecord objects, or None if zone was not found
+ """
+ result = self.get_zone_with_records_by_id(zone_id, prefix=prefix, record_type=record_type)
+ return result.records if result is not None else None
+
+ def get_zone_by_name(self, name):
+ """
+ Given a zone name, return the zone contents if found.
+
+ @param name: The zone name (string)
+ @return The zone information (DNSZone), or None if not found
+ """
+ zone = self.get_zone_with_records_by_name(name)
+ return zone.zone if zone else None
+
+ def get_zone_by_id(self, id):
+ """
+ Given a zone ID, return the zone contents if found.
+
+ @param id: The zone ID
+ @return The zone information (DNSZone), or None if not found
+ """
+ zone = self.get_zone_with_records_by_id(id)
+ return zone.zone if zone else None
+
+ def add_record(self, zone_id, record):
+ """
+ Adds a new record to an existing zone.
+
+ @param zone_id: The zone ID
+ @param record: The DNS record (DNSRecord)
+ @return The created DNS record (DNSRecord)
+ """
+ self._announce('add record')
+ command = self._prepare()
+ command.add_simple_command('addRecord', search=str(zone_id), recorddata=_encode_record(record, include_id=False))
+ try:
+ return _create_record_from_encoding(self._execute(command, 'addRecordResponse', dict))
+ except WSDLError as exc:
+ raise_from(DNSAPIError('Error while adding record: {0}'.format(to_native(exc))), exc)
+ except WSDLNetworkError as exc:
+ raise_from(DNSAPIError('Network error while adding record: {0}'.format(to_native(exc))), exc)
+
+ def update_record(self, zone_id, record):
+ """
+ Update a record.
+
+ @param zone_id: The zone ID
+ @param record: The DNS record (DNSRecord)
+ @return The DNS record (DNSRecord)
+ """
+ if record.id is None:
+ raise DNSAPIError('Need record ID to update record!')
+ self._announce('update record')
+ command = self._prepare()
+ command.add_simple_command('updateRecord', recordId=record.id, recorddata=_encode_record(record, include_id=False))
+ try:
+ return _create_record_from_encoding(self._execute(command, 'updateRecordResponse', dict))
+ except WSDLError as exc:
+ raise_from(DNSAPIError('Error while updating record: {0}'.format(to_native(exc))), exc)
+ except WSDLNetworkError as exc:
+ raise_from(DNSAPIError('Network error while updating record: {0}'.format(to_native(exc))), exc)
+
+ def delete_record(self, zone_id, record):
+ """
+ Delete a record.
+
+ @param zone_id: The zone ID
+ @param record: The DNS record (DNSRecord)
+ @return True in case of success (boolean)
+ """
+ if record.id is None:
+ raise DNSAPIError('Need record ID to delete record!')
+ self._announce('delete record')
+ command = self._prepare()
+ command.add_simple_command('deleteRecord', recordId=record.id)
+ try:
+ return self._execute(command, 'deleteRecordResponse', bool)
+ except WSDLError as exc:
+ raise_from(DNSAPIError('Error while deleting record: {0}'.format(to_native(exc))), exc)
+ except WSDLNetworkError as exc:
+ raise_from(DNSAPIError('Network error while deleting record: {0}'.format(to_native(exc))), exc)
diff --git a/ansible_collections/community/dns/plugins/module_utils/http.py b/ansible_collections/community/dns/plugins/module_utils/http.py
new file mode 100644
index 000000000..fc4e1a590
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/module_utils/http.py
@@ -0,0 +1,76 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+import abc
+
+from ansible.module_utils import six
+from ansible.module_utils.common.text.converters import to_native
+from ansible.module_utils.six import PY3
+from ansible.module_utils.urls import fetch_url, open_url, urllib_error, NoSSLError, ConnectionError
+
+
+class NetworkError(Exception):
+ pass
+
+
+@six.add_metaclass(abc.ABCMeta)
+class HTTPHelper(object):
+ @abc.abstractmethod
+ def fetch_url(self, url, method='GET', headers=None, data=None, timeout=None):
+ """
+ Execute a HTTP request and return a tuple (response_content, info).
+
+ In case of errors, either raise NetworkError or terminate the program (for modules only!).
+ """
+
+
+class ModuleHTTPHelper(HTTPHelper):
+ def __init__(self, module):
+ self.module = module
+
+ def fetch_url(self, url, method='GET', headers=None, data=None, timeout=None):
+ response, info = fetch_url(self.module, url, method=method, headers=headers, data=data, timeout=timeout)
+ try:
+ # In Python 2, reading from a closed response yields a TypeError.
+ # In Python 3, read() simply returns ''
+ if PY3 and response.closed:
+ raise TypeError
+ content = response.read()
+ except (AttributeError, TypeError):
+ content = info.pop('body', None)
+ return content, info
+
+
+class OpenURLHelper(HTTPHelper):
+ def fetch_url(self, url, method='GET', headers=None, data=None, timeout=None):
+ info = {}
+ try:
+ req = open_url(url, method=method, headers=headers, data=data, timeout=timeout)
+ result = req.read()
+ info.update(dict((k.lower(), v) for k, v in req.info().items()))
+ info['status'] = req.code
+ info['url'] = req.geturl()
+ req.close()
+ except urllib_error.HTTPError as e:
+ try:
+ result = e.read()
+ except AttributeError:
+ result = ''
+ try:
+ info.update(dict((k.lower(), v) for k, v in e.info().items()))
+ except Exception:
+ pass
+ info['status'] = e.code
+ except NoSSLError as e:
+ raise NetworkError('Cannot connect via SSL: {0}'.format(to_native(e)))
+ except (ConnectionError, ValueError) as e:
+ raise NetworkError('Connection error: {0}'.format(to_native(e)))
+
+ return result, info
diff --git a/ansible_collections/community/dns/plugins/module_utils/json_api_helper.py b/ansible_collections/community/dns/plugins/module_utils/json_api_helper.py
new file mode 100644
index 000000000..af5a927b5
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/module_utils/json_api_helper.py
@@ -0,0 +1,193 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# Copyright (c) 2020 Markus Bergholz <markuman+spambelongstogoogle@gmail.com>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+import json
+import time
+
+from ansible.module_utils.six.moves.urllib.parse import urlencode
+from ansible.module_utils.common.text.converters import to_native
+
+from ansible_collections.community.dns.plugins.module_utils.zone_record_api import (
+ DNSAPIError,
+ DNSAPIAuthenticationError,
+)
+
+ERROR_CODES = {
+ 200: "Successful response",
+ 401: "Unauthorized",
+ 403: "Forbidden",
+ 404: "Not found",
+ 406: "Not acceptable",
+ 409: "Conflict",
+ 422: "Unprocessable entity",
+ 500: "Internal Server Error",
+}
+UNKNOWN_ERROR = "Unknown Error"
+
+
+def _get_header_value(info, header_name):
+ header_name = header_name.lower()
+ header_value = info.get(header_name)
+ for k, v in info.items():
+ if k.lower() == header_name:
+ header_value = v
+ return header_value
+
+
+class JSONAPIHelper(object):
+ def __init__(self, http_helper, token, api, debug=False):
+ """
+ Create a new JSON API helper instance with given API key.
+ """
+ self._api = api
+ self._http_helper = http_helper
+ self._token = token
+ self._debug = debug
+
+ def _build_url(self, url, query=None):
+ return '{0}{1}{2}'.format(self._api, url, ('?' + urlencode(query)) if query else '')
+
+ def _extract_error_message(self, result):
+ if result is None:
+ return ''
+ return ' with data: {0}'.format(result)
+
+ def _validate(self, result=None, info=None, expected=None, method='GET'):
+ if info is None:
+ raise DNSAPIError('Internal error: info needs to be provided')
+ status = info['status']
+ url = info['url']
+ # Check expected status
+ error_code = ERROR_CODES.get(status, UNKNOWN_ERROR)
+ if expected is not None:
+ if status not in expected:
+ more = self._extract_error_message(result)
+ raise DNSAPIError(
+ 'Expected HTTP status {0} for {1} {2}, but got HTTP status {3} ({4}){5}'.format(
+ ', '.join(['{0}'.format(e) for e in expected]), method, url, status, error_code, more))
+ else:
+ if status < 200 or status >= 300:
+ more = self._extract_error_message(result)
+ raise DNSAPIError(
+ 'Expected successful HTTP status for {0} {1}, but got HTTP status {2} ({3}){4}'.format(
+ method, url, status, error_code, more))
+
+ def _process_json_result(self, content, info, must_have_content=True, method='GET', expected=None):
+ if isinstance(must_have_content, (list, tuple)):
+ must_have_content = info['status'] in must_have_content
+ # Check for unauthenticated
+ if info['status'] == 401:
+ message = 'Unauthorized: the authentication parameters are incorrect (HTTP status 401)'
+ try:
+ body = json.loads(content.decode('utf8'))
+ if body['message']:
+ message = '{0}: {1}'.format(message, body['message'])
+ except Exception:
+ pass
+ raise DNSAPIAuthenticationError(message)
+ if info['status'] == 403:
+ message = 'Forbidden: you do not have access to this resource (HTTP status 403)'
+ try:
+ body = json.loads(content.decode('utf8'))
+ if body['message']:
+ message = '{0}: {1}'.format(message, body['message'])
+ except Exception:
+ pass
+ raise DNSAPIAuthenticationError(message)
+ # Check Content-Type header
+ content_type = _get_header_value(info, 'content-type')
+ if content_type != 'application/json' and (content_type is None or not content_type.startswith('application/json;')):
+ if must_have_content:
+ raise DNSAPIError(
+ '{0} {1} did not yield JSON data, but HTTP status code {2} with Content-Type "{3}" and data: {4}'.format(
+ method, info['url'], info['status'], content_type, to_native(content)))
+ self._validate(result=content, info=info, expected=expected, method=method)
+ return None, info
+ # Decode content as JSON
+ try:
+ result = json.loads(content.decode('utf8'))
+ except Exception:
+ if must_have_content:
+ raise DNSAPIError(
+ '{0} {1} did not yield JSON data, but HTTP status code {2} with data: {3}'.format(
+ method, info['url'], info['status'], to_native(content)))
+ self._validate(result=content, info=info, expected=expected, method=method)
+ return None, info
+ self._validate(result=result, info=info, expected=expected, method=method)
+ return result, info
+
+ def _request(self, url, **kwargs):
+ """Execute a HTTP request and handle common things like rate limiting."""
+ number_retries = 10
+ countdown = number_retries + 1
+ while True:
+ content, info = self._http_helper.fetch_url(url, **kwargs)
+ countdown -= 1
+ if info['status'] == 429:
+ if countdown <= 0:
+ break
+ try:
+ retry_after = max(min(float(_get_header_value(info, 'retry-after')), 60), 1)
+ except (ValueError, TypeError):
+ retry_after = 10
+ time.sleep(retry_after)
+ continue
+ return content, info
+ raise DNSAPIError('Stopping after {0} failed retries with 429 Too Many Attempts'.format(number_retries))
+
+ def _create_headers(self):
+ return dict(
+ accept='application/json',
+ )
+
+ def _get(self, url, query=None, must_have_content=True, expected=None):
+ full_url = self._build_url(url, query)
+ if self._debug:
+ pass
+ # q.q('Request: GET {0}'.format(full_url))
+ headers = self._create_headers()
+ content, info = self._request(full_url, headers=headers, method='GET')
+ return self._process_json_result(content, info, must_have_content=must_have_content, method='GET', expected=expected)
+
+ def _post(self, url, data=None, query=None, must_have_content=True, expected=None):
+ full_url = self._build_url(url, query)
+ if self._debug:
+ pass
+ # q.q('Request: POST {0}'.format(full_url))
+ headers = self._create_headers()
+ encoded_data = None
+ if data is not None:
+ headers['content-type'] = 'application/json'
+ encoded_data = json.dumps(data).encode('utf-8')
+ content, info = self._request(full_url, headers=headers, method='POST', data=encoded_data)
+ return self._process_json_result(content, info, must_have_content=must_have_content, method='POST', expected=expected)
+
+ def _put(self, url, data=None, query=None, must_have_content=True, expected=None):
+ full_url = self._build_url(url, query)
+ if self._debug:
+ pass
+ # q.q('Request: PUT {0}'.format(full_url))
+ headers = self._create_headers()
+ encoded_data = None
+ if data is not None:
+ headers['content-type'] = 'application/json'
+ encoded_data = json.dumps(data).encode('utf-8')
+ content, info = self._request(full_url, headers=headers, method='PUT', data=encoded_data)
+ return self._process_json_result(content, info, must_have_content=must_have_content, method='PUT', expected=expected)
+
+ def _delete(self, url, query=None, must_have_content=True, expected=None):
+ full_url = self._build_url(url, query)
+ if self._debug:
+ pass
+ # q.q('Request: DELETE {0}'.format(full_url))
+ headers = self._create_headers()
+ content, info = self._request(full_url, headers=headers, method='DELETE')
+ return self._process_json_result(content, info, must_have_content=must_have_content, method='DELETE', expected=expected)
diff --git a/ansible_collections/community/dns/plugins/module_utils/module/_utils.py b/ansible_collections/community/dns/plugins/module_utils/module/_utils.py
new file mode 100644
index 000000000..404e86976
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/module_utils/module/_utils.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017-2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# This module_utils is PRIVATE and should only be used by this collection. Breaking changes can occur any time.
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+from ansible_collections.community.dns.plugins.module_utils.names import (
+ split_into_labels,
+ join_labels,
+ normalize_label,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.zone_record_api import (
+ DNSAPIError,
+)
+
+
+def normalize_dns_name(name):
+ if name is None:
+ return name
+ labels, dummy = split_into_labels(name)
+ return join_labels([normalize_label(label) for label in labels])
+
+
+def get_prefix(normalized_zone, provider_information, normalized_record=None, prefix=None):
+ # If normalized_record is not specified, use prefix
+ if normalized_record is None:
+ if prefix is not None:
+ prefix = provider_information.normalize_prefix(normalize_dns_name(prefix))
+ return (prefix + '.' + normalized_zone) if prefix else normalized_zone, prefix
+ # Convert record to prefix
+ if not normalized_record.endswith('.' + normalized_zone) and normalized_record != normalized_zone:
+ raise DNSAPIError('Record must be in zone')
+ if normalized_record == normalized_zone:
+ return normalized_record, None
+ else:
+ return normalized_record, normalized_record[:len(normalized_record) - len(normalized_zone) - 1]
diff --git a/ansible_collections/community/dns/plugins/module_utils/module/record.py b/ansible_collections/community/dns/plugins/module_utils/module/record.py
new file mode 100644
index 000000000..1ebbfe725
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/module_utils/module/record.py
@@ -0,0 +1,195 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017-2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# This module_utils is PRIVATE and should only be used by this collection. Breaking changes can occur any time.
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+import traceback
+
+from ansible.module_utils.common.text.converters import to_text
+
+from ansible_collections.community.dns.plugins.module_utils.argspec import (
+ ArgumentSpec,
+ ModuleOptionProvider,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.conversion.base import (
+ DNSConversionError,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.conversion.converter import (
+ RecordConverter,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.options import (
+ create_record_transformation_argspec,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.record import (
+ DNSRecord,
+ format_record_for_output,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.zone_record_api import (
+ DNSAPIError,
+ DNSAPIAuthenticationError,
+ NOT_PROVIDED,
+ filter_records,
+)
+
+from ._utils import (
+ normalize_dns_name,
+ get_prefix,
+)
+
+
+def create_module_argument_spec(provider_information):
+ return ArgumentSpec(
+ argument_spec=dict(
+ state=dict(type='str', choices=['present', 'absent'], required=True),
+ zone_name=dict(type='str', aliases=['zone']),
+ zone_id=dict(type=provider_information.get_zone_id_type()),
+ record=dict(type='str'),
+ prefix=dict(type='str'),
+ ttl=dict(type='int', default=provider_information.get_record_default_ttl()),
+ type=dict(choices=provider_information.get_supported_record_types(), required=True),
+ value=dict(type='str', required=True),
+ ),
+ required_one_of=[
+ ('zone_name', 'zone_id'),
+ ('record', 'prefix'),
+ ],
+ mutually_exclusive=[
+ ('zone_name', 'zone_id'),
+ ('record', 'prefix'),
+ ],
+ ).merge(create_record_transformation_argspec())
+
+
+def run_module(module, create_api, provider_information):
+ option_provider = ModuleOptionProvider(module)
+ record_converter = RecordConverter(provider_information, option_provider)
+ record_converter.emit_deprecations(module.deprecate)
+
+ record_in = normalize_dns_name(module.params.get('record'))
+ prefix_in = module.params.get('prefix')
+ type_in = module.params.get('type')
+ try:
+ # Create API
+ api = create_api()
+
+ # Get zone information
+ if module.params.get('zone_name') is not None:
+ zone_in = normalize_dns_name(module.params.get('zone_name'))
+ record_in, prefix = get_prefix(
+ normalized_zone=zone_in, normalized_record=record_in, prefix=prefix_in, provider_information=provider_information)
+ zone = api.get_zone_with_records_by_name(zone_in, prefix=prefix, record_type=type_in)
+ if zone is None:
+ module.fail_json(msg='Zone not found')
+ zone_id = zone.zone.id
+ records = zone.records
+ elif record_in is not None:
+ zone = api.get_zone_with_records_by_id(
+ module.params.get('zone_id'),
+ record_type=type_in,
+ prefix=provider_information.normalize_prefix(prefix_in) if prefix_in is not None else NOT_PROVIDED,
+ )
+ if zone is None:
+ module.fail_json(msg='Zone not found')
+ zone_in = normalize_dns_name(zone.zone.name)
+ record_in, prefix = get_prefix(
+ normalized_zone=zone_in, normalized_record=record_in, prefix=prefix_in, provider_information=provider_information)
+ zone_id = zone.zone.id
+ records = zone.records
+ else:
+ zone_id = module.params.get('zone_id')
+ prefix = provider_information.normalize_prefix(prefix_in)
+ records = api.get_zone_records(
+ zone_id,
+ record_type=type_in,
+ prefix=prefix,
+ )
+ if records is None:
+ module.fail_json(msg='Zone not found')
+ zone_in = None
+ record_in = None
+
+ # Find matching records
+ records = filter_records(records, prefix=prefix)
+ record_converter.process_multiple_from_api(records)
+
+ # Parse records
+ value_in = module.params.get('value')
+ value_in = record_converter.process_value_from_user(type_in, value_in)
+
+ # Compare records
+ existing_record = None
+ exact_match = False
+ ttl_in = module.params.get('ttl')
+ for record in records:
+ if record.target == value_in:
+ existing_record = record
+ exact_match = record.ttl == ttl_in
+ break
+
+ before = existing_record.clone() if existing_record else None
+ after = before
+ changed = False
+
+ if module.params.get('state') == 'present':
+ if existing_record is None:
+ # Create record
+ record = DNSRecord()
+ record.prefix = prefix
+ record.type = type_in
+ record.ttl = ttl_in
+ record.target = value_in
+ api_record = record_converter.clone_to_api(record)
+ if not module.check_mode:
+ new_api_record = api.add_record(zone_id, api_record)
+ record = record_converter.clone_from_api(new_api_record)
+ after = record
+ changed = True
+ elif not exact_match:
+ # Update record
+ record = existing_record
+ record.ttl = ttl_in
+ api_record = record_converter.clone_to_api(record)
+ if not module.check_mode:
+ new_api_record = api.update_record(zone_id, api_record)
+ record = record_converter.clone_from_api(new_api_record)
+ after = record
+ changed = True
+ else:
+ if existing_record is not None:
+ # Delete record
+ api_record = record_converter.clone_to_api(record)
+ if not module.check_mode:
+ api.delete_record(zone_id, api_record)
+ after = None
+ changed = True
+
+ # Compose result
+ result = dict(
+ changed=changed,
+ zone_id=zone_id,
+ )
+ if module._diff:
+ result['diff'] = dict(
+ before=format_record_for_output(before, record_in, prefix, record_converter=record_converter) if before else {},
+ after=format_record_for_output(after, record_in, prefix, record_converter=record_converter) if after else {},
+ )
+
+ module.exit_json(**result)
+ except DNSConversionError as e:
+ module.fail_json(msg='Error while converting DNS values: {0}'.format(e.error_message), error=e.error_message, exception=traceback.format_exc())
+ except DNSAPIAuthenticationError as e:
+ module.fail_json(msg='Cannot authenticate: {0}'.format(e), error=to_text(e), exception=traceback.format_exc())
+ except DNSAPIError as e:
+ module.fail_json(msg='Error: {0}'.format(e), error=to_text(e), exception=traceback.format_exc())
diff --git a/ansible_collections/community/dns/plugins/module_utils/module/record_info.py b/ansible_collections/community/dns/plugins/module_utils/module/record_info.py
new file mode 100644
index 000000000..f59a8afeb
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/module_utils/module/record_info.py
@@ -0,0 +1,149 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# This module_utils is PRIVATE and should only be used by this collection. Breaking changes can occur any time.
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+import traceback
+
+from ansible.module_utils.common.text.converters import to_text
+
+from ansible_collections.community.dns.plugins.module_utils.argspec import (
+ ArgumentSpec,
+ ModuleOptionProvider,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.conversion.base import (
+ DNSConversionError,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.conversion.converter import (
+ RecordConverter,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.options import (
+ create_record_transformation_argspec,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.record import (
+ format_record_for_output,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.zone_record_api import (
+ DNSAPIError,
+ DNSAPIAuthenticationError,
+ NOT_PROVIDED,
+)
+
+from ._utils import (
+ normalize_dns_name,
+ get_prefix,
+)
+
+
+def create_module_argument_spec(provider_information):
+ return ArgumentSpec(
+ argument_spec=dict(
+ what=dict(type='str', choices=['single_record', 'all_types_for_record', 'all_records'], default='single_record'),
+ zone_name=dict(type='str', aliases=['zone']),
+ zone_id=dict(type=provider_information.get_zone_id_type()),
+ record=dict(type='str'),
+ prefix=dict(type='str'),
+ type=dict(type='str', choices=provider_information.get_supported_record_types(), default=None),
+ ),
+ required_if=[
+ ('what', 'single_record', ['type']),
+ ('what', 'single_record', ['record', 'prefix'], True),
+ ('what', 'all_types_for_record', ['record', 'prefix'], True),
+ ],
+ required_one_of=[
+ ('zone_name', 'zone_id'),
+ ],
+ mutually_exclusive=[
+ ('zone_name', 'zone_id'),
+ ('record', 'prefix'),
+ ],
+ ).merge(create_record_transformation_argspec())
+
+
+def run_module(module, create_api, provider_information):
+ option_provider = ModuleOptionProvider(module)
+ record_converter = RecordConverter(provider_information, option_provider)
+ record_converter.emit_deprecations(module.deprecate)
+
+ filter_record_type = NOT_PROVIDED
+ filter_prefix = NOT_PROVIDED
+ if module.params.get('what') == 'single_record':
+ filter_record_type = module.params.get('type')
+ if module.params.get('prefix') is not None:
+ filter_prefix = provider_information.normalize_prefix(module.params.get('prefix'))
+ elif module.params.get('what') == 'all_types_for_record':
+ if module.params.get('prefix') is not None:
+ filter_prefix = provider_information.normalize_prefix(module.params.get('prefix'))
+
+ try:
+ # Create API
+ api = create_api()
+
+ # Get zone information
+ if module.params.get('zone_name') is not None:
+ zone_in = normalize_dns_name(module.params.get('zone_name'))
+ zone = api.get_zone_with_records_by_name(zone_in, prefix=filter_prefix, record_type=filter_record_type)
+ if zone is None:
+ module.fail_json(msg='Zone not found')
+ else:
+ zone = api.get_zone_with_records_by_id(module.params.get('zone_id'), prefix=filter_prefix, record_type=filter_record_type)
+ if zone is None:
+ module.fail_json(msg='Zone not found')
+ zone_in = normalize_dns_name(zone.zone.name)
+
+ # Retrieve requested information
+ records = []
+ if module.params.get('what') in ('single_record', 'all_types_for_record'):
+ check_prefix = True
+ record_in = normalize_dns_name(module.params.get('record'))
+ prefix_in = module.params.get('prefix')
+ record_in, prefix = get_prefix(
+ normalized_zone=zone_in, normalized_record=record_in, prefix=prefix_in, provider_information=provider_information)
+ else:
+ check_prefix = False
+ prefix = None
+
+ # Find matching records
+ records = []
+ for record in zone.records:
+ if check_prefix:
+ if record.prefix != prefix:
+ continue
+ records.append((
+ (record.prefix + '.' + zone_in) if record.prefix else zone_in,
+ record,
+ ))
+
+ # Convert records
+ only_records = [record for record_name, record in records]
+ record_converter.process_multiple_from_api(only_records)
+ record_converter.process_multiple_to_user(only_records)
+
+ # Format output
+ data = [
+ format_record_for_output(record, record_name, prefix=record.prefix)
+ for record_name, record in records
+ ]
+ module.exit_json(
+ changed=False,
+ records=data,
+ zone_id=zone.zone.id,
+ )
+ except DNSConversionError as e:
+ module.fail_json(msg='Error while converting DNS values: {0}'.format(e.error_message), error=e.error_message, exception=traceback.format_exc())
+ except DNSAPIAuthenticationError as e:
+ module.fail_json(msg='Cannot authenticate: {0}'.format(e), error=to_text(e), exception=traceback.format_exc())
+ except DNSAPIError as e:
+ module.fail_json(msg='Error: {0}'.format(e), error=to_text(e), exception=traceback.format_exc())
diff --git a/ansible_collections/community/dns/plugins/module_utils/module/record_set.py b/ansible_collections/community/dns/plugins/module_utils/module/record_set.py
new file mode 100644
index 000000000..d60d6f127
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/module_utils/module/record_set.py
@@ -0,0 +1,272 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017-2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# This module_utils is PRIVATE and should only be used by this collection. Breaking changes can occur any time.
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+import traceback
+
+from ansible.module_utils.common.text.converters import to_text
+
+from ansible_collections.community.dns.plugins.module_utils.argspec import (
+ ArgumentSpec,
+ ModuleOptionProvider,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.conversion.base import (
+ DNSConversionError,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.conversion.converter import (
+ RecordConverter,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.options import (
+ create_bulk_operations_argspec,
+ create_record_transformation_argspec,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.record import (
+ DNSRecord,
+ format_records_for_output,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.zone_record_api import (
+ DNSAPIError,
+ DNSAPIAuthenticationError,
+ NOT_PROVIDED,
+ filter_records,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.zone_record_helpers import (
+ bulk_apply_changes,
+)
+
+from ._utils import (
+ normalize_dns_name,
+ get_prefix,
+)
+
+
+def create_module_argument_spec(provider_information):
+ return ArgumentSpec(
+ argument_spec=dict(
+ state=dict(type='str', choices=['present', 'absent'], required=True),
+ zone_name=dict(type='str', aliases=['zone']),
+ zone_id=dict(type=provider_information.get_zone_id_type()),
+ record=dict(type='str'),
+ prefix=dict(type='str'),
+ ttl=dict(type='int', default=provider_information.get_record_default_ttl()),
+ type=dict(choices=provider_information.get_supported_record_types(), required=True),
+ value=dict(type='list', elements='str'),
+ on_existing=dict(type='str', default='replace', choices=['replace', 'keep_and_fail', 'keep_and_warn', 'keep']),
+ ),
+ required_one_of=[
+ ('zone_name', 'zone_id'),
+ ('record', 'prefix'),
+ ],
+ mutually_exclusive=[
+ ('zone_name', 'zone_id'),
+ ('record', 'prefix'),
+ ],
+ required_if=[
+ ('state', 'present', ['value']),
+ ('on_existing', 'keep_and_fail', ['value']),
+ ('on_existing', 'keep_and_warn', ['value']),
+ ('on_existing', 'keep', ['value']),
+ ],
+ ).merge(create_bulk_operations_argspec(provider_information)).merge(create_record_transformation_argspec())
+
+
+def run_module(module, create_api, provider_information):
+ option_provider = ModuleOptionProvider(module)
+ record_converter = RecordConverter(provider_information, option_provider)
+ record_converter.emit_deprecations(module.deprecate)
+
+ record_in = normalize_dns_name(module.params.get('record'))
+ prefix_in = module.params.get('prefix')
+ type_in = module.params.get('type')
+ try:
+ # Create API
+ api = create_api()
+
+ # Get zone information
+ if module.params.get('zone_name') is not None:
+ zone_in = normalize_dns_name(module.params.get('zone_name'))
+ record_in, prefix = get_prefix(
+ normalized_zone=zone_in, normalized_record=record_in, prefix=prefix_in, provider_information=provider_information)
+ zone = api.get_zone_with_records_by_name(zone_in, prefix=prefix, record_type=type_in)
+ if zone is None:
+ module.fail_json(msg='Zone not found')
+ zone_id = zone.zone.id
+ records = zone.records
+ elif record_in is not None:
+ zone = api.get_zone_with_records_by_id(
+ module.params.get('zone_id'),
+ record_type=type_in,
+ prefix=provider_information.normalize_prefix(prefix_in) if prefix_in is not None else NOT_PROVIDED,
+ )
+ if zone is None:
+ module.fail_json(msg='Zone not found')
+ zone_in = normalize_dns_name(zone.zone.name)
+ record_in, prefix = get_prefix(
+ normalized_zone=zone_in, normalized_record=record_in, prefix=prefix_in, provider_information=provider_information)
+ zone_id = zone.zone.id
+ records = zone.records
+ else:
+ zone_id = module.params.get('zone_id')
+ prefix = provider_information.normalize_prefix(prefix_in)
+ records = api.get_zone_records(
+ zone_id,
+ record_type=type_in,
+ prefix=prefix,
+ )
+ if records is None:
+ module.fail_json(msg='Zone not found')
+ zone_in = None
+ record_in = None
+
+ # Find matching records
+ records = filter_records(records, prefix=prefix)
+ record_converter.process_multiple_from_api(records)
+
+ # Parse records
+ values = []
+ value_in = module.params.get('value') or []
+ value_in = record_converter.process_values_from_user(type_in, value_in)
+ values = value_in[:]
+
+ # Compare records
+ ttl_in = module.params.get('ttl')
+ mismatch = False
+ mismatch_records = []
+ keep_records = []
+ for record in records:
+ if record.ttl != ttl_in:
+ mismatch = True
+ mismatch_records.append(record)
+ continue
+ val = record.target
+ if val in values:
+ values.remove(val)
+ keep_records.append(record)
+ else:
+ mismatch = True
+ mismatch_records.append(record)
+ continue
+ if values:
+ mismatch = True
+
+ before = [record.clone() for record in records]
+ after = keep_records[:]
+
+ # Determine what to do
+ to_create = []
+ to_delete = []
+ to_change = []
+ on_existing = module.params.get('on_existing')
+ no_mod = False
+ if module.params.get('state') == 'present':
+ if records and mismatch:
+ # Mismatch: user wants to overwrite?
+ if on_existing == 'replace':
+ to_delete.extend(mismatch_records)
+ elif on_existing == 'keep_and_fail':
+ module.fail_json(msg="Record already exists with different value. Set on_existing=replace to replace it")
+ elif on_existing == 'keep_and_warn':
+ module.warn("Record already exists with different value. Set on_existing=replace to replace it")
+ no_mod = True
+ else: # on_existing == 'keep'
+ no_mod = True
+ if no_mod:
+ after = before[:]
+ else:
+ for target in values:
+ if to_delete:
+ # If there's a record to delete, change it to new record
+ record = to_delete.pop()
+ to_change.append(record)
+ else:
+ # Otherwise create new record
+ record = DNSRecord()
+ to_create.append(record)
+ record.prefix = prefix
+ record.type = type_in
+ record.ttl = ttl_in
+ record.target = target
+ after.append(record)
+ if module.params.get('state') == 'absent':
+ if mismatch:
+ # Mismatch: user wants to overwrite?
+ if on_existing == 'replace':
+ no_mod = False
+ elif on_existing == 'keep_and_fail':
+ module.fail_json(msg="Record already exists with different value. Set on_existing=replace to remove it")
+ elif on_existing == 'keep_and_warn':
+ module.warn("Record already exists with different value. Set on_existing=replace to remove it")
+ no_mod = True
+ else: # on_existing == 'keep'
+ no_mod = True
+ if no_mod:
+ after = before[:]
+ else:
+ to_delete.extend(records)
+ after = []
+
+ # Compose result
+ result = dict(
+ changed=False,
+ zone_id=zone_id,
+ )
+
+ # Determine whether there's something to do
+ if to_create or to_delete or to_change:
+ # Actually do something
+ records_to_delete = record_converter.clone_multiple_to_api(to_delete)
+ records_to_change = record_converter.clone_multiple_to_api(to_change)
+ records_to_create = record_converter.clone_multiple_to_api(to_create)
+ result['changed'] = True
+ if not module.check_mode:
+ dummy, errors, success = bulk_apply_changes(
+ api,
+ zone_id=zone_id,
+ records_to_delete=records_to_delete,
+ records_to_change=records_to_change,
+ records_to_create=records_to_create,
+ provider_information=provider_information,
+ options=option_provider,
+ )
+ if errors:
+ if len(errors) == 1:
+ raise errors[0]
+ module.fail_json(
+ msg='Errors: {0}'.format('; '.join([str(e) for e in errors])),
+ errors=[str(e) for e in errors],
+ )
+
+ # Include diff information
+ if module._diff:
+ result['diff'] = dict(
+ before=(
+ format_records_for_output(sorted(before, key=lambda record: record.target), record_in, prefix, record_converter=record_converter)
+ if before else dict()
+ ),
+ after=(
+ format_records_for_output(sorted(after, key=lambda record: record.target), record_in, prefix, record_converter=record_converter)
+ if after else dict()
+ ),
+ )
+
+ module.exit_json(**result)
+ except DNSConversionError as e:
+ module.fail_json(msg='Error while converting DNS values: {0}'.format(e.error_message), error=e.error_message, exception=traceback.format_exc())
+ except DNSAPIAuthenticationError as e:
+ module.fail_json(msg='Cannot authenticate: {0}'.format(e), error=to_text(e), exception=traceback.format_exc())
+ except DNSAPIError as e:
+ module.fail_json(msg='Error: {0}'.format(e), error=to_text(e), exception=traceback.format_exc())
diff --git a/ansible_collections/community/dns/plugins/module_utils/module/record_set_info.py b/ansible_collections/community/dns/plugins/module_utils/module/record_set_info.py
new file mode 100644
index 000000000..4c4961bc9
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/module_utils/module/record_set_info.py
@@ -0,0 +1,175 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017-2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# This module_utils is PRIVATE and should only be used by this collection. Breaking changes can occur any time.
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+import traceback
+
+from ansible.module_utils.common.text.converters import to_text
+
+from ansible_collections.community.dns.plugins.module_utils.argspec import (
+ ArgumentSpec,
+ ModuleOptionProvider,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.conversion.base import (
+ DNSConversionError,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.conversion.converter import (
+ RecordConverter,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.options import (
+ create_record_transformation_argspec,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.record import (
+ format_records_for_output,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.zone_record_api import (
+ DNSAPIError,
+ DNSAPIAuthenticationError,
+ NOT_PROVIDED,
+)
+
+from ._utils import (
+ normalize_dns_name,
+ get_prefix,
+)
+
+
+def create_module_argument_spec(provider_information):
+ return ArgumentSpec(
+ argument_spec=dict(
+ what=dict(type='str', choices=['single_record', 'all_types_for_record', 'all_records'], default='single_record'),
+ zone_name=dict(type='str', aliases=['zone']),
+ zone_id=dict(type=provider_information.get_zone_id_type()),
+ record=dict(type='str'),
+ prefix=dict(type='str'),
+ type=dict(type='str', choices=provider_information.get_supported_record_types(), default=None),
+ ),
+ required_if=[
+ ('what', 'single_record', ['type']),
+ ('what', 'single_record', ['record', 'prefix'], True),
+ ('what', 'all_types_for_record', ['record', 'prefix'], True),
+ ],
+ required_one_of=[
+ ('zone_name', 'zone_id'),
+ ],
+ mutually_exclusive=[
+ ('zone_name', 'zone_id'),
+ ('record', 'prefix'),
+ ],
+ ).merge(create_record_transformation_argspec())
+
+
+def run_module(module, create_api, provider_information):
+ option_provider = ModuleOptionProvider(module)
+ record_converter = RecordConverter(provider_information, option_provider)
+ record_converter.emit_deprecations(module.deprecate)
+
+ filter_record_type = NOT_PROVIDED
+ filter_prefix = NOT_PROVIDED
+ if module.params.get('what') == 'single_record':
+ filter_record_type = module.params.get('type')
+ if module.params.get('prefix') is not None:
+ filter_prefix = provider_information.normalize_prefix(module.params.get('prefix'))
+ elif module.params.get('what') == 'all_types_for_record':
+ if module.params.get('prefix') is not None:
+ filter_prefix = provider_information.normalize_prefix(module.params.get('prefix'))
+
+ try:
+ # Create API
+ api = create_api()
+
+ # Get zone information
+ if module.params.get('zone_name') is not None:
+ zone_in = normalize_dns_name(module.params.get('zone_name'))
+ zone = api.get_zone_with_records_by_name(zone_in, prefix=filter_prefix, record_type=filter_record_type)
+ if zone is None:
+ module.fail_json(msg='Zone not found')
+ else:
+ zone = api.get_zone_with_records_by_id(module.params.get('zone_id'), prefix=filter_prefix, record_type=filter_record_type)
+ if zone is None:
+ module.fail_json(msg='Zone not found')
+ zone_in = normalize_dns_name(zone.zone.name)
+
+ # Retrieve requested information
+ if module.params.get('what') == 'single_record':
+ # Extract prefix
+ record_in = normalize_dns_name(module.params.get('record'))
+ prefix_in = module.params.get('prefix')
+ record_in, prefix = get_prefix(
+ normalized_zone=zone_in, normalized_record=record_in, prefix=prefix_in, provider_information=provider_information)
+
+ # Find matching records
+ records = []
+ for record in zone.records:
+ if record.prefix == prefix:
+ records.append(record)
+
+ # Convert records
+ record_converter.process_multiple_from_api(records)
+ record_converter.process_multiple_to_user(records)
+
+ # Format output
+ data = format_records_for_output(records, record_in, prefix) if records else {}
+ module.exit_json(
+ changed=False,
+ set=data,
+ zone_id=zone.zone.id,
+ )
+ else:
+ # Extract prefix if necessary
+ if module.params.get('what') == 'all_types_for_record':
+ check_prefix = True
+ record_in = normalize_dns_name(module.params.get('record'))
+ prefix_in = module.params.get('prefix')
+ record_in, prefix = get_prefix(
+ normalized_zone=zone_in, normalized_record=record_in, prefix=prefix_in, provider_information=provider_information)
+ else:
+ check_prefix = False
+ prefix = None
+
+ # Find matching records
+ records = {}
+ for record in zone.records:
+ if check_prefix:
+ if record.prefix != prefix:
+ continue
+ key = ((record.prefix + '.' + zone_in) if record.prefix else zone_in, record.type)
+ record_list = records.get(key)
+ if record_list is None:
+ record_list = records[key] = []
+ record_list.append(record)
+
+ # Convert records
+ for record_list in records.values():
+ record_converter.process_multiple_from_api(record_list)
+ record_converter.process_multiple_to_user(record_list)
+
+ # Format output
+ data = [
+ format_records_for_output(record_list, record_name, record_list[0].prefix)
+ for (record_name, dummy), record_list in sorted(records.items())
+ ]
+ module.exit_json(
+ changed=False,
+ sets=data,
+ zone_id=zone.zone.id,
+ )
+ except DNSConversionError as e:
+ module.fail_json(msg='Error while converting DNS values: {0}'.format(e.error_message), error=e.error_message, exception=traceback.format_exc())
+ except DNSAPIAuthenticationError as e:
+ module.fail_json(msg='Cannot authenticate: {0}'.format(e), error=to_text(e), exception=traceback.format_exc())
+ except DNSAPIError as e:
+ module.fail_json(msg='Error: {0}'.format(e), error=to_text(e), exception=traceback.format_exc())
diff --git a/ansible_collections/community/dns/plugins/module_utils/module/record_sets.py b/ansible_collections/community/dns/plugins/module_utils/module/record_sets.py
new file mode 100644
index 000000000..bc93e32ee
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/module_utils/module/record_sets.py
@@ -0,0 +1,264 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017-2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# This module_utils is PRIVATE and should only be used by this collection. Breaking changes can occur any time.
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+import traceback
+
+from ansible.module_utils.common.text.converters import to_text
+
+from ansible_collections.community.dns.plugins.module_utils.argspec import (
+ ArgumentSpec,
+ ModuleOptionProvider,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.conversion.base import (
+ DNSConversionError,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.conversion.converter import (
+ RecordConverter,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.options import (
+ create_bulk_operations_argspec,
+ create_record_transformation_argspec,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.record import (
+ DNSRecord,
+ format_records_for_output,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.zone_record_api import (
+ DNSAPIError,
+ DNSAPIAuthenticationError,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.zone_record_helpers import (
+ bulk_apply_changes,
+)
+
+from ._utils import (
+ normalize_dns_name,
+ get_prefix,
+)
+
+
+def create_module_argument_spec(provider_information):
+ return ArgumentSpec(
+ argument_spec=dict(
+ zone_name=dict(type='str', aliases=['zone']),
+ zone_id=dict(type=provider_information.get_zone_id_type()),
+ prune=dict(type='bool', default=False),
+ record_sets=dict(
+ type='list',
+ elements='dict',
+ required=True,
+ aliases=['records'],
+ options=dict(
+ record=dict(type='str'),
+ prefix=dict(type='str'),
+ ttl=dict(type='int', default=provider_information.get_record_default_ttl()),
+ type=dict(choices=provider_information.get_supported_record_types(), required=True),
+ value=dict(type='list', elements='str'),
+ ignore=dict(type='bool', default=False),
+ ),
+ required_if=[('ignore', False, ['value'])],
+ required_one_of=[('record', 'prefix')],
+ mutually_exclusive=[('record', 'prefix')],
+ ),
+ ),
+ required_one_of=[
+ ('zone_name', 'zone_id'),
+ ],
+ mutually_exclusive=[
+ ('zone_name', 'zone_id'),
+ ],
+ ).merge(create_bulk_operations_argspec(provider_information)).merge(create_record_transformation_argspec())
+
+
+def run_module(module, create_api, provider_information):
+ option_provider = ModuleOptionProvider(module)
+ record_converter = RecordConverter(provider_information, option_provider)
+ record_converter.emit_deprecations(module.deprecate)
+
+ try:
+ # Create API
+ api = create_api()
+
+ # Get zone information
+ if module.params['zone_name'] is not None:
+ zone_in = normalize_dns_name(module.params['zone_name'])
+ zone = api.get_zone_with_records_by_name(zone_in)
+ if zone is None:
+ module.fail_json(msg='Zone not found')
+ zone_id = zone.zone.id
+ zone_records = zone.records
+ else:
+ zone = api.get_zone_with_records_by_id(module.params['zone_id'])
+ if zone is None:
+ module.fail_json(msg='Zone not found')
+ zone_in = normalize_dns_name(zone.zone.name)
+ zone_id = zone.zone.id
+ zone_records = zone.records
+
+ record_converter.process_multiple_from_api(zone_records)
+
+ # Process parameters
+ prune = module.params['prune']
+ record_sets = module.params['record_sets']
+ record_sets_dict = dict()
+ for index, record_set in enumerate(record_sets):
+ record_set = record_set.copy()
+ record_name = record_set['record']
+ prefix = record_set['prefix']
+ record_name, prefix = get_prefix(
+ normalized_zone=zone_in, normalized_record=record_name, prefix=prefix, provider_information=provider_information)
+ record_set['record'] = record_name
+ record_set['prefix'] = prefix
+ if record_set['value']:
+ record_set['value'] = record_converter.process_values_from_user(record_set['type'], record_set['value'])
+ key = (prefix, record_set['type'])
+ if key in record_sets_dict:
+ module.fail_json(msg='Found multiple sets for record {record} and type {type}: index #{i1} and #{i2}'.format(
+ record=record_name,
+ type=record_set['type'],
+ i1=record_sets_dict[key][0],
+ i2=index,
+ ))
+ record_sets_dict[key] = (index, record_set)
+
+ # Group existing record sets
+ existing_record_sets = dict()
+ for record in zone_records:
+ key = (record.prefix, record.type)
+ if key not in existing_record_sets:
+ existing_record_sets[key] = []
+ existing_record_sets[key].append(record)
+
+ # Data required for diff
+ old_record_sets = dict([(k, [r.clone() for r in v]) for k, v in existing_record_sets.items()])
+ new_record_sets = dict([(k, list(v)) for k, v in existing_record_sets.items()])
+
+ # Create action lists
+ to_create = []
+ to_delete = []
+ to_change = []
+ for (prefix, record_type), (dummy, record_set) in record_sets_dict.items():
+ key = (prefix, record_type)
+ if key not in new_record_sets:
+ new_record_sets[key] = []
+ existing_recs = existing_record_sets.get(key, [])
+ existing_record_sets[key] = []
+ new_recs = new_record_sets[key]
+
+ if record_set['ignore']:
+ continue
+
+ mismatch_recs = []
+ keep_record_sets = []
+ values = list(record_set['value'])
+ for record in existing_recs:
+ if record.ttl != record_set['ttl']:
+ mismatch_recs.append(record)
+ new_recs.remove(record)
+ continue
+ if record.target in values:
+ values.remove(record.target)
+ keep_record_sets.append(record)
+ else:
+ mismatch_recs.append(record)
+ new_recs.remove(record)
+
+ for target in values:
+ if mismatch_recs:
+ record = mismatch_recs.pop()
+ to_change.append(record)
+ else:
+ # Otherwise create new record
+ record = DNSRecord()
+ to_create.append(record)
+ record.prefix = prefix
+ record.type = record_type
+ record.ttl = record_set['ttl']
+ record.target = target
+ new_recs.append(record)
+
+ to_delete.extend(mismatch_recs)
+
+ # If pruning, remove superfluous record sets
+ if prune:
+ for key, record_set in existing_record_sets.items():
+ to_delete.extend(record_set)
+ for record in record_set:
+ new_record_sets[key].remove(record)
+
+ # Compose result
+ result = dict(
+ changed=False,
+ zone_id=zone_id,
+ )
+
+ # Apply changes
+ if to_create or to_delete or to_change:
+ records_to_delete = record_converter.clone_multiple_to_api(to_delete)
+ records_to_change = record_converter.clone_multiple_to_api(to_change)
+ records_to_create = record_converter.clone_multiple_to_api(to_create)
+ result['changed'] = True
+ if not module.check_mode:
+ dummy, errors, success = bulk_apply_changes(
+ api,
+ zone_id=zone_id,
+ records_to_delete=records_to_delete,
+ records_to_change=records_to_change,
+ records_to_create=records_to_create,
+ provider_information=provider_information,
+ options=option_provider,
+ )
+ if errors:
+ if len(errors) == 1:
+ raise errors[0]
+ module.fail_json(
+ msg='Errors: {0}'.format('; '.join([str(e) for e in errors])),
+ errors=[str(e) for e in errors],
+ )
+
+ # Include diff information
+ if module._diff:
+ def sort_items(dictionary):
+ items = [
+ (zone_in if prefix is None else (prefix + '.' + zone_in), type, prefix, record_set)
+ for (prefix, type), record_set in dictionary.items() if len(record_set) > 0
+ ]
+ return sorted(items)
+
+ result['diff'] = dict(
+ before=dict(
+ record_sets=[
+ format_records_for_output(record_set, record_name, prefix, record_converter=record_converter)
+ for record_name, type, prefix, record_set in sort_items(old_record_sets)
+ ],
+ ),
+ after=dict(
+ record_sets=[
+ format_records_for_output(record_set, record_name, prefix, record_converter=record_converter)
+ for record_name, type, prefix, record_set in sort_items(new_record_sets)
+ ],
+ ),
+ )
+
+ module.exit_json(**result)
+ except DNSConversionError as e:
+ module.fail_json(msg='Error while converting DNS values: {0}'.format(e.error_message), error=e.error_message, exception=traceback.format_exc())
+ except DNSAPIAuthenticationError as e:
+ module.fail_json(msg='Cannot authenticate: {0}'.format(e), error=to_text(e), exception=traceback.format_exc())
+ except DNSAPIError as e:
+ module.fail_json(msg='Error: {0}'.format(e), error=to_text(e), exception=traceback.format_exc())
diff --git a/ansible_collections/community/dns/plugins/module_utils/module/zone_info.py b/ansible_collections/community/dns/plugins/module_utils/module/zone_info.py
new file mode 100644
index 000000000..00ea2af8a
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/module_utils/module/zone_info.py
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# This module_utils is PRIVATE and should only be used by this collection. Breaking changes can occur any time.
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+import traceback
+
+from ansible.module_utils.common.text.converters import to_text
+
+from ansible_collections.community.dns.plugins.module_utils.argspec import (
+ ArgumentSpec,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.zone_record_api import (
+ DNSAPIError,
+ DNSAPIAuthenticationError,
+)
+
+from ._utils import (
+ normalize_dns_name,
+)
+
+
+def create_module_argument_spec(provider_information):
+ return ArgumentSpec(
+ argument_spec=dict(
+ zone_name=dict(type='str', aliases=['zone']),
+ zone_id=dict(type=provider_information.get_zone_id_type()),
+ ),
+ required_one_of=[
+ ('zone_name', 'zone_id'),
+ ],
+ mutually_exclusive=[
+ ('zone_name', 'zone_id'),
+ ],
+ )
+
+
+def run_module(module, create_api, provider_information):
+ try:
+ # Create API
+ api = create_api()
+
+ # Get zone information
+ if module.params.get('zone_name') is not None:
+ zone_id = normalize_dns_name(module.params.get('zone_name'))
+ zone = api.get_zone_by_name(zone_id)
+ if zone is None:
+ module.fail_json(msg='Zone not found')
+ else:
+ zone = api.get_zone_by_id(module.params.get('zone_id'))
+ if zone is None:
+ module.fail_json(msg='Zone not found')
+
+ module.exit_json(
+ changed=False,
+ zone_name=zone.name,
+ zone_id=zone.id,
+ zone_info=zone.info,
+ )
+ except DNSAPIAuthenticationError as e:
+ module.fail_json(msg='Cannot authenticate: {0}'.format(e), error=to_text(e), exception=traceback.format_exc())
+ except DNSAPIError as e:
+ module.fail_json(msg='Error: {0}'.format(e), error=to_text(e), exception=traceback.format_exc())
diff --git a/ansible_collections/community/dns/plugins/module_utils/names.py b/ansible_collections/community/dns/plugins/module_utils/names.py
new file mode 100644
index 000000000..1c65630f6
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/module_utils/names.py
@@ -0,0 +1,73 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import re
+
+from ansible.module_utils.common.text.converters import to_text
+
+
+_ASCII_PRINTABLE_MATCHER = re.compile(r'^[\x20-\x7e]*$')
+
+
+def is_ascii_label(domain):
+ '''
+ Check whether domain name has only ASCII labels.
+ '''
+ return _ASCII_PRINTABLE_MATCHER.match(domain) is not None
+
+
+class InvalidDomainName(Exception):
+ '''
+ The provided domain name is not valid.
+ '''
+ pass
+
+
+def split_into_labels(domain):
+ '''
+ Split domain name to a list of labels. Start with the top-most label.
+
+ Returns a list of labels and a tail, which is either ``''`` or ``'.'``.
+ Raises ``InvalidDomainName`` if the domain name is not valid.
+ '''
+ result = []
+ index = len(domain)
+ tail = ''
+ if domain.endswith('.'):
+ index -= 1
+ tail = '.'
+ if index > 0:
+ while index >= 0:
+ next_index = domain.rfind('.', 0, index)
+ label = domain[next_index + 1:index]
+ if label == '' or label[0] == '-' or label[-1] == '-' or len(label) > 63:
+ raise InvalidDomainName(domain)
+ result.append(label)
+ index = next_index
+ return result, tail
+
+
+def join_labels(labels, tail=''):
+ '''
+ Combines the result of split_into_labels() back into a domain name.
+ '''
+ return '.'.join(reversed(labels)) + tail
+
+
+def normalize_label(label):
+ '''
+ Normalize a domain label. Returns a lower-case ASCII label.
+
+ If a ulabel is provided, it is converted to an alabel.
+ '''
+ if not is_ascii_label(label):
+ # Convert ulabel to alabel
+ label = to_text(b'xn--' + to_text(label).encode('punycode'))
+ # Always convert to lower-case
+ return label.lower()
diff --git a/ansible_collections/community/dns/plugins/module_utils/options.py b/ansible_collections/community/dns/plugins/module_utils/options.py
new file mode 100644
index 000000000..3906b4b6c
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/module_utils/options.py
@@ -0,0 +1,37 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+from ansible_collections.community.dns.plugins.module_utils.argspec import (
+ ArgumentSpec,
+)
+
+
+def create_bulk_operations_argspec(provider_information):
+ """
+ If the provider supports bulk operations, return an ArgumentSpec object with appropriate
+ options. Otherwise return an empty one.
+ """
+ if not provider_information.supports_bulk_actions():
+ return ArgumentSpec()
+
+ return ArgumentSpec(
+ argument_spec=dict(
+ bulk_operation_threshold=dict(type='int', default=2),
+ ),
+ )
+
+
+def create_record_transformation_argspec():
+ return ArgumentSpec(
+ argument_spec=dict(
+ txt_transformation=dict(type='str', default='unquoted', choices=['api', 'quoted', 'unquoted']),
+ txt_character_encoding=dict(type='str', choices=['decimal', 'octal']),
+ ),
+ )
diff --git a/ansible_collections/community/dns/plugins/module_utils/provider.py b/ansible_collections/community/dns/plugins/module_utils/provider.py
new file mode 100644
index 000000000..56282d9c9
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/module_utils/provider.py
@@ -0,0 +1,109 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017-2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+import abc
+
+from ansible.module_utils import six
+
+from ansible.module_utils.common.validation import (
+ check_type_str,
+ check_type_list,
+ check_type_dict,
+ check_type_bool,
+ check_type_int,
+ check_type_float,
+)
+
+
+def ensure_type(value, type_name):
+ if type_name == 'str':
+ return check_type_str(value)
+ if type_name == 'list':
+ return check_type_list(value)
+ if type_name == 'dict':
+ return check_type_dict(value)
+ if type_name == 'bool':
+ return check_type_bool(value)
+ if type_name == 'int':
+ return check_type_int(value)
+ if type_name == 'float':
+ return check_type_float(value)
+ return value
+
+
+@six.add_metaclass(abc.ABCMeta)
+class ProviderInformation(object):
+ @abc.abstractmethod
+ def get_zone_id_type(self):
+ """
+ Return the (short) type for zone IDs, like ``'int'`` or ``'str'``.
+ """
+
+ @abc.abstractmethod
+ def get_record_id_type(self):
+ """
+ Return the (short) type for record IDs, like ``'int'`` or ``'str'``.
+ """
+
+ @abc.abstractmethod
+ def get_record_default_ttl(self):
+ """
+ Return the default TTL for records, like 300, 3600 or None.
+ None means that some other TTL (usually from the zone) will be used.
+ """
+
+ @abc.abstractmethod
+ def get_supported_record_types(self):
+ """
+ Return a list of supported record types.
+ """
+
+ def normalize_prefix(self, prefix):
+ """
+ Given a prefix (string or None), return its normalized form.
+
+ The result should always be None for the trivial prefix, and a non-zero length DNS name
+ for a non-trivial prefix.
+
+ If a provider supports other identifiers for the trivial prefix, such as '@', this
+ function needs to convert them to None as well.
+ """
+ return prefix or None
+
+ def supports_bulk_actions(self):
+ """
+ Return whether the API supports some kind of bulk actions.
+ """
+ return False
+
+ @abc.abstractmethod
+ def txt_record_handling(self):
+ """
+ Return how the API handles TXT records.
+
+ Returns one of the following strings:
+ * 'decoded' - the API works with unencoded values
+ * 'encoded' - the API works with encoded values
+ * 'encoded-no-char-encoding' - the API works with encoded values, but without character encoding
+ """
+
+ def txt_character_encoding(self):
+ """
+ Return how the API handles escape sequences in TXT records.
+
+ Returns one of the following strings:
+ * 'octal' - the API works with octal escape sequences
+ * 'decimal' - the API works with decimal escape sequences
+
+ This return value is only used if txt_record_handling returns 'encoded'.
+
+ WARNING: the default return value will change to 'decimal' for community.dns 3.0.0!
+ """
+ return 'octal'
diff --git a/ansible_collections/community/dns/plugins/module_utils/record.py b/ansible_collections/community/dns/plugins/module_utils/record.py
new file mode 100644
index 000000000..bb1f9ce77
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/module_utils/record.py
@@ -0,0 +1,100 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017-2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+def format_ttl(ttl):
+ if ttl is None:
+ return 'default'
+ sec = ttl % 60
+ ttl //= 60
+ min = ttl % 60
+ ttl //= 60
+ h = ttl
+ result = []
+ if h:
+ result.append('{0}h'.format(h))
+ if min:
+ result.append('{0}m'.format(min))
+ if sec:
+ result.append('{0}s'.format(sec))
+ return ' '.join(result)
+
+
+class DNSRecord(object):
+ def __init__(self):
+ self.id = None
+ self.type = None
+ self.prefix = None
+ self.target = None
+ self.ttl = 86400 # 24 * 60 * 60
+ self.extra = {}
+
+ def clone(self):
+ result = DNSRecord()
+ result.id = self.id
+ result.type = self.type
+ result.prefix = self.prefix
+ result.target = self.target
+ result.ttl = self.ttl
+ result.extra = dict(self.extra)
+ return result
+
+ def __str__(self):
+ data = []
+ if self.id:
+ data.append('id: {0}'.format(self.id))
+ data.append('type: {0}'.format(self.type))
+ if self.prefix:
+ data.append('prefix: "{0}"'.format(self.prefix))
+ else:
+ data.append('prefix: (none)')
+ data.append('target: "{0}"'.format(self.target))
+ data.append('ttl: {0}'.format(format_ttl(self.ttl)))
+ if self.extra:
+ data.append('extra: {0}'.format(self.extra))
+ return 'DNSRecord(' + ', '.join(data) + ')'
+
+ def __repr__(self):
+ return self.__str__()
+
+
+def sorted_ttls(ttls):
+ return sorted(ttls, key=lambda ttl: 0 if ttl is None else ttl)
+
+
+def format_records_for_output(records, record_name, prefix=None, record_converter=None):
+ ttls = sorted_ttls(set([record.ttl for record in records]))
+ entry = {
+ 'prefix': prefix or '',
+ 'type': min([record.type for record in records]) if records else None,
+ 'ttl': ttls[0] if len(ttls) > 0 else None,
+ 'value': [record.target for record in records],
+ }
+ if record_converter:
+ entry['value'] = record_converter.process_values_to_user(entry['type'], entry['value'])
+ if record_name is not None:
+ entry['record'] = record_name
+ if len(ttls) > 1:
+ entry['ttls'] = ttls
+ return entry
+
+
+def format_record_for_output(record, record_name, prefix=None, record_converter=None):
+ entry = {
+ 'prefix': prefix or '',
+ 'type': record.type,
+ 'ttl': record.ttl,
+ 'value': record.target,
+ 'extra': record.extra,
+ }
+ if record_converter:
+ entry['value'] = record_converter.process_value_to_user(entry['type'], entry['value'])
+ if record_name is not None:
+ entry['record'] = record_name
+ return entry
diff --git a/ansible_collections/community/dns/plugins/module_utils/resolver.py b/ansible_collections/community/dns/plugins/module_utils/resolver.py
new file mode 100644
index 000000000..98f1034e0
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/module_utils/resolver.py
@@ -0,0 +1,213 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2021, Felix Fontein <felix@fontein.de>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import traceback
+
+from ansible.module_utils.basic import missing_required_lib
+from ansible.module_utils.common.text.converters import to_text
+
+try:
+ import dns
+ import dns.exception
+ import dns.name
+ import dns.message
+ import dns.query
+ import dns.rcode
+ import dns.rdatatype
+ import dns.resolver
+except ImportError:
+ DNSPYTHON_IMPORTERROR = traceback.format_exc()
+else:
+ DNSPYTHON_IMPORTERROR = None
+
+
+class ResolverError(Exception):
+ pass
+
+
+class ResolveDirectlyFromNameServers(object):
+ def __init__(self, timeout=10, timeout_retries=3, always_ask_default_resolver=True):
+ self.cache = {}
+ self.timeout = timeout
+ self.timeout_retries = timeout_retries
+ self.default_resolver = dns.resolver.get_default_resolver()
+ self.default_nameservers = self.default_resolver.nameservers
+ self.always_ask_default_resolver = always_ask_default_resolver
+
+ def _handle_reponse_errors(self, target, response, nameserver=None, query=None):
+ rcode = response.rcode()
+ if rcode == dns.rcode.NOERROR:
+ return True
+ if rcode == dns.rcode.NXDOMAIN:
+ raise dns.resolver.NXDOMAIN(qnames=[target], responses={target: response})
+ msg = 'Error %s' % dns.rcode.to_text(rcode)
+ if nameserver:
+ msg = '%s while querying %s' % (msg, nameserver)
+ if query:
+ msg = '%s with query %s' % (msg, query)
+ raise ResolverError(msg)
+
+ def _handle_timeout(self, function, *args, **kwargs):
+ retry = 0
+ while True:
+ try:
+ return function(*args, **kwargs)
+ except dns.exception.Timeout as exc:
+ if retry >= self.timeout_retries:
+ raise exc
+ retry += 1
+
+ def _lookup_ns_names(self, target, nameservers=None, nameserver_ips=None):
+ if self.always_ask_default_resolver:
+ nameservers = None
+ nameserver_ips = self.default_nameservers
+ if nameservers is None and nameserver_ips is None:
+ nameserver_ips = self.default_nameservers
+ if not nameserver_ips and nameservers:
+ nameserver_ips = self._lookup_address(nameservers[0])
+ if not nameserver_ips:
+ raise ResolverError('Have neither nameservers nor nameserver IPs')
+
+ query = dns.message.make_query(target, dns.rdatatype.NS)
+ response = self._handle_timeout(dns.query.udp, query, nameserver_ips[0], timeout=self.timeout)
+ self._handle_reponse_errors(target, response, nameserver=nameserver_ips[0], query='get NS for "%s"' % target)
+
+ cname = None
+ for rrset in response.answer:
+ if rrset.rdtype == dns.rdatatype.CNAME:
+ cname = dns.name.from_text(to_text(rrset[0]))
+
+ new_nameservers = []
+ rrsets = list(response.authority)
+ rrsets.extend(response.answer)
+ for rrset in rrsets:
+ if rrset.rdtype == dns.rdatatype.SOA:
+ # We keep the current nameservers
+ return None, cname
+ if rrset.rdtype == dns.rdatatype.NS:
+ new_nameservers.extend(str(ns_record.target) for ns_record in rrset)
+ return sorted(set(new_nameservers)) if new_nameservers else None, cname
+
+ def _lookup_address_impl(self, target, rdtype):
+ try:
+ try:
+ answer = self._handle_timeout(self.default_resolver.resolve, target, rdtype=rdtype, lifetime=self.timeout)
+ except AttributeError:
+ # For dnspython < 2.0.0
+ self.default_resolver.search = False
+ try:
+ answer = self._handle_timeout(self.default_resolver.query, target, rdtype=rdtype, lifetime=self.timeout)
+ except TypeError:
+ # For dnspython < 1.6.0
+ self.default_resolver.lifetime = self.timeout
+ answer = self._handle_timeout(self.default_resolver.query, target, rdtype=rdtype)
+ return [str(res) for res in answer.rrset]
+ except dns.resolver.NoAnswer:
+ return []
+
+ def _lookup_address(self, target):
+ result = self.cache.get((target, 'addr'))
+ if not result:
+ result = self._lookup_address_impl(target, dns.rdatatype.A)
+ result.extend(self._lookup_address_impl(target, dns.rdatatype.AAAA))
+ self.cache[(target, 'addr')] = result
+ return result
+
+ def _do_lookup_ns(self, target):
+ nameserver_ips = self.default_nameservers
+ nameservers = None
+ for i in range(2, len(target.labels) + 1):
+ target_part = target.split(i)[1]
+ _nameservers = self.cache.get((str(target_part), 'ns'))
+ if _nameservers is None:
+ nameserver_names, cname = self._lookup_ns_names(target_part, nameservers=nameservers, nameserver_ips=nameserver_ips)
+ if nameserver_names is not None:
+ nameservers = nameserver_names
+
+ self.cache[(str(target_part), 'ns')] = nameservers
+ self.cache[(str(target_part), 'cname')] = cname
+ else:
+ nameservers = _nameservers
+ nameserver_ips = None
+
+ return nameservers
+
+ def _lookup_ns(self, target):
+ result = self.cache.get((str(target), 'ns'))
+ if not result:
+ result = self._do_lookup_ns(target)
+ self.cache[(str(target), 'ns')] = result
+ return result
+
+ def _get_resolver(self, dnsname, nameservers):
+ cache_index = ('|'.join([str(dnsname)] + sorted(nameservers)), 'resolver')
+ resolver = self.cache.get(cache_index)
+ if resolver is None:
+ resolver = dns.resolver.Resolver(configure=False)
+ resolver.timeout = self.timeout
+ nameserver_ips = set()
+ for nameserver in nameservers:
+ nameserver_ips.update(self._lookup_address(nameserver))
+ resolver.nameservers = sorted(nameserver_ips)
+ self.cache[cache_index] = resolver
+ return resolver
+
+ def resolve_nameservers(self, target, resolve_addresses=False):
+ nameservers = self._lookup_ns(dns.name.from_unicode(to_text(target)))
+ if resolve_addresses:
+ nameserver_ips = set()
+ for nameserver in nameservers:
+ nameserver_ips.update(self._lookup_address(nameserver))
+ nameservers = list(nameserver_ips)
+ return sorted(nameservers)
+
+ def resolve(self, target, nxdomain_is_empty=True, **kwargs):
+ dnsname = dns.name.from_unicode(to_text(target))
+ loop_catcher = set()
+ while True:
+ try:
+ nameservers = self._lookup_ns(dnsname)
+ except dns.resolver.NXDOMAIN:
+ if nxdomain_is_empty:
+ return {}
+ raise
+ cname = self.cache.get((str(dnsname), 'cname'))
+ if cname is None:
+ break
+ dnsname = cname
+ if dnsname in loop_catcher:
+ raise ResolverError('Found CNAME loop starting at {0}'.format(target))
+ loop_catcher.add(dnsname)
+
+ results = {}
+ for nameserver in nameservers:
+ results[nameserver] = None
+ resolver = self._get_resolver(dnsname, [nameserver])
+ try:
+ try:
+ response = self._handle_timeout(resolver.resolve, dnsname, lifetime=self.timeout, **kwargs)
+ except AttributeError:
+ # For dnspython < 2.0.0
+ resolver.search = False
+ try:
+ response = self._handle_timeout(resolver.query, dnsname, lifetime=self.timeout, **kwargs)
+ except TypeError:
+ # For dnspython < 1.6.0
+ resolver.lifetime = self.timeout
+ response = self._handle_timeout(resolver.query, dnsname, **kwargs)
+ if response.rrset:
+ results[nameserver] = response.rrset
+ except dns.resolver.NoAnswer:
+ pass
+ return results
+
+
+def assert_requirements_present(module):
+ if DNSPYTHON_IMPORTERROR is not None:
+ module.fail_json(msg=missing_required_lib('dnspython'), exception=DNSPYTHON_IMPORTERROR)
diff --git a/ansible_collections/community/dns/plugins/module_utils/wsdl.py b/ansible_collections/community/dns/plugins/module_utils/wsdl.py
new file mode 100644
index 000000000..713803e5b
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/module_utils/wsdl.py
@@ -0,0 +1,308 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017-2020 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+from ansible.module_utils.common.text.converters import to_native
+from ansible.module_utils.six import string_types
+
+try:
+ import lxml.etree
+ HAS_LXML_ETREE = True
+except ImportError:
+ HAS_LXML_ETREE = False
+
+from ansible_collections.community.dns.plugins.module_utils.http import (
+ NetworkError,
+)
+
+
+class WSDLException(Exception):
+ pass
+
+
+class WSDLNetworkError(WSDLException):
+ pass
+
+
+class WSDLError(WSDLException):
+ def __init__(self, origin, error_code, message):
+ super(WSDLError, self).__init__('{0} ({1}): {2}'.format(origin, error_code, message))
+ self.error_origin = origin
+ self.error_code = error_code
+ self.error_message = message
+
+
+class WSDLCodingException(WSDLException):
+ pass
+
+
+def _split_text_namespace(node, text):
+ i = text.find(':')
+ if i < 0:
+ return text, None
+ ns = node.nsmap.get(text[:i])
+ text = text[i + 1:]
+ return text, ns
+
+
+_NAMESPACE_ENVELOPE = 'http://schemas.xmlsoap.org/soap/envelope/'
+_NAMESPACE_XSI = 'http://www.w3.org/2001/XMLSchema-instance'
+_NAMESPACE_XSD = 'http://www.w3.org/2001/XMLSchema'
+_NAMESPACE_XML_SOAP = 'http://xml.apache.org/xml-soap'
+_NAMESPACE_XML_SOAP_ENCODING = 'http://schemas.xmlsoap.org/soap/encoding/'
+
+
+def _set_type(node, type_value, namespace=None):
+ if namespace is not None:
+ type_value = lxml.etree.QName(namespace, type_value)
+ node.set(lxml.etree.QName(_NAMESPACE_XSI, 'type').text, type_value)
+
+
+def encode_wsdl(node, value):
+ if value is None:
+ node.set(lxml.etree.QName(_NAMESPACE_XSI, 'nil').text, 'true')
+ elif isinstance(value, string_types):
+ _set_type(node, 'xsd:string')
+ node.text = value
+ elif isinstance(value, int):
+ _set_type(node, 'xsd:int')
+ node.text = str(value)
+ elif isinstance(value, bool):
+ _set_type(node, 'xsd:boolean')
+ node.text = ('true' if value else 'false')
+ elif isinstance(value, dict):
+ _set_type(node, 'Map', _NAMESPACE_XML_SOAP)
+ for key, val in sorted(value.items()):
+ child = lxml.etree.Element('item')
+ ke = lxml.etree.Element('key')
+ encode_wsdl(ke, key)
+ child.append(ke)
+ ve = lxml.etree.Element('value')
+ encode_wsdl(ve, val)
+ child.append(ve)
+ node.append(child)
+ elif isinstance(value, list):
+ _set_type(node, 'SOAP-ENC:Array')
+ for elt in value:
+ child = lxml.etree.Element('item')
+ encode_wsdl(child, elt)
+ node.append(child)
+ else:
+ raise WSDLCodingException('Do not know how to encode {0}!'.format(type(value)))
+
+
+def _decode_wsdl_array(result, node, root_ns, ids):
+ for item in node:
+ if item.tag != 'item':
+ raise WSDLCodingException('Invalid child tag "{0}" in map!'.format(item.tag))
+ result.append(decode_wsdl(item, root_ns, ids))
+
+
+def decode_wsdl(node, root_ns, ids):
+ href = node.get('href')
+ nil = node.get(lxml.etree.QName(_NAMESPACE_XSI, 'nil'))
+ id = node.get('id')
+ if href is not None:
+ if not href.startswith('#'):
+ raise WSDLCodingException('Global reference "{0}" not supported!'.format(href))
+ href = href[1:]
+ if href not in ids:
+ raise WSDLCodingException('ID "{0}" not yet defined!'.format(href))
+ result = ids[href]
+ elif nil == 'true':
+ result = None
+ else:
+ type_with_ns = node.get(lxml.etree.QName(_NAMESPACE_XSI, 'type'))
+ if type_with_ns is None:
+ raise WSDLCodingException('Element "{0}" has no "xsi:type" tag!'.format(node))
+ type, ns = _split_text_namespace(node, type_with_ns)
+ if ns is None:
+ raise WSDLCodingException('Cannot find namespace for "{0}"!'.format(type_with_ns))
+ if ns == _NAMESPACE_XSD:
+ if type == 'boolean':
+ if node.text == 'true':
+ result = True
+ elif node.text == 'false':
+ result = False
+ else:
+ raise WSDLCodingException('Invalid value for boolean: "{0}"'.format(node.text))
+ elif type == 'int':
+ result = int(node.text)
+ elif type == 'string':
+ result = node.text
+ else:
+ raise WSDLCodingException('Unknown XSD type "{0}"!'.format(type))
+ elif ns == _NAMESPACE_XML_SOAP:
+ if type == 'Map':
+ result = dict()
+ if id is not None:
+ ids[id] = result
+ for item in node:
+ if item.tag != 'item':
+ raise WSDLCodingException('Invalid child tag "{0}" in map!'.format(item.tag))
+ key = item.find('key')
+ if key is None:
+ raise WSDLCodingException('Cannot find key for "{0}"!'.format(item))
+ key = decode_wsdl(key, root_ns, ids)
+ value = item.find('value')
+ if value is None:
+ raise WSDLCodingException('Cannot find value for "{0}"!'.format(item))
+ value = decode_wsdl(value, root_ns, ids)
+ result[key] = value
+ return result
+ else:
+ raise WSDLCodingException('Unknown XSD type "{0}"!'.format(type))
+ elif ns == _NAMESPACE_XML_SOAP_ENCODING:
+ if type == 'Array':
+ result = []
+ if id is not None:
+ ids[id] = result
+ _decode_wsdl_array(result, node, root_ns, ids)
+ else:
+ raise WSDLCodingException('Unknown XSD type "{0}"!'.format(type))
+ elif ns == root_ns:
+ array_type = node.get(lxml.etree.QName(_NAMESPACE_XML_SOAP_ENCODING, 'arrayType'))
+ if array_type is not None:
+ result = []
+ if id is not None:
+ ids[id] = result
+ _decode_wsdl_array(result, node, root_ns, ids)
+ else:
+ result = dict()
+ if id is not None:
+ ids[id] = result
+ for item in node:
+ result[item.tag] = decode_wsdl(item, root_ns, ids)
+ else:
+ raise WSDLCodingException('Unknown type namespace "{0}" (with type "{1}")!'.format(ns, type))
+ if id is not None:
+ ids[id] = result
+ return result
+
+
+class Parser(object):
+ def _parse(self, result, node, where):
+ for child in node:
+ tag = lxml.etree.QName(child.tag)
+ if tag.namespace != self._api:
+ raise WSDLCodingException('Cannot interpret {0} item of type "{1}"!'.format(where, tag))
+ for res in child.iter('return'):
+ result[tag.localname] = decode_wsdl(res, self._api, {})
+
+ def __init__(self, api, root):
+ self._main_ns = _NAMESPACE_ENVELOPE
+ self._api = api
+ self._root = root
+ for fault in self._root.iter(lxml.etree.QName(self._main_ns, 'Fault').text):
+ fault_code = fault.find('faultcode')
+ fault_code_val = None
+ fault_string = fault.find('faultstring')
+ origin = 'server'
+ if fault_code is not None and fault_code.text:
+ code, code_ns = _split_text_namespace(fault, fault_code.text)
+ fault_code_val = code
+ if code_ns == self._main_ns:
+ origin = code.lower()
+ if fault_string is not None and fault_string.text:
+ raise WSDLError(origin, fault_code_val, fault_string.text)
+ raise WSDLError(origin, fault_code_val, lxml.etree.tostring(fault).decode('utf-8'))
+ self._header = dict()
+ self._body = dict()
+ for header in self._root.iter(lxml.etree.QName(self._main_ns, 'Header').text):
+ self._parse(self._header, header, 'header')
+ for body in self._root.iter(lxml.etree.QName(self._main_ns, 'Body').text):
+ self._parse(self._body, body, 'body')
+
+ def get_header(self, header):
+ return self._header[header]
+
+ def get_result(self, body):
+ return self._body[body]
+
+ def __str__(self):
+ return 'header={0}, body={1}'.format(self._header, self._body)
+
+ def __repr__(self):
+ return '''<?xml version='1.0' encoding='utf-8'?>''' + '\n' + lxml.etree.tostring(self._root, pretty_print=True).decode('utf-8')
+
+
+class Composer(object):
+ @staticmethod
+ def _create(tag, namespace=None, **kwarg):
+ if namespace:
+ return lxml.etree.Element(lxml.etree.QName(namespace, tag), **kwarg)
+ else:
+ return lxml.etree.Element(tag, **kwarg)
+
+ def __str__(self):
+ return '''<?xml version='1.0' encoding='utf-8'?>''' + '\n' + lxml.etree.tostring(self._root, pretty_print=True).decode('utf-8')
+
+ def _create_envelope(self, tag, **kwarg):
+ return self._create(tag, self._main_ns, **kwarg)
+
+ def __init__(self, http_helper, api, namespaces=None):
+ self._http_helper = http_helper
+ self._main_ns = _NAMESPACE_ENVELOPE
+ self._api = api
+ # Compose basic document
+ all_namespaces = {
+ 'SOAP-ENV': _NAMESPACE_ENVELOPE,
+ 'xsd': _NAMESPACE_XSD,
+ 'xsi': _NAMESPACE_XSI,
+ 'ns2': 'auth',
+ 'SOAP-ENC': _NAMESPACE_XML_SOAP_ENCODING,
+ }
+ if namespaces is not None:
+ all_namespaces.update(namespaces)
+ self._root = self._create_envelope('Envelope', nsmap=all_namespaces)
+ self._root.set(lxml.etree.QName(self._main_ns, 'encodingStyle').text, _NAMESPACE_XML_SOAP_ENCODING)
+ self._header = self._create_envelope('Header')
+ self._root.append(self._header)
+ self._body = self._create_envelope('Body')
+ self._root.append(self._body)
+ self._command = None
+
+ def add_auth(self, username, password):
+ auth = self._create('authenticate', 'auth')
+ user = self._create('UserName')
+ user.text = username
+ auth.append(user)
+ pw = self._create('Password')
+ pw.text = password
+ auth.append(pw)
+ self._header.append(auth)
+
+ def add_simple_command(self, command, **args):
+ self._command = command
+ command = self._create(command, self._api)
+ for arg, value in args.items():
+ arg = self._create(arg)
+ encode_wsdl(arg, value)
+ command.append(arg)
+ self._body.append(command)
+
+ def execute(self, debug=False):
+ payload = b'''<?xml version='1.0' encoding='utf-8'?>''' + b'\n' + lxml.etree.tostring(self._root) + b'\n'
+ try:
+ headers = {
+ 'Content-Type': 'text/xml; charset=utf-8',
+ 'Content-Length': str(len(payload)),
+ }
+ if self._command:
+ headers['SOAPAction'] = '"{0}#{1}"'.format(self._api, self._command)
+ result, info = self._http_helper.fetch_url(self._api, data=payload, method='POST', timeout=300, headers=headers)
+ code = info['status']
+ except NetworkError as e:
+ raise WSDLNetworkError(to_native(e))
+ # if debug:
+ # q.q('Result: {0}, content: {1}'.format(code, result.decode('utf-8')))
+ if code < 200 or code >= 300:
+ Parser(self._api, lxml.etree.fromstring(result))
+ raise WSDLError('server', 'Error {0} while executing WSDL command:\n{1}'.format(code, result.decode('utf-8')))
+ return Parser(self._api, lxml.etree.fromstring(result))
diff --git a/ansible_collections/community/dns/plugins/module_utils/zone.py b/ansible_collections/community/dns/plugins/module_utils/zone.py
new file mode 100644
index 000000000..d71d3ecdf
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/module_utils/zone.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017-2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+class DNSZone(object):
+ def __init__(self, name, info=None):
+ self.id = None
+ self.name = name
+ self.info = info or dict()
+
+ def __str__(self):
+ data = []
+ if self.id is not None:
+ data.append('id: {0}'.format(self.id))
+ data.append('name: {0}'.format(self.name))
+ data.append('info: {0}'.format(self.info))
+ return 'DNSZone(' + ', '.join(data) + ')'
+
+ def __repr__(self):
+ return self.__str__()
+
+
+class DNSZoneWithRecords(object):
+ def __init__(self, zone, records):
+ self.zone = zone
+ self.records = records
+
+ def __str__(self):
+ return '({0}, {1})'.format(self.zone, self.records)
+
+ def __repr__(self):
+ return 'DNSZoneWithRecords({0!r}, {1!r})'.format(self.zone, self.records)
diff --git a/ansible_collections/community/dns/plugins/module_utils/zone_record_api.py b/ansible_collections/community/dns/plugins/module_utils/zone_record_api.py
new file mode 100644
index 000000000..c42a5de3b
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/module_utils/zone_record_api.py
@@ -0,0 +1,221 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017-2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+import abc
+
+from ansible.module_utils import six
+
+from ansible_collections.community.dns.plugins.module_utils.zone import (
+ DNSZoneWithRecords,
+)
+
+
+class DNSAPIError(Exception):
+ pass
+
+
+class DNSAPIAuthenticationError(DNSAPIError):
+ pass
+
+
+class NotProvidedType(object):
+ pass
+
+
+NOT_PROVIDED = NotProvidedType()
+
+
+@six.add_metaclass(abc.ABCMeta)
+class ZoneRecordAPI(object):
+ @abc.abstractmethod
+ def get_zone_by_name(self, name):
+ """
+ Given a zone name, return the zone contents if found.
+
+ @param name: The zone name (string)
+ @return The zone information (DNSZone), or None if not found
+ """
+
+ @abc.abstractmethod
+ def get_zone_by_id(self, id):
+ """
+ Given a zone ID, return the zone contents if found.
+
+ @param id: The zone ID
+ @return The zone information (DNSZone), or None if not found
+ """
+
+ def get_zone_with_records_by_name(self, name, prefix=NOT_PROVIDED, record_type=NOT_PROVIDED):
+ """
+ Given a zone name, return the zone contents with records if found.
+
+ @param name: The zone name (string)
+ @param prefix: The prefix to filter for, if provided. Since None is a valid value,
+ the special constant NOT_PROVIDED indicates that we are not filtering.
+ @param record_type: The record type to filter for, if provided
+ @return The zone information with records (DNSZoneWithRecords), or None if not found
+ """
+ zone = self.get_zone_by_name(name)
+ if zone is None:
+ return None
+ return DNSZoneWithRecords(zone, self.get_zone_records(zone.id, prefix=prefix, record_type=record_type))
+
+ def get_zone_with_records_by_id(self, id, prefix=NOT_PROVIDED, record_type=NOT_PROVIDED):
+ """
+ Given a zone ID, return the zone contents with records if found.
+
+ @param id: The zone ID
+ @param prefix: The prefix to filter for, if provided. Since None is a valid value,
+ the special constant NOT_PROVIDED indicates that we are not filtering.
+ @param record_type: The record type to filter for, if provided
+ @return The zone information with records (DNSZoneWithRecords), or None if not found
+ """
+ zone = self.get_zone_by_id(id)
+ if zone is None:
+ return None
+ return DNSZoneWithRecords(zone, self.get_zone_records(zone.id, prefix=prefix, record_type=record_type))
+
+ @abc.abstractmethod
+ def get_zone_records(self, zone_id, prefix=NOT_PROVIDED, record_type=NOT_PROVIDED):
+ """
+ Given a zone ID, return a list of records, optionally filtered by the provided criteria.
+
+ @param zone_id: The zone ID
+ @param prefix: The prefix to filter for, if provided. Since None is a valid value,
+ the special constant NOT_PROVIDED indicates that we are not filtering.
+ @param record_type: The record type to filter for, if provided
+ @return A list of DNSrecord objects, or None if zone was not found
+ """
+
+ @abc.abstractmethod
+ def add_record(self, zone_id, record):
+ """
+ Adds a new record to an existing zone.
+
+ @param zone_id: The zone ID
+ @param record: The DNS record (DNSRecord)
+ @return The created DNS record (DNSRecord)
+ """
+
+ @abc.abstractmethod
+ def update_record(self, zone_id, record):
+ """
+ Update a record.
+
+ @param zone_id: The zone ID
+ @param record: The DNS record (DNSRecord)
+ @return The DNS record (DNSRecord)
+ """
+
+ @abc.abstractmethod
+ def delete_record(self, zone_id, record):
+ """
+ Delete a record.
+
+ @param zone_id: The zone ID
+ @param record: The DNS record (DNSRecord)
+ @return True in case of success (boolean)
+ """
+
+ def add_records(self, records_per_zone_id, stop_early_on_errors=True):
+ """
+ Add new records to an existing zone.
+
+ @param records_per_zone_id: Maps a zone ID to a list of DNS records (DNSRecord)
+ @param stop_early_on_errors: If set to ``True``, try to stop changes after the first error happens.
+ This might only work on some APIs.
+ @return A dictionary mapping zone IDs to lists of tuples ``(record, created, failed)``.
+ Here ``created`` indicates whether the record was created (``True``) or not (``False``).
+ If it was created, ``record`` contains the record ID and ``failed`` is ``None``.
+ If it was not created, ``failed`` should be a ``DNSAPIError`` instance indicating why
+ it was not created. It is possible that the API only creates records if all succeed,
+ in that case ``failed`` can be ``None`` even though ``created`` is ``False``.
+ """
+ results_per_zone_id = {}
+ for zone_id, records in records_per_zone_id.items():
+ result = []
+ results_per_zone_id[zone_id] = result
+ for record in records:
+ try:
+ result.append((self.add_record(zone_id, record), True, None))
+ except DNSAPIError as e:
+ result.append((record, False, e))
+ if stop_early_on_errors:
+ return results_per_zone_id
+ return results_per_zone_id
+
+ def update_records(self, records_per_zone_id, stop_early_on_errors=True):
+ """
+ Update multiple records.
+
+ @param records_per_zone_id: Maps a zone ID to a list of DNS records (DNSRecord)
+ @param stop_early_on_errors: If set to ``True``, try to stop changes after the first error happens.
+ This might only work on some APIs.
+ @return A dictionary mapping zone IDs to lists of tuples ``(record, updated, failed)``.
+ Here ``updated`` indicates whether the record was updated (``True``) or not (``False``).
+ If it was not updated, ``failed`` should be a ``DNSAPIError`` instance. If it was
+ updated, ``failed`` should be ``None``. It is possible that the API only updates
+ records if all succeed, in that case ``failed`` can be ``None`` even though
+ ``updated`` is ``False``.
+ """
+ results_per_zone_id = {}
+ for zone_id, records in records_per_zone_id.items():
+ result = []
+ results_per_zone_id[zone_id] = result
+ for record in records:
+ try:
+ result.append((self.update_record(zone_id, record), True, None))
+ except DNSAPIError as e:
+ result.append((record, False, e))
+ if stop_early_on_errors:
+ return results_per_zone_id
+ return results_per_zone_id
+
+ def delete_records(self, records_per_zone_id, stop_early_on_errors=True):
+ """
+ Delete multiple records.
+
+ @param records_per_zone_id: Maps a zone ID to a list of DNS records (DNSRecord)
+ @param stop_early_on_errors: If set to ``True``, try to stop changes after the first error happens.
+ This might only work on some APIs.
+ @return A dictionary mapping zone IDs to lists of tuples ``(record, deleted, failed)``.
+ In case ``record`` was deleted or not deleted, ``deleted`` is ``True``
+ respectively ``False``, and ``failed`` is ``None``. In case an error happened
+ while deleting, ``deleted`` is ``False`` and ``failed`` is a ``DNSAPIError``
+ instance hopefully providing information on the error.
+ """
+ results_per_zone_id = {}
+ for zone_id, records in records_per_zone_id.items():
+ result = []
+ results_per_zone_id[zone_id] = result
+ for record in records:
+ try:
+ result.append((record, self.delete_record(zone_id, record), None))
+ except DNSAPIError as e:
+ result.append((record, False, e))
+ if stop_early_on_errors:
+ return results_per_zone_id
+ return results_per_zone_id
+
+
+def filter_records(records, prefix=NOT_PROVIDED, record_type=NOT_PROVIDED):
+ """
+ Given a list of records, returns a filtered subset.
+
+ @param prefix: The prefix to filter for, if provided. Since None is a valid value,
+ the special constant NOT_PROVIDED indicates that we are not filtering.
+ @param record_type: The record type to filter for, if provided
+ @return The list of records matching the provided filters.
+ """
+ if prefix is not NOT_PROVIDED:
+ records = [record for record in records if record.prefix == prefix]
+ if record_type is not NOT_PROVIDED:
+ records = [record for record in records if record.type == record_type]
+ return records
diff --git a/ansible_collections/community/dns/plugins/module_utils/zone_record_helpers.py b/ansible_collections/community/dns/plugins/module_utils/zone_record_helpers.py
new file mode 100644
index 000000000..57277e29b
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/module_utils/zone_record_helpers.py
@@ -0,0 +1,132 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+from ansible_collections.community.dns.plugins.module_utils.zone_record_api import (
+ DNSAPIError,
+)
+
+
+def bulk_apply_changes(api,
+ provider_information,
+ options,
+ zone_id,
+ records_to_delete=None,
+ records_to_change=None,
+ records_to_create=None,
+ stop_early_on_errors=True,
+ ):
+ """
+ Update multiple records. If an operation failed, raise a DNSAPIException.
+
+ @param api: A ZoneRecordAPI instance
+ @param provider_information: A ProviderInformation object.
+ @param options: A object compatible with ModuleOptionProvider that gives access to the module/plugin
+ options.
+ @param zone_id: Zone ID to apply changes to
+ @param records_to_delete: Optional list of DNS records to delete (DNSRecord)
+ @param records_to_change: Optional list of DNS records to change (DNSRecord)
+ @param records_to_create: Optional list of DNS records to create (DNSRecord)
+ @param bulk_threshold: Minimum number of changes for using the bulk API instead of the regular API
+ @param stop_early_on_errors: If set to ``True``, try to stop changes after the first error happens.
+ This might only work on some APIs.
+ @return A tuple (changed, errors, success) where ``changed`` is a boolean which indicates whether a
+ change was made, ``errors`` is a list of ``DNSAPIError`` instances for the errors occured,
+ and ``success`` is a dictionary with three lists ``success['deleted']``,
+ ``success['changed']`` and ``success['created']``, which list all records that were deleted,
+ changed and created, respectively.
+ """
+ records_to_delete = records_to_delete or []
+ records_to_change = records_to_change or []
+ records_to_create = records_to_create or []
+
+ has_change = False
+ errors = []
+
+ bulk_threshold = 2
+ if provider_information.supports_bulk_actions():
+ bulk_threshold = options.get_option('bulk_operation_threshold')
+
+ success = {
+ 'deleted': [],
+ 'changed': [],
+ 'created': [],
+ }
+
+ # Delete records
+ if len(records_to_delete) >= bulk_threshold:
+ results = api.delete_records({zone_id: records_to_delete}, stop_early_on_errors=stop_early_on_errors)
+ result = results.get(zone_id) or []
+ for record, deleted, failed in result:
+ has_change |= deleted
+ if failed is not None:
+ errors.append(failed)
+ if deleted:
+ success['deleted'].append(record)
+ if errors and stop_early_on_errors:
+ return has_change, errors, success
+ else:
+ for record in records_to_delete:
+ try:
+ deleted = api.delete_record(zone_id, record)
+ has_change |= deleted
+ if deleted:
+ success['deleted'].append(record)
+ except DNSAPIError as e:
+ errors.append(e)
+ if stop_early_on_errors:
+ return has_change, errors, success
+
+ # Change records
+ if len(records_to_change) >= bulk_threshold:
+ results = api.update_records({zone_id: records_to_change}, stop_early_on_errors=stop_early_on_errors)
+ result = results.get(zone_id) or []
+ for record, changed, failed in result:
+ has_change |= changed
+ if failed is not None:
+ errors.append(failed)
+ if changed:
+ success['changed'].append(record)
+ if errors and stop_early_on_errors:
+ return has_change, errors, success
+ else:
+ for record in records_to_change:
+ try:
+ record = api.update_record(zone_id, record)
+ has_change = True
+ success['changed'].append(record)
+ except DNSAPIError as e:
+ errors.append(e)
+ if stop_early_on_errors:
+ return has_change, errors, success
+
+ # Create records
+ if len(records_to_create) >= bulk_threshold:
+ results = api.add_records({zone_id: records_to_create}, stop_early_on_errors=stop_early_on_errors)
+ result = results.get(zone_id) or []
+ for record, created, failed in result:
+ has_change |= created
+ if failed is not None:
+ errors.append(failed)
+ if created:
+ success['created'].append(record)
+ if errors and stop_early_on_errors:
+ return has_change, errors, success
+ else:
+ for record in records_to_create:
+ try:
+ record = api.add_record(zone_id, record)
+ has_change = True
+ success['created'].append(record)
+ except DNSAPIError as e:
+ errors.append(e)
+ if stop_early_on_errors:
+ return has_change, errors, success
+
+ return has_change, errors, success
diff --git a/ansible_collections/community/dns/plugins/modules/hetzner_dns_record.py b/ansible_collections/community/dns/plugins/modules/hetzner_dns_record.py
new file mode 100644
index 000000000..b17e0842c
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/modules/hetzner_dns_record.py
@@ -0,0 +1,118 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+DOCUMENTATION = '''
+---
+module: hetzner_dns_record
+
+short_description: Add or delete a single record in Hetzner DNS service
+
+version_added: 2.0.0
+
+description:
+ - "Creates and deletes single DNS records in Hetzner DNS service."
+ - If you do not want to add/remove values, but replace values, you will be interested in
+ modifying a B(record set) and not a single record. This is in particular important
+ when working with C(CNAME) and C(SOA) records.
+ Use the M(community.dns.hetzner_dns_record_set) module for working with record sets.
+
+extends_documentation_fragment:
+ - community.dns.hetzner
+ - community.dns.hetzner.record_default_ttl
+ - community.dns.hetzner.record_type_choices
+ - community.dns.hetzner.zone_id_type
+ - community.dns.module_record
+ - community.dns.options.record_transformation
+ - community.dns.attributes
+ - community.dns.attributes.actiongroup_hetzner
+
+attributes:
+ action_group:
+ version_added: 2.4.0
+ check_mode:
+ support: full
+ diff_mode:
+ support: full
+
+options:
+ prefix:
+ aliases:
+ - name
+
+author:
+ - Markus Bergholz (@markuman) <markuman+spambelongstogoogle@gmail.com>
+ - Felix Fontein (@felixfontein)
+'''
+
+EXAMPLES = '''
+- name: Add a new.foo.com A record
+ community.dns.hetzner_dns_record:
+ state: present
+ zone: foo.com
+ record: new.foo.com
+ type: A
+ ttl: 7200
+ value: 1.1.1.1
+ hetzner_token: access_token
+
+- name: Remove a new.foo.com A record
+ community.dns.hetzner_dns_record:
+ state: absent
+ zone_name: foo.com
+ record: new.foo.com
+ type: A
+ ttl: 7200
+ value: 2.2.2.2
+ hetzner_token: access_token
+'''
+
+RETURN = '''
+zone_id:
+ description: The ID of the zone.
+ type: str
+ returned: success
+ sample: 23
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ansible_collections.community.dns.plugins.module_utils.argspec import (
+ ModuleOptionProvider,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.http import (
+ ModuleHTTPHelper,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.hetzner.api import (
+ create_hetzner_argument_spec,
+ create_hetzner_api,
+ create_hetzner_provider_information,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.module.record import (
+ create_module_argument_spec,
+ run_module,
+)
+
+
+def main():
+ provider_information = create_hetzner_provider_information()
+ argument_spec = create_hetzner_argument_spec()
+ argument_spec.merge(create_module_argument_spec(provider_information=provider_information))
+ argument_spec.argument_spec['prefix']['aliases'] = ['name']
+ argument_spec.argument_spec['prefix']['deprecated_aliases'] = [dict(name='name', version='3.0.0', collection_name='community.dns')]
+ module = AnsibleModule(supports_check_mode=True, **argument_spec.to_kwargs())
+ run_module(module, lambda: create_hetzner_api(ModuleOptionProvider(module), ModuleHTTPHelper(module)), provider_information=provider_information)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/dns/plugins/modules/hetzner_dns_record_info.py b/ansible_collections/community/dns/plugins/modules/hetzner_dns_record_info.py
new file mode 100644
index 000000000..9d77a38d5
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/modules/hetzner_dns_record_info.py
@@ -0,0 +1,136 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+DOCUMENTATION = '''
+---
+module: hetzner_dns_record_info
+
+short_description: Retrieve records in Hetzner DNS service
+
+version_added: 2.0.0
+
+description:
+ - "Retrieves DNS records in Hetzner DNS service."
+
+extends_documentation_fragment:
+ - community.dns.hetzner
+ - community.dns.hetzner.record_type_choices
+ - community.dns.hetzner.zone_id_type
+ - community.dns.module_record_info
+ - community.dns.options.record_transformation
+ - community.dns.attributes
+ - community.dns.attributes.actiongroup_hetzner
+ - community.dns.attributes.info_module
+
+attributes:
+ action_group:
+ version_added: 2.4.0
+
+author:
+ - Markus Bergholz (@markuman) <markuman+spambelongstogoogle@gmail.com>
+ - Felix Fontein (@felixfontein)
+'''
+
+EXAMPLES = '''
+- name: Retrieve the details for the A records of new.foo.com
+ community.dns.hetzner_dns_record_info:
+ zone: foo.com
+ record: new.foo.com
+ type: A
+ hetzner_token: access_token
+ register: rec
+
+- name: Print the A records
+ ansible.builtin.debug:
+ msg: "{{ rec.records }}"
+'''
+
+RETURN = '''
+records:
+ description: The list of fetched records.
+ type: list
+ elements: dict
+ returned: success and I(what) is not C(single_record)
+ contains:
+ record:
+ description: The record name.
+ type: str
+ sample: sample.example.com
+ prefix:
+ description: The record prefix.
+ type: str
+ sample: sample
+ type:
+ description: The DNS record type.
+ type: str
+ sample: A
+ ttl:
+ description:
+ - The TTL.
+ - Will return C(none) if the zone's default TTL is used.
+ type: int
+ sample: 3600
+ value:
+ description: The DNS record's value.
+ type: str
+ sample: 1.2.3.4
+ extra:
+ description: Extra information on records.
+ type: dict
+ sample:
+ created: '2021-07-09T11:18:37Z'
+ modified: '2021-07-09T11:18:37Z'
+ sample:
+ - record: sample.example.com
+ type: A
+ ttl: 3600
+ value: 1.2.3.4
+ extra: {}
+
+zone_id:
+ description: The ID of the zone.
+ type: str
+ returned: success
+ sample: 23
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ansible_collections.community.dns.plugins.module_utils.argspec import (
+ ModuleOptionProvider,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.http import (
+ ModuleHTTPHelper,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.hetzner.api import (
+ create_hetzner_argument_spec,
+ create_hetzner_api,
+ create_hetzner_provider_information,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.module.record_info import (
+ run_module,
+ create_module_argument_spec,
+)
+
+
+def main():
+ provider_information = create_hetzner_provider_information()
+ argument_spec = create_hetzner_argument_spec()
+ argument_spec.merge(create_module_argument_spec(provider_information=provider_information))
+ module = AnsibleModule(supports_check_mode=True, **argument_spec.to_kwargs())
+ run_module(module, lambda: create_hetzner_api(ModuleOptionProvider(module), ModuleHTTPHelper(module)), provider_information=provider_information)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/dns/plugins/modules/hetzner_dns_record_set.py b/ansible_collections/community/dns/plugins/modules/hetzner_dns_record_set.py
new file mode 100644
index 000000000..0766e6465
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/modules/hetzner_dns_record_set.py
@@ -0,0 +1,234 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+DOCUMENTATION = '''
+---
+module: hetzner_dns_record_set
+
+short_description: Add or delete record sets in Hetzner DNS service
+
+version_added: 2.0.0
+
+description:
+ - "Creates and deletes DNS record sets in Hetzner DNS service."
+
+extends_documentation_fragment:
+ - community.dns.hetzner
+ - community.dns.hetzner.record_default_ttl
+ - community.dns.hetzner.record_type_choices
+ - community.dns.hetzner.zone_id_type
+ - community.dns.module_record_set
+ - community.dns.options.bulk_operations
+ - community.dns.options.record_transformation
+ - community.dns.attributes
+ - community.dns.attributes.actiongroup_hetzner
+
+attributes:
+ action_group:
+ version_added: 2.4.0
+ check_mode:
+ support: full
+ diff_mode:
+ support: full
+
+options:
+ prefix:
+ aliases:
+ - name
+
+author:
+ - Markus Bergholz (@markuman) <markuman+spambelongstogoogle@gmail.com>
+ - Felix Fontein (@felixfontein)
+'''
+
+EXAMPLES = '''
+- name: Add new.foo.com as an A record with 3 IPs
+ community.dns.hetzner_dns_record_set:
+ state: present
+ zone: foo.com
+ record: new.foo.com
+ type: A
+ ttl: 7200
+ value: 1.1.1.1,2.2.2.2,3.3.3.3
+ hetzner_token: access_token
+
+- name: Update new.foo.com as an A record with a list of 3 IPs
+ community.dns.hetzner_dns_record_set:
+ state: present
+ zone: foo.com
+ record: new.foo.com
+ type: A
+ ttl: 7200
+ value:
+ - 1.1.1.1
+ - 2.2.2.2
+ - 3.3.3.3
+ hetzner_token: access_token
+
+- name: Retrieve the details for new.foo.com
+ community.dns.hetzner_dns_record_set_info:
+ zone: foo.com
+ record: new.foo.com
+ type: A
+ hetzner_token: access_token
+ register: rec
+
+- name: Delete new.foo.com A record using the results from the facts retrieval command
+ community.dns.hetzner_dns_record_set:
+ state: absent
+ zone: foo.com
+ record: "{{ rec.set.record }}"
+ ttl: "{{ rec.set.ttl }}"
+ type: "{{ rec.set.type }}"
+ value: "{{ rec.set.value }}"
+ hetzner_token: access_token
+
+- name: Add an AAAA record
+ # Note that because there are colons in the value that the IPv6 address must be quoted!
+ community.dns.hetzner_dns_record_set:
+ state: present
+ zone: foo.com
+ record: localhost.foo.com
+ type: AAAA
+ ttl: 7200
+ value: "::1"
+ hetzner_token: access_token
+
+- name: Add a TXT record
+ community.dns.hetzner_dns_record_set:
+ state: present
+ zone: foo.com
+ record: localhost.foo.com
+ type: TXT
+ ttl: 7200
+ value: 'bar'
+ hetzner_token: access_token
+
+- name: Remove the TXT record
+ community.dns.hetzner_dns_record_set:
+ state: absent
+ zone: foo.com
+ record: localhost.foo.com
+ type: TXT
+ ttl: 7200
+ value: 'bar'
+ hetzner_token: access_token
+
+- name: Add a CAA record
+ community.dns.hetzner_dns_record_set:
+ state: present
+ zone: foo.com
+ record: foo.com
+ type: CAA
+ ttl: 3600
+ value:
+ - "128 issue letsencrypt.org"
+ - "128 iodef mailto:webmaster@foo.com"
+ hetzner_token: access_token
+
+- name: Add an MX record
+ community.dns.hetzner_dns_record_set:
+ state: present
+ zone: foo.com
+ record: foo.com
+ type: MX
+ ttl: 3600
+ value:
+ - "10 mail.foo.com"
+ hetzner_token: access_token
+
+- name: Add a CNAME record
+ community.dns.hetzner_dns_record_set:
+ state: present
+ zone: bla.foo.com
+ record: foo.com
+ type: CNAME
+ ttl: 3600
+ value:
+ - foo.foo.com
+ hetzner_token: access_token
+
+- name: Add a PTR record
+ community.dns.hetzner_dns_record_set:
+ state: present
+ zone: foo.foo.com
+ record: foo.com
+ type: PTR
+ ttl: 3600
+ value:
+ - foo.foo.com
+ hetzner_token: access_token
+
+- name: Add an SPF record
+ community.dns.hetzner_dns_record_set:
+ state: present
+ zone: foo.com
+ record: foo.com
+ type: SPF
+ ttl: 3600
+ value:
+ - "v=spf1 a mx ~all"
+ hetzner_token: access_token
+
+- name: Add a PTR record
+ community.dns.hetzner_dns_record_set:
+ state: present
+ zone: foo.com
+ record: foo.com
+ type: PTR
+ ttl: 3600
+ value:
+ - "10 100 3333 service.foo.com"
+ hetzner_token: access_token
+'''
+
+RETURN = '''
+zone_id:
+ description: The ID of the zone.
+ type: str
+ returned: success
+ sample: 23
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ansible_collections.community.dns.plugins.module_utils.argspec import (
+ ModuleOptionProvider,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.http import (
+ ModuleHTTPHelper,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.hetzner.api import (
+ create_hetzner_argument_spec,
+ create_hetzner_api,
+ create_hetzner_provider_information,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.module.record_set import (
+ create_module_argument_spec,
+ run_module,
+)
+
+
+def main():
+ provider_information = create_hetzner_provider_information()
+ argument_spec = create_hetzner_argument_spec()
+ argument_spec.merge(create_module_argument_spec(provider_information=provider_information))
+ argument_spec.argument_spec['prefix']['aliases'] = ['name']
+ argument_spec.argument_spec['prefix']['deprecated_aliases'] = [dict(name='name', version='3.0.0', collection_name='community.dns')]
+ module = AnsibleModule(supports_check_mode=True, **argument_spec.to_kwargs())
+ run_module(module, lambda: create_hetzner_api(ModuleOptionProvider(module), ModuleHTTPHelper(module)), provider_information=provider_information)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/dns/plugins/modules/hetzner_dns_record_set_info.py b/ansible_collections/community/dns/plugins/modules/hetzner_dns_record_set_info.py
new file mode 100644
index 000000000..5c70d1fb0
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/modules/hetzner_dns_record_set_info.py
@@ -0,0 +1,199 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+DOCUMENTATION = '''
+---
+module: hetzner_dns_record_set_info
+
+short_description: Retrieve record sets in Hetzner DNS service
+
+version_added: 2.0.0
+
+description:
+ - "Retrieves DNS record sets in Hetzner DNS service."
+
+extends_documentation_fragment:
+ - community.dns.hetzner
+ - community.dns.hetzner.record_type_choices
+ - community.dns.hetzner.zone_id_type
+ - community.dns.module_record_set_info
+ - community.dns.options.record_transformation
+ - community.dns.attributes
+ - community.dns.attributes.actiongroup_hetzner
+ - community.dns.attributes.info_module
+
+attributes:
+ action_group:
+ version_added: 2.4.0
+
+author:
+ - Markus Bergholz (@markuman) <markuman+spambelongstogoogle@gmail.com>
+ - Felix Fontein (@felixfontein)
+'''
+
+EXAMPLES = '''
+- name: Retrieve the details for the A records of new.foo.com
+ community.dns.hetzner_dns_record_set_info:
+ zone: foo.com
+ record: new.foo.com
+ type: A
+ hetzner_token: access_token
+ register: rec
+
+- name: Print the A record set
+ ansible.builtin.debug:
+ msg: "{{ rec.set }}"
+'''
+
+RETURN = '''
+set:
+ description: The fetched record set. Is empty if record set does not exist.
+ type: dict
+ returned: success and I(what) is C(single_record)
+ contains:
+ record:
+ description: The record name.
+ type: str
+ sample: sample.example.com
+ prefix:
+ description: The record prefix.
+ type: str
+ sample: sample
+ version_added: 0.2.0
+ type:
+ description: The DNS record type.
+ type: str
+ sample: A
+ ttl:
+ description:
+ - The TTL.
+ - If there are records in this set with different TTLs, the minimum of the TTLs will be presented here.
+ - Will return C(none) if the zone's default TTL is used.
+ type: int
+ sample: 3600
+ ttls:
+ description:
+ - If there are records with different TTL values in this set, this will be the list of TTLs appearing
+ in the records.
+ - Every distinct TTL will appear once, and the TTLs are in ascending order.
+ returned: When there is more than one distinct TTL
+ type: list
+ elements: int
+ sample:
+ - 300
+ - 3600
+ value:
+ description: The DNS record set's value.
+ type: list
+ elements: str
+ sample:
+ - 1.2.3.4
+ - 1.2.3.5
+ sample:
+ record: sample.example.com
+ type: A
+ ttl: 3600
+ value:
+ - 1.2.3.4
+ - 1.2.3.5
+
+sets:
+ description: The list of fetched record sets.
+ type: list
+ elements: dict
+ returned: success and I(what) is not C(single_record)
+ contains:
+ record:
+ description: The record name.
+ type: str
+ sample: sample.example.com
+ prefix:
+ description: The record prefix.
+ type: str
+ sample: sample
+ version_added: 0.2.0
+ type:
+ description: The DNS record type.
+ type: str
+ sample: A
+ ttl:
+ description:
+ - The TTL.
+ - If there are records in this set with different TTLs, the minimum of the TTLs will be presented here.
+ - Will return C(none) if the zone's default TTL is used.
+ type: int
+ sample: 3600
+ ttls:
+ description:
+ - If there are records with different TTL values in this set, this will be the list of TTLs appearing
+ in the records.
+ - Every distinct TTL will appear once, and the TTLs are in ascending order.
+ returned: When there is more than one distinct TTL
+ type: list
+ elements: int
+ sample:
+ - 300
+ - 3600
+ value:
+ description: The DNS record set's value.
+ type: list
+ elements: str
+ sample:
+ - 1.2.3.4
+ - 1.2.3.5
+ sample:
+ - record: sample.example.com
+ type: A
+ ttl: 3600
+ value:
+ - 1.2.3.4
+ - 1.2.3.5
+
+zone_id:
+ description: The ID of the zone.
+ type: str
+ returned: success
+ sample: 23
+ version_added: 0.2.0
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ansible_collections.community.dns.plugins.module_utils.argspec import (
+ ModuleOptionProvider,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.http import (
+ ModuleHTTPHelper,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.hetzner.api import (
+ create_hetzner_argument_spec,
+ create_hetzner_api,
+ create_hetzner_provider_information,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.module.record_set_info import (
+ run_module,
+ create_module_argument_spec,
+)
+
+
+def main():
+ provider_information = create_hetzner_provider_information()
+ argument_spec = create_hetzner_argument_spec()
+ argument_spec.merge(create_module_argument_spec(provider_information=provider_information))
+ module = AnsibleModule(supports_check_mode=True, **argument_spec.to_kwargs())
+ run_module(module, lambda: create_hetzner_api(ModuleOptionProvider(module), ModuleHTTPHelper(module)), provider_information=provider_information)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/dns/plugins/modules/hetzner_dns_record_sets.py b/ansible_collections/community/dns/plugins/modules/hetzner_dns_record_sets.py
new file mode 100644
index 000000000..52857186c
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/modules/hetzner_dns_record_sets.py
@@ -0,0 +1,129 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+DOCUMENTATION = '''
+---
+module: hetzner_dns_record_sets
+
+short_description: Bulk synchronize DNS record sets in Hetzner DNS service
+
+version_added: 2.0.0
+
+description:
+ - Bulk synchronize DNS record sets in Hetzner DNS service.
+
+extends_documentation_fragment:
+ - community.dns.hetzner
+ - community.dns.hetzner.record_type_choices_record_sets_module
+ - community.dns.hetzner.zone_id_type
+ - community.dns.module_record_sets
+ - community.dns.options.bulk_operations
+ - community.dns.options.record_transformation
+ - community.dns.attributes
+ - community.dns.attributes.actiongroup_hetzner
+
+attributes:
+ action_group:
+ version_added: 2.4.0
+ check_mode:
+ support: full
+ diff_mode:
+ support: full
+
+author:
+ - Markus Bergholz (@markuman) <markuman+spambelongstogoogle@gmail.com>
+ - Felix Fontein (@felixfontein)
+
+'''
+
+EXAMPLES = '''
+- name: Make sure some records exist and have the expected values
+ community.dns.hetzner_dns_record_sets:
+ zone: foo.com
+ records:
+ - prefix: new
+ type: A
+ ttl: 7200
+ value:
+ - 1.1.1.1
+ - 2.2.2.2
+ - prefix: new
+ type: AAAA
+ ttl: 7200
+ value:
+ - "::1"
+ - record: foo.com
+ type: TXT
+ value:
+ - test
+ hetzner_token: access_token
+
+- name: Synchronize DNS zone with a fixed set of records
+ # If a record exists that is not mentioned here, it will be deleted
+ community.dns.hetzner_dns_record_sets:
+ zone_id: 23
+ purge: true
+ records:
+ - prefix: ''
+ type: A
+ value: 127.0.0.1
+ - prefix: ''
+ type: AAAA
+ value: "::1"
+ - prefix: ''
+ type: NS
+ value:
+ - ns-1.hoster.com
+ - ns-2.hoster.com
+ - ns-3.hoster.com
+ hetzner_token: access_token
+'''
+
+RETURN = '''
+zone_id:
+ description: The ID of the zone.
+ type: str
+ returned: success
+ sample: 23
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ansible_collections.community.dns.plugins.module_utils.argspec import (
+ ModuleOptionProvider,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.http import (
+ ModuleHTTPHelper,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.hetzner.api import (
+ create_hetzner_argument_spec,
+ create_hetzner_api,
+ create_hetzner_provider_information,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.module.record_sets import (
+ create_module_argument_spec,
+ run_module,
+)
+
+
+def main():
+ provider_information = create_hetzner_provider_information()
+ argument_spec = create_hetzner_argument_spec()
+ argument_spec.merge(create_module_argument_spec(provider_information=provider_information))
+ module = AnsibleModule(supports_check_mode=True, **argument_spec.to_kwargs())
+ run_module(module, lambda: create_hetzner_api(ModuleOptionProvider(module), ModuleHTTPHelper(module)), provider_information=provider_information)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/dns/plugins/modules/hetzner_dns_zone_info.py b/ansible_collections/community/dns/plugins/modules/hetzner_dns_zone_info.py
new file mode 100644
index 000000000..44cc88af5
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/modules/hetzner_dns_zone_info.py
@@ -0,0 +1,199 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+DOCUMENTATION = '''
+---
+module: hetzner_dns_zone_info
+
+short_description: Retrieve zone information in Hetzner DNS service
+
+version_added: 2.0.0
+
+description:
+ - "Retrieves zone information in Hetzner DNS service."
+
+extends_documentation_fragment:
+ - community.dns.hetzner
+ - community.dns.hetzner.zone_id_type
+ - community.dns.module_zone_info
+ - community.dns.attributes
+ - community.dns.attributes.actiongroup_hetzner
+ - community.dns.attributes.info_module
+
+attributes:
+ action_group:
+ version_added: 2.4.0
+
+author:
+ - Markus Bergholz (@markuman) <markuman+spambelongstogoogle@gmail.com>
+ - Felix Fontein (@felixfontein)
+'''
+
+EXAMPLES = '''
+- name: Retrieve details for foo.com zone
+ community.dns.hetzner_dns_zone_info:
+ zone: foo.com
+ hetzner_token: access_token
+ register: rec
+
+- name: Retrieve details for zone 23
+ community.dns.hetzner_dns_zone_info:
+ zone_id: 23
+ hetzner_token: access_token
+'''
+
+RETURN = '''
+zone_name:
+ description: The name of the zone.
+ type: int
+ returned: success
+ sample: example.com
+
+zone_id:
+ description: The ID of the zone.
+ type: str
+ returned: success
+ sample: 23
+
+zone_info:
+ description:
+ - Extra information returned by the API.
+ type: dict
+ returned: success
+ contains:
+ created:
+ description:
+ - The time when the zone was created.
+ type: str
+ sample: "2021-07-15T19:23:58Z"
+ modified:
+ description:
+ - The time the zone was last modified.
+ type: str
+ sample: "2021-07-15T19:23:58Z"
+ legacy_dns_host:
+ description:
+ # TODO
+ - Unknown.
+ type: str
+ legacy_ns:
+ description:
+ - List of nameservers during import.
+ type: list
+ elements: str
+ ns:
+ description:
+ - List of nameservers the zone should have for using Hetzner's DNS.
+ type: list
+ elements: str
+ owner:
+ description:
+ - Owner of the zone.
+ type: str
+ paused:
+ description:
+ # TODO
+ - Unknown.
+ type: bool
+ sample: true
+ permission:
+ description:
+ - Zone's permissions.
+ type: str
+ project:
+ description:
+ # TODO
+ - Unknown.
+ type: str
+ registrar:
+ description:
+ # TODO
+ - Unknown.
+ type: str
+ status:
+ description:
+ - Status of the zone.
+ - Can be one of C(verified), C(failed) and C(pending).
+ type: str
+ sample: verified
+ # choices:
+ # - verified
+ # - failed
+ # - pending
+ ttl:
+ description:
+ - TTL of zone.
+ type: int
+ sample: 0
+ verified:
+ description:
+ - Time when zone was verified.
+ type: str
+ sample: "2021-07-15T19:23:58Z"
+ records_count:
+ description:
+ - Number of records associated to this zone.
+ type: int
+ sample: 0
+ is_secondary_dns:
+ description:
+ - Indicates whether the zone is a secondary DNS zone.
+ type: bool
+ sample: true
+ txt_verification:
+ description:
+ - Shape of the TXT record that has to be set to verify a zone.
+ - If name and token are empty, no TXT record needs to be set.
+ type: dict
+ sample: {'name': '', 'token': ''}
+ contains:
+ name:
+ description:
+ - The TXT record's name.
+ type: str
+ token:
+ description:
+ - The TXT record's content.
+ type: str
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ansible_collections.community.dns.plugins.module_utils.argspec import (
+ ModuleOptionProvider,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.http import (
+ ModuleHTTPHelper,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.hetzner.api import (
+ create_hetzner_argument_spec,
+ create_hetzner_api,
+ create_hetzner_provider_information,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.module.zone_info import (
+ run_module,
+ create_module_argument_spec,
+)
+
+
+def main():
+ provider_information = create_hetzner_provider_information()
+ argument_spec = create_hetzner_argument_spec()
+ argument_spec.merge(create_module_argument_spec(provider_information=provider_information))
+ module = AnsibleModule(supports_check_mode=True, **argument_spec.to_kwargs())
+ run_module(module, lambda: create_hetzner_api(ModuleOptionProvider(module), ModuleHTTPHelper(module)), provider_information=provider_information)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/dns/plugins/modules/hosttech_dns_record.py b/ansible_collections/community/dns/plugins/modules/hosttech_dns_record.py
new file mode 100644
index 000000000..0756b6a4c
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/modules/hosttech_dns_record.py
@@ -0,0 +1,111 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017-2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+DOCUMENTATION = '''
+---
+module: hosttech_dns_record
+
+short_description: Add or delete a single record in Hosttech DNS service
+
+version_added: 2.0.0
+
+description:
+ - "Creates and deletes single DNS records in Hosttech DNS service."
+ - This module replaces C(hosttech_dns_record) from community.dns before 2.0.0.
+ - If you do not want to add/remove values, but replace values, you will be interested in
+ modifying a B(record set) and not a single record. This is in particular important
+ when working with C(CNAME) and C(SOA) records.
+ Use the M(community.dns.hosttech_dns_record_set) module for working with record sets.
+
+extends_documentation_fragment:
+ - community.dns.hosttech
+ - community.dns.hosttech.record_default_ttl
+ - community.dns.hosttech.record_type_choices
+ - community.dns.hosttech.zone_id_type
+ - community.dns.module_record
+ - community.dns.options.record_transformation
+ - community.dns.attributes
+ - community.dns.attributes.actiongroup_hosttech
+
+attributes:
+ action_group:
+ version_added: 2.4.0
+ check_mode:
+ support: full
+ diff_mode:
+ support: full
+
+author:
+ - Felix Fontein (@felixfontein)
+'''
+
+EXAMPLES = '''
+- name: Add a new.foo.com A record
+ community.dns.hosttech_dns_record:
+ state: present
+ zone: foo.com
+ record: new.foo.com
+ type: A
+ ttl: 7200
+ value: 1.1.1.1
+ hosttech_token: access_token
+
+- name: Remove a new.foo.com A record
+ community.dns.hosttech_dns_record:
+ state: absent
+ zone_name: foo.com
+ record: new.foo.com
+ type: A
+ ttl: 7200
+ value: 2.2.2.2
+ hosttech_token: access_token
+'''
+
+RETURN = '''
+zone_id:
+ description: The ID of the zone.
+ type: int
+ returned: success
+ sample: 23
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ansible_collections.community.dns.plugins.module_utils.argspec import (
+ ModuleOptionProvider,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.http import (
+ ModuleHTTPHelper,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.hosttech.api import (
+ create_hosttech_argument_spec,
+ create_hosttech_api,
+ create_hosttech_provider_information,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.module.record import (
+ create_module_argument_spec,
+ run_module,
+)
+
+
+def main():
+ provider_information = create_hosttech_provider_information()
+ argument_spec = create_hosttech_argument_spec()
+ argument_spec.merge(create_module_argument_spec(provider_information=provider_information))
+ module = AnsibleModule(supports_check_mode=True, **argument_spec.to_kwargs())
+ run_module(module, lambda: create_hosttech_api(ModuleOptionProvider(module), ModuleHTTPHelper(module)), provider_information=provider_information)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/dns/plugins/modules/hosttech_dns_record_info.py b/ansible_collections/community/dns/plugins/modules/hosttech_dns_record_info.py
new file mode 100644
index 000000000..84ee8e3b2
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/modules/hosttech_dns_record_info.py
@@ -0,0 +1,133 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+DOCUMENTATION = '''
+---
+module: hosttech_dns_record_info
+
+short_description: Retrieve records in Hosttech DNS service
+
+version_added: 2.0.0
+
+description:
+ - "Retrieves DNS records in Hosttech DNS service."
+
+extends_documentation_fragment:
+ - community.dns.hosttech
+ - community.dns.hosttech.record_type_choices
+ - community.dns.hosttech.zone_id_type
+ - community.dns.module_record_info
+ - community.dns.options.record_transformation
+ - community.dns.attributes
+ - community.dns.attributes.actiongroup_hosttech
+ - community.dns.attributes.info_module
+
+attributes:
+ action_group:
+ version_added: 2.4.0
+
+author:
+ - Felix Fontein (@felixfontein)
+'''
+
+EXAMPLES = '''
+- name: Retrieve the details for the A records of new.foo.com
+ community.dns.hosttech_dns_record_info:
+ zone_name: foo.com
+ record: new.foo.com
+ type: A
+ hosttech_token: access_token
+ register: rec
+
+- name: Print the A records
+ ansible.builtin.debug:
+ msg: "{{ rec.records }}"
+'''
+
+RETURN = '''
+records:
+ description: The list of fetched records.
+ type: list
+ elements: dict
+ returned: success and I(what) is not C(single_record)
+ contains:
+ record:
+ description: The record name.
+ type: str
+ sample: sample.example.com
+ prefix:
+ description: The record prefix.
+ type: str
+ sample: sample
+ type:
+ description: The DNS record type.
+ type: str
+ sample: A
+ ttl:
+ description:
+ - The TTL.
+ type: int
+ sample: 3600
+ value:
+ description: The DNS record's value.
+ type: str
+ sample: 1.2.3.4
+ extra:
+ description: Extra information on records.
+ type: dict
+ sample:
+ comment: ''
+ sample:
+ - record: sample.example.com
+ type: A
+ ttl: 3600
+ value: 1.2.3.4
+ extra: {}
+
+zone_id:
+ description: The ID of the zone.
+ type: int
+ returned: success
+ sample: 23
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ansible_collections.community.dns.plugins.module_utils.argspec import (
+ ModuleOptionProvider,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.http import (
+ ModuleHTTPHelper,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.hosttech.api import (
+ create_hosttech_argument_spec,
+ create_hosttech_api,
+ create_hosttech_provider_information,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.module.record_info import (
+ run_module,
+ create_module_argument_spec,
+)
+
+
+def main():
+ provider_information = create_hosttech_provider_information()
+ argument_spec = create_hosttech_argument_spec()
+ argument_spec.merge(create_module_argument_spec(provider_information=provider_information))
+ module = AnsibleModule(supports_check_mode=True, **argument_spec.to_kwargs())
+ run_module(module, lambda: create_hosttech_api(ModuleOptionProvider(module), ModuleHTTPHelper(module)), provider_information=provider_information)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/dns/plugins/modules/hosttech_dns_record_set.py b/ansible_collections/community/dns/plugins/modules/hosttech_dns_record_set.py
new file mode 100644
index 000000000..d16c82ad7
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/modules/hosttech_dns_record_set.py
@@ -0,0 +1,233 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017-2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+DOCUMENTATION = '''
+---
+module: hosttech_dns_record_set
+
+short_description: Add or delete record sets in Hosttech DNS service
+
+version_added: 2.0.0
+
+description:
+ - "Creates and deletes DNS record sets in Hosttech DNS service."
+ - This module replaces C(hosttech_dns_record) from community.dns before 2.0.0.
+
+extends_documentation_fragment:
+ - community.dns.hosttech
+ - community.dns.hosttech.record_default_ttl
+ - community.dns.hosttech.record_type_choices
+ - community.dns.hosttech.zone_id_type
+ - community.dns.module_record_set
+ - community.dns.options.record_transformation
+ - community.dns.attributes
+ - community.dns.attributes.actiongroup_hosttech
+
+attributes:
+ action_group:
+ version_added: 2.4.0
+ check_mode:
+ support: full
+ diff_mode:
+ support: full
+
+author:
+ - Felix Fontein (@felixfontein)
+'''
+
+EXAMPLES = '''
+- name: Add new.foo.com as an A record with 3 IPs
+ community.dns.hosttech_dns_record_set:
+ state: present
+ zone_name: foo.com
+ record: new.foo.com
+ type: A
+ ttl: 7200
+ value: 1.1.1.1,2.2.2.2,3.3.3.3
+ hosttech_token: access_token
+
+- name: Update new.foo.com as an A record with a list of 3 IPs
+ community.dns.hosttech_dns_record_set:
+ state: present
+ zone_name: foo.com
+ record: new.foo.com
+ type: A
+ ttl: 7200
+ value:
+ - 1.1.1.1
+ - 2.2.2.2
+ - 3.3.3.3
+ hosttech_token: access_token
+
+- name: Retrieve the details for new.foo.com
+ community.dns.hosttech_dns_record_set_info:
+ zone_name: foo.com
+ record: new.foo.com
+ type: A
+ hosttech_username: foo
+ hosttech_password: bar
+ register: rec
+
+- name: Delete new.foo.com A record using the results from the facts retrieval command
+ community.dns.hosttech_dns_record_set:
+ state: absent
+ zone_name: foo.com
+ record: "{{ rec.set.record }}"
+ ttl: "{{ rec.set.ttl }}"
+ type: "{{ rec.set.type }}"
+ value: "{{ rec.set.value }}"
+ hosttech_username: foo
+ hosttech_password: bar
+
+- name: Add an AAAA record
+ # Note that because there are colons in the value that the IPv6 address must be quoted!
+ community.dns.hosttech_dns_record_set:
+ state: present
+ zone_name: foo.com
+ record: localhost.foo.com
+ type: AAAA
+ ttl: 7200
+ value: "::1"
+ hosttech_token: access_token
+
+- name: Add a TXT record
+ community.dns.hosttech_dns_record_set:
+ state: present
+ zone_name: foo.com
+ record: localhost.foo.com
+ type: TXT
+ ttl: 7200
+ value: 'bar'
+ hosttech_username: foo
+ hosttech_password: bar
+
+- name: Remove the TXT record
+ community.dns.hosttech_dns_record_set:
+ state: absent
+ zone_name: foo.com
+ record: localhost.foo.com
+ type: TXT
+ ttl: 7200
+ value: 'bar'
+ hosttech_username: foo
+ hosttech_password: bar
+
+- name: Add a CAA record
+ community.dns.hosttech_dns_record_set:
+ state: present
+ zone_name: foo.com
+ record: foo.com
+ type: CAA
+ ttl: 3600
+ value:
+ - "128 issue letsencrypt.org"
+ - "128 iodef mailto:webmaster@foo.com"
+ hosttech_token: access_token
+
+- name: Add an MX record
+ community.dns.hosttech_dns_record_set:
+ state: present
+ zone_name: foo.com
+ record: foo.com
+ type: MX
+ ttl: 3600
+ value:
+ - "10 mail.foo.com"
+ hosttech_token: access_token
+
+- name: Add a CNAME record
+ community.dns.hosttech_dns_record_set:
+ state: present
+ zone_name: bla.foo.com
+ record: foo.com
+ type: CNAME
+ ttl: 3600
+ value:
+ - foo.foo.com
+ hosttech_username: foo
+ hosttech_password: bar
+
+- name: Add a PTR record
+ community.dns.hosttech_dns_record_set:
+ state: present
+ zone_name: foo.foo.com
+ record: foo.com
+ type: PTR
+ ttl: 3600
+ value:
+ - foo.foo.com
+ hosttech_token: access_token
+
+- name: Add an SPF record
+ community.dns.hosttech_dns_record_set:
+ state: present
+ zone_name: foo.com
+ record: foo.com
+ type: SPF
+ ttl: 3600
+ value:
+ - "v=spf1 a mx ~all"
+ hosttech_username: foo
+ hosttech_password: bar
+
+- name: Add a PTR record
+ community.dns.hosttech_dns_record_set:
+ state: present
+ zone_name: foo.com
+ record: foo.com
+ type: PTR
+ ttl: 3600
+ value:
+ - "10 100 3333 service.foo.com"
+ hosttech_token: access_token
+'''
+
+RETURN = '''
+zone_id:
+ description: The ID of the zone.
+ type: int
+ returned: success
+ sample: 23
+ version_added: 0.2.0
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ansible_collections.community.dns.plugins.module_utils.argspec import (
+ ModuleOptionProvider,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.http import (
+ ModuleHTTPHelper,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.hosttech.api import (
+ create_hosttech_argument_spec,
+ create_hosttech_api,
+ create_hosttech_provider_information,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.module.record_set import (
+ create_module_argument_spec,
+ run_module,
+)
+
+
+def main():
+ provider_information = create_hosttech_provider_information()
+ argument_spec = create_hosttech_argument_spec()
+ argument_spec.merge(create_module_argument_spec(provider_information=provider_information))
+ module = AnsibleModule(supports_check_mode=True, **argument_spec.to_kwargs())
+ run_module(module, lambda: create_hosttech_api(ModuleOptionProvider(module), ModuleHTTPHelper(module)), provider_information=provider_information)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/dns/plugins/modules/hosttech_dns_record_set_info.py b/ansible_collections/community/dns/plugins/modules/hosttech_dns_record_set_info.py
new file mode 100644
index 000000000..5b7c576b8
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/modules/hosttech_dns_record_set_info.py
@@ -0,0 +1,198 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017-2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+DOCUMENTATION = '''
+---
+module: hosttech_dns_record_set_info
+
+short_description: Retrieve record sets in Hosttech DNS service
+
+version_added: 0.1.0
+
+description:
+ - "Retrieves DNS record sets in Hosttech DNS service."
+ - This module was renamed from C(community.dns.hosttech_dns_record_info) to C(community.dns.hosttech_dns_record_set_info)
+ in community.dns 2.0.0.
+
+extends_documentation_fragment:
+ - community.dns.hosttech
+ - community.dns.hosttech.record_type_choices
+ - community.dns.hosttech.zone_id_type
+ - community.dns.module_record_set_info
+ - community.dns.options.record_transformation
+ - community.dns.attributes
+ - community.dns.attributes.actiongroup_hosttech
+ - community.dns.attributes.info_module
+
+attributes:
+ action_group:
+ version_added: 2.4.0
+
+author:
+ - Felix Fontein (@felixfontein)
+'''
+
+EXAMPLES = '''
+- name: Retrieve the details for the A records of new.foo.com
+ community.dns.hosttech_dns_record_set_info:
+ zone_name: foo.com
+ record: new.foo.com
+ type: A
+ hosttech_token: access_token
+ register: rec
+
+- name: Print the A record set
+ ansible.builtin.debug:
+ msg: "{{ rec.set }}"
+'''
+
+RETURN = '''
+set:
+ description: The fetched record set. Is empty if record set does not exist.
+ type: dict
+ returned: success and I(what) is C(single_record)
+ contains:
+ record:
+ description: The record name.
+ type: str
+ sample: sample.example.com
+ prefix:
+ description: The record prefix.
+ type: str
+ sample: sample
+ version_added: 0.2.0
+ type:
+ description: The DNS record type.
+ type: str
+ sample: A
+ ttl:
+ description:
+ - The TTL.
+ - If there are records in this set with different TTLs, the minimum of the TTLs will be presented here.
+ type: int
+ sample: 3600
+ ttls:
+ description:
+ - If there are records with different TTL values in this set, this will be the list of TTLs appearing
+ in the records.
+ - Every distinct TTL will appear once, and the TTLs are in ascending order.
+ returned: When there is more than one distinct TTL
+ type: list
+ elements: int
+ sample:
+ - 300
+ - 3600
+ value:
+ description: The DNS record set's value.
+ type: list
+ elements: str
+ sample:
+ - 1.2.3.4
+ - 1.2.3.5
+ sample:
+ record: sample.example.com
+ type: A
+ ttl: 3600
+ value:
+ - 1.2.3.4
+ - 1.2.3.5
+
+sets:
+ description: The list of fetched record sets.
+ type: list
+ elements: dict
+ returned: success and I(what) is not C(single_record)
+ contains:
+ record:
+ description: The record name.
+ type: str
+ sample: sample.example.com
+ prefix:
+ description: The record prefix.
+ type: str
+ sample: sample
+ version_added: 0.2.0
+ type:
+ description: The DNS record type.
+ type: str
+ sample: A
+ ttl:
+ description:
+ - The TTL.
+ - If there are records in this set with different TTLs, the minimum of the TTLs will be presented here.
+ type: int
+ sample: 3600
+ ttls:
+ description:
+ - If there are records with different TTL values in this set, this will be the list of TTLs appearing
+ in the records.
+ - Every distinct TTL will appear once, and the TTLs are in ascending order.
+ returned: When there is more than one distinct TTL
+ type: list
+ elements: int
+ sample:
+ - 300
+ - 3600
+ value:
+ description: The DNS record set's value.
+ type: list
+ elements: str
+ sample:
+ - 1.2.3.4
+ - 1.2.3.5
+ sample:
+ - record: sample.example.com
+ type: A
+ ttl: 3600
+ value:
+ - 1.2.3.4
+ - 1.2.3.5
+
+zone_id:
+ description: The ID of the zone.
+ type: int
+ returned: success
+ sample: 23
+ version_added: 0.2.0
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ansible_collections.community.dns.plugins.module_utils.argspec import (
+ ModuleOptionProvider,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.http import (
+ ModuleHTTPHelper,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.hosttech.api import (
+ create_hosttech_argument_spec,
+ create_hosttech_api,
+ create_hosttech_provider_information,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.module.record_set_info import (
+ run_module,
+ create_module_argument_spec,
+)
+
+
+def main():
+ provider_information = create_hosttech_provider_information()
+ argument_spec = create_hosttech_argument_spec()
+ argument_spec.merge(create_module_argument_spec(provider_information=provider_information))
+ module = AnsibleModule(supports_check_mode=True, **argument_spec.to_kwargs())
+ run_module(module, lambda: create_hosttech_api(ModuleOptionProvider(module), ModuleHTTPHelper(module)), provider_information=provider_information)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/dns/plugins/modules/hosttech_dns_record_sets.py b/ansible_collections/community/dns/plugins/modules/hosttech_dns_record_sets.py
new file mode 100644
index 000000000..c985779ca
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/modules/hosttech_dns_record_sets.py
@@ -0,0 +1,128 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017-2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+DOCUMENTATION = '''
+---
+module: hosttech_dns_record_sets
+
+short_description: Bulk synchronize DNS record sets in Hosttech DNS service
+
+version_added: 2.0.0
+
+description:
+ - Bulk synchronize DNS record sets in Hosttech DNS service.
+ - This module replaces C(hosttech_dns_records) from community.dns before 2.0.0.
+
+extends_documentation_fragment:
+ - community.dns.hosttech
+ - community.dns.hosttech.record_type_choices_record_sets_module
+ - community.dns.hosttech.zone_id_type
+ - community.dns.module_record_sets
+ - community.dns.options.record_transformation
+ - community.dns.attributes
+ - community.dns.attributes.actiongroup_hosttech
+
+attributes:
+ action_group:
+ version_added: 2.4.0
+ check_mode:
+ support: full
+ diff_mode:
+ support: full
+
+author:
+ - Felix Fontein (@felixfontein)
+
+'''
+
+EXAMPLES = '''
+- name: Make sure some records exist and have the expected values
+ community.dns.hosttech_dns_record_sets:
+ zone_name: foo.com
+ records:
+ - prefix: new
+ type: A
+ ttl: 7200
+ value:
+ - 1.1.1.1
+ - 2.2.2.2
+ - prefix: new
+ type: AAAA
+ ttl: 7200
+ value:
+ - "::1"
+ - record: foo.com
+ type: TXT
+ value:
+ - test
+ hosttech_token: access_token
+
+- name: Synchronize DNS zone with a fixed set of records
+ # If a record exists that is not mentioned here, it will be deleted
+ community.dns.hosttech_dns_record_sets:
+ zone_id: 23
+ purge: true
+ records:
+ - prefix: ''
+ type: A
+ value: 127.0.0.1
+ - prefix: ''
+ type: AAAA
+ value: "::1"
+ - prefix: ''
+ type: NS
+ value:
+ - ns-1.hoster.com
+ - ns-2.hoster.com
+ - ns-3.hoster.com
+ hosttech_token: access_token
+'''
+
+RETURN = '''
+zone_id:
+ description: The ID of the zone.
+ type: int
+ returned: success
+ sample: 23
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ansible_collections.community.dns.plugins.module_utils.argspec import (
+ ModuleOptionProvider,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.http import (
+ ModuleHTTPHelper,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.hosttech.api import (
+ create_hosttech_argument_spec,
+ create_hosttech_api,
+ create_hosttech_provider_information,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.module.record_sets import (
+ create_module_argument_spec,
+ run_module,
+)
+
+
+def main():
+ provider_information = create_hosttech_provider_information()
+ argument_spec = create_hosttech_argument_spec()
+ argument_spec.merge(create_module_argument_spec(provider_information=provider_information))
+ module = AnsibleModule(supports_check_mode=True, **argument_spec.to_kwargs())
+ run_module(module, lambda: create_hosttech_api(ModuleOptionProvider(module), ModuleHTTPHelper(module)), provider_information=provider_information)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/dns/plugins/modules/hosttech_dns_records.py b/ansible_collections/community/dns/plugins/modules/hosttech_dns_records.py
new file mode 100644
index 000000000..c985779ca
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/modules/hosttech_dns_records.py
@@ -0,0 +1,128 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017-2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+DOCUMENTATION = '''
+---
+module: hosttech_dns_record_sets
+
+short_description: Bulk synchronize DNS record sets in Hosttech DNS service
+
+version_added: 2.0.0
+
+description:
+ - Bulk synchronize DNS record sets in Hosttech DNS service.
+ - This module replaces C(hosttech_dns_records) from community.dns before 2.0.0.
+
+extends_documentation_fragment:
+ - community.dns.hosttech
+ - community.dns.hosttech.record_type_choices_record_sets_module
+ - community.dns.hosttech.zone_id_type
+ - community.dns.module_record_sets
+ - community.dns.options.record_transformation
+ - community.dns.attributes
+ - community.dns.attributes.actiongroup_hosttech
+
+attributes:
+ action_group:
+ version_added: 2.4.0
+ check_mode:
+ support: full
+ diff_mode:
+ support: full
+
+author:
+ - Felix Fontein (@felixfontein)
+
+'''
+
+EXAMPLES = '''
+- name: Make sure some records exist and have the expected values
+ community.dns.hosttech_dns_record_sets:
+ zone_name: foo.com
+ records:
+ - prefix: new
+ type: A
+ ttl: 7200
+ value:
+ - 1.1.1.1
+ - 2.2.2.2
+ - prefix: new
+ type: AAAA
+ ttl: 7200
+ value:
+ - "::1"
+ - record: foo.com
+ type: TXT
+ value:
+ - test
+ hosttech_token: access_token
+
+- name: Synchronize DNS zone with a fixed set of records
+ # If a record exists that is not mentioned here, it will be deleted
+ community.dns.hosttech_dns_record_sets:
+ zone_id: 23
+ purge: true
+ records:
+ - prefix: ''
+ type: A
+ value: 127.0.0.1
+ - prefix: ''
+ type: AAAA
+ value: "::1"
+ - prefix: ''
+ type: NS
+ value:
+ - ns-1.hoster.com
+ - ns-2.hoster.com
+ - ns-3.hoster.com
+ hosttech_token: access_token
+'''
+
+RETURN = '''
+zone_id:
+ description: The ID of the zone.
+ type: int
+ returned: success
+ sample: 23
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ansible_collections.community.dns.plugins.module_utils.argspec import (
+ ModuleOptionProvider,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.http import (
+ ModuleHTTPHelper,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.hosttech.api import (
+ create_hosttech_argument_spec,
+ create_hosttech_api,
+ create_hosttech_provider_information,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.module.record_sets import (
+ create_module_argument_spec,
+ run_module,
+)
+
+
+def main():
+ provider_information = create_hosttech_provider_information()
+ argument_spec = create_hosttech_argument_spec()
+ argument_spec.merge(create_module_argument_spec(provider_information=provider_information))
+ module = AnsibleModule(supports_check_mode=True, **argument_spec.to_kwargs())
+ run_module(module, lambda: create_hosttech_api(ModuleOptionProvider(module), ModuleHTTPHelper(module)), provider_information=provider_information)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/dns/plugins/modules/hosttech_dns_zone_info.py b/ansible_collections/community/dns/plugins/modules/hosttech_dns_zone_info.py
new file mode 100644
index 000000000..6621893a7
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/modules/hosttech_dns_zone_info.py
@@ -0,0 +1,185 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+DOCUMENTATION = '''
+---
+module: hosttech_dns_zone_info
+
+short_description: Retrieve zone information in Hosttech DNS service
+
+version_added: 0.2.0
+
+description:
+ - "Retrieves zone information in Hosttech DNS service."
+
+extends_documentation_fragment:
+ - community.dns.hosttech
+ - community.dns.hosttech.zone_id_type
+ - community.dns.module_zone_info
+ - community.dns.attributes
+ - community.dns.attributes.actiongroup_hosttech
+ - community.dns.attributes.info_module
+
+attributes:
+ action_group:
+ version_added: 2.4.0
+
+author:
+ - Felix Fontein (@felixfontein)
+'''
+
+EXAMPLES = '''
+- name: Retrieve details for foo.com zone
+ community.dns.hosttech_dns_zone_info:
+ zone_name: foo.com
+ hosttech_username: foo
+ hosttech_password: bar
+ register: rec
+
+- name: Retrieve details for zone 23
+ community.dns.hosttech_dns_zone_info:
+ zone_id: 23
+ hosttech_token: access_token
+'''
+
+RETURN = '''
+zone_name:
+ description: The name of the zone.
+ type: int
+ returned: success
+ sample: example.com
+
+zone_id:
+ description: The ID of the zone.
+ type: int
+ returned: success
+ sample: 23
+
+zone_info:
+ description:
+ - Extra information returned by the API.
+ type: dict
+ returned: success
+ version_added: 2.0.0
+ sample:
+ dnssec: true
+ dnssec_email: test@example.com
+ ds_records: []
+ email: test@example.com
+ ttl: 3600
+ contains:
+ dnssec:
+ description:
+ - Whether DNSSEC is enabled for the zone or not.
+ type: bool
+ returned: When I(hosttech_token) has been specified.
+ dnssec_email:
+ description:
+ - The email address contacted when the DNSSEC key is changed.
+ - Is C(none) if DNSSEC is not enabled.
+ type: str
+ returned: When I(hosttech_token) has been specified.
+ ds_records:
+ description:
+ - The DS records.
+ - See L(Section 5 of RFC 4034,https://datatracker.ietf.org/doc/html/rfc4034#section-5) and
+ L(Section 2.1 of RFC 4034,https://datatracker.ietf.org/doc/html/rfc4034#section-2.1) for details.
+ - Is C(none) if DNSSEC is not enabled.
+ type: list
+ elements: dict
+ returned: When I(hosttech_token) has been specified.
+ contains:
+ algorithm:
+ description:
+ - This value is the algorithm number of the DNSKEY RR referred to by the DS record.
+ - A list of values can be found in L(Appendix A.1 of RFC 4034,https://datatracker.ietf.org/doc/html/rfc4034#appendix-A.1).
+ type: int
+ sample: 8
+ digest:
+ description:
+ - A digest of the DNSKEY RR record this DS record refers to.
+ type: str
+ sample: 012356789ABCDEF0123456789ABCDEF012345678
+ digest_type:
+ description:
+ - This value identifies the algorithm used to construct the digest.
+ - A list of values can be found in L(Appendix A.2 of RFC 4034,https://datatracker.ietf.org/doc/html/rfc4034#appendix-A.2).
+ type: int
+ sample: 1
+ flags:
+ description:
+ - The Zone Key flag. See L(Section 2.1.1 of RFC 4034,https://datatracker.ietf.org/doc/html/rfc4034#section-2.1.1) for details.
+ type: int
+ sample: 257
+ key_tag:
+ description:
+ - The Key Tag field lists the key tag of the DNSKEY RR referred to by the DS record.
+ type: int
+ sample: 12345
+ protocol:
+ description:
+ - Must be 3 according to RFC 4034.
+ type: int
+ sample: 3
+ public_key:
+ description:
+ - The public key material.
+ type: str
+ sample: >-
+ MuhdzsQdqEGShwjtJDKZZjdKqUSGluFzTTinpuEeIRzLLcgkwgAPKWFa
+ eQntNlmcNDeCziGwpdvhJnvKXEMbFcZwsaDIJuWqERxAQNGABWfPlCLh
+ HQPnbpRPNKipSdBaUhuOubvFvjBpFAwiwSAapRDVsAgKvjXucfXpFfYb
+ pCundbAXBWhbpHVbqgmGoixXzFSwUsGVYLPpBCiDlLJwzjRKYYaoVYge
+ kMtKFYUVnWIKbectWkDFdVqXwkKigCUDiuTTJxOBRJRNzGiDNMWBjYSm
+ bBCAHMaMYaghLbYTwyKXltdHTHwBwtswGNfpnEdSpKFzZJonBZArQfHD
+ lfceKgmKwEF=
+ email:
+ description:
+ - The zone's DNS contact mail in the SOA record.
+ type: str
+ ttl:
+ description:
+ - The zone's TTL.
+ type: int
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ansible_collections.community.dns.plugins.module_utils.argspec import (
+ ModuleOptionProvider,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.http import (
+ ModuleHTTPHelper,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.hosttech.api import (
+ create_hosttech_argument_spec,
+ create_hosttech_api,
+ create_hosttech_provider_information,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.module.zone_info import (
+ run_module,
+ create_module_argument_spec,
+)
+
+
+def main():
+ provider_information = create_hosttech_provider_information()
+ argument_spec = create_hosttech_argument_spec()
+ argument_spec.merge(create_module_argument_spec(provider_information=provider_information))
+ module = AnsibleModule(supports_check_mode=True, **argument_spec.to_kwargs())
+ run_module(module, lambda: create_hosttech_api(ModuleOptionProvider(module), ModuleHTTPHelper(module)), provider_information=provider_information)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/dns/plugins/modules/wait_for_txt.py b/ansible_collections/community/dns/plugins/modules/wait_for_txt.py
new file mode 100644
index 000000000..1a09c5ed6
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/modules/wait_for_txt.py
@@ -0,0 +1,335 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2021, Felix Fontein <felix@fontein.de>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+DOCUMENTATION = r'''
+---
+module: wait_for_txt
+short_description: Wait for TXT entries to be available on all authoritative nameservers
+version_added: 0.1.0
+description:
+ - Wait for TXT entries with specific values to show up on B(all) authoritative nameservers for the DNS name.
+extends_documentation_fragment:
+ - community.dns.attributes
+attributes:
+ check_mode:
+ support: full
+ details:
+ - This action does not modify state.
+ version_added: 2.4.0
+ diff_mode:
+ support: N/A
+ details:
+ - This action does not modify state.
+author:
+ - Felix Fontein (@felixfontein)
+options:
+ records:
+ description:
+ - A list of DNS names with TXT entries to look out for.
+ required: true
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - A DNS name, like C(www.example.com).
+ type: str
+ required: true
+ values:
+ description:
+ - The TXT values to look for.
+ type: list
+ elements: str
+ required: true
+ mode:
+ description:
+ - Comparison modes for the values in I(values).
+ - If C(subset), I(values) should be a (not necessarily proper) subset of the TXT values set for
+ the DNS name.
+ - If C(superset), I(values) should be a (not necessarily proper) superset of the TXT values set
+ for the DNS name.
+ This includes the case that no TXT entries are set.
+ - If C(superset_not_empty), I(values) should be a (not necessarily proper) superset of the TXT
+ values set for the DNS name, assuming at least one TXT record is present.
+ - If C(equals), I(values) should be the same set of strings as the TXT values for the DNS name
+ (up to order).
+ - If C(equals_ordered), I(values) should be the same ordered list of strings as the TXT values
+ for the DNS name.
+ type: str
+ default: subset
+ choices:
+ - subset
+ - superset
+ - superset_not_empty
+ - equals
+ - equals_ordered
+ query_retry:
+ description:
+ - Number of retries for DNS query timeouts.
+ type: int
+ default: 3
+ query_timeout:
+ description:
+ - Timeout per DNS query in seconds.
+ type: float
+ default: 10
+ timeout:
+ description:
+ - Global timeout for waiting for all records in seconds.
+ - If not set, will wait indefinitely.
+ type: float
+ max_sleep:
+ description:
+ - Maximal amount of seconds to sleep between two rounds of probing the TXT records.
+ type: float
+ default: 10
+ always_ask_default_resolver:
+ description:
+ - When set to C(true) (default), will use the default resolver to find the authoritative nameservers
+ of a subzone.
+ - When set to C(false), will use the authoritative nameservers of the parent zone to find the
+ authoritative nameservers of a subzone. This only makes sense when the nameservers were recently
+ changed and haven't propagated.
+ type: bool
+ default: true
+requirements:
+ - dnspython >= 1.15.0 (maybe older versions also work)
+'''
+
+EXAMPLES = r'''
+- name: Wait for a TXT entry to appear
+ community.dns.wait_for_txt:
+ records:
+ # We want that www.example.com has a single TXT record with value 'Hello world!'.
+ # There should not be any other TXT record for www.example.com.
+ - name: www.example.com
+ values: "Hello world!"
+ mode: equals
+ # We want that example.com has a specific SPF record set.
+ # We do not care about other TXT records.
+ - name: www.example.com
+ values: "v=spf1 a mx -all"
+ mode: subset
+'''
+
+RETURN = r'''
+records:
+ description:
+ - Results on the TXT records queried.
+ - The entries are in a 1:1 correspondence to the entries of the I(records) parameter,
+ in exactly the same order.
+ returned: always
+ type: list
+ elements: dict
+ contains:
+ name:
+ description:
+ - The DNS name this check is for.
+ returned: always
+ type: str
+ sample: example.com
+ done:
+ description:
+ - Whether the check completed.
+ returned: always
+ type: bool
+ sample: false
+ values:
+ description:
+ - For every authoritative nameserver for the DNS name, lists the TXT records retrieved during the last lookup made.
+ - Once the check completed for all TXT records retrieved, the TXT records for this DNS name are no longer checked.
+ - If these are multiple TXT entries for a nameserver, the order is as it was received from that nameserver. This
+ might not be the same order provided in the check.
+ returned: lookup was done at least once
+ type: dict
+ elements: list
+ sample:
+ ns1.example.com:
+ - TXT value 1
+ - TXT value 2
+ ns2.example.com:
+ - TXT value 2
+ check_count:
+ description:
+ - How often the TXT records for this DNS name were checked.
+ returned: always
+ type: int
+ sample: 3
+ sample:
+ - name: example.com
+ done: true
+ values: [a, b, c]
+ check_count: 1
+ - name: foo.example.org
+ done: false
+ check_count: 0
+completed:
+ description:
+ - How many of the checks were completed.
+ returned: always
+ type: int
+ sample: 3
+'''
+
+import time
+import traceback
+
+try:
+ from time import monotonic
+except ImportError:
+ from time import clock as monotonic
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.common.text.converters import to_native, to_text
+
+from ansible_collections.community.dns.plugins.module_utils.resolver import (
+ ResolveDirectlyFromNameServers,
+ ResolverError,
+ assert_requirements_present,
+)
+
+try:
+ import dns.exception
+ import dns.rdatatype
+except ImportError:
+ pass # handled in assert_requirements_present()
+
+
+def lookup(resolver, name):
+ result = {}
+ txts = resolver.resolve(name, rdtype=dns.rdatatype.TXT)
+ for key, txt in txts.items():
+ res = []
+ if txt is not None:
+ for data in txt:
+ line = []
+ for str in data.strings:
+ line.append(to_text(str))
+ res.append(u''.join(line))
+ result[key] = res
+ txts[key] = []
+ return result
+
+
+def validate_check(record_values, expected_values, comparison_mode):
+ if comparison_mode == 'subset':
+ return set(expected_values) <= set(record_values)
+
+ if comparison_mode == 'superset':
+ return set(expected_values) >= set(record_values)
+
+ if comparison_mode == 'superset_not_empty':
+ return bool(record_values) and set(expected_values) >= set(record_values)
+
+ if comparison_mode == 'equals':
+ return sorted(record_values) == sorted(expected_values)
+
+ if comparison_mode == 'equals_ordered':
+ return record_values == expected_values
+
+ raise Exception('Internal error!')
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ records=dict(required=True, type='list', elements='dict', options=dict(
+ name=dict(required=True, type='str'),
+ values=dict(required=True, type='list', elements='str'),
+ mode=dict(type='str', default='subset', choices=['subset', 'superset', 'superset_not_empty', 'equals', 'equals_ordered']),
+ )),
+ query_retry=dict(type='int', default=3),
+ query_timeout=dict(type='float', default=10),
+ timeout=dict(type='float'),
+ max_sleep=dict(type='float', default=10),
+ always_ask_default_resolver=dict(type='bool', default=True),
+ ),
+ supports_check_mode=True,
+ )
+ assert_requirements_present(module)
+
+ resolver = ResolveDirectlyFromNameServers(
+ timeout=module.params['query_timeout'],
+ timeout_retries=module.params['query_retry'],
+ always_ask_default_resolver=module.params['always_ask_default_resolver'],
+ )
+ records = module.params['records']
+ timeout = module.params['timeout']
+ max_sleep = module.params['max_sleep']
+
+ results = [None] * len(records)
+ for index in range(len(records)):
+ results[index] = {
+ 'name': records[index]['name'],
+ 'done': False,
+ 'check_count': 0,
+ }
+ finished_checks = 0
+
+ start_time = monotonic()
+ try:
+ step = 0
+ while True:
+ has_timeout = False
+ if timeout is not None:
+ expired = monotonic() - start_time
+ has_timeout = expired > timeout
+
+ done = True
+ for index, record in enumerate(records):
+ if results[index]['done']:
+ continue
+ txts = lookup(resolver, record['name'])
+ results[index]['values'] = txts
+ results[index]['check_count'] += 1
+ if txts and all(validate_check(txt, record['values'], record['mode']) for txt in txts.values()):
+ results[index]['done'] = True
+ finished_checks += 1
+ else:
+ done = False
+
+ if done:
+ module.exit_json(
+ msg='All checks passed',
+ records=results,
+ completed=finished_checks)
+
+ if has_timeout:
+ module.fail_json(
+ msg='Timeout ({0} out of {1} check(s) passed).'.format(finished_checks, len(records)),
+ records=results,
+ completed=finished_checks)
+
+ # Simple quadratic sleep with maximum wait of max_sleep seconds
+ wait = min(2 + step * 0.5, max_sleep)
+ if timeout is not None:
+ # Make sure we do not exceed the timeout by much by waiting
+ expired = monotonic() - start_time
+ wait = max(min(wait, timeout - expired + 0.1), 0.1)
+
+ time.sleep(wait)
+ step += 1
+ except ResolverError as e:
+ module.fail_json(
+ msg='Unexpected resolving error: {0}'.format(to_native(e)),
+ records=results,
+ completed=finished_checks,
+ exception=traceback.format_exc())
+ except dns.exception.DNSException as e:
+ module.fail_json(
+ msg='Unexpected DNS error: {0}'.format(to_native(e)),
+ records=results,
+ completed=finished_checks,
+ exception=traceback.format_exc())
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/community/dns/plugins/plugin_utils/inventory/records.py b/ansible_collections/community/dns/plugins/plugin_utils/inventory/records.py
new file mode 100644
index 000000000..461a92b35
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/plugin_utils/inventory/records.py
@@ -0,0 +1,124 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Felix Fontein
+# Copyright (c) 2020 Markus Bergholz <markuman+spambelongstogoogle@gmail.com>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import abc
+
+from ansible.errors import AnsibleError
+from ansible.module_utils import six
+from ansible.module_utils.common._collections_compat import Sequence
+from ansible.plugins.inventory import BaseInventoryPlugin
+from ansible.utils.display import Display
+from ansible.template import Templar
+
+from ansible_collections.community.dns.plugins.module_utils.provider import (
+ ensure_type,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.zone_record_api import (
+ DNSAPIError,
+ DNSAPIAuthenticationError,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.conversion.base import (
+ DNSConversionError,
+)
+
+from ansible_collections.community.dns.plugins.module_utils.conversion.converter import (
+ RecordConverter,
+)
+
+display = Display()
+
+
+@six.add_metaclass(abc.ABCMeta)
+class RecordsInventoryModule(BaseInventoryPlugin):
+ VALID_ENDINGS = ('dns.yaml', 'dns.yml')
+
+ def __init__(self):
+ super(RecordsInventoryModule, self).__init__()
+
+ @abc.abstractmethod
+ def setup_api(self):
+ """
+ This function needs to set up self.provider_information and self.api.
+ It can indicate errors by raising DNSAPIError.
+ """
+
+ def verify_file(self, path):
+ if super(RecordsInventoryModule, self).verify_file(path):
+ if path.endswith(self.VALID_ENDINGS):
+ return True
+ else:
+ display.debug("{name} inventory filename must end with {endings}".format(
+ name=self.NAME,
+ endings=' or '.join(["'{0}'".format(ending) for ending in self.VALID_ENDINGS])
+ ))
+ return False
+
+ def parse(self, inventory, loader, path, cache=False):
+ super(RecordsInventoryModule, self).parse(inventory, loader, path, cache)
+
+ self._read_config_data(path)
+
+ self.templar = Templar(loader=loader)
+
+ try:
+ self.setup_api()
+ self.record_converter = RecordConverter(self.provider_information, self)
+ self.record_converter.emit_deprecations(display.deprecated)
+
+ zone_name = self.get_option('zone_name')
+ if self.templar.is_template(zone_name):
+ zone_name = self.templar.template(variable=zone_name, disable_lookups=False)
+ zone_id = self.get_option('zone_id')
+ if zone_id is not None:
+ if self.templar.is_template(zone_id):
+ zone_id = self.templar.template(variable=zone_id, disable_lookups=False)
+ # For templating, we need to make the zone_id type 'string' or 'raw'.
+ # This converts the value to its proper type expected by the API.
+ zone_id_type = self.provider_information.get_record_id_type()
+ try:
+ zone_id = ensure_type(zone_id, zone_id_type)
+ except TypeError as exc:
+ raise AnsibleError(u'Error while ensuring that zone_id is of type {0}: {1}'.format(zone_id_type, exc))
+
+ if zone_name is not None:
+ zone_with_records = self.api.get_zone_with_records_by_name(zone_name)
+ elif zone_id is not None:
+ zone_with_records = self.api.get_zone_with_records_by_id(zone_id)
+ else:
+ raise AnsibleError('One of zone_name and zone_id must be specified!')
+
+ if zone_with_records is None:
+ raise AnsibleError('Zone does not exist')
+
+ self.record_converter.process_multiple_from_api(zone_with_records.records)
+ self.record_converter.process_multiple_to_user(zone_with_records.records)
+
+ except DNSConversionError as e:
+ raise AnsibleError(u'Error while converting DNS values: {0}'.format(e.error_message))
+ except DNSAPIAuthenticationError as e:
+ raise AnsibleError('Cannot authenticate: %s' % e)
+ except DNSAPIError as e:
+ raise AnsibleError('Error: %s' % e)
+
+ filters = self.get_option('filters')
+
+ filter_types = filters.get('type') or ['A', 'AAAA', 'CNAME']
+ if not isinstance(filter_types, Sequence) or isinstance(filter_types, six.string_types):
+ filter_types = [filter_types]
+
+ for record in zone_with_records.records:
+ if record.type in filter_types:
+ name = zone_with_records.zone.name
+ if record.prefix:
+ name = '%s.%s' % (record.prefix, name)
+ self.inventory.add_host(name)
+ self.inventory.set_variable(name, 'ansible_host', record.target)
diff --git a/ansible_collections/community/dns/plugins/plugin_utils/public_suffix.py b/ansible_collections/community/dns/plugins/plugin_utils/public_suffix.py
new file mode 100644
index 000000000..1388bbd36
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/plugin_utils/public_suffix.py
@@ -0,0 +1,214 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2021, Felix Fontein <felix@fontein.de>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import os.path
+import re
+
+from ansible_collections.community.dns.plugins.module_utils.names import InvalidDomainName, split_into_labels, normalize_label
+
+
+_BEGIN_SUBSET_MATCHER = re.compile(r'===BEGIN ([^=]*) DOMAINS===')
+_END_SUBSET_MATCHER = re.compile(r'===END ([^=]*) DOMAINS===')
+
+
+class PublicSuffixEntry(object):
+ '''
+ Contains a Public Suffix List entry with metadata.
+ '''
+
+ def __init__(self, labels, exception_rule=False, part=None):
+ self.labels = labels
+ self.exception_rule = exception_rule
+ self.part = part
+
+ def matches(self, normalized_labels):
+ '''
+ Match PSL entry with a given normalized list of labels.
+ '''
+ if len(normalized_labels) < len(self.labels):
+ return False
+ for i, label in enumerate(self.labels):
+ normalized_label = normalized_labels[i]
+ if label not in (normalized_label, '*'):
+ return False
+ return True
+
+
+def select_prevailing_rule(rules):
+ '''
+ Given a non-empty set of rules matching a domain name, finds the prevailing rule.
+
+ It uses the algorithm specified on https://publicsuffix.org/list/.
+ '''
+ max_length_rule = rules[0]
+ max_length = len(max_length_rule.labels)
+ for rule in rules:
+ if rule.exception_rule:
+ return rule
+ if len(rule.labels) > max_length:
+ max_length = len(rule.labels)
+ max_length_rule = rule
+ return max_length_rule
+
+
+class PublicSuffixList(object):
+ '''
+ Contains the Public Suffix List.
+ '''
+
+ def __init__(self, rules):
+ self._generic_rule = PublicSuffixEntry(('*', ))
+ self._rules = sorted(rules, key=lambda entry: entry.labels)
+
+ @classmethod
+ def load(cls, filename):
+ '''
+ Load Public Suffix List from the given filename.
+ '''
+ rules = []
+ part = None
+ with open(filename, 'rb') as content_file:
+ content = content_file.read().decode('utf-8')
+ for line in content.splitlines():
+ line = line.strip()
+ if line.startswith('//') or not line:
+ m = _BEGIN_SUBSET_MATCHER.search(line)
+ if m:
+ part = m.group(1).lower()
+ m = _END_SUBSET_MATCHER.search(line)
+ if m:
+ part = None
+ continue
+ if part is None:
+ raise Exception('Internal error: found PSL entry with no part!')
+ exception_rule = False
+ if line.startswith('!'):
+ exception_rule = True
+ line = line[1:]
+ if line.startswith('.'):
+ line = line[1:]
+ labels = tuple(normalize_label(label) for label in split_into_labels(line)[0])
+ rules.append(PublicSuffixEntry(labels, exception_rule=exception_rule, part=part))
+ return cls(rules)
+
+ def get_suffix_length_and_rule(self, normalized_labels, icann_only=False):
+ '''
+ Given a list of normalized labels, searches for a matching rule.
+
+ Returns the tuple ``(suffix_length, rule)``. The ``rule`` is never ``None``
+ except if ``normalized_labels`` is empty, in which case ``(0, None)`` is returned.
+
+ If ``icann_only`` is set to ``True``, only official ICANN rules are used. If
+ ``icann_only`` is ``False`` (default), also private rules are used.
+ '''
+ if not normalized_labels:
+ return 0, None
+
+ # Find matching rules
+ rules = []
+ for rule in self._rules:
+ if icann_only and rule.part != 'icann':
+ continue
+ if rule.matches(normalized_labels):
+ rules.append(rule)
+ if not rules:
+ rules.append(self._generic_rule)
+
+ # Select prevailing rule
+ rule = select_prevailing_rule(rules)
+
+ # Determine suffix
+ suffix_length = len(rule.labels)
+ if rule.exception_rule:
+ suffix_length -= 1
+
+ # Return result
+ return suffix_length, rule
+
+ def get_suffix(self, domain, keep_unknown_suffix=True, normalize_result=False,
+ icann_only=False):
+ '''
+ Given a domain name, extracts the public suffix.
+
+ If ``keep_unknown_suffix`` is set to ``False``, only suffixes matching explicit
+ entries from the PSL are returned. If ``keep_unknown_suffix`` is ``True`` (default),
+ the implicit ``*`` rule is used if no other rule matches.
+
+ If ``normalize_result`` is set to ``True``, the result is re-combined form the
+ normalized labels. In that case, the result is lower-case ASCII. If
+ ``normalize_result`` is ``False`` (default), the result ``result`` always satisfies
+ ``domain.endswith(result)``.
+
+ If ``icann_only`` is set to ``True``, only official ICANN rules are used. If
+ ``icann_only`` is ``False`` (default), also private rules are used.
+ '''
+ # Split into labels and normalize
+ try:
+ labels, tail = split_into_labels(domain)
+ normalized_labels = [normalize_label(label) for label in labels]
+ except InvalidDomainName:
+ return ''
+ if normalize_result:
+ labels = normalized_labels
+
+ # Get suffix length
+ suffix_length, rule = self.get_suffix_length_and_rule(normalized_labels, icann_only=icann_only)
+ if rule is None:
+ return ''
+ if not keep_unknown_suffix and rule is self._generic_rule:
+ return ''
+ return '.'.join(reversed(labels[:suffix_length])) + tail
+
+ def get_registrable_domain(self, domain, keep_unknown_suffix=True, only_if_registerable=True,
+ normalize_result=False, icann_only=False):
+ '''
+ Given a domain name, extracts the registrable domain. This is the public suffix
+ including the last label before the suffix.
+
+ If ``keep_unknown_suffix`` is set to ``False``, only suffixes matching explicit
+ entries from the PSL are returned. If no suffix can be found, ``''`` is returned.
+ If ``keep_unknown_suffix`` is ``True`` (default), the implicit ``*`` rule is used
+ if no other rule matches.
+
+ If ``only_if_registerable`` is set to ``False``, the public suffix is returned
+ if there is no label before the suffix. If ``only_if_registerable`` is ``True``
+ (default), ``''`` is returned in that case.
+
+ If ``normalize_result`` is set to ``True``, the result is re-combined form the
+ normalized labels. In that case, the result is lower-case ASCII. If
+ ``normalize_result`` is ``False`` (default), the result ``result`` always satisfies
+ ``domain.endswith(result)``.
+
+ If ``icann_only`` is set to ``True``, only official ICANN rules are used. If
+ ``icann_only`` is ``False`` (default), also private rules are used.
+ '''
+ # Split into labels and normalize
+ try:
+ labels, tail = split_into_labels(domain)
+ normalized_labels = [normalize_label(label) for label in labels]
+ except InvalidDomainName:
+ return ''
+ if normalize_result:
+ labels = normalized_labels
+
+ # Get suffix length
+ suffix_length, rule = self.get_suffix_length_and_rule(normalized_labels, icann_only=icann_only)
+ if rule is None:
+ return ''
+ if not keep_unknown_suffix and rule is self._generic_rule:
+ return ''
+ if suffix_length < len(labels):
+ suffix_length += 1
+ elif only_if_registerable:
+ return ''
+ return '.'.join(reversed(labels[:suffix_length])) + tail
+
+
+# The official Public Suffix List
+PUBLIC_SUFFIX_LIST = PublicSuffixList.load(os.path.join(os.path.dirname(__file__), '..', 'public_suffix_list.dat'))
diff --git a/ansible_collections/community/dns/plugins/plugin_utils/templated_options.py b/ansible_collections/community/dns/plugins/plugin_utils/templated_options.py
new file mode 100644
index 000000000..86bb5c282
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/plugin_utils/templated_options.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2022 Felix Fontein
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+class TemplatedOptionProvider(object):
+ def __init__(self, plugin, templar):
+ self.plugin = plugin
+ self.templar = templar
+
+ def get_option(self, option_name):
+ value = self.plugin.get_option(option_name)
+ if self.templar.is_template(value):
+ value = self.templar.template(variable=value, disable_lookups=False)
+ return value
diff --git a/ansible_collections/community/dns/plugins/public_suffix_list.dat b/ansible_collections/community/dns/plugins/public_suffix_list.dat
new file mode 100644
index 000000000..b63242437
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/public_suffix_list.dat
@@ -0,0 +1,13806 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+// Please pull this list from, and only from https://publicsuffix.org/list/public_suffix_list.dat,
+// rather than any other VCS sites. Pulling from any other URL is not guaranteed to be supported.
+
+// Instructions on pulling and using this list can be found at https://publicsuffix.org/list/.
+
+// ===BEGIN ICANN DOMAINS===
+
+// ac : http://nic.ac/rules.htm
+ac
+com.ac
+edu.ac
+gov.ac
+net.ac
+mil.ac
+org.ac
+
+// ad : https://en.wikipedia.org/wiki/.ad
+ad
+nom.ad
+
+// ae : https://tdra.gov.ae/en/aeda/ae-policies
+ae
+co.ae
+net.ae
+org.ae
+sch.ae
+ac.ae
+gov.ae
+mil.ae
+
+// aero : see https://www.information.aero/index.php?id=66
+aero
+accident-investigation.aero
+accident-prevention.aero
+aerobatic.aero
+aeroclub.aero
+aerodrome.aero
+agents.aero
+aircraft.aero
+airline.aero
+airport.aero
+air-surveillance.aero
+airtraffic.aero
+air-traffic-control.aero
+ambulance.aero
+amusement.aero
+association.aero
+author.aero
+ballooning.aero
+broker.aero
+caa.aero
+cargo.aero
+catering.aero
+certification.aero
+championship.aero
+charter.aero
+civilaviation.aero
+club.aero
+conference.aero
+consultant.aero
+consulting.aero
+control.aero
+council.aero
+crew.aero
+design.aero
+dgca.aero
+educator.aero
+emergency.aero
+engine.aero
+engineer.aero
+entertainment.aero
+equipment.aero
+exchange.aero
+express.aero
+federation.aero
+flight.aero
+fuel.aero
+gliding.aero
+government.aero
+groundhandling.aero
+group.aero
+hanggliding.aero
+homebuilt.aero
+insurance.aero
+journal.aero
+journalist.aero
+leasing.aero
+logistics.aero
+magazine.aero
+maintenance.aero
+media.aero
+microlight.aero
+modelling.aero
+navigation.aero
+parachuting.aero
+paragliding.aero
+passenger-association.aero
+pilot.aero
+press.aero
+production.aero
+recreation.aero
+repbody.aero
+res.aero
+research.aero
+rotorcraft.aero
+safety.aero
+scientist.aero
+services.aero
+show.aero
+skydiving.aero
+software.aero
+student.aero
+trader.aero
+trading.aero
+trainer.aero
+union.aero
+workinggroup.aero
+works.aero
+
+// af : http://www.nic.af/help.jsp
+af
+gov.af
+com.af
+org.af
+net.af
+edu.af
+
+// ag : http://www.nic.ag/prices.htm
+ag
+com.ag
+org.ag
+net.ag
+co.ag
+nom.ag
+
+// ai : http://nic.com.ai/
+ai
+off.ai
+com.ai
+net.ai
+org.ai
+
+// al : http://www.ert.gov.al/ert_alb/faq_det.html?Id=31
+al
+com.al
+edu.al
+gov.al
+mil.al
+net.al
+org.al
+
+// am : https://www.amnic.net/policy/en/Policy_EN.pdf
+am
+co.am
+com.am
+commune.am
+net.am
+org.am
+
+// ao : https://en.wikipedia.org/wiki/.ao
+// http://www.dns.ao/REGISTR.DOC
+ao
+ed.ao
+gv.ao
+og.ao
+co.ao
+pb.ao
+it.ao
+
+// aq : https://en.wikipedia.org/wiki/.aq
+aq
+
+// ar : https://nic.ar/es/nic-argentina/normativa
+ar
+bet.ar
+com.ar
+coop.ar
+edu.ar
+gob.ar
+gov.ar
+int.ar
+mil.ar
+musica.ar
+mutual.ar
+net.ar
+org.ar
+senasa.ar
+tur.ar
+
+// arpa : https://en.wikipedia.org/wiki/.arpa
+// Confirmed by registry <iana-questions@icann.org> 2008-06-18
+arpa
+e164.arpa
+in-addr.arpa
+ip6.arpa
+iris.arpa
+uri.arpa
+urn.arpa
+
+// as : https://en.wikipedia.org/wiki/.as
+as
+gov.as
+
+// asia : https://en.wikipedia.org/wiki/.asia
+asia
+
+// at : https://en.wikipedia.org/wiki/.at
+// Confirmed by registry <it@nic.at> 2008-06-17
+at
+ac.at
+co.at
+gv.at
+or.at
+sth.ac.at
+
+// au : https://en.wikipedia.org/wiki/.au
+// http://www.auda.org.au/
+au
+// 2LDs
+com.au
+net.au
+org.au
+edu.au
+gov.au
+asn.au
+id.au
+// Historic 2LDs (closed to new registration, but sites still exist)
+info.au
+conf.au
+oz.au
+// CGDNs - http://www.cgdn.org.au/
+act.au
+nsw.au
+nt.au
+qld.au
+sa.au
+tas.au
+vic.au
+wa.au
+// 3LDs
+act.edu.au
+catholic.edu.au
+// eq.edu.au - Removed at the request of the Queensland Department of Education
+nsw.edu.au
+nt.edu.au
+qld.edu.au
+sa.edu.au
+tas.edu.au
+vic.edu.au
+wa.edu.au
+// act.gov.au Bug 984824 - Removed at request of Greg Tankard
+// nsw.gov.au Bug 547985 - Removed at request of <Shae.Donelan@services.nsw.gov.au>
+// nt.gov.au Bug 940478 - Removed at request of Greg Connors <Greg.Connors@nt.gov.au>
+qld.gov.au
+sa.gov.au
+tas.gov.au
+vic.gov.au
+wa.gov.au
+// 4LDs
+// education.tas.edu.au - Removed at the request of the Department of Education Tasmania
+schools.nsw.edu.au
+
+// aw : https://en.wikipedia.org/wiki/.aw
+aw
+com.aw
+
+// ax : https://en.wikipedia.org/wiki/.ax
+ax
+
+// az : https://en.wikipedia.org/wiki/.az
+az
+com.az
+net.az
+int.az
+gov.az
+org.az
+edu.az
+info.az
+pp.az
+mil.az
+name.az
+pro.az
+biz.az
+
+// ba : http://nic.ba/users_data/files/pravilnik_o_registraciji.pdf
+ba
+com.ba
+edu.ba
+gov.ba
+mil.ba
+net.ba
+org.ba
+
+// bb : https://en.wikipedia.org/wiki/.bb
+bb
+biz.bb
+co.bb
+com.bb
+edu.bb
+gov.bb
+info.bb
+net.bb
+org.bb
+store.bb
+tv.bb
+
+// bd : https://en.wikipedia.org/wiki/.bd
+*.bd
+
+// be : https://en.wikipedia.org/wiki/.be
+// Confirmed by registry <tech@dns.be> 2008-06-08
+be
+ac.be
+
+// bf : https://en.wikipedia.org/wiki/.bf
+bf
+gov.bf
+
+// bg : https://en.wikipedia.org/wiki/.bg
+// https://www.register.bg/user/static/rules/en/index.html
+bg
+a.bg
+b.bg
+c.bg
+d.bg
+e.bg
+f.bg
+g.bg
+h.bg
+i.bg
+j.bg
+k.bg
+l.bg
+m.bg
+n.bg
+o.bg
+p.bg
+q.bg
+r.bg
+s.bg
+t.bg
+u.bg
+v.bg
+w.bg
+x.bg
+y.bg
+z.bg
+0.bg
+1.bg
+2.bg
+3.bg
+4.bg
+5.bg
+6.bg
+7.bg
+8.bg
+9.bg
+
+// bh : https://en.wikipedia.org/wiki/.bh
+bh
+com.bh
+edu.bh
+net.bh
+org.bh
+gov.bh
+
+// bi : https://en.wikipedia.org/wiki/.bi
+// http://whois.nic.bi/
+bi
+co.bi
+com.bi
+edu.bi
+or.bi
+org.bi
+
+// biz : https://en.wikipedia.org/wiki/.biz
+biz
+
+// bj : https://nic.bj/bj-suffixes.txt
+// submitted by registry <contact@nic.bj>
+bj
+africa.bj
+agro.bj
+architectes.bj
+assur.bj
+avocats.bj
+co.bj
+com.bj
+eco.bj
+econo.bj
+edu.bj
+info.bj
+loisirs.bj
+money.bj
+net.bj
+org.bj
+ote.bj
+resto.bj
+restaurant.bj
+tourism.bj
+univ.bj
+
+// bm : http://www.bermudanic.bm/dnr-text.txt
+bm
+com.bm
+edu.bm
+gov.bm
+net.bm
+org.bm
+
+// bn : http://www.bnnic.bn/faqs
+bn
+com.bn
+edu.bn
+gov.bn
+net.bn
+org.bn
+
+// bo : https://nic.bo/delegacion2015.php#h-1.10
+bo
+com.bo
+edu.bo
+gob.bo
+int.bo
+org.bo
+net.bo
+mil.bo
+tv.bo
+web.bo
+// Social Domains
+academia.bo
+agro.bo
+arte.bo
+blog.bo
+bolivia.bo
+ciencia.bo
+cooperativa.bo
+democracia.bo
+deporte.bo
+ecologia.bo
+economia.bo
+empresa.bo
+indigena.bo
+industria.bo
+info.bo
+medicina.bo
+movimiento.bo
+musica.bo
+natural.bo
+nombre.bo
+noticias.bo
+patria.bo
+politica.bo
+profesional.bo
+plurinacional.bo
+pueblo.bo
+revista.bo
+salud.bo
+tecnologia.bo
+tksat.bo
+transporte.bo
+wiki.bo
+
+// br : http://registro.br/dominio/categoria.html
+// Submitted by registry <fneves@registro.br>
+br
+9guacu.br
+abc.br
+adm.br
+adv.br
+agr.br
+aju.br
+am.br
+anani.br
+aparecida.br
+app.br
+arq.br
+art.br
+ato.br
+b.br
+barueri.br
+belem.br
+bhz.br
+bib.br
+bio.br
+blog.br
+bmd.br
+boavista.br
+bsb.br
+campinagrande.br
+campinas.br
+caxias.br
+cim.br
+cng.br
+cnt.br
+com.br
+contagem.br
+coop.br
+coz.br
+cri.br
+cuiaba.br
+curitiba.br
+def.br
+des.br
+det.br
+dev.br
+ecn.br
+eco.br
+edu.br
+emp.br
+enf.br
+eng.br
+esp.br
+etc.br
+eti.br
+far.br
+feira.br
+flog.br
+floripa.br
+fm.br
+fnd.br
+fortal.br
+fot.br
+foz.br
+fst.br
+g12.br
+geo.br
+ggf.br
+goiania.br
+gov.br
+// gov.br 26 states + df https://en.wikipedia.org/wiki/States_of_Brazil
+ac.gov.br
+al.gov.br
+am.gov.br
+ap.gov.br
+ba.gov.br
+ce.gov.br
+df.gov.br
+es.gov.br
+go.gov.br
+ma.gov.br
+mg.gov.br
+ms.gov.br
+mt.gov.br
+pa.gov.br
+pb.gov.br
+pe.gov.br
+pi.gov.br
+pr.gov.br
+rj.gov.br
+rn.gov.br
+ro.gov.br
+rr.gov.br
+rs.gov.br
+sc.gov.br
+se.gov.br
+sp.gov.br
+to.gov.br
+gru.br
+imb.br
+ind.br
+inf.br
+jab.br
+jampa.br
+jdf.br
+joinville.br
+jor.br
+jus.br
+leg.br
+lel.br
+log.br
+londrina.br
+macapa.br
+maceio.br
+manaus.br
+maringa.br
+mat.br
+med.br
+mil.br
+morena.br
+mp.br
+mus.br
+natal.br
+net.br
+niteroi.br
+*.nom.br
+not.br
+ntr.br
+odo.br
+ong.br
+org.br
+osasco.br
+palmas.br
+poa.br
+ppg.br
+pro.br
+psc.br
+psi.br
+pvh.br
+qsl.br
+radio.br
+rec.br
+recife.br
+rep.br
+ribeirao.br
+rio.br
+riobranco.br
+riopreto.br
+salvador.br
+sampa.br
+santamaria.br
+santoandre.br
+saobernardo.br
+saogonca.br
+seg.br
+sjc.br
+slg.br
+slz.br
+sorocaba.br
+srv.br
+taxi.br
+tc.br
+tec.br
+teo.br
+the.br
+tmp.br
+trd.br
+tur.br
+tv.br
+udi.br
+vet.br
+vix.br
+vlog.br
+wiki.br
+zlg.br
+
+// bs : http://www.nic.bs/rules.html
+bs
+com.bs
+net.bs
+org.bs
+edu.bs
+gov.bs
+
+// bt : https://en.wikipedia.org/wiki/.bt
+bt
+com.bt
+edu.bt
+gov.bt
+net.bt
+org.bt
+
+// bv : No registrations at this time.
+// Submitted by registry <jarle@uninett.no>
+bv
+
+// bw : https://en.wikipedia.org/wiki/.bw
+// http://www.gobin.info/domainname/bw.doc
+// list of other 2nd level tlds ?
+bw
+co.bw
+org.bw
+
+// by : https://en.wikipedia.org/wiki/.by
+// http://tld.by/rules_2006_en.html
+// list of other 2nd level tlds ?
+by
+gov.by
+mil.by
+// Official information does not indicate that com.by is a reserved
+// second-level domain, but it's being used as one (see www.google.com.by and
+// www.yahoo.com.by, for example), so we list it here for safety's sake.
+com.by
+
+// http://hoster.by/
+of.by
+
+// bz : https://en.wikipedia.org/wiki/.bz
+// http://www.belizenic.bz/
+bz
+com.bz
+net.bz
+org.bz
+edu.bz
+gov.bz
+
+// ca : https://en.wikipedia.org/wiki/.ca
+ca
+// ca geographical names
+ab.ca
+bc.ca
+mb.ca
+nb.ca
+nf.ca
+nl.ca
+ns.ca
+nt.ca
+nu.ca
+on.ca
+pe.ca
+qc.ca
+sk.ca
+yk.ca
+// gc.ca: https://en.wikipedia.org/wiki/.gc.ca
+// see also: http://registry.gc.ca/en/SubdomainFAQ
+gc.ca
+
+// cat : https://en.wikipedia.org/wiki/.cat
+cat
+
+// cc : https://en.wikipedia.org/wiki/.cc
+cc
+
+// cd : https://en.wikipedia.org/wiki/.cd
+// see also: https://www.nic.cd/domain/insertDomain_2.jsp?act=1
+cd
+gov.cd
+
+// cf : https://en.wikipedia.org/wiki/.cf
+cf
+
+// cg : https://en.wikipedia.org/wiki/.cg
+cg
+
+// ch : https://en.wikipedia.org/wiki/.ch
+ch
+
+// ci : https://en.wikipedia.org/wiki/.ci
+// http://www.nic.ci/index.php?page=charte
+ci
+org.ci
+or.ci
+com.ci
+co.ci
+edu.ci
+ed.ci
+ac.ci
+net.ci
+go.ci
+asso.ci
+aéroport.ci
+int.ci
+presse.ci
+md.ci
+gouv.ci
+
+// ck : https://en.wikipedia.org/wiki/.ck
+*.ck
+!www.ck
+
+// cl : https://www.nic.cl
+// Confirmed by .CL registry <hsalgado@nic.cl>
+cl
+co.cl
+gob.cl
+gov.cl
+mil.cl
+
+// cm : https://en.wikipedia.org/wiki/.cm plus bug 981927
+cm
+co.cm
+com.cm
+gov.cm
+net.cm
+
+// cn : https://en.wikipedia.org/wiki/.cn
+// Submitted by registry <tanyaling@cnnic.cn>
+cn
+ac.cn
+com.cn
+edu.cn
+gov.cn
+net.cn
+org.cn
+mil.cn
+公司.cn
+网络.cn
+網絡.cn
+// cn geographic names
+ah.cn
+bj.cn
+cq.cn
+fj.cn
+gd.cn
+gs.cn
+gz.cn
+gx.cn
+ha.cn
+hb.cn
+he.cn
+hi.cn
+hl.cn
+hn.cn
+jl.cn
+js.cn
+jx.cn
+ln.cn
+nm.cn
+nx.cn
+qh.cn
+sc.cn
+sd.cn
+sh.cn
+sn.cn
+sx.cn
+tj.cn
+xj.cn
+xz.cn
+yn.cn
+zj.cn
+hk.cn
+mo.cn
+tw.cn
+
+// co : https://en.wikipedia.org/wiki/.co
+// Submitted by registry <tecnico@uniandes.edu.co>
+co
+arts.co
+com.co
+edu.co
+firm.co
+gov.co
+info.co
+int.co
+mil.co
+net.co
+nom.co
+org.co
+rec.co
+web.co
+
+// com : https://en.wikipedia.org/wiki/.com
+com
+
+// coop : https://en.wikipedia.org/wiki/.coop
+coop
+
+// cr : http://www.nic.cr/niccr_publico/showRegistroDominiosScreen.do
+cr
+ac.cr
+co.cr
+ed.cr
+fi.cr
+go.cr
+or.cr
+sa.cr
+
+// cu : https://en.wikipedia.org/wiki/.cu
+cu
+com.cu
+edu.cu
+org.cu
+net.cu
+gov.cu
+inf.cu
+
+// cv : https://en.wikipedia.org/wiki/.cv
+// cv : http://www.dns.cv/tldcv_portal/do?com=DS;5446457100;111;+PAGE(4000018)+K-CAT-CODIGO(RDOM)+RCNT(100); <- registration rules
+cv
+com.cv
+edu.cv
+int.cv
+nome.cv
+org.cv
+
+// cw : http://www.una.cw/cw_registry/
+// Confirmed by registry <registry@una.net> 2013-03-26
+cw
+com.cw
+edu.cw
+net.cw
+org.cw
+
+// cx : https://en.wikipedia.org/wiki/.cx
+// list of other 2nd level tlds ?
+cx
+gov.cx
+
+// cy : http://www.nic.cy/
+// Submitted by registry Panayiotou Fotia <cydns@ucy.ac.cy>
+// namespace policies URL https://www.nic.cy/portal//sites/default/files/symfonia_gia_eggrafi.pdf
+cy
+ac.cy
+biz.cy
+com.cy
+ekloges.cy
+gov.cy
+ltd.cy
+mil.cy
+net.cy
+org.cy
+press.cy
+pro.cy
+tm.cy
+
+// cz : https://en.wikipedia.org/wiki/.cz
+cz
+
+// de : https://en.wikipedia.org/wiki/.de
+// Confirmed by registry <ops@denic.de> (with technical
+// reservations) 2008-07-01
+de
+
+// dj : https://en.wikipedia.org/wiki/.dj
+dj
+
+// dk : https://en.wikipedia.org/wiki/.dk
+// Confirmed by registry <robert@dk-hostmaster.dk> 2008-06-17
+dk
+
+// dm : https://en.wikipedia.org/wiki/.dm
+dm
+com.dm
+net.dm
+org.dm
+edu.dm
+gov.dm
+
+// do : https://en.wikipedia.org/wiki/.do
+do
+art.do
+com.do
+edu.do
+gob.do
+gov.do
+mil.do
+net.do
+org.do
+sld.do
+web.do
+
+// dz : http://www.nic.dz/images/pdf_nic/charte.pdf
+dz
+art.dz
+asso.dz
+com.dz
+edu.dz
+gov.dz
+org.dz
+net.dz
+pol.dz
+soc.dz
+tm.dz
+
+// ec : http://www.nic.ec/reg/paso1.asp
+// Submitted by registry <vabboud@nic.ec>
+ec
+com.ec
+info.ec
+net.ec
+fin.ec
+k12.ec
+med.ec
+pro.ec
+org.ec
+edu.ec
+gov.ec
+gob.ec
+mil.ec
+
+// edu : https://en.wikipedia.org/wiki/.edu
+edu
+
+// ee : http://www.eenet.ee/EENet/dom_reeglid.html#lisa_B
+ee
+edu.ee
+gov.ee
+riik.ee
+lib.ee
+med.ee
+com.ee
+pri.ee
+aip.ee
+org.ee
+fie.ee
+
+// eg : https://en.wikipedia.org/wiki/.eg
+eg
+com.eg
+edu.eg
+eun.eg
+gov.eg
+mil.eg
+name.eg
+net.eg
+org.eg
+sci.eg
+
+// er : https://en.wikipedia.org/wiki/.er
+*.er
+
+// es : https://www.nic.es/site_ingles/ingles/dominios/index.html
+es
+com.es
+nom.es
+org.es
+gob.es
+edu.es
+
+// et : https://en.wikipedia.org/wiki/.et
+et
+com.et
+gov.et
+org.et
+edu.et
+biz.et
+name.et
+info.et
+net.et
+
+// eu : https://en.wikipedia.org/wiki/.eu
+eu
+
+// fi : https://en.wikipedia.org/wiki/.fi
+fi
+// aland.fi : https://en.wikipedia.org/wiki/.ax
+// This domain is being phased out in favor of .ax. As there are still many
+// domains under aland.fi, we still keep it on the list until aland.fi is
+// completely removed.
+// TODO: Check for updates (expected to be phased out around Q1/2009)
+aland.fi
+
+// fj : http://domains.fj/
+// Submitted by registry <garth.miller@cocca.org.nz> 2020-02-11
+fj
+ac.fj
+biz.fj
+com.fj
+gov.fj
+info.fj
+mil.fj
+name.fj
+net.fj
+org.fj
+pro.fj
+
+// fk : https://en.wikipedia.org/wiki/.fk
+*.fk
+
+// fm : https://en.wikipedia.org/wiki/.fm
+com.fm
+edu.fm
+net.fm
+org.fm
+fm
+
+// fo : https://en.wikipedia.org/wiki/.fo
+fo
+
+// fr : https://www.afnic.fr/ https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf
+fr
+asso.fr
+com.fr
+gouv.fr
+nom.fr
+prd.fr
+tm.fr
+// Former "domaines sectoriels", still registration suffixes
+aeroport.fr
+avocat.fr
+avoues.fr
+cci.fr
+chambagri.fr
+chirurgiens-dentistes.fr
+experts-comptables.fr
+geometre-expert.fr
+greta.fr
+huissier-justice.fr
+medecin.fr
+notaires.fr
+pharmacien.fr
+port.fr
+veterinaire.fr
+
+// ga : https://en.wikipedia.org/wiki/.ga
+ga
+
+// gb : This registry is effectively dormant
+// Submitted by registry <Damien.Shaw@ja.net>
+gb
+
+// gd : https://en.wikipedia.org/wiki/.gd
+edu.gd
+gov.gd
+gd
+
+// ge : http://www.nic.net.ge/policy_en.pdf
+ge
+com.ge
+edu.ge
+gov.ge
+org.ge
+mil.ge
+net.ge
+pvt.ge
+
+// gf : https://en.wikipedia.org/wiki/.gf
+gf
+
+// gg : http://www.channelisles.net/register-domains/
+// Confirmed by registry <nigel@channelisles.net> 2013-11-28
+gg
+co.gg
+net.gg
+org.gg
+
+// gh : https://en.wikipedia.org/wiki/.gh
+// see also: http://www.nic.gh/reg_now.php
+// Although domains directly at second level are not possible at the moment,
+// they have been possible for some time and may come back.
+gh
+com.gh
+edu.gh
+gov.gh
+org.gh
+mil.gh
+
+// gi : http://www.nic.gi/rules.html
+gi
+com.gi
+ltd.gi
+gov.gi
+mod.gi
+edu.gi
+org.gi
+
+// gl : https://en.wikipedia.org/wiki/.gl
+// http://nic.gl
+gl
+co.gl
+com.gl
+edu.gl
+net.gl
+org.gl
+
+// gm : http://www.nic.gm/htmlpages%5Cgm-policy.htm
+gm
+
+// gn : http://psg.com/dns/gn/gn.txt
+// Submitted by registry <randy@psg.com>
+gn
+ac.gn
+com.gn
+edu.gn
+gov.gn
+org.gn
+net.gn
+
+// gov : https://en.wikipedia.org/wiki/.gov
+gov
+
+// gp : http://www.nic.gp/index.php?lang=en
+gp
+com.gp
+net.gp
+mobi.gp
+edu.gp
+org.gp
+asso.gp
+
+// gq : https://en.wikipedia.org/wiki/.gq
+gq
+
+// gr : https://grweb.ics.forth.gr/english/1617-B-2005.html
+// Submitted by registry <segred@ics.forth.gr>
+gr
+com.gr
+edu.gr
+net.gr
+org.gr
+gov.gr
+
+// gs : https://en.wikipedia.org/wiki/.gs
+gs
+
+// gt : https://www.gt/sitio/registration_policy.php?lang=en
+gt
+com.gt
+edu.gt
+gob.gt
+ind.gt
+mil.gt
+net.gt
+org.gt
+
+// gu : http://gadao.gov.gu/register.html
+// University of Guam : https://www.uog.edu
+// Submitted by uognoc@triton.uog.edu
+gu
+com.gu
+edu.gu
+gov.gu
+guam.gu
+info.gu
+net.gu
+org.gu
+web.gu
+
+// gw : https://en.wikipedia.org/wiki/.gw
+// gw : https://nic.gw/regras/
+gw
+
+// gy : https://en.wikipedia.org/wiki/.gy
+// http://registry.gy/
+gy
+co.gy
+com.gy
+edu.gy
+gov.gy
+net.gy
+org.gy
+
+// hk : https://www.hkirc.hk
+// Submitted by registry <hk.tech@hkirc.hk>
+hk
+com.hk
+edu.hk
+gov.hk
+idv.hk
+net.hk
+org.hk
+公司.hk
+教育.hk
+敎育.hk
+政府.hk
+個人.hk
+个人.hk
+箇人.hk
+網络.hk
+网络.hk
+组織.hk
+網絡.hk
+网絡.hk
+组织.hk
+組織.hk
+組织.hk
+
+// hm : https://en.wikipedia.org/wiki/.hm
+hm
+
+// hn : http://www.nic.hn/politicas/ps02,,05.html
+hn
+com.hn
+edu.hn
+org.hn
+net.hn
+mil.hn
+gob.hn
+
+// hr : http://www.dns.hr/documents/pdf/HRTLD-regulations.pdf
+hr
+iz.hr
+from.hr
+name.hr
+com.hr
+
+// ht : http://www.nic.ht/info/charte.cfm
+ht
+com.ht
+shop.ht
+firm.ht
+info.ht
+adult.ht
+net.ht
+pro.ht
+org.ht
+med.ht
+art.ht
+coop.ht
+pol.ht
+asso.ht
+edu.ht
+rel.ht
+gouv.ht
+perso.ht
+
+// hu : http://www.domain.hu/domain/English/sld.html
+// Confirmed by registry <pasztor@iszt.hu> 2008-06-12
+hu
+co.hu
+info.hu
+org.hu
+priv.hu
+sport.hu
+tm.hu
+2000.hu
+agrar.hu
+bolt.hu
+casino.hu
+city.hu
+erotica.hu
+erotika.hu
+film.hu
+forum.hu
+games.hu
+hotel.hu
+ingatlan.hu
+jogasz.hu
+konyvelo.hu
+lakas.hu
+media.hu
+news.hu
+reklam.hu
+sex.hu
+shop.hu
+suli.hu
+szex.hu
+tozsde.hu
+utazas.hu
+video.hu
+
+// id : https://pandi.id/en/domain/registration-requirements/
+id
+ac.id
+biz.id
+co.id
+desa.id
+go.id
+mil.id
+my.id
+net.id
+or.id
+ponpes.id
+sch.id
+web.id
+
+// ie : https://en.wikipedia.org/wiki/.ie
+ie
+gov.ie
+
+// il : http://www.isoc.org.il/domains/
+// see also: https://en.isoc.org.il/il-cctld/registration-rules
+// ISOC-IL (operated by .il Registry)
+il
+ac.il
+co.il
+gov.il
+idf.il
+k12.il
+muni.il
+net.il
+org.il
+// xn--4dbrk0ce ("Israel", Hebrew) : IL
+ישראל
+// xn--4dbgdty6c.xn--4dbrk0ce.
+אקדמיה.ישראל
+// xn--5dbhl8d.xn--4dbrk0ce.
+ישוב.ישראל
+// xn--8dbq2a.xn--4dbrk0ce.
+צהל.ישראל
+// xn--hebda8b.xn--4dbrk0ce.
+ממשל.ישראל
+
+// im : https://www.nic.im/
+// Submitted by registry <info@nic.im>
+im
+ac.im
+co.im
+com.im
+ltd.co.im
+net.im
+org.im
+plc.co.im
+tt.im
+tv.im
+
+// in : https://en.wikipedia.org/wiki/.in
+// see also: https://registry.in/policies
+// Please note, that nic.in is not an official eTLD, but used by most
+// government institutions.
+in
+5g.in
+6g.in
+ac.in
+ai.in
+am.in
+bihar.in
+biz.in
+business.in
+ca.in
+cn.in
+co.in
+com.in
+coop.in
+cs.in
+delhi.in
+dr.in
+edu.in
+er.in
+firm.in
+gen.in
+gov.in
+gujarat.in
+ind.in
+info.in
+int.in
+internet.in
+io.in
+me.in
+mil.in
+net.in
+nic.in
+org.in
+pg.in
+post.in
+pro.in
+res.in
+travel.in
+tv.in
+uk.in
+up.in
+us.in
+
+// info : https://en.wikipedia.org/wiki/.info
+info
+
+// int : https://en.wikipedia.org/wiki/.int
+// Confirmed by registry <iana-questions@icann.org> 2008-06-18
+int
+eu.int
+
+// io : http://www.nic.io/rules.htm
+// list of other 2nd level tlds ?
+io
+com.io
+
+// iq : http://www.cmc.iq/english/iq/iqregister1.htm
+iq
+gov.iq
+edu.iq
+mil.iq
+com.iq
+org.iq
+net.iq
+
+// ir : http://www.nic.ir/Terms_and_Conditions_ir,_Appendix_1_Domain_Rules
+// Also see http://www.nic.ir/Internationalized_Domain_Names
+// Two <iran>.ir entries added at request of <tech-team@nic.ir>, 2010-04-16
+ir
+ac.ir
+co.ir
+gov.ir
+id.ir
+net.ir
+org.ir
+sch.ir
+// xn--mgba3a4f16a.ir (<iran>.ir, Persian YEH)
+ایران.ir
+// xn--mgba3a4fra.ir (<iran>.ir, Arabic YEH)
+ايران.ir
+
+// is : http://www.isnic.is/domain/rules.php
+// Confirmed by registry <marius@isgate.is> 2008-12-06
+is
+net.is
+com.is
+edu.is
+gov.is
+org.is
+int.is
+
+// it : https://en.wikipedia.org/wiki/.it
+it
+gov.it
+edu.it
+// Reserved geo-names (regions and provinces):
+// https://www.nic.it/sites/default/files/archivio/docs/Regulation_assignation_v7.1.pdf
+// Regions
+abr.it
+abruzzo.it
+aosta-valley.it
+aostavalley.it
+bas.it
+basilicata.it
+cal.it
+calabria.it
+cam.it
+campania.it
+emilia-romagna.it
+emiliaromagna.it
+emr.it
+friuli-v-giulia.it
+friuli-ve-giulia.it
+friuli-vegiulia.it
+friuli-venezia-giulia.it
+friuli-veneziagiulia.it
+friuli-vgiulia.it
+friuliv-giulia.it
+friulive-giulia.it
+friulivegiulia.it
+friulivenezia-giulia.it
+friuliveneziagiulia.it
+friulivgiulia.it
+fvg.it
+laz.it
+lazio.it
+lig.it
+liguria.it
+lom.it
+lombardia.it
+lombardy.it
+lucania.it
+mar.it
+marche.it
+mol.it
+molise.it
+piedmont.it
+piemonte.it
+pmn.it
+pug.it
+puglia.it
+sar.it
+sardegna.it
+sardinia.it
+sic.it
+sicilia.it
+sicily.it
+taa.it
+tos.it
+toscana.it
+trentin-sud-tirol.it
+trentin-süd-tirol.it
+trentin-sudtirol.it
+trentin-südtirol.it
+trentin-sued-tirol.it
+trentin-suedtirol.it
+trentino-a-adige.it
+trentino-aadige.it
+trentino-alto-adige.it
+trentino-altoadige.it
+trentino-s-tirol.it
+trentino-stirol.it
+trentino-sud-tirol.it
+trentino-süd-tirol.it
+trentino-sudtirol.it
+trentino-südtirol.it
+trentino-sued-tirol.it
+trentino-suedtirol.it
+trentino.it
+trentinoa-adige.it
+trentinoaadige.it
+trentinoalto-adige.it
+trentinoaltoadige.it
+trentinos-tirol.it
+trentinostirol.it
+trentinosud-tirol.it
+trentinosüd-tirol.it
+trentinosudtirol.it
+trentinosüdtirol.it
+trentinosued-tirol.it
+trentinosuedtirol.it
+trentinsud-tirol.it
+trentinsüd-tirol.it
+trentinsudtirol.it
+trentinsüdtirol.it
+trentinsued-tirol.it
+trentinsuedtirol.it
+tuscany.it
+umb.it
+umbria.it
+val-d-aosta.it
+val-daosta.it
+vald-aosta.it
+valdaosta.it
+valle-aosta.it
+valle-d-aosta.it
+valle-daosta.it
+valleaosta.it
+valled-aosta.it
+valledaosta.it
+vallee-aoste.it
+vallée-aoste.it
+vallee-d-aoste.it
+vallée-d-aoste.it
+valleeaoste.it
+valléeaoste.it
+valleedaoste.it
+valléedaoste.it
+vao.it
+vda.it
+ven.it
+veneto.it
+// Provinces
+ag.it
+agrigento.it
+al.it
+alessandria.it
+alto-adige.it
+altoadige.it
+an.it
+ancona.it
+andria-barletta-trani.it
+andria-trani-barletta.it
+andriabarlettatrani.it
+andriatranibarletta.it
+ao.it
+aosta.it
+aoste.it
+ap.it
+aq.it
+aquila.it
+ar.it
+arezzo.it
+ascoli-piceno.it
+ascolipiceno.it
+asti.it
+at.it
+av.it
+avellino.it
+ba.it
+balsan-sudtirol.it
+balsan-südtirol.it
+balsan-suedtirol.it
+balsan.it
+bari.it
+barletta-trani-andria.it
+barlettatraniandria.it
+belluno.it
+benevento.it
+bergamo.it
+bg.it
+bi.it
+biella.it
+bl.it
+bn.it
+bo.it
+bologna.it
+bolzano-altoadige.it
+bolzano.it
+bozen-sudtirol.it
+bozen-südtirol.it
+bozen-suedtirol.it
+bozen.it
+br.it
+brescia.it
+brindisi.it
+bs.it
+bt.it
+bulsan-sudtirol.it
+bulsan-südtirol.it
+bulsan-suedtirol.it
+bulsan.it
+bz.it
+ca.it
+cagliari.it
+caltanissetta.it
+campidano-medio.it
+campidanomedio.it
+campobasso.it
+carbonia-iglesias.it
+carboniaiglesias.it
+carrara-massa.it
+carraramassa.it
+caserta.it
+catania.it
+catanzaro.it
+cb.it
+ce.it
+cesena-forli.it
+cesena-forlì.it
+cesenaforli.it
+cesenaforlì.it
+ch.it
+chieti.it
+ci.it
+cl.it
+cn.it
+co.it
+como.it
+cosenza.it
+cr.it
+cremona.it
+crotone.it
+cs.it
+ct.it
+cuneo.it
+cz.it
+dell-ogliastra.it
+dellogliastra.it
+en.it
+enna.it
+fc.it
+fe.it
+fermo.it
+ferrara.it
+fg.it
+fi.it
+firenze.it
+florence.it
+fm.it
+foggia.it
+forli-cesena.it
+forlì-cesena.it
+forlicesena.it
+forlìcesena.it
+fr.it
+frosinone.it
+ge.it
+genoa.it
+genova.it
+go.it
+gorizia.it
+gr.it
+grosseto.it
+iglesias-carbonia.it
+iglesiascarbonia.it
+im.it
+imperia.it
+is.it
+isernia.it
+kr.it
+la-spezia.it
+laquila.it
+laspezia.it
+latina.it
+lc.it
+le.it
+lecce.it
+lecco.it
+li.it
+livorno.it
+lo.it
+lodi.it
+lt.it
+lu.it
+lucca.it
+macerata.it
+mantova.it
+massa-carrara.it
+massacarrara.it
+matera.it
+mb.it
+mc.it
+me.it
+medio-campidano.it
+mediocampidano.it
+messina.it
+mi.it
+milan.it
+milano.it
+mn.it
+mo.it
+modena.it
+monza-brianza.it
+monza-e-della-brianza.it
+monza.it
+monzabrianza.it
+monzaebrianza.it
+monzaedellabrianza.it
+ms.it
+mt.it
+na.it
+naples.it
+napoli.it
+no.it
+novara.it
+nu.it
+nuoro.it
+og.it
+ogliastra.it
+olbia-tempio.it
+olbiatempio.it
+or.it
+oristano.it
+ot.it
+pa.it
+padova.it
+padua.it
+palermo.it
+parma.it
+pavia.it
+pc.it
+pd.it
+pe.it
+perugia.it
+pesaro-urbino.it
+pesarourbino.it
+pescara.it
+pg.it
+pi.it
+piacenza.it
+pisa.it
+pistoia.it
+pn.it
+po.it
+pordenone.it
+potenza.it
+pr.it
+prato.it
+pt.it
+pu.it
+pv.it
+pz.it
+ra.it
+ragusa.it
+ravenna.it
+rc.it
+re.it
+reggio-calabria.it
+reggio-emilia.it
+reggiocalabria.it
+reggioemilia.it
+rg.it
+ri.it
+rieti.it
+rimini.it
+rm.it
+rn.it
+ro.it
+roma.it
+rome.it
+rovigo.it
+sa.it
+salerno.it
+sassari.it
+savona.it
+si.it
+siena.it
+siracusa.it
+so.it
+sondrio.it
+sp.it
+sr.it
+ss.it
+suedtirol.it
+südtirol.it
+sv.it
+ta.it
+taranto.it
+te.it
+tempio-olbia.it
+tempioolbia.it
+teramo.it
+terni.it
+tn.it
+to.it
+torino.it
+tp.it
+tr.it
+trani-andria-barletta.it
+trani-barletta-andria.it
+traniandriabarletta.it
+tranibarlettaandria.it
+trapani.it
+trento.it
+treviso.it
+trieste.it
+ts.it
+turin.it
+tv.it
+ud.it
+udine.it
+urbino-pesaro.it
+urbinopesaro.it
+va.it
+varese.it
+vb.it
+vc.it
+ve.it
+venezia.it
+venice.it
+verbania.it
+vercelli.it
+verona.it
+vi.it
+vibo-valentia.it
+vibovalentia.it
+vicenza.it
+viterbo.it
+vr.it
+vs.it
+vt.it
+vv.it
+
+// je : http://www.channelisles.net/register-domains/
+// Confirmed by registry <nigel@channelisles.net> 2013-11-28
+je
+co.je
+net.je
+org.je
+
+// jm : http://www.com.jm/register.html
+*.jm
+
+// jo : http://www.dns.jo/Registration_policy.aspx
+jo
+com.jo
+org.jo
+net.jo
+edu.jo
+sch.jo
+gov.jo
+mil.jo
+name.jo
+
+// jobs : https://en.wikipedia.org/wiki/.jobs
+jobs
+
+// jp : https://en.wikipedia.org/wiki/.jp
+// http://jprs.co.jp/en/jpdomain.html
+// Submitted by registry <info@jprs.jp>
+jp
+// jp organizational type names
+ac.jp
+ad.jp
+co.jp
+ed.jp
+go.jp
+gr.jp
+lg.jp
+ne.jp
+or.jp
+// jp prefecture type names
+aichi.jp
+akita.jp
+aomori.jp
+chiba.jp
+ehime.jp
+fukui.jp
+fukuoka.jp
+fukushima.jp
+gifu.jp
+gunma.jp
+hiroshima.jp
+hokkaido.jp
+hyogo.jp
+ibaraki.jp
+ishikawa.jp
+iwate.jp
+kagawa.jp
+kagoshima.jp
+kanagawa.jp
+kochi.jp
+kumamoto.jp
+kyoto.jp
+mie.jp
+miyagi.jp
+miyazaki.jp
+nagano.jp
+nagasaki.jp
+nara.jp
+niigata.jp
+oita.jp
+okayama.jp
+okinawa.jp
+osaka.jp
+saga.jp
+saitama.jp
+shiga.jp
+shimane.jp
+shizuoka.jp
+tochigi.jp
+tokushima.jp
+tokyo.jp
+tottori.jp
+toyama.jp
+wakayama.jp
+yamagata.jp
+yamaguchi.jp
+yamanashi.jp
+栃木.jp
+愛知.jp
+愛媛.jp
+兵庫.jp
+熊本.jp
+茨城.jp
+北海道.jp
+千葉.jp
+和歌山.jp
+長崎.jp
+長野.jp
+新潟.jp
+青森.jp
+静岡.jp
+東京.jp
+石川.jp
+埼玉.jp
+三重.jp
+京都.jp
+佐賀.jp
+大分.jp
+大阪.jp
+奈良.jp
+宮城.jp
+宮崎.jp
+富山.jp
+山口.jp
+山形.jp
+山梨.jp
+岩手.jp
+岐阜.jp
+岡山.jp
+島根.jp
+広島.jp
+徳島.jp
+沖縄.jp
+滋賀.jp
+神奈川.jp
+福井.jp
+福岡.jp
+福島.jp
+秋田.jp
+群馬.jp
+香川.jp
+高知.jp
+鳥取.jp
+鹿児島.jp
+// jp geographic type names
+// http://jprs.jp/doc/rule/saisoku-1.html
+*.kawasaki.jp
+*.kitakyushu.jp
+*.kobe.jp
+*.nagoya.jp
+*.sapporo.jp
+*.sendai.jp
+*.yokohama.jp
+!city.kawasaki.jp
+!city.kitakyushu.jp
+!city.kobe.jp
+!city.nagoya.jp
+!city.sapporo.jp
+!city.sendai.jp
+!city.yokohama.jp
+// 4th level registration
+aisai.aichi.jp
+ama.aichi.jp
+anjo.aichi.jp
+asuke.aichi.jp
+chiryu.aichi.jp
+chita.aichi.jp
+fuso.aichi.jp
+gamagori.aichi.jp
+handa.aichi.jp
+hazu.aichi.jp
+hekinan.aichi.jp
+higashiura.aichi.jp
+ichinomiya.aichi.jp
+inazawa.aichi.jp
+inuyama.aichi.jp
+isshiki.aichi.jp
+iwakura.aichi.jp
+kanie.aichi.jp
+kariya.aichi.jp
+kasugai.aichi.jp
+kira.aichi.jp
+kiyosu.aichi.jp
+komaki.aichi.jp
+konan.aichi.jp
+kota.aichi.jp
+mihama.aichi.jp
+miyoshi.aichi.jp
+nishio.aichi.jp
+nisshin.aichi.jp
+obu.aichi.jp
+oguchi.aichi.jp
+oharu.aichi.jp
+okazaki.aichi.jp
+owariasahi.aichi.jp
+seto.aichi.jp
+shikatsu.aichi.jp
+shinshiro.aichi.jp
+shitara.aichi.jp
+tahara.aichi.jp
+takahama.aichi.jp
+tobishima.aichi.jp
+toei.aichi.jp
+togo.aichi.jp
+tokai.aichi.jp
+tokoname.aichi.jp
+toyoake.aichi.jp
+toyohashi.aichi.jp
+toyokawa.aichi.jp
+toyone.aichi.jp
+toyota.aichi.jp
+tsushima.aichi.jp
+yatomi.aichi.jp
+akita.akita.jp
+daisen.akita.jp
+fujisato.akita.jp
+gojome.akita.jp
+hachirogata.akita.jp
+happou.akita.jp
+higashinaruse.akita.jp
+honjo.akita.jp
+honjyo.akita.jp
+ikawa.akita.jp
+kamikoani.akita.jp
+kamioka.akita.jp
+katagami.akita.jp
+kazuno.akita.jp
+kitaakita.akita.jp
+kosaka.akita.jp
+kyowa.akita.jp
+misato.akita.jp
+mitane.akita.jp
+moriyoshi.akita.jp
+nikaho.akita.jp
+noshiro.akita.jp
+odate.akita.jp
+oga.akita.jp
+ogata.akita.jp
+semboku.akita.jp
+yokote.akita.jp
+yurihonjo.akita.jp
+aomori.aomori.jp
+gonohe.aomori.jp
+hachinohe.aomori.jp
+hashikami.aomori.jp
+hiranai.aomori.jp
+hirosaki.aomori.jp
+itayanagi.aomori.jp
+kuroishi.aomori.jp
+misawa.aomori.jp
+mutsu.aomori.jp
+nakadomari.aomori.jp
+noheji.aomori.jp
+oirase.aomori.jp
+owani.aomori.jp
+rokunohe.aomori.jp
+sannohe.aomori.jp
+shichinohe.aomori.jp
+shingo.aomori.jp
+takko.aomori.jp
+towada.aomori.jp
+tsugaru.aomori.jp
+tsuruta.aomori.jp
+abiko.chiba.jp
+asahi.chiba.jp
+chonan.chiba.jp
+chosei.chiba.jp
+choshi.chiba.jp
+chuo.chiba.jp
+funabashi.chiba.jp
+futtsu.chiba.jp
+hanamigawa.chiba.jp
+ichihara.chiba.jp
+ichikawa.chiba.jp
+ichinomiya.chiba.jp
+inzai.chiba.jp
+isumi.chiba.jp
+kamagaya.chiba.jp
+kamogawa.chiba.jp
+kashiwa.chiba.jp
+katori.chiba.jp
+katsuura.chiba.jp
+kimitsu.chiba.jp
+kisarazu.chiba.jp
+kozaki.chiba.jp
+kujukuri.chiba.jp
+kyonan.chiba.jp
+matsudo.chiba.jp
+midori.chiba.jp
+mihama.chiba.jp
+minamiboso.chiba.jp
+mobara.chiba.jp
+mutsuzawa.chiba.jp
+nagara.chiba.jp
+nagareyama.chiba.jp
+narashino.chiba.jp
+narita.chiba.jp
+noda.chiba.jp
+oamishirasato.chiba.jp
+omigawa.chiba.jp
+onjuku.chiba.jp
+otaki.chiba.jp
+sakae.chiba.jp
+sakura.chiba.jp
+shimofusa.chiba.jp
+shirako.chiba.jp
+shiroi.chiba.jp
+shisui.chiba.jp
+sodegaura.chiba.jp
+sosa.chiba.jp
+tako.chiba.jp
+tateyama.chiba.jp
+togane.chiba.jp
+tohnosho.chiba.jp
+tomisato.chiba.jp
+urayasu.chiba.jp
+yachimata.chiba.jp
+yachiyo.chiba.jp
+yokaichiba.chiba.jp
+yokoshibahikari.chiba.jp
+yotsukaido.chiba.jp
+ainan.ehime.jp
+honai.ehime.jp
+ikata.ehime.jp
+imabari.ehime.jp
+iyo.ehime.jp
+kamijima.ehime.jp
+kihoku.ehime.jp
+kumakogen.ehime.jp
+masaki.ehime.jp
+matsuno.ehime.jp
+matsuyama.ehime.jp
+namikata.ehime.jp
+niihama.ehime.jp
+ozu.ehime.jp
+saijo.ehime.jp
+seiyo.ehime.jp
+shikokuchuo.ehime.jp
+tobe.ehime.jp
+toon.ehime.jp
+uchiko.ehime.jp
+uwajima.ehime.jp
+yawatahama.ehime.jp
+echizen.fukui.jp
+eiheiji.fukui.jp
+fukui.fukui.jp
+ikeda.fukui.jp
+katsuyama.fukui.jp
+mihama.fukui.jp
+minamiechizen.fukui.jp
+obama.fukui.jp
+ohi.fukui.jp
+ono.fukui.jp
+sabae.fukui.jp
+sakai.fukui.jp
+takahama.fukui.jp
+tsuruga.fukui.jp
+wakasa.fukui.jp
+ashiya.fukuoka.jp
+buzen.fukuoka.jp
+chikugo.fukuoka.jp
+chikuho.fukuoka.jp
+chikujo.fukuoka.jp
+chikushino.fukuoka.jp
+chikuzen.fukuoka.jp
+chuo.fukuoka.jp
+dazaifu.fukuoka.jp
+fukuchi.fukuoka.jp
+hakata.fukuoka.jp
+higashi.fukuoka.jp
+hirokawa.fukuoka.jp
+hisayama.fukuoka.jp
+iizuka.fukuoka.jp
+inatsuki.fukuoka.jp
+kaho.fukuoka.jp
+kasuga.fukuoka.jp
+kasuya.fukuoka.jp
+kawara.fukuoka.jp
+keisen.fukuoka.jp
+koga.fukuoka.jp
+kurate.fukuoka.jp
+kurogi.fukuoka.jp
+kurume.fukuoka.jp
+minami.fukuoka.jp
+miyako.fukuoka.jp
+miyama.fukuoka.jp
+miyawaka.fukuoka.jp
+mizumaki.fukuoka.jp
+munakata.fukuoka.jp
+nakagawa.fukuoka.jp
+nakama.fukuoka.jp
+nishi.fukuoka.jp
+nogata.fukuoka.jp
+ogori.fukuoka.jp
+okagaki.fukuoka.jp
+okawa.fukuoka.jp
+oki.fukuoka.jp
+omuta.fukuoka.jp
+onga.fukuoka.jp
+onojo.fukuoka.jp
+oto.fukuoka.jp
+saigawa.fukuoka.jp
+sasaguri.fukuoka.jp
+shingu.fukuoka.jp
+shinyoshitomi.fukuoka.jp
+shonai.fukuoka.jp
+soeda.fukuoka.jp
+sue.fukuoka.jp
+tachiarai.fukuoka.jp
+tagawa.fukuoka.jp
+takata.fukuoka.jp
+toho.fukuoka.jp
+toyotsu.fukuoka.jp
+tsuiki.fukuoka.jp
+ukiha.fukuoka.jp
+umi.fukuoka.jp
+usui.fukuoka.jp
+yamada.fukuoka.jp
+yame.fukuoka.jp
+yanagawa.fukuoka.jp
+yukuhashi.fukuoka.jp
+aizubange.fukushima.jp
+aizumisato.fukushima.jp
+aizuwakamatsu.fukushima.jp
+asakawa.fukushima.jp
+bandai.fukushima.jp
+date.fukushima.jp
+fukushima.fukushima.jp
+furudono.fukushima.jp
+futaba.fukushima.jp
+hanawa.fukushima.jp
+higashi.fukushima.jp
+hirata.fukushima.jp
+hirono.fukushima.jp
+iitate.fukushima.jp
+inawashiro.fukushima.jp
+ishikawa.fukushima.jp
+iwaki.fukushima.jp
+izumizaki.fukushima.jp
+kagamiishi.fukushima.jp
+kaneyama.fukushima.jp
+kawamata.fukushima.jp
+kitakata.fukushima.jp
+kitashiobara.fukushima.jp
+koori.fukushima.jp
+koriyama.fukushima.jp
+kunimi.fukushima.jp
+miharu.fukushima.jp
+mishima.fukushima.jp
+namie.fukushima.jp
+nango.fukushima.jp
+nishiaizu.fukushima.jp
+nishigo.fukushima.jp
+okuma.fukushima.jp
+omotego.fukushima.jp
+ono.fukushima.jp
+otama.fukushima.jp
+samegawa.fukushima.jp
+shimogo.fukushima.jp
+shirakawa.fukushima.jp
+showa.fukushima.jp
+soma.fukushima.jp
+sukagawa.fukushima.jp
+taishin.fukushima.jp
+tamakawa.fukushima.jp
+tanagura.fukushima.jp
+tenei.fukushima.jp
+yabuki.fukushima.jp
+yamato.fukushima.jp
+yamatsuri.fukushima.jp
+yanaizu.fukushima.jp
+yugawa.fukushima.jp
+anpachi.gifu.jp
+ena.gifu.jp
+gifu.gifu.jp
+ginan.gifu.jp
+godo.gifu.jp
+gujo.gifu.jp
+hashima.gifu.jp
+hichiso.gifu.jp
+hida.gifu.jp
+higashishirakawa.gifu.jp
+ibigawa.gifu.jp
+ikeda.gifu.jp
+kakamigahara.gifu.jp
+kani.gifu.jp
+kasahara.gifu.jp
+kasamatsu.gifu.jp
+kawaue.gifu.jp
+kitagata.gifu.jp
+mino.gifu.jp
+minokamo.gifu.jp
+mitake.gifu.jp
+mizunami.gifu.jp
+motosu.gifu.jp
+nakatsugawa.gifu.jp
+ogaki.gifu.jp
+sakahogi.gifu.jp
+seki.gifu.jp
+sekigahara.gifu.jp
+shirakawa.gifu.jp
+tajimi.gifu.jp
+takayama.gifu.jp
+tarui.gifu.jp
+toki.gifu.jp
+tomika.gifu.jp
+wanouchi.gifu.jp
+yamagata.gifu.jp
+yaotsu.gifu.jp
+yoro.gifu.jp
+annaka.gunma.jp
+chiyoda.gunma.jp
+fujioka.gunma.jp
+higashiagatsuma.gunma.jp
+isesaki.gunma.jp
+itakura.gunma.jp
+kanna.gunma.jp
+kanra.gunma.jp
+katashina.gunma.jp
+kawaba.gunma.jp
+kiryu.gunma.jp
+kusatsu.gunma.jp
+maebashi.gunma.jp
+meiwa.gunma.jp
+midori.gunma.jp
+minakami.gunma.jp
+naganohara.gunma.jp
+nakanojo.gunma.jp
+nanmoku.gunma.jp
+numata.gunma.jp
+oizumi.gunma.jp
+ora.gunma.jp
+ota.gunma.jp
+shibukawa.gunma.jp
+shimonita.gunma.jp
+shinto.gunma.jp
+showa.gunma.jp
+takasaki.gunma.jp
+takayama.gunma.jp
+tamamura.gunma.jp
+tatebayashi.gunma.jp
+tomioka.gunma.jp
+tsukiyono.gunma.jp
+tsumagoi.gunma.jp
+ueno.gunma.jp
+yoshioka.gunma.jp
+asaminami.hiroshima.jp
+daiwa.hiroshima.jp
+etajima.hiroshima.jp
+fuchu.hiroshima.jp
+fukuyama.hiroshima.jp
+hatsukaichi.hiroshima.jp
+higashihiroshima.hiroshima.jp
+hongo.hiroshima.jp
+jinsekikogen.hiroshima.jp
+kaita.hiroshima.jp
+kui.hiroshima.jp
+kumano.hiroshima.jp
+kure.hiroshima.jp
+mihara.hiroshima.jp
+miyoshi.hiroshima.jp
+naka.hiroshima.jp
+onomichi.hiroshima.jp
+osakikamijima.hiroshima.jp
+otake.hiroshima.jp
+saka.hiroshima.jp
+sera.hiroshima.jp
+seranishi.hiroshima.jp
+shinichi.hiroshima.jp
+shobara.hiroshima.jp
+takehara.hiroshima.jp
+abashiri.hokkaido.jp
+abira.hokkaido.jp
+aibetsu.hokkaido.jp
+akabira.hokkaido.jp
+akkeshi.hokkaido.jp
+asahikawa.hokkaido.jp
+ashibetsu.hokkaido.jp
+ashoro.hokkaido.jp
+assabu.hokkaido.jp
+atsuma.hokkaido.jp
+bibai.hokkaido.jp
+biei.hokkaido.jp
+bifuka.hokkaido.jp
+bihoro.hokkaido.jp
+biratori.hokkaido.jp
+chippubetsu.hokkaido.jp
+chitose.hokkaido.jp
+date.hokkaido.jp
+ebetsu.hokkaido.jp
+embetsu.hokkaido.jp
+eniwa.hokkaido.jp
+erimo.hokkaido.jp
+esan.hokkaido.jp
+esashi.hokkaido.jp
+fukagawa.hokkaido.jp
+fukushima.hokkaido.jp
+furano.hokkaido.jp
+furubira.hokkaido.jp
+haboro.hokkaido.jp
+hakodate.hokkaido.jp
+hamatonbetsu.hokkaido.jp
+hidaka.hokkaido.jp
+higashikagura.hokkaido.jp
+higashikawa.hokkaido.jp
+hiroo.hokkaido.jp
+hokuryu.hokkaido.jp
+hokuto.hokkaido.jp
+honbetsu.hokkaido.jp
+horokanai.hokkaido.jp
+horonobe.hokkaido.jp
+ikeda.hokkaido.jp
+imakane.hokkaido.jp
+ishikari.hokkaido.jp
+iwamizawa.hokkaido.jp
+iwanai.hokkaido.jp
+kamifurano.hokkaido.jp
+kamikawa.hokkaido.jp
+kamishihoro.hokkaido.jp
+kamisunagawa.hokkaido.jp
+kamoenai.hokkaido.jp
+kayabe.hokkaido.jp
+kembuchi.hokkaido.jp
+kikonai.hokkaido.jp
+kimobetsu.hokkaido.jp
+kitahiroshima.hokkaido.jp
+kitami.hokkaido.jp
+kiyosato.hokkaido.jp
+koshimizu.hokkaido.jp
+kunneppu.hokkaido.jp
+kuriyama.hokkaido.jp
+kuromatsunai.hokkaido.jp
+kushiro.hokkaido.jp
+kutchan.hokkaido.jp
+kyowa.hokkaido.jp
+mashike.hokkaido.jp
+matsumae.hokkaido.jp
+mikasa.hokkaido.jp
+minamifurano.hokkaido.jp
+mombetsu.hokkaido.jp
+moseushi.hokkaido.jp
+mukawa.hokkaido.jp
+muroran.hokkaido.jp
+naie.hokkaido.jp
+nakagawa.hokkaido.jp
+nakasatsunai.hokkaido.jp
+nakatombetsu.hokkaido.jp
+nanae.hokkaido.jp
+nanporo.hokkaido.jp
+nayoro.hokkaido.jp
+nemuro.hokkaido.jp
+niikappu.hokkaido.jp
+niki.hokkaido.jp
+nishiokoppe.hokkaido.jp
+noboribetsu.hokkaido.jp
+numata.hokkaido.jp
+obihiro.hokkaido.jp
+obira.hokkaido.jp
+oketo.hokkaido.jp
+okoppe.hokkaido.jp
+otaru.hokkaido.jp
+otobe.hokkaido.jp
+otofuke.hokkaido.jp
+otoineppu.hokkaido.jp
+oumu.hokkaido.jp
+ozora.hokkaido.jp
+pippu.hokkaido.jp
+rankoshi.hokkaido.jp
+rebun.hokkaido.jp
+rikubetsu.hokkaido.jp
+rishiri.hokkaido.jp
+rishirifuji.hokkaido.jp
+saroma.hokkaido.jp
+sarufutsu.hokkaido.jp
+shakotan.hokkaido.jp
+shari.hokkaido.jp
+shibecha.hokkaido.jp
+shibetsu.hokkaido.jp
+shikabe.hokkaido.jp
+shikaoi.hokkaido.jp
+shimamaki.hokkaido.jp
+shimizu.hokkaido.jp
+shimokawa.hokkaido.jp
+shinshinotsu.hokkaido.jp
+shintoku.hokkaido.jp
+shiranuka.hokkaido.jp
+shiraoi.hokkaido.jp
+shiriuchi.hokkaido.jp
+sobetsu.hokkaido.jp
+sunagawa.hokkaido.jp
+taiki.hokkaido.jp
+takasu.hokkaido.jp
+takikawa.hokkaido.jp
+takinoue.hokkaido.jp
+teshikaga.hokkaido.jp
+tobetsu.hokkaido.jp
+tohma.hokkaido.jp
+tomakomai.hokkaido.jp
+tomari.hokkaido.jp
+toya.hokkaido.jp
+toyako.hokkaido.jp
+toyotomi.hokkaido.jp
+toyoura.hokkaido.jp
+tsubetsu.hokkaido.jp
+tsukigata.hokkaido.jp
+urakawa.hokkaido.jp
+urausu.hokkaido.jp
+uryu.hokkaido.jp
+utashinai.hokkaido.jp
+wakkanai.hokkaido.jp
+wassamu.hokkaido.jp
+yakumo.hokkaido.jp
+yoichi.hokkaido.jp
+aioi.hyogo.jp
+akashi.hyogo.jp
+ako.hyogo.jp
+amagasaki.hyogo.jp
+aogaki.hyogo.jp
+asago.hyogo.jp
+ashiya.hyogo.jp
+awaji.hyogo.jp
+fukusaki.hyogo.jp
+goshiki.hyogo.jp
+harima.hyogo.jp
+himeji.hyogo.jp
+ichikawa.hyogo.jp
+inagawa.hyogo.jp
+itami.hyogo.jp
+kakogawa.hyogo.jp
+kamigori.hyogo.jp
+kamikawa.hyogo.jp
+kasai.hyogo.jp
+kasuga.hyogo.jp
+kawanishi.hyogo.jp
+miki.hyogo.jp
+minamiawaji.hyogo.jp
+nishinomiya.hyogo.jp
+nishiwaki.hyogo.jp
+ono.hyogo.jp
+sanda.hyogo.jp
+sannan.hyogo.jp
+sasayama.hyogo.jp
+sayo.hyogo.jp
+shingu.hyogo.jp
+shinonsen.hyogo.jp
+shiso.hyogo.jp
+sumoto.hyogo.jp
+taishi.hyogo.jp
+taka.hyogo.jp
+takarazuka.hyogo.jp
+takasago.hyogo.jp
+takino.hyogo.jp
+tamba.hyogo.jp
+tatsuno.hyogo.jp
+toyooka.hyogo.jp
+yabu.hyogo.jp
+yashiro.hyogo.jp
+yoka.hyogo.jp
+yokawa.hyogo.jp
+ami.ibaraki.jp
+asahi.ibaraki.jp
+bando.ibaraki.jp
+chikusei.ibaraki.jp
+daigo.ibaraki.jp
+fujishiro.ibaraki.jp
+hitachi.ibaraki.jp
+hitachinaka.ibaraki.jp
+hitachiomiya.ibaraki.jp
+hitachiota.ibaraki.jp
+ibaraki.ibaraki.jp
+ina.ibaraki.jp
+inashiki.ibaraki.jp
+itako.ibaraki.jp
+iwama.ibaraki.jp
+joso.ibaraki.jp
+kamisu.ibaraki.jp
+kasama.ibaraki.jp
+kashima.ibaraki.jp
+kasumigaura.ibaraki.jp
+koga.ibaraki.jp
+miho.ibaraki.jp
+mito.ibaraki.jp
+moriya.ibaraki.jp
+naka.ibaraki.jp
+namegata.ibaraki.jp
+oarai.ibaraki.jp
+ogawa.ibaraki.jp
+omitama.ibaraki.jp
+ryugasaki.ibaraki.jp
+sakai.ibaraki.jp
+sakuragawa.ibaraki.jp
+shimodate.ibaraki.jp
+shimotsuma.ibaraki.jp
+shirosato.ibaraki.jp
+sowa.ibaraki.jp
+suifu.ibaraki.jp
+takahagi.ibaraki.jp
+tamatsukuri.ibaraki.jp
+tokai.ibaraki.jp
+tomobe.ibaraki.jp
+tone.ibaraki.jp
+toride.ibaraki.jp
+tsuchiura.ibaraki.jp
+tsukuba.ibaraki.jp
+uchihara.ibaraki.jp
+ushiku.ibaraki.jp
+yachiyo.ibaraki.jp
+yamagata.ibaraki.jp
+yawara.ibaraki.jp
+yuki.ibaraki.jp
+anamizu.ishikawa.jp
+hakui.ishikawa.jp
+hakusan.ishikawa.jp
+kaga.ishikawa.jp
+kahoku.ishikawa.jp
+kanazawa.ishikawa.jp
+kawakita.ishikawa.jp
+komatsu.ishikawa.jp
+nakanoto.ishikawa.jp
+nanao.ishikawa.jp
+nomi.ishikawa.jp
+nonoichi.ishikawa.jp
+noto.ishikawa.jp
+shika.ishikawa.jp
+suzu.ishikawa.jp
+tsubata.ishikawa.jp
+tsurugi.ishikawa.jp
+uchinada.ishikawa.jp
+wajima.ishikawa.jp
+fudai.iwate.jp
+fujisawa.iwate.jp
+hanamaki.iwate.jp
+hiraizumi.iwate.jp
+hirono.iwate.jp
+ichinohe.iwate.jp
+ichinoseki.iwate.jp
+iwaizumi.iwate.jp
+iwate.iwate.jp
+joboji.iwate.jp
+kamaishi.iwate.jp
+kanegasaki.iwate.jp
+karumai.iwate.jp
+kawai.iwate.jp
+kitakami.iwate.jp
+kuji.iwate.jp
+kunohe.iwate.jp
+kuzumaki.iwate.jp
+miyako.iwate.jp
+mizusawa.iwate.jp
+morioka.iwate.jp
+ninohe.iwate.jp
+noda.iwate.jp
+ofunato.iwate.jp
+oshu.iwate.jp
+otsuchi.iwate.jp
+rikuzentakata.iwate.jp
+shiwa.iwate.jp
+shizukuishi.iwate.jp
+sumita.iwate.jp
+tanohata.iwate.jp
+tono.iwate.jp
+yahaba.iwate.jp
+yamada.iwate.jp
+ayagawa.kagawa.jp
+higashikagawa.kagawa.jp
+kanonji.kagawa.jp
+kotohira.kagawa.jp
+manno.kagawa.jp
+marugame.kagawa.jp
+mitoyo.kagawa.jp
+naoshima.kagawa.jp
+sanuki.kagawa.jp
+tadotsu.kagawa.jp
+takamatsu.kagawa.jp
+tonosho.kagawa.jp
+uchinomi.kagawa.jp
+utazu.kagawa.jp
+zentsuji.kagawa.jp
+akune.kagoshima.jp
+amami.kagoshima.jp
+hioki.kagoshima.jp
+isa.kagoshima.jp
+isen.kagoshima.jp
+izumi.kagoshima.jp
+kagoshima.kagoshima.jp
+kanoya.kagoshima.jp
+kawanabe.kagoshima.jp
+kinko.kagoshima.jp
+kouyama.kagoshima.jp
+makurazaki.kagoshima.jp
+matsumoto.kagoshima.jp
+minamitane.kagoshima.jp
+nakatane.kagoshima.jp
+nishinoomote.kagoshima.jp
+satsumasendai.kagoshima.jp
+soo.kagoshima.jp
+tarumizu.kagoshima.jp
+yusui.kagoshima.jp
+aikawa.kanagawa.jp
+atsugi.kanagawa.jp
+ayase.kanagawa.jp
+chigasaki.kanagawa.jp
+ebina.kanagawa.jp
+fujisawa.kanagawa.jp
+hadano.kanagawa.jp
+hakone.kanagawa.jp
+hiratsuka.kanagawa.jp
+isehara.kanagawa.jp
+kaisei.kanagawa.jp
+kamakura.kanagawa.jp
+kiyokawa.kanagawa.jp
+matsuda.kanagawa.jp
+minamiashigara.kanagawa.jp
+miura.kanagawa.jp
+nakai.kanagawa.jp
+ninomiya.kanagawa.jp
+odawara.kanagawa.jp
+oi.kanagawa.jp
+oiso.kanagawa.jp
+sagamihara.kanagawa.jp
+samukawa.kanagawa.jp
+tsukui.kanagawa.jp
+yamakita.kanagawa.jp
+yamato.kanagawa.jp
+yokosuka.kanagawa.jp
+yugawara.kanagawa.jp
+zama.kanagawa.jp
+zushi.kanagawa.jp
+aki.kochi.jp
+geisei.kochi.jp
+hidaka.kochi.jp
+higashitsuno.kochi.jp
+ino.kochi.jp
+kagami.kochi.jp
+kami.kochi.jp
+kitagawa.kochi.jp
+kochi.kochi.jp
+mihara.kochi.jp
+motoyama.kochi.jp
+muroto.kochi.jp
+nahari.kochi.jp
+nakamura.kochi.jp
+nankoku.kochi.jp
+nishitosa.kochi.jp
+niyodogawa.kochi.jp
+ochi.kochi.jp
+okawa.kochi.jp
+otoyo.kochi.jp
+otsuki.kochi.jp
+sakawa.kochi.jp
+sukumo.kochi.jp
+susaki.kochi.jp
+tosa.kochi.jp
+tosashimizu.kochi.jp
+toyo.kochi.jp
+tsuno.kochi.jp
+umaji.kochi.jp
+yasuda.kochi.jp
+yusuhara.kochi.jp
+amakusa.kumamoto.jp
+arao.kumamoto.jp
+aso.kumamoto.jp
+choyo.kumamoto.jp
+gyokuto.kumamoto.jp
+kamiamakusa.kumamoto.jp
+kikuchi.kumamoto.jp
+kumamoto.kumamoto.jp
+mashiki.kumamoto.jp
+mifune.kumamoto.jp
+minamata.kumamoto.jp
+minamioguni.kumamoto.jp
+nagasu.kumamoto.jp
+nishihara.kumamoto.jp
+oguni.kumamoto.jp
+ozu.kumamoto.jp
+sumoto.kumamoto.jp
+takamori.kumamoto.jp
+uki.kumamoto.jp
+uto.kumamoto.jp
+yamaga.kumamoto.jp
+yamato.kumamoto.jp
+yatsushiro.kumamoto.jp
+ayabe.kyoto.jp
+fukuchiyama.kyoto.jp
+higashiyama.kyoto.jp
+ide.kyoto.jp
+ine.kyoto.jp
+joyo.kyoto.jp
+kameoka.kyoto.jp
+kamo.kyoto.jp
+kita.kyoto.jp
+kizu.kyoto.jp
+kumiyama.kyoto.jp
+kyotamba.kyoto.jp
+kyotanabe.kyoto.jp
+kyotango.kyoto.jp
+maizuru.kyoto.jp
+minami.kyoto.jp
+minamiyamashiro.kyoto.jp
+miyazu.kyoto.jp
+muko.kyoto.jp
+nagaokakyo.kyoto.jp
+nakagyo.kyoto.jp
+nantan.kyoto.jp
+oyamazaki.kyoto.jp
+sakyo.kyoto.jp
+seika.kyoto.jp
+tanabe.kyoto.jp
+uji.kyoto.jp
+ujitawara.kyoto.jp
+wazuka.kyoto.jp
+yamashina.kyoto.jp
+yawata.kyoto.jp
+asahi.mie.jp
+inabe.mie.jp
+ise.mie.jp
+kameyama.mie.jp
+kawagoe.mie.jp
+kiho.mie.jp
+kisosaki.mie.jp
+kiwa.mie.jp
+komono.mie.jp
+kumano.mie.jp
+kuwana.mie.jp
+matsusaka.mie.jp
+meiwa.mie.jp
+mihama.mie.jp
+minamiise.mie.jp
+misugi.mie.jp
+miyama.mie.jp
+nabari.mie.jp
+shima.mie.jp
+suzuka.mie.jp
+tado.mie.jp
+taiki.mie.jp
+taki.mie.jp
+tamaki.mie.jp
+toba.mie.jp
+tsu.mie.jp
+udono.mie.jp
+ureshino.mie.jp
+watarai.mie.jp
+yokkaichi.mie.jp
+furukawa.miyagi.jp
+higashimatsushima.miyagi.jp
+ishinomaki.miyagi.jp
+iwanuma.miyagi.jp
+kakuda.miyagi.jp
+kami.miyagi.jp
+kawasaki.miyagi.jp
+marumori.miyagi.jp
+matsushima.miyagi.jp
+minamisanriku.miyagi.jp
+misato.miyagi.jp
+murata.miyagi.jp
+natori.miyagi.jp
+ogawara.miyagi.jp
+ohira.miyagi.jp
+onagawa.miyagi.jp
+osaki.miyagi.jp
+rifu.miyagi.jp
+semine.miyagi.jp
+shibata.miyagi.jp
+shichikashuku.miyagi.jp
+shikama.miyagi.jp
+shiogama.miyagi.jp
+shiroishi.miyagi.jp
+tagajo.miyagi.jp
+taiwa.miyagi.jp
+tome.miyagi.jp
+tomiya.miyagi.jp
+wakuya.miyagi.jp
+watari.miyagi.jp
+yamamoto.miyagi.jp
+zao.miyagi.jp
+aya.miyazaki.jp
+ebino.miyazaki.jp
+gokase.miyazaki.jp
+hyuga.miyazaki.jp
+kadogawa.miyazaki.jp
+kawaminami.miyazaki.jp
+kijo.miyazaki.jp
+kitagawa.miyazaki.jp
+kitakata.miyazaki.jp
+kitaura.miyazaki.jp
+kobayashi.miyazaki.jp
+kunitomi.miyazaki.jp
+kushima.miyazaki.jp
+mimata.miyazaki.jp
+miyakonojo.miyazaki.jp
+miyazaki.miyazaki.jp
+morotsuka.miyazaki.jp
+nichinan.miyazaki.jp
+nishimera.miyazaki.jp
+nobeoka.miyazaki.jp
+saito.miyazaki.jp
+shiiba.miyazaki.jp
+shintomi.miyazaki.jp
+takaharu.miyazaki.jp
+takanabe.miyazaki.jp
+takazaki.miyazaki.jp
+tsuno.miyazaki.jp
+achi.nagano.jp
+agematsu.nagano.jp
+anan.nagano.jp
+aoki.nagano.jp
+asahi.nagano.jp
+azumino.nagano.jp
+chikuhoku.nagano.jp
+chikuma.nagano.jp
+chino.nagano.jp
+fujimi.nagano.jp
+hakuba.nagano.jp
+hara.nagano.jp
+hiraya.nagano.jp
+iida.nagano.jp
+iijima.nagano.jp
+iiyama.nagano.jp
+iizuna.nagano.jp
+ikeda.nagano.jp
+ikusaka.nagano.jp
+ina.nagano.jp
+karuizawa.nagano.jp
+kawakami.nagano.jp
+kiso.nagano.jp
+kisofukushima.nagano.jp
+kitaaiki.nagano.jp
+komagane.nagano.jp
+komoro.nagano.jp
+matsukawa.nagano.jp
+matsumoto.nagano.jp
+miasa.nagano.jp
+minamiaiki.nagano.jp
+minamimaki.nagano.jp
+minamiminowa.nagano.jp
+minowa.nagano.jp
+miyada.nagano.jp
+miyota.nagano.jp
+mochizuki.nagano.jp
+nagano.nagano.jp
+nagawa.nagano.jp
+nagiso.nagano.jp
+nakagawa.nagano.jp
+nakano.nagano.jp
+nozawaonsen.nagano.jp
+obuse.nagano.jp
+ogawa.nagano.jp
+okaya.nagano.jp
+omachi.nagano.jp
+omi.nagano.jp
+ookuwa.nagano.jp
+ooshika.nagano.jp
+otaki.nagano.jp
+otari.nagano.jp
+sakae.nagano.jp
+sakaki.nagano.jp
+saku.nagano.jp
+sakuho.nagano.jp
+shimosuwa.nagano.jp
+shinanomachi.nagano.jp
+shiojiri.nagano.jp
+suwa.nagano.jp
+suzaka.nagano.jp
+takagi.nagano.jp
+takamori.nagano.jp
+takayama.nagano.jp
+tateshina.nagano.jp
+tatsuno.nagano.jp
+togakushi.nagano.jp
+togura.nagano.jp
+tomi.nagano.jp
+ueda.nagano.jp
+wada.nagano.jp
+yamagata.nagano.jp
+yamanouchi.nagano.jp
+yasaka.nagano.jp
+yasuoka.nagano.jp
+chijiwa.nagasaki.jp
+futsu.nagasaki.jp
+goto.nagasaki.jp
+hasami.nagasaki.jp
+hirado.nagasaki.jp
+iki.nagasaki.jp
+isahaya.nagasaki.jp
+kawatana.nagasaki.jp
+kuchinotsu.nagasaki.jp
+matsuura.nagasaki.jp
+nagasaki.nagasaki.jp
+obama.nagasaki.jp
+omura.nagasaki.jp
+oseto.nagasaki.jp
+saikai.nagasaki.jp
+sasebo.nagasaki.jp
+seihi.nagasaki.jp
+shimabara.nagasaki.jp
+shinkamigoto.nagasaki.jp
+togitsu.nagasaki.jp
+tsushima.nagasaki.jp
+unzen.nagasaki.jp
+ando.nara.jp
+gose.nara.jp
+heguri.nara.jp
+higashiyoshino.nara.jp
+ikaruga.nara.jp
+ikoma.nara.jp
+kamikitayama.nara.jp
+kanmaki.nara.jp
+kashiba.nara.jp
+kashihara.nara.jp
+katsuragi.nara.jp
+kawai.nara.jp
+kawakami.nara.jp
+kawanishi.nara.jp
+koryo.nara.jp
+kurotaki.nara.jp
+mitsue.nara.jp
+miyake.nara.jp
+nara.nara.jp
+nosegawa.nara.jp
+oji.nara.jp
+ouda.nara.jp
+oyodo.nara.jp
+sakurai.nara.jp
+sango.nara.jp
+shimoichi.nara.jp
+shimokitayama.nara.jp
+shinjo.nara.jp
+soni.nara.jp
+takatori.nara.jp
+tawaramoto.nara.jp
+tenkawa.nara.jp
+tenri.nara.jp
+uda.nara.jp
+yamatokoriyama.nara.jp
+yamatotakada.nara.jp
+yamazoe.nara.jp
+yoshino.nara.jp
+aga.niigata.jp
+agano.niigata.jp
+gosen.niigata.jp
+itoigawa.niigata.jp
+izumozaki.niigata.jp
+joetsu.niigata.jp
+kamo.niigata.jp
+kariwa.niigata.jp
+kashiwazaki.niigata.jp
+minamiuonuma.niigata.jp
+mitsuke.niigata.jp
+muika.niigata.jp
+murakami.niigata.jp
+myoko.niigata.jp
+nagaoka.niigata.jp
+niigata.niigata.jp
+ojiya.niigata.jp
+omi.niigata.jp
+sado.niigata.jp
+sanjo.niigata.jp
+seiro.niigata.jp
+seirou.niigata.jp
+sekikawa.niigata.jp
+shibata.niigata.jp
+tagami.niigata.jp
+tainai.niigata.jp
+tochio.niigata.jp
+tokamachi.niigata.jp
+tsubame.niigata.jp
+tsunan.niigata.jp
+uonuma.niigata.jp
+yahiko.niigata.jp
+yoita.niigata.jp
+yuzawa.niigata.jp
+beppu.oita.jp
+bungoono.oita.jp
+bungotakada.oita.jp
+hasama.oita.jp
+hiji.oita.jp
+himeshima.oita.jp
+hita.oita.jp
+kamitsue.oita.jp
+kokonoe.oita.jp
+kuju.oita.jp
+kunisaki.oita.jp
+kusu.oita.jp
+oita.oita.jp
+saiki.oita.jp
+taketa.oita.jp
+tsukumi.oita.jp
+usa.oita.jp
+usuki.oita.jp
+yufu.oita.jp
+akaiwa.okayama.jp
+asakuchi.okayama.jp
+bizen.okayama.jp
+hayashima.okayama.jp
+ibara.okayama.jp
+kagamino.okayama.jp
+kasaoka.okayama.jp
+kibichuo.okayama.jp
+kumenan.okayama.jp
+kurashiki.okayama.jp
+maniwa.okayama.jp
+misaki.okayama.jp
+nagi.okayama.jp
+niimi.okayama.jp
+nishiawakura.okayama.jp
+okayama.okayama.jp
+satosho.okayama.jp
+setouchi.okayama.jp
+shinjo.okayama.jp
+shoo.okayama.jp
+soja.okayama.jp
+takahashi.okayama.jp
+tamano.okayama.jp
+tsuyama.okayama.jp
+wake.okayama.jp
+yakage.okayama.jp
+aguni.okinawa.jp
+ginowan.okinawa.jp
+ginoza.okinawa.jp
+gushikami.okinawa.jp
+haebaru.okinawa.jp
+higashi.okinawa.jp
+hirara.okinawa.jp
+iheya.okinawa.jp
+ishigaki.okinawa.jp
+ishikawa.okinawa.jp
+itoman.okinawa.jp
+izena.okinawa.jp
+kadena.okinawa.jp
+kin.okinawa.jp
+kitadaito.okinawa.jp
+kitanakagusuku.okinawa.jp
+kumejima.okinawa.jp
+kunigami.okinawa.jp
+minamidaito.okinawa.jp
+motobu.okinawa.jp
+nago.okinawa.jp
+naha.okinawa.jp
+nakagusuku.okinawa.jp
+nakijin.okinawa.jp
+nanjo.okinawa.jp
+nishihara.okinawa.jp
+ogimi.okinawa.jp
+okinawa.okinawa.jp
+onna.okinawa.jp
+shimoji.okinawa.jp
+taketomi.okinawa.jp
+tarama.okinawa.jp
+tokashiki.okinawa.jp
+tomigusuku.okinawa.jp
+tonaki.okinawa.jp
+urasoe.okinawa.jp
+uruma.okinawa.jp
+yaese.okinawa.jp
+yomitan.okinawa.jp
+yonabaru.okinawa.jp
+yonaguni.okinawa.jp
+zamami.okinawa.jp
+abeno.osaka.jp
+chihayaakasaka.osaka.jp
+chuo.osaka.jp
+daito.osaka.jp
+fujiidera.osaka.jp
+habikino.osaka.jp
+hannan.osaka.jp
+higashiosaka.osaka.jp
+higashisumiyoshi.osaka.jp
+higashiyodogawa.osaka.jp
+hirakata.osaka.jp
+ibaraki.osaka.jp
+ikeda.osaka.jp
+izumi.osaka.jp
+izumiotsu.osaka.jp
+izumisano.osaka.jp
+kadoma.osaka.jp
+kaizuka.osaka.jp
+kanan.osaka.jp
+kashiwara.osaka.jp
+katano.osaka.jp
+kawachinagano.osaka.jp
+kishiwada.osaka.jp
+kita.osaka.jp
+kumatori.osaka.jp
+matsubara.osaka.jp
+minato.osaka.jp
+minoh.osaka.jp
+misaki.osaka.jp
+moriguchi.osaka.jp
+neyagawa.osaka.jp
+nishi.osaka.jp
+nose.osaka.jp
+osakasayama.osaka.jp
+sakai.osaka.jp
+sayama.osaka.jp
+sennan.osaka.jp
+settsu.osaka.jp
+shijonawate.osaka.jp
+shimamoto.osaka.jp
+suita.osaka.jp
+tadaoka.osaka.jp
+taishi.osaka.jp
+tajiri.osaka.jp
+takaishi.osaka.jp
+takatsuki.osaka.jp
+tondabayashi.osaka.jp
+toyonaka.osaka.jp
+toyono.osaka.jp
+yao.osaka.jp
+ariake.saga.jp
+arita.saga.jp
+fukudomi.saga.jp
+genkai.saga.jp
+hamatama.saga.jp
+hizen.saga.jp
+imari.saga.jp
+kamimine.saga.jp
+kanzaki.saga.jp
+karatsu.saga.jp
+kashima.saga.jp
+kitagata.saga.jp
+kitahata.saga.jp
+kiyama.saga.jp
+kouhoku.saga.jp
+kyuragi.saga.jp
+nishiarita.saga.jp
+ogi.saga.jp
+omachi.saga.jp
+ouchi.saga.jp
+saga.saga.jp
+shiroishi.saga.jp
+taku.saga.jp
+tara.saga.jp
+tosu.saga.jp
+yoshinogari.saga.jp
+arakawa.saitama.jp
+asaka.saitama.jp
+chichibu.saitama.jp
+fujimi.saitama.jp
+fujimino.saitama.jp
+fukaya.saitama.jp
+hanno.saitama.jp
+hanyu.saitama.jp
+hasuda.saitama.jp
+hatogaya.saitama.jp
+hatoyama.saitama.jp
+hidaka.saitama.jp
+higashichichibu.saitama.jp
+higashimatsuyama.saitama.jp
+honjo.saitama.jp
+ina.saitama.jp
+iruma.saitama.jp
+iwatsuki.saitama.jp
+kamiizumi.saitama.jp
+kamikawa.saitama.jp
+kamisato.saitama.jp
+kasukabe.saitama.jp
+kawagoe.saitama.jp
+kawaguchi.saitama.jp
+kawajima.saitama.jp
+kazo.saitama.jp
+kitamoto.saitama.jp
+koshigaya.saitama.jp
+kounosu.saitama.jp
+kuki.saitama.jp
+kumagaya.saitama.jp
+matsubushi.saitama.jp
+minano.saitama.jp
+misato.saitama.jp
+miyashiro.saitama.jp
+miyoshi.saitama.jp
+moroyama.saitama.jp
+nagatoro.saitama.jp
+namegawa.saitama.jp
+niiza.saitama.jp
+ogano.saitama.jp
+ogawa.saitama.jp
+ogose.saitama.jp
+okegawa.saitama.jp
+omiya.saitama.jp
+otaki.saitama.jp
+ranzan.saitama.jp
+ryokami.saitama.jp
+saitama.saitama.jp
+sakado.saitama.jp
+satte.saitama.jp
+sayama.saitama.jp
+shiki.saitama.jp
+shiraoka.saitama.jp
+soka.saitama.jp
+sugito.saitama.jp
+toda.saitama.jp
+tokigawa.saitama.jp
+tokorozawa.saitama.jp
+tsurugashima.saitama.jp
+urawa.saitama.jp
+warabi.saitama.jp
+yashio.saitama.jp
+yokoze.saitama.jp
+yono.saitama.jp
+yorii.saitama.jp
+yoshida.saitama.jp
+yoshikawa.saitama.jp
+yoshimi.saitama.jp
+aisho.shiga.jp
+gamo.shiga.jp
+higashiomi.shiga.jp
+hikone.shiga.jp
+koka.shiga.jp
+konan.shiga.jp
+kosei.shiga.jp
+koto.shiga.jp
+kusatsu.shiga.jp
+maibara.shiga.jp
+moriyama.shiga.jp
+nagahama.shiga.jp
+nishiazai.shiga.jp
+notogawa.shiga.jp
+omihachiman.shiga.jp
+otsu.shiga.jp
+ritto.shiga.jp
+ryuoh.shiga.jp
+takashima.shiga.jp
+takatsuki.shiga.jp
+torahime.shiga.jp
+toyosato.shiga.jp
+yasu.shiga.jp
+akagi.shimane.jp
+ama.shimane.jp
+gotsu.shimane.jp
+hamada.shimane.jp
+higashiizumo.shimane.jp
+hikawa.shimane.jp
+hikimi.shimane.jp
+izumo.shimane.jp
+kakinoki.shimane.jp
+masuda.shimane.jp
+matsue.shimane.jp
+misato.shimane.jp
+nishinoshima.shimane.jp
+ohda.shimane.jp
+okinoshima.shimane.jp
+okuizumo.shimane.jp
+shimane.shimane.jp
+tamayu.shimane.jp
+tsuwano.shimane.jp
+unnan.shimane.jp
+yakumo.shimane.jp
+yasugi.shimane.jp
+yatsuka.shimane.jp
+arai.shizuoka.jp
+atami.shizuoka.jp
+fuji.shizuoka.jp
+fujieda.shizuoka.jp
+fujikawa.shizuoka.jp
+fujinomiya.shizuoka.jp
+fukuroi.shizuoka.jp
+gotemba.shizuoka.jp
+haibara.shizuoka.jp
+hamamatsu.shizuoka.jp
+higashiizu.shizuoka.jp
+ito.shizuoka.jp
+iwata.shizuoka.jp
+izu.shizuoka.jp
+izunokuni.shizuoka.jp
+kakegawa.shizuoka.jp
+kannami.shizuoka.jp
+kawanehon.shizuoka.jp
+kawazu.shizuoka.jp
+kikugawa.shizuoka.jp
+kosai.shizuoka.jp
+makinohara.shizuoka.jp
+matsuzaki.shizuoka.jp
+minamiizu.shizuoka.jp
+mishima.shizuoka.jp
+morimachi.shizuoka.jp
+nishiizu.shizuoka.jp
+numazu.shizuoka.jp
+omaezaki.shizuoka.jp
+shimada.shizuoka.jp
+shimizu.shizuoka.jp
+shimoda.shizuoka.jp
+shizuoka.shizuoka.jp
+susono.shizuoka.jp
+yaizu.shizuoka.jp
+yoshida.shizuoka.jp
+ashikaga.tochigi.jp
+bato.tochigi.jp
+haga.tochigi.jp
+ichikai.tochigi.jp
+iwafune.tochigi.jp
+kaminokawa.tochigi.jp
+kanuma.tochigi.jp
+karasuyama.tochigi.jp
+kuroiso.tochigi.jp
+mashiko.tochigi.jp
+mibu.tochigi.jp
+moka.tochigi.jp
+motegi.tochigi.jp
+nasu.tochigi.jp
+nasushiobara.tochigi.jp
+nikko.tochigi.jp
+nishikata.tochigi.jp
+nogi.tochigi.jp
+ohira.tochigi.jp
+ohtawara.tochigi.jp
+oyama.tochigi.jp
+sakura.tochigi.jp
+sano.tochigi.jp
+shimotsuke.tochigi.jp
+shioya.tochigi.jp
+takanezawa.tochigi.jp
+tochigi.tochigi.jp
+tsuga.tochigi.jp
+ujiie.tochigi.jp
+utsunomiya.tochigi.jp
+yaita.tochigi.jp
+aizumi.tokushima.jp
+anan.tokushima.jp
+ichiba.tokushima.jp
+itano.tokushima.jp
+kainan.tokushima.jp
+komatsushima.tokushima.jp
+matsushige.tokushima.jp
+mima.tokushima.jp
+minami.tokushima.jp
+miyoshi.tokushima.jp
+mugi.tokushima.jp
+nakagawa.tokushima.jp
+naruto.tokushima.jp
+sanagochi.tokushima.jp
+shishikui.tokushima.jp
+tokushima.tokushima.jp
+wajiki.tokushima.jp
+adachi.tokyo.jp
+akiruno.tokyo.jp
+akishima.tokyo.jp
+aogashima.tokyo.jp
+arakawa.tokyo.jp
+bunkyo.tokyo.jp
+chiyoda.tokyo.jp
+chofu.tokyo.jp
+chuo.tokyo.jp
+edogawa.tokyo.jp
+fuchu.tokyo.jp
+fussa.tokyo.jp
+hachijo.tokyo.jp
+hachioji.tokyo.jp
+hamura.tokyo.jp
+higashikurume.tokyo.jp
+higashimurayama.tokyo.jp
+higashiyamato.tokyo.jp
+hino.tokyo.jp
+hinode.tokyo.jp
+hinohara.tokyo.jp
+inagi.tokyo.jp
+itabashi.tokyo.jp
+katsushika.tokyo.jp
+kita.tokyo.jp
+kiyose.tokyo.jp
+kodaira.tokyo.jp
+koganei.tokyo.jp
+kokubunji.tokyo.jp
+komae.tokyo.jp
+koto.tokyo.jp
+kouzushima.tokyo.jp
+kunitachi.tokyo.jp
+machida.tokyo.jp
+meguro.tokyo.jp
+minato.tokyo.jp
+mitaka.tokyo.jp
+mizuho.tokyo.jp
+musashimurayama.tokyo.jp
+musashino.tokyo.jp
+nakano.tokyo.jp
+nerima.tokyo.jp
+ogasawara.tokyo.jp
+okutama.tokyo.jp
+ome.tokyo.jp
+oshima.tokyo.jp
+ota.tokyo.jp
+setagaya.tokyo.jp
+shibuya.tokyo.jp
+shinagawa.tokyo.jp
+shinjuku.tokyo.jp
+suginami.tokyo.jp
+sumida.tokyo.jp
+tachikawa.tokyo.jp
+taito.tokyo.jp
+tama.tokyo.jp
+toshima.tokyo.jp
+chizu.tottori.jp
+hino.tottori.jp
+kawahara.tottori.jp
+koge.tottori.jp
+kotoura.tottori.jp
+misasa.tottori.jp
+nanbu.tottori.jp
+nichinan.tottori.jp
+sakaiminato.tottori.jp
+tottori.tottori.jp
+wakasa.tottori.jp
+yazu.tottori.jp
+yonago.tottori.jp
+asahi.toyama.jp
+fuchu.toyama.jp
+fukumitsu.toyama.jp
+funahashi.toyama.jp
+himi.toyama.jp
+imizu.toyama.jp
+inami.toyama.jp
+johana.toyama.jp
+kamiichi.toyama.jp
+kurobe.toyama.jp
+nakaniikawa.toyama.jp
+namerikawa.toyama.jp
+nanto.toyama.jp
+nyuzen.toyama.jp
+oyabe.toyama.jp
+taira.toyama.jp
+takaoka.toyama.jp
+tateyama.toyama.jp
+toga.toyama.jp
+tonami.toyama.jp
+toyama.toyama.jp
+unazuki.toyama.jp
+uozu.toyama.jp
+yamada.toyama.jp
+arida.wakayama.jp
+aridagawa.wakayama.jp
+gobo.wakayama.jp
+hashimoto.wakayama.jp
+hidaka.wakayama.jp
+hirogawa.wakayama.jp
+inami.wakayama.jp
+iwade.wakayama.jp
+kainan.wakayama.jp
+kamitonda.wakayama.jp
+katsuragi.wakayama.jp
+kimino.wakayama.jp
+kinokawa.wakayama.jp
+kitayama.wakayama.jp
+koya.wakayama.jp
+koza.wakayama.jp
+kozagawa.wakayama.jp
+kudoyama.wakayama.jp
+kushimoto.wakayama.jp
+mihama.wakayama.jp
+misato.wakayama.jp
+nachikatsuura.wakayama.jp
+shingu.wakayama.jp
+shirahama.wakayama.jp
+taiji.wakayama.jp
+tanabe.wakayama.jp
+wakayama.wakayama.jp
+yuasa.wakayama.jp
+yura.wakayama.jp
+asahi.yamagata.jp
+funagata.yamagata.jp
+higashine.yamagata.jp
+iide.yamagata.jp
+kahoku.yamagata.jp
+kaminoyama.yamagata.jp
+kaneyama.yamagata.jp
+kawanishi.yamagata.jp
+mamurogawa.yamagata.jp
+mikawa.yamagata.jp
+murayama.yamagata.jp
+nagai.yamagata.jp
+nakayama.yamagata.jp
+nanyo.yamagata.jp
+nishikawa.yamagata.jp
+obanazawa.yamagata.jp
+oe.yamagata.jp
+oguni.yamagata.jp
+ohkura.yamagata.jp
+oishida.yamagata.jp
+sagae.yamagata.jp
+sakata.yamagata.jp
+sakegawa.yamagata.jp
+shinjo.yamagata.jp
+shirataka.yamagata.jp
+shonai.yamagata.jp
+takahata.yamagata.jp
+tendo.yamagata.jp
+tozawa.yamagata.jp
+tsuruoka.yamagata.jp
+yamagata.yamagata.jp
+yamanobe.yamagata.jp
+yonezawa.yamagata.jp
+yuza.yamagata.jp
+abu.yamaguchi.jp
+hagi.yamaguchi.jp
+hikari.yamaguchi.jp
+hofu.yamaguchi.jp
+iwakuni.yamaguchi.jp
+kudamatsu.yamaguchi.jp
+mitou.yamaguchi.jp
+nagato.yamaguchi.jp
+oshima.yamaguchi.jp
+shimonoseki.yamaguchi.jp
+shunan.yamaguchi.jp
+tabuse.yamaguchi.jp
+tokuyama.yamaguchi.jp
+toyota.yamaguchi.jp
+ube.yamaguchi.jp
+yuu.yamaguchi.jp
+chuo.yamanashi.jp
+doshi.yamanashi.jp
+fuefuki.yamanashi.jp
+fujikawa.yamanashi.jp
+fujikawaguchiko.yamanashi.jp
+fujiyoshida.yamanashi.jp
+hayakawa.yamanashi.jp
+hokuto.yamanashi.jp
+ichikawamisato.yamanashi.jp
+kai.yamanashi.jp
+kofu.yamanashi.jp
+koshu.yamanashi.jp
+kosuge.yamanashi.jp
+minami-alps.yamanashi.jp
+minobu.yamanashi.jp
+nakamichi.yamanashi.jp
+nanbu.yamanashi.jp
+narusawa.yamanashi.jp
+nirasaki.yamanashi.jp
+nishikatsura.yamanashi.jp
+oshino.yamanashi.jp
+otsuki.yamanashi.jp
+showa.yamanashi.jp
+tabayama.yamanashi.jp
+tsuru.yamanashi.jp
+uenohara.yamanashi.jp
+yamanakako.yamanashi.jp
+yamanashi.yamanashi.jp
+
+// ke : http://www.kenic.or.ke/index.php/en/ke-domains/ke-domains
+ke
+ac.ke
+co.ke
+go.ke
+info.ke
+me.ke
+mobi.ke
+ne.ke
+or.ke
+sc.ke
+
+// kg : http://www.domain.kg/dmn_n.html
+kg
+org.kg
+net.kg
+com.kg
+edu.kg
+gov.kg
+mil.kg
+
+// kh : http://www.mptc.gov.kh/dns_registration.htm
+*.kh
+
+// ki : http://www.ki/dns/index.html
+ki
+edu.ki
+biz.ki
+net.ki
+org.ki
+gov.ki
+info.ki
+com.ki
+
+// km : https://en.wikipedia.org/wiki/.km
+// http://www.domaine.km/documents/charte.doc
+km
+org.km
+nom.km
+gov.km
+prd.km
+tm.km
+edu.km
+mil.km
+ass.km
+com.km
+// These are only mentioned as proposed suggestions at domaine.km, but
+// https://en.wikipedia.org/wiki/.km says they're available for registration:
+coop.km
+asso.km
+presse.km
+medecin.km
+notaires.km
+pharmaciens.km
+veterinaire.km
+gouv.km
+
+// kn : https://en.wikipedia.org/wiki/.kn
+// http://www.dot.kn/domainRules.html
+kn
+net.kn
+org.kn
+edu.kn
+gov.kn
+
+// kp : http://www.kcce.kp/en_index.php
+kp
+com.kp
+edu.kp
+gov.kp
+org.kp
+rep.kp
+tra.kp
+
+// kr : https://en.wikipedia.org/wiki/.kr
+// see also: http://domain.nida.or.kr/eng/registration.jsp
+kr
+ac.kr
+co.kr
+es.kr
+go.kr
+hs.kr
+kg.kr
+mil.kr
+ms.kr
+ne.kr
+or.kr
+pe.kr
+re.kr
+sc.kr
+// kr geographical names
+busan.kr
+chungbuk.kr
+chungnam.kr
+daegu.kr
+daejeon.kr
+gangwon.kr
+gwangju.kr
+gyeongbuk.kr
+gyeonggi.kr
+gyeongnam.kr
+incheon.kr
+jeju.kr
+jeonbuk.kr
+jeonnam.kr
+seoul.kr
+ulsan.kr
+
+// kw : https://www.nic.kw/policies/
+// Confirmed by registry <nic.tech@citra.gov.kw>
+kw
+com.kw
+edu.kw
+emb.kw
+gov.kw
+ind.kw
+net.kw
+org.kw
+
+// ky : http://www.icta.ky/da_ky_reg_dom.php
+// Confirmed by registry <kysupport@perimeterusa.com> 2008-06-17
+ky
+com.ky
+edu.ky
+net.ky
+org.ky
+
+// kz : https://en.wikipedia.org/wiki/.kz
+// see also: http://www.nic.kz/rules/index.jsp
+kz
+org.kz
+edu.kz
+net.kz
+gov.kz
+mil.kz
+com.kz
+
+// la : https://en.wikipedia.org/wiki/.la
+// Submitted by registry <gavin.brown@nic.la>
+la
+int.la
+net.la
+info.la
+edu.la
+gov.la
+per.la
+com.la
+org.la
+
+// lb : https://en.wikipedia.org/wiki/.lb
+// Submitted by registry <randy@psg.com>
+lb
+com.lb
+edu.lb
+gov.lb
+net.lb
+org.lb
+
+// lc : https://en.wikipedia.org/wiki/.lc
+// see also: http://www.nic.lc/rules.htm
+lc
+com.lc
+net.lc
+co.lc
+org.lc
+edu.lc
+gov.lc
+
+// li : https://en.wikipedia.org/wiki/.li
+li
+
+// lk : https://www.nic.lk/index.php/domain-registration/lk-domain-naming-structure
+lk
+gov.lk
+sch.lk
+net.lk
+int.lk
+com.lk
+org.lk
+edu.lk
+ngo.lk
+soc.lk
+web.lk
+ltd.lk
+assn.lk
+grp.lk
+hotel.lk
+ac.lk
+
+// lr : http://psg.com/dns/lr/lr.txt
+// Submitted by registry <randy@psg.com>
+lr
+com.lr
+edu.lr
+gov.lr
+org.lr
+net.lr
+
+// ls : http://www.nic.ls/
+// Confirmed by registry <lsadmin@nic.ls>
+ls
+ac.ls
+biz.ls
+co.ls
+edu.ls
+gov.ls
+info.ls
+net.ls
+org.ls
+sc.ls
+
+// lt : https://en.wikipedia.org/wiki/.lt
+lt
+// gov.lt : http://www.gov.lt/index_en.php
+gov.lt
+
+// lu : http://www.dns.lu/en/
+lu
+
+// lv : http://www.nic.lv/DNS/En/generic.php
+lv
+com.lv
+edu.lv
+gov.lv
+org.lv
+mil.lv
+id.lv
+net.lv
+asn.lv
+conf.lv
+
+// ly : http://www.nic.ly/regulations.php
+ly
+com.ly
+net.ly
+gov.ly
+plc.ly
+edu.ly
+sch.ly
+med.ly
+org.ly
+id.ly
+
+// ma : https://en.wikipedia.org/wiki/.ma
+// http://www.anrt.ma/fr/admin/download/upload/file_fr782.pdf
+ma
+co.ma
+net.ma
+gov.ma
+org.ma
+ac.ma
+press.ma
+
+// mc : http://www.nic.mc/
+mc
+tm.mc
+asso.mc
+
+// md : https://en.wikipedia.org/wiki/.md
+md
+
+// me : https://en.wikipedia.org/wiki/.me
+me
+co.me
+net.me
+org.me
+edu.me
+ac.me
+gov.me
+its.me
+priv.me
+
+// mg : http://nic.mg/nicmg/?page_id=39
+mg
+org.mg
+nom.mg
+gov.mg
+prd.mg
+tm.mg
+edu.mg
+mil.mg
+com.mg
+co.mg
+
+// mh : https://en.wikipedia.org/wiki/.mh
+mh
+
+// mil : https://en.wikipedia.org/wiki/.mil
+mil
+
+// mk : https://en.wikipedia.org/wiki/.mk
+// see also: http://dns.marnet.net.mk/postapka.php
+mk
+com.mk
+org.mk
+net.mk
+edu.mk
+gov.mk
+inf.mk
+name.mk
+
+// ml : http://www.gobin.info/domainname/ml-template.doc
+// see also: https://en.wikipedia.org/wiki/.ml
+ml
+com.ml
+edu.ml
+gouv.ml
+gov.ml
+net.ml
+org.ml
+presse.ml
+
+// mm : https://en.wikipedia.org/wiki/.mm
+*.mm
+
+// mn : https://en.wikipedia.org/wiki/.mn
+mn
+gov.mn
+edu.mn
+org.mn
+
+// mo : http://www.monic.net.mo/
+mo
+com.mo
+net.mo
+org.mo
+edu.mo
+gov.mo
+
+// mobi : https://en.wikipedia.org/wiki/.mobi
+mobi
+
+// mp : http://www.dot.mp/
+// Confirmed by registry <dcamacho@saipan.com> 2008-06-17
+mp
+
+// mq : https://en.wikipedia.org/wiki/.mq
+mq
+
+// mr : https://en.wikipedia.org/wiki/.mr
+mr
+gov.mr
+
+// ms : http://www.nic.ms/pdf/MS_Domain_Name_Rules.pdf
+ms
+com.ms
+edu.ms
+gov.ms
+net.ms
+org.ms
+
+// mt : https://www.nic.org.mt/go/policy
+// Submitted by registry <help@nic.org.mt>
+mt
+com.mt
+edu.mt
+net.mt
+org.mt
+
+// mu : https://en.wikipedia.org/wiki/.mu
+mu
+com.mu
+net.mu
+org.mu
+gov.mu
+ac.mu
+co.mu
+or.mu
+
+// museum : https://welcome.museum/wp-content/uploads/2018/05/20180525-Registration-Policy-MUSEUM-EN_VF-2.pdf https://welcome.museum/buy-your-dot-museum-2/
+museum
+
+// mv : https://en.wikipedia.org/wiki/.mv
+// "mv" included because, contra Wikipedia, google.mv exists.
+mv
+aero.mv
+biz.mv
+com.mv
+coop.mv
+edu.mv
+gov.mv
+info.mv
+int.mv
+mil.mv
+museum.mv
+name.mv
+net.mv
+org.mv
+pro.mv
+
+// mw : http://www.registrar.mw/
+mw
+ac.mw
+biz.mw
+co.mw
+com.mw
+coop.mw
+edu.mw
+gov.mw
+int.mw
+museum.mw
+net.mw
+org.mw
+
+// mx : http://www.nic.mx/
+// Submitted by registry <farias@nic.mx>
+mx
+com.mx
+org.mx
+gob.mx
+edu.mx
+net.mx
+
+// my : http://www.mynic.my/
+// Available strings: https://mynic.my/resources/domains/buying-a-domain/
+my
+biz.my
+com.my
+edu.my
+gov.my
+mil.my
+name.my
+net.my
+org.my
+
+// mz : http://www.uem.mz/
+// Submitted by registry <antonio@uem.mz>
+mz
+ac.mz
+adv.mz
+co.mz
+edu.mz
+gov.mz
+mil.mz
+net.mz
+org.mz
+
+// na : http://www.na-nic.com.na/
+// http://www.info.na/domain/
+na
+info.na
+pro.na
+name.na
+school.na
+or.na
+dr.na
+us.na
+mx.na
+ca.na
+in.na
+cc.na
+tv.na
+ws.na
+mobi.na
+co.na
+com.na
+org.na
+
+// name : has 2nd-level tlds, but there's no list of them
+name
+
+// nc : http://www.cctld.nc/
+nc
+asso.nc
+nom.nc
+
+// ne : https://en.wikipedia.org/wiki/.ne
+ne
+
+// net : https://en.wikipedia.org/wiki/.net
+net
+
+// nf : https://en.wikipedia.org/wiki/.nf
+nf
+com.nf
+net.nf
+per.nf
+rec.nf
+web.nf
+arts.nf
+firm.nf
+info.nf
+other.nf
+store.nf
+
+// ng : http://www.nira.org.ng/index.php/join-us/register-ng-domain/189-nira-slds
+ng
+com.ng
+edu.ng
+gov.ng
+i.ng
+mil.ng
+mobi.ng
+name.ng
+net.ng
+org.ng
+sch.ng
+
+// ni : http://www.nic.ni/
+ni
+ac.ni
+biz.ni
+co.ni
+com.ni
+edu.ni
+gob.ni
+in.ni
+info.ni
+int.ni
+mil.ni
+net.ni
+nom.ni
+org.ni
+web.ni
+
+// nl : https://en.wikipedia.org/wiki/.nl
+// https://www.sidn.nl/
+// ccTLD for the Netherlands
+nl
+
+// no : https://www.norid.no/en/om-domenenavn/regelverk-for-no/
+// Norid geographical second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-b/
+// Norid category second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-c/
+// Norid category second-level domains managed by parties other than Norid : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-d/
+// RSS feed: https://teknisk.norid.no/en/feed/
+no
+// Norid category second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-c/
+fhs.no
+vgs.no
+fylkesbibl.no
+folkebibl.no
+museum.no
+idrett.no
+priv.no
+// Norid category second-level domains managed by parties other than Norid : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-d/
+mil.no
+stat.no
+dep.no
+kommune.no
+herad.no
+// Norid geographical second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-b/
+// counties
+aa.no
+ah.no
+bu.no
+fm.no
+hl.no
+hm.no
+jan-mayen.no
+mr.no
+nl.no
+nt.no
+of.no
+ol.no
+oslo.no
+rl.no
+sf.no
+st.no
+svalbard.no
+tm.no
+tr.no
+va.no
+vf.no
+// primary and lower secondary schools per county
+gs.aa.no
+gs.ah.no
+gs.bu.no
+gs.fm.no
+gs.hl.no
+gs.hm.no
+gs.jan-mayen.no
+gs.mr.no
+gs.nl.no
+gs.nt.no
+gs.of.no
+gs.ol.no
+gs.oslo.no
+gs.rl.no
+gs.sf.no
+gs.st.no
+gs.svalbard.no
+gs.tm.no
+gs.tr.no
+gs.va.no
+gs.vf.no
+// cities
+akrehamn.no
+åkrehamn.no
+algard.no
+ålgård.no
+arna.no
+brumunddal.no
+bryne.no
+bronnoysund.no
+brønnøysund.no
+drobak.no
+drøbak.no
+egersund.no
+fetsund.no
+floro.no
+florø.no
+fredrikstad.no
+hokksund.no
+honefoss.no
+hønefoss.no
+jessheim.no
+jorpeland.no
+jørpeland.no
+kirkenes.no
+kopervik.no
+krokstadelva.no
+langevag.no
+langevåg.no
+leirvik.no
+mjondalen.no
+mjøndalen.no
+mo-i-rana.no
+mosjoen.no
+mosjøen.no
+nesoddtangen.no
+orkanger.no
+osoyro.no
+osøyro.no
+raholt.no
+råholt.no
+sandnessjoen.no
+sandnessjøen.no
+skedsmokorset.no
+slattum.no
+spjelkavik.no
+stathelle.no
+stavern.no
+stjordalshalsen.no
+stjørdalshalsen.no
+tananger.no
+tranby.no
+vossevangen.no
+// communities
+afjord.no
+åfjord.no
+agdenes.no
+al.no
+ål.no
+alesund.no
+ålesund.no
+alstahaug.no
+alta.no
+áltá.no
+alaheadju.no
+álaheadju.no
+alvdal.no
+amli.no
+åmli.no
+amot.no
+åmot.no
+andebu.no
+andoy.no
+andøy.no
+andasuolo.no
+ardal.no
+årdal.no
+aremark.no
+arendal.no
+ås.no
+aseral.no
+åseral.no
+asker.no
+askim.no
+askvoll.no
+askoy.no
+askøy.no
+asnes.no
+åsnes.no
+audnedaln.no
+aukra.no
+aure.no
+aurland.no
+aurskog-holand.no
+aurskog-høland.no
+austevoll.no
+austrheim.no
+averoy.no
+averøy.no
+balestrand.no
+ballangen.no
+balat.no
+bálát.no
+balsfjord.no
+bahccavuotna.no
+báhccavuotna.no
+bamble.no
+bardu.no
+beardu.no
+beiarn.no
+bajddar.no
+bájddar.no
+baidar.no
+báidár.no
+berg.no
+bergen.no
+berlevag.no
+berlevåg.no
+bearalvahki.no
+bearalváhki.no
+bindal.no
+birkenes.no
+bjarkoy.no
+bjarkøy.no
+bjerkreim.no
+bjugn.no
+bodo.no
+bodø.no
+badaddja.no
+bådåddjå.no
+budejju.no
+bokn.no
+bremanger.no
+bronnoy.no
+brønnøy.no
+bygland.no
+bykle.no
+barum.no
+bærum.no
+bo.telemark.no
+bø.telemark.no
+bo.nordland.no
+bø.nordland.no
+bievat.no
+bievát.no
+bomlo.no
+bømlo.no
+batsfjord.no
+båtsfjord.no
+bahcavuotna.no
+báhcavuotna.no
+dovre.no
+drammen.no
+drangedal.no
+dyroy.no
+dyrøy.no
+donna.no
+dønna.no
+eid.no
+eidfjord.no
+eidsberg.no
+eidskog.no
+eidsvoll.no
+eigersund.no
+elverum.no
+enebakk.no
+engerdal.no
+etne.no
+etnedal.no
+evenes.no
+evenassi.no
+evenášši.no
+evje-og-hornnes.no
+farsund.no
+fauske.no
+fuossko.no
+fuoisku.no
+fedje.no
+fet.no
+finnoy.no
+finnøy.no
+fitjar.no
+fjaler.no
+fjell.no
+flakstad.no
+flatanger.no
+flekkefjord.no
+flesberg.no
+flora.no
+fla.no
+flå.no
+folldal.no
+forsand.no
+fosnes.no
+frei.no
+frogn.no
+froland.no
+frosta.no
+frana.no
+fræna.no
+froya.no
+frøya.no
+fusa.no
+fyresdal.no
+forde.no
+førde.no
+gamvik.no
+gangaviika.no
+gáŋgaviika.no
+gaular.no
+gausdal.no
+gildeskal.no
+gildeskål.no
+giske.no
+gjemnes.no
+gjerdrum.no
+gjerstad.no
+gjesdal.no
+gjovik.no
+gjøvik.no
+gloppen.no
+gol.no
+gran.no
+grane.no
+granvin.no
+gratangen.no
+grimstad.no
+grong.no
+kraanghke.no
+kråanghke.no
+grue.no
+gulen.no
+hadsel.no
+halden.no
+halsa.no
+hamar.no
+hamaroy.no
+habmer.no
+hábmer.no
+hapmir.no
+hápmir.no
+hammerfest.no
+hammarfeasta.no
+hámmárfeasta.no
+haram.no
+hareid.no
+harstad.no
+hasvik.no
+aknoluokta.no
+ákŋoluokta.no
+hattfjelldal.no
+aarborte.no
+haugesund.no
+hemne.no
+hemnes.no
+hemsedal.no
+heroy.more-og-romsdal.no
+herøy.møre-og-romsdal.no
+heroy.nordland.no
+herøy.nordland.no
+hitra.no
+hjartdal.no
+hjelmeland.no
+hobol.no
+hobøl.no
+hof.no
+hol.no
+hole.no
+holmestrand.no
+holtalen.no
+holtålen.no
+hornindal.no
+horten.no
+hurdal.no
+hurum.no
+hvaler.no
+hyllestad.no
+hagebostad.no
+hægebostad.no
+hoyanger.no
+høyanger.no
+hoylandet.no
+høylandet.no
+ha.no
+hå.no
+ibestad.no
+inderoy.no
+inderøy.no
+iveland.no
+jevnaker.no
+jondal.no
+jolster.no
+jølster.no
+karasjok.no
+karasjohka.no
+kárášjohka.no
+karlsoy.no
+galsa.no
+gálsá.no
+karmoy.no
+karmøy.no
+kautokeino.no
+guovdageaidnu.no
+klepp.no
+klabu.no
+klæbu.no
+kongsberg.no
+kongsvinger.no
+kragero.no
+kragerø.no
+kristiansand.no
+kristiansund.no
+krodsherad.no
+krødsherad.no
+kvalsund.no
+rahkkeravju.no
+ráhkkerávju.no
+kvam.no
+kvinesdal.no
+kvinnherad.no
+kviteseid.no
+kvitsoy.no
+kvitsøy.no
+kvafjord.no
+kvæfjord.no
+giehtavuoatna.no
+kvanangen.no
+kvænangen.no
+navuotna.no
+návuotna.no
+kafjord.no
+kåfjord.no
+gaivuotna.no
+gáivuotna.no
+larvik.no
+lavangen.no
+lavagis.no
+loabat.no
+loabát.no
+lebesby.no
+davvesiida.no
+leikanger.no
+leirfjord.no
+leka.no
+leksvik.no
+lenvik.no
+leangaviika.no
+leaŋgaviika.no
+lesja.no
+levanger.no
+lier.no
+lierne.no
+lillehammer.no
+lillesand.no
+lindesnes.no
+lindas.no
+lindås.no
+lom.no
+loppa.no
+lahppi.no
+láhppi.no
+lund.no
+lunner.no
+luroy.no
+lurøy.no
+luster.no
+lyngdal.no
+lyngen.no
+ivgu.no
+lardal.no
+lerdal.no
+lærdal.no
+lodingen.no
+lødingen.no
+lorenskog.no
+lørenskog.no
+loten.no
+løten.no
+malvik.no
+masoy.no
+måsøy.no
+muosat.no
+muosát.no
+mandal.no
+marker.no
+marnardal.no
+masfjorden.no
+meland.no
+meldal.no
+melhus.no
+meloy.no
+meløy.no
+meraker.no
+meråker.no
+moareke.no
+moåreke.no
+midsund.no
+midtre-gauldal.no
+modalen.no
+modum.no
+molde.no
+moskenes.no
+moss.no
+mosvik.no
+malselv.no
+målselv.no
+malatvuopmi.no
+málatvuopmi.no
+namdalseid.no
+aejrie.no
+namsos.no
+namsskogan.no
+naamesjevuemie.no
+nååmesjevuemie.no
+laakesvuemie.no
+nannestad.no
+narvik.no
+narviika.no
+naustdal.no
+nedre-eiker.no
+nes.akershus.no
+nes.buskerud.no
+nesna.no
+nesodden.no
+nesseby.no
+unjarga.no
+unjárga.no
+nesset.no
+nissedal.no
+nittedal.no
+nord-aurdal.no
+nord-fron.no
+nord-odal.no
+norddal.no
+nordkapp.no
+davvenjarga.no
+davvenjárga.no
+nordre-land.no
+nordreisa.no
+raisa.no
+ráisa.no
+nore-og-uvdal.no
+notodden.no
+naroy.no
+nærøy.no
+notteroy.no
+nøtterøy.no
+odda.no
+oksnes.no
+øksnes.no
+oppdal.no
+oppegard.no
+oppegård.no
+orkdal.no
+orland.no
+ørland.no
+orskog.no
+ørskog.no
+orsta.no
+ørsta.no
+os.hedmark.no
+os.hordaland.no
+osen.no
+osteroy.no
+osterøy.no
+ostre-toten.no
+østre-toten.no
+overhalla.no
+ovre-eiker.no
+øvre-eiker.no
+oyer.no
+øyer.no
+oygarden.no
+øygarden.no
+oystre-slidre.no
+øystre-slidre.no
+porsanger.no
+porsangu.no
+porsáŋgu.no
+porsgrunn.no
+radoy.no
+radøy.no
+rakkestad.no
+rana.no
+ruovat.no
+randaberg.no
+rauma.no
+rendalen.no
+rennebu.no
+rennesoy.no
+rennesøy.no
+rindal.no
+ringebu.no
+ringerike.no
+ringsaker.no
+rissa.no
+risor.no
+risør.no
+roan.no
+rollag.no
+rygge.no
+ralingen.no
+rælingen.no
+rodoy.no
+rødøy.no
+romskog.no
+rømskog.no
+roros.no
+røros.no
+rost.no
+røst.no
+royken.no
+røyken.no
+royrvik.no
+røyrvik.no
+rade.no
+råde.no
+salangen.no
+siellak.no
+saltdal.no
+salat.no
+sálát.no
+sálat.no
+samnanger.no
+sande.more-og-romsdal.no
+sande.møre-og-romsdal.no
+sande.vestfold.no
+sandefjord.no
+sandnes.no
+sandoy.no
+sandøy.no
+sarpsborg.no
+sauda.no
+sauherad.no
+sel.no
+selbu.no
+selje.no
+seljord.no
+sigdal.no
+siljan.no
+sirdal.no
+skaun.no
+skedsmo.no
+ski.no
+skien.no
+skiptvet.no
+skjervoy.no
+skjervøy.no
+skierva.no
+skiervá.no
+skjak.no
+skjåk.no
+skodje.no
+skanland.no
+skånland.no
+skanit.no
+skánit.no
+smola.no
+smøla.no
+snillfjord.no
+snasa.no
+snåsa.no
+snoasa.no
+snaase.no
+snåase.no
+sogndal.no
+sokndal.no
+sola.no
+solund.no
+songdalen.no
+sortland.no
+spydeberg.no
+stange.no
+stavanger.no
+steigen.no
+steinkjer.no
+stjordal.no
+stjørdal.no
+stokke.no
+stor-elvdal.no
+stord.no
+stordal.no
+storfjord.no
+omasvuotna.no
+strand.no
+stranda.no
+stryn.no
+sula.no
+suldal.no
+sund.no
+sunndal.no
+surnadal.no
+sveio.no
+svelvik.no
+sykkylven.no
+sogne.no
+søgne.no
+somna.no
+sømna.no
+sondre-land.no
+søndre-land.no
+sor-aurdal.no
+sør-aurdal.no
+sor-fron.no
+sør-fron.no
+sor-odal.no
+sør-odal.no
+sor-varanger.no
+sør-varanger.no
+matta-varjjat.no
+mátta-várjjat.no
+sorfold.no
+sørfold.no
+sorreisa.no
+sørreisa.no
+sorum.no
+sørum.no
+tana.no
+deatnu.no
+time.no
+tingvoll.no
+tinn.no
+tjeldsund.no
+dielddanuorri.no
+tjome.no
+tjøme.no
+tokke.no
+tolga.no
+torsken.no
+tranoy.no
+tranøy.no
+tromso.no
+tromsø.no
+tromsa.no
+romsa.no
+trondheim.no
+troandin.no
+trysil.no
+trana.no
+træna.no
+trogstad.no
+trøgstad.no
+tvedestrand.no
+tydal.no
+tynset.no
+tysfjord.no
+divtasvuodna.no
+divttasvuotna.no
+tysnes.no
+tysvar.no
+tysvær.no
+tonsberg.no
+tønsberg.no
+ullensaker.no
+ullensvang.no
+ulvik.no
+utsira.no
+vadso.no
+vadsø.no
+cahcesuolo.no
+čáhcesuolo.no
+vaksdal.no
+valle.no
+vang.no
+vanylven.no
+vardo.no
+vardø.no
+varggat.no
+várggát.no
+vefsn.no
+vaapste.no
+vega.no
+vegarshei.no
+vegårshei.no
+vennesla.no
+verdal.no
+verran.no
+vestby.no
+vestnes.no
+vestre-slidre.no
+vestre-toten.no
+vestvagoy.no
+vestvågøy.no
+vevelstad.no
+vik.no
+vikna.no
+vindafjord.no
+volda.no
+voss.no
+varoy.no
+værøy.no
+vagan.no
+vågan.no
+voagat.no
+vagsoy.no
+vågsøy.no
+vaga.no
+vågå.no
+valer.ostfold.no
+våler.østfold.no
+valer.hedmark.no
+våler.hedmark.no
+
+// np : http://www.mos.com.np/register.html
+*.np
+
+// nr : http://cenpac.net.nr/dns/index.html
+// Submitted by registry <technician@cenpac.net.nr>
+nr
+biz.nr
+info.nr
+gov.nr
+edu.nr
+org.nr
+net.nr
+com.nr
+
+// nu : https://en.wikipedia.org/wiki/.nu
+nu
+
+// nz : https://en.wikipedia.org/wiki/.nz
+// Submitted by registry <jay@nzrs.net.nz>
+nz
+ac.nz
+co.nz
+cri.nz
+geek.nz
+gen.nz
+govt.nz
+health.nz
+iwi.nz
+kiwi.nz
+maori.nz
+mil.nz
+māori.nz
+net.nz
+org.nz
+parliament.nz
+school.nz
+
+// om : https://en.wikipedia.org/wiki/.om
+om
+co.om
+com.om
+edu.om
+gov.om
+med.om
+museum.om
+net.om
+org.om
+pro.om
+
+// onion : https://tools.ietf.org/html/rfc7686
+onion
+
+// org : https://en.wikipedia.org/wiki/.org
+org
+
+// pa : http://www.nic.pa/
+// Some additional second level "domains" resolve directly as hostnames, such as
+// pannet.pa, so we add a rule for "pa".
+pa
+ac.pa
+gob.pa
+com.pa
+org.pa
+sld.pa
+edu.pa
+net.pa
+ing.pa
+abo.pa
+med.pa
+nom.pa
+
+// pe : https://www.nic.pe/InformeFinalComision.pdf
+pe
+edu.pe
+gob.pe
+nom.pe
+mil.pe
+org.pe
+com.pe
+net.pe
+
+// pf : http://www.gobin.info/domainname/formulaire-pf.pdf
+pf
+com.pf
+org.pf
+edu.pf
+
+// pg : https://en.wikipedia.org/wiki/.pg
+*.pg
+
+// ph : http://www.domains.ph/FAQ2.asp
+// Submitted by registry <jed@email.com.ph>
+ph
+com.ph
+net.ph
+org.ph
+gov.ph
+edu.ph
+ngo.ph
+mil.ph
+i.ph
+
+// pk : http://pk5.pknic.net.pk/pk5/msgNamepk.PK
+pk
+com.pk
+net.pk
+edu.pk
+org.pk
+fam.pk
+biz.pk
+web.pk
+gov.pk
+gob.pk
+gok.pk
+gon.pk
+gop.pk
+gos.pk
+info.pk
+
+// pl http://www.dns.pl/english/index.html
+// Submitted by registry
+pl
+com.pl
+net.pl
+org.pl
+// pl functional domains (http://www.dns.pl/english/index.html)
+aid.pl
+agro.pl
+atm.pl
+auto.pl
+biz.pl
+edu.pl
+gmina.pl
+gsm.pl
+info.pl
+mail.pl
+miasta.pl
+media.pl
+mil.pl
+nieruchomosci.pl
+nom.pl
+pc.pl
+powiat.pl
+priv.pl
+realestate.pl
+rel.pl
+sex.pl
+shop.pl
+sklep.pl
+sos.pl
+szkola.pl
+targi.pl
+tm.pl
+tourism.pl
+travel.pl
+turystyka.pl
+// Government domains
+gov.pl
+ap.gov.pl
+griw.gov.pl
+ic.gov.pl
+is.gov.pl
+kmpsp.gov.pl
+konsulat.gov.pl
+kppsp.gov.pl
+kwp.gov.pl
+kwpsp.gov.pl
+mup.gov.pl
+mw.gov.pl
+oia.gov.pl
+oirm.gov.pl
+oke.gov.pl
+oow.gov.pl
+oschr.gov.pl
+oum.gov.pl
+pa.gov.pl
+pinb.gov.pl
+piw.gov.pl
+po.gov.pl
+pr.gov.pl
+psp.gov.pl
+psse.gov.pl
+pup.gov.pl
+rzgw.gov.pl
+sa.gov.pl
+sdn.gov.pl
+sko.gov.pl
+so.gov.pl
+sr.gov.pl
+starostwo.gov.pl
+ug.gov.pl
+ugim.gov.pl
+um.gov.pl
+umig.gov.pl
+upow.gov.pl
+uppo.gov.pl
+us.gov.pl
+uw.gov.pl
+uzs.gov.pl
+wif.gov.pl
+wiih.gov.pl
+winb.gov.pl
+wios.gov.pl
+witd.gov.pl
+wiw.gov.pl
+wkz.gov.pl
+wsa.gov.pl
+wskr.gov.pl
+wsse.gov.pl
+wuoz.gov.pl
+wzmiuw.gov.pl
+zp.gov.pl
+zpisdn.gov.pl
+// pl regional domains (http://www.dns.pl/english/index.html)
+augustow.pl
+babia-gora.pl
+bedzin.pl
+beskidy.pl
+bialowieza.pl
+bialystok.pl
+bielawa.pl
+bieszczady.pl
+boleslawiec.pl
+bydgoszcz.pl
+bytom.pl
+cieszyn.pl
+czeladz.pl
+czest.pl
+dlugoleka.pl
+elblag.pl
+elk.pl
+glogow.pl
+gniezno.pl
+gorlice.pl
+grajewo.pl
+ilawa.pl
+jaworzno.pl
+jelenia-gora.pl
+jgora.pl
+kalisz.pl
+kazimierz-dolny.pl
+karpacz.pl
+kartuzy.pl
+kaszuby.pl
+katowice.pl
+kepno.pl
+ketrzyn.pl
+klodzko.pl
+kobierzyce.pl
+kolobrzeg.pl
+konin.pl
+konskowola.pl
+kutno.pl
+lapy.pl
+lebork.pl
+legnica.pl
+lezajsk.pl
+limanowa.pl
+lomza.pl
+lowicz.pl
+lubin.pl
+lukow.pl
+malbork.pl
+malopolska.pl
+mazowsze.pl
+mazury.pl
+mielec.pl
+mielno.pl
+mragowo.pl
+naklo.pl
+nowaruda.pl
+nysa.pl
+olawa.pl
+olecko.pl
+olkusz.pl
+olsztyn.pl
+opoczno.pl
+opole.pl
+ostroda.pl
+ostroleka.pl
+ostrowiec.pl
+ostrowwlkp.pl
+pila.pl
+pisz.pl
+podhale.pl
+podlasie.pl
+polkowice.pl
+pomorze.pl
+pomorskie.pl
+prochowice.pl
+pruszkow.pl
+przeworsk.pl
+pulawy.pl
+radom.pl
+rawa-maz.pl
+rybnik.pl
+rzeszow.pl
+sanok.pl
+sejny.pl
+slask.pl
+slupsk.pl
+sosnowiec.pl
+stalowa-wola.pl
+skoczow.pl
+starachowice.pl
+stargard.pl
+suwalki.pl
+swidnica.pl
+swiebodzin.pl
+swinoujscie.pl
+szczecin.pl
+szczytno.pl
+tarnobrzeg.pl
+tgory.pl
+turek.pl
+tychy.pl
+ustka.pl
+walbrzych.pl
+warmia.pl
+warszawa.pl
+waw.pl
+wegrow.pl
+wielun.pl
+wlocl.pl
+wloclawek.pl
+wodzislaw.pl
+wolomin.pl
+wroclaw.pl
+zachpomor.pl
+zagan.pl
+zarow.pl
+zgora.pl
+zgorzelec.pl
+
+// pm : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf
+pm
+
+// pn : http://www.government.pn/PnRegistry/policies.htm
+pn
+gov.pn
+co.pn
+org.pn
+edu.pn
+net.pn
+
+// post : https://en.wikipedia.org/wiki/.post
+post
+
+// pr : http://www.nic.pr/index.asp?f=1
+pr
+com.pr
+net.pr
+org.pr
+gov.pr
+edu.pr
+isla.pr
+pro.pr
+biz.pr
+info.pr
+name.pr
+// these aren't mentioned on nic.pr, but on https://en.wikipedia.org/wiki/.pr
+est.pr
+prof.pr
+ac.pr
+
+// pro : http://registry.pro/get-pro
+pro
+aaa.pro
+aca.pro
+acct.pro
+avocat.pro
+bar.pro
+cpa.pro
+eng.pro
+jur.pro
+law.pro
+med.pro
+recht.pro
+
+// ps : https://en.wikipedia.org/wiki/.ps
+// http://www.nic.ps/registration/policy.html#reg
+ps
+edu.ps
+gov.ps
+sec.ps
+plo.ps
+com.ps
+org.ps
+net.ps
+
+// pt : https://www.dns.pt/en/domain/pt-terms-and-conditions-registration-rules/
+pt
+net.pt
+gov.pt
+org.pt
+edu.pt
+int.pt
+publ.pt
+com.pt
+nome.pt
+
+// pw : https://en.wikipedia.org/wiki/.pw
+pw
+co.pw
+ne.pw
+or.pw
+ed.pw
+go.pw
+belau.pw
+
+// py : http://www.nic.py/pautas.html#seccion_9
+// Submitted by registry
+py
+com.py
+coop.py
+edu.py
+gov.py
+mil.py
+net.py
+org.py
+
+// qa : http://domains.qa/en/
+qa
+com.qa
+edu.qa
+gov.qa
+mil.qa
+name.qa
+net.qa
+org.qa
+sch.qa
+
+// re : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf
+re
+asso.re
+com.re
+nom.re
+
+// ro : http://www.rotld.ro/
+ro
+arts.ro
+com.ro
+firm.ro
+info.ro
+nom.ro
+nt.ro
+org.ro
+rec.ro
+store.ro
+tm.ro
+www.ro
+
+// rs : https://www.rnids.rs/en/domains/national-domains
+rs
+ac.rs
+co.rs
+edu.rs
+gov.rs
+in.rs
+org.rs
+
+// ru : https://cctld.ru/files/pdf/docs/en/rules_ru-rf.pdf
+// Submitted by George Georgievsky <gug@cctld.ru>
+ru
+
+// rw : https://www.ricta.org.rw/sites/default/files/resources/registry_registrar_contract_0.pdf
+rw
+ac.rw
+co.rw
+coop.rw
+gov.rw
+mil.rw
+net.rw
+org.rw
+
+// sa : http://www.nic.net.sa/
+sa
+com.sa
+net.sa
+org.sa
+gov.sa
+med.sa
+pub.sa
+edu.sa
+sch.sa
+
+// sb : http://www.sbnic.net.sb/
+// Submitted by registry <lee.humphries@telekom.com.sb>
+sb
+com.sb
+edu.sb
+gov.sb
+net.sb
+org.sb
+
+// sc : http://www.nic.sc/
+sc
+com.sc
+gov.sc
+net.sc
+org.sc
+edu.sc
+
+// sd : http://www.isoc.sd/sudanic.isoc.sd/billing_pricing.htm
+// Submitted by registry <admin@isoc.sd>
+sd
+com.sd
+net.sd
+org.sd
+edu.sd
+med.sd
+tv.sd
+gov.sd
+info.sd
+
+// se : https://en.wikipedia.org/wiki/.se
+// Submitted by registry <patrik.wallstrom@iis.se>
+se
+a.se
+ac.se
+b.se
+bd.se
+brand.se
+c.se
+d.se
+e.se
+f.se
+fh.se
+fhsk.se
+fhv.se
+g.se
+h.se
+i.se
+k.se
+komforb.se
+kommunalforbund.se
+komvux.se
+l.se
+lanbib.se
+m.se
+n.se
+naturbruksgymn.se
+o.se
+org.se
+p.se
+parti.se
+pp.se
+press.se
+r.se
+s.se
+t.se
+tm.se
+u.se
+w.se
+x.se
+y.se
+z.se
+
+// sg : http://www.nic.net.sg/page/registration-policies-procedures-and-guidelines
+sg
+com.sg
+net.sg
+org.sg
+gov.sg
+edu.sg
+per.sg
+
+// sh : http://nic.sh/rules.htm
+sh
+com.sh
+net.sh
+gov.sh
+org.sh
+mil.sh
+
+// si : https://en.wikipedia.org/wiki/.si
+si
+
+// sj : No registrations at this time.
+// Submitted by registry <jarle@uninett.no>
+sj
+
+// sk : https://en.wikipedia.org/wiki/.sk
+// list of 2nd level domains ?
+sk
+
+// sl : http://www.nic.sl
+// Submitted by registry <adam@neoip.com>
+sl
+com.sl
+net.sl
+edu.sl
+gov.sl
+org.sl
+
+// sm : https://en.wikipedia.org/wiki/.sm
+sm
+
+// sn : https://en.wikipedia.org/wiki/.sn
+sn
+art.sn
+com.sn
+edu.sn
+gouv.sn
+org.sn
+perso.sn
+univ.sn
+
+// so : http://sonic.so/policies/
+so
+com.so
+edu.so
+gov.so
+me.so
+net.so
+org.so
+
+// sr : https://en.wikipedia.org/wiki/.sr
+sr
+
+// ss : https://registry.nic.ss/
+// Submitted by registry <technical@nic.ss>
+ss
+biz.ss
+com.ss
+edu.ss
+gov.ss
+me.ss
+net.ss
+org.ss
+sch.ss
+
+// st : http://www.nic.st/html/policyrules/
+st
+co.st
+com.st
+consulado.st
+edu.st
+embaixada.st
+mil.st
+net.st
+org.st
+principe.st
+saotome.st
+store.st
+
+// su : https://en.wikipedia.org/wiki/.su
+su
+
+// sv : http://www.svnet.org.sv/niveldos.pdf
+sv
+com.sv
+edu.sv
+gob.sv
+org.sv
+red.sv
+
+// sx : https://en.wikipedia.org/wiki/.sx
+// Submitted by registry <jcvignes@openregistry.com>
+sx
+gov.sx
+
+// sy : https://en.wikipedia.org/wiki/.sy
+// see also: http://www.gobin.info/domainname/sy.doc
+sy
+edu.sy
+gov.sy
+net.sy
+mil.sy
+com.sy
+org.sy
+
+// sz : https://en.wikipedia.org/wiki/.sz
+// http://www.sispa.org.sz/
+sz
+co.sz
+ac.sz
+org.sz
+
+// tc : https://en.wikipedia.org/wiki/.tc
+tc
+
+// td : https://en.wikipedia.org/wiki/.td
+td
+
+// tel: https://en.wikipedia.org/wiki/.tel
+// http://www.telnic.org/
+tel
+
+// tf : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf
+tf
+
+// tg : https://en.wikipedia.org/wiki/.tg
+// http://www.nic.tg/
+tg
+
+// th : https://en.wikipedia.org/wiki/.th
+// Submitted by registry <krit@thains.co.th>
+th
+ac.th
+co.th
+go.th
+in.th
+mi.th
+net.th
+or.th
+
+// tj : http://www.nic.tj/policy.html
+tj
+ac.tj
+biz.tj
+co.tj
+com.tj
+edu.tj
+go.tj
+gov.tj
+int.tj
+mil.tj
+name.tj
+net.tj
+nic.tj
+org.tj
+test.tj
+web.tj
+
+// tk : https://en.wikipedia.org/wiki/.tk
+tk
+
+// tl : https://en.wikipedia.org/wiki/.tl
+tl
+gov.tl
+
+// tm : http://www.nic.tm/local.html
+tm
+com.tm
+co.tm
+org.tm
+net.tm
+nom.tm
+gov.tm
+mil.tm
+edu.tm
+
+// tn : http://www.registre.tn/fr/
+// https://whois.ati.tn/
+tn
+com.tn
+ens.tn
+fin.tn
+gov.tn
+ind.tn
+info.tn
+intl.tn
+mincom.tn
+nat.tn
+net.tn
+org.tn
+perso.tn
+tourism.tn
+
+// to : https://en.wikipedia.org/wiki/.to
+// Submitted by registry <egullich@colo.to>
+to
+com.to
+gov.to
+net.to
+org.to
+edu.to
+mil.to
+
+// tr : https://nic.tr/
+// https://nic.tr/forms/eng/policies.pdf
+// https://nic.tr/index.php?USRACTN=PRICELST
+tr
+av.tr
+bbs.tr
+bel.tr
+biz.tr
+com.tr
+dr.tr
+edu.tr
+gen.tr
+gov.tr
+info.tr
+mil.tr
+k12.tr
+kep.tr
+name.tr
+net.tr
+org.tr
+pol.tr
+tel.tr
+tsk.tr
+tv.tr
+web.tr
+// Used by Northern Cyprus
+nc.tr
+// Used by government agencies of Northern Cyprus
+gov.nc.tr
+
+// tt : http://www.nic.tt/
+tt
+co.tt
+com.tt
+org.tt
+net.tt
+biz.tt
+info.tt
+pro.tt
+int.tt
+coop.tt
+jobs.tt
+mobi.tt
+travel.tt
+museum.tt
+aero.tt
+name.tt
+gov.tt
+edu.tt
+
+// tv : https://en.wikipedia.org/wiki/.tv
+// Not listing any 2LDs as reserved since none seem to exist in practice,
+// Wikipedia notwithstanding.
+tv
+
+// tw : https://en.wikipedia.org/wiki/.tw
+tw
+edu.tw
+gov.tw
+mil.tw
+com.tw
+net.tw
+org.tw
+idv.tw
+game.tw
+ebiz.tw
+club.tw
+網路.tw
+組織.tw
+商業.tw
+
+// tz : http://www.tznic.or.tz/index.php/domains
+// Submitted by registry <manager@tznic.or.tz>
+tz
+ac.tz
+co.tz
+go.tz
+hotel.tz
+info.tz
+me.tz
+mil.tz
+mobi.tz
+ne.tz
+or.tz
+sc.tz
+tv.tz
+
+// ua : https://hostmaster.ua/policy/?ua
+// Submitted by registry <dk@cctld.ua>
+ua
+// ua 2LD
+com.ua
+edu.ua
+gov.ua
+in.ua
+net.ua
+org.ua
+// ua geographic names
+// https://hostmaster.ua/2ld/
+cherkassy.ua
+cherkasy.ua
+chernigov.ua
+chernihiv.ua
+chernivtsi.ua
+chernovtsy.ua
+ck.ua
+cn.ua
+cr.ua
+crimea.ua
+cv.ua
+dn.ua
+dnepropetrovsk.ua
+dnipropetrovsk.ua
+donetsk.ua
+dp.ua
+if.ua
+ivano-frankivsk.ua
+kh.ua
+kharkiv.ua
+kharkov.ua
+kherson.ua
+khmelnitskiy.ua
+khmelnytskyi.ua
+kiev.ua
+kirovograd.ua
+km.ua
+kr.ua
+kropyvnytskyi.ua
+krym.ua
+ks.ua
+kv.ua
+kyiv.ua
+lg.ua
+lt.ua
+lugansk.ua
+lutsk.ua
+lv.ua
+lviv.ua
+mk.ua
+mykolaiv.ua
+nikolaev.ua
+od.ua
+odesa.ua
+odessa.ua
+pl.ua
+poltava.ua
+rivne.ua
+rovno.ua
+rv.ua
+sb.ua
+sebastopol.ua
+sevastopol.ua
+sm.ua
+sumy.ua
+te.ua
+ternopil.ua
+uz.ua
+uzhgorod.ua
+vinnica.ua
+vinnytsia.ua
+vn.ua
+volyn.ua
+yalta.ua
+zaporizhzhe.ua
+zaporizhzhia.ua
+zhitomir.ua
+zhytomyr.ua
+zp.ua
+zt.ua
+
+// ug : https://www.registry.co.ug/
+ug
+co.ug
+or.ug
+ac.ug
+sc.ug
+go.ug
+ne.ug
+com.ug
+org.ug
+
+// uk : https://en.wikipedia.org/wiki/.uk
+// Submitted by registry <Michael.Daly@nominet.org.uk>
+uk
+ac.uk
+co.uk
+gov.uk
+ltd.uk
+me.uk
+net.uk
+nhs.uk
+org.uk
+plc.uk
+police.uk
+*.sch.uk
+
+// us : https://en.wikipedia.org/wiki/.us
+us
+dni.us
+fed.us
+isa.us
+kids.us
+nsn.us
+// us geographic names
+ak.us
+al.us
+ar.us
+as.us
+az.us
+ca.us
+co.us
+ct.us
+dc.us
+de.us
+fl.us
+ga.us
+gu.us
+hi.us
+ia.us
+id.us
+il.us
+in.us
+ks.us
+ky.us
+la.us
+ma.us
+md.us
+me.us
+mi.us
+mn.us
+mo.us
+ms.us
+mt.us
+nc.us
+nd.us
+ne.us
+nh.us
+nj.us
+nm.us
+nv.us
+ny.us
+oh.us
+ok.us
+or.us
+pa.us
+pr.us
+ri.us
+sc.us
+sd.us
+tn.us
+tx.us
+ut.us
+vi.us
+vt.us
+va.us
+wa.us
+wi.us
+wv.us
+wy.us
+// The registrar notes several more specific domains available in each state,
+// such as state.*.us, dst.*.us, etc., but resolution of these is somewhat
+// haphazard; in some states these domains resolve as addresses, while in others
+// only subdomains are available, or even nothing at all. We include the
+// most common ones where it's clear that different sites are different
+// entities.
+k12.ak.us
+k12.al.us
+k12.ar.us
+k12.as.us
+k12.az.us
+k12.ca.us
+k12.co.us
+k12.ct.us
+k12.dc.us
+k12.de.us
+k12.fl.us
+k12.ga.us
+k12.gu.us
+// k12.hi.us Bug 614565 - Hawaii has a state-wide DOE login
+k12.ia.us
+k12.id.us
+k12.il.us
+k12.in.us
+k12.ks.us
+k12.ky.us
+k12.la.us
+k12.ma.us
+k12.md.us
+k12.me.us
+k12.mi.us
+k12.mn.us
+k12.mo.us
+k12.ms.us
+k12.mt.us
+k12.nc.us
+// k12.nd.us Bug 1028347 - Removed at request of Travis Rosso <trossow@nd.gov>
+k12.ne.us
+k12.nh.us
+k12.nj.us
+k12.nm.us
+k12.nv.us
+k12.ny.us
+k12.oh.us
+k12.ok.us
+k12.or.us
+k12.pa.us
+k12.pr.us
+// k12.ri.us Removed at request of Kim Cournoyer <netsupport@staff.ri.net>
+k12.sc.us
+// k12.sd.us Bug 934131 - Removed at request of James Booze <James.Booze@k12.sd.us>
+k12.tn.us
+k12.tx.us
+k12.ut.us
+k12.vi.us
+k12.vt.us
+k12.va.us
+k12.wa.us
+k12.wi.us
+// k12.wv.us Bug 947705 - Removed at request of Verne Britton <verne@wvnet.edu>
+k12.wy.us
+cc.ak.us
+cc.al.us
+cc.ar.us
+cc.as.us
+cc.az.us
+cc.ca.us
+cc.co.us
+cc.ct.us
+cc.dc.us
+cc.de.us
+cc.fl.us
+cc.ga.us
+cc.gu.us
+cc.hi.us
+cc.ia.us
+cc.id.us
+cc.il.us
+cc.in.us
+cc.ks.us
+cc.ky.us
+cc.la.us
+cc.ma.us
+cc.md.us
+cc.me.us
+cc.mi.us
+cc.mn.us
+cc.mo.us
+cc.ms.us
+cc.mt.us
+cc.nc.us
+cc.nd.us
+cc.ne.us
+cc.nh.us
+cc.nj.us
+cc.nm.us
+cc.nv.us
+cc.ny.us
+cc.oh.us
+cc.ok.us
+cc.or.us
+cc.pa.us
+cc.pr.us
+cc.ri.us
+cc.sc.us
+cc.sd.us
+cc.tn.us
+cc.tx.us
+cc.ut.us
+cc.vi.us
+cc.vt.us
+cc.va.us
+cc.wa.us
+cc.wi.us
+cc.wv.us
+cc.wy.us
+lib.ak.us
+lib.al.us
+lib.ar.us
+lib.as.us
+lib.az.us
+lib.ca.us
+lib.co.us
+lib.ct.us
+lib.dc.us
+// lib.de.us Issue #243 - Moved to Private section at request of Ed Moore <Ed.Moore@lib.de.us>
+lib.fl.us
+lib.ga.us
+lib.gu.us
+lib.hi.us
+lib.ia.us
+lib.id.us
+lib.il.us
+lib.in.us
+lib.ks.us
+lib.ky.us
+lib.la.us
+lib.ma.us
+lib.md.us
+lib.me.us
+lib.mi.us
+lib.mn.us
+lib.mo.us
+lib.ms.us
+lib.mt.us
+lib.nc.us
+lib.nd.us
+lib.ne.us
+lib.nh.us
+lib.nj.us
+lib.nm.us
+lib.nv.us
+lib.ny.us
+lib.oh.us
+lib.ok.us
+lib.or.us
+lib.pa.us
+lib.pr.us
+lib.ri.us
+lib.sc.us
+lib.sd.us
+lib.tn.us
+lib.tx.us
+lib.ut.us
+lib.vi.us
+lib.vt.us
+lib.va.us
+lib.wa.us
+lib.wi.us
+// lib.wv.us Bug 941670 - Removed at request of Larry W Arnold <arnold@wvlc.lib.wv.us>
+lib.wy.us
+// k12.ma.us contains school districts in Massachusetts. The 4LDs are
+// managed independently except for private (PVT), charter (CHTR) and
+// parochial (PAROCH) schools. Those are delegated directly to the
+// 5LD operators. <k12-ma-hostmaster _ at _ rsuc.gweep.net>
+pvt.k12.ma.us
+chtr.k12.ma.us
+paroch.k12.ma.us
+// Merit Network, Inc. maintains the registry for =~ /(k12|cc|lib).mi.us/ and the following
+// see also: http://domreg.merit.edu
+// see also: whois -h whois.domreg.merit.edu help
+ann-arbor.mi.us
+cog.mi.us
+dst.mi.us
+eaton.mi.us
+gen.mi.us
+mus.mi.us
+tec.mi.us
+washtenaw.mi.us
+
+// uy : http://www.nic.org.uy/
+uy
+com.uy
+edu.uy
+gub.uy
+mil.uy
+net.uy
+org.uy
+
+// uz : http://www.reg.uz/
+uz
+co.uz
+com.uz
+net.uz
+org.uz
+
+// va : https://en.wikipedia.org/wiki/.va
+va
+
+// vc : https://en.wikipedia.org/wiki/.vc
+// Submitted by registry <kshah@ca.afilias.info>
+vc
+com.vc
+net.vc
+org.vc
+gov.vc
+mil.vc
+edu.vc
+
+// ve : https://registro.nic.ve/
+// Submitted by registry nic@nic.ve and nicve@conatel.gob.ve
+ve
+arts.ve
+bib.ve
+co.ve
+com.ve
+e12.ve
+edu.ve
+firm.ve
+gob.ve
+gov.ve
+info.ve
+int.ve
+mil.ve
+net.ve
+nom.ve
+org.ve
+rar.ve
+rec.ve
+store.ve
+tec.ve
+web.ve
+
+// vg : https://en.wikipedia.org/wiki/.vg
+vg
+
+// vi : http://www.nic.vi/newdomainform.htm
+// http://www.nic.vi/Domain_Rules/body_domain_rules.html indicates some other
+// TLDs are "reserved", such as edu.vi and gov.vi, but doesn't actually say they
+// are available for registration (which they do not seem to be).
+vi
+co.vi
+com.vi
+k12.vi
+net.vi
+org.vi
+
+// vn : https://www.vnnic.vn/en/domain/cctld-vn
+// https://vnnic.vn/sites/default/files/tailieu/vn.cctld.domains.txt
+vn
+ac.vn
+ai.vn
+biz.vn
+com.vn
+edu.vn
+gov.vn
+health.vn
+id.vn
+info.vn
+int.vn
+io.vn
+name.vn
+net.vn
+org.vn
+pro.vn
+
+// vn geographical names
+angiang.vn
+bacgiang.vn
+backan.vn
+baclieu.vn
+bacninh.vn
+baria-vungtau.vn
+bentre.vn
+binhdinh.vn
+binhduong.vn
+binhphuoc.vn
+binhthuan.vn
+camau.vn
+cantho.vn
+caobang.vn
+daklak.vn
+daknong.vn
+danang.vn
+dienbien.vn
+dongnai.vn
+dongthap.vn
+gialai.vn
+hagiang.vn
+haiduong.vn
+haiphong.vn
+hanam.vn
+hanoi.vn
+hatinh.vn
+haugiang.vn
+hoabinh.vn
+hungyen.vn
+khanhhoa.vn
+kiengiang.vn
+kontum.vn
+laichau.vn
+lamdong.vn
+langson.vn
+laocai.vn
+longan.vn
+namdinh.vn
+nghean.vn
+ninhbinh.vn
+ninhthuan.vn
+phutho.vn
+phuyen.vn
+quangbinh.vn
+quangnam.vn
+quangngai.vn
+quangninh.vn
+quangtri.vn
+soctrang.vn
+sonla.vn
+tayninh.vn
+thaibinh.vn
+thainguyen.vn
+thanhhoa.vn
+thanhphohochiminh.vn
+thuathienhue.vn
+tiengiang.vn
+travinh.vn
+tuyenquang.vn
+vinhlong.vn
+vinhphuc.vn
+yenbai.vn
+
+// vu : https://en.wikipedia.org/wiki/.vu
+// http://www.vunic.vu/
+vu
+com.vu
+edu.vu
+net.vu
+org.vu
+
+// wf : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf
+wf
+
+// ws : https://en.wikipedia.org/wiki/.ws
+// http://samoanic.ws/index.dhtml
+ws
+com.ws
+net.ws
+org.ws
+gov.ws
+edu.ws
+
+// yt : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf
+yt
+
+// IDN ccTLDs
+// When submitting patches, please maintain a sort by ISO 3166 ccTLD, then
+// U-label, and follow this format:
+// // A-Label ("<Latin renderings>", <language name>[, variant info]) : <ISO 3166 ccTLD>
+// // [sponsoring org]
+// U-Label
+
+// xn--mgbaam7a8h ("Emerat", Arabic) : AE
+// http://nic.ae/english/arabicdomain/rules.jsp
+امارات
+
+// xn--y9a3aq ("hye", Armenian) : AM
+// ISOC AM (operated by .am Registry)
+հայ
+
+// xn--54b7fta0cc ("Bangla", Bangla) : BD
+বাংলা
+
+// xn--90ae ("bg", Bulgarian) : BG
+бг
+
+// xn--mgbcpq6gpa1a ("albahrain", Arabic) : BH
+البحرين
+
+// xn--90ais ("bel", Belarusian/Russian Cyrillic) : BY
+// Operated by .by registry
+бел
+
+// xn--fiqs8s ("Zhongguo/China", Chinese, Simplified) : CN
+// CNNIC
+// http://cnnic.cn/html/Dir/2005/10/11/3218.htm
+中国
+
+// xn--fiqz9s ("Zhongguo/China", Chinese, Traditional) : CN
+// CNNIC
+// http://cnnic.cn/html/Dir/2005/10/11/3218.htm
+中國
+
+// xn--lgbbat1ad8j ("Algeria/Al Jazair", Arabic) : DZ
+الجزائر
+
+// xn--wgbh1c ("Egypt/Masr", Arabic) : EG
+// http://www.dotmasr.eg/
+مصر
+
+// xn--e1a4c ("eu", Cyrillic) : EU
+// https://eurid.eu
+ею
+
+// xn--qxa6a ("eu", Greek) : EU
+// https://eurid.eu
+ευ
+
+// xn--mgbah1a3hjkrd ("Mauritania", Arabic) : MR
+موريتانيا
+
+// xn--node ("ge", Georgian Mkhedruli) : GE
+გე
+
+// xn--qxam ("el", Greek) : GR
+// Hellenic Ministry of Infrastructure, Transport, and Networks
+ελ
+
+// xn--j6w193g ("Hong Kong", Chinese) : HK
+// https://www.hkirc.hk
+// Submitted by registry <hk.tech@hkirc.hk>
+// https://www.hkirc.hk/content.jsp?id=30#!/34
+香港
+公司.香港
+教育.香港
+政府.香港
+個人.香港
+網絡.香港
+組織.香港
+
+// xn--2scrj9c ("Bharat", Kannada) : IN
+// India
+ಭಾರತ
+
+// xn--3hcrj9c ("Bharat", Oriya) : IN
+// India
+ଭାରତ
+
+// xn--45br5cyl ("Bharatam", Assamese) : IN
+// India
+ভাৰত
+
+// xn--h2breg3eve ("Bharatam", Sanskrit) : IN
+// India
+भारतम्
+
+// xn--h2brj9c8c ("Bharot", Santali) : IN
+// India
+भारोत
+
+// xn--mgbgu82a ("Bharat", Sindhi) : IN
+// India
+ڀارت
+
+// xn--rvc1e0am3e ("Bharatam", Malayalam) : IN
+// India
+ഭാരതം
+
+// xn--h2brj9c ("Bharat", Devanagari) : IN
+// India
+भारत
+
+// xn--mgbbh1a ("Bharat", Kashmiri) : IN
+// India
+بارت
+
+// xn--mgbbh1a71e ("Bharat", Arabic) : IN
+// India
+بھارت
+
+// xn--fpcrj9c3d ("Bharat", Telugu) : IN
+// India
+భారత్
+
+// xn--gecrj9c ("Bharat", Gujarati) : IN
+// India
+ભારત
+
+// xn--s9brj9c ("Bharat", Gurmukhi) : IN
+// India
+ਭਾਰਤ
+
+// xn--45brj9c ("Bharat", Bengali) : IN
+// India
+ভারত
+
+// xn--xkc2dl3a5ee0h ("India", Tamil) : IN
+// India
+இந்தியா
+
+// xn--mgba3a4f16a ("Iran", Persian) : IR
+ایران
+
+// xn--mgba3a4fra ("Iran", Arabic) : IR
+ايران
+
+// xn--mgbtx2b ("Iraq", Arabic) : IQ
+// Communications and Media Commission
+عراق
+
+// xn--mgbayh7gpa ("al-Ordon", Arabic) : JO
+// National Information Technology Center (NITC)
+// Royal Scientific Society, Al-Jubeiha
+الاردن
+
+// xn--3e0b707e ("Republic of Korea", Hangul) : KR
+한국
+
+// xn--80ao21a ("Kaz", Kazakh) : KZ
+қаз
+
+// xn--q7ce6a ("Lao", Lao) : LA
+ລາວ
+
+// xn--fzc2c9e2c ("Lanka", Sinhalese-Sinhala) : LK
+// https://nic.lk
+ලංකා
+
+// xn--xkc2al3hye2a ("Ilangai", Tamil) : LK
+// https://nic.lk
+இலங்கை
+
+// xn--mgbc0a9azcg ("Morocco/al-Maghrib", Arabic) : MA
+المغرب
+
+// xn--d1alf ("mkd", Macedonian) : MK
+// MARnet
+мкд
+
+// xn--l1acc ("mon", Mongolian) : MN
+мон
+
+// xn--mix891f ("Macao", Chinese, Traditional) : MO
+// MONIC / HNET Asia (Registry Operator for .mo)
+澳門
+
+// xn--mix082f ("Macao", Chinese, Simplified) : MO
+澳门
+
+// xn--mgbx4cd0ab ("Malaysia", Malay) : MY
+مليسيا
+
+// xn--mgb9awbf ("Oman", Arabic) : OM
+عمان
+
+// xn--mgbai9azgqp6j ("Pakistan", Urdu/Arabic) : PK
+پاکستان
+
+// xn--mgbai9a5eva00b ("Pakistan", Urdu/Arabic, variant) : PK
+پاكستان
+
+// xn--ygbi2ammx ("Falasteen", Arabic) : PS
+// The Palestinian National Internet Naming Authority (PNINA)
+// http://www.pnina.ps
+فلسطين
+
+// xn--90a3ac ("srb", Cyrillic) : RS
+// https://www.rnids.rs/en/domains/national-domains
+срб
+пр.срб
+орг.срб
+обр.срб
+од.срб
+упр.срб
+ак.срб
+
+// xn--p1ai ("rf", Russian-Cyrillic) : RU
+// https://cctld.ru/files/pdf/docs/en/rules_ru-rf.pdf
+// Submitted by George Georgievsky <gug@cctld.ru>
+рф
+
+// xn--wgbl6a ("Qatar", Arabic) : QA
+// http://www.ict.gov.qa/
+قطر
+
+// xn--mgberp4a5d4ar ("AlSaudiah", Arabic) : SA
+// http://www.nic.net.sa/
+السعودية
+
+// xn--mgberp4a5d4a87g ("AlSaudiah", Arabic, variant) : SA
+السعودیة
+
+// xn--mgbqly7c0a67fbc ("AlSaudiah", Arabic, variant) : SA
+السعودیۃ
+
+// xn--mgbqly7cvafr ("AlSaudiah", Arabic, variant) : SA
+السعوديه
+
+// xn--mgbpl2fh ("sudan", Arabic) : SD
+// Operated by .sd registry
+سودان
+
+// xn--yfro4i67o Singapore ("Singapore", Chinese) : SG
+新加坡
+
+// xn--clchc0ea0b2g2a9gcd ("Singapore", Tamil) : SG
+சிங்கப்பூர்
+
+// xn--ogbpf8fl ("Syria", Arabic) : SY
+سورية
+
+// xn--mgbtf8fl ("Syria", Arabic, variant) : SY
+سوريا
+
+// xn--o3cw4h ("Thai", Thai) : TH
+// http://www.thnic.co.th
+ไทย
+ศึกษา.ไทย
+ธุรกิจ.ไทย
+รัฐบาล.ไทย
+ทหาร.ไทย
+เน็ต.ไทย
+องค์กร.ไทย
+
+// xn--pgbs0dh ("Tunisia", Arabic) : TN
+// http://nic.tn
+تونس
+
+// xn--kpry57d ("Taiwan", Chinese, Traditional) : TW
+// http://www.twnic.net/english/dn/dn_07a.htm
+台灣
+
+// xn--kprw13d ("Taiwan", Chinese, Simplified) : TW
+// http://www.twnic.net/english/dn/dn_07a.htm
+台湾
+
+// xn--nnx388a ("Taiwan", Chinese, variant) : TW
+臺灣
+
+// xn--j1amh ("ukr", Cyrillic) : UA
+укр
+
+// xn--mgb2ddes ("AlYemen", Arabic) : YE
+اليمن
+
+// xxx : http://icmregistry.com
+xxx
+
+// ye : http://www.y.net.ye/services/domain_name.htm
+ye
+com.ye
+edu.ye
+gov.ye
+net.ye
+mil.ye
+org.ye
+
+// za : https://www.zadna.org.za/content/page/domain-information/
+ac.za
+agric.za
+alt.za
+co.za
+edu.za
+gov.za
+grondar.za
+law.za
+mil.za
+net.za
+ngo.za
+nic.za
+nis.za
+nom.za
+org.za
+school.za
+tm.za
+web.za
+
+// zm : https://zicta.zm/
+// Submitted by registry <info@zicta.zm>
+zm
+ac.zm
+biz.zm
+co.zm
+com.zm
+edu.zm
+gov.zm
+info.zm
+mil.zm
+net.zm
+org.zm
+sch.zm
+
+// zw : https://www.potraz.gov.zw/
+// Confirmed by registry <bmtengwa@potraz.gov.zw> 2017-01-25
+zw
+ac.zw
+co.zw
+gov.zw
+mil.zw
+org.zw
+
+
+// newGTLDs
+
+// List of new gTLDs imported from https://www.icann.org/resources/registries/gtlds/v2/gtlds.json on 2023-06-16T15:12:40Z
+// This list is auto-generated, don't edit it manually.
+// aaa : 2015-02-26 American Automobile Association, Inc.
+aaa
+
+// aarp : 2015-05-21 AARP
+aarp
+
+// abb : 2014-10-24 ABB Ltd
+abb
+
+// abbott : 2014-07-24 Abbott Laboratories, Inc.
+abbott
+
+// abbvie : 2015-07-30 AbbVie Inc.
+abbvie
+
+// abc : 2015-07-30 Disney Enterprises, Inc.
+abc
+
+// able : 2015-06-25 Able Inc.
+able
+
+// abogado : 2014-04-24 Registry Services, LLC
+abogado
+
+// abudhabi : 2015-07-30 Abu Dhabi Systems and Information Centre
+abudhabi
+
+// academy : 2013-11-07 Binky Moon, LLC
+academy
+
+// accenture : 2014-08-15 Accenture plc
+accenture
+
+// accountant : 2014-11-20 dot Accountant Limited
+accountant
+
+// accountants : 2014-03-20 Binky Moon, LLC
+accountants
+
+// aco : 2015-01-08 ACO Severin Ahlmann GmbH & Co. KG
+aco
+
+// actor : 2013-12-12 Dog Beach, LLC
+actor
+
+// ads : 2014-12-04 Charleston Road Registry Inc.
+ads
+
+// adult : 2014-10-16 ICM Registry AD LLC
+adult
+
+// aeg : 2015-03-19 Aktiebolaget Electrolux
+aeg
+
+// aetna : 2015-05-21 Aetna Life Insurance Company
+aetna
+
+// afl : 2014-10-02 Australian Football League
+afl
+
+// africa : 2014-03-24 ZA Central Registry NPC trading as Registry.Africa
+africa
+
+// agakhan : 2015-04-23 Fondation Aga Khan (Aga Khan Foundation)
+agakhan
+
+// agency : 2013-11-14 Binky Moon, LLC
+agency
+
+// aig : 2014-12-18 American International Group, Inc.
+aig
+
+// airbus : 2015-07-30 Airbus S.A.S.
+airbus
+
+// airforce : 2014-03-06 Dog Beach, LLC
+airforce
+
+// airtel : 2014-10-24 Bharti Airtel Limited
+airtel
+
+// akdn : 2015-04-23 Fondation Aga Khan (Aga Khan Foundation)
+akdn
+
+// alibaba : 2015-01-15 Alibaba Group Holding Limited
+alibaba
+
+// alipay : 2015-01-15 Alibaba Group Holding Limited
+alipay
+
+// allfinanz : 2014-07-03 Allfinanz Deutsche Vermögensberatung Aktiengesellschaft
+allfinanz
+
+// allstate : 2015-07-31 Allstate Fire and Casualty Insurance Company
+allstate
+
+// ally : 2015-06-18 Ally Financial Inc.
+ally
+
+// alsace : 2014-07-02 Region Grand Est
+alsace
+
+// alstom : 2015-07-30 ALSTOM
+alstom
+
+// amazon : 2019-12-19 Amazon Registry Services, Inc.
+amazon
+
+// americanexpress : 2015-07-31 American Express Travel Related Services Company, Inc.
+americanexpress
+
+// americanfamily : 2015-07-23 AmFam, Inc.
+americanfamily
+
+// amex : 2015-07-31 American Express Travel Related Services Company, Inc.
+amex
+
+// amfam : 2015-07-23 AmFam, Inc.
+amfam
+
+// amica : 2015-05-28 Amica Mutual Insurance Company
+amica
+
+// amsterdam : 2014-07-24 Gemeente Amsterdam
+amsterdam
+
+// analytics : 2014-12-18 Campus IP LLC
+analytics
+
+// android : 2014-08-07 Charleston Road Registry Inc.
+android
+
+// anquan : 2015-01-08 Beijing Qihu Keji Co., Ltd.
+anquan
+
+// anz : 2015-07-31 Australia and New Zealand Banking Group Limited
+anz
+
+// aol : 2015-09-17 Oath Inc.
+aol
+
+// apartments : 2014-12-11 Binky Moon, LLC
+apartments
+
+// app : 2015-05-14 Charleston Road Registry Inc.
+app
+
+// apple : 2015-05-14 Apple Inc.
+apple
+
+// aquarelle : 2014-07-24 Aquarelle.com
+aquarelle
+
+// arab : 2015-11-12 League of Arab States
+arab
+
+// aramco : 2014-11-20 Aramco Services Company
+aramco
+
+// archi : 2014-02-06 Identity Digital Limited
+archi
+
+// army : 2014-03-06 Dog Beach, LLC
+army
+
+// art : 2016-03-24 UK Creative Ideas Limited
+art
+
+// arte : 2014-12-11 Association Relative à la Télévision Européenne G.E.I.E.
+arte
+
+// asda : 2015-07-31 Wal-Mart Stores, Inc.
+asda
+
+// associates : 2014-03-06 Binky Moon, LLC
+associates
+
+// athleta : 2015-07-30 The Gap, Inc.
+athleta
+
+// attorney : 2014-03-20 Dog Beach, LLC
+attorney
+
+// auction : 2014-03-20 Dog Beach, LLC
+auction
+
+// audi : 2015-05-21 AUDI Aktiengesellschaft
+audi
+
+// audible : 2015-06-25 Amazon Registry Services, Inc.
+audible
+
+// audio : 2014-03-20 XYZ.COM LLC
+audio
+
+// auspost : 2015-08-13 Australian Postal Corporation
+auspost
+
+// author : 2014-12-18 Amazon Registry Services, Inc.
+author
+
+// auto : 2014-11-13 XYZ.COM LLC
+auto
+
+// autos : 2014-01-09 XYZ.COM LLC
+autos
+
+// avianca : 2015-01-08 Avianca Inc.
+avianca
+
+// aws : 2015-06-25 AWS Registry LLC
+aws
+
+// axa : 2013-12-19 AXA Group Operations SAS
+axa
+
+// azure : 2014-12-18 Microsoft Corporation
+azure
+
+// baby : 2015-04-09 XYZ.COM LLC
+baby
+
+// baidu : 2015-01-08 Baidu, Inc.
+baidu
+
+// banamex : 2015-07-30 Citigroup Inc.
+banamex
+
+// bananarepublic : 2015-07-31 The Gap, Inc.
+bananarepublic
+
+// band : 2014-06-12 Dog Beach, LLC
+band
+
+// bank : 2014-09-25 fTLD Registry Services LLC
+bank
+
+// bar : 2013-12-12 Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable
+bar
+
+// barcelona : 2014-07-24 Municipi de Barcelona
+barcelona
+
+// barclaycard : 2014-11-20 Barclays Bank PLC
+barclaycard
+
+// barclays : 2014-11-20 Barclays Bank PLC
+barclays
+
+// barefoot : 2015-06-11 Gallo Vineyards, Inc.
+barefoot
+
+// bargains : 2013-11-14 Binky Moon, LLC
+bargains
+
+// baseball : 2015-10-29 MLB Advanced Media DH, LLC
+baseball
+
+// basketball : 2015-08-20 Fédération Internationale de Basketball (FIBA)
+basketball
+
+// bauhaus : 2014-04-17 Werkhaus GmbH
+bauhaus
+
+// bayern : 2014-01-23 Bayern Connect GmbH
+bayern
+
+// bbc : 2014-12-18 British Broadcasting Corporation
+bbc
+
+// bbt : 2015-07-23 BB&T Corporation
+bbt
+
+// bbva : 2014-10-02 BANCO BILBAO VIZCAYA ARGENTARIA, S.A.
+bbva
+
+// bcg : 2015-04-02 The Boston Consulting Group, Inc.
+bcg
+
+// bcn : 2014-07-24 Municipi de Barcelona
+bcn
+
+// beats : 2015-05-14 Beats Electronics, LLC
+beats
+
+// beauty : 2015-12-03 XYZ.COM LLC
+beauty
+
+// beer : 2014-01-09 Registry Services, LLC
+beer
+
+// bentley : 2014-12-18 Bentley Motors Limited
+bentley
+
+// berlin : 2013-10-31 dotBERLIN GmbH & Co. KG
+berlin
+
+// best : 2013-12-19 BestTLD Pty Ltd
+best
+
+// bestbuy : 2015-07-31 BBY Solutions, Inc.
+bestbuy
+
+// bet : 2015-05-07 Identity Digital Limited
+bet
+
+// bharti : 2014-01-09 Bharti Enterprises (Holding) Private Limited
+bharti
+
+// bible : 2014-06-19 American Bible Society
+bible
+
+// bid : 2013-12-19 dot Bid Limited
+bid
+
+// bike : 2013-08-27 Binky Moon, LLC
+bike
+
+// bing : 2014-12-18 Microsoft Corporation
+bing
+
+// bingo : 2014-12-04 Binky Moon, LLC
+bingo
+
+// bio : 2014-03-06 Identity Digital Limited
+bio
+
+// black : 2014-01-16 Identity Digital Limited
+black
+
+// blackfriday : 2014-01-16 Registry Services, LLC
+blackfriday
+
+// blockbuster : 2015-07-30 Dish DBS Corporation
+blockbuster
+
+// blog : 2015-05-14 Knock Knock WHOIS There, LLC
+blog
+
+// bloomberg : 2014-07-17 Bloomberg IP Holdings LLC
+bloomberg
+
+// blue : 2013-11-07 Identity Digital Limited
+blue
+
+// bms : 2014-10-30 Bristol-Myers Squibb Company
+bms
+
+// bmw : 2014-01-09 Bayerische Motoren Werke Aktiengesellschaft
+bmw
+
+// bnpparibas : 2014-05-29 BNP Paribas
+bnpparibas
+
+// boats : 2014-12-04 XYZ.COM LLC
+boats
+
+// boehringer : 2015-07-09 Boehringer Ingelheim International GmbH
+boehringer
+
+// bofa : 2015-07-31 Bank of America Corporation
+bofa
+
+// bom : 2014-10-16 Núcleo de Informação e Coordenação do Ponto BR - NIC.br
+bom
+
+// bond : 2014-06-05 ShortDot SA
+bond
+
+// boo : 2014-01-30 Charleston Road Registry Inc.
+boo
+
+// book : 2015-08-27 Amazon Registry Services, Inc.
+book
+
+// booking : 2015-07-16 Booking.com B.V.
+booking
+
+// bosch : 2015-06-18 Robert Bosch GMBH
+bosch
+
+// bostik : 2015-05-28 Bostik SA
+bostik
+
+// boston : 2015-12-10 Registry Services, LLC
+boston
+
+// bot : 2014-12-18 Amazon Registry Services, Inc.
+bot
+
+// boutique : 2013-11-14 Binky Moon, LLC
+boutique
+
+// box : 2015-11-12 Intercap Registry Inc.
+box
+
+// bradesco : 2014-12-18 Banco Bradesco S.A.
+bradesco
+
+// bridgestone : 2014-12-18 Bridgestone Corporation
+bridgestone
+
+// broadway : 2014-12-22 Celebrate Broadway, Inc.
+broadway
+
+// broker : 2014-12-11 Dog Beach, LLC
+broker
+
+// brother : 2015-01-29 Brother Industries, Ltd.
+brother
+
+// brussels : 2014-02-06 DNS.be vzw
+brussels
+
+// build : 2013-11-07 Plan Bee LLC
+build
+
+// builders : 2013-11-07 Binky Moon, LLC
+builders
+
+// business : 2013-11-07 Binky Moon, LLC
+business
+
+// buy : 2014-12-18 Amazon Registry Services, Inc.
+buy
+
+// buzz : 2013-10-02 DOTSTRATEGY CO.
+buzz
+
+// bzh : 2014-02-27 Association www.bzh
+bzh
+
+// cab : 2013-10-24 Binky Moon, LLC
+cab
+
+// cafe : 2015-02-11 Binky Moon, LLC
+cafe
+
+// cal : 2014-07-24 Charleston Road Registry Inc.
+cal
+
+// call : 2014-12-18 Amazon Registry Services, Inc.
+call
+
+// calvinklein : 2015-07-30 PVH gTLD Holdings LLC
+calvinklein
+
+// cam : 2016-04-21 Cam Connecting SARL
+cam
+
+// camera : 2013-08-27 Binky Moon, LLC
+camera
+
+// camp : 2013-11-07 Binky Moon, LLC
+camp
+
+// canon : 2014-09-12 Canon Inc.
+canon
+
+// capetown : 2014-03-24 ZA Central Registry NPC trading as ZA Central Registry
+capetown
+
+// capital : 2014-03-06 Binky Moon, LLC
+capital
+
+// capitalone : 2015-08-06 Capital One Financial Corporation
+capitalone
+
+// car : 2015-01-22 XYZ.COM LLC
+car
+
+// caravan : 2013-12-12 Caravan International, Inc.
+caravan
+
+// cards : 2013-12-05 Binky Moon, LLC
+cards
+
+// care : 2014-03-06 Binky Moon, LLC
+care
+
+// career : 2013-10-09 dotCareer LLC
+career
+
+// careers : 2013-10-02 Binky Moon, LLC
+careers
+
+// cars : 2014-11-13 XYZ.COM LLC
+cars
+
+// casa : 2013-11-21 Registry Services, LLC
+casa
+
+// case : 2015-09-03 Digity, LLC
+case
+
+// cash : 2014-03-06 Binky Moon, LLC
+cash
+
+// casino : 2014-12-18 Binky Moon, LLC
+casino
+
+// catering : 2013-12-05 Binky Moon, LLC
+catering
+
+// catholic : 2015-10-21 Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication)
+catholic
+
+// cba : 2014-06-26 COMMONWEALTH BANK OF AUSTRALIA
+cba
+
+// cbn : 2014-08-22 The Christian Broadcasting Network, Inc.
+cbn
+
+// cbre : 2015-07-02 CBRE, Inc.
+cbre
+
+// cbs : 2015-08-06 CBS Domains Inc.
+cbs
+
+// center : 2013-11-07 Binky Moon, LLC
+center
+
+// ceo : 2013-11-07 CEOTLD Pty Ltd
+ceo
+
+// cern : 2014-06-05 European Organization for Nuclear Research ("CERN")
+cern
+
+// cfa : 2014-08-28 CFA Institute
+cfa
+
+// cfd : 2014-12-11 ShortDot SA
+cfd
+
+// chanel : 2015-04-09 Chanel International B.V.
+chanel
+
+// channel : 2014-05-08 Charleston Road Registry Inc.
+channel
+
+// charity : 2018-04-11 Public Interest Registry
+charity
+
+// chase : 2015-04-30 JPMorgan Chase Bank, National Association
+chase
+
+// chat : 2014-12-04 Binky Moon, LLC
+chat
+
+// cheap : 2013-11-14 Binky Moon, LLC
+cheap
+
+// chintai : 2015-06-11 CHINTAI Corporation
+chintai
+
+// christmas : 2013-11-21 XYZ.COM LLC
+christmas
+
+// chrome : 2014-07-24 Charleston Road Registry Inc.
+chrome
+
+// church : 2014-02-06 Binky Moon, LLC
+church
+
+// cipriani : 2015-02-19 Hotel Cipriani Srl
+cipriani
+
+// circle : 2014-12-18 Amazon Registry Services, Inc.
+circle
+
+// cisco : 2014-12-22 Cisco Technology, Inc.
+cisco
+
+// citadel : 2015-07-23 Citadel Domain LLC
+citadel
+
+// citi : 2015-07-30 Citigroup Inc.
+citi
+
+// citic : 2014-01-09 CITIC Group Corporation
+citic
+
+// city : 2014-05-29 Binky Moon, LLC
+city
+
+// cityeats : 2014-12-11 Lifestyle Domain Holdings, Inc.
+cityeats
+
+// claims : 2014-03-20 Binky Moon, LLC
+claims
+
+// cleaning : 2013-12-05 Binky Moon, LLC
+cleaning
+
+// click : 2014-06-05 Internet Naming Company LLC
+click
+
+// clinic : 2014-03-20 Binky Moon, LLC
+clinic
+
+// clinique : 2015-10-01 The Estée Lauder Companies Inc.
+clinique
+
+// clothing : 2013-08-27 Binky Moon, LLC
+clothing
+
+// cloud : 2015-04-16 Aruba PEC S.p.A.
+cloud
+
+// club : 2013-11-08 Registry Services, LLC
+club
+
+// clubmed : 2015-06-25 Club Méditerranée S.A.
+clubmed
+
+// coach : 2014-10-09 Binky Moon, LLC
+coach
+
+// codes : 2013-10-31 Binky Moon, LLC
+codes
+
+// coffee : 2013-10-17 Binky Moon, LLC
+coffee
+
+// college : 2014-01-16 XYZ.COM LLC
+college
+
+// cologne : 2014-02-05 dotKoeln GmbH
+cologne
+
+// comcast : 2015-07-23 Comcast IP Holdings I, LLC
+comcast
+
+// commbank : 2014-06-26 COMMONWEALTH BANK OF AUSTRALIA
+commbank
+
+// community : 2013-12-05 Binky Moon, LLC
+community
+
+// company : 2013-11-07 Binky Moon, LLC
+company
+
+// compare : 2015-10-08 Registry Services, LLC
+compare
+
+// computer : 2013-10-24 Binky Moon, LLC
+computer
+
+// comsec : 2015-01-08 VeriSign, Inc.
+comsec
+
+// condos : 2013-12-05 Binky Moon, LLC
+condos
+
+// construction : 2013-09-16 Binky Moon, LLC
+construction
+
+// consulting : 2013-12-05 Dog Beach, LLC
+consulting
+
+// contact : 2015-01-08 Dog Beach, LLC
+contact
+
+// contractors : 2013-09-10 Binky Moon, LLC
+contractors
+
+// cooking : 2013-11-21 Registry Services, LLC
+cooking
+
+// cool : 2013-11-14 Binky Moon, LLC
+cool
+
+// corsica : 2014-09-25 Collectivité de Corse
+corsica
+
+// country : 2013-12-19 Internet Naming Company LLC
+country
+
+// coupon : 2015-02-26 Amazon Registry Services, Inc.
+coupon
+
+// coupons : 2015-03-26 Binky Moon, LLC
+coupons
+
+// courses : 2014-12-04 Registry Services, LLC
+courses
+
+// cpa : 2019-06-10 American Institute of Certified Public Accountants
+cpa
+
+// credit : 2014-03-20 Binky Moon, LLC
+credit
+
+// creditcard : 2014-03-20 Binky Moon, LLC
+creditcard
+
+// creditunion : 2015-01-22 DotCooperation LLC
+creditunion
+
+// cricket : 2014-10-09 dot Cricket Limited
+cricket
+
+// crown : 2014-10-24 Crown Equipment Corporation
+crown
+
+// crs : 2014-04-03 Federated Co-operatives Limited
+crs
+
+// cruise : 2015-12-10 Viking River Cruises (Bermuda) Ltd.
+cruise
+
+// cruises : 2013-12-05 Binky Moon, LLC
+cruises
+
+// cuisinella : 2014-04-03 SCHMIDT GROUPE S.A.S.
+cuisinella
+
+// cymru : 2014-05-08 Nominet UK
+cymru
+
+// cyou : 2015-01-22 ShortDot SA
+cyou
+
+// dabur : 2014-02-06 Dabur India Limited
+dabur
+
+// dad : 2014-01-23 Charleston Road Registry Inc.
+dad
+
+// dance : 2013-10-24 Dog Beach, LLC
+dance
+
+// data : 2016-06-02 Dish DBS Corporation
+data
+
+// date : 2014-11-20 dot Date Limited
+date
+
+// dating : 2013-12-05 Binky Moon, LLC
+dating
+
+// datsun : 2014-03-27 NISSAN MOTOR CO., LTD.
+datsun
+
+// day : 2014-01-30 Charleston Road Registry Inc.
+day
+
+// dclk : 2014-11-20 Charleston Road Registry Inc.
+dclk
+
+// dds : 2015-05-07 Registry Services, LLC
+dds
+
+// deal : 2015-06-25 Amazon Registry Services, Inc.
+deal
+
+// dealer : 2014-12-22 Intercap Registry Inc.
+dealer
+
+// deals : 2014-05-22 Binky Moon, LLC
+deals
+
+// degree : 2014-03-06 Dog Beach, LLC
+degree
+
+// delivery : 2014-09-11 Binky Moon, LLC
+delivery
+
+// dell : 2014-10-24 Dell Inc.
+dell
+
+// deloitte : 2015-07-31 Deloitte Touche Tohmatsu
+deloitte
+
+// delta : 2015-02-19 Delta Air Lines, Inc.
+delta
+
+// democrat : 2013-10-24 Dog Beach, LLC
+democrat
+
+// dental : 2014-03-20 Binky Moon, LLC
+dental
+
+// dentist : 2014-03-20 Dog Beach, LLC
+dentist
+
+// desi : 2013-11-14 Desi Networks LLC
+desi
+
+// design : 2014-11-07 Registry Services, LLC
+design
+
+// dev : 2014-10-16 Charleston Road Registry Inc.
+dev
+
+// dhl : 2015-07-23 Deutsche Post AG
+dhl
+
+// diamonds : 2013-09-22 Binky Moon, LLC
+diamonds
+
+// diet : 2014-06-26 XYZ.COM LLC
+diet
+
+// digital : 2014-03-06 Binky Moon, LLC
+digital
+
+// direct : 2014-04-10 Binky Moon, LLC
+direct
+
+// directory : 2013-09-20 Binky Moon, LLC
+directory
+
+// discount : 2014-03-06 Binky Moon, LLC
+discount
+
+// discover : 2015-07-23 Discover Financial Services
+discover
+
+// dish : 2015-07-30 Dish DBS Corporation
+dish
+
+// diy : 2015-11-05 Lifestyle Domain Holdings, Inc.
+diy
+
+// dnp : 2013-12-13 Dai Nippon Printing Co., Ltd.
+dnp
+
+// docs : 2014-10-16 Charleston Road Registry Inc.
+docs
+
+// doctor : 2016-06-02 Binky Moon, LLC
+doctor
+
+// dog : 2014-12-04 Binky Moon, LLC
+dog
+
+// domains : 2013-10-17 Binky Moon, LLC
+domains
+
+// dot : 2015-05-21 Dish DBS Corporation
+dot
+
+// download : 2014-11-20 dot Support Limited
+download
+
+// drive : 2015-03-05 Charleston Road Registry Inc.
+drive
+
+// dtv : 2015-06-04 Dish DBS Corporation
+dtv
+
+// dubai : 2015-01-01 Dubai Smart Government Department
+dubai
+
+// dunlop : 2015-07-02 The Goodyear Tire & Rubber Company
+dunlop
+
+// dupont : 2015-06-25 DuPont Specialty Products USA, LLC
+dupont
+
+// durban : 2014-03-24 ZA Central Registry NPC trading as ZA Central Registry
+durban
+
+// dvag : 2014-06-23 Deutsche Vermögensberatung Aktiengesellschaft DVAG
+dvag
+
+// dvr : 2016-05-26 DISH Technologies L.L.C.
+dvr
+
+// earth : 2014-12-04 Interlink Systems Innovation Institute K.K.
+earth
+
+// eat : 2014-01-23 Charleston Road Registry Inc.
+eat
+
+// eco : 2016-07-08 Big Room Inc.
+eco
+
+// edeka : 2014-12-18 EDEKA Verband kaufmännischer Genossenschaften e.V.
+edeka
+
+// education : 2013-11-07 Binky Moon, LLC
+education
+
+// email : 2013-10-31 Binky Moon, LLC
+email
+
+// emerck : 2014-04-03 Merck KGaA
+emerck
+
+// energy : 2014-09-11 Binky Moon, LLC
+energy
+
+// engineer : 2014-03-06 Dog Beach, LLC
+engineer
+
+// engineering : 2014-03-06 Binky Moon, LLC
+engineering
+
+// enterprises : 2013-09-20 Binky Moon, LLC
+enterprises
+
+// epson : 2014-12-04 Seiko Epson Corporation
+epson
+
+// equipment : 2013-08-27 Binky Moon, LLC
+equipment
+
+// ericsson : 2015-07-09 Telefonaktiebolaget L M Ericsson
+ericsson
+
+// erni : 2014-04-03 ERNI Group Holding AG
+erni
+
+// esq : 2014-05-08 Charleston Road Registry Inc.
+esq
+
+// estate : 2013-08-27 Binky Moon, LLC
+estate
+
+// etisalat : 2015-09-03 Emirates Telecommunications Corporation (trading as Etisalat)
+etisalat
+
+// eurovision : 2014-04-24 European Broadcasting Union (EBU)
+eurovision
+
+// eus : 2013-12-12 Puntueus Fundazioa
+eus
+
+// events : 2013-12-05 Binky Moon, LLC
+events
+
+// exchange : 2014-03-06 Binky Moon, LLC
+exchange
+
+// expert : 2013-11-21 Binky Moon, LLC
+expert
+
+// exposed : 2013-12-05 Binky Moon, LLC
+exposed
+
+// express : 2015-02-11 Binky Moon, LLC
+express
+
+// extraspace : 2015-05-14 Extra Space Storage LLC
+extraspace
+
+// fage : 2014-12-18 Fage International S.A.
+fage
+
+// fail : 2014-03-06 Binky Moon, LLC
+fail
+
+// fairwinds : 2014-11-13 FairWinds Partners, LLC
+fairwinds
+
+// faith : 2014-11-20 dot Faith Limited
+faith
+
+// family : 2015-04-02 Dog Beach, LLC
+family
+
+// fan : 2014-03-06 Dog Beach, LLC
+fan
+
+// fans : 2014-11-07 ZDNS International Limited
+fans
+
+// farm : 2013-11-07 Binky Moon, LLC
+farm
+
+// farmers : 2015-07-09 Farmers Insurance Exchange
+farmers
+
+// fashion : 2014-07-03 Registry Services, LLC
+fashion
+
+// fast : 2014-12-18 Amazon Registry Services, Inc.
+fast
+
+// fedex : 2015-08-06 Federal Express Corporation
+fedex
+
+// feedback : 2013-12-19 Top Level Spectrum, Inc.
+feedback
+
+// ferrari : 2015-07-31 Fiat Chrysler Automobiles N.V.
+ferrari
+
+// ferrero : 2014-12-18 Ferrero Trading Lux S.A.
+ferrero
+
+// fidelity : 2015-07-30 Fidelity Brokerage Services LLC
+fidelity
+
+// fido : 2015-08-06 Rogers Communications Canada Inc.
+fido
+
+// film : 2015-01-08 Motion Picture Domain Registry Pty Ltd
+film
+
+// final : 2014-10-16 Núcleo de Informação e Coordenação do Ponto BR - NIC.br
+final
+
+// finance : 2014-03-20 Binky Moon, LLC
+finance
+
+// financial : 2014-03-06 Binky Moon, LLC
+financial
+
+// fire : 2015-06-25 Amazon Registry Services, Inc.
+fire
+
+// firestone : 2014-12-18 Bridgestone Licensing Services, Inc
+firestone
+
+// firmdale : 2014-03-27 Firmdale Holdings Limited
+firmdale
+
+// fish : 2013-12-12 Binky Moon, LLC
+fish
+
+// fishing : 2013-11-21 Registry Services, LLC
+fishing
+
+// fit : 2014-11-07 Registry Services, LLC
+fit
+
+// fitness : 2014-03-06 Binky Moon, LLC
+fitness
+
+// flickr : 2015-04-02 Flickr, Inc.
+flickr
+
+// flights : 2013-12-05 Binky Moon, LLC
+flights
+
+// flir : 2015-07-23 FLIR Systems, Inc.
+flir
+
+// florist : 2013-11-07 Binky Moon, LLC
+florist
+
+// flowers : 2014-10-09 XYZ.COM LLC
+flowers
+
+// fly : 2014-05-08 Charleston Road Registry Inc.
+fly
+
+// foo : 2014-01-23 Charleston Road Registry Inc.
+foo
+
+// food : 2016-04-21 Lifestyle Domain Holdings, Inc.
+food
+
+// football : 2014-12-18 Binky Moon, LLC
+football
+
+// ford : 2014-11-13 Ford Motor Company
+ford
+
+// forex : 2014-12-11 Dog Beach, LLC
+forex
+
+// forsale : 2014-05-22 Dog Beach, LLC
+forsale
+
+// forum : 2015-04-02 Fegistry, LLC
+forum
+
+// foundation : 2013-12-05 Public Interest Registry
+foundation
+
+// fox : 2015-09-11 FOX Registry, LLC
+fox
+
+// free : 2015-12-10 Amazon Registry Services, Inc.
+free
+
+// fresenius : 2015-07-30 Fresenius Immobilien-Verwaltungs-GmbH
+fresenius
+
+// frl : 2014-05-15 FRLregistry B.V.
+frl
+
+// frogans : 2013-12-19 OP3FT
+frogans
+
+// frontdoor : 2015-07-02 Lifestyle Domain Holdings, Inc.
+frontdoor
+
+// frontier : 2015-02-05 Frontier Communications Corporation
+frontier
+
+// ftr : 2015-07-16 Frontier Communications Corporation
+ftr
+
+// fujitsu : 2015-07-30 Fujitsu Limited
+fujitsu
+
+// fun : 2016-01-14 Radix FZC
+fun
+
+// fund : 2014-03-20 Binky Moon, LLC
+fund
+
+// furniture : 2014-03-20 Binky Moon, LLC
+furniture
+
+// futbol : 2013-09-20 Dog Beach, LLC
+futbol
+
+// fyi : 2015-04-02 Binky Moon, LLC
+fyi
+
+// gal : 2013-11-07 Asociación puntoGAL
+gal
+
+// gallery : 2013-09-13 Binky Moon, LLC
+gallery
+
+// gallo : 2015-06-11 Gallo Vineyards, Inc.
+gallo
+
+// gallup : 2015-02-19 Gallup, Inc.
+gallup
+
+// game : 2015-05-28 XYZ.COM LLC
+game
+
+// games : 2015-05-28 Dog Beach, LLC
+games
+
+// gap : 2015-07-31 The Gap, Inc.
+gap
+
+// garden : 2014-06-26 Registry Services, LLC
+garden
+
+// gay : 2019-05-23 Registry Services, LLC
+gay
+
+// gbiz : 2014-07-17 Charleston Road Registry Inc.
+gbiz
+
+// gdn : 2014-07-31 Joint Stock Company "Navigation-information systems"
+gdn
+
+// gea : 2014-12-04 GEA Group Aktiengesellschaft
+gea
+
+// gent : 2014-01-23 Easyhost BV
+gent
+
+// genting : 2015-03-12 Resorts World Inc Pte. Ltd.
+genting
+
+// george : 2015-07-31 Wal-Mart Stores, Inc.
+george
+
+// ggee : 2014-01-09 GMO Internet, Inc.
+ggee
+
+// gift : 2013-10-17 DotGift, LLC
+gift
+
+// gifts : 2014-07-03 Binky Moon, LLC
+gifts
+
+// gives : 2014-03-06 Public Interest Registry
+gives
+
+// giving : 2014-11-13 Public Interest Registry
+giving
+
+// glass : 2013-11-07 Binky Moon, LLC
+glass
+
+// gle : 2014-07-24 Charleston Road Registry Inc.
+gle
+
+// global : 2014-04-17 Identity Digital Limited
+global
+
+// globo : 2013-12-19 Globo Comunicação e Participações S.A
+globo
+
+// gmail : 2014-05-01 Charleston Road Registry Inc.
+gmail
+
+// gmbh : 2016-01-29 Binky Moon, LLC
+gmbh
+
+// gmo : 2014-01-09 GMO Internet, Inc.
+gmo
+
+// gmx : 2014-04-24 1&1 Mail & Media GmbH
+gmx
+
+// godaddy : 2015-07-23 Go Daddy East, LLC
+godaddy
+
+// gold : 2015-01-22 Binky Moon, LLC
+gold
+
+// goldpoint : 2014-11-20 YODOBASHI CAMERA CO.,LTD.
+goldpoint
+
+// golf : 2014-12-18 Binky Moon, LLC
+golf
+
+// goo : 2014-12-18 NTT Resonant Inc.
+goo
+
+// goodyear : 2015-07-02 The Goodyear Tire & Rubber Company
+goodyear
+
+// goog : 2014-11-20 Charleston Road Registry Inc.
+goog
+
+// google : 2014-07-24 Charleston Road Registry Inc.
+google
+
+// gop : 2014-01-16 Republican State Leadership Committee, Inc.
+gop
+
+// got : 2014-12-18 Amazon Registry Services, Inc.
+got
+
+// grainger : 2015-05-07 Grainger Registry Services, LLC
+grainger
+
+// graphics : 2013-09-13 Binky Moon, LLC
+graphics
+
+// gratis : 2014-03-20 Binky Moon, LLC
+gratis
+
+// green : 2014-05-08 Identity Digital Limited
+green
+
+// gripe : 2014-03-06 Binky Moon, LLC
+gripe
+
+// grocery : 2016-06-16 Wal-Mart Stores, Inc.
+grocery
+
+// group : 2014-08-15 Binky Moon, LLC
+group
+
+// guardian : 2015-07-30 The Guardian Life Insurance Company of America
+guardian
+
+// gucci : 2014-11-13 Guccio Gucci S.p.a.
+gucci
+
+// guge : 2014-08-28 Charleston Road Registry Inc.
+guge
+
+// guide : 2013-09-13 Binky Moon, LLC
+guide
+
+// guitars : 2013-11-14 XYZ.COM LLC
+guitars
+
+// guru : 2013-08-27 Binky Moon, LLC
+guru
+
+// hair : 2015-12-03 XYZ.COM LLC
+hair
+
+// hamburg : 2014-02-20 Hamburg Top-Level-Domain GmbH
+hamburg
+
+// hangout : 2014-11-13 Charleston Road Registry Inc.
+hangout
+
+// haus : 2013-12-05 Dog Beach, LLC
+haus
+
+// hbo : 2015-07-30 HBO Registry Services, Inc.
+hbo
+
+// hdfc : 2015-07-30 HOUSING DEVELOPMENT FINANCE CORPORATION LIMITED
+hdfc
+
+// hdfcbank : 2015-02-12 HDFC Bank Limited
+hdfcbank
+
+// health : 2015-02-11 Registry Services, LLC
+health
+
+// healthcare : 2014-06-12 Binky Moon, LLC
+healthcare
+
+// help : 2014-06-26 Innovation service Limited
+help
+
+// helsinki : 2015-02-05 City of Helsinki
+helsinki
+
+// here : 2014-02-06 Charleston Road Registry Inc.
+here
+
+// hermes : 2014-07-10 HERMES INTERNATIONAL
+hermes
+
+// hiphop : 2014-03-06 Dot Hip Hop, LLC
+hiphop
+
+// hisamitsu : 2015-07-16 Hisamitsu Pharmaceutical Co.,Inc.
+hisamitsu
+
+// hitachi : 2014-10-31 Hitachi, Ltd.
+hitachi
+
+// hiv : 2014-03-13 Internet Naming Company LLC
+hiv
+
+// hkt : 2015-05-14 PCCW-HKT DataCom Services Limited
+hkt
+
+// hockey : 2015-03-19 Binky Moon, LLC
+hockey
+
+// holdings : 2013-08-27 Binky Moon, LLC
+holdings
+
+// holiday : 2013-11-07 Binky Moon, LLC
+holiday
+
+// homedepot : 2015-04-02 Home Depot Product Authority, LLC
+homedepot
+
+// homegoods : 2015-07-16 The TJX Companies, Inc.
+homegoods
+
+// homes : 2014-01-09 XYZ.COM LLC
+homes
+
+// homesense : 2015-07-16 The TJX Companies, Inc.
+homesense
+
+// honda : 2014-12-18 Honda Motor Co., Ltd.
+honda
+
+// horse : 2013-11-21 Registry Services, LLC
+horse
+
+// hospital : 2016-10-20 Binky Moon, LLC
+hospital
+
+// host : 2014-04-17 Radix FZC
+host
+
+// hosting : 2014-05-29 XYZ.COM LLC
+hosting
+
+// hot : 2015-08-27 Amazon Registry Services, Inc.
+hot
+
+// hoteles : 2015-03-05 Travel Reservations SRL
+hoteles
+
+// hotels : 2016-04-07 Booking.com B.V.
+hotels
+
+// hotmail : 2014-12-18 Microsoft Corporation
+hotmail
+
+// house : 2013-11-07 Binky Moon, LLC
+house
+
+// how : 2014-01-23 Charleston Road Registry Inc.
+how
+
+// hsbc : 2014-10-24 HSBC Global Services (UK) Limited
+hsbc
+
+// hughes : 2015-07-30 Hughes Satellite Systems Corporation
+hughes
+
+// hyatt : 2015-07-30 Hyatt GTLD, L.L.C.
+hyatt
+
+// hyundai : 2015-07-09 Hyundai Motor Company
+hyundai
+
+// ibm : 2014-07-31 International Business Machines Corporation
+ibm
+
+// icbc : 2015-02-19 Industrial and Commercial Bank of China Limited
+icbc
+
+// ice : 2014-10-30 IntercontinentalExchange, Inc.
+ice
+
+// icu : 2015-01-08 ShortDot SA
+icu
+
+// ieee : 2015-07-23 IEEE Global LLC
+ieee
+
+// ifm : 2014-01-30 ifm electronic gmbh
+ifm
+
+// ikano : 2015-07-09 Ikano S.A.
+ikano
+
+// imamat : 2015-08-06 Fondation Aga Khan (Aga Khan Foundation)
+imamat
+
+// imdb : 2015-06-25 Amazon Registry Services, Inc.
+imdb
+
+// immo : 2014-07-10 Binky Moon, LLC
+immo
+
+// immobilien : 2013-11-07 Dog Beach, LLC
+immobilien
+
+// inc : 2018-03-10 Intercap Registry Inc.
+inc
+
+// industries : 2013-12-05 Binky Moon, LLC
+industries
+
+// infiniti : 2014-03-27 NISSAN MOTOR CO., LTD.
+infiniti
+
+// ing : 2014-01-23 Charleston Road Registry Inc.
+ing
+
+// ink : 2013-12-05 Registry Services, LLC
+ink
+
+// institute : 2013-11-07 Binky Moon, LLC
+institute
+
+// insurance : 2015-02-19 fTLD Registry Services LLC
+insurance
+
+// insure : 2014-03-20 Binky Moon, LLC
+insure
+
+// international : 2013-11-07 Binky Moon, LLC
+international
+
+// intuit : 2015-07-30 Intuit Administrative Services, Inc.
+intuit
+
+// investments : 2014-03-20 Binky Moon, LLC
+investments
+
+// ipiranga : 2014-08-28 Ipiranga Produtos de Petroleo S.A.
+ipiranga
+
+// irish : 2014-08-07 Binky Moon, LLC
+irish
+
+// ismaili : 2015-08-06 Fondation Aga Khan (Aga Khan Foundation)
+ismaili
+
+// ist : 2014-08-28 Istanbul Metropolitan Municipality
+ist
+
+// istanbul : 2014-08-28 Istanbul Metropolitan Municipality
+istanbul
+
+// itau : 2014-10-02 Itau Unibanco Holding S.A.
+itau
+
+// itv : 2015-07-09 ITV Services Limited
+itv
+
+// jaguar : 2014-11-13 Jaguar Land Rover Ltd
+jaguar
+
+// java : 2014-06-19 Oracle Corporation
+java
+
+// jcb : 2014-11-20 JCB Co., Ltd.
+jcb
+
+// jeep : 2015-07-30 FCA US LLC.
+jeep
+
+// jetzt : 2014-01-09 Binky Moon, LLC
+jetzt
+
+// jewelry : 2015-03-05 Binky Moon, LLC
+jewelry
+
+// jio : 2015-04-02 Reliance Industries Limited
+jio
+
+// jll : 2015-04-02 Jones Lang LaSalle Incorporated
+jll
+
+// jmp : 2015-03-26 Matrix IP LLC
+jmp
+
+// jnj : 2015-06-18 Johnson & Johnson Services, Inc.
+jnj
+
+// joburg : 2014-03-24 ZA Central Registry NPC trading as ZA Central Registry
+joburg
+
+// jot : 2014-12-18 Amazon Registry Services, Inc.
+jot
+
+// joy : 2014-12-18 Amazon Registry Services, Inc.
+joy
+
+// jpmorgan : 2015-04-30 JPMorgan Chase Bank, National Association
+jpmorgan
+
+// jprs : 2014-09-18 Japan Registry Services Co., Ltd.
+jprs
+
+// juegos : 2014-03-20 Internet Naming Company LLC
+juegos
+
+// juniper : 2015-07-30 JUNIPER NETWORKS, INC.
+juniper
+
+// kaufen : 2013-11-07 Dog Beach, LLC
+kaufen
+
+// kddi : 2014-09-12 KDDI CORPORATION
+kddi
+
+// kerryhotels : 2015-04-30 Kerry Trading Co. Limited
+kerryhotels
+
+// kerrylogistics : 2015-04-09 Kerry Trading Co. Limited
+kerrylogistics
+
+// kerryproperties : 2015-04-09 Kerry Trading Co. Limited
+kerryproperties
+
+// kfh : 2014-12-04 Kuwait Finance House
+kfh
+
+// kia : 2015-07-09 KIA MOTORS CORPORATION
+kia
+
+// kids : 2021-08-13 DotKids Foundation Limited
+kids
+
+// kim : 2013-09-23 Identity Digital Limited
+kim
+
+// kinder : 2014-11-07 Ferrero Trading Lux S.A.
+kinder
+
+// kindle : 2015-06-25 Amazon Registry Services, Inc.
+kindle
+
+// kitchen : 2013-09-20 Binky Moon, LLC
+kitchen
+
+// kiwi : 2013-09-20 DOT KIWI LIMITED
+kiwi
+
+// koeln : 2014-01-09 dotKoeln GmbH
+koeln
+
+// komatsu : 2015-01-08 Komatsu Ltd.
+komatsu
+
+// kosher : 2015-08-20 Kosher Marketing Assets LLC
+kosher
+
+// kpmg : 2015-04-23 KPMG International Cooperative (KPMG International Genossenschaft)
+kpmg
+
+// kpn : 2015-01-08 Koninklijke KPN N.V.
+kpn
+
+// krd : 2013-12-05 KRG Department of Information Technology
+krd
+
+// kred : 2013-12-19 KredTLD Pty Ltd
+kred
+
+// kuokgroup : 2015-04-09 Kerry Trading Co. Limited
+kuokgroup
+
+// kyoto : 2014-11-07 Academic Institution: Kyoto Jyoho Gakuen
+kyoto
+
+// lacaixa : 2014-01-09 Fundación Bancaria Caixa d’Estalvis i Pensions de Barcelona, “la Caixa”
+lacaixa
+
+// lamborghini : 2015-06-04 Automobili Lamborghini S.p.A.
+lamborghini
+
+// lamer : 2015-10-01 The Estée Lauder Companies Inc.
+lamer
+
+// lancaster : 2015-02-12 LANCASTER
+lancaster
+
+// land : 2013-09-10 Binky Moon, LLC
+land
+
+// landrover : 2014-11-13 Jaguar Land Rover Ltd
+landrover
+
+// lanxess : 2015-07-30 LANXESS Corporation
+lanxess
+
+// lasalle : 2015-04-02 Jones Lang LaSalle Incorporated
+lasalle
+
+// lat : 2014-10-16 XYZ.COM LLC
+lat
+
+// latino : 2015-07-30 Dish DBS Corporation
+latino
+
+// latrobe : 2014-06-16 La Trobe University
+latrobe
+
+// law : 2015-01-22 Registry Services, LLC
+law
+
+// lawyer : 2014-03-20 Dog Beach, LLC
+lawyer
+
+// lds : 2014-03-20 IRI Domain Management, LLC
+lds
+
+// lease : 2014-03-06 Binky Moon, LLC
+lease
+
+// leclerc : 2014-08-07 A.C.D. LEC Association des Centres Distributeurs Edouard Leclerc
+leclerc
+
+// lefrak : 2015-07-16 LeFrak Organization, Inc.
+lefrak
+
+// legal : 2014-10-16 Binky Moon, LLC
+legal
+
+// lego : 2015-07-16 LEGO Juris A/S
+lego
+
+// lexus : 2015-04-23 TOYOTA MOTOR CORPORATION
+lexus
+
+// lgbt : 2014-05-08 Identity Digital Limited
+lgbt
+
+// lidl : 2014-09-18 Schwarz Domains und Services GmbH & Co. KG
+lidl
+
+// life : 2014-02-06 Binky Moon, LLC
+life
+
+// lifeinsurance : 2015-01-15 American Council of Life Insurers
+lifeinsurance
+
+// lifestyle : 2014-12-11 Lifestyle Domain Holdings, Inc.
+lifestyle
+
+// lighting : 2013-08-27 Binky Moon, LLC
+lighting
+
+// like : 2014-12-18 Amazon Registry Services, Inc.
+like
+
+// lilly : 2015-07-31 Eli Lilly and Company
+lilly
+
+// limited : 2014-03-06 Binky Moon, LLC
+limited
+
+// limo : 2013-10-17 Binky Moon, LLC
+limo
+
+// lincoln : 2014-11-13 Ford Motor Company
+lincoln
+
+// link : 2013-11-14 Nova Registry Ltd
+link
+
+// lipsy : 2015-06-25 Lipsy Ltd
+lipsy
+
+// live : 2014-12-04 Dog Beach, LLC
+live
+
+// living : 2015-07-30 Lifestyle Domain Holdings, Inc.
+living
+
+// llc : 2017-12-14 Identity Digital Limited
+llc
+
+// llp : 2019-08-26 Intercap Registry Inc.
+llp
+
+// loan : 2014-11-20 dot Loan Limited
+loan
+
+// loans : 2014-03-20 Binky Moon, LLC
+loans
+
+// locker : 2015-06-04 Dish DBS Corporation
+locker
+
+// locus : 2015-06-25 Locus Analytics LLC
+locus
+
+// lol : 2015-01-30 XYZ.COM LLC
+lol
+
+// london : 2013-11-14 Dot London Domains Limited
+london
+
+// lotte : 2014-11-07 Lotte Holdings Co., Ltd.
+lotte
+
+// lotto : 2014-04-10 Identity Digital Limited
+lotto
+
+// love : 2014-12-22 Merchant Law Group LLP
+love
+
+// lpl : 2015-07-30 LPL Holdings, Inc.
+lpl
+
+// lplfinancial : 2015-07-30 LPL Holdings, Inc.
+lplfinancial
+
+// ltd : 2014-09-25 Binky Moon, LLC
+ltd
+
+// ltda : 2014-04-17 InterNetX, Corp
+ltda
+
+// lundbeck : 2015-08-06 H. Lundbeck A/S
+lundbeck
+
+// luxe : 2014-01-09 Registry Services, LLC
+luxe
+
+// luxury : 2013-10-17 Luxury Partners, LLC
+luxury
+
+// madrid : 2014-05-01 Comunidad de Madrid
+madrid
+
+// maif : 2014-10-02 Mutuelle Assurance Instituteur France (MAIF)
+maif
+
+// maison : 2013-12-05 Binky Moon, LLC
+maison
+
+// makeup : 2015-01-15 XYZ.COM LLC
+makeup
+
+// man : 2014-12-04 MAN SE
+man
+
+// management : 2013-11-07 Binky Moon, LLC
+management
+
+// mango : 2013-10-24 PUNTO FA S.L.
+mango
+
+// map : 2016-06-09 Charleston Road Registry Inc.
+map
+
+// market : 2014-03-06 Dog Beach, LLC
+market
+
+// marketing : 2013-11-07 Binky Moon, LLC
+marketing
+
+// markets : 2014-12-11 Dog Beach, LLC
+markets
+
+// marriott : 2014-10-09 Marriott Worldwide Corporation
+marriott
+
+// marshalls : 2015-07-16 The TJX Companies, Inc.
+marshalls
+
+// mattel : 2015-08-06 Mattel Sites, Inc.
+mattel
+
+// mba : 2015-04-02 Binky Moon, LLC
+mba
+
+// mckinsey : 2015-07-31 McKinsey Holdings, Inc.
+mckinsey
+
+// med : 2015-08-06 Medistry LLC
+med
+
+// media : 2014-03-06 Binky Moon, LLC
+media
+
+// meet : 2014-01-16 Charleston Road Registry Inc.
+meet
+
+// melbourne : 2014-05-29 The Crown in right of the State of Victoria, represented by its Department of State Development, Business and Innovation
+melbourne
+
+// meme : 2014-01-30 Charleston Road Registry Inc.
+meme
+
+// memorial : 2014-10-16 Dog Beach, LLC
+memorial
+
+// men : 2015-02-26 Exclusive Registry Limited
+men
+
+// menu : 2013-09-11 Dot Menu Registry, LLC
+menu
+
+// merckmsd : 2016-07-14 MSD Registry Holdings, Inc.
+merckmsd
+
+// miami : 2013-12-19 Registry Services, LLC
+miami
+
+// microsoft : 2014-12-18 Microsoft Corporation
+microsoft
+
+// mini : 2014-01-09 Bayerische Motoren Werke Aktiengesellschaft
+mini
+
+// mint : 2015-07-30 Intuit Administrative Services, Inc.
+mint
+
+// mit : 2015-07-02 Massachusetts Institute of Technology
+mit
+
+// mitsubishi : 2015-07-23 Mitsubishi Corporation
+mitsubishi
+
+// mlb : 2015-05-21 MLB Advanced Media DH, LLC
+mlb
+
+// mls : 2015-04-23 The Canadian Real Estate Association
+mls
+
+// mma : 2014-11-07 MMA IARD
+mma
+
+// mobile : 2016-06-02 Dish DBS Corporation
+mobile
+
+// moda : 2013-11-07 Dog Beach, LLC
+moda
+
+// moe : 2013-11-13 Interlink Systems Innovation Institute K.K.
+moe
+
+// moi : 2014-12-18 Amazon Registry Services, Inc.
+moi
+
+// mom : 2015-04-16 XYZ.COM LLC
+mom
+
+// monash : 2013-09-30 Monash University
+monash
+
+// money : 2014-10-16 Binky Moon, LLC
+money
+
+// monster : 2015-09-11 XYZ.COM LLC
+monster
+
+// mormon : 2013-12-05 IRI Domain Management, LLC
+mormon
+
+// mortgage : 2014-03-20 Dog Beach, LLC
+mortgage
+
+// moscow : 2013-12-19 Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID)
+moscow
+
+// moto : 2015-06-04 Motorola Trademark Holdings, LLC
+moto
+
+// motorcycles : 2014-01-09 XYZ.COM LLC
+motorcycles
+
+// mov : 2014-01-30 Charleston Road Registry Inc.
+mov
+
+// movie : 2015-02-05 Binky Moon, LLC
+movie
+
+// msd : 2015-07-23 MSD Registry Holdings, Inc.
+msd
+
+// mtn : 2014-12-04 MTN Dubai Limited
+mtn
+
+// mtr : 2015-03-12 MTR Corporation Limited
+mtr
+
+// music : 2021-05-04 DotMusic Limited
+music
+
+// mutual : 2015-04-02 Northwestern Mutual MU TLD Registry, LLC
+mutual
+
+// nab : 2015-08-20 National Australia Bank Limited
+nab
+
+// nagoya : 2013-10-24 GMO Registry, Inc.
+nagoya
+
+// natura : 2015-03-12 NATURA COSMÉTICOS S.A.
+natura
+
+// navy : 2014-03-06 Dog Beach, LLC
+navy
+
+// nba : 2015-07-31 NBA REGISTRY, LLC
+nba
+
+// nec : 2015-01-08 NEC Corporation
+nec
+
+// netbank : 2014-06-26 COMMONWEALTH BANK OF AUSTRALIA
+netbank
+
+// netflix : 2015-06-18 Netflix, Inc.
+netflix
+
+// network : 2013-11-14 Binky Moon, LLC
+network
+
+// neustar : 2013-12-05 NeuStar, Inc.
+neustar
+
+// new : 2014-01-30 Charleston Road Registry Inc.
+new
+
+// news : 2014-12-18 Dog Beach, LLC
+news
+
+// next : 2015-06-18 Next plc
+next
+
+// nextdirect : 2015-06-18 Next plc
+nextdirect
+
+// nexus : 2014-07-24 Charleston Road Registry Inc.
+nexus
+
+// nfl : 2015-07-23 NFL Reg Ops LLC
+nfl
+
+// ngo : 2014-03-06 Public Interest Registry
+ngo
+
+// nhk : 2014-02-13 Japan Broadcasting Corporation (NHK)
+nhk
+
+// nico : 2014-12-04 DWANGO Co., Ltd.
+nico
+
+// nike : 2015-07-23 NIKE, Inc.
+nike
+
+// nikon : 2015-05-21 NIKON CORPORATION
+nikon
+
+// ninja : 2013-11-07 Dog Beach, LLC
+ninja
+
+// nissan : 2014-03-27 NISSAN MOTOR CO., LTD.
+nissan
+
+// nissay : 2015-10-29 Nippon Life Insurance Company
+nissay
+
+// nokia : 2015-01-08 Nokia Corporation
+nokia
+
+// northwesternmutual : 2015-06-18 Northwestern Mutual Registry, LLC
+northwesternmutual
+
+// norton : 2014-12-04 NortonLifeLock Inc.
+norton
+
+// now : 2015-06-25 Amazon Registry Services, Inc.
+now
+
+// nowruz : 2014-09-04 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
+nowruz
+
+// nowtv : 2015-05-14 Starbucks (HK) Limited
+nowtv
+
+// nra : 2014-05-22 NRA Holdings Company, INC.
+nra
+
+// nrw : 2013-11-21 Minds + Machines GmbH
+nrw
+
+// ntt : 2014-10-31 NIPPON TELEGRAPH AND TELEPHONE CORPORATION
+ntt
+
+// nyc : 2014-01-23 The City of New York by and through the New York City Department of Information Technology & Telecommunications
+nyc
+
+// obi : 2014-09-25 OBI Group Holding SE & Co. KGaA
+obi
+
+// observer : 2015-04-30 Dog Beach, LLC
+observer
+
+// office : 2015-03-12 Microsoft Corporation
+office
+
+// okinawa : 2013-12-05 BRregistry, Inc.
+okinawa
+
+// olayan : 2015-05-14 Competrol (Luxembourg) Sarl
+olayan
+
+// olayangroup : 2015-05-14 Competrol (Luxembourg) Sarl
+olayangroup
+
+// oldnavy : 2015-07-31 The Gap, Inc.
+oldnavy
+
+// ollo : 2015-06-04 Dish DBS Corporation
+ollo
+
+// omega : 2015-01-08 The Swatch Group Ltd
+omega
+
+// one : 2014-11-07 One.com A/S
+one
+
+// ong : 2014-03-06 Public Interest Registry
+ong
+
+// onl : 2013-09-16 iRegistry GmbH
+onl
+
+// online : 2015-01-15 Radix FZC
+online
+
+// ooo : 2014-01-09 INFIBEAM AVENUES LIMITED
+ooo
+
+// open : 2015-07-31 American Express Travel Related Services Company, Inc.
+open
+
+// oracle : 2014-06-19 Oracle Corporation
+oracle
+
+// orange : 2015-03-12 Orange Brand Services Limited
+orange
+
+// organic : 2014-03-27 Identity Digital Limited
+organic
+
+// origins : 2015-10-01 The Estée Lauder Companies Inc.
+origins
+
+// osaka : 2014-09-04 Osaka Registry Co., Ltd.
+osaka
+
+// otsuka : 2013-10-11 Otsuka Holdings Co., Ltd.
+otsuka
+
+// ott : 2015-06-04 Dish DBS Corporation
+ott
+
+// ovh : 2014-01-16 MédiaBC
+ovh
+
+// page : 2014-12-04 Charleston Road Registry Inc.
+page
+
+// panasonic : 2015-07-30 Panasonic Holdings Corporation
+panasonic
+
+// paris : 2014-01-30 City of Paris
+paris
+
+// pars : 2014-09-04 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
+pars
+
+// partners : 2013-12-05 Binky Moon, LLC
+partners
+
+// parts : 2013-12-05 Binky Moon, LLC
+parts
+
+// party : 2014-09-11 Blue Sky Registry Limited
+party
+
+// passagens : 2015-03-05 Travel Reservations SRL
+passagens
+
+// pay : 2015-08-27 Amazon Registry Services, Inc.
+pay
+
+// pccw : 2015-05-14 PCCW Enterprises Limited
+pccw
+
+// pet : 2015-05-07 Identity Digital Limited
+pet
+
+// pfizer : 2015-09-11 Pfizer Inc.
+pfizer
+
+// pharmacy : 2014-06-19 National Association of Boards of Pharmacy
+pharmacy
+
+// phd : 2016-07-28 Charleston Road Registry Inc.
+phd
+
+// philips : 2014-11-07 Koninklijke Philips N.V.
+philips
+
+// phone : 2016-06-02 Dish DBS Corporation
+phone
+
+// photo : 2013-11-14 Registry Services, LLC
+photo
+
+// photography : 2013-09-20 Binky Moon, LLC
+photography
+
+// photos : 2013-10-17 Binky Moon, LLC
+photos
+
+// physio : 2014-05-01 PhysBiz Pty Ltd
+physio
+
+// pics : 2013-11-14 XYZ.COM LLC
+pics
+
+// pictet : 2014-06-26 Pictet Europe S.A.
+pictet
+
+// pictures : 2014-03-06 Binky Moon, LLC
+pictures
+
+// pid : 2015-01-08 Top Level Spectrum, Inc.
+pid
+
+// pin : 2014-12-18 Amazon Registry Services, Inc.
+pin
+
+// ping : 2015-06-11 Ping Registry Provider, Inc.
+ping
+
+// pink : 2013-10-01 Identity Digital Limited
+pink
+
+// pioneer : 2015-07-16 Pioneer Corporation
+pioneer
+
+// pizza : 2014-06-26 Binky Moon, LLC
+pizza
+
+// place : 2014-04-24 Binky Moon, LLC
+place
+
+// play : 2015-03-05 Charleston Road Registry Inc.
+play
+
+// playstation : 2015-07-02 Sony Interactive Entertainment Inc.
+playstation
+
+// plumbing : 2013-09-10 Binky Moon, LLC
+plumbing
+
+// plus : 2015-02-05 Binky Moon, LLC
+plus
+
+// pnc : 2015-07-02 PNC Domain Co., LLC
+pnc
+
+// pohl : 2014-06-23 Deutsche Vermögensberatung Aktiengesellschaft DVAG
+pohl
+
+// poker : 2014-07-03 Identity Digital Limited
+poker
+
+// politie : 2015-08-20 Politie Nederland
+politie
+
+// porn : 2014-10-16 ICM Registry PN LLC
+porn
+
+// pramerica : 2015-07-30 Prudential Financial, Inc.
+pramerica
+
+// praxi : 2013-12-05 Praxi S.p.A.
+praxi
+
+// press : 2014-04-03 Radix FZC
+press
+
+// prime : 2015-06-25 Amazon Registry Services, Inc.
+prime
+
+// prod : 2014-01-23 Charleston Road Registry Inc.
+prod
+
+// productions : 2013-12-05 Binky Moon, LLC
+productions
+
+// prof : 2014-07-24 Charleston Road Registry Inc.
+prof
+
+// progressive : 2015-07-23 Progressive Casualty Insurance Company
+progressive
+
+// promo : 2014-12-18 Identity Digital Limited
+promo
+
+// properties : 2013-12-05 Binky Moon, LLC
+properties
+
+// property : 2014-05-22 Internet Naming Company LLC
+property
+
+// protection : 2015-04-23 XYZ.COM LLC
+protection
+
+// pru : 2015-07-30 Prudential Financial, Inc.
+pru
+
+// prudential : 2015-07-30 Prudential Financial, Inc.
+prudential
+
+// pub : 2013-12-12 Dog Beach, LLC
+pub
+
+// pwc : 2015-10-29 PricewaterhouseCoopers LLP
+pwc
+
+// qpon : 2013-11-14 dotQPON LLC
+qpon
+
+// quebec : 2013-12-19 PointQuébec Inc
+quebec
+
+// quest : 2015-03-26 XYZ.COM LLC
+quest
+
+// racing : 2014-12-04 Premier Registry Limited
+racing
+
+// radio : 2016-07-21 European Broadcasting Union (EBU)
+radio
+
+// read : 2014-12-18 Amazon Registry Services, Inc.
+read
+
+// realestate : 2015-09-11 dotRealEstate LLC
+realestate
+
+// realtor : 2014-05-29 Real Estate Domains LLC
+realtor
+
+// realty : 2015-03-19 Dog Beach, LLC
+realty
+
+// recipes : 2013-10-17 Binky Moon, LLC
+recipes
+
+// red : 2013-11-07 Identity Digital Limited
+red
+
+// redstone : 2014-10-31 Redstone Haute Couture Co., Ltd.
+redstone
+
+// redumbrella : 2015-03-26 Travelers TLD, LLC
+redumbrella
+
+// rehab : 2014-03-06 Dog Beach, LLC
+rehab
+
+// reise : 2014-03-13 Binky Moon, LLC
+reise
+
+// reisen : 2014-03-06 Binky Moon, LLC
+reisen
+
+// reit : 2014-09-04 National Association of Real Estate Investment Trusts, Inc.
+reit
+
+// reliance : 2015-04-02 Reliance Industries Limited
+reliance
+
+// ren : 2013-12-12 ZDNS International Limited
+ren
+
+// rent : 2014-12-04 XYZ.COM LLC
+rent
+
+// rentals : 2013-12-05 Binky Moon, LLC
+rentals
+
+// repair : 2013-11-07 Binky Moon, LLC
+repair
+
+// report : 2013-12-05 Binky Moon, LLC
+report
+
+// republican : 2014-03-20 Dog Beach, LLC
+republican
+
+// rest : 2013-12-19 Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable
+rest
+
+// restaurant : 2014-07-03 Binky Moon, LLC
+restaurant
+
+// review : 2014-11-20 dot Review Limited
+review
+
+// reviews : 2013-09-13 Dog Beach, LLC
+reviews
+
+// rexroth : 2015-06-18 Robert Bosch GMBH
+rexroth
+
+// rich : 2013-11-21 iRegistry GmbH
+rich
+
+// richardli : 2015-05-14 Pacific Century Asset Management (HK) Limited
+richardli
+
+// ricoh : 2014-11-20 Ricoh Company, Ltd.
+ricoh
+
+// ril : 2015-04-02 Reliance Industries Limited
+ril
+
+// rio : 2014-02-27 Empresa Municipal de Informática SA - IPLANRIO
+rio
+
+// rip : 2014-07-10 Dog Beach, LLC
+rip
+
+// rocher : 2014-12-18 Ferrero Trading Lux S.A.
+rocher
+
+// rocks : 2013-11-14 Dog Beach, LLC
+rocks
+
+// rodeo : 2013-12-19 Registry Services, LLC
+rodeo
+
+// rogers : 2015-08-06 Rogers Communications Canada Inc.
+rogers
+
+// room : 2014-12-18 Amazon Registry Services, Inc.
+room
+
+// rsvp : 2014-05-08 Charleston Road Registry Inc.
+rsvp
+
+// rugby : 2016-12-15 World Rugby Strategic Developments Limited
+rugby
+
+// ruhr : 2013-10-02 dotSaarland GmbH
+ruhr
+
+// run : 2015-03-19 Binky Moon, LLC
+run
+
+// rwe : 2015-04-02 RWE AG
+rwe
+
+// ryukyu : 2014-01-09 BRregistry, Inc.
+ryukyu
+
+// saarland : 2013-12-12 dotSaarland GmbH
+saarland
+
+// safe : 2014-12-18 Amazon Registry Services, Inc.
+safe
+
+// safety : 2015-01-08 Safety Registry Services, LLC.
+safety
+
+// sakura : 2014-12-18 SAKURA Internet Inc.
+sakura
+
+// sale : 2014-10-16 Dog Beach, LLC
+sale
+
+// salon : 2014-12-11 Binky Moon, LLC
+salon
+
+// samsclub : 2015-07-31 Wal-Mart Stores, Inc.
+samsclub
+
+// samsung : 2014-04-03 SAMSUNG SDS CO., LTD
+samsung
+
+// sandvik : 2014-11-13 Sandvik AB
+sandvik
+
+// sandvikcoromant : 2014-11-07 Sandvik AB
+sandvikcoromant
+
+// sanofi : 2014-10-09 Sanofi
+sanofi
+
+// sap : 2014-03-27 SAP AG
+sap
+
+// sarl : 2014-07-03 Binky Moon, LLC
+sarl
+
+// sas : 2015-04-02 Research IP LLC
+sas
+
+// save : 2015-06-25 Amazon Registry Services, Inc.
+save
+
+// saxo : 2014-10-31 Saxo Bank A/S
+saxo
+
+// sbi : 2015-03-12 STATE BANK OF INDIA
+sbi
+
+// sbs : 2014-11-07 ShortDot SA
+sbs
+
+// sca : 2014-03-13 SVENSKA CELLULOSA AKTIEBOLAGET SCA (publ)
+sca
+
+// scb : 2014-02-20 The Siam Commercial Bank Public Company Limited ("SCB")
+scb
+
+// schaeffler : 2015-08-06 Schaeffler Technologies AG & Co. KG
+schaeffler
+
+// schmidt : 2014-04-03 SCHMIDT GROUPE S.A.S.
+schmidt
+
+// scholarships : 2014-04-24 Scholarships.com, LLC
+scholarships
+
+// school : 2014-12-18 Binky Moon, LLC
+school
+
+// schule : 2014-03-06 Binky Moon, LLC
+schule
+
+// schwarz : 2014-09-18 Schwarz Domains und Services GmbH & Co. KG
+schwarz
+
+// science : 2014-09-11 dot Science Limited
+science
+
+// scot : 2014-01-23 Dot Scot Registry Limited
+scot
+
+// search : 2016-06-09 Charleston Road Registry Inc.
+search
+
+// seat : 2014-05-22 SEAT, S.A. (Sociedad Unipersonal)
+seat
+
+// secure : 2015-08-27 Amazon Registry Services, Inc.
+secure
+
+// security : 2015-05-14 XYZ.COM LLC
+security
+
+// seek : 2014-12-04 Seek Limited
+seek
+
+// select : 2015-10-08 Registry Services, LLC
+select
+
+// sener : 2014-10-24 Sener Ingeniería y Sistemas, S.A.
+sener
+
+// services : 2014-02-27 Binky Moon, LLC
+services
+
+// seven : 2015-08-06 Seven West Media Ltd
+seven
+
+// sew : 2014-07-17 SEW-EURODRIVE GmbH & Co KG
+sew
+
+// sex : 2014-11-13 ICM Registry SX LLC
+sex
+
+// sexy : 2013-09-11 Internet Naming Company LLC
+sexy
+
+// sfr : 2015-08-13 Societe Francaise du Radiotelephone - SFR
+sfr
+
+// shangrila : 2015-09-03 Shangri‐La International Hotel Management Limited
+shangrila
+
+// sharp : 2014-05-01 Sharp Corporation
+sharp
+
+// shaw : 2015-04-23 Shaw Cablesystems G.P.
+shaw
+
+// shell : 2015-07-30 Shell Information Technology International Inc
+shell
+
+// shia : 2014-09-04 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
+shia
+
+// shiksha : 2013-11-14 Identity Digital Limited
+shiksha
+
+// shoes : 2013-10-02 Binky Moon, LLC
+shoes
+
+// shop : 2016-04-08 GMO Registry, Inc.
+shop
+
+// shopping : 2016-03-31 Binky Moon, LLC
+shopping
+
+// shouji : 2015-01-08 Beijing Qihu Keji Co., Ltd.
+shouji
+
+// show : 2015-03-05 Binky Moon, LLC
+show
+
+// showtime : 2015-08-06 CBS Domains Inc.
+showtime
+
+// silk : 2015-06-25 Amazon Registry Services, Inc.
+silk
+
+// sina : 2015-03-12 Sina Corporation
+sina
+
+// singles : 2013-08-27 Binky Moon, LLC
+singles
+
+// site : 2015-01-15 Radix FZC
+site
+
+// ski : 2015-04-09 Identity Digital Limited
+ski
+
+// skin : 2015-01-15 XYZ.COM LLC
+skin
+
+// sky : 2014-06-19 Sky International AG
+sky
+
+// skype : 2014-12-18 Microsoft Corporation
+skype
+
+// sling : 2015-07-30 DISH Technologies L.L.C.
+sling
+
+// smart : 2015-07-09 Smart Communications, Inc. (SMART)
+smart
+
+// smile : 2014-12-18 Amazon Registry Services, Inc.
+smile
+
+// sncf : 2015-02-19 Société Nationale SNCF
+sncf
+
+// soccer : 2015-03-26 Binky Moon, LLC
+soccer
+
+// social : 2013-11-07 Dog Beach, LLC
+social
+
+// softbank : 2015-07-02 SoftBank Group Corp.
+softbank
+
+// software : 2014-03-20 Dog Beach, LLC
+software
+
+// sohu : 2013-12-19 Sohu.com Limited
+sohu
+
+// solar : 2013-11-07 Binky Moon, LLC
+solar
+
+// solutions : 2013-11-07 Binky Moon, LLC
+solutions
+
+// song : 2015-02-26 Amazon Registry Services, Inc.
+song
+
+// sony : 2015-01-08 Sony Corporation
+sony
+
+// soy : 2014-01-23 Charleston Road Registry Inc.
+soy
+
+// spa : 2019-09-19 Asia Spa and Wellness Promotion Council Limited
+spa
+
+// space : 2014-04-03 Radix FZC
+space
+
+// sport : 2017-11-16 Global Association of International Sports Federations (GAISF)
+sport
+
+// spot : 2015-02-26 Amazon Registry Services, Inc.
+spot
+
+// srl : 2015-05-07 InterNetX, Corp
+srl
+
+// stada : 2014-11-13 STADA Arzneimittel AG
+stada
+
+// staples : 2015-07-30 Staples, Inc.
+staples
+
+// star : 2015-01-08 Star India Private Limited
+star
+
+// statebank : 2015-03-12 STATE BANK OF INDIA
+statebank
+
+// statefarm : 2015-07-30 State Farm Mutual Automobile Insurance Company
+statefarm
+
+// stc : 2014-10-09 Saudi Telecom Company
+stc
+
+// stcgroup : 2014-10-09 Saudi Telecom Company
+stcgroup
+
+// stockholm : 2014-12-18 Stockholms kommun
+stockholm
+
+// storage : 2014-12-22 XYZ.COM LLC
+storage
+
+// store : 2015-04-09 Radix FZC
+store
+
+// stream : 2016-01-08 dot Stream Limited
+stream
+
+// studio : 2015-02-11 Dog Beach, LLC
+studio
+
+// study : 2014-12-11 Registry Services, LLC
+study
+
+// style : 2014-12-04 Binky Moon, LLC
+style
+
+// sucks : 2014-12-22 Vox Populi Registry Ltd.
+sucks
+
+// supplies : 2013-12-19 Binky Moon, LLC
+supplies
+
+// supply : 2013-12-19 Binky Moon, LLC
+supply
+
+// support : 2013-10-24 Binky Moon, LLC
+support
+
+// surf : 2014-01-09 Registry Services, LLC
+surf
+
+// surgery : 2014-03-20 Binky Moon, LLC
+surgery
+
+// suzuki : 2014-02-20 SUZUKI MOTOR CORPORATION
+suzuki
+
+// swatch : 2015-01-08 The Swatch Group Ltd
+swatch
+
+// swiss : 2014-10-16 Swiss Confederation
+swiss
+
+// sydney : 2014-09-18 State of New South Wales, Department of Premier and Cabinet
+sydney
+
+// systems : 2013-11-07 Binky Moon, LLC
+systems
+
+// tab : 2014-12-04 Tabcorp Holdings Limited
+tab
+
+// taipei : 2014-07-10 Taipei City Government
+taipei
+
+// talk : 2015-04-09 Amazon Registry Services, Inc.
+talk
+
+// taobao : 2015-01-15 Alibaba Group Holding Limited
+taobao
+
+// target : 2015-07-31 Target Domain Holdings, LLC
+target
+
+// tatamotors : 2015-03-12 Tata Motors Ltd
+tatamotors
+
+// tatar : 2014-04-24 Limited Liability Company "Coordination Center of Regional Domain of Tatarstan Republic"
+tatar
+
+// tattoo : 2013-08-30 Registry Services, LLC
+tattoo
+
+// tax : 2014-03-20 Binky Moon, LLC
+tax
+
+// taxi : 2015-03-19 Binky Moon, LLC
+taxi
+
+// tci : 2014-09-12 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
+tci
+
+// tdk : 2015-06-11 TDK Corporation
+tdk
+
+// team : 2015-03-05 Binky Moon, LLC
+team
+
+// tech : 2015-01-30 Radix FZC
+tech
+
+// technology : 2013-09-13 Binky Moon, LLC
+technology
+
+// temasek : 2014-08-07 Temasek Holdings (Private) Limited
+temasek
+
+// tennis : 2014-12-04 Binky Moon, LLC
+tennis
+
+// teva : 2015-07-02 Teva Pharmaceutical Industries Limited
+teva
+
+// thd : 2015-04-02 Home Depot Product Authority, LLC
+thd
+
+// theater : 2015-03-19 Binky Moon, LLC
+theater
+
+// theatre : 2015-05-07 XYZ.COM LLC
+theatre
+
+// tiaa : 2015-07-23 Teachers Insurance and Annuity Association of America
+tiaa
+
+// tickets : 2015-02-05 XYZ.COM LLC
+tickets
+
+// tienda : 2013-11-14 Binky Moon, LLC
+tienda
+
+// tiffany : 2015-01-30 Tiffany and Company
+tiffany
+
+// tips : 2013-09-20 Binky Moon, LLC
+tips
+
+// tires : 2014-11-07 Binky Moon, LLC
+tires
+
+// tirol : 2014-04-24 punkt Tirol GmbH
+tirol
+
+// tjmaxx : 2015-07-16 The TJX Companies, Inc.
+tjmaxx
+
+// tjx : 2015-07-16 The TJX Companies, Inc.
+tjx
+
+// tkmaxx : 2015-07-16 The TJX Companies, Inc.
+tkmaxx
+
+// tmall : 2015-01-15 Alibaba Group Holding Limited
+tmall
+
+// today : 2013-09-20 Binky Moon, LLC
+today
+
+// tokyo : 2013-11-13 GMO Registry, Inc.
+tokyo
+
+// tools : 2013-11-21 Binky Moon, LLC
+tools
+
+// top : 2014-03-20 .TOP Registry
+top
+
+// toray : 2014-12-18 Toray Industries, Inc.
+toray
+
+// toshiba : 2014-04-10 TOSHIBA Corporation
+toshiba
+
+// total : 2015-08-06 TotalEnergies SE
+total
+
+// tours : 2015-01-22 Binky Moon, LLC
+tours
+
+// town : 2014-03-06 Binky Moon, LLC
+town
+
+// toyota : 2015-04-23 TOYOTA MOTOR CORPORATION
+toyota
+
+// toys : 2014-03-06 Binky Moon, LLC
+toys
+
+// trade : 2014-01-23 Elite Registry Limited
+trade
+
+// trading : 2014-12-11 Dog Beach, LLC
+trading
+
+// training : 2013-11-07 Binky Moon, LLC
+training
+
+// travel : 2015-10-09 Dog Beach, LLC
+travel
+
+// travelers : 2015-03-26 Travelers TLD, LLC
+travelers
+
+// travelersinsurance : 2015-03-26 Travelers TLD, LLC
+travelersinsurance
+
+// trust : 2014-10-16 Internet Naming Company LLC
+trust
+
+// trv : 2015-03-26 Travelers TLD, LLC
+trv
+
+// tube : 2015-06-11 Latin American Telecom LLC
+tube
+
+// tui : 2014-07-03 TUI AG
+tui
+
+// tunes : 2015-02-26 Amazon Registry Services, Inc.
+tunes
+
+// tushu : 2014-12-18 Amazon Registry Services, Inc.
+tushu
+
+// tvs : 2015-02-19 T V SUNDRAM IYENGAR & SONS LIMITED
+tvs
+
+// ubank : 2015-08-20 National Australia Bank Limited
+ubank
+
+// ubs : 2014-12-11 UBS AG
+ubs
+
+// unicom : 2015-10-15 China United Network Communications Corporation Limited
+unicom
+
+// university : 2014-03-06 Binky Moon, LLC
+university
+
+// uno : 2013-09-11 Radix FZC
+uno
+
+// uol : 2014-05-01 UBN INTERNET LTDA.
+uol
+
+// ups : 2015-06-25 UPS Market Driver, Inc.
+ups
+
+// vacations : 2013-12-05 Binky Moon, LLC
+vacations
+
+// vana : 2014-12-11 Lifestyle Domain Holdings, Inc.
+vana
+
+// vanguard : 2015-09-03 The Vanguard Group, Inc.
+vanguard
+
+// vegas : 2014-01-16 Dot Vegas, Inc.
+vegas
+
+// ventures : 2013-08-27 Binky Moon, LLC
+ventures
+
+// verisign : 2015-08-13 VeriSign, Inc.
+verisign
+
+// versicherung : 2014-03-20 tldbox GmbH
+versicherung
+
+// vet : 2014-03-06 Dog Beach, LLC
+vet
+
+// viajes : 2013-10-17 Binky Moon, LLC
+viajes
+
+// video : 2014-10-16 Dog Beach, LLC
+video
+
+// vig : 2015-05-14 VIENNA INSURANCE GROUP AG Wiener Versicherung Gruppe
+vig
+
+// viking : 2015-04-02 Viking River Cruises (Bermuda) Ltd.
+viking
+
+// villas : 2013-12-05 Binky Moon, LLC
+villas
+
+// vin : 2015-06-18 Binky Moon, LLC
+vin
+
+// vip : 2015-01-22 Registry Services, LLC
+vip
+
+// virgin : 2014-09-25 Virgin Enterprises Limited
+virgin
+
+// visa : 2015-07-30 Visa Worldwide Pte. Limited
+visa
+
+// vision : 2013-12-05 Binky Moon, LLC
+vision
+
+// viva : 2014-11-07 Saudi Telecom Company
+viva
+
+// vivo : 2015-07-31 Telefonica Brasil S.A.
+vivo
+
+// vlaanderen : 2014-02-06 DNS.be vzw
+vlaanderen
+
+// vodka : 2013-12-19 Registry Services, LLC
+vodka
+
+// volkswagen : 2015-05-14 Volkswagen Group of America Inc.
+volkswagen
+
+// volvo : 2015-11-12 Volvo Holding Sverige Aktiebolag
+volvo
+
+// vote : 2013-11-21 Monolith Registry LLC
+vote
+
+// voting : 2013-11-13 Valuetainment Corp.
+voting
+
+// voto : 2013-11-21 Monolith Registry LLC
+voto
+
+// voyage : 2013-08-27 Binky Moon, LLC
+voyage
+
+// vuelos : 2015-03-05 Travel Reservations SRL
+vuelos
+
+// wales : 2014-05-08 Nominet UK
+wales
+
+// walmart : 2015-07-31 Wal-Mart Stores, Inc.
+walmart
+
+// walter : 2014-11-13 Sandvik AB
+walter
+
+// wang : 2013-10-24 Zodiac Wang Limited
+wang
+
+// wanggou : 2014-12-18 Amazon Registry Services, Inc.
+wanggou
+
+// watch : 2013-11-14 Binky Moon, LLC
+watch
+
+// watches : 2014-12-22 Identity Digital Limited
+watches
+
+// weather : 2015-01-08 International Business Machines Corporation
+weather
+
+// weatherchannel : 2015-03-12 International Business Machines Corporation
+weatherchannel
+
+// webcam : 2014-01-23 dot Webcam Limited
+webcam
+
+// weber : 2015-06-04 Saint-Gobain Weber SA
+weber
+
+// website : 2014-04-03 Radix FZC
+website
+
+// wedding : 2014-04-24 Registry Services, LLC
+wedding
+
+// weibo : 2015-03-05 Sina Corporation
+weibo
+
+// weir : 2015-01-29 Weir Group IP Limited
+weir
+
+// whoswho : 2014-02-20 Who's Who Registry
+whoswho
+
+// wien : 2013-10-28 punkt.wien GmbH
+wien
+
+// wiki : 2013-11-07 Registry Services, LLC
+wiki
+
+// williamhill : 2014-03-13 William Hill Organization Limited
+williamhill
+
+// win : 2014-11-20 First Registry Limited
+win
+
+// windows : 2014-12-18 Microsoft Corporation
+windows
+
+// wine : 2015-06-18 Binky Moon, LLC
+wine
+
+// winners : 2015-07-16 The TJX Companies, Inc.
+winners
+
+// wme : 2014-02-13 William Morris Endeavor Entertainment, LLC
+wme
+
+// wolterskluwer : 2015-08-06 Wolters Kluwer N.V.
+wolterskluwer
+
+// woodside : 2015-07-09 Woodside Petroleum Limited
+woodside
+
+// work : 2013-12-19 Registry Services, LLC
+work
+
+// works : 2013-11-14 Binky Moon, LLC
+works
+
+// world : 2014-06-12 Binky Moon, LLC
+world
+
+// wow : 2015-10-08 Amazon Registry Services, Inc.
+wow
+
+// wtc : 2013-12-19 World Trade Centers Association, Inc.
+wtc
+
+// wtf : 2014-03-06 Binky Moon, LLC
+wtf
+
+// xbox : 2014-12-18 Microsoft Corporation
+xbox
+
+// xerox : 2014-10-24 Xerox DNHC LLC
+xerox
+
+// xfinity : 2015-07-09 Comcast IP Holdings I, LLC
+xfinity
+
+// xihuan : 2015-01-08 Beijing Qihu Keji Co., Ltd.
+xihuan
+
+// xin : 2014-12-11 Elegant Leader Limited
+xin
+
+// xn--11b4c3d : 2015-01-15 VeriSign Sarl
+कॉम
+
+// xn--1ck2e1b : 2015-02-26 Amazon Registry Services, Inc.
+セール
+
+// xn--1qqw23a : 2014-01-09 Guangzhou YU Wei Information Technology Co., Ltd.
+佛山
+
+// xn--30rr7y : 2014-06-12 Excellent First Limited
+慈善
+
+// xn--3bst00m : 2013-09-13 Eagle Horizon Limited
+集团
+
+// xn--3ds443g : 2013-09-08 TLD REGISTRY LIMITED OY
+在线
+
+// xn--3pxu8k : 2015-01-15 VeriSign Sarl
+点看
+
+// xn--42c2d9a : 2015-01-15 VeriSign Sarl
+คอม
+
+// xn--45q11c : 2013-11-21 Zodiac Gemini Ltd
+八卦
+
+// xn--4gbrim : 2013-10-04 Helium TLDs Ltd
+موقع
+
+// xn--55qw42g : 2013-11-08 China Organizational Name Administration Center
+公益
+
+// xn--55qx5d : 2013-11-14 China Internet Network Information Center (CNNIC)
+公司
+
+// xn--5su34j936bgsg : 2015-09-03 Shangri‐La International Hotel Management Limited
+香格里拉
+
+// xn--5tzm5g : 2014-12-22 Global Website TLD Asia Limited
+网站
+
+// xn--6frz82g : 2013-09-23 Identity Digital Limited
+移动
+
+// xn--6qq986b3xl : 2013-09-13 Tycoon Treasure Limited
+我爱你
+
+// xn--80adxhks : 2013-12-19 Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID)
+москва
+
+// xn--80aqecdr1a : 2015-10-21 Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication)
+католик
+
+// xn--80asehdb : 2013-07-14 CORE Association
+онлайн
+
+// xn--80aswg : 2013-07-14 CORE Association
+сайт
+
+// xn--8y0a063a : 2015-03-26 China United Network Communications Corporation Limited
+联通
+
+// xn--9dbq2a : 2015-01-15 VeriSign Sarl
+קום
+
+// xn--9et52u : 2014-06-12 RISE VICTORY LIMITED
+时尚
+
+// xn--9krt00a : 2015-03-12 Sina Corporation
+微博
+
+// xn--b4w605ferd : 2014-08-07 Temasek Holdings (Private) Limited
+淡马锡
+
+// xn--bck1b9a5dre4c : 2015-02-26 Amazon Registry Services, Inc.
+ファッション
+
+// xn--c1avg : 2013-11-14 Public Interest Registry
+орг
+
+// xn--c2br7g : 2015-01-15 VeriSign Sarl
+नेट
+
+// xn--cck2b3b : 2015-02-26 Amazon Registry Services, Inc.
+ストア
+
+// xn--cckwcxetd : 2019-12-19 Amazon Registry Services, Inc.
+アマゾン
+
+// xn--cg4bki : 2013-09-27 SAMSUNG SDS CO., LTD
+삼성
+
+// xn--czr694b : 2014-01-16 Internet DotTrademark Organisation Limited
+商标
+
+// xn--czrs0t : 2013-12-19 Binky Moon, LLC
+商店
+
+// xn--czru2d : 2013-11-21 Zodiac Aquarius Limited
+商城
+
+// xn--d1acj3b : 2013-11-20 The Foundation for Network Initiatives “The Smart Internet”
+дети
+
+// xn--eckvdtc9d : 2014-12-18 Amazon Registry Services, Inc.
+ポイント
+
+// xn--efvy88h : 2014-08-22 Guangzhou YU Wei Information Technology Co., Ltd.
+新闻
+
+// xn--fct429k : 2015-04-09 Amazon Registry Services, Inc.
+家電
+
+// xn--fhbei : 2015-01-15 VeriSign Sarl
+كوم
+
+// xn--fiq228c5hs : 2013-09-08 TLD REGISTRY LIMITED OY
+中文网
+
+// xn--fiq64b : 2013-10-14 CITIC Group Corporation
+中信
+
+// xn--fjq720a : 2014-05-22 Binky Moon, LLC
+娱乐
+
+// xn--flw351e : 2014-07-31 Charleston Road Registry Inc.
+谷歌
+
+// xn--fzys8d69uvgm : 2015-05-14 PCCW Enterprises Limited
+電訊盈科
+
+// xn--g2xx48c : 2015-01-30 Nawang Heli(Xiamen) Network Service Co., LTD.
+购物
+
+// xn--gckr3f0f : 2015-02-26 Amazon Registry Services, Inc.
+クラウド
+
+// xn--gk3at1e : 2015-10-08 Amazon Registry Services, Inc.
+通販
+
+// xn--hxt814e : 2014-05-15 Zodiac Taurus Limited
+网店
+
+// xn--i1b6b1a6a2e : 2013-11-14 Public Interest Registry
+संगठन
+
+// xn--imr513n : 2014-12-11 Internet DotTrademark Organisation Limited
+餐厅
+
+// xn--io0a7i : 2013-11-14 China Internet Network Information Center (CNNIC)
+网络
+
+// xn--j1aef : 2015-01-15 VeriSign Sarl
+ком
+
+// xn--jlq480n2rg : 2019-12-19 Amazon Registry Services, Inc.
+亚马逊
+
+// xn--jvr189m : 2015-02-26 Amazon Registry Services, Inc.
+食品
+
+// xn--kcrx77d1x4a : 2014-11-07 Koninklijke Philips N.V.
+飞利浦
+
+// xn--kput3i : 2014-02-13 Beijing RITT-Net Technology Development Co., Ltd
+手机
+
+// xn--mgba3a3ejt : 2014-11-20 Aramco Services Company
+ارامكو
+
+// xn--mgba7c0bbn0a : 2015-05-14 Competrol (Luxembourg) Sarl
+العليان
+
+// xn--mgbaakc7dvf : 2015-09-03 Emirates Telecommunications Corporation (trading as Etisalat)
+اتصالات
+
+// xn--mgbab2bd : 2013-10-31 CORE Association
+بازار
+
+// xn--mgbca7dzdo : 2015-07-30 Abu Dhabi Systems and Information Centre
+ابوظبي
+
+// xn--mgbi4ecexp : 2015-10-21 Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication)
+كاثوليك
+
+// xn--mgbt3dhd : 2014-09-04 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
+همراه
+
+// xn--mk1bu44c : 2015-01-15 VeriSign Sarl
+닷컴
+
+// xn--mxtq1m : 2014-03-06 Net-Chinese Co., Ltd.
+政府
+
+// xn--ngbc5azd : 2013-07-13 International Domain Registry Pty. Ltd.
+شبكة
+
+// xn--ngbe9e0a : 2014-12-04 Kuwait Finance House
+بيتك
+
+// xn--ngbrx : 2015-11-12 League of Arab States
+عرب
+
+// xn--nqv7f : 2013-11-14 Public Interest Registry
+机构
+
+// xn--nqv7fs00ema : 2013-11-14 Public Interest Registry
+组织机构
+
+// xn--nyqy26a : 2014-11-07 Stable Tone Limited
+健康
+
+// xn--otu796d : 2017-08-06 Jiang Yu Liang Cai Technology Company Limited
+招聘
+
+// xn--p1acf : 2013-12-12 Rusnames Limited
+рус
+
+// xn--pssy2u : 2015-01-15 VeriSign Sarl
+大拿
+
+// xn--q9jyb4c : 2013-09-17 Charleston Road Registry Inc.
+みんな
+
+// xn--qcka1pmc : 2014-07-31 Charleston Road Registry Inc.
+グーグル
+
+// xn--rhqv96g : 2013-09-11 Stable Tone Limited
+世界
+
+// xn--rovu88b : 2015-02-26 Amazon Registry Services, Inc.
+書籍
+
+// xn--ses554g : 2014-01-16 KNET Co., Ltd.
+网址
+
+// xn--t60b56a : 2015-01-15 VeriSign Sarl
+닷넷
+
+// xn--tckwe : 2015-01-15 VeriSign Sarl
+コム
+
+// xn--tiq49xqyj : 2015-10-21 Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication)
+天主教
+
+// xn--unup4y : 2013-07-14 Binky Moon, LLC
+游戏
+
+// xn--vermgensberater-ctb : 2014-06-23 Deutsche Vermögensberatung Aktiengesellschaft DVAG
+vermögensberater
+
+// xn--vermgensberatung-pwb : 2014-06-23 Deutsche Vermögensberatung Aktiengesellschaft DVAG
+vermögensberatung
+
+// xn--vhquv : 2013-08-27 Binky Moon, LLC
+企业
+
+// xn--vuq861b : 2014-10-16 Beijing Tele-info Technology Co., Ltd.
+信息
+
+// xn--w4r85el8fhu5dnra : 2015-04-30 Kerry Trading Co. Limited
+嘉里大酒店
+
+// xn--w4rs40l : 2015-07-30 Kerry Trading Co. Limited
+嘉里
+
+// xn--xhq521b : 2013-11-14 Guangzhou YU Wei Information Technology Co., Ltd.
+广东
+
+// xn--zfr164b : 2013-11-08 China Organizational Name Administration Center
+政务
+
+// xyz : 2013-12-05 XYZ.COM LLC
+xyz
+
+// yachts : 2014-01-09 XYZ.COM LLC
+yachts
+
+// yahoo : 2015-04-02 Oath Inc.
+yahoo
+
+// yamaxun : 2014-12-18 Amazon Registry Services, Inc.
+yamaxun
+
+// yandex : 2014-04-10 Yandex Europe B.V.
+yandex
+
+// yodobashi : 2014-11-20 YODOBASHI CAMERA CO.,LTD.
+yodobashi
+
+// yoga : 2014-05-29 Registry Services, LLC
+yoga
+
+// yokohama : 2013-12-12 GMO Registry, Inc.
+yokohama
+
+// you : 2015-04-09 Amazon Registry Services, Inc.
+you
+
+// youtube : 2014-05-01 Charleston Road Registry Inc.
+youtube
+
+// yun : 2015-01-08 Beijing Qihu Keji Co., Ltd.
+yun
+
+// zappos : 2015-06-25 Amazon Registry Services, Inc.
+zappos
+
+// zara : 2014-11-07 Industria de Diseño Textil, S.A. (INDITEX, S.A.)
+zara
+
+// zero : 2014-12-18 Amazon Registry Services, Inc.
+zero
+
+// zip : 2014-05-08 Charleston Road Registry Inc.
+zip
+
+// zone : 2013-11-14 Binky Moon, LLC
+zone
+
+// zuerich : 2014-11-07 Kanton Zürich (Canton of Zurich)
+zuerich
+
+
+// ===END ICANN DOMAINS===
+// ===BEGIN PRIVATE DOMAINS===
+// (Note: these are in alphabetical order by company name)
+
+// 1GB LLC : https://www.1gb.ua/
+// Submitted by 1GB LLC <noc@1gb.com.ua>
+cc.ua
+inf.ua
+ltd.ua
+
+// 611coin : https://611project.org/
+611.to
+
+// Aaron Marais' Gitlab pages: https://lab.aaronleem.co.za
+// Submitted by Aaron Marais <its_me@aaronleem.co.za>
+graphox.us
+
+// accesso Technology Group, plc. : https://accesso.com/
+// Submitted by accesso Team <accessoecommerce@accesso.com>
+*.devcdnaccesso.com
+
+// Acorn Labs : https://acorn.io
+// Submitted by Craig Jellick <domains@acorn.io>
+*.on-acorn.io
+
+// ActiveTrail: https://www.activetrail.biz/
+// Submitted by Ofer Kalaora <postmaster@activetrail.com>
+activetrail.biz
+
+// Adobe : https://www.adobe.com/
+// Submitted by Ian Boston <boston@adobe.com> and Lars Trieloff <trieloff@adobe.com>
+adobeaemcloud.com
+*.dev.adobeaemcloud.com
+hlx.live
+adobeaemcloud.net
+hlx.page
+hlx3.page
+
+// Adobe Developer Platform : https://developer.adobe.com
+// Submitted by Jesse MacFadyen<jessem@adobe.com>
+adobeio-static.net
+adobeioruntime.net
+
+// Agnat sp. z o.o. : https://domena.pl
+// Submitted by Przemyslaw Plewa <it-admin@domena.pl>
+beep.pl
+
+// Airkit : https://www.airkit.com/
+// Submitted by Grant Cooksey <security@airkit.com>
+airkitapps.com
+airkitapps-au.com
+airkitapps.eu
+
+// Aiven: https://aiven.io/
+// Submitted by Etienne Stalmans <security@aiven.io>
+aivencloud.com
+
+// Akamai : https://www.akamai.com/
+// Submitted by Akamai Team <publicsuffixlist@akamai.com>
+akadns.net
+akamai.net
+akamai-staging.net
+akamaiedge.net
+akamaiedge-staging.net
+akamaihd.net
+akamaihd-staging.net
+akamaiorigin.net
+akamaiorigin-staging.net
+akamaized.net
+akamaized-staging.net
+edgekey.net
+edgekey-staging.net
+edgesuite.net
+edgesuite-staging.net
+
+// alboto.ca : http://alboto.ca
+// Submitted by Anton Avramov <avramov@alboto.ca>
+barsy.ca
+
+// Alces Software Ltd : http://alces-software.com
+// Submitted by Mark J. Titorenko <mark.titorenko@alces-software.com>
+*.compute.estate
+*.alces.network
+
+// all-inkl.com : https://all-inkl.com
+// Submitted by Werner Kaltofen <wk@all-inkl.com>
+kasserver.com
+
+// Altervista: https://www.altervista.org
+// Submitted by Carlo Cannas <tech_staff@altervista.it>
+altervista.org
+
+// alwaysdata : https://www.alwaysdata.com
+// Submitted by Cyril <admin@alwaysdata.com>
+alwaysdata.net
+
+// Amaze Software : https://amaze.co
+// Submitted by Domain Admin <domainadmin@amaze.co>
+myamaze.net
+
+// Amazon : https://www.amazon.com/
+// Submitted by AWS Security <psl-maintainers@amazon.com>
+// Subsections of Amazon/subsidiaries will appear until "concludes" tag
+
+// Amazon CloudFront
+// Submitted by Donavan Miller <donavanm@amazon.com>
+// Reference: 54144616-fd49-4435-8535-19c6a601bdb3
+cloudfront.net
+
+// Amazon EC2
+// Submitted by Luke Wells <psl-maintainers@amazon.com>
+// Reference: 4c38fa71-58ac-4768-99e5-689c1767e537
+*.compute.amazonaws.com
+*.compute-1.amazonaws.com
+*.compute.amazonaws.com.cn
+us-east-1.amazonaws.com
+
+// Amazon S3
+// Submitted by Luke Wells <psl-maintainers@amazon.com>
+// Reference: d068bd97-f0a9-4838-a6d8-954b622ef4ae
+s3.cn-north-1.amazonaws.com.cn
+s3.dualstack.ap-northeast-1.amazonaws.com
+s3.dualstack.ap-northeast-2.amazonaws.com
+s3.ap-northeast-2.amazonaws.com
+s3-website.ap-northeast-2.amazonaws.com
+s3.dualstack.ap-south-1.amazonaws.com
+s3.ap-south-1.amazonaws.com
+s3-website.ap-south-1.amazonaws.com
+s3.dualstack.ap-southeast-1.amazonaws.com
+s3.dualstack.ap-southeast-2.amazonaws.com
+s3.dualstack.ca-central-1.amazonaws.com
+s3.ca-central-1.amazonaws.com
+s3-website.ca-central-1.amazonaws.com
+s3.dualstack.eu-central-1.amazonaws.com
+s3.eu-central-1.amazonaws.com
+s3-website.eu-central-1.amazonaws.com
+s3.dualstack.eu-west-1.amazonaws.com
+s3.dualstack.eu-west-2.amazonaws.com
+s3.eu-west-2.amazonaws.com
+s3-website.eu-west-2.amazonaws.com
+s3.dualstack.eu-west-3.amazonaws.com
+s3.eu-west-3.amazonaws.com
+s3-website.eu-west-3.amazonaws.com
+s3.amazonaws.com
+s3-ap-northeast-1.amazonaws.com
+s3-ap-northeast-2.amazonaws.com
+s3-ap-south-1.amazonaws.com
+s3-ap-southeast-1.amazonaws.com
+s3-ap-southeast-2.amazonaws.com
+s3-ca-central-1.amazonaws.com
+s3-eu-central-1.amazonaws.com
+s3-eu-west-1.amazonaws.com
+s3-eu-west-2.amazonaws.com
+s3-eu-west-3.amazonaws.com
+s3-external-1.amazonaws.com
+s3-fips-us-gov-west-1.amazonaws.com
+s3-sa-east-1.amazonaws.com
+s3-us-east-2.amazonaws.com
+s3-us-gov-west-1.amazonaws.com
+s3-us-west-1.amazonaws.com
+s3-us-west-2.amazonaws.com
+s3-website-ap-northeast-1.amazonaws.com
+s3-website-ap-southeast-1.amazonaws.com
+s3-website-ap-southeast-2.amazonaws.com
+s3-website-eu-west-1.amazonaws.com
+s3-website-sa-east-1.amazonaws.com
+s3-website-us-east-1.amazonaws.com
+s3-website-us-west-1.amazonaws.com
+s3-website-us-west-2.amazonaws.com
+s3.dualstack.sa-east-1.amazonaws.com
+s3.dualstack.us-east-1.amazonaws.com
+s3.dualstack.us-east-2.amazonaws.com
+s3.us-east-2.amazonaws.com
+s3-website.us-east-2.amazonaws.com
+
+// AWS Cloud9
+// Submitted by: AWS Security <psl-maintainers@amazon.com>
+// Reference: 2b6dfa9a-3a7f-4367-b2e7-0321e77c0d59
+vfs.cloud9.af-south-1.amazonaws.com
+webview-assets.cloud9.af-south-1.amazonaws.com
+vfs.cloud9.ap-east-1.amazonaws.com
+webview-assets.cloud9.ap-east-1.amazonaws.com
+vfs.cloud9.ap-northeast-1.amazonaws.com
+webview-assets.cloud9.ap-northeast-1.amazonaws.com
+vfs.cloud9.ap-northeast-2.amazonaws.com
+webview-assets.cloud9.ap-northeast-2.amazonaws.com
+vfs.cloud9.ap-northeast-3.amazonaws.com
+webview-assets.cloud9.ap-northeast-3.amazonaws.com
+vfs.cloud9.ap-south-1.amazonaws.com
+webview-assets.cloud9.ap-south-1.amazonaws.com
+vfs.cloud9.ap-southeast-1.amazonaws.com
+webview-assets.cloud9.ap-southeast-1.amazonaws.com
+vfs.cloud9.ap-southeast-2.amazonaws.com
+webview-assets.cloud9.ap-southeast-2.amazonaws.com
+vfs.cloud9.ca-central-1.amazonaws.com
+webview-assets.cloud9.ca-central-1.amazonaws.com
+vfs.cloud9.eu-central-1.amazonaws.com
+webview-assets.cloud9.eu-central-1.amazonaws.com
+vfs.cloud9.eu-north-1.amazonaws.com
+webview-assets.cloud9.eu-north-1.amazonaws.com
+vfs.cloud9.eu-south-1.amazonaws.com
+webview-assets.cloud9.eu-south-1.amazonaws.com
+vfs.cloud9.eu-west-1.amazonaws.com
+webview-assets.cloud9.eu-west-1.amazonaws.com
+vfs.cloud9.eu-west-2.amazonaws.com
+webview-assets.cloud9.eu-west-2.amazonaws.com
+vfs.cloud9.eu-west-3.amazonaws.com
+webview-assets.cloud9.eu-west-3.amazonaws.com
+vfs.cloud9.me-south-1.amazonaws.com
+webview-assets.cloud9.me-south-1.amazonaws.com
+vfs.cloud9.sa-east-1.amazonaws.com
+webview-assets.cloud9.sa-east-1.amazonaws.com
+vfs.cloud9.us-east-1.amazonaws.com
+webview-assets.cloud9.us-east-1.amazonaws.com
+vfs.cloud9.us-east-2.amazonaws.com
+webview-assets.cloud9.us-east-2.amazonaws.com
+vfs.cloud9.us-west-1.amazonaws.com
+webview-assets.cloud9.us-west-1.amazonaws.com
+vfs.cloud9.us-west-2.amazonaws.com
+webview-assets.cloud9.us-west-2.amazonaws.com
+
+// AWS Elastic Beanstalk
+// Submitted by Luke Wells <psl-maintainers@amazon.com>
+// Reference: aa202394-43a0-4857-b245-8db04549137e
+cn-north-1.eb.amazonaws.com.cn
+cn-northwest-1.eb.amazonaws.com.cn
+elasticbeanstalk.com
+ap-northeast-1.elasticbeanstalk.com
+ap-northeast-2.elasticbeanstalk.com
+ap-northeast-3.elasticbeanstalk.com
+ap-south-1.elasticbeanstalk.com
+ap-southeast-1.elasticbeanstalk.com
+ap-southeast-2.elasticbeanstalk.com
+ca-central-1.elasticbeanstalk.com
+eu-central-1.elasticbeanstalk.com
+eu-west-1.elasticbeanstalk.com
+eu-west-2.elasticbeanstalk.com
+eu-west-3.elasticbeanstalk.com
+sa-east-1.elasticbeanstalk.com
+us-east-1.elasticbeanstalk.com
+us-east-2.elasticbeanstalk.com
+us-gov-west-1.elasticbeanstalk.com
+us-west-1.elasticbeanstalk.com
+us-west-2.elasticbeanstalk.com
+
+// (AWS) Elastic Load Balancing
+// Submitted by Luke Wells <psl-maintainers@amazon.com>
+// Reference: 12a3d528-1bac-4433-a359-a395867ffed2
+*.elb.amazonaws.com.cn
+*.elb.amazonaws.com
+
+// AWS Global Accelerator
+// Submitted by Daniel Massaguer <psl-maintainers@amazon.com>
+// Reference: d916759d-a08b-4241-b536-4db887383a6a
+awsglobalaccelerator.com
+
+// eero
+// Submitted by Yue Kang <eero-dynamic-dns@amazon.com>
+// Reference: 264afe70-f62c-4c02-8ab9-b5281ed24461
+eero.online
+eero-stage.online
+
+// concludes Amazon
+
+// Amune : https://amune.org/
+// Submitted by Team Amune <cert@amune.org>
+t3l3p0rt.net
+tele.amune.org
+
+// Apigee : https://apigee.com/
+// Submitted by Apigee Security Team <security@apigee.com>
+apigee.io
+
+// Apphud : https://apphud.com
+// Submitted by Alexander Selivanov <alex@apphud.com>
+siiites.com
+
+// Appspace : https://www.appspace.com
+// Submitted by Appspace Security Team <security@appspace.com>
+appspacehosted.com
+appspaceusercontent.com
+
+// Appudo UG (haftungsbeschränkt) : https://www.appudo.com
+// Submitted by Alexander Hochbaum <admin@appudo.com>
+appudo.net
+
+// Aptible : https://www.aptible.com/
+// Submitted by Thomas Orozco <thomas@aptible.com>
+on-aptible.com
+
+// ASEINet : https://www.aseinet.com/
+// Submitted by Asei SEKIGUCHI <mail@aseinet.com>
+user.aseinet.ne.jp
+gv.vc
+d.gv.vc
+
+// Asociación Amigos de la Informática "Euskalamiga" : http://encounter.eus/
+// Submitted by Hector Martin <marcan@euskalencounter.org>
+user.party.eus
+
+// Association potager.org : https://potager.org/
+// Submitted by Lunar <jardiniers@potager.org>
+pimienta.org
+poivron.org
+potager.org
+sweetpepper.org
+
+// ASUSTOR Inc. : http://www.asustor.com
+// Submitted by Vincent Tseng <vincenttseng@asustor.com>
+myasustor.com
+
+// Atlassian : https://atlassian.com
+// Submitted by Sam Smyth <devloop@atlassian.com>
+cdn.prod.atlassian-dev.net
+
+// Authentick UG (haftungsbeschränkt) : https://authentick.net
+// Submitted by Lukas Reschke <lukas@authentick.net>
+translated.page
+
+// Autocode : https://autocode.com
+// Submitted by Jacob Lee <jacob@autocode.com>
+autocode.dev
+
+// AVM : https://avm.de
+// Submitted by Andreas Weise <a.weise@avm.de>
+myfritz.net
+
+// AVStack Pte. Ltd. : https://avstack.io
+// Submitted by Jasper Hugo <jasper@avstack.io>
+onavstack.net
+
+// AW AdvisorWebsites.com Software Inc : https://advisorwebsites.com
+// Submitted by James Kennedy <domains@advisorwebsites.com>
+*.awdev.ca
+*.advisor.ws
+
+// AZ.pl sp. z.o.o: https://az.pl
+// Submitted by Krzysztof Wolski <krzysztof.wolski@home.eu>
+ecommerce-shop.pl
+
+// b-data GmbH : https://www.b-data.io
+// Submitted by Olivier Benz <olivier.benz@b-data.ch>
+b-data.io
+
+// backplane : https://www.backplane.io
+// Submitted by Anthony Voutas <anthony@backplane.io>
+backplaneapp.io
+
+// Balena : https://www.balena.io
+// Submitted by Petros Angelatos <petrosagg@balena.io>
+balena-devices.com
+
+// University of Banja Luka : https://unibl.org
+// Domains for Republic of Srpska administrative entity.
+// Submitted by Marko Ivanovic <kormang@hotmail.rs>
+rs.ba
+
+// Banzai Cloud
+// Submitted by Janos Matyas <info@banzaicloud.com>
+*.banzai.cloud
+app.banzaicloud.io
+*.backyards.banzaicloud.io
+
+// BASE, Inc. : https://binc.jp
+// Submitted by Yuya NAGASAWA <public-suffix-list@binc.jp>
+base.ec
+official.ec
+buyshop.jp
+fashionstore.jp
+handcrafted.jp
+kawaiishop.jp
+supersale.jp
+theshop.jp
+shopselect.net
+base.shop
+
+// BeagleBoard.org Foundation : https://beagleboard.org
+// Submitted by Jason Kridner <jkridner@beagleboard.org>
+beagleboard.io
+
+// Beget Ltd
+// Submitted by Lev Nekrasov <lnekrasov@beget.com>
+*.beget.app
+
+// BetaInABox
+// Submitted by Adrian <adrian@betainabox.com>
+betainabox.com
+
+// BinaryLane : http://www.binarylane.com
+// Submitted by Nathan O'Sullivan <nathan@mammoth.com.au>
+bnr.la
+
+// Bitbucket : http://bitbucket.org
+// Submitted by Andy Ortlieb <aortlieb@atlassian.com>
+bitbucket.io
+
+// Blackbaud, Inc. : https://www.blackbaud.com
+// Submitted by Paul Crowder <paul.crowder@blackbaud.com>
+blackbaudcdn.net
+
+// Blatech : http://www.blatech.net
+// Submitted by Luke Bratch <luke@bratch.co.uk>
+of.je
+
+// Blue Bite, LLC : https://bluebite.com
+// Submitted by Joshua Weiss <admin.engineering@bluebite.com>
+bluebite.io
+
+// Boomla : https://boomla.com
+// Submitted by Tibor Halter <thalter@boomla.com>
+boomla.net
+
+// Boutir : https://www.boutir.com
+// Submitted by Eric Ng Ka Ka <ngkaka@boutir.com>
+boutir.com
+
+// Boxfuse : https://boxfuse.com
+// Submitted by Axel Fontaine <axel@boxfuse.com>
+boxfuse.io
+
+// bplaced : https://www.bplaced.net/
+// Submitted by Miroslav Bozic <security@bplaced.net>
+square7.ch
+bplaced.com
+bplaced.de
+square7.de
+bplaced.net
+square7.net
+
+// Brendly : https://brendly.rs
+// Submitted by Dusan Radovanovic <dusan.radovanovic@brendly.rs>
+shop.brendly.rs
+
+// BrowserSafetyMark
+// Submitted by Dave Tharp <browsersafetymark.io@quicinc.com>
+browsersafetymark.io
+
+// Bytemark Hosting : https://www.bytemark.co.uk
+// Submitted by Paul Cammish <paul.cammish@bytemark.co.uk>
+uk0.bigv.io
+dh.bytemark.co.uk
+vm.bytemark.co.uk
+
+// Caf.js Labs LLC : https://www.cafjs.com
+// Submitted by Antonio Lain <antlai@cafjs.com>
+cafjs.com
+
+// callidomus : https://www.callidomus.com/
+// Submitted by Marcus Popp <admin@callidomus.com>
+mycd.eu
+
+// Canva Pty Ltd : https://canva.com/
+// Submitted by Joel Aquilina <publicsuffixlist@canva.com>
+canva-apps.cn
+canva-apps.com
+
+// Carrd : https://carrd.co
+// Submitted by AJ <aj@carrd.co>
+drr.ac
+uwu.ai
+carrd.co
+crd.co
+ju.mp
+
+// CentralNic : http://www.centralnic.com/names/domains
+// Submitted by registry <gavin.brown@centralnic.com>
+ae.org
+br.com
+cn.com
+com.de
+com.se
+de.com
+eu.com
+gb.net
+hu.net
+jp.net
+jpn.com
+mex.com
+ru.com
+sa.com
+se.net
+uk.com
+uk.net
+us.com
+za.bz
+za.com
+
+// No longer operated by CentralNic, these entries should be adopted and/or removed by current operators
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+ar.com
+hu.com
+kr.com
+no.com
+qc.com
+uy.com
+
+// Africa.com Web Solutions Ltd : https://registry.africa.com
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+africa.com
+
+// iDOT Services Limited : http://www.domain.gr.com
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+gr.com
+
+// Radix FZC : http://domains.in.net
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+in.net
+web.in
+
+// US REGISTRY LLC : http://us.org
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+us.org
+
+// co.com Registry, LLC : https://registry.co.com
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+co.com
+
+// Roar Domains LLC : https://roar.basketball/
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+aus.basketball
+nz.basketball
+
+// BRS Media : https://brsmedia.com/
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+radio.am
+radio.fm
+
+// c.la : http://www.c.la/
+c.la
+
+// certmgr.org : https://certmgr.org
+// Submitted by B. Blechschmidt <hostmaster@certmgr.org>
+certmgr.org
+
+// Cityhost LLC : https://cityhost.ua
+// Submitted by Maksym Rivtin <support@cityhost.net.ua>
+cx.ua
+
+// Civilized Discourse Construction Kit, Inc. : https://www.discourse.org/
+// Submitted by Rishabh Nambiar & Michael Brown <team@discourse.org>
+discourse.group
+discourse.team
+
+// Clever Cloud : https://www.clever-cloud.com/
+// Submitted by Quentin Adam <noc@clever-cloud.com>
+cleverapps.io
+
+// Clerk : https://www.clerk.dev
+// Submitted by Colin Sidoti <systems@clerk.dev>
+clerk.app
+clerkstage.app
+*.lcl.dev
+*.lclstage.dev
+*.stg.dev
+*.stgstage.dev
+
+// ClickRising : https://clickrising.com/
+// Submitted by Umut Gumeli <infrastructure-publicsuffixlist@clickrising.com>
+clickrising.net
+
+// Cloud66 : https://www.cloud66.com/
+// Submitted by Khash Sajadi <khash@cloud66.com>
+c66.me
+cloud66.ws
+cloud66.zone
+
+// CloudAccess.net : https://www.cloudaccess.net/
+// Submitted by Pawel Panek <noc@cloudaccess.net>
+jdevcloud.com
+wpdevcloud.com
+cloudaccess.host
+freesite.host
+cloudaccess.net
+
+// cloudControl : https://www.cloudcontrol.com/
+// Submitted by Tobias Wilken <tw@cloudcontrol.com>
+cloudcontrolled.com
+cloudcontrolapp.com
+
+// Cloudera, Inc. : https://www.cloudera.com/
+// Submitted by Kedarnath Waikar <security@cloudera.com>
+*.cloudera.site
+
+// Cloudflare, Inc. : https://www.cloudflare.com/
+// Submitted by Cloudflare Team <publicsuffixlist@cloudflare.com>
+cf-ipfs.com
+cloudflare-ipfs.com
+trycloudflare.com
+pages.dev
+r2.dev
+workers.dev
+
+// Clovyr : https://clovyr.io
+// Submitted by Patrick Nielsen <patrick@clovyr.io>
+wnext.app
+
+// co.ca : http://registry.co.ca/
+co.ca
+
+// Co & Co : https://co-co.nl/
+// Submitted by Govert Versluis <govert@co-co.nl>
+*.otap.co
+
+// i-registry s.r.o. : http://www.i-registry.cz/
+// Submitted by Martin Semrad <semrad@i-registry.cz>
+co.cz
+
+// CDN77.com : http://www.cdn77.com
+// Submitted by Jan Krpes <jan.krpes@cdn77.com>
+c.cdn77.org
+cdn77-ssl.net
+r.cdn77.net
+rsc.cdn77.org
+ssl.origin.cdn77-secure.org
+
+// Cloud DNS Ltd : http://www.cloudns.net
+// Submitted by Aleksander Hristov <noc@cloudns.net>
+cloudns.asia
+cloudns.biz
+cloudns.club
+cloudns.cc
+cloudns.eu
+cloudns.in
+cloudns.info
+cloudns.org
+cloudns.pro
+cloudns.pw
+cloudns.us
+
+// CNPY : https://cnpy.gdn
+// Submitted by Angelo Gladding <angelo@lahacker.net>
+cnpy.gdn
+
+// Codeberg e. V. : https://codeberg.org
+// Submitted by Moritz Marquardt <git@momar.de>
+codeberg.page
+
+// CoDNS B.V.
+co.nl
+co.no
+
+// Combell.com : https://www.combell.com
+// Submitted by Thomas Wouters <thomas.wouters@combellgroup.com>
+webhosting.be
+hosting-cluster.nl
+
+// Coordination Center for TLD RU and XN--P1AI : https://cctld.ru/en/domains/domens_ru/reserved/
+// Submitted by George Georgievsky <gug@cctld.ru>
+ac.ru
+edu.ru
+gov.ru
+int.ru
+mil.ru
+test.ru
+
+// COSIMO GmbH : http://www.cosimo.de
+// Submitted by Rene Marticke <rmarticke@cosimo.de>
+dyn.cosidns.de
+dynamisches-dns.de
+dnsupdater.de
+internet-dns.de
+l-o-g-i-n.de
+dynamic-dns.info
+feste-ip.net
+knx-server.net
+static-access.net
+
+// Craynic, s.r.o. : http://www.craynic.com/
+// Submitted by Ales Krajnik <ales.krajnik@craynic.com>
+realm.cz
+
+// Cryptonomic : https://cryptonomic.net/
+// Submitted by Andrew Cady <public-suffix-list@cryptonomic.net>
+*.cryptonomic.net
+
+// Cupcake : https://cupcake.io/
+// Submitted by Jonathan Rudenberg <jonathan@cupcake.io>
+cupcake.is
+
+// Curv UG : https://curv-labs.de/
+// Submitted by Marvin Wiesner <Marvin@curv-labs.de>
+curv.dev
+
+// Customer OCI - Oracle Dyn https://cloud.oracle.com/home https://dyn.com/dns/
+// Submitted by Gregory Drake <support@dyn.com>
+// Note: This is intended to also include customer-oci.com due to wildcards implicitly including the current label
+*.customer-oci.com
+*.oci.customer-oci.com
+*.ocp.customer-oci.com
+*.ocs.customer-oci.com
+
+// cyon GmbH : https://www.cyon.ch/
+// Submitted by Dominic Luechinger <dol@cyon.ch>
+cyon.link
+cyon.site
+
+// Danger Science Group: https://dangerscience.com/
+// Submitted by Skylar MacDonald <skylar@dangerscience.com>
+fnwk.site
+folionetwork.site
+platform0.app
+
+// Daplie, Inc : https://daplie.com
+// Submitted by AJ ONeal <aj@daplie.com>
+daplie.me
+localhost.daplie.me
+
+// Datto, Inc. : https://www.datto.com/
+// Submitted by Philipp Heckel <ph@datto.com>
+dattolocal.com
+dattorelay.com
+dattoweb.com
+mydatto.com
+dattolocal.net
+mydatto.net
+
+// Dansk.net : http://www.dansk.net/
+// Submitted by Anani Voule <digital@digital.co.dk>
+biz.dk
+co.dk
+firm.dk
+reg.dk
+store.dk
+
+// dappnode.io : https://dappnode.io/
+// Submitted by Abel Boldu / DAppNode Team <community@dappnode.io>
+dyndns.dappnode.io
+
+// dapps.earth : https://dapps.earth/
+// Submitted by Daniil Burdakov <icqkill@gmail.com>
+*.dapps.earth
+*.bzz.dapps.earth
+
+// Dark, Inc. : https://darklang.com
+// Submitted by Paul Biggar <ops@darklang.com>
+builtwithdark.com
+
+// DataDetect, LLC. : https://datadetect.com
+// Submitted by Andrew Banchich <abanchich@sceven.com>
+demo.datadetect.com
+instance.datadetect.com
+
+// Datawire, Inc : https://www.datawire.io
+// Submitted by Richard Li <secalert@datawire.io>
+edgestack.me
+
+// DDNS5 : https://ddns5.com
+// Submitted by Cameron Elliott <cameron@cameronelliott.com>
+ddns5.com
+
+// Debian : https://www.debian.org/
+// Submitted by Peter Palfrader / Debian Sysadmin Team <dsa-publicsuffixlist@debian.org>
+debian.net
+
+// Deno Land Inc : https://deno.com/
+// Submitted by Luca Casonato <hostmaster@deno.com>
+deno.dev
+deno-staging.dev
+
+// deSEC : https://desec.io/
+// Submitted by Peter Thomassen <peter@desec.io>
+dedyn.io
+
+// Deta: https://www.deta.sh/
+// Submitted by Aavash Shrestha <aavash@deta.sh>
+deta.app
+deta.dev
+
+// Diher Solutions : https://diher.solutions
+// Submitted by Didi Hermawan <mail@diher.solutions>
+*.rss.my.id
+*.diher.solutions
+
+// Discord Inc : https://discord.com
+// Submitted by Sahn Lam <slam@discordapp.com>
+discordsays.com
+discordsez.com
+
+// DNS Africa Ltd https://dns.business
+// Submitted by Calvin Browne <calvin@dns.business>
+jozi.biz
+
+// DNShome : https://www.dnshome.de/
+// Submitted by Norbert Auler <mail@dnshome.de>
+dnshome.de
+
+// DotArai : https://www.dotarai.com/
+// Submitted by Atsadawat Netcharadsang <atsadawat@dotarai.co.th>
+online.th
+shop.th
+
+// DrayTek Corp. : https://www.draytek.com/
+// Submitted by Paul Fang <mis@draytek.com>
+drayddns.com
+
+// DreamCommerce : https://shoper.pl/
+// Submitted by Konrad Kotarba <konrad.kotarba@dreamcommerce.com>
+shoparena.pl
+
+// DreamHost : http://www.dreamhost.com/
+// Submitted by Andrew Farmer <andrew.farmer@dreamhost.com>
+dreamhosters.com
+
+// Drobo : http://www.drobo.com/
+// Submitted by Ricardo Padilha <rpadilha@drobo.com>
+mydrobo.com
+
+// Drud Holdings, LLC. : https://www.drud.com/
+// Submitted by Kevin Bridges <kevin@drud.com>
+drud.io
+drud.us
+
+// DuckDNS : http://www.duckdns.org/
+// Submitted by Richard Harper <richard@duckdns.org>
+duckdns.org
+
+// Bip : https://bip.sh
+// Submitted by Joel Kennedy <joel@bip.sh>
+bip.sh
+
+// bitbridge.net : Submitted by Craig Welch, abeliidev@gmail.com
+bitbridge.net
+
+// dy.fi : http://dy.fi/
+// Submitted by Heikki Hannikainen <hessu@hes.iki.fi>
+dy.fi
+tunk.org
+
+// DynDNS.com : http://www.dyndns.com/services/dns/dyndns/
+dyndns-at-home.com
+dyndns-at-work.com
+dyndns-blog.com
+dyndns-free.com
+dyndns-home.com
+dyndns-ip.com
+dyndns-mail.com
+dyndns-office.com
+dyndns-pics.com
+dyndns-remote.com
+dyndns-server.com
+dyndns-web.com
+dyndns-wiki.com
+dyndns-work.com
+dyndns.biz
+dyndns.info
+dyndns.org
+dyndns.tv
+at-band-camp.net
+ath.cx
+barrel-of-knowledge.info
+barrell-of-knowledge.info
+better-than.tv
+blogdns.com
+blogdns.net
+blogdns.org
+blogsite.org
+boldlygoingnowhere.org
+broke-it.net
+buyshouses.net
+cechire.com
+dnsalias.com
+dnsalias.net
+dnsalias.org
+dnsdojo.com
+dnsdojo.net
+dnsdojo.org
+does-it.net
+doesntexist.com
+doesntexist.org
+dontexist.com
+dontexist.net
+dontexist.org
+doomdns.com
+doomdns.org
+dvrdns.org
+dyn-o-saur.com
+dynalias.com
+dynalias.net
+dynalias.org
+dynathome.net
+dyndns.ws
+endofinternet.net
+endofinternet.org
+endoftheinternet.org
+est-a-la-maison.com
+est-a-la-masion.com
+est-le-patron.com
+est-mon-blogueur.com
+for-better.biz
+for-more.biz
+for-our.info
+for-some.biz
+for-the.biz
+forgot.her.name
+forgot.his.name
+from-ak.com
+from-al.com
+from-ar.com
+from-az.net
+from-ca.com
+from-co.net
+from-ct.com
+from-dc.com
+from-de.com
+from-fl.com
+from-ga.com
+from-hi.com
+from-ia.com
+from-id.com
+from-il.com
+from-in.com
+from-ks.com
+from-ky.com
+from-la.net
+from-ma.com
+from-md.com
+from-me.org
+from-mi.com
+from-mn.com
+from-mo.com
+from-ms.com
+from-mt.com
+from-nc.com
+from-nd.com
+from-ne.com
+from-nh.com
+from-nj.com
+from-nm.com
+from-nv.com
+from-ny.net
+from-oh.com
+from-ok.com
+from-or.com
+from-pa.com
+from-pr.com
+from-ri.com
+from-sc.com
+from-sd.com
+from-tn.com
+from-tx.com
+from-ut.com
+from-va.com
+from-vt.com
+from-wa.com
+from-wi.com
+from-wv.com
+from-wy.com
+ftpaccess.cc
+fuettertdasnetz.de
+game-host.org
+game-server.cc
+getmyip.com
+gets-it.net
+go.dyndns.org
+gotdns.com
+gotdns.org
+groks-the.info
+groks-this.info
+ham-radio-op.net
+here-for-more.info
+hobby-site.com
+hobby-site.org
+home.dyndns.org
+homedns.org
+homeftp.net
+homeftp.org
+homeip.net
+homelinux.com
+homelinux.net
+homelinux.org
+homeunix.com
+homeunix.net
+homeunix.org
+iamallama.com
+in-the-band.net
+is-a-anarchist.com
+is-a-blogger.com
+is-a-bookkeeper.com
+is-a-bruinsfan.org
+is-a-bulls-fan.com
+is-a-candidate.org
+is-a-caterer.com
+is-a-celticsfan.org
+is-a-chef.com
+is-a-chef.net
+is-a-chef.org
+is-a-conservative.com
+is-a-cpa.com
+is-a-cubicle-slave.com
+is-a-democrat.com
+is-a-designer.com
+is-a-doctor.com
+is-a-financialadvisor.com
+is-a-geek.com
+is-a-geek.net
+is-a-geek.org
+is-a-green.com
+is-a-guru.com
+is-a-hard-worker.com
+is-a-hunter.com
+is-a-knight.org
+is-a-landscaper.com
+is-a-lawyer.com
+is-a-liberal.com
+is-a-libertarian.com
+is-a-linux-user.org
+is-a-llama.com
+is-a-musician.com
+is-a-nascarfan.com
+is-a-nurse.com
+is-a-painter.com
+is-a-patsfan.org
+is-a-personaltrainer.com
+is-a-photographer.com
+is-a-player.com
+is-a-republican.com
+is-a-rockstar.com
+is-a-socialist.com
+is-a-soxfan.org
+is-a-student.com
+is-a-teacher.com
+is-a-techie.com
+is-a-therapist.com
+is-an-accountant.com
+is-an-actor.com
+is-an-actress.com
+is-an-anarchist.com
+is-an-artist.com
+is-an-engineer.com
+is-an-entertainer.com
+is-by.us
+is-certified.com
+is-found.org
+is-gone.com
+is-into-anime.com
+is-into-cars.com
+is-into-cartoons.com
+is-into-games.com
+is-leet.com
+is-lost.org
+is-not-certified.com
+is-saved.org
+is-slick.com
+is-uberleet.com
+is-very-bad.org
+is-very-evil.org
+is-very-good.org
+is-very-nice.org
+is-very-sweet.org
+is-with-theband.com
+isa-geek.com
+isa-geek.net
+isa-geek.org
+isa-hockeynut.com
+issmarterthanyou.com
+isteingeek.de
+istmein.de
+kicks-ass.net
+kicks-ass.org
+knowsitall.info
+land-4-sale.us
+lebtimnetz.de
+leitungsen.de
+likes-pie.com
+likescandy.com
+merseine.nu
+mine.nu
+misconfused.org
+mypets.ws
+myphotos.cc
+neat-url.com
+office-on-the.net
+on-the-web.tv
+podzone.net
+podzone.org
+readmyblog.org
+saves-the-whales.com
+scrapper-site.net
+scrapping.cc
+selfip.biz
+selfip.com
+selfip.info
+selfip.net
+selfip.org
+sells-for-less.com
+sells-for-u.com
+sells-it.net
+sellsyourhome.org
+servebbs.com
+servebbs.net
+servebbs.org
+serveftp.net
+serveftp.org
+servegame.org
+shacknet.nu
+simple-url.com
+space-to-rent.com
+stuff-4-sale.org
+stuff-4-sale.us
+teaches-yoga.com
+thruhere.net
+traeumtgerade.de
+webhop.biz
+webhop.info
+webhop.net
+webhop.org
+worse-than.tv
+writesthisblog.com
+
+// ddnss.de : https://www.ddnss.de/
+// Submitted by Robert Niedziela <webmaster@ddnss.de>
+ddnss.de
+dyn.ddnss.de
+dyndns.ddnss.de
+dyndns1.de
+dyn-ip24.de
+home-webserver.de
+dyn.home-webserver.de
+myhome-server.de
+ddnss.org
+
+// Definima : http://www.definima.com/
+// Submitted by Maxence Bitterli <maxence@definima.com>
+definima.net
+definima.io
+
+// DigitalOcean App Platform : https://www.digitalocean.com/products/app-platform/
+// Submitted by Braxton Huggins <psl-maintainers@digitalocean.com>
+ondigitalocean.app
+
+// DigitalOcean Spaces : https://www.digitalocean.com/products/spaces/
+// Submitted by Robin H. Johnson <psl-maintainers@digitalocean.com>
+*.digitaloceanspaces.com
+
+// dnstrace.pro : https://dnstrace.pro/
+// Submitted by Chris Partridge <chris@partridge.tech>
+bci.dnstrace.pro
+
+// Dynu.com : https://www.dynu.com/
+// Submitted by Sue Ye <sue@dynu.com>
+ddnsfree.com
+ddnsgeek.com
+giize.com
+gleeze.com
+kozow.com
+loseyourip.com
+ooguy.com
+theworkpc.com
+casacam.net
+dynu.net
+accesscam.org
+camdvr.org
+freeddns.org
+mywire.org
+webredirect.org
+myddns.rocks
+blogsite.xyz
+
+// dynv6 : https://dynv6.com
+// Submitted by Dominik Menke <dom@digineo.de>
+dynv6.net
+
+// E4YOU spol. s.r.o. : https://e4you.cz/
+// Submitted by Vladimir Dudr <info@e4you.cz>
+e4.cz
+
+// Easypanel : https://easypanel.io
+// Submitted by Andrei Canta <andrei@easypanel.io>
+easypanel.app
+easypanel.host
+
+// Elementor : Elementor Ltd.
+// Submitted by Anton Barkan <antonb@elementor.com>
+elementor.cloud
+elementor.cool
+
+// En root‽ : https://en-root.org
+// Submitted by Emmanuel Raviart <emmanuel@raviart.com>
+en-root.fr
+
+// Enalean SAS: https://www.enalean.com
+// Submitted by Thomas Cottier <thomas.cottier@enalean.com>
+mytuleap.com
+tuleap-partners.com
+
+// Encoretivity AB: https://encore.dev
+// Submitted by André Eriksson <andre@encore.dev>
+encr.app
+encoreapi.com
+
+// ECG Robotics, Inc: https://ecgrobotics.org
+// Submitted by <frc1533@ecgrobotics.org>
+onred.one
+staging.onred.one
+
+// encoway GmbH : https://www.encoway.de
+// Submitted by Marcel Daus <cloudops@encoway.de>
+eu.encoway.cloud
+
+// EU.org https://eu.org/
+// Submitted by Pierre Beyssac <hostmaster@eu.org>
+eu.org
+al.eu.org
+asso.eu.org
+at.eu.org
+au.eu.org
+be.eu.org
+bg.eu.org
+ca.eu.org
+cd.eu.org
+ch.eu.org
+cn.eu.org
+cy.eu.org
+cz.eu.org
+de.eu.org
+dk.eu.org
+edu.eu.org
+ee.eu.org
+es.eu.org
+fi.eu.org
+fr.eu.org
+gr.eu.org
+hr.eu.org
+hu.eu.org
+ie.eu.org
+il.eu.org
+in.eu.org
+int.eu.org
+is.eu.org
+it.eu.org
+jp.eu.org
+kr.eu.org
+lt.eu.org
+lu.eu.org
+lv.eu.org
+mc.eu.org
+me.eu.org
+mk.eu.org
+mt.eu.org
+my.eu.org
+net.eu.org
+ng.eu.org
+nl.eu.org
+no.eu.org
+nz.eu.org
+paris.eu.org
+pl.eu.org
+pt.eu.org
+q-a.eu.org
+ro.eu.org
+ru.eu.org
+se.eu.org
+si.eu.org
+sk.eu.org
+tr.eu.org
+uk.eu.org
+us.eu.org
+
+// Eurobyte : https://eurobyte.ru
+// Submitted by Evgeniy Subbotin <e.subbotin@eurobyte.ru>
+eurodir.ru
+
+// Evennode : http://www.evennode.com/
+// Submitted by Michal Kralik <support@evennode.com>
+eu-1.evennode.com
+eu-2.evennode.com
+eu-3.evennode.com
+eu-4.evennode.com
+us-1.evennode.com
+us-2.evennode.com
+us-3.evennode.com
+us-4.evennode.com
+
+// eDirect Corp. : https://hosting.url.com.tw/
+// Submitted by C.S. chang <cschang@corp.url.com.tw>
+twmail.cc
+twmail.net
+twmail.org
+mymailer.com.tw
+url.tw
+
+// Fabrica Technologies, Inc. : https://www.fabrica.dev/
+// Submitted by Eric Jiang <eric@fabrica.dev>
+onfabrica.com
+
+// Facebook, Inc.
+// Submitted by Peter Ruibal <public-suffix@fb.com>
+apps.fbsbx.com
+
+// FAITID : https://faitid.org/
+// Submitted by Maxim Alzoba <tech.contact@faitid.org>
+// https://www.flexireg.net/stat_info
+ru.net
+adygeya.ru
+bashkiria.ru
+bir.ru
+cbg.ru
+com.ru
+dagestan.ru
+grozny.ru
+kalmykia.ru
+kustanai.ru
+marine.ru
+mordovia.ru
+msk.ru
+mytis.ru
+nalchik.ru
+nov.ru
+pyatigorsk.ru
+spb.ru
+vladikavkaz.ru
+vladimir.ru
+abkhazia.su
+adygeya.su
+aktyubinsk.su
+arkhangelsk.su
+armenia.su
+ashgabad.su
+azerbaijan.su
+balashov.su
+bashkiria.su
+bryansk.su
+bukhara.su
+chimkent.su
+dagestan.su
+east-kazakhstan.su
+exnet.su
+georgia.su
+grozny.su
+ivanovo.su
+jambyl.su
+kalmykia.su
+kaluga.su
+karacol.su
+karaganda.su
+karelia.su
+khakassia.su
+krasnodar.su
+kurgan.su
+kustanai.su
+lenug.su
+mangyshlak.su
+mordovia.su
+msk.su
+murmansk.su
+nalchik.su
+navoi.su
+north-kazakhstan.su
+nov.su
+obninsk.su
+penza.su
+pokrovsk.su
+sochi.su
+spb.su
+tashkent.su
+termez.su
+togliatti.su
+troitsk.su
+tselinograd.su
+tula.su
+tuva.su
+vladikavkaz.su
+vladimir.su
+vologda.su
+
+// Fancy Bits, LLC : http://getchannels.com
+// Submitted by Aman Gupta <aman@getchannels.com>
+channelsdvr.net
+u.channelsdvr.net
+
+// Fastly Inc. : http://www.fastly.com/
+// Submitted by Fastly Security <security@fastly.com>
+edgecompute.app
+fastly-edge.com
+fastly-terrarium.com
+fastlylb.net
+map.fastlylb.net
+freetls.fastly.net
+map.fastly.net
+a.prod.fastly.net
+global.prod.fastly.net
+a.ssl.fastly.net
+b.ssl.fastly.net
+global.ssl.fastly.net
+
+// Fastmail : https://www.fastmail.com/
+// Submitted by Marc Bradshaw <marc@fastmailteam.com>
+*.user.fm
+
+// FASTVPS EESTI OU : https://fastvps.ru/
+// Submitted by Likhachev Vasiliy <lihachev@fastvps.ru>
+fastvps-server.com
+fastvps.host
+myfast.host
+fastvps.site
+myfast.space
+
+// Fedora : https://fedoraproject.org/
+// submitted by Patrick Uiterwijk <puiterwijk@fedoraproject.org>
+fedorainfracloud.org
+fedorapeople.org
+cloud.fedoraproject.org
+app.os.fedoraproject.org
+app.os.stg.fedoraproject.org
+
+// FearWorks Media Ltd. : https://fearworksmedia.co.uk
+// submitted by Keith Fairley <domains@fearworksmedia.co.uk>
+conn.uk
+copro.uk
+hosp.uk
+
+// Fermax : https://fermax.com/
+// submitted by Koen Van Isterdael <k.vanisterdael@fermax.be>
+mydobiss.com
+
+// FH Muenster : https://www.fh-muenster.de
+// Submitted by Robin Naundorf <r.naundorf@fh-muenster.de>
+fh-muenster.io
+
+// Filegear Inc. : https://www.filegear.com
+// Submitted by Jason Zhu <jason@owtware.com>
+filegear.me
+filegear-au.me
+filegear-de.me
+filegear-gb.me
+filegear-ie.me
+filegear-jp.me
+filegear-sg.me
+
+// Firebase, Inc.
+// Submitted by Chris Raynor <chris@firebase.com>
+firebaseapp.com
+
+// Firewebkit : https://www.firewebkit.com
+// Submitted by Majid Qureshi <mqureshi@amrayn.com>
+fireweb.app
+
+// FLAP : https://www.flap.cloud
+// Submitted by Louis Chemineau <louis@chmn.me>
+flap.id
+
+// FlashDrive : https://flashdrive.io
+// Submitted by Eric Chan <support@flashdrive.io>
+onflashdrive.app
+fldrv.com
+
+// fly.io: https://fly.io
+// Submitted by Kurt Mackey <kurt@fly.io>
+fly.dev
+edgeapp.net
+shw.io
+
+// Flynn : https://flynn.io
+// Submitted by Jonathan Rudenberg <jonathan@flynn.io>
+flynnhosting.net
+
+// Forgerock : https://www.forgerock.com
+// Submitted by Roderick Parr <roderick.parr@forgerock.com>
+forgeblocks.com
+id.forgerock.io
+
+// Framer : https://www.framer.com
+// Submitted by Koen Rouwhorst <koenrh@framer.com>
+framer.app
+framercanvas.com
+framer.media
+framer.photos
+framer.website
+framer.wiki
+
+// Frusky MEDIA&PR : https://www.frusky.de
+// Submitted by Victor Pupynin <hallo@frusky.de>
+*.frusky.de
+
+// RavPage : https://www.ravpage.co.il
+// Submitted by Roni Horowitz <roni@responder.co.il>
+ravpage.co.il
+
+// Frederik Braun https://frederik-braun.com
+// Submitted by Frederik Braun <fb@frederik-braun.com>
+0e.vc
+
+// Freebox : http://www.freebox.fr
+// Submitted by Romain Fliedel <rfliedel@freebox.fr>
+freebox-os.com
+freeboxos.com
+fbx-os.fr
+fbxos.fr
+freebox-os.fr
+freeboxos.fr
+
+// freedesktop.org : https://www.freedesktop.org
+// Submitted by Daniel Stone <daniel@fooishbar.org>
+freedesktop.org
+
+// freemyip.com : https://freemyip.com
+// Submitted by Cadence <contact@freemyip.com>
+freemyip.com
+
+// FunkFeuer - Verein zur Förderung freier Netze : https://www.funkfeuer.at
+// Submitted by Daniel A. Maierhofer <vorstand@funkfeuer.at>
+wien.funkfeuer.at
+
+// Futureweb OG : http://www.futureweb.at
+// Submitted by Andreas Schnederle-Wagner <schnederle@futureweb.at>
+*.futurecms.at
+*.ex.futurecms.at
+*.in.futurecms.at
+futurehosting.at
+futuremailing.at
+*.ex.ortsinfo.at
+*.kunden.ortsinfo.at
+*.statics.cloud
+
+// GDS : https://www.gov.uk/service-manual/technology/managing-domain-names
+// Submitted by Stephen Ford <hostmaster@digital.cabinet-office.gov.uk>
+independent-commission.uk
+independent-inquest.uk
+independent-inquiry.uk
+independent-panel.uk
+independent-review.uk
+public-inquiry.uk
+royal-commission.uk
+campaign.gov.uk
+service.gov.uk
+
+// CDDO : https://www.gov.uk/guidance/get-an-api-domain-on-govuk
+// Submitted by Jamie Tanna <jamie.tanna@digital.cabinet-office.gov.uk>
+api.gov.uk
+
+// Gehirn Inc. : https://www.gehirn.co.jp/
+// Submitted by Kohei YOSHIDA <tech@gehirn.co.jp>
+gehirn.ne.jp
+usercontent.jp
+
+// Gentlent, Inc. : https://www.gentlent.com
+// Submitted by Tom Klein <tom@gentlent.com>
+gentapps.com
+gentlentapis.com
+lab.ms
+cdn-edges.net
+
+// Ghost Foundation : https://ghost.org
+// Submitted by Matt Hanley <security@ghost.org>
+ghost.io
+
+// GignoSystemJapan: http://gsj.bz
+// Submitted by GignoSystemJapan <kakutou-ec@gsj.bz>
+gsj.bz
+
+// GitHub, Inc.
+// Submitted by Patrick Toomey <security@github.com>
+githubusercontent.com
+githubpreview.dev
+github.io
+
+// GitLab, Inc.
+// Submitted by Alex Hanselka <alex@gitlab.com>
+gitlab.io
+
+// Gitplac.si - https://gitplac.si
+// Submitted by Aljaž Starc <me@aljaxus.eu>
+gitapp.si
+gitpage.si
+
+// Glitch, Inc : https://glitch.com
+// Submitted by Mads Hartmann <mads@glitch.com>
+glitch.me
+
+// Global NOG Alliance : https://nogalliance.org/
+// Submitted by Sander Steffann <sander@nogalliance.org>
+nog.community
+
+// Globe Hosting SRL : https://www.globehosting.com/
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+co.ro
+shop.ro
+
+// GMO Pepabo, Inc. : https://pepabo.com/
+// Submitted by Hosting Div <admin@pepabo.com>
+lolipop.io
+angry.jp
+babyblue.jp
+babymilk.jp
+backdrop.jp
+bambina.jp
+bitter.jp
+blush.jp
+boo.jp
+boy.jp
+boyfriend.jp
+but.jp
+candypop.jp
+capoo.jp
+catfood.jp
+cheap.jp
+chicappa.jp
+chillout.jp
+chips.jp
+chowder.jp
+chu.jp
+ciao.jp
+cocotte.jp
+coolblog.jp
+cranky.jp
+cutegirl.jp
+daa.jp
+deca.jp
+deci.jp
+digick.jp
+egoism.jp
+fakefur.jp
+fem.jp
+flier.jp
+floppy.jp
+fool.jp
+frenchkiss.jp
+girlfriend.jp
+girly.jp
+gloomy.jp
+gonna.jp
+greater.jp
+hacca.jp
+heavy.jp
+her.jp
+hiho.jp
+hippy.jp
+holy.jp
+hungry.jp
+icurus.jp
+itigo.jp
+jellybean.jp
+kikirara.jp
+kill.jp
+kilo.jp
+kuron.jp
+littlestar.jp
+lolipopmc.jp
+lolitapunk.jp
+lomo.jp
+lovepop.jp
+lovesick.jp
+main.jp
+mods.jp
+mond.jp
+mongolian.jp
+moo.jp
+namaste.jp
+nikita.jp
+nobushi.jp
+noor.jp
+oops.jp
+parallel.jp
+parasite.jp
+pecori.jp
+peewee.jp
+penne.jp
+pepper.jp
+perma.jp
+pigboat.jp
+pinoko.jp
+punyu.jp
+pupu.jp
+pussycat.jp
+pya.jp
+raindrop.jp
+readymade.jp
+sadist.jp
+schoolbus.jp
+secret.jp
+staba.jp
+stripper.jp
+sub.jp
+sunnyday.jp
+thick.jp
+tonkotsu.jp
+under.jp
+upper.jp
+velvet.jp
+verse.jp
+versus.jp
+vivian.jp
+watson.jp
+weblike.jp
+whitesnow.jp
+zombie.jp
+heteml.net
+
+// GOV.UK Platform as a Service : https://www.cloud.service.gov.uk/
+// Submitted by Tom Whitwell <gov-uk-paas-support@digital.cabinet-office.gov.uk>
+cloudapps.digital
+london.cloudapps.digital
+
+// GOV.UK Pay : https://www.payments.service.gov.uk/
+// Submitted by Richard Baker <richard.baker@digital.cabinet-office.gov.uk>
+pymnt.uk
+
+// UKHomeOffice : https://www.gov.uk/government/organisations/home-office
+// Submitted by Jon Shanks <jon.shanks@digital.homeoffice.gov.uk>
+homeoffice.gov.uk
+
+// GlobeHosting, Inc.
+// Submitted by Zoltan Egresi <egresi@globehosting.com>
+ro.im
+
+// GoIP DNS Services : http://www.goip.de
+// Submitted by Christian Poulter <milchstrasse@goip.de>
+goip.de
+
+// Google, Inc.
+// Submitted by Eduardo Vela <evn@google.com>
+run.app
+a.run.app
+web.app
+*.0emm.com
+appspot.com
+*.r.appspot.com
+codespot.com
+googleapis.com
+googlecode.com
+pagespeedmobilizer.com
+publishproxy.com
+withgoogle.com
+withyoutube.com
+*.gateway.dev
+cloud.goog
+translate.goog
+*.usercontent.goog
+cloudfunctions.net
+blogspot.ae
+blogspot.al
+blogspot.am
+blogspot.ba
+blogspot.be
+blogspot.bg
+blogspot.bj
+blogspot.ca
+blogspot.cf
+blogspot.ch
+blogspot.cl
+blogspot.co.at
+blogspot.co.id
+blogspot.co.il
+blogspot.co.ke
+blogspot.co.nz
+blogspot.co.uk
+blogspot.co.za
+blogspot.com
+blogspot.com.ar
+blogspot.com.au
+blogspot.com.br
+blogspot.com.by
+blogspot.com.co
+blogspot.com.cy
+blogspot.com.ee
+blogspot.com.eg
+blogspot.com.es
+blogspot.com.mt
+blogspot.com.ng
+blogspot.com.tr
+blogspot.com.uy
+blogspot.cv
+blogspot.cz
+blogspot.de
+blogspot.dk
+blogspot.fi
+blogspot.fr
+blogspot.gr
+blogspot.hk
+blogspot.hr
+blogspot.hu
+blogspot.ie
+blogspot.in
+blogspot.is
+blogspot.it
+blogspot.jp
+blogspot.kr
+blogspot.li
+blogspot.lt
+blogspot.lu
+blogspot.md
+blogspot.mk
+blogspot.mr
+blogspot.mx
+blogspot.my
+blogspot.nl
+blogspot.no
+blogspot.pe
+blogspot.pt
+blogspot.qa
+blogspot.re
+blogspot.ro
+blogspot.rs
+blogspot.ru
+blogspot.se
+blogspot.sg
+blogspot.si
+blogspot.sk
+blogspot.sn
+blogspot.td
+blogspot.tw
+blogspot.ug
+blogspot.vn
+
+// Goupile : https://goupile.fr
+// Submitted by Niels Martignene <hello@goupile.fr>
+goupile.fr
+
+// Government of the Netherlands: https://www.government.nl
+// Submitted by <domeinnaam@minaz.nl>
+gov.nl
+
+// Group 53, LLC : https://www.group53.com
+// Submitted by Tyler Todd <noc@nova53.net>
+awsmppl.com
+
+// GünstigBestellen : https://günstigbestellen.de
+// Submitted by Furkan Akkoc <info@hendelzon.de>
+günstigbestellen.de
+günstigliefern.de
+
+// Hakaran group: http://hakaran.cz
+// Submitted by Arseniy Sokolov <security@hakaran.cz>
+fin.ci
+free.hr
+caa.li
+ua.rs
+conf.se
+
+// Handshake : https://handshake.org
+// Submitted by Mike Damm <md@md.vc>
+hs.zone
+hs.run
+
+// Hashbang : https://hashbang.sh
+hashbang.sh
+
+// Hasura : https://hasura.io
+// Submitted by Shahidh K Muhammed <shahidh@hasura.io>
+hasura.app
+hasura-app.io
+
+// Heilbronn University of Applied Sciences - Faculty Informatics (GitLab Pages): https://www.hs-heilbronn.de
+// Submitted by Richard Zowalla <mi-admin@hs-heilbronn.de>
+pages.it.hs-heilbronn.de
+
+// Hepforge : https://www.hepforge.org
+// Submitted by David Grellscheid <admin@hepforge.org>
+hepforge.org
+
+// Heroku : https://www.heroku.com/
+// Submitted by Tom Maher <tmaher@heroku.com>
+herokuapp.com
+herokussl.com
+
+// Hibernating Rhinos
+// Submitted by Oren Eini <oren@ravendb.net>
+ravendb.cloud
+ravendb.community
+ravendb.me
+development.run
+ravendb.run
+
+// home.pl S.A.: https://home.pl
+// Submitted by Krzysztof Wolski <krzysztof.wolski@home.eu>
+homesklep.pl
+
+// Hong Kong Productivity Council: https://www.hkpc.org/
+// Submitted by SECaaS Team <summchan@hkpc.org>
+secaas.hk
+
+// Hoplix : https://www.hoplix.com
+// Submitted by Danilo De Franco<info@hoplix.shop>
+hoplix.shop
+
+
+// HOSTBIP REGISTRY : https://www.hostbip.com/
+// Submitted by Atanunu Igbunuroghene <publicsuffixlist@hostbip.com>
+orx.biz
+biz.gl
+col.ng
+firm.ng
+gen.ng
+ltd.ng
+ngo.ng
+edu.scot
+sch.so
+
+// HostFly : https://www.ie.ua
+// Submitted by Bohdan Dub <support@hostfly.com.ua>
+ie.ua
+
+// HostyHosting (hostyhosting.com)
+hostyhosting.io
+
+// Häkkinen.fi
+// Submitted by Eero Häkkinen <Eero+psl@Häkkinen.fi>
+häkkinen.fi
+
+// Ici la Lune : http://www.icilalune.com/
+// Submitted by Simon Morvan <simon@icilalune.com>
+*.moonscale.io
+moonscale.net
+
+// iki.fi
+// Submitted by Hannu Aronsson <haa@iki.fi>
+iki.fi
+
+// iliad italia: https://www.iliad.it
+// Submitted by Marios Makassikis <mmakassikis@freebox.fr>
+ibxos.it
+iliadboxos.it
+
+// Impertrix Solutions : <https://impertrixcdn.com>
+// Submitted by Zhixiang Zhao <csuite@impertrix.com>
+impertrixcdn.com
+impertrix.com
+
+// Incsub, LLC: https://incsub.com/
+// Submitted by Aaron Edwards <sysadmins@incsub.com>
+smushcdn.com
+wphostedmail.com
+wpmucdn.com
+tempurl.host
+wpmudev.host
+
+// Individual Network Berlin e.V. : https://www.in-berlin.de/
+// Submitted by Christian Seitz <chris@in-berlin.de>
+dyn-berlin.de
+in-berlin.de
+in-brb.de
+in-butter.de
+in-dsl.de
+in-dsl.net
+in-dsl.org
+in-vpn.de
+in-vpn.net
+in-vpn.org
+
+// info.at : http://www.info.at/
+biz.at
+info.at
+
+// info.cx : http://info.cx
+// Submitted by Jacob Slater <whois@igloo.to>
+info.cx
+
+// Interlegis : http://www.interlegis.leg.br
+// Submitted by Gabriel Ferreira <registrobr@interlegis.leg.br>
+ac.leg.br
+al.leg.br
+am.leg.br
+ap.leg.br
+ba.leg.br
+ce.leg.br
+df.leg.br
+es.leg.br
+go.leg.br
+ma.leg.br
+mg.leg.br
+ms.leg.br
+mt.leg.br
+pa.leg.br
+pb.leg.br
+pe.leg.br
+pi.leg.br
+pr.leg.br
+rj.leg.br
+rn.leg.br
+ro.leg.br
+rr.leg.br
+rs.leg.br
+sc.leg.br
+se.leg.br
+sp.leg.br
+to.leg.br
+
+// intermetrics GmbH : https://pixolino.com/
+// Submitted by Wolfgang Schwarz <admin@intermetrics.de>
+pixolino.com
+
+// Internet-Pro, LLP: https://netangels.ru/
+// Submitted by Vasiliy Sheredeko <piphon@gmail.com>
+na4u.ru
+
+// iopsys software solutions AB : https://iopsys.eu/
+// Submitted by Roman Azarenko <roman.azarenko@iopsys.eu>
+iopsys.se
+
+// IPiFony Systems, Inc. : https://www.ipifony.com/
+// Submitted by Matthew Hardeman <mhardeman@ipifony.com>
+ipifony.net
+
+// IServ GmbH : https://iserv.de
+// Submitted by Mario Hoberg <info@iserv.de>
+iservschule.de
+mein-iserv.de
+schulplattform.de
+schulserver.de
+test-iserv.de
+iserv.dev
+
+// I-O DATA DEVICE, INC. : http://www.iodata.com/
+// Submitted by Yuji Minagawa <domains-admin@iodata.jp>
+iobb.net
+
+// Jelastic, Inc. : https://jelastic.com/
+// Submitted by Ihor Kolodyuk <ik@jelastic.com>
+mel.cloudlets.com.au
+cloud.interhostsolutions.be
+users.scale.virtualcloud.com.br
+mycloud.by
+alp1.ae.flow.ch
+appengine.flow.ch
+es-1.axarnet.cloud
+diadem.cloud
+vip.jelastic.cloud
+jele.cloud
+it1.eur.aruba.jenv-aruba.cloud
+it1.jenv-aruba.cloud
+keliweb.cloud
+cs.keliweb.cloud
+oxa.cloud
+tn.oxa.cloud
+uk.oxa.cloud
+primetel.cloud
+uk.primetel.cloud
+ca.reclaim.cloud
+uk.reclaim.cloud
+us.reclaim.cloud
+ch.trendhosting.cloud
+de.trendhosting.cloud
+jele.club
+amscompute.com
+clicketcloud.com
+dopaas.com
+hidora.com
+paas.hosted-by-previder.com
+rag-cloud.hosteur.com
+rag-cloud-ch.hosteur.com
+jcloud.ik-server.com
+jcloud-ver-jpc.ik-server.com
+demo.jelastic.com
+kilatiron.com
+paas.massivegrid.com
+jed.wafaicloud.com
+lon.wafaicloud.com
+ryd.wafaicloud.com
+j.scaleforce.com.cy
+jelastic.dogado.eu
+fi.cloudplatform.fi
+demo.datacenter.fi
+paas.datacenter.fi
+jele.host
+mircloud.host
+paas.beebyte.io
+sekd1.beebyteapp.io
+jele.io
+cloud-fr1.unispace.io
+jc.neen.it
+cloud.jelastic.open.tim.it
+jcloud.kz
+upaas.kazteleport.kz
+cloudjiffy.net
+fra1-de.cloudjiffy.net
+west1-us.cloudjiffy.net
+jls-sto1.elastx.net
+jls-sto2.elastx.net
+jls-sto3.elastx.net
+faststacks.net
+fr-1.paas.massivegrid.net
+lon-1.paas.massivegrid.net
+lon-2.paas.massivegrid.net
+ny-1.paas.massivegrid.net
+ny-2.paas.massivegrid.net
+sg-1.paas.massivegrid.net
+jelastic.saveincloud.net
+nordeste-idc.saveincloud.net
+j.scaleforce.net
+jelastic.tsukaeru.net
+sdscloud.pl
+unicloud.pl
+mircloud.ru
+jelastic.regruhosting.ru
+enscaled.sg
+jele.site
+jelastic.team
+orangecloud.tn
+j.layershift.co.uk
+phx.enscaled.us
+mircloud.us
+
+// Jino : https://www.jino.ru
+// Submitted by Sergey Ulyashin <ulyashin@jino.ru>
+myjino.ru
+*.hosting.myjino.ru
+*.landing.myjino.ru
+*.spectrum.myjino.ru
+*.vps.myjino.ru
+
+// Jotelulu S.L. : https://jotelulu.com
+// Submitted by Daniel Fariña <ingenieria@jotelulu.com>
+jotelulu.cloud
+
+// Joyent : https://www.joyent.com/
+// Submitted by Brian Bennett <brian.bennett@joyent.com>
+*.triton.zone
+*.cns.joyent.com
+
+// JS.ORG : http://dns.js.org
+// Submitted by Stefan Keim <admin@js.org>
+js.org
+
+// KaasHosting : http://www.kaashosting.nl/
+// Submitted by Wouter Bakker <hostmaster@kaashosting.nl>
+kaas.gg
+khplay.nl
+
+// Kakao : https://www.kakaocorp.com/
+// Submitted by JaeYoong Lee <cec@kakaocorp.com>
+ktistory.com
+
+// Kapsi : https://kapsi.fi
+// Submitted by Tomi Juntunen <erani@kapsi.fi>
+kapsi.fi
+
+// Keyweb AG : https://www.keyweb.de
+// Submitted by Martin Dannehl <postmaster@keymachine.de>
+keymachine.de
+
+// KingHost : https://king.host
+// Submitted by Felipe Keller Braz <felipebraz@kinghost.com.br>
+kinghost.net
+uni5.net
+
+// KnightPoint Systems, LLC : http://www.knightpoint.com/
+// Submitted by Roy Keene <rkeene@knightpoint.com>
+knightpoint.systems
+
+// KoobinEvent, SL: https://www.koobin.com
+// Submitted by Iván Oliva <ivan.oliva@koobin.com>
+koobin.events
+
+// KUROKU LTD : https://kuroku.ltd/
+// Submitted by DisposaBoy <security@oya.to>
+oya.to
+
+// Katholieke Universiteit Leuven: https://www.kuleuven.be
+// Submitted by Abuse KU Leuven <abuse@kuleuven.be>
+kuleuven.cloud
+ezproxy.kuleuven.be
+
+// .KRD : http://nic.krd/data/krd/Registration%20Policy.pdf
+co.krd
+edu.krd
+
+// Krellian Ltd. : https://krellian.com
+// Submitted by Ben Francis <ben@krellian.com>
+krellian.net
+webthings.io
+
+// LCube - Professional hosting e.K. : https://www.lcube-webhosting.de
+// Submitted by Lars Laehn <info@lcube.de>
+git-repos.de
+lcube-server.de
+svn-repos.de
+
+// Leadpages : https://www.leadpages.net
+// Submitted by Greg Dallavalle <domains@leadpages.net>
+leadpages.co
+lpages.co
+lpusercontent.com
+
+// Lelux.fi : https://lelux.fi/
+// Submitted by Lelux Admin <publisuffix@lelux.site>
+lelux.site
+
+// Lifetime Hosting : https://Lifetime.Hosting/
+// Submitted by Mike Fillator <support@lifetime.hosting>
+co.business
+co.education
+co.events
+co.financial
+co.network
+co.place
+co.technology
+
+// Lightmaker Property Manager, Inc. : https://app.lmpm.com/
+// Submitted by Greg Holland <greg.holland@lmpm.com>
+app.lmpm.com
+
+// linkyard ldt: https://www.linkyard.ch/
+// Submitted by Mario Siegenthaler <mario.siegenthaler@linkyard.ch>
+linkyard.cloud
+linkyard-cloud.ch
+
+// Linode : https://linode.com
+// Submitted by <security@linode.com>
+members.linode.com
+*.nodebalancer.linode.com
+*.linodeobjects.com
+ip.linodeusercontent.com
+
+// LiquidNet Ltd : http://www.liquidnetlimited.com/
+// Submitted by Victor Velchev <admin@liquidnetlimited.com>
+we.bs
+
+// Localcert : https://localcert.dev
+// Submitted by Lann Martin <security@localcert.dev>
+*.user.localcert.dev
+
+// localzone.xyz
+// Submitted by Kenny Niehage <hello@yahe.sh>
+localzone.xyz
+
+// Log'in Line : https://www.loginline.com/
+// Submitted by Rémi Mach <remi.mach@loginline.com>
+loginline.app
+loginline.dev
+loginline.io
+loginline.services
+loginline.site
+
+// Lokalized : https://lokalized.nl
+// Submitted by Noah Taheij <noah@lokalized.nl>
+servers.run
+
+// Lõhmus Family, The
+// Submitted by Heiki Lõhmus <hostmaster at lohmus dot me>
+lohmus.me
+
+// LubMAN UMCS Sp. z o.o : https://lubman.pl/
+// Submitted by Ireneusz Maliszewski <ireneusz.maliszewski@lubman.pl>
+krasnik.pl
+leczna.pl
+lubartow.pl
+lublin.pl
+poniatowa.pl
+swidnik.pl
+
+// Lug.org.uk : https://lug.org.uk
+// Submitted by Jon Spriggs <admin@lug.org.uk>
+glug.org.uk
+lug.org.uk
+lugs.org.uk
+
+// Lukanet Ltd : https://lukanet.com
+// Submitted by Anton Avramov <register@lukanet.com>
+barsy.bg
+barsy.co.uk
+barsyonline.co.uk
+barsycenter.com
+barsyonline.com
+barsy.club
+barsy.de
+barsy.eu
+barsy.in
+barsy.info
+barsy.io
+barsy.me
+barsy.menu
+barsy.mobi
+barsy.net
+barsy.online
+barsy.org
+barsy.pro
+barsy.pub
+barsy.ro
+barsy.shop
+barsy.site
+barsy.support
+barsy.uk
+
+// Magento Commerce
+// Submitted by Damien Tournoud <dtournoud@magento.cloud>
+*.magentosite.cloud
+
+// May First - People Link : https://mayfirst.org/
+// Submitted by Jamie McClelland <info@mayfirst.org>
+mayfirst.info
+mayfirst.org
+
+// Mail.Ru Group : https://hb.cldmail.ru
+// Submitted by Ilya Zaretskiy <zaretskiy@corp.mail.ru>
+hb.cldmail.ru
+
+// Mail Transfer Platform : https://www.neupeer.com
+// Submitted by Li Hui <lihui@neupeer.com>
+cn.vu
+
+// Maze Play: https://www.mazeplay.com
+// Submitted by Adam Humpherys <adam@mws.dev>
+mazeplay.com
+
+// mcpe.me : https://mcpe.me
+// Submitted by Noa Heyl <hi@noa.dev>
+mcpe.me
+
+// McHost : https://mchost.ru
+// Submitted by Evgeniy Subbotin <e.subbotin@mchost.ru>
+mcdir.me
+mcdir.ru
+mcpre.ru
+vps.mcdir.ru
+
+// Mediatech : https://mediatech.by
+// Submitted by Evgeniy Kozhuhovskiy <ugenk@mediatech.by>
+mediatech.by
+mediatech.dev
+
+// Medicom Health : https://medicomhealth.com
+// Submitted by Michael Olson <molson@medicomhealth.com>
+hra.health
+
+// Memset hosting : https://www.memset.com
+// Submitted by Tom Whitwell <domains@memset.com>
+miniserver.com
+memset.net
+
+// Messerli Informatik AG : https://www.messerli.ch/
+// Submitted by Ruben Schmidmeister <psl-maintainers@messerli.ch>
+messerli.app
+
+// MetaCentrum, CESNET z.s.p.o. : https://www.metacentrum.cz/en/
+// Submitted by Zdeněk Šustr <zdenek.sustr@cesnet.cz>
+*.cloud.metacentrum.cz
+custom.metacentrum.cz
+
+// MetaCentrum, CESNET z.s.p.o. : https://www.metacentrum.cz/en/
+// Submitted by Radim Janča <janca@cesnet.cz>
+flt.cloud.muni.cz
+usr.cloud.muni.cz
+
+// Meteor Development Group : https://www.meteor.com/hosting
+// Submitted by Pierre Carrier <pierre@meteor.com>
+meteorapp.com
+eu.meteorapp.com
+
+// Michau Enterprises Limited : http://www.co.pl/
+co.pl
+
+// Microsoft Corporation : http://microsoft.com
+// Submitted by Public Suffix List Admin <msftpsladmin@microsoft.com>
+*.azurecontainer.io
+azurewebsites.net
+azure-mobile.net
+cloudapp.net
+azurestaticapps.net
+1.azurestaticapps.net
+2.azurestaticapps.net
+3.azurestaticapps.net
+centralus.azurestaticapps.net
+eastasia.azurestaticapps.net
+eastus2.azurestaticapps.net
+westeurope.azurestaticapps.net
+westus2.azurestaticapps.net
+
+// minion.systems : http://minion.systems
+// Submitted by Robert Böttinger <r@minion.systems>
+csx.cc
+
+// Mintere : https://mintere.com/
+// Submitted by Ben Aubin <security@mintere.com>
+mintere.site
+
+// MobileEducation, LLC : https://joinforte.com
+// Submitted by Grayson Martin <grayson.martin@mobileeducation.us>
+forte.id
+
+// Mozilla Corporation : https://mozilla.com
+// Submitted by Ben Francis <bfrancis@mozilla.com>
+mozilla-iot.org
+
+// Mozilla Foundation : https://mozilla.org/
+// Submitted by glob <glob@mozilla.com>
+bmoattachments.org
+
+// MSK-IX : https://www.msk-ix.ru/
+// Submitted by Khannanov Roman <r.khannanov@msk-ix.ru>
+net.ru
+org.ru
+pp.ru
+
+// Mythic Beasts : https://www.mythic-beasts.com
+// Submitted by Paul Cammish <kelduum@mythic-beasts.com>
+hostedpi.com
+customer.mythic-beasts.com
+caracal.mythic-beasts.com
+fentiger.mythic-beasts.com
+lynx.mythic-beasts.com
+ocelot.mythic-beasts.com
+oncilla.mythic-beasts.com
+onza.mythic-beasts.com
+sphinx.mythic-beasts.com
+vs.mythic-beasts.com
+x.mythic-beasts.com
+yali.mythic-beasts.com
+cust.retrosnub.co.uk
+
+// Nabu Casa : https://www.nabucasa.com
+// Submitted by Paulus Schoutsen <infra@nabucasa.com>
+ui.nabu.casa
+
+// Net at Work Gmbh : https://www.netatwork.de
+// Submitted by Jan Jaeschke <jan.jaeschke@netatwork.de>
+cloud.nospamproxy.com
+
+// Netlify : https://www.netlify.com
+// Submitted by Jessica Parsons <jessica@netlify.com>
+netlify.app
+
+// Neustar Inc.
+// Submitted by Trung Tran <Trung.Tran@neustar.biz>
+4u.com
+
+// ngrok : https://ngrok.com/
+// Submitted by Alan Shreve <alan@ngrok.com>
+ngrok.app
+ngrok-free.app
+ngrok.dev
+ngrok-free.dev
+ngrok.io
+ap.ngrok.io
+au.ngrok.io
+eu.ngrok.io
+in.ngrok.io
+jp.ngrok.io
+sa.ngrok.io
+us.ngrok.io
+ngrok.pizza
+
+// Nimbus Hosting Ltd. : https://www.nimbushosting.co.uk/
+// Submitted by Nicholas Ford <nick@nimbushosting.co.uk>
+nh-serv.co.uk
+
+// NFSN, Inc. : https://www.NearlyFreeSpeech.NET/
+// Submitted by Jeff Wheelhouse <support@nearlyfreespeech.net>
+nfshost.com
+
+// Noop : https://noop.app
+// Submitted by Nathaniel Schweinberg <noop@rearc.io>
+*.developer.app
+noop.app
+
+// Northflank Ltd. : https://northflank.com/
+// Submitted by Marco Suter <marco@northflank.com>
+*.northflank.app
+*.build.run
+*.code.run
+*.database.run
+*.migration.run
+
+// Noticeable : https://noticeable.io
+// Submitted by Laurent Pellegrino <security@noticeable.io>
+noticeable.news
+
+// Now-DNS : https://now-dns.com
+// Submitted by Steve Russell <steve@now-dns.com>
+dnsking.ch
+mypi.co
+n4t.co
+001www.com
+ddnslive.com
+myiphost.com
+forumz.info
+16-b.it
+32-b.it
+64-b.it
+soundcast.me
+tcp4.me
+dnsup.net
+hicam.net
+now-dns.net
+ownip.net
+vpndns.net
+dynserv.org
+now-dns.org
+x443.pw
+now-dns.top
+ntdll.top
+freeddns.us
+crafting.xyz
+zapto.xyz
+
+// nsupdate.info : https://www.nsupdate.info/
+// Submitted by Thomas Waldmann <info@nsupdate.info>
+nsupdate.info
+nerdpol.ovh
+
+// No-IP.com : https://noip.com/
+// Submitted by Deven Reza <publicsuffixlist@noip.com>
+blogsyte.com
+brasilia.me
+cable-modem.org
+ciscofreak.com
+collegefan.org
+couchpotatofries.org
+damnserver.com
+ddns.me
+ditchyourip.com
+dnsfor.me
+dnsiskinky.com
+dvrcam.info
+dynns.com
+eating-organic.net
+fantasyleague.cc
+geekgalaxy.com
+golffan.us
+health-carereform.com
+homesecuritymac.com
+homesecuritypc.com
+hopto.me
+ilovecollege.info
+loginto.me
+mlbfan.org
+mmafan.biz
+myactivedirectory.com
+mydissent.net
+myeffect.net
+mymediapc.net
+mypsx.net
+mysecuritycamera.com
+mysecuritycamera.net
+mysecuritycamera.org
+net-freaks.com
+nflfan.org
+nhlfan.net
+no-ip.ca
+no-ip.co.uk
+no-ip.net
+noip.us
+onthewifi.com
+pgafan.net
+point2this.com
+pointto.us
+privatizehealthinsurance.net
+quicksytes.com
+read-books.org
+securitytactics.com
+serveexchange.com
+servehumour.com
+servep2p.com
+servesarcasm.com
+stufftoread.com
+ufcfan.org
+unusualperson.com
+workisboring.com
+3utilities.com
+bounceme.net
+ddns.net
+ddnsking.com
+gotdns.ch
+hopto.org
+myftp.biz
+myftp.org
+myvnc.com
+no-ip.biz
+no-ip.info
+no-ip.org
+noip.me
+redirectme.net
+servebeer.com
+serveblog.net
+servecounterstrike.com
+serveftp.com
+servegame.com
+servehalflife.com
+servehttp.com
+serveirc.com
+serveminecraft.net
+servemp3.com
+servepics.com
+servequake.com
+sytes.net
+webhop.me
+zapto.org
+
+// NodeArt : https://nodeart.io
+// Submitted by Konstantin Nosov <Nosov@nodeart.io>
+stage.nodeart.io
+
+// Nucleos Inc. : https://nucleos.com
+// Submitted by Piotr Zduniak <piotr@nucleos.com>
+pcloud.host
+
+// NYC.mn : http://www.information.nyc.mn
+// Submitted by Matthew Brown <mattbrown@nyc.mn>
+nyc.mn
+
+// Observable, Inc. : https://observablehq.com
+// Submitted by Mike Bostock <dns@observablehq.com>
+static.observableusercontent.com
+
+// Octopodal Solutions, LLC. : https://ulterius.io/
+// Submitted by Andrew Sampson <andrew@ulterius.io>
+cya.gg
+
+// OMG.LOL : <https://omg.lol>
+// Submitted by Adam Newbold <adam@omg.lol>
+omg.lol
+
+// Omnibond Systems, LLC. : https://www.omnibond.com
+// Submitted by Cole Estep <cole@omnibond.com>
+cloudycluster.net
+
+// OmniWe Limited: https://omniwe.com
+// Submitted by Vicary Archangel <vicary@omniwe.com>
+omniwe.site
+
+// One.com: https://www.one.com/
+// Submitted by Jacob Bunk Nielsen <jbn@one.com>
+123hjemmeside.dk
+123hjemmeside.no
+123homepage.it
+123kotisivu.fi
+123minsida.se
+123miweb.es
+123paginaweb.pt
+123sait.ru
+123siteweb.fr
+123webseite.at
+123webseite.de
+123website.be
+123website.ch
+123website.lu
+123website.nl
+service.one
+simplesite.com
+simplesite.com.br
+simplesite.gr
+simplesite.pl
+
+// One Fold Media : http://www.onefoldmedia.com/
+// Submitted by Eddie Jones <eddie@onefoldmedia.com>
+nid.io
+
+// Open Social : https://www.getopensocial.com/
+// Submitted by Alexander Varwijk <security@getopensocial.com>
+opensocial.site
+
+// OpenCraft GmbH : http://opencraft.com/
+// Submitted by Sven Marnach <sven@opencraft.com>
+opencraft.hosting
+
+// OpenResearch GmbH: https://openresearch.com/
+// Submitted by Philipp Schmid <ops@openresearch.com>
+orsites.com
+
+// Opera Software, A.S.A.
+// Submitted by Yngve Pettersen <yngve@opera.com>
+operaunite.com
+
+// Orange : https://www.orange.com
+// Submitted by Alexandre Linte <alexandre.linte@orange.com>
+tech.orange
+
+// Oursky Limited : https://authgear.com/, https://skygear.io/
+// Submitted by Authgear Team <hello@authgear.com>, Skygear Developer <hello@skygear.io>
+authgear-staging.com
+authgearapps.com
+skygearapp.com
+
+// OutSystems
+// Submitted by Duarte Santos <domain-admin@outsystemscloud.com>
+outsystemscloud.com
+
+// OVHcloud: https://ovhcloud.com
+// Submitted by Vincent Cassé <vincent.casse@ovhcloud.com>
+*.webpaas.ovh.net
+*.hosting.ovh.net
+
+// OwnProvider GmbH: http://www.ownprovider.com
+// Submitted by Jan Moennich <jan.moennich@ownprovider.com>
+ownprovider.com
+own.pm
+
+// OwO : https://whats-th.is/
+// Submitted by Dean Sheather <dean@deansheather.com>
+*.owo.codes
+
+// OX : http://www.ox.rs
+// Submitted by Adam Grand <webmaster@mail.ox.rs>
+ox.rs
+
+// oy.lc
+// Submitted by Charly Coste <changaco@changaco.oy.lc>
+oy.lc
+
+// Pagefog : https://pagefog.com/
+// Submitted by Derek Myers <derek@pagefog.com>
+pgfog.com
+
+// Pagefront : https://www.pagefronthq.com/
+// Submitted by Jason Kriss <jason@pagefronthq.com>
+pagefrontapp.com
+
+// PageXL : https://pagexl.com
+// Submitted by Yann Guichard <yann@pagexl.com>
+pagexl.com
+
+// Paywhirl, Inc : https://paywhirl.com/
+// Submitted by Daniel Netzer <dan@paywhirl.com>
+*.paywhirl.com
+
+// pcarrier.ca Software Inc: https://pcarrier.ca/
+// Submitted by Pierre Carrier <pc@rrier.ca>
+bar0.net
+bar1.net
+bar2.net
+rdv.to
+
+// .pl domains (grandfathered)
+art.pl
+gliwice.pl
+krakow.pl
+poznan.pl
+wroc.pl
+zakopane.pl
+
+// Pantheon Systems, Inc. : https://pantheon.io/
+// Submitted by Gary Dylina <gary@pantheon.io>
+pantheonsite.io
+gotpantheon.com
+
+// Peplink | Pepwave : http://peplink.com/
+// Submitted by Steve Leung <steveleung@peplink.com>
+mypep.link
+
+// Perspecta : https://perspecta.com/
+// Submitted by Kenneth Van Alstyne <kvanalstyne@perspecta.com>
+perspecta.cloud
+
+// PE Ulyanov Kirill Sergeevich : https://airy.host
+// Submitted by Kirill Ulyanov <k.ulyanov@airy.host>
+lk3.ru
+
+// Planet-Work : https://www.planet-work.com/
+// Submitted by Frédéric VANNIÈRE <f.vanniere@planet-work.com>
+on-web.fr
+
+// Platform.sh : https://platform.sh
+// Submitted by Nikola Kotur <nikola@platform.sh>
+bc.platform.sh
+ent.platform.sh
+eu.platform.sh
+us.platform.sh
+*.platformsh.site
+*.tst.site
+
+// Platter: https://platter.dev
+// Submitted by Patrick Flor <patrick@platter.dev>
+platter-app.com
+platter-app.dev
+platterp.us
+
+// Plesk : https://www.plesk.com/
+// Submitted by Anton Akhtyamov <program-managers@plesk.com>
+pdns.page
+plesk.page
+pleskns.com
+
+// Port53 : https://port53.io/
+// Submitted by Maximilian Schieder <maxi@zeug.co>
+dyn53.io
+
+// Porter : https://porter.run/
+// Submitted by Rudraksh MK <rudi@porter.run>
+onporter.run
+
+// Positive Codes Technology Company : http://co.bn/faq.html
+// Submitted by Zulfais <pc@co.bn>
+co.bn
+
+// Postman, Inc : https://postman.com
+// Submitted by Rahul Dhawan <security@postman.com>
+postman-echo.com
+pstmn.io
+mock.pstmn.io
+httpbin.org
+
+//prequalifyme.today : https://prequalifyme.today
+//Submitted by DeepakTiwari deepak@ivylead.io
+prequalifyme.today
+
+// prgmr.com : https://prgmr.com/
+// Submitted by Sarah Newman <owner@prgmr.com>
+xen.prgmr.com
+
+// priv.at : http://www.nic.priv.at/
+// Submitted by registry <lendl@nic.at>
+priv.at
+
+// privacytools.io : https://www.privacytools.io/
+// Submitted by Jonah Aragon <jonah@privacytools.io>
+prvcy.page
+
+// Protocol Labs : https://protocol.ai/
+// Submitted by Michael Burns <noc@protocol.ai>
+*.dweb.link
+
+// Protonet GmbH : http://protonet.io
+// Submitted by Martin Meier <admin@protonet.io>
+protonet.io
+
+// Publication Presse Communication SARL : https://ppcom.fr
+// Submitted by Yaacov Akiba Slama <admin@chirurgiens-dentistes-en-france.fr>
+chirurgiens-dentistes-en-france.fr
+byen.site
+
+// pubtls.org: https://www.pubtls.org
+// Submitted by Kor Nielsen <kor@pubtls.org>
+pubtls.org
+
+// PythonAnywhere LLP: https://www.pythonanywhere.com
+// Submitted by Giles Thomas <giles@pythonanywhere.com>
+pythonanywhere.com
+eu.pythonanywhere.com
+
+// QOTO, Org.
+// Submitted by Jeffrey Phillips Freeman <jeffrey.freeman@qoto.org>
+qoto.io
+
+// Qualifio : https://qualifio.com/
+// Submitted by Xavier De Cock <xdecock@gmail.com>
+qualifioapp.com
+
+// Quality Unit: https://qualityunit.com
+// Submitted by Vasyl Tsalko <vtsalko@qualityunit.com>
+ladesk.com
+
+// QuickBackend: https://www.quickbackend.com
+// Submitted by Dani Biro <dani@pymet.com>
+qbuser.com
+
+// Rad Web Hosting: https://radwebhosting.com
+// Submitted by Scott Claeys <s.claeys@radwebhosting.com>
+cloudsite.builders
+
+// Redgate Software: https://red-gate.com
+// Submitted by Andrew Farries <andrew.farries@red-gate.com>
+instances.spawn.cc
+
+// Redstar Consultants : https://www.redstarconsultants.com/
+// Submitted by Jons Slemmer <jons@redstarconsultants.com>
+instantcloud.cn
+
+// Russian Academy of Sciences
+// Submitted by Tech Support <support@rasnet.ru>
+ras.ru
+
+// QA2
+// Submitted by Daniel Dent (https://www.danieldent.com/)
+qa2.com
+
+// QCX
+// Submitted by Cassandra Beelen <cassandra@beelen.one>
+qcx.io
+*.sys.qcx.io
+
+// QNAP System Inc : https://www.qnap.com
+// Submitted by Nick Chang <nickchang@qnap.com>
+dev-myqnapcloud.com
+alpha-myqnapcloud.com
+myqnapcloud.com
+
+// Quip : https://quip.com
+// Submitted by Patrick Linehan <plinehan@quip.com>
+*.quipelements.com
+
+// Qutheory LLC : http://qutheory.io
+// Submitted by Jonas Schwartz <jonas@qutheory.io>
+vapor.cloud
+vaporcloud.io
+
+// Rackmaze LLC : https://www.rackmaze.com
+// Submitted by Kirill Pertsev <kika@rackmaze.com>
+rackmaze.com
+rackmaze.net
+
+// Rakuten Games, Inc : https://dev.viberplay.io
+// Submitted by Joshua Zhang <public-suffix@rgames.jp>
+g.vbrplsbx.io
+
+// Rancher Labs, Inc : https://rancher.com
+// Submitted by Vincent Fiduccia <domains@rancher.com>
+*.on-k3s.io
+*.on-rancher.cloud
+*.on-rio.io
+
+// Read The Docs, Inc : https://www.readthedocs.org
+// Submitted by David Fischer <team@readthedocs.org>
+readthedocs.io
+
+// Red Hat, Inc. OpenShift : https://openshift.redhat.com/
+// Submitted by Tim Kramer <tkramer@rhcloud.com>
+rhcloud.com
+
+// Render : https://render.com
+// Submitted by Anurag Goel <dev@render.com>
+app.render.com
+onrender.com
+
+// Repl.it : https://repl.it
+// Submitted by Lincoln Bergeson <lincoln@replit.com>
+firewalledreplit.co
+id.firewalledreplit.co
+repl.co
+id.repl.co
+repl.run
+
+// Resin.io : https://resin.io
+// Submitted by Tim Perry <tim@resin.io>
+resindevice.io
+devices.resinstaging.io
+
+// RethinkDB : https://www.rethinkdb.com/
+// Submitted by Chris Kastorff <info@rethinkdb.com>
+hzc.io
+
+// Revitalised Limited : http://www.revitalised.co.uk
+// Submitted by Jack Price <jack@revitalised.co.uk>
+wellbeingzone.eu
+wellbeingzone.co.uk
+
+// Rico Developments Limited : https://adimo.co
+// Submitted by Colin Brown <hello@adimo.co>
+adimo.co.uk
+
+// Riseup Networks : https://riseup.net
+// Submitted by Micah Anderson <micah@riseup.net>
+itcouldbewor.se
+
+// Rochester Institute of Technology : http://www.rit.edu/
+// Submitted by Jennifer Herting <jchits@rit.edu>
+git-pages.rit.edu
+
+// Rocky Enterprise Software Foundation : https://resf.org
+// Submitted by Neil Hanlon <neil@resf.org>
+rocky.page
+
+// Rusnames Limited: http://rusnames.ru/
+// Submitted by Sergey Zotov <admin@rusnames.ru>
+биз.рус
+ком.рус
+крым.рус
+мир.рус
+мск.рус
+орг.рус
+самара.рус
+сочи.рус
+спб.рус
+я.рус
+
+// SAKURA Internet Inc. : https://www.sakura.ad.jp/
+// Submitted by Internet Service Department <rs-vendor-ml@sakura.ad.jp>
+180r.com
+dojin.com
+sakuratan.com
+sakuraweb.com
+x0.com
+2-d.jp
+bona.jp
+crap.jp
+daynight.jp
+eek.jp
+flop.jp
+halfmoon.jp
+jeez.jp
+matrix.jp
+mimoza.jp
+ivory.ne.jp
+mail-box.ne.jp
+mints.ne.jp
+mokuren.ne.jp
+opal.ne.jp
+sakura.ne.jp
+sumomo.ne.jp
+topaz.ne.jp
+netgamers.jp
+nyanta.jp
+o0o0.jp
+rdy.jp
+rgr.jp
+rulez.jp
+s3.isk01.sakurastorage.jp
+s3.isk02.sakurastorage.jp
+saloon.jp
+sblo.jp
+skr.jp
+tank.jp
+uh-oh.jp
+undo.jp
+rs.webaccel.jp
+user.webaccel.jp
+websozai.jp
+xii.jp
+squares.net
+jpn.org
+kirara.st
+x0.to
+from.tv
+sakura.tv
+
+// Salesforce.com, Inc. https://salesforce.com/
+// Submitted by Michael Biven <mbiven@salesforce.com>
+*.builder.code.com
+*.dev-builder.code.com
+*.stg-builder.code.com
+
+// Sandstorm Development Group, Inc. : https://sandcats.io/
+// Submitted by Asheesh Laroia <asheesh@sandstorm.io>
+sandcats.io
+
+// SBE network solutions GmbH : https://www.sbe.de/
+// Submitted by Norman Meilick <nm@sbe.de>
+logoip.de
+logoip.com
+
+// Scaleway : https://www.scaleway.com/
+// Submitted by Rémy Léone <rleone@scaleway.com>
+fr-par-1.baremetal.scw.cloud
+fr-par-2.baremetal.scw.cloud
+nl-ams-1.baremetal.scw.cloud
+fnc.fr-par.scw.cloud
+functions.fnc.fr-par.scw.cloud
+k8s.fr-par.scw.cloud
+nodes.k8s.fr-par.scw.cloud
+s3.fr-par.scw.cloud
+s3-website.fr-par.scw.cloud
+whm.fr-par.scw.cloud
+priv.instances.scw.cloud
+pub.instances.scw.cloud
+k8s.scw.cloud
+k8s.nl-ams.scw.cloud
+nodes.k8s.nl-ams.scw.cloud
+s3.nl-ams.scw.cloud
+s3-website.nl-ams.scw.cloud
+whm.nl-ams.scw.cloud
+k8s.pl-waw.scw.cloud
+nodes.k8s.pl-waw.scw.cloud
+s3.pl-waw.scw.cloud
+s3-website.pl-waw.scw.cloud
+scalebook.scw.cloud
+smartlabeling.scw.cloud
+dedibox.fr
+
+// schokokeks.org GbR : https://schokokeks.org/
+// Submitted by Hanno Böck <hanno@schokokeks.org>
+schokokeks.net
+
+// Scottish Government: https://www.gov.scot
+// Submitted by Martin Ellis <martin.ellis@gov.scot>
+gov.scot
+service.gov.scot
+
+// Scry Security : http://www.scrysec.com
+// Submitted by Shante Adam <shante@skyhat.io>
+scrysec.com
+
+// Securepoint GmbH : https://www.securepoint.de
+// Submitted by Erik Anders <erik.anders@securepoint.de>
+firewall-gateway.com
+firewall-gateway.de
+my-gateway.de
+my-router.de
+spdns.de
+spdns.eu
+firewall-gateway.net
+my-firewall.org
+myfirewall.org
+spdns.org
+
+// Seidat : https://www.seidat.com
+// Submitted by Artem Kondratev <accounts@seidat.com>
+seidat.net
+
+// Sellfy : https://sellfy.com
+// Submitted by Yuriy Romadin <contact@sellfy.com>
+sellfy.store
+
+// Senseering GmbH : https://www.senseering.de
+// Submitted by Felix Mönckemeyer <f.moenckemeyer@senseering.de>
+senseering.net
+
+// Sendmsg: https://www.sendmsg.co.il
+// Submitted by Assaf Stern <domains@comstar.co.il>
+minisite.ms
+
+// Service Magnet : https://myservicemagnet.com
+// Submitted by Dave Sanders <dave@myservicemagnet.com>
+magnet.page
+
+// Service Online LLC : http://drs.ua/
+// Submitted by Serhii Bulakh <support@drs.ua>
+biz.ua
+co.ua
+pp.ua
+
+// Shift Crypto AG : https://shiftcrypto.ch
+// Submitted by alex <alex@shiftcrypto.ch>
+shiftcrypto.dev
+shiftcrypto.io
+
+// ShiftEdit : https://shiftedit.net/
+// Submitted by Adam Jimenez <adam@shiftcreate.com>
+shiftedit.io
+
+// Shopblocks : http://www.shopblocks.com/
+// Submitted by Alex Bowers <alex@shopblocks.com>
+myshopblocks.com
+
+// Shopify : https://www.shopify.com
+// Submitted by Alex Richter <alex.richter@shopify.com>
+myshopify.com
+
+// Shopit : https://www.shopitcommerce.com/
+// Submitted by Craig McMahon <craig@shopitcommerce.com>
+shopitsite.com
+
+// shopware AG : https://shopware.com
+// Submitted by Jens Küper <cloud@shopware.com>
+shopware.store
+
+// Siemens Mobility GmbH
+// Submitted by Oliver Graebner <security@mo-siemens.io>
+mo-siemens.io
+
+// SinaAppEngine : http://sae.sina.com.cn/
+// Submitted by SinaAppEngine <saesupport@sinacloud.com>
+1kapp.com
+appchizi.com
+applinzi.com
+sinaapp.com
+vipsinaapp.com
+
+// Siteleaf : https://www.siteleaf.com/
+// Submitted by Skylar Challand <support@siteleaf.com>
+siteleaf.net
+
+// Skyhat : http://www.skyhat.io
+// Submitted by Shante Adam <shante@skyhat.io>
+bounty-full.com
+alpha.bounty-full.com
+beta.bounty-full.com
+
+// Small Technology Foundation : https://small-tech.org
+// Submitted by Aral Balkan <aral@small-tech.org>
+small-web.org
+
+// Smoove.io : https://www.smoove.io/
+// Submitted by Dan Kozak <dan@smoove.io>
+vp4.me
+
+// Snowflake Inc : https://www.snowflake.com/
+// Submitted by Faith Olapade <faith.olapade@snowflake.com>
+snowflake.app
+privatelink.snowflake.app
+streamlit.app
+streamlitapp.com
+
+// Snowplow Analytics : https://snowplowanalytics.com/
+// Submitted by Ian Streeter <ian@snowplowanalytics.com>
+try-snowplow.com
+
+// SourceHut : https://sourcehut.org
+// Submitted by Drew DeVault <sir@cmpwn.com>
+srht.site
+
+// Stackhero : https://www.stackhero.io
+// Submitted by Adrien Gillon <adrien+public-suffix-list@stackhero.io>
+stackhero-network.com
+
+// Staclar : https://staclar.com
+// Submitted by Q Misell <q@staclar.com>
+musician.io
+// Submitted by Matthias Merkel <matthias.merkel@staclar.com>
+novecore.site
+
+// staticland : https://static.land
+// Submitted by Seth Vincent <sethvincent@gmail.com>
+static.land
+dev.static.land
+sites.static.land
+
+// Storebase : https://www.storebase.io
+// Submitted by Tony Schirmer <tony@storebase.io>
+storebase.store
+
+// Strategic System Consulting (eApps Hosting): https://www.eapps.com/
+// Submitted by Alex Oancea <aoancea@cloudscale365.com>
+vps-host.net
+atl.jelastic.vps-host.net
+njs.jelastic.vps-host.net
+ric.jelastic.vps-host.net
+
+// Sony Interactive Entertainment LLC : https://sie.com/
+// Submitted by David Coles <david.coles@sony.com>
+playstation-cloud.com
+
+// SourceLair PC : https://www.sourcelair.com
+// Submitted by Antonis Kalipetis <akalipetis@sourcelair.com>
+apps.lair.io
+*.stolos.io
+
+// SpaceKit : https://www.spacekit.io/
+// Submitted by Reza Akhavan <spacekit.io@gmail.com>
+spacekit.io
+
+// SpeedPartner GmbH: https://www.speedpartner.de/
+// Submitted by Stefan Neufeind <info@speedpartner.de>
+customer.speedpartner.de
+
+// Spreadshop (sprd.net AG) : https://www.spreadshop.com/
+// Submitted by Martin Breest <security@spreadshop.com>
+myspreadshop.at
+myspreadshop.com.au
+myspreadshop.be
+myspreadshop.ca
+myspreadshop.ch
+myspreadshop.com
+myspreadshop.de
+myspreadshop.dk
+myspreadshop.es
+myspreadshop.fi
+myspreadshop.fr
+myspreadshop.ie
+myspreadshop.it
+myspreadshop.net
+myspreadshop.nl
+myspreadshop.no
+myspreadshop.pl
+myspreadshop.se
+myspreadshop.co.uk
+
+// Standard Library : https://stdlib.com
+// Submitted by Jacob Lee <jacob@stdlib.com>
+api.stdlib.com
+
+// Storj Labs Inc. : https://storj.io/
+// Submitted by Philip Hutchins <hostmaster@storj.io>
+storj.farm
+
+// Studenten Net Twente : http://www.snt.utwente.nl/
+// Submitted by Silke Hofstra <syscom@snt.utwente.nl>
+utwente.io
+
+// Student-Run Computing Facility : https://www.srcf.net/
+// Submitted by Edwin Balani <sysadmins@srcf.net>
+soc.srcf.net
+user.srcf.net
+
+// Sub 6 Limited: http://www.sub6.com
+// Submitted by Dan Miller <dm@sub6.com>
+temp-dns.com
+
+// Supabase : https://supabase.io
+// Submitted by Inian Parameshwaran <security@supabase.io>
+supabase.co
+supabase.in
+supabase.net
+su.paba.se
+
+// Symfony, SAS : https://symfony.com/
+// Submitted by Fabien Potencier <fabien@symfony.com>
+*.s5y.io
+*.sensiosite.cloud
+
+// Syncloud : https://syncloud.org
+// Submitted by Boris Rybalkin <syncloud@syncloud.it>
+syncloud.it
+
+// Synology, Inc. : https://www.synology.com/
+// Submitted by Rony Weng <ronyweng@synology.com>
+dscloud.biz
+direct.quickconnect.cn
+dsmynas.com
+familyds.com
+diskstation.me
+dscloud.me
+i234.me
+myds.me
+synology.me
+dscloud.mobi
+dsmynas.net
+familyds.net
+dsmynas.org
+familyds.org
+vpnplus.to
+direct.quickconnect.to
+
+// Tabit Technologies Ltd. : https://tabit.cloud/
+// Submitted by Oren Agiv <oren@tabit.cloud>
+tabitorder.co.il
+mytabit.co.il
+mytabit.com
+
+// TAIFUN Software AG : http://taifun-software.de
+// Submitted by Bjoern Henke <dev-server@taifun-software.de>
+taifun-dns.de
+
+// Tailscale Inc. : https://www.tailscale.com
+// Submitted by David Anderson <danderson@tailscale.com>
+beta.tailscale.net
+ts.net
+
+// TASK geographical domains (www.task.gda.pl/uslugi/dns)
+gda.pl
+gdansk.pl
+gdynia.pl
+med.pl
+sopot.pl
+
+// team.blue https://team.blue
+// Submitted by Cedric Dubois <cedric.dubois@team.blue>
+site.tb-hosting.com
+
+// Teckids e.V. : https://www.teckids.org
+// Submitted by Dominik George <dominik.george@teckids.org>
+edugit.io
+s3.teckids.org
+
+// Telebit : https://telebit.cloud
+// Submitted by AJ ONeal <aj@telebit.cloud>
+telebit.app
+telebit.io
+*.telebit.xyz
+
+// Thingdust AG : https://thingdust.com/
+// Submitted by Adrian Imboden <adi@thingdust.com>
+*.firenet.ch
+*.svc.firenet.ch
+reservd.com
+thingdustdata.com
+cust.dev.thingdust.io
+cust.disrec.thingdust.io
+cust.prod.thingdust.io
+cust.testing.thingdust.io
+reservd.dev.thingdust.io
+reservd.disrec.thingdust.io
+reservd.testing.thingdust.io
+
+// ticket i/O GmbH : https://ticket.io
+// Submitted by Christian Franke <it@ticket.io>
+tickets.io
+
+// Tlon.io : https://tlon.io
+// Submitted by Mark Staarink <mark@tlon.io>
+arvo.network
+azimuth.network
+tlon.network
+
+// Tor Project, Inc. : https://torproject.org
+// Submitted by Antoine Beaupré <anarcat@torproject.org
+torproject.net
+pages.torproject.net
+
+// TownNews.com : http://www.townnews.com
+// Submitted by Dustin Ward <dward@townnews.com>
+bloxcms.com
+townnews-staging.com
+
+// TrafficPlex GmbH : https://www.trafficplex.de/
+// Submitted by Phillipp Röll <phillipp.roell@trafficplex.de>
+12hp.at
+2ix.at
+4lima.at
+lima-city.at
+12hp.ch
+2ix.ch
+4lima.ch
+lima-city.ch
+trafficplex.cloud
+de.cool
+12hp.de
+2ix.de
+4lima.de
+lima-city.de
+1337.pictures
+clan.rip
+lima-city.rocks
+webspace.rocks
+lima.zone
+
+// TransIP : https://www.transip.nl
+// Submitted by Rory Breuk <rbreuk@transip.nl>
+*.transurl.be
+*.transurl.eu
+*.transurl.nl
+
+// TransIP: https://www.transip.nl
+// Submitted by Cedric Dubois <cedric.dubois@team.blue>
+site.transip.me
+
+// TuxFamily : http://tuxfamily.org
+// Submitted by TuxFamily administrators <adm@staff.tuxfamily.org>
+tuxfamily.org
+
+// TwoDNS : https://www.twodns.de/
+// Submitted by TwoDNS-Support <support@two-dns.de>
+dd-dns.de
+diskstation.eu
+diskstation.org
+dray-dns.de
+draydns.de
+dyn-vpn.de
+dynvpn.de
+mein-vigor.de
+my-vigor.de
+my-wan.de
+syno-ds.de
+synology-diskstation.de
+synology-ds.de
+
+// Typedream : https://typedream.com
+// Submitted by Putri Karunia <putri@typedream.com>
+typedream.app
+
+// Typeform : https://www.typeform.com
+// Submitted by Sergi Ferriz <sergi.ferriz@typeform.com>
+pro.typeform.com
+
+// Uberspace : https://uberspace.de
+// Submitted by Moritz Werner <mwerner@jonaspasche.com>
+uber.space
+*.uberspace.de
+
+// UDR Limited : http://www.udr.hk.com
+// Submitted by registry <hostmaster@udr.hk.com>
+hk.com
+hk.org
+ltd.hk
+inc.hk
+
+// UK Intis Telecom LTD : https://it.com
+// Submitted by ITComdomains <to@it.com>
+it.com
+
+// UNIVERSAL DOMAIN REGISTRY : https://www.udr.org.yt/
+// see also: whois -h whois.udr.org.yt help
+// Submitted by Atanunu Igbunuroghene <publicsuffixlist@udr.org.yt>
+name.pm
+sch.tf
+biz.wf
+sch.wf
+org.yt
+
+// United Gameserver GmbH : https://united-gameserver.de
+// Submitted by Stefan Schwarz <sysadm@united-gameserver.de>
+virtualuser.de
+virtual-user.de
+
+// Upli : https://upli.io
+// Submitted by Lenny Bakkalian <lenny.bakkalian@gmail.com>
+upli.io
+
+// urown.net : https://urown.net
+// Submitted by Hostmaster <hostmaster@urown.net>
+urown.cloud
+dnsupdate.info
+
+// .US
+// Submitted by Ed Moore <Ed.Moore@lib.de.us>
+lib.de.us
+
+// VeryPositive SIA : http://very.lv
+// Submitted by Danko Aleksejevs <danko@very.lv>
+2038.io
+
+// Vercel, Inc : https://vercel.com/
+// Submitted by Connor Davis <security@vercel.com>
+vercel.app
+vercel.dev
+now.sh
+
+// Viprinet Europe GmbH : http://www.viprinet.com
+// Submitted by Simon Kissel <hostmaster@viprinet.com>
+router.management
+
+// Virtual-Info : https://www.virtual-info.info/
+// Submitted by Adnan RIHAN <hostmaster@v-info.info>
+v-info.info
+
+// Voorloper.com: https://voorloper.com
+// Submitted by Nathan van Bakel <info@voorloper.com>
+voorloper.cloud
+
+// Voxel.sh DNS : https://voxel.sh/dns/
+// Submitted by Mia Rehlinger <dns@voxel.sh>
+neko.am
+nyaa.am
+be.ax
+cat.ax
+es.ax
+eu.ax
+gg.ax
+mc.ax
+us.ax
+xy.ax
+nl.ci
+xx.gl
+app.gp
+blog.gt
+de.gt
+to.gt
+be.gy
+cc.hn
+blog.kg
+io.kg
+jp.kg
+tv.kg
+uk.kg
+us.kg
+de.ls
+at.md
+de.md
+jp.md
+to.md
+indie.porn
+vxl.sh
+ch.tc
+me.tc
+we.tc
+nyan.to
+at.vg
+blog.vu
+dev.vu
+me.vu
+
+// V.UA Domain Administrator : https://domain.v.ua/
+// Submitted by Serhii Rostilo <sergey@rostilo.kiev.ua>
+v.ua
+
+// Vultr Objects : https://www.vultr.com/products/object-storage/
+// Submitted by Niels Maumenee <storage@vultr.com>
+*.vultrobjects.com
+
+// Waffle Computer Inc., Ltd. : https://docs.waffleinfo.com
+// Submitted by Masayuki Note <masa@blade.wafflecell.com>
+wafflecell.com
+
+// WebHare bv: https://www.webhare.com/
+// Submitted by Arnold Hendriks <info@webhare.com>
+*.webhare.dev
+
+// WebHotelier Technologies Ltd: https://www.webhotelier.net/
+// Submitted by Apostolos Tsakpinis <apostolos.tsakpinis@gmail.com>
+reserve-online.net
+reserve-online.com
+bookonline.app
+hotelwithflight.com
+
+// WeDeploy by Liferay, Inc. : https://www.wedeploy.com
+// Submitted by Henrique Vicente <security@wedeploy.com>
+wedeploy.io
+wedeploy.me
+wedeploy.sh
+
+// Western Digital Technologies, Inc : https://www.wdc.com
+// Submitted by Jung Jin <jungseok.jin@wdc.com>
+remotewd.com
+
+// WIARD Enterprises : https://wiardweb.com
+// Submitted by Kidd Hustle <kiddhustle@wiardweb.com>
+pages.wiardweb.com
+
+// Wikimedia Labs : https://wikitech.wikimedia.org
+// Submitted by Arturo Borrero Gonzalez <aborrero@wikimedia.org>
+wmflabs.org
+toolforge.org
+wmcloud.org
+
+// WISP : https://wisp.gg
+// Submitted by Stepan Fedotov <stepan@wisp.gg>
+panel.gg
+daemon.panel.gg
+
+// Wizard Zines : https://wizardzines.com
+// Submitted by Julia Evans <julia@wizardzines.com>
+messwithdns.com
+
+// WoltLab GmbH : https://www.woltlab.com
+// Submitted by Tim Düsterhus <security@woltlab.cloud>
+woltlab-demo.com
+myforum.community
+community-pro.de
+diskussionsbereich.de
+community-pro.net
+meinforum.net
+
+// Woods Valldata : https://www.woodsvalldata.co.uk/
+// Submitted by Chris Whittle <chris.whittle@woodsvalldata.co.uk>
+affinitylottery.org.uk
+raffleentry.org.uk
+weeklylottery.org.uk
+
+// WP Engine : https://wpengine.com/
+// Submitted by Michael Smith <michael.smith@wpengine.com>
+// Submitted by Brandon DuRette <brandon.durette@wpengine.com>
+wpenginepowered.com
+js.wpenginepowered.com
+
+// Wix.com, Inc. : https://www.wix.com
+// Submitted by Shahar Talmi <shahar@wix.com>
+wixsite.com
+editorx.io
+
+// XenonCloud GbR: https://xenoncloud.net
+// Submitted by Julian Uphoff <publicsuffixlist@xenoncloud.net>
+half.host
+
+// XnBay Technology : http://www.xnbay.com/
+// Submitted by XnBay Developer <developer.xncloud@gmail.com>
+xnbay.com
+u2.xnbay.com
+u2-local.xnbay.com
+
+// XS4ALL Internet bv : https://www.xs4all.nl/
+// Submitted by Daniel Mostertman <unixbeheer+publicsuffix@xs4all.net>
+cistron.nl
+demon.nl
+xs4all.space
+
+// Yandex.Cloud LLC: https://cloud.yandex.com
+// Submitted by Alexander Lodin <security+psl@yandex-team.ru>
+yandexcloud.net
+storage.yandexcloud.net
+website.yandexcloud.net
+
+// YesCourse Pty Ltd : https://yescourse.com
+// Submitted by Atul Bhouraskar <atul@yescourse.com>
+official.academy
+
+// Yola : https://www.yola.com/
+// Submitted by Stefano Rivera <stefano@yola.com>
+yolasite.com
+
+// Yombo : https://yombo.net
+// Submitted by Mitch Schwenk <mitch@yombo.net>
+ybo.faith
+yombo.me
+homelink.one
+ybo.party
+ybo.review
+ybo.science
+ybo.trade
+
+// Yunohost : https://yunohost.org
+// Submitted by Valentin Grimaud <security@yunohost.org>
+ynh.fr
+nohost.me
+noho.st
+
+// ZaNiC : http://www.za.net/
+// Submitted by registry <hostmaster@nic.za.net>
+za.net
+za.org
+
+// Zine EOOD : https://zine.bg/
+// Submitted by Martin Angelov <martin@zine.bg>
+bss.design
+
+// Zitcom A/S : https://www.zitcom.dk
+// Submitted by Emil Stahl <esp@zitcom.dk>
+basicserver.io
+virtualserver.io
+enterprisecloud.nu
+
+// ===END PRIVATE DOMAINS===
diff --git a/ansible_collections/community/dns/plugins/public_suffix_list.dat.license b/ansible_collections/community/dns/plugins/public_suffix_list.dat.license
new file mode 100644
index 000000000..b94dfff7a
--- /dev/null
+++ b/ansible_collections/community/dns/plugins/public_suffix_list.dat.license
@@ -0,0 +1,2 @@
+SPDX-License-Identifier: MPL-2.0
+SPDX-FileCopyrightText: The Public Suffix List Authors