Repository: Domain-Connect/domainconnect_python

Issue:
Doesn't work for .app domains
Hi,
i was trying to use it with an .app domain but it did not work.
Looks like it is doing a DNS lookup for 
_domainconnect.app
instead of
_domainconnect.domain.app

Full command line and error message below.

Can you check and fix it please?
regards
michael


\# domain-connect-dyndns setup --domain test.mydomain.app
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/domainconnect/domainconnect.py", line 203, in _identify_domain_connect_api
    dns = self._resolver.query('_domainconnect.{}'.format(domain_root), 'TXT')
  File "/usr/local/lib/python3.6/site-packages/dns/resolver.py", line 1002, in query
    raise NXDOMAIN(qnames=qnames_to_try, responses=nxdomain_responses)
dns.resolver.NXDOMAIN: None of DNS query names exist: _domainconnect.app., _domainconnect.app.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/dyndns/domain_setup.py", line 16, in main
    config = dc.get_domain_config(domain)
  File "/usr/local/lib/python3.6/site-packages/domainconnect/domainconnect.py", line 243, in get_domain_config
    domain_connect_api = self._identify_domain_connect_api(domain_root)
  File "/usr/local/lib/python3.6/site-packages/domainconnect/domainconnect.py", line 213, in _identify_domain_connect_api
    raise NoDomainConnectRecordException('Failed to resolve "{}"'.format(domain_root))
domainconnect.domainconnect.NoDomainConnectRecordException: Failed to resolve "app"

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/bin/domain-connect-dyndns", line 11, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.6/site-packages/dyndns/command_line.py", line 59, in main
    print(domain_setup.main(domain, settings))
  File "/usr/local/lib/python3.6/site-packages/dyndns/domain_setup.py", line 17, in main
    except NoDomainConnectRecordException or NoDomainConnectSettingsException:
NameError: name 'NoDomainConnectRecordException' is not defined

Discussion:

