mckabue commited on
Commit
f37aff9
·
1 Parent(s): 857226b

Enhance domain checking functionality and update Dockerfile

Browse files

- Added support for WHOIS and RDAP checks for domain availability.
- Implemented handling for unsupported TLDs.
- Updated Dockerfile to install necessary packages.
- Modified HTML and JavaScript for improved user experience and API integration.

Files changed (3) hide show
  1. Dockerfile +2 -0
  2. app.py +112 -64
  3. index.html +14 -10
Dockerfile CHANGED
@@ -11,6 +11,8 @@ COPY --chown=user ./requirements.txt requirements.txt
11
 
12
  RUN pip install --no-cache-dir --upgrade -r requirements.txt
13
 
 
 
14
  COPY --chown=user . /app
15
 
16
  CMD ["gunicorn", "-b", "0.0.0.0:7860", "app:app"]
 
11
 
12
  RUN pip install --no-cache-dir --upgrade -r requirements.txt
13
 
14
+ RUN apt-get -y update && apt-get -y install whois && apt-get -y install netbase
15
+
16
  COPY --chown=user . /app
17
 
18
  CMD ["gunicorn", "-b", "0.0.0.0:7860", "app:app"]
app.py CHANGED
@@ -5,8 +5,17 @@ import requests
5
  from urllib.parse import urlparse
6
  import dns.resolver
7
  import socket
 
 
 
8
 
9
  app = Flask(__name__)
 
 
 
 
 
 
10
 
11
  @app.route('/')
12
  def index():
@@ -16,95 +25,82 @@ def index():
16
  except Exception as e:
17
  return str(e)
18
 
19
- @app.route('/check', methods=['POST'])
20
- def check_domain():
21
  """Check domain availability"""
22
  try:
23
- domain = request.json.get('domain', '').strip().lower().strip('/')
24
  if '://' in domain:
25
  domain = urlparse(domain).netloc
26
- result = check_domain_availability(domain) or {}
27
- return {
28
- "domain": domain,
29
- "available": result.get("available"),
30
- "method": result.get("method", None),
31
- "error": result.get("error", None)
32
- }
33
- except Exception as e:
34
- return { "domain": domain, "error": str(e) }
 
 
 
 
 
 
 
 
 
 
35
 
36
  def check_domain_availability(domain):
37
  """Check domain availability using multiple methods."""
38
  # First try DNS resolution
39
- dns_exists, record_type = check_dns_records(domain)
40
- if dns_exists:
41
- return { "available": False, "method": f"DNS:{record_type}" }
42
 
43
  # Try RDAP
44
- rdap_status_code, rdap_base_url = check_rdap_records(domain)
45
- if rdap_status_code == 404:
46
- return { "available": True, "method": f"RDAP:{rdap_base_url}" }
47
- elif rdap_status_code == 200:
48
- return { "available": False, "method": f"RDAP:{rdap_base_url}" }
49
 
50
  # Fall back to WHOIS
51
- whois_server = get_whois_server(domain)
52
- if whois_server and no_whois_records(domain, whois_server):
53
- return {"available": True, "method": f"WHOIS:{whois_server}"}
54
-
55
- def get_whois_server(domain):
56
- """Get WHOIS server from IANA root zone database."""
57
- try:
58
- response = requests.get(f'https://www.iana.org/whois?q={domain}')
59
- if 'whois:' in response.text.lower():
60
- for line in response.text.split('\n'):
61
- if 'whois:' in line.lower():
62
- return line.split(':')[1].strip()
63
- except:
64
- pass
65
- return None
66
 
67
- def check_dns_records(domain):
68
  """Check if domain exists in DNS by looking for common record types."""
69
  # Check NS records first as they're required for valid domains
70
  for record_type in ['NS', 'A', 'AAAA', 'MX', 'CNAME']:
71
  try:
72
  dns.resolver.resolve(domain, record_type)
73
- return True, record_type
74
  except:
75
  continue
76
- return False, None
77
 
78
- def check_rdap_records(domain):
79
  try:
80
  bootstrap_url = "https://data.iana.org/rdap/dns.json"
81
  bootstrap_data = requests.get(bootstrap_url, timeout=5).json()
82
-
83
  tld = domain.split('.')[-1]
84
- rdap_base_url = None
85
-
86
- for service in bootstrap_data['services']:
87
- if tld in service[0]:
88
- rdap_base_url = service[1][0].strip('/')
89
- break
90
-
91
- if rdap_base_url:
92
- rdap_url = f"{rdap_base_url}/domain/{domain}"
93
- response = requests.get(rdap_url, timeout=5)
94
- return response.status_code, rdap_base_url
95
  except:
96
  pass
97
- return None, None
98
 
99
- def no_whois_records(domain, whois_server) -> bool:
100
  try:
