diff --git a/setup.py b/setup.py index 011b2d9..9a9ce24 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ import sys, os import setuptools -version = '0.7.0' +version = '0.7.2' setuptools.setup( name='python-whois', diff --git a/test/samples/expected/google.de b/test/samples/expected/google.de index 8e29f25..df4681d 100644 --- a/test/samples/expected/google.de +++ b/test/samples/expected/google.de @@ -1 +1 @@ -{"domain_name": "google.de", "status": "connect", "updated_date": "2018-03-12T21:44:25+01:00", "name": null, "org": null, "address": null, "zipcode": null, "city": null, "country_code": null, "phone": null, "fax": null, "name_servers": ["ns1.google.com", "ns2.google.com", "ns3.google.com", "ns4.google.com"], "emails": null} \ No newline at end of file +{"domain_name": "google.de", "status": "connect", "updated_date": "2018-03-12 21:44:25+01:00", "name": null, "org": null, "address": null, "zipcode": null, "city": null, "country_code": null, "phone": null, "fax": null, "name_servers": ["ns1.google.com", "ns2.google.com", "ns3.google.com", "ns4.google.com"], "emails": null} \ No newline at end of file diff --git a/test/samples/expected/sapo.pt b/test/samples/expected/sapo.pt index 28dcba7..4799b87 100644 --- a/test/samples/expected/sapo.pt +++ b/test/samples/expected/sapo.pt @@ -1 +1 @@ -{"domain_name": "sapo.pt", "expiration_date": "2018-11-02 00:00:00", "updated_date": null, "creation_date": "2002-10-30 00:00:00", "status": "ACTIVE"} \ No newline at end of file +{"domain_name": "sapo.pt", "expiration_date": "2019-11-02 23:59:00", "updated_date": null, "creation_date": "2002-10-30 00:00:00", "status": "Registered"} \ No newline at end of file diff --git a/test/samples/expected/web.de b/test/samples/expected/web.de index e4ab5fb..011ad5c 100644 --- a/test/samples/expected/web.de +++ b/test/samples/expected/web.de @@ -1 +1 @@ -{"domain_name": "web.de", "expiration_date": null, "updated_date": ["2016-04-11T11:09:54+02:00", "2011-08-10T17:09:10+02:00"], "creation_date": null, "status": "connect"} \ No newline at end of file +{"domain_name": "web.de", "expiration_date": null, "updated_date": ["2016-04-11 11:09:54+02:00", "2011-08-10 17:09:10+02:00"], "creation_date": null, "status": "connect"} \ No newline at end of file diff --git a/test/samples/whois/sapo.pt b/test/samples/whois/sapo.pt index 9518655..01e978c 100644 --- a/test/samples/whois/sapo.pt +++ b/test/samples/whois/sapo.pt @@ -1,31 +1,20 @@ -Nome de domínio / Domain Name: sapo.pt -Data de registo / Creation Date (dd/mm/yyyy): 30/10/2002 -Data de expiração / Expiration Date (dd/mm/yyyy): 02/11/2018 -Estado / Status: ACTIVE - -Titular / Registrant - MEO - SERVIÇOS DE COMUNICAÇÕES E MULTIMÉDIA S.A. - A/C Direção de Tecnologias de Informação - Av. Fontes Pereira de Melo, 40 - 1069-300 Lisboa - Email: gestao.dominios@telecom.pt - -Entidade Gestora / Billing Contact - MEO - SERVIÇOS DE COMUNICAÇÕES E MULTIMÉDIA S.A. - Email: gestao.dominios@telecom.pt - -Responsável Técnico / Tech Contact - MEO - SERVIÇOS DE COMUNICAÇÕES E MULTIMÉDIA S.A. - Email: gestao.dominios@telecom.pt - -Nameserver Information - Nameserver: sapo.pt NS ns2.sapo.pt. - Nameserver: sapo.pt NS dns02.sapo.pt. - Nameserver: sapo.pt NS ns.sapo.pt. - Nameserver: sapo.pt NS dns01.sapo.pt. - Nameserver: ns2.sapo.pt. A 212.55.154.194 - Nameserver: dns02.sapo.pt. AAAA 2001:8a0:2206:4:213:13:30:116 - Nameserver: ns.sapo.pt. A 212.55.154.202 - Nameserver: dns01.sapo.pt. AAAA 2001:8a0:2106:4:213:13:28:116 - Nameserver: dns01.sapo.pt. A 213.13.28.116 - Nameserver: dns02.sapo.pt. A 213.13.30.116 \ No newline at end of file +Domain: sapo.pt +Domain Status: Registered +Creation Date: 30/10/2002 00:00:00 +Expiration Date: 02/11/2019 23:59:00 +Owner Name: MEO - SERVI?OS DE COMUNICA??ES E MULTIM?DIA S.A. +Owner Address: A/C Dire??o de Tecnologias de Informa??o +Owner Locality: Av. Fontes Pereira de Melo, 40 +Owner ZipCode: 1069-300 +Owner Locality ZipCode: Av. Fontes Pereira de Melo, 40 +Owner Email: gestao.dominios@telecom.pt +Admin Name: MEO - SERVI?OS DE COMUNICA??ES E MULTIM?DIA S.A. +Admin Address: A/C Dire??o de Tecnologias de Informa??o +Admin Locality: Av. Fontes Pereira de Melo, 40 +Admin ZipCode: 1069-300 +Admin Locality ZipCode: Av. Fontes Pereira de Melo, 40 +Admin Email: gestao.dominios@telecom.pt +Name Server: ns2.sapo.pt | IPv4: 212.55.154.194 and IPv6: +Name Server: dns01.sapo.pt | IPv4: 213.13.28.116 and IPv6: 2001:8a0:2106:4:213:13:28:116 +Name Server: dns02.sapo.pt | IPv4: 213.13.30.116 and IPv6: 2001:8a0:2206:4:213:13:30:116 +Name Server: ns.sapo.pt | IPv4: 212.55.154.202 and IPv6: \ No newline at end of file diff --git a/test/test_main.py b/test/test_main.py index 84afbb3..5443dbd 100644 --- a/test/test_main.py +++ b/test/test_main.py @@ -31,3 +31,17 @@ def test_unicode_domain_and_tld(self): url = 'http://россия.рф/' domain = 'россия.рф' self.assertEqual(domain, extract_domain(url)) + + def test_ipv6(self): + """ Verify that ipv6 addresses work """ + url = '2607:f8b0:4006:802::200e' + domain = '1e100.net' + # double extract_domain() so we avoid possibly changing hostnames like lga34s12-in-x0e.1e100.net + self.assertEqual(domain, extract_domain(extract_domain(url))) + + def test_ipv4(self): + """ Verify that ipv4 addresses work """ + url = '172.217.3.110' + domain = '1e100.net' + # double extract_domain() so we avoid possibly changing hostnames like lga34s18-in-f14.1e100.net + self.assertEqual(domain, extract_domain(extract_domain(url))) diff --git a/test/test_parser.py b/test/test_parser.py index 5ce5801..c435ce8 100644 --- a/test/test_parser.py +++ b/test/test_parser.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from __future__ import print_function from __future__ import unicode_literals from __future__ import division @@ -97,7 +98,7 @@ def date2str4json(obj): print("%s \t(%s):\t Missing in results" % (domain, key,)) fail += 1 continue - + result = results.get(key) if isinstance(result, list): result = [str(element) for element in result] @@ -125,7 +126,7 @@ def test_ca_parse(self): Registry Registrant ID: 70 Registrant Name: Test Industries - Registrant Organization: + Registrant Organization: Admin Name: Test Person1 Admin Street: Test Address @@ -169,6 +170,39 @@ def test_ca_parse(self): } self._parse_and_compare('testcompany.ca', data, expected_results, whois_entry=WhoisCa) + + def test_cn_parse(self): + data = """ + Domain Name: cnnic.com.cn + ROID: 20021209s10011s00047242-cn + Domain Status: serverDeleteProhibited + Domain Status: serverUpdateProhibited + Domain Status: serverTransferProhibited + Registrant ID: s1255673574881 + Registrant: 中国互联网络信息中心 + Registrant Contact Email: servicei@cnnic.cn + Sponsoring Registrar: 北京新网数码信息技术有限公司 + Name Server: a.cnnic.cn + Name Server: b.cnnic.cn + Name Server: c.cnnic.cn + Name Server: d.cnnic.cn + Name Server: e.cnnic.cn + Registration Time: 2000-09-14 00:00:00 + Expiration Time: 2023-08-16 16:26:39 + DNSSEC: unsigned + """ + expected_results = { + "domain_name": "cnnic.com.cn", + "registrar": "北京新网数码信息技术有限公司", + "creation_date": "2000-09-14 00:00:00", + "expiration_date": "2023-08-16 16:26:39", + "name_servers": ["a.cnnic.cn", "b.cnnic.cn", "c.cnnic.cn", "d.cnnic.cn", "e.cnnic.cn"], + "status": ["serverDeleteProhibited", "serverUpdateProhibited", "serverTransferProhibited"], + "emails": "servicei@cnnic.cn", + "dnssec": "unsigned", + "name": "中国互联网络信息中心" + } + self._parse_and_compare('cnnic.com.cn', data, expected_results) def test_il_parse(self): data = """ diff --git a/test/test_query.py b/test/test_query.py index 9954764..14f25e7 100644 --- a/test/test_query.py +++ b/test/test_query.py @@ -23,3 +23,27 @@ def test_simple_unicode_domain(self): def test_unicode_domain_and_tld(self): domain = 'россия.рф' whois(domain) + + def test_ipv4(self): + """ Verify ipv4 addresses. """ + domain = '172.217.3.110' + whois_results = whois(domain) + if isinstance(whois_results['domain_name'], list): + domain_names = [_.lower() for _ in whois_results['domain_name']] + else: + domain_names = [whois_results['domain_name'].lower()] + + self.assertIn('1e100.net', domain_names) + self.assertIn('ns1.google.com', [_.lower() for _ in whois_results['name_servers']]) + + def test_ipv6(self): + """ Verify ipv6 addresses. """ + domain = '2607:f8b0:4006:802::200e' + whois_results = whois(domain) + if isinstance(whois_results['domain_name'], list): + domain_names = [_.lower() for _ in whois_results['domain_name']] + else: + domain_names = [whois_results['domain_name'].lower()] + + self.assertIn('1e100.net', domain_names) + self.assertIn('ns1.google.com', [_.lower() for _ in whois_results['name_servers']]) diff --git a/whois/__init__.py b/whois/__init__.py index 8cf055f..c21c0ea 100644 --- a/whois/__init__.py +++ b/whois/__init__.py @@ -16,10 +16,13 @@ from .whois import NICClient +# thanks to https://www.regextester.com/104038 +IPV4_OR_V6 = re.compile(r"((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))") + def whois(url, command=False, flags=0): # clean domain to expose netloc - ip_match = re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", url) + ip_match = IPV4_OR_V6.match(url) if ip_match: domain = url try: @@ -63,8 +66,12 @@ def extract_domain(url): globo.com >>> print(extract_domain('1-0-1-1-1-0-1-1-1-1-1-1-1-.0-0-0-0-0-0-0-0-0-0-0-0-0-10-0-0-0-0-0-0-0-0-0-0-0-0-0.info')) 0-0-0-0-0-0-0-0-0-0-0-0-0-10-0-0-0-0-0-0-0-0-0-0-0-0-0.info + >>> print(extract_domain('2607:f8b0:4006:802::200e')) + 1e100.net + >>> print(extract_domain('172.217.3.110')) + 1e100.net """ - if re.match(r'\d+\.\d+\.\d+\.\d+', url): + if IPV4_OR_V6.match(url): # this is an IP address return socket.gethostbyaddr(url)[0] diff --git a/whois/parser.py b/whois/parser.py index f9cf97b..c77f363 100644 --- a/whois/parser.py +++ b/whois/parser.py @@ -194,6 +194,8 @@ def load(domain, text): return WhoisName(domain, text) elif domain.endswith('.me'): return WhoisMe(domain, text) + elif domain.endswith('ae'): + return WhoisAe(domain, text) elif domain.endswith('.au'): return WhoisAU(domain, text) elif domain.endswith('.ru'): @@ -244,6 +246,8 @@ def load(domain, text): return WhoisInfo(domain, text) elif domain.endswith('.su'): return WhoisSu(domain, text) + elif domain.endswith('si'): + return WhoisSi(domain, text) elif domain.endswith('.kg'): return WhoisKg(domain, text) elif domain.endswith('.io'): @@ -262,6 +266,8 @@ def load(domain, text): return WhoisSK(domain, text) elif domain.endswith('.se'): return WhoisSe(domain, text) + elif domain.endswith('no'): + return WhoisNo(domain, text) elif domain.endswith('.nu'): return WhoisSe(domain, text) elif domain.endswith('.is'): @@ -469,7 +475,7 @@ class WhoisRu(WhoisEntry): } def __init__(self, domain, text): - if text.strip() == 'No entries found': + if 'No entries found' in text: raise PywhoisError(text) else: WhoisEntry.__init__(self, domain, text, self.regex) @@ -640,6 +646,7 @@ class WhoisCa(WhoisEntry): 'registrar_url': 'Registrar URL: *(.+)', 'registrant_name': 'Registrant Name: *(.+)', 'registrant_number': 'Registry Registrant ID: *(.+)', + 'admin_name': 'Admin Name: *(.+)', 'domain_status': 'Domain status: *(.+)', 'emails': 'Email: *(.+)', 'updated_date': 'Updated Date: *(.+)', @@ -651,7 +658,7 @@ class WhoisCa(WhoisEntry): } def __init__(self, domain, text): - if 'Domain status: available' in text: + if 'Domain status: available' in text or 'Not found:' in text: raise PywhoisError(text) else: WhoisEntry.__init__(self, domain, text, self.regex) @@ -831,6 +838,7 @@ class WhoisAU(WhoisEntry): 'registrar': 'Registrar Name: *(.+)\n', 'status': 'Status: *(.+)', 'registrant_name': 'Registrant: *(.+)', + 'registrant_contact_name': 'Registrant Contact Name: (.+)', 'name_servers': 'Name Server: *(.+)', } @@ -926,7 +934,7 @@ class WhoisKr(WhoisEntry): """ regex = { 'domain_name': 'Domain Name\s*: *(.+)', - 'registrant_org': 'Registrant\s*: *(.+)', + 'registrant': 'Registrant\s*: *(.+)', 'registrant_address': 'Registrant Address\s*: *(.+)', 'registrant_zip': 'Registrant Zip Code\s*: *(.+)', 'admin_name': 'Administrative Contact\(AC\)\s*: *(.+)', @@ -950,13 +958,24 @@ class WhoisPt(WhoisEntry): """Whois parser for .pt domains """ regex = { - 'domain_name': 'domain name: *(.+)', - 'creation_date': 'creation date \(dd\/mm\/yyyy\): *(.+)', - 'expiration_date': 'expiration date \(dd\/mm\/yyyy\): *(.+)', - 'name_servers': '\tNS\t(.+).', # list of name servers - 'status': 'status: *(.+)', # list of statuses + 'domain_name': 'Domain: *(.+)', + 'creation_date': 'Creation Date: *(.+)', + 'expiration_date': 'Expiration Date: *(.+)', + 'registrant': 'Owner Name: *(.+)', + 'registrant_street': 'Owner Address: *(.+)', + 'registrant_city': 'Owner Locality: *(.+)', + 'registrant_postal_code': 'Owner ZipCode: *(.+)', + 'registrant_email': 'Owner Email: *(.+)', + 'admin': 'Admin Name: *(.+)', + 'admin_street': 'Admin Address: *(.+)', + 'admin_city': 'Admin Locality: *(.+)', + 'admin_postal_code':'Admin ZipCode: *(.+)', + 'admin_email': 'Admin Email: *(.+)', + 'name_servers': 'Name Server: *(.+) \|', # list of name servers + 'status': 'Domain Status: *(.+)', # list of statuses 'emails': EMAIL_REGEX, # list of email addresses } + dayfirst = True def __init__(self, domain, text): if text.strip() == 'No entries found': @@ -1024,6 +1043,7 @@ class WhoisAt(WhoisEntry): 'phone': 'phone: *(.+)', 'fax': 'fax-no: *(.+)', 'changed': 'changed: *(.+)', + 'email': 'e-mail: *(.+)', } def __init__(self, domain, text): @@ -1296,7 +1316,8 @@ class WhoisChLi(WhoisEntry): """ regex = { 'domain_name': '\nDomain name:\n*(.+)', - 'registrant': 'Holder of domain name:\n*([\n\s\S]+)\nContractual Language:', + 'registrant': 'Holder of domain name:\s*(?:.*\n){1}\s*(.+)', + 'registrant_address': 'Holder of domain name:\s*(?:.*\n){2}\s*(.+)', 'registrar': 'Registrar:\n*(.+)', 'creation_date': 'First registration date:\n*(.+)', 'dnssec': 'DNSSEC:*([\S]+)', @@ -1359,6 +1380,7 @@ class WhoisSe(WhoisEntry): """ regex = { 'domain_name': 'domain\.*: *(.+)', + 'registrant': 'holder\.*: *(.+)', 'creation_date': 'created\.*: *(.+)', 'updated_date': 'modified\.*: *(.+)', 'expiration_date': 'expires\.*: *(.+)', @@ -1523,7 +1545,10 @@ class WhoisSK(WhoisEntry): 'expiration_date': 'Valid Until: *(.+)', 'name_servers': 'Nameserver: *(.+)', - 'registrant': 'Registrant:\s*(.+)', + 'registrant': 'Name:\s*(.+)', + 'registrant_email': 'Email:\s*(.+)', + 'registrant_phone': 'Phone:\s*(.+)', + 'registrant_address': 'Street:\s*(.+)', 'registrar': '(?<=Registrar)[\s\S]*?Organization:(.*)', 'registrar_organization_id': '(?<=Registrar)[\s\S]*?Organization ID:(.*)', @@ -1682,10 +1707,12 @@ class WhoisIs(WhoisEntry): """ regex = { 'domain_name': 'domain\.*: *(.+)', + 'registrant': 'registrant: *(.+)', 'name': 'person\.*: *(.+)', 'address': 'address\.*: *(.+)', 'creation_date': 'created\.*: *(.+)', 'expiration_date': 'expires\.*: *(.+)', + 'email': 'e-mail: *(.+)', 'name_servers': 'nserver\.*: *(.+)', # list of name servers 'dnssec': 'dnssec\.*: *(.+)', } @@ -1757,16 +1784,17 @@ class WhoisIl(WhoisEntry): """Whois parser for .il domains """ regex = { - 'domain_name': 'domain: *(.+)', - 'expiration_date': 'validity: *(.+)', - 'registrant_name': 'person: *(.+)', - 'dnssec': 'DNSSEC: *(.+)', - 'status': 'status: *(.+)', - 'name_servers': 'nserver: *(.+)', - 'emails': 'e-mail: *(.+)', - 'phone': 'phone: *(.+)', - 'registrar': 'registrar name: *(.+)', - 'referral_url': 'registrar info: *(.+)', + 'domain_name': 'domain: *(.+)', + 'expiration_date': 'validity: *(.+)', + 'registrant': 'person: *(.+)', + 'registrant_address': 'address *(.+)', + 'dnssec': 'DNSSEC: *(.+)', + 'status': 'status: *(.+)', + 'name_servers': 'nserver: *(.+)', + 'emails': 'e-mail: *(.+)', + 'phone': 'phone: *(.+)', + 'registrar': 'registrar name: *(.+)', + 'referral_url': 'registrar info: *(.+)', } dayfirst = True @@ -1838,6 +1866,7 @@ class WhoisIe(WhoisEntry): """ regex = { 'domain_name': 'Domain: *(.+)', + 'registrant': 'Domain Holder: *(.+)', 'description': 'descr: *(.+)', 'source': 'Source: *(.+)', 'creation_date': 'Registration Date: *(.+)', @@ -1923,7 +1952,7 @@ class WhoisCz(WhoisEntry): """ regex = { 'domain_name': 'domain: *(.+)', - 'registrant_name': 'registrant: *(.+)', + 'registrant': 'registrant: *(.+)', 'registrar': 'registrar: *(.+)', 'creation_date': 'registered: *(.+)', 'updated_date': 'changed: *(.+)', @@ -1932,7 +1961,7 @@ class WhoisCz(WhoisEntry): } def __init__(self, domain, text): - if '% No entries found.' in text: + if '% No entries found.' in text or 'Your connection limit exceeded' in text: raise PywhoisError(text) else: WhoisEntry.__init__(self, domain, text, self.regex) @@ -1977,7 +2006,9 @@ class WhoisHr(WhoisEntry): 'updated_date': 'Updated Date: *(.+)', 'creation_date': 'Creation Date: *(.+)', 'expiration_date': 'Registrar Registration Expiration Date: *(.+)', - 'name_servers': 'Name Server: *(.+)' + 'name_servers': 'Name Server: *(.+)', + 'registrant': 'Registrant Name:\s(.+)', + 'registrant_address': 'Reigstrant Street:\s*(.+)', } def __init__(self, domain, text): @@ -2032,7 +2063,7 @@ class WhoisHk(WhoisEntry): } def __init__(self, domain, text): - if 'ERROR: No entries found' in text: + if 'ERROR: No entries found' in text or 'The domain has not been registered in text': raise PywhoisError(text) else: WhoisEntry.__init__(self, domain, text, self.regex) @@ -2236,6 +2267,7 @@ class WhoisCn(WhoisEntry): 'status': 'Status: *(.+)', # list of statuses 'emails': EMAIL_REGEX, # list of email s 'dnssec': 'dnssec: *([\S]+)', + 'name': 'Registrant: *(.+)', } def __init__(self, domain, text): @@ -2507,3 +2539,55 @@ def __init__(self, domain, text): raise PywhoisError(text) else: WhoisEntry.__init__(self, domain, text, self.regex) + + +class WhoisAe(WhoisEntry): + """Whois parser for .ae domains + """ + regex = { + 'domain_name': 'Domain Name: *(.+)', + 'status': 'Status: *(.+)', + 'registrant': 'Registrant Contact Name: *(.+)', + 'tech_name': 'Tech Contact Name: *(.+)', + } + + def __init__(self, domain, text): + if text.strip() == 'No Data Found': + raise PywhoisError(text) + else: + WhoisEntry.__init__(self, domain, text, self.regex) + + +class WhoisSi(WhoisEntry): + """Whois parser for .si domains + """ + regex = { + 'domain_name': 'domain: *(.+)', + 'registrar': 'registrar: *(.+)', + 'name_servers': 'nameserver: *(.+)', + 'registrant': 'registrant: *(.+)', + 'creation_date': 'created: *(.+)', + 'expiration_date': 'expire: *(.+)', + } + + def __init__(self, domain, text): + if 'No entries found for the selected source(s).' in text: + raise PywhoisError(text) + else: + WhoisEntry.__init__(self, domain, text, self.regex) + + +class WhoisNo(WhoisEntry): + """Whois parser for .no domains + """ + regex = { + 'domain_name': 'Domain Name.*:\s*(.+)', + 'creation_date': 'Additional information:\nCreated:\s*(.+)', + 'updated_date': 'Additional information:\n(?:.*\n)Last updated:\s*(.+)', + } + + def __init__(self, domain, text): + if 'No match' in text: + raise PywhoisError(text) + else: + WhoisEntry.__init__(self, domain, text, self.regex) diff --git a/whois/whois.py b/whois/whois.py index 7a8b772..9bd0971 100644 --- a/whois/whois.py +++ b/whois/whois.py @@ -82,6 +82,7 @@ class NICClient(object): LI_HOST = "whois.nic.li" MX_HOST = "whois.mx" PE_HOST = "kero.yachay.pe" + ONLINE_HOST = "whois.nic.online" WHOIS_RECURSE = 0x01 WHOIS_QUICK = 0x02 @@ -123,12 +124,18 @@ def whois(self, query, hostname, flags, many_results=False): except ImportError as e: print("You need to install the Python socks module. Install PIP (https://bootstrap.pypa.io/get-pip.py) and then 'pip install PySocks'") raise e - socksproxy, port = os.environ["SOCKS"].split(":") - socks_proto = None + socks_user, socks_password = None, None + if "@" in os.environ["SOCKS"]: + creds, proxy = os.environ["SOCKS"].split("@") + socks_user, socks_password = creds.split(":") + else: + proxy = os.environ["SOCKS"] + socksproxy, port = proxy.split(":") + socks_proto = socket.AF_INET if socket.AF_INET6 in [sock[0] for sock in socket.getaddrinfo(socksproxy, port)]: socks_proto=socket.AF_INET6 s = socks.socksocket(socks_proto) - s.set_proxy(socks.SOCKS5, socksproxy, int(port)) + s.set_proxy(socks.SOCKS5, socksproxy, int(port), True, socks_user, socks_password) else: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(10) @@ -165,7 +172,10 @@ def whois(self, query, hostname, flags, many_results=False): if flags & NICClient.WHOIS_RECURSE and nhost is None: nhost = self.findwhois_server(response, hostname, query) if nhost is not None: - response += self.whois(query, nhost, 0) + try: + response += self.whois(query, nhost, 0) + except socket.gaierror: + pass return response def choose_server(self, domain): @@ -196,7 +206,7 @@ def choose_server(self, domain): elif tld == 'money': return NICClient.MONEY_HOST elif tld == 'online': - return 'whois.nic.online' + return NICClient.ONLINE_HOST elif tld == 'cl': return NICClient.CL_HOST elif tld == 'ar':