Patch:
diff --git a/README.md b/README.md
index 4e0bd1c..c96f770 100644
--- a/README.md
+++ b/README.md
@@ -145,5 +145,6 @@ dc = DomainConnect(
 ## CHANGELOG
 | version | date       | changes |
 | ------- | -----------| ------ |
+| 0.0.7  | 2019-10-29 | Bugfix: error when setting up .app domain |
 | 0.0.6  | 2019-07-05 | UPDATE: moved from pycrypto to cryptography (due to know security issues) |
 | 0.0.5  | 2019-03-12 | NEW FEATURE: url signing capability added |
diff --git a/domainconnect/domainconnect.py b/domainconnect/domainconnect.py
index 1effe31..a66214f 100644
--- a/domainconnect/domainconnect.py
+++ b/domainconnect/domainconnect.py
@@ -13,7 +13,7 @@ import time
 from six.moves import urllib
 from dns.exception import Timeout
 from dns.resolver import Resolver, NXDOMAIN, YXDOMAIN, NoAnswer, NoNameservers
-from publicsuffix import PublicSuffixList
+from publicsuffixlist import PublicSuffixList
 import webbrowser
 from .network import get_json, get_http, http_request_json, NetworkContext
 
@@ -195,7 +195,7 @@ class DomainConnect:
 
     @staticmethod
     def identify_domain_root(domain):
-        return psl.get_public_suffix(domain)
+        return psl.privatesuffix(domain)
 
     def _identify_domain_connect_api(self, domain_root):
         # noinspection PyBroadException
diff --git a/requirements.txt b/requirements.txt
index b933b75..e291b06 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,11 +1,11 @@
-asn1crypto==0.24.0
-cffi==1.12.3
-cryptography==2.7
+asn1crypto==1.2.0
+cffi==1.13.1
+cryptography==2.8
 dnspython==1.16.0
-future==0.17.1
+future==0.18.1
 linecache2==1.0.0
 publicsuffix==1.1.0
-publicsuffixlist==0.6.5
+publicsuffixlist==0.6.11
 pybase64==0.5.0
 pycparser==2.19
 six==1.12.0
diff --git a/setup.py b/setup.py
index 6d4f36b..b95416c 100644
--- a/setup.py
+++ b/setup.py
@@ -3,7 +3,7 @@
 from setuptools import setup
 
 setup(name='domain_connect',
-      version='0.0.6',
+      version='0.0.7',
       description='Python client library for Domain Connect protocol. See: https://domainconnect.org',
       long_description_content_type="text/markdown",
       long_description=open('README.md').read(),
@@ -19,11 +19,11 @@ setup(name='domain_connect',
           'domainconnect',
       ],
       install_requires=[
-          'dnspython >= 1.15.0',
+          'dnspython >= 1.16.0',
           'publicsuffix >= 1.1.0',
-          'publicsuffixlist >= 0.6.1',
-          'six >= 1.11.0',
-          'future >= 0.16.0',
-          'cryptography >= 2.7'
+          'publicsuffixlist >= 0.6.11',
+          'six >= 1.12.0',
+          'future >= 0.18.1',
+          'cryptography >= 2.8'
       ],
       )


Test patch:
diff --git a/domainconnect/tests/test_domainConnect.py b/domainconnect/tests/test_domainConnect.py
index 0d1022e..d610471 100644
--- a/domainconnect/tests/test_domainConnect.py
+++ b/domainconnect/tests/test_domainConnect.py
@@ -185,6 +185,44 @@ class TestDomainConnect(TestCase):
         assert (res.urlAPI == config['API_URL']), 'urlAPI not correct: {}'.format(res.urlAPI)
         assert (res.providerName == config['PROVIDER_ID']), 'providerName not correct: {}'.format(res.providerName)
 
+    domainroottests = [
+        ('test.com', 'test.com', ''),
+        ('subdomain.test.com', 'test.com', 'subdomain'),
+        ('sub.subdomain.test.com', 'test.com', 'sub.subdomain'),
+        ('test.co.uk', 'test.co.uk', ''),
+        ('subdomain.test.co.uk', 'test.co.uk', 'subdomain'),
+        ('sub.subdomain.test.co.uk', 'test.co.uk', 'sub.subdomain'),
+        ('test.uk', 'test.uk', ''),
+        ('subdomain.test.uk', 'test.uk', 'subdomain'),
+        ('sub.subdomain.test.uk', 'test.uk', 'sub.subdomain'),
+        ('test.com.de', 'test.com.de', ''),
+        ('subdomain.test.com.de', 'test.com.de', 'subdomain'),
+        ('sub.subdomain.test.com.de', 'test.com.de', 'sub.subdomain'),
+        ('test.poznan.pl', 'test.poznan.pl', ''),
+        ('subdomain.test.poznan.pl', 'test.poznan.pl', 'subdomain'),
+        ('sub.subdomain.test.poznan.pl', 'test.poznan.pl', 'sub.subdomain'),
+        ('get.app', 'get.app', ''),
+        ('subdomain.get.app', 'get.app', 'subdomain'),
+        ('sub.subdomain.get.app', 'get.app', 'sub.subdomain'),
+        ('get.unkownnewtld', 'get.unkownnewtld', ''),
+        ('subdomain.get.unkownnewtld', 'get.unkownnewtld', 'subdomain'),
+        ('sub.subdomain.get.unkownnewtld', 'get.unkownnewtld', 'sub.subdomain'),
+    ]
+
+    def test_get_domain_root(self):
+        for i in TestDomainConnect.domainroottests:
+            with self.subTest(i=i):
+                self._test_get_domain_root(i)
+
+    @staticmethod
+    def _test_get_domain_root(config):
+        dc = DomainConnect()
+        domain_root = dc.identify_domain_root(config[0]);
+        assert domain_root is not None, "No domain root found"
+        assert domain_root == config[1], "Wrong domain root found. Expected {}, Found {}".format(config[1], domain_root)
+        assert '{}{}{}'.format(config[2], '' if config[2] == '' else '.', domain_root) == config[0], "Domain root + subdomain != domain"
+
+
     def test_get_domain_connect_template_async_url(self):
         for i in configs:
             with self.subTest(i=i):