101
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
102
- sock.settimeout(5)
103
- sock.connect((whois_server, 43))
104
- sock.send(f"{domain}\r\n".encode())
105
- response = sock.recv(4096).decode(errors='ignore')
106
- sock.close()
107
-
108
  available_patterns = [
109
  'no match',
110
  'not found',
@@ -115,11 +111,63 @@ def no_whois_records(domain, whois_server) -> bool:
115
  'status: free',
116
  'domain not found'
117
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
 
119
  response_lower = response.lower()
120
- for pattern in available_patterns:
121
- if pattern in response_lower:
122
- return True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  except:
124
  pass
125
- return False
 
5
  from urllib.parse import urlparse
6
  import dns.resolver
7
  import socket
8
+ import platform
9
+ import subprocess
10
+ from shutil import which
11
 
12
  app = Flask(__name__)
13
+ unsupported_TLDs = [
14
+ {
15
+ "tld": '.ly',
16
+ "check": "https://reg.ly/ly-domain/"
17
+ }
18
+ ]
19
 
20
  @app.route('/')
21
  def index():
 
25
  except Exception as e:
26
  return str(e)
27
 
28
+ @app.route('/check/<domain>', methods=['POST'])
29
+ def check_domain(domain: str):
30
  """Check domain availability"""
31
  try:
32
+ domain = domain.lower().strip('/').strip()
33
  if '://' in domain:
34
  domain = urlparse(domain).netloc
35
+
36
+ for unsupported_TLD in unsupported_TLDs:
37
+ if domain.endswith(unsupported_TLD.get('tld', '')):
38
+ return {
39
+ 'domain': domain,
40
+ "available": False,
41
+ "method": "Unsupported TLD"
42
+ }
43
+
44
+ result = check_domain_availability(domain)
45
+ if result:
46
+ return { "domain": domain, **result }
47
+ except:
48
+ pass
49
+ return {
50
+ 'domain': domain,
51
+ "available": False,
52
+ "method": "Cannot confirm if doimain is available"
53
+ }
54
 
55
  def check_domain_availability(domain):
56
  """Check domain availability using multiple methods."""
57
  # First try DNS resolution
58
+ is_available, availability_method, _continue = dns_is_available(domain)
59
+ if not _continue:
60
+ return { "available": is_available, "method": f"DNS:{availability_method}" }
61
 
62
  # Try RDAP
63
+ is_available, availability_method, _continue = rdap_is_available(domain)
64
+ if not _continue:
65
+ return { "available": is_available, "method": f"RDAP:{availability_method}" }
 
 
66
 
67
  # Fall back to WHOIS
68
+ is_available, availability_method, _continue = whois_is_available(domain)
69
+ if not _continue:
70
+ return {"available": is_available, "method": f"WHOIS:{availability_method}"}
 
 
 
 
 
 
 
 
 
 
 
 
71
 
72
+ def dns_is_available(domain):
73
  """Check if domain exists in DNS by looking for common record types."""
74
  # Check NS records first as they're required for valid domains
75
  for record_type in ['NS', 'A', 'AAAA', 'MX', 'CNAME']:
76
  try:
77
  dns.resolver.resolve(domain, record_type)
78
+ return False, record_type, False
79
  except:
80
  continue
81
+ return True, None, True
82
 
83
+ def rdap_is_available(domain):
84
  try:
85
  bootstrap_url = "https://data.iana.org/rdap/dns.json"
86
  bootstrap_data = requests.get(bootstrap_url, timeout=5).json()
 
87
  tld = domain.split('.')[-1]
88
+ services: list[tuple[list[str], list[str]]] = bootstrap_data['services']
89
+ for [tlds, rdap_base_urls] in services:
90
+ if tld in tlds:
91
+ for rdap_base_url in rdap_base_urls:
92
+ response = requests.get(
93
+ f"{rdap_base_url.lstrip('/')}/domain/{domain}", timeout=5)
94
+ if response.status_code == 404:
95
+ return True, rdap_base_url, False
96
+ elif response.status_code == 200:
97
+ return False, rdap_base_url, False
 
98
  except:
99
  pass
100
+ return False, None, True
101
 
102
+ def whois_is_available(domain) -> bool:
103
  try:
 
 
 
 
 
 
 
104
  available_patterns = [
105
  'no match',
106
  'not found',
 
111
  'status: free',
112
  'domain not found'
113
  ]
114
+ is_available_callback = lambda output: any(pattern in output for pattern in available_patterns)
115
+ is_available, availability_method = socket_whois_is_available(domain, is_available_callback)
116
+ if is_available:
117
+ return True, availability_method, False
118
+ is_available, availability_method = terminal_whois_is_available(domain, is_available_callback)
119
+ if is_available:
120
+ return True, availability_method, False
121
+ except:
122
+ pass
123
+ return False, None, True
124
+
125
+ def socket_whois_is_available(domain, is_available_callback):
126
+ try:
127
+ whois_server = get_whois_server(domain)
128
+ whois_server = "whois.reg.ly"
129
+
130
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
131
+ sock.settimeout(5)
132
+ sock.connect((whois_server, 43))
133
+ sock.send(f"{domain}\r\n".encode())
134
+ response = sock.recv(4096).decode(errors='ignore')
135
+ sock.close()
136
 
137
  response_lower = response.lower()
138
+ return is_available_callback(response_lower), whois_server
139
+ except:
140
+ pass
141
+ return False, None
142
+
143
+ def terminal_whois_is_available(domain, is_available_callback):
144
+ try:
145
+ # Check if OS is Linux
146
+ if platform.system().lower() == 'linux':
147
+ if which('whois') is not None:
148
+ # Run whois command with timeout
149
+ process = subprocess.Popen(
150
+ ['whois', domain],
151
+ stdout=subprocess.PIPE,
152
+ stderr=subprocess.PIPE)
153
+ try:
154
+ stdout, _ = process.communicate(timeout=60)
155
+ output = stdout.decode('utf-8', errors='ignore').lower()
156
+ return is_available_callback(output), "system whois"
157
+ except subprocess.TimeoutExpired:
158
+ process.kill()
159
+ except:
160
+ pass
161
+ return False, None
162
+
163
+ def get_whois_server(domain):
164
+ """Get WHOIS server from IANA root zone database."""
165
+ try:
166
+ response = requests.get(f'https://www.iana.org/whois?q={domain}')
167
+ if 'whois:' in response.text.lower():
168
+ for line in response.text.split('\n'):
169
+ if 'whois:' in line.lower():
170
+ return line.split(':')[1].strip()
171
  except:
172
  pass
173
+ return None
index.html CHANGED
@@ -13,8 +13,13 @@
13
  <div class="col-md-6">
14
  <div class="card">
15
  <div class="card-body">
16
- <h3 class="text-center mb-4">Private Domain Checker - ToKnow.ai</h3>
17
- <form id="searchForm" class="mb-3">
 
 
 
 
 
18
  <div class="input-group">
19
  <input type="text" id="domain" class="form-control" placeholder="Enter domain name...">
20
  <button class="btn btn-primary" type="submit">Check</button>
@@ -24,6 +29,11 @@
24
  <div class="spinner-border text-primary d-none" id="spinner"></div>
25
  <div id="resultText"></div>
26
  </div>
 
 
 
 
 
27
  </div>
28
  </div>
29
  </div>
@@ -43,19 +53,13 @@
43
  resultText.innerHTML = '';
44
 
45
  try {
46
- const response = await fetch('/check', {
47
- method: 'POST',
48
- headers: { 'Content-Type': 'application/json' },
49
- body: JSON.stringify({ domain })
50
- });
51
-
52
  const data = await response.json();
53
 
54
  resultText.innerHTML = `
55
  <div class="alert ${data.available ? 'alert-success' : 'alert-danger'} mt-3">
56
- <strong>${domain}</strong> is ${data.available ? 'available' : 'not available'}
57
  ${data.method ? `<br>(checked via ${data.method})` : ''}
58
- ${data.error ? `<br>(error: ${data.error})` : ''}
59
  </div>
60
  `;
61
  } catch (err) {
 
13
  <div class="col-md-6">
14
  <div class="card">
15
  <div class="card-body">
16
+ <h3 class="text-center">Private Domain Checker</h3>
17
+ <p class="text-center text-body-secondary">
18
+ <a href="https://toknow.ai" class="text-decoration-none text-reset">
19
+ ToKnow.ai
20
+ </a>
21
+ </p>
22
+ <form id="searchForm" class="mb-3 mt-4">
23
  <div class="input-group">
24
  <input type="text" id="domain" class="form-control" placeholder="Enter domain name...">
25
  <button class="btn btn-primary" type="submit">Check</button>
 
29
  <div class="spinner-border text-primary d-none" id="spinner"></div>
30
  <div id="resultText"></div>
31
  </div>
32
+ <p class="text-center text-body-secondary mt-4">
33
+ <a href="https://toknow.ai/about" class="text-decoration-none">
34
+ Get help at ToKnow.ai
35
+ </a>
36
+ </p>
37
  </div>
38
  </div>
39
  </div>
 
53
  resultText.innerHTML = '';
54
 
55
  try {
56
+ const response = await fetch(`/check/${domain}`, { method: 'POST' });
 
 
 
 
 
57
  const data = await response.json();
58
 
59
  resultText.innerHTML = `
60
  <div class="alert ${data.available ? 'alert-success' : 'alert-danger'} mt-3">
61
+ <strong>${data.domain ?? domain}</strong> is ${data.available ? 'available' : 'not available'}
62
  ${data.method ? `<br>(checked via ${data.method})` : ''}
 
63
  </div>
64
  `;
65
  } catch (err) {