diff --git a/public/assets/2021whois.js b/public/assets/2021whois.js
new file mode 100644
index 0000000..0429029
--- /dev/null
+++ b/public/assets/2021whois.js
@@ -0,0 +1,98 @@
+(function() {
+ var locked = false,
+ input = document.getElementById('lookup-input'),
+ submit = document.getElementById('lookup-submit'),
+ result = document.getElementById('lookup-result');
+
+ var lock = function() {
+ if(locked)
+ return false;
+
+ document.body.classList.add('whois-locked');
+ input.disabled = true;
+ locked = true;
+ return true;
+ }
+
+ var unlock = function() {
+ if(!locked)
+ return false;
+
+ document.body.classList.remove('whois-locked');
+ input.disabled = false;
+ locked = false;
+ return true;
+ }
+
+ var lookup = function(target) {
+ if(!lock())
+ return;
+
+ var xhr = new XMLHttpRequest;
+ xhr.addEventListener('load', function() {
+ var resp = JSON.parse(xhr.responseText);
+
+ if(resp.error) {
+ alert(resp.text);
+ } else {
+ var lastWhois = resp.result.responses[resp.result.responses.length - 1],
+ isIpAddr = false;
+
+ for(var i = 0; i < lastWhois.lines.length; ++i) {
+ var line = lastWhois.lines[i];
+ if(line.substring(0, 4) === 'inet') {
+ isIpAddr = true;
+ break;
+ }
+ }
+
+ var text = '';
+
+ if(isIpAddr) {
+ text += 'RESULTS FOUND: ' + resp.result.responses.length.toString() + "\r\n";
+ for(var i = 0; i < resp.result.responses.length; ++i) {
+ var response = resp.result.responses[i];
+ text += "\r\n-------------\r\n";
+ text += 'Lookup results for ' + resp.result.target + ' from ' + response.server + ":\r\n\r\n";
+ text += response.lines.join("\r\n").trim() + "\r\n";
+ }
+ } else {
+ for(var i = 0; i < resp.result.responses.length; ++i) {
+ var response = resp.result.responses[i];
+ text += resp.result.target + ' domain lookup result from ' + response.server + "\r\n\r\n";
+ text += response.lines.join("\r\n").trim() + "\r\n\r\n";
+ }
+ }
+
+ result.textContent = text;
+ }
+
+ unlock();
+ });
+ xhr.open('GET', '/whois/lookup?target=' + encodeURIComponent(target));
+ xhr.send();
+ }
+
+ var readHash = function() {
+ if(location.hash.length < 2) {
+ result.textContent = 'Enter a domain or IP address!';
+ return;
+ }
+
+ var target = decodeURIComponent(location.hash.substring(1));
+ if(input.value !== target)
+ input.value = target;
+ lookup(target);
+ };
+
+ window.addEventListener('hashchange', function(ev) {
+ readHash();
+ }, false);
+
+ submit.addEventListener('click', function(ev) {
+ ev.preventDefault();
+ location.hash = encodeURIComponent(input.value);
+ });
+
+ readHash();
+})();
diff --git a/public/css/2021.css b/public/css/2021.css
index c994cec..0e41c91 100644
--- a/public/css/2021.css
+++ b/public/css/2021.css
@@ -1121,3 +1121,64 @@ body {
max-width: 1200px;
margin: 0 auto;
}
+
+.whois-container {
+ max-width: 1200px;
+ margin: 0 auto;
+}
+
+.whois-lookup-form {
+ display: flex;
+ border-radius: 5px;
+ margin: 10px;
+ overflow: hidden;
+ box-shadow: 0 1px 5px #222;
+ transition: opacity .2s;
+}
+.whois-locked .whois-lookup-form {
+ opacity: .5;
+}
+.whois-lookup-form-input {
+ display: block;
+ flex: 1 1 auto;
+ padding: 10px;
+ background-color: #212121;
+ background-image: linear-gradient(0deg, #262626, #202020);
+ border-width: 0;
+ color: #fff;
+ font-family: Tahoma, Geneva, 'Dejavu Sans', Arial, Helvetica, sans-serif;
+}
+.whois-lookup-form-submit {
+ flex: 0 0 auto;
+ background-color: #333333;
+ background-image: linear-gradient(0deg, transparent, #404040);
+ color: #fff;
+ border-width: 0;
+ padding: 10px;
+ cursor: pointer;
+ border-radius: 0;
+ transition: background-color .2s;
+}
+.whois-lookup-form-submit:focus,
+.whois-lookup-form-submit:hover {
+ background-color: #3F3F3F;
+}
+.whois-lookup-form-submit:active {
+ background-color: #393939;
+}
+
+.whois-result {
+ margin: 10px;
+ padding: 6px 10px;
+ white-space: pre-wrap;
+ font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
+ font-size: 14px;
+ transition: opacity .2s;
+ background-color: #202020;
+ border-radius: 5px;
+ box-shadow: 0 1px 5px #222;
+ overflow: auto;
+}
+.whois-locked .whois-result {
+ opacity: .5;
+}
diff --git a/public/index.php b/public/index.php
index dfee66a..c7d7c0d 100644
--- a/public/index.php
+++ b/public/index.php
@@ -269,5 +269,49 @@ $router->get('/git_keys_rw', function() use ($db) {
return $body;
});
+$router->get('/whois', function() use ($ctx) {
+ return $ctx->getTemplating()->render('whois');
+});
+
+$router->get('/whois/lookup', function($response, $request) use ($ctx) {
+ $target = trim((string)$request->getParam('target'));
+ if(empty($target))
+ return [
+ 'error' => true,
+ 'text' => 'Missing input data.',
+ ];
+
+ $hash = hash('sha256', $target);
+ $prefix = 'fm:whois2:target:';
+ $source = '';
+
+ try {
+ $redis = new \Redis;
+ $redis->connect('/var/run/redis/redis-server.sock');
+
+ $result = $redis->get($prefix . $hash);
+
+ if(empty($result)) {
+ $client = new \Makai\Whois\WhoisClient;
+ $result = $client->lookup($target);
+ $redis->setEx($prefix . $hash, 1800, serialize($result));
+ $source = 'fresh';
+ } else {
+ $result = unserialize($result);
+ $source = 'cache';
+ }
+ } catch (\RuntimeException $ex) {
+ return [
+ 'error' => true,
+ 'text' => $ex->getMessage(),
+ ];
+ }
+
+ return [
+ 'error' => false,
+ 'source' => $source,
+ 'result' => $result,
+ ];
+});
$router->dispatch();
diff --git a/public/whois/index.php b/public/whois/index.php
deleted file mode 100644
index 2751f1f..0000000
--- a/public/whois/index.php
+++ /dev/null
@@ -1,110 +0,0 @@
-connect('/var/run/redis/redis-server.sock');
-
- $result = $redis->get(FMWHOIS_PREFIX . $domainHash);
-
- if(empty($result)) {
- $whois = new Whois\Client;
-
- try {
- $result = $whois->lookup($domain);
- $redis->setEx(FMWHOIS_PREFIX . $domainHash, 1800, json_encode($result));
- } catch (Whois\WhoisException $ex) {
- $error = $ex->getMessage();
- }
- } else {
- $result = json_decode($result);
- }
-}
-
-if(!empty($error)) {
- $responseText = $error;
-} elseif(!empty($result)) {
- $responseText = '';
-
- switch($result->type) {
- case 'domain':
- foreach($result->responses as $server => $response) {
- $responseText .= "{$result->target} domain lookup results from {$server}\r\n\r\n";
- $responseText .= trim($response) . "\r\n";
- }
- break;
-
- case 'ip':
- $responseText .= "RESULTS FOUND: " . count($result->responses);
-
- foreach($result->responses as $server => $response) {
- $responseText .= "-------------\r\n";
- $responseText .= "Lookup results for {$result->target} from {$server}:\r\n\r\n";
- $responseText .= trim($response) . "\r\n";
- }
- break;
-
- default:
- $responseText .= 'Something happened.';
- break;
- }
-} else {
- $responseText = 'Enter a domain or IP address!';
-}
-
-if(isset($_GET['ajax'])) {
- header('Content-Type: application/json; charset=utf-8');
-
- if(!is_object($result))
- $result = new stdClass;
-
- $result->responseText = $responseText;
- die(json_encode($result));
-}
-?>
-
-
-
-
- flash.moe whois
-
-
-
-
-
-
-
-
-
-
=htmlentities($responseText);?>
-
-
-
-
-
-
-
diff --git a/public/whois/script.js b/public/whois/script.js
deleted file mode 100644
index b5c7b8a..0000000
--- a/public/whois/script.js
+++ /dev/null
@@ -1,54 +0,0 @@
-var documentLocked = false;
-
-window.addEventListener('load', function() {
- document.getElementById('lookup-submit').addEventListener('click', function(ev) {
- ev.preventDefault();
- whoisLookup(document.getElementById('lookup-input').value);
- });
-});
-
-window.addEventListener('popstate', function(ev) {
- if(!ev.state)
- return;
- document.getElementById('lookup-input').value = ev.state.target || '';
- document.getElementById('lookup-result').innerText = ev.state.responseText || 'Enter a domain or IP address!';
-});
-
-function lockDocument() {
- if(documentLocked)
- return false;
-
- document.body.classList.add('locked');
- document.getElementById('lookup-input').disabled = true;
- documentLocked = true;
- return true;
-}
-
-function freeDocument() {
- if(!documentLocked)
- return false;
-
- document.body.classList.remove('locked');
- document.getElementById('lookup-input').disabled = false;
- documentLocked = false;
- return true;
-}
-
-function whoisLookup(domain) {
- if(!lockDocument())
- return;
-
- var xhr = new XMLHttpRequest;
- xhr.addEventListener('readystatechange', function() {
- if(xhr.readyState != 4)
- return;
-
- var response = JSON.parse(xhr.responseText);
-
- history.pushState(response, null, location.pathname + '?domain=' + encodeURIComponent(domain));
- document.getElementById('lookup-result').innerText = response.responseText;
- freeDocument();
- });
- xhr.open('GET', whoisUrl + '?ajax=1&domain=' + encodeURIComponent(domain));
- xhr.send();
-}
diff --git a/public/whois/style.css b/public/whois/style.css
deleted file mode 100644
index 2499770..0000000
--- a/public/whois/style.css
+++ /dev/null
@@ -1,129 +0,0 @@
-* {
- margin: 0;
- padding: 0;
- box-sizing: border-box;
- position: relative;
- outline-style: none;
- scrollbar-color: #4a3650 #111;
-}
-
-html,
-body {
- width: 100%;
- height: 100%;
-}
-
-body {
- font-family: Tahoma, Geneva, 'Dejavu Sans', Arial, Helvetica, sans-serif;
- font-size: 12px;
- line-height: 20px;
- background-color: #111;
- color: #fff;
- padding: 1px;
-}
-
-.container {
- max-width: 1020px;
- margin: 0 auto;
-}
-
-.header {
- display: flex;
- justify-content: space-between;
- margin: 10px;
-}
-.title {
- font-size: 20px;
- line-height: 25px;
-}
-.nav {
- display: flex;
- font-size: 16px;
- line-height: 25px;
-}
-.nav a {
- color: #ccc;
- text-decoration: none;
- padding: 2px 5px;
- transition: color .2s, text-shadow .2s;
-}
-.nav a:hover,
-.nav a:focus {
- color: #fff;
- text-shadow: 0 0 10px #fcfcfc;
-}
-.nav a:active {
- color: #ddd;
- text-shadow: 0 0 6px #fcfcfc;
-}
-
-.lookup-form {
- display: flex;
- border-radius: 5px;
- margin: 10px;
- overflow: hidden;
- box-shadow: 0 1px 5px #222;
- transition: opacity .2s;
-}
-.locked .lookup-form {
- opacity: .5;
-}
-.lookup-form-input {
- display: block;
- flex: 1 1 auto;
- padding: 10px;
- background-color: #212121;
- background-image: linear-gradient(0deg, #262626, #202020);
- border-width: 0;
- color: #fff;
- font-family: Tahoma, Geneva, 'Dejavu Sans', Arial, Helvetica, sans-serif;
-}
-.lookup-form-submit {
- flex: 0 0 auto;
- background-color: #333333;
- background-image: linear-gradient(0deg, transparent, #404040);
- color: #fff;
- border-width: 0;
- padding: 10px;
- cursor: pointer;
- border-radius: 0;
- transition: background-color .2s;
-}
-.lookup-form-submit:focus,
-.lookup-form-submit:hover {
- background-color: #3F3F3F;
-}
-.lookup-form-submit:active {
- background-color: #393939;
-}
-
-.result {
- margin: 10px;
- padding: 6px 10px;
- white-space: pre-wrap;
- font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
- font-size: 14px;
- transition: opacity .2s;
- background-color: #202020;
- border-radius: 5px;
- box-shadow: 0 1px 5px #222;
- overflow: auto;
-}
-.locked .result {
- opacity: .5;
-}
-
-.copy {
- font-size: 11px;
- line-height: 16px;
- text-align: center;
- margin: 10px;
-}
-.copy a {
- color: inherit;
- text-decoration: none;
-}
-.copy a:hover,
-.copy a:focus {
- text-decoration: underline;
-}
diff --git a/public/whois/whois-php/src/Whois/Client.php b/public/whois/whois-php/src/Whois/Client.php
deleted file mode 100644
index 92bd951..0000000
--- a/public/whois/whois-php/src/Whois/Client.php
+++ /dev/null
@@ -1,234 +0,0 @@
-servers = new $servers;
- }
-
- /**
- * Loop up a target (ip/domain) on whois
- *
- * @param string $target The target domain/ip.
- *
- * @throws WhoisException if the given target doesn't validate as an IP or domain.
- *
- * @return Result The response from the whois server
- */
- public function lookup($target)
- {
- // Check if IP
- if (filter_var($target, FILTER_VALIDATE_IP)) {
- return $this->lookupIP($target);
- }
-
- // Check if domain
- if ($this->verifyDomainName($target)) {
- return $this->lookupDomain($target);
- }
-
- // Throw an error if both are false
- throw new WhoisException('The target supplied does not appear to be a valid IP or domain name.');
- }
-
- private function lookupDomain($target)
- {
- // Make target completely lowercase
- $target = strtolower($target);
-
- // Split the dots
- $targetSplit = explode('.', $target);
-
- // Create server variable
- $server = null;
-
- // Select a server
- while (count($targetSplit)) {
- // Glue array
- $tld = implode('.', $targetSplit);
-
- // Check if it exists in the tld servers variable
- if (array_key_exists($tld, $this->servers->tld)) {
- $server = $this->servers->tld[$tld];
- break;
- }
-
- // Remove first entry
- array_shift($targetSplit);
- }
-
- // If we get here and $server is still null throw an exception
- if ($server === null) {
- throw new WhoisException('No whois server found for this domain.');
- }
-
- // Create a Result object
- $result = new Result();
-
- // Set the domain
- $result->target = $target;
-
- // Set the type
- $result->type = 'domain';
-
- // Create responses container
- $responses = [];
-
- // Query the server
- $response = $this->query($target, $server);
-
- // Process the data if anything was returned
- if ($response) {
- // Add the response to the array
- $responses[$server] = $response;
-
- // Check if there's a secondary whois server
- if ($position = strpos(strtolower($response), 'whois server:')) {
- // Grab the uri from the response
- preg_match("/whois server: (.*)/", strtolower($response), $matches);
-
- // Check if it's not the same server
- if (isset($matches[1]) && trim(strtolower($server)) !== trim($matches[1]) && strpos($matches[1], ':') === false) {
- // Set the secondary server
- $second = trim($matches[1]);
-
- // Do a query
- $response = $this->query($target, $second);
-
- // Check if that was something
- if ($response) {
- // Add it to the responses
- $responses[$second] = $response;
- }
- }
- }
- }
-
- // Set the count
- $result->count = count($responses);
-
- // Set the responses
- $result->responses = array_reverse($responses);
-
- // Return the result
- return $result;
- }
-
- /**
- * Whois an IP address.
- *
- * @param string $address The IP address to query.
- *
- * @return Result The whois results.
- */
- private function lookupIP($address)
- {
- // Create the responses storage array
- $responses = [];
-
- // Query every server in the IP list
- foreach ($this->servers->ip as $server) {
- // Check if we haven't queried this server yet
- if (array_key_exists($server, $responses)) {
- continue;
- }
-
- // Query the server
- $responses[$server] = $this->query($address, $server);
- }
-
- // Create a result object
- $result = new Result();
-
- // Set target
- $result->target = $address;
-
- // Set the type
- $result->type = 'ip';
-
- // Set response count
- $result->count = count($responses);
-
- // Set responses
- $result->responses = array_reverse($responses);
-
- // Return the Result object
- return $result;
- }
-
- /**
- * Validates a domain name.
- *
- * @param string $domain The string to validate as a domain.
- *
- * @return bool will be positive if the string is a domain.
- */
- private function verifyDomainName($domain)
- {
- return (preg_match("/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i", $domain) // Valid chars check
- && preg_match("/^.{1,253}$/", $domain) // Overall length check
- && preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain)); // Length of each label
- }
-
- /**
- * Query the whois server.
- *
- * @param string $target The target IP/domain.
- * @param string $server The server to be queried.
- * @param int $port The port for the whois server.
- * @param int $timeout The timeout.
- *
- * @throws WhoisException if the socket failed to open.
- *
- * @return string The response from the whois server.
- */
- private function query($target, $server, $port = 43, $timeout = 5)
- {
- // Create the socket
- $sock = @fsockopen($server, $port, $errno, $errstr, $timeout);
-
- // Check for errors
- if (!$sock) {
- // Throw an exception with the error string
- throw new WhoisException($errstr);
- }
-
- // Write the target to the socket
- fwrite($sock, $target . "\r\n");
-
- // Create storage variable
- $response = '';
-
- // Await output
- while ($line = fgets($sock)) {
- $response .= $line;
- }
-
- // Close the socket
- fclose($sock);
-
- // Return the response
- return $response;
- }
-}
diff --git a/public/whois/whois-php/src/Whois/Result.php b/public/whois/whois-php/src/Whois/Result.php
deleted file mode 100644
index 3035805..0000000
--- a/public/whois/whois-php/src/Whois/Result.php
+++ /dev/null
@@ -1,34 +0,0 @@
- 'whois.nic.ac',
- 'ac' => 'whois.nic.ac',
- 'academy' => 'whois.donuts.co',
- 'accountants' => 'whois.donuts.co',
- 'active' => 'whois.afilias-srs.net',
- 'actor' => 'whois.unitedtld.com',
- 'actor' => 'whois.unitedtld.com',
- 'ad' => 'whois.ripe.net',
- 'ae' => 'whois.nic.ae',
- 'aero' => 'whois.aero',
- 'af' => 'whois.nic.af',
- 'ag' => 'whois.nic.ag',
- 'agency' => 'whois.donuts.co',
- 'ai' => 'whois.ai',
- 'airforce' => 'whois.unitedtld.com',
- 'al' => 'whois.ripe.net',
- 'allfinanz' => 'whois.ksregistry.net',
- 'alsace' => 'whois-alsace.nic.fr',
- 'am' => 'whois.amnic.net',
- 'army' => 'whois.rightside.co',
- 'arpa' => 'whois.iana.org',
- 'as' => 'whois.nic.as',
- 'asia' => 'whois.nic.asia',
- 'associates' => 'whois.donuts.co',
- 'at' => 'whois.nic.at',
- 'attorney' => 'whois.rightside.co',
- 'au' => 'whois.audns.net.au',
- 'auction' => 'whois.unitedtld.com',
- 'audio' => 'whois.uniregistry.net',
- 'autos' => 'whois.afilias-srs.net',
- 'aw' => 'whois.nic.aw',
- 'ax' => 'whois.ax',
- 'az' => 'whois.ripe.net',
- 'band' => 'whois.rightside.co',
- 'bar' => 'whois.nic.bar',
- 'bargains' => 'whois.donuts.co',
- 'bayern' => 'whois-dub.mm-registry.com',
- 'be' => 'whois.dns.be',
- 'beer' => 'whois-dub.mm-registry.com',
- 'berlin' => 'whois.nic.berlin',
- 'best' => 'whois.nic.best',
- 'bg' => 'whois.register.bg',
- 'bi' => 'whois.nic.bi',
- 'bike' => 'whois.donuts.co',
- 'bio' => 'whois.ksregistry.net',
- 'biz' => 'whois.biz',
- 'bj' => 'whois.nic.bj',
- 'black' => 'whois.afilias.net',
- 'blackfriday' => 'whois.uniregistry.net',
- 'blue' => 'whois.afilias.net',
- 'bmw' => 'whois.ksregistry.net',
- 'bn' => 'whois.bn',
- 'bnpparibas' => 'whois.afilias-srs.net',
- 'bo' => 'whois.nic.bo',
- 'boo' => 'domain-registry-whois.l.google.com',
- 'boutique' => 'whois.donuts.co',
- 'br' => 'whois.nic.br',
- 'brussels' => 'whois.nic.brussels',
- 'bt' => 'whois.netnames.net',
- 'budapest' => 'whois-dub.mm-registry.com',
- 'build' => 'whois.nic.build',
- 'builders' => 'whois.donuts.co',
- 'business' => 'whois.donuts.co',
- 'business' => 'whois.donuts.co',
- 'bw' => 'whois.nic.net.bw',
- 'by' => 'whois.cctld.by',
- 'bz' => 'whois.belizenic.bz',
- 'bzh' => 'whois-bzh.nic.fr',
- 'ca' => 'whois.cira.ca',
- 'cab' => 'whois.donuts.co',
- 'cal' => 'domain-registry-whois.l.google.com',
- 'camera' => 'whois.donuts.co',
- 'camp' => 'whois.donuts.co',
- 'cancerresearch' => 'whois.nic.cancerresearch',
- 'capetown' => 'capetown-whois.registry.net.za',
- 'capital' => 'whois.donuts.co',
- 'cards' => 'whois.donuts.co',
- 'care' => 'whois.donuts.co',
- 'career' => 'whois.nic.career',
- 'careers' => 'whois.donuts.co',
- 'casa' => 'whois-dub.mm-registry.com',
- 'cash' => 'whois.donuts.co',
- 'cat' => 'whois.cat',
- 'catering' => 'whois.donuts.co',
- 'cc' => 'ccwhois.verisign-grs.com',
- 'cd' => 'whois.nic.cd',
- 'center' => 'whois.donuts.co',
- 'ceo' => 'whois.nic.ceo',
- 'cern' => 'whois.afilias-srs.net',
- 'cf' => 'whois.dot.cf',
- 'ch' => 'whois.nic.ch',
- 'channel' => 'domain-registry-whois.l.google.com',
- 'cheap' => 'whois.donuts.co',
- 'christmas' => 'whois.uniregistry.net',
- 'chrome' => 'domain-registry-whois.l.google.com',
- 'church' => 'whois.donuts.co',
- 'ci' => 'whois.nic.ci',
- 'city' => 'whois.donuts.co',
- 'ck' => 'whois.nic.ck',
- 'cl' => 'whois.nic.cl',
- 'claims' => 'whois.donuts.co',
- 'cleaning' => 'whois.donuts.co',
- 'click' => 'whois.uniregistry.net',
- 'clinic' => 'whois.donuts.co',
- 'clothing' => 'whois.donuts.co',
- 'club' => 'whois.nic.club',
- 'cn' => 'whois.cnnic.net.cn',
- 'co' => 'whois.co',
- 'codes' => 'whois.donuts.co',
- 'coffee' => 'whois.donuts.co',
- 'college' => 'whois.centralnic.com',
- 'cologne' => 'whois-fe1.pdt.cologne.tango.knipp.de',
- 'com' => 'whois.verisign-grs.com',
- 'community' => 'whois.donuts.co',
- 'company' => 'whois.donuts.co',
- 'computer' => 'whois.donuts.co',
- 'condos' => 'whois.donuts.co',
- 'construction' => 'whois.donuts.co',
- 'consulting' => 'whois.unitedtld.com',
- 'contractors' => 'whois.donuts.co',
- 'cooking' => 'whois-dub.mm-registry.com',
- 'cool' => 'whois.donuts.co',
- 'coop' => 'whois.nic.coop',
- 'country' => 'whois-dub.mm-registry.com',
- 'credit' => 'whois.donuts.co',
- 'creditcard' => 'whois.donuts.co',
- 'cruises' => 'whois.donuts.co',
- 'cuisinella' => 'whois.nic.cuisinella',
- 'cx' => 'whois.nic.cx',
- 'cymru' => 'whois.nic.cymru',
- 'cz' => 'whois.nic.cz',
- 'dad' => 'domain-registry-whois.l.google.com',
- 'dance' => 'whois.unitedtld.com',
- 'dating' => 'whois.donuts.co',
- 'day' => 'domain-registry-whois.l.google.com',
- 'de' => 'whois.denic.de',
- 'deals' => 'whois.donuts.co',
- 'democrat' => 'whois.rightside.co',
- 'degree' => 'whois.unitedtld.com',
- 'dental' => 'whois.donuts.co',
- 'dentist' => 'whois.rightside.co',
- 'desi' => 'whois.ksregistry.net',
- 'diamonds' => 'whois.donuts.co',
- 'diet' => 'whois.uniregistry.net',
- 'digital' => 'whois.donuts.co',
- 'direct' => 'whois.donuts.co',
- 'directory' => 'whois.donuts.co',
- 'discount' => 'whois.donuts.co',
- 'dk' => 'whois.dk-hostmaster.dk',
- 'dm' => 'whois.nic.dm',
- 'domains' => 'whois.donuts.co',
- 'durban' => 'durban-whois.registry.net.za',
- 'dvag' => 'whois.ksregistry.net',
- 'dz' => 'whois.nic.dz',
- 'eat' => 'domain-registry-whois.l.google.com',
- 'ec' => 'whois.nic.ec',
- 'edu' => 'whois.educause.edu',
- 'education' => 'whois.donuts.co',
- 'ee' => 'whois.eenet.ee',
- 'eg' => 'whois.ripe.net',
- 'email' => 'whois.donuts.co',
- 'engineer' => 'whois.rightside.co',
- 'engineering' => 'whois.donuts.co',
- 'enterprises' => 'whois.donuts.co',
- 'equipment' => 'whois.donuts.co',
- 'es' => 'whois.nic.es',
- 'esq' => 'domain-registry-whois.l.google.com',
- 'estate' => 'whois.donuts.co',
- 'eu' => 'whois.eu',
- 'eus' => 'whois.eus.coreregistry.net',
- 'events' => 'whois.donuts.co',
- 'exchange' => 'whois.donuts.co',
- 'expert' => 'whois.donuts.co',
- 'exposed' => 'whois.donuts.co',
- 'fail' => 'whois.donuts.co',
- 'farm' => 'whois.donuts.co',
- 'feedback' => 'whois.centralnic.com',
- 'fi' => 'whois.ficora.fi',
- 'finance' => 'whois.donuts.co',
- 'financial' => 'whois.donuts.co',
- 'fish' => 'whois.donuts.co',
- 'fishing' => 'whois-dub.mm-registry.com',
- 'fitness' => 'whois.donuts.co',
- 'flights' => 'whois.donuts.co',
- 'florist' => 'whois.donuts.co',
- 'flsmidth' => 'whois.ksregistry.net',
- 'fly' => 'domain-registry-whois.l.google.com',
- 'fo' => 'whois.nic.fo',
- 'foo' => 'domain-registry-whois.l.google.com',
- 'forsale' => 'whois.unitedtld.com',
- 'foundation' => 'whois.donuts.co',
- 'fr' => 'whois.nic.fr',
- 'frl' => 'whois.nic.frl',
- 'frogans' => 'whois-frogans.nic.fr',
- 'fund' => 'whois.donuts.co',
- 'furniture' => 'whois.donuts.co',
- 'futbol' => 'whois.unitedtld.com',
- 'gal' => 'whois.gal.coreregistry.net',
- 'gallery' => 'whois.donuts.co',
- 'gbiz' => 'domain-registry-whois.l.google.com',
- 'gd' => 'whois.nic.gd',
- 'gent' => 'whois.nic.gent',
- 'gg' => 'whois.channelisles.net',
- 'gi' => 'whois2.afilias-grs.net',
- 'gift' => 'whois.uniregistry.net',
- 'gifts' => 'whois.donuts.co',
- 'gives' => 'whois.rightside.co',
- 'gl' => 'whois.nic.gl',
- 'glass' => 'whois.donuts.co',
- 'gle' => 'domain-registry-whois.l.google.com',
- 'global' => 'whois.afilias-srs.net',
- 'globo' => 'whois.gtlds.nic.br',
- 'gmail' => 'domain-registry-whois.l.google.com',
- 'gmx' => 'whois-fe1.gmx.tango.knipp.de',
- 'google' => 'domain-registry-whois.l.google.com',
- 'gop' => 'whois-cl01.mm-registry.com',
- 'gov' => 'whois.nic.gov',
- 'gq' => 'whois.dominio.gq',
- 'gr' => 'whois.ripe.net',
- 'gratis' => 'whois.donuts.co',
- 'green' => 'whois.afilias.net',
- 'gripe' => 'whois.donuts.co',
- 'gs' => 'whois.nic.gs',
- 'guide' => 'whois.donuts.co',
- 'guitars' => 'whois.uniregistry.net',
- 'guru' => 'whois.donuts.co',
- 'gy' => 'whois.registry.gy',
- 'hamburg' => 'whois.nic.hamburg',
- 'haus' => 'whois.unitedtld.com',
- 'healthcare' => 'whois.donuts.co',
- 'help' => 'whois.uniregistry.net',
- 'here' => 'domain-registry-whois.l.google.com',
- 'hiphop' => 'whois.uniregistry.net',
- 'hiv' => 'whois.afilias-srs.net',
- 'hk' => 'whois.hkirc.hk',
- 'hn' => 'whois.nic.hn',
- 'holdings' => 'whois.donuts.co',
- 'holiday' => 'whois.donuts.co',
- 'homes' => 'whois.afilias-srs.net',
- 'horse' => 'whois-dub.mm-registry.com',
- 'host' => 'whois.nic.host',
- 'hosting' => 'whois.uniregistry.net',
- 'house' => 'whois.donuts.co',
- 'how' => 'domain-registry-whois.l.google.com',
- 'hr' => 'whois.dns.hr',
- 'ht' => 'whois.nic.ht',
- 'hu' => 'whois.nic.hu',
- 'ibm' => 'whois.nic.ibm',
- 'id' => 'whois.pandi.or.id',
- 'ie' => 'whois.domainregistry.ie',
- 'il' => 'whois.isoc.org.il',
- 'im' => 'whois.nic.im',
- 'immo' => 'whois.donuts.co',
- 'immobilien' => 'whois.unitedtld.com',
- 'in' => 'whois.inregistry.net',
- 'industries' => 'whois.donuts.co',
- 'info' => 'whois.afilias.net',
- 'ing' => 'domain-registry-whois.l.google.com',
- 'ink' => 'whois.centralnic.com',
- 'institute' => 'whois.donuts.co',
- 'insure' => 'whois.donuts.co',
- 'int' => 'whois.iana.org',
- 'international' => 'whois.donuts.co',
- 'investments' => 'whois.donuts.co',
- 'io' => 'whois.nic.io',
- 'iq' => 'whois.cmc.iq',
- 'ir' => 'whois.nic.ir',
- 'is' => 'whois.isnic.is',
- 'it' => 'whois.nic.it',
- 'je' => 'whois.channelisles.net',
- 'jobs' => 'jobswhois.verisign-grs.com',
- 'joburg' => 'joburg-whois.registry.net.za',
- 'jp' => 'whois.jprs.jp',
- 'juegos' => 'whois.uniregistry.net',
- 'kaufen' => 'whois.unitedtld.com',
- 'ke' => 'whois.kenic.or.ke',
- 'kg' => 'www.domain.kg',
- 'ki' => 'whois.nic.ki',
- 'kim' => 'whois.afilias.net',
- 'kitchen' => 'whois.donuts.co',
- 'kiwi' => 'whois.nic.kiwi',
- 'koeln' => 'whois-fe1.pdt.koeln.tango.knipp.de',
- 'kr' => 'whois.kr',
- 'krd' => 'whois.aridnrs.net.au',
- 'kz' => 'whois.nic.kz',
- 'la' => 'whois.nic.la',
- 'lacaixa' => 'whois.nic.lacaixa',
- 'land' => 'whois.donuts.co',
- 'lawyer' => 'whois.rightside.co',
- 'lease' => 'whois.donuts.co',
- 'lgbt' => 'whois.afilias.net',
- 'li' => 'whois.nic.li',
- 'life' => 'whois.donuts.co',
- 'lighting' => 'whois.donuts.co',
- 'limited' => 'whois.donuts.co',
- 'limo' => 'whois.donuts.co',
- 'link' => 'whois.uniregistry.net',
- 'loans' => 'whois.donuts.co',
- 'london' => 'whois-lon.mm-registry.com',
- 'lotto' => 'whois.afilias.net',
- 'love' => 'whois.nic.love',
- 'lt' => 'whois.domreg.lt',
- 'ltda' => 'whois.afilias-srs.net',
- 'lu' => 'whois.dns.lu',
- 'luxe' => 'whois-dub.mm-registry.com',
- 'luxury' => 'whois.nic.luxury',
- 'lv' => 'whois.nic.lv',
- 'ly' => 'whois.nic.ly',
- 'ma' => 'whois.iam.net.ma',
- 'maison' => 'whois.donuts.co',
- 'management' => 'whois.donuts.co',
- 'mango' => 'whois.mango.coreregistry.net',
- 'market' => 'whois.rightside.co',
- 'marketing' => 'whois.donuts.co',
- 'md' => 'whois.nic.md',
- 'me' => 'whois.nic.me',
- 'media' => 'whois.donuts.co',
- 'meet' => 'whois.afilias.net',
- 'melbourne' => 'whois.aridnrs.net.au',
- 'meme' => 'domain-registry-whois.l.google.com',
- 'menu' => 'whois.nic.menu',
- 'mg' => 'whois.nic.mg',
- 'miami' => 'whois-dub.mm-registry.com',
- 'mil' => 'whois.internic.net',
- 'mini' => 'whois.ksregistry.net',
- 'mk' => 'whois.marnet.mk',
- 'ml' => 'whois.dot.ml',
- 'mn' => 'whois.nic.mn',
- 'mo' => 'whois.monic.mo',
- 'mobi' => 'whois.dotmobiregistry.net',
- 'moda' => 'whois.unitedtld.com',
- 'moe' => 'whois.nic.moe',
- 'monash' => 'whois.nic.monash',
- 'mortgage' => 'whois.rightside.co',
- 'moscow' => 'whois.nic.moscow',
- 'motorcycles' => 'whois.afilias-srs.net',
- 'mov' => 'domain-registry-whois.l.google.com',
- 'mp' => 'whois.nic.mp',
- 'ms' => 'whois.nic.ms',
- 'mu' => 'whois.nic.mu',
- 'museum' => 'whois.museum',
- 'mx' => 'whois.mx',
- 'my' => 'whois.mynic.my',
- 'mz' => 'whois.nic.mz',
- 'na' => 'whois.na-nic.com.na',
- 'name' => 'whois.nic.name',
- 'navy' => 'whois.rightside.co',
- 'nc' => 'whois.nc',
- 'net' => 'whois.verisign-grs.net',
- 'network' => 'whois.donuts.co',
- 'new' => 'domain-registry-whois.l.google.com',
- 'nexus' => 'domain-registry-whois.l.google.com',
- 'nf' => 'whois.nic.nf',
- 'ng' => 'whois.nic.net.ng',
- 'ngo' => 'whois.publicinterestregistry.net',
- 'ninja' => 'whois.unitedtld.com',
- 'nl' => 'whois.domain-registry.nl',
- 'no' => 'whois.norid.no',
- 'nra' => 'whois.afilias-srs.net',
- 'nrw' => 'whois.nic.nrw',
- 'nu' => 'whois.nic.nu',
- 'nz' => 'whois.srs.net.nz',
- 'om' => 'whois.registry.om',
- 'one' => 'whois.nic.one',
- 'ong' => 'whois.publicinterestregistry.net',
- 'onl' => 'whois.afilias-srs.net',
- 'ooo' => 'whois.nic.ooo',
- 'org' => 'whois.pir.org',
- 'organic' => 'whois.afilias.net',
- 'ovh' => 'whois-ovh.nic.fr',
- 'paris' => 'whois-paris.nic.fr',
- 'partners' => 'whois.donuts.co',
- 'parts' => 'whois.donuts.co',
- 'pe' => 'kero.yachay.pe',
- 'pf' => 'whois.registry.pf',
- 'photo' => 'whois.uniregistry.net',
- 'photography' => 'whois.donuts.co',
- 'photos' => 'whois.donuts.co',
- 'physio' => 'whois.nic.physio',
- 'pics' => 'whois.uniregistry.net',
- 'pictures' => 'whois.donuts.co',
- 'pink' => 'whois.afilias.net',
- 'pizza' => 'whois.donuts.co',
- 'pl' => 'whois.dns.pl',
- 'place' => 'whois.donuts.co',
- 'plumbing' => 'whois.donuts.co',
- 'pm' => 'whois.nic.pm',
- 'pohl' => 'whois.ksregistry.net',
- 'poker' => 'whois.afilias.net',
- 'post' => 'whois.dotpostregistry.net',
- 'pr' => 'whois.nic.pr',
- 'press' => 'whois.nic.press',
- 'pro' => 'whois.nic.pro',
- 'prod' => 'domain-registry-whois.l.google.com',
- 'productions' => 'whois.donuts.co',
- 'prof' => 'domain-registry-whois.l.google.com',
- 'properties' => 'whois.donuts.co',
- 'property' => 'whois.uniregistry.net',
- 'pt' => 'whois.dns.pt',
- 'pub' => 'whois.unitedtld.com',
- 'pw' => 'whois.nic.pw',
- 'qa' => 'whois.registry.qa',
- 'quebec' => 'whois.quebec.rs.corenic.net',
- 're' => 'whois.nic.re',
- 'recipes' => 'whois.donuts.co',
- 'red' => 'whois.afilias.net',
- 'rehab' => 'whois.rightside.co',
- 'reise' => 'whois.nic.reise',
- 'reisen' => 'whois.donuts.co',
- 'rentals' => 'whois.donuts.co',
- 'repair' => 'whois.donuts.co',
- 'report' => 'whois.donuts.co',
- 'republican' => 'whois.rightside.co',
- 'rest' => 'whois.centralnic.com',
- 'restaurant' => 'whois.donuts.co',
- 'reviews' => 'whois.unitedtld.com',
- 'rich' => 'whois.afilias-srs.net',
- 'rio' => 'whois.gtlds.nic.br',
- 'rip' => 'whois.rightside.co',
- 'ro' => 'whois.rotld.ro',
- 'rocks' => 'whois.unitedtld.com',
- 'rodeo' => 'whois-dub.mm-registry.com',
- 'rs' => 'whois.rnids.rs',
- 'rsvp' => 'domain-registry-whois.l.google.com',
- 'ru' => 'whois.ripn.net',
- 'ruhr' => 'whois.nic.ruhr',
- 'sa' => 'whois.nic.net.sa',
- 'saarland' => 'whois.ksregistry.net',
- 'sarl' => 'whois.donuts.co',
- 'sb' => 'whois.nic.net.sb',
- 'sc' => 'whois2.afilias-grs.net',
- 'sca' => 'whois.nic.scb',
- 'schmidt' => 'whois.nic.schmidt',
- 'schule' => 'whois.donuts.co',
- 'scot' => 'whois.scot.coreregistry.net',
- 'se' => 'whois.iis.se',
- 'services' => 'whois.donuts.co',
- 'sexy' => 'whois.uniregistry.net',
- 'sg' => 'whois.nic.net.sg',
- 'sh' => 'whois.nic.sh',
- 'shiksha' => 'whois.afilias.net',
- 'shoes' => 'whois.donuts.co',
- 'si' => 'whois.arnes.si',
- 'singles' => 'whois.donuts.co',
- 'sk' => 'whois.sk-nic.sk',
- 'sm' => 'whois.nic.sm',
- 'sn' => 'whois.nic.sn',
- 'so' => 'whois.nic.so',
- 'social' => 'whois.unitedtld.com',
- 'software' => 'whois.rightside.co',
- 'solar' => 'whois.donuts.co',
- 'solutions' => 'whois.donuts.co',
- 'soy' => 'domain-registry-whois.l.google.com',
- 'space' => 'whois.nic.space',
- 'spiegel' => 'whois.ksregistry.net',
- 'st' => 'whois.nic.st',
- 'store' => 'whois.nic.store',
- 'su' => 'whois.tcinet.ru',
- 'supplies' => 'whois.donuts.co',
- 'supply' => 'whois.donuts.co',
- 'support' => 'whois.donuts.co',
- 'surf' => 'whois-dub.mm-registry.com',
- 'surgery' => 'whois.donuts.co',
- 'sx' => 'whois.sx',
- 'sy' => 'whois.tld.sy',
- 'systems' => 'whois.donuts.co',
- 'tatar' => 'whois.nic.tatar',
- 'tattoo' => 'whois.uniregistry.net',
- 'tax' => 'whois.donuts.co',
- 'tc' => 'whois.meridiantld.net',
- 'technology' => 'whois.donuts.co',
- 'tel' => 'whois.nic.tel',
- 'tf' => 'whois.nic.tf',
- 'th' => 'whois.thnic.co.th',
- 'tienda' => 'whois.donuts.co',
- 'tips' => 'whois.donuts.co',
- 'tirol' => 'whois.nic.tirol',
- 'tj' => 'whois.nic.tj',
- 'tk' => 'whois.dot.tk',
- 'tl' => 'whois.nic.tl',
- 'tm' => 'whois.nic.tm',
- 'tn' => 'whois.ati.tn',
- 'to' => 'whois.tonic.to',
- 'today' => 'whois.donuts.co',
- 'tools' => 'whois.donuts.co',
- 'top' => 'whois.nic.top',
- 'town' => 'whois.donuts.co',
- 'toys' => 'whois.donuts.co',
- 'tp' => 'whois.nic.tl',
- 'tr' => 'whois.nic.tr',
- 'training' => 'whois.donuts.co',
- 'travel' => 'whois.nic.travel',
- 'tui' => 'whois.ksregistry.net',
- 'tv' => 'tvwhois.verisign-grs.com',
- 'tw' => 'whois.twnic.net.tw',
- 'tz' => 'whois.tznic.or.tz',
- 'ua' => 'whois.ua',
- 'ug' => 'whois.co.ug',
- 'uk' => 'whois.nic.uk',
- 'university' => 'whois.donuts.co',
- 'uol' => 'whois.gtlds.nic.br',
- 'us' => 'whois.nic.us',
- 'uy' => 'whois.nic.org.uy',
- 'uz' => 'whois.cctld.uz',
- 'vacations' => 'whois.donuts.co',
- 'vc' => 'whois2.afilias-grs.net',
- 've' => 'whois.nic.ve',
- 'vegas' => 'whois.afilias-srs.net',
- 'ventures' => 'whois.donuts.co',
- 'vermögensberater' => 'whois.ksregistry.net',
- 'vermögensberatung' => 'whois.ksregistry.net',
- 'versicherung' => 'whois.nic.versicherung',
- 'vet' => 'whois.rightside.co',
- 'vg' => 'ccwhois.ksregistry.net',
- 'viajes' => 'whois.donuts.co',
- 'villas' => 'whois.donuts.co',
- 'vision' => 'whois.donuts.co',
- 'vlaanderen' => 'whois.nic.vlaanderen',
- 'vodka' => 'whois-dub.mm-registry.com',
- 'vote' => 'whois.afilias.net',
- 'voting' => 'whois.voting.tld-box.at',
- 'voto' => 'whois.afilias.net',
- 'voyage' => 'whois.donuts.co',
- 'vu' => 'vunic.vu',
- 'wf' => 'whois.nic.wf',
- 'wales' => 'whois.nic.wales',
- 'wang' => 'whois.gtld.knet.cn',
- 'watch' => 'whois.donuts.co',
- 'website' => 'whois.nic.website',
- 'wed' => 'whois.nic.wed',
- 'wedding' => 'whois-dub.mm-registry.com',
- 'wf' => 'whois.nic.wf',
- 'wien' => 'whois.nic.wien',
- 'wiki' => 'whois.nic.wiki',
- 'wme' => 'whois.centralnic.com',
- 'work' => 'whois-dub.mm-registry.com',
- 'works' => 'whois.donuts.co',
- 'world' => 'whois.donuts.co',
- 'ws' => 'whois.website.ws',
- 'wtc' => 'whois.nic.wtc',
- 'wtf' => 'whois.donuts.co',
- 'xxx' => 'whois.nic.xxx',
- 'xyz' => 'whois.nic.xyz',
- 'yachts' => 'whois.afilias-srs.net',
- 'yoga' => 'whois-dub.mm-registry.com',
- 'youtube' => 'domain-registry-whois.l.google.com',
- 'yt' => 'whois.nic.yt',
- 'zip' => 'domain-registry-whois.l.google.com',
- 'zm' => 'whois.nic.zm',
- 'zone' => 'whois.donuts.co',
- 'дети' => 'whois.nic.xn--d1acj3b',
- 'москва' => 'whois.nic.xn--80adxhks',
- 'онлайн' => 'whois.online.rs.corenic.net',
- 'орг' => 'whois.publicinterestregistry.net',
- 'рус' => 'whois.nic.xn--p1acf',
- 'рф' => 'whois.tcinet.ru',
- 'сайт' => 'whois.site.rs.corenic.net',
- 'укр' => 'whois.dotukr.com',
- 'қаз' => 'whois.nic.kz',
- 'الجزائر' => 'whois.nic.dz',
- 'السعودية' => 'whois.nic.net.sa',
- 'امارات' => 'whois.aeda.net.ae',
- 'ایران' => 'whois.nic.ir',
- 'بازار' => 'whois.bazaar.coreregistry.net',
- 'سورية' => 'whois.tld.sy',
- 'شبكة' => 'whois.nic.xn--ngbc5azd',
- 'عمان' => 'whois.registry.om',
- 'قطر' => 'whois.registry.qa',
- 'مليسيا' => 'whois.mynic.my',
- 'موقع' => 'whois.afilias-srs.net',
- 'சிங்கப்பூர்' => 'whois.sgnic.sg',
- 'ไทย' => 'whois.thnic.co.th',
- 'みんな' => 'domain-registry-whois.l.google.com',
- '中信' => 'whois.gtld.knet.cn',
- '中国' => 'cwhois.cnnic.cn',
- '中國' => 'cwhois.cnnic.cn',
- '中文网' => 'whois.afilias-srs.net',
- '企业' => 'whois.donuts.co',
- '佛山' => 'whois.ngtld.cn',
- '公司' => 'whois.ngtld.cn',
- '公益' => 'whois.conac.cn',
- '台湾' => 'whois.twnic.net.tw',
- '台灣' => 'whois.twnic.net.tw',
- '商城' => 'whois.gtld.knet.cn',
- '在线' => 'whois.afilias-srs.net',
- '广东' => 'whois.ngtld.cn',
- '我爱你' => 'whois.gtld.knet.cn',
- '手机' => 'whois.afilias-srs.net',
- '政务' => 'whois.conac.cn',
- '新加坡' => 'whois.sgnic.sg',
- '新加坡' => 'whois.sgnic.sg',
- '游戏' => 'whois.donuts.co',
- '移动' => 'whois.afilias.net',
- '组织机构' => 'whois.publicinterestregistry.net',
- '网络' => 'whois.ngtld.cn',
- '集团' => 'whois.gtld.knet.cn',
- '香港' => 'whois.hkirc.hk',
- ];
-
- public $ip = [
- 'whois.lacnic.net',
- 'whois.apnic.net',
- 'whois.arin.net',
- 'whois.ripe.net',
- ];
-}
diff --git a/public/whois/whois-php/src/Whois/WhoisException.php b/public/whois/whois-php/src/Whois/WhoisException.php
deleted file mode 100644
index 57bba77..0000000
--- a/public/whois/whois-php/src/Whois/WhoisException.php
+++ /dev/null
@@ -1,8 +0,0 @@
-server;
+ }
+
+ public function query(string $target, int $timeout = 5): WhoisResponse {
+ $sock = @fsockopen($this->server, self::PORT, $errno, $errstr, $timeout);
+ if(!$sock) throw new \RuntimeException('WhoisClient: ' . $errstr);
+
+ try {
+ fwrite($sock, $target . "\r\n");
+
+ $lines = [];
+ while($line = fgets($sock))
+ $lines[] = trim($line);
+ } finally {
+ fclose($sock);
+ }
+
+ return new WhoisResponse($this->server, $lines);
+ }
+
+ public function lookup(string $target, int $timeout = 5): WhoisResult {
+ $target = idn_to_ascii(mb_strtolower($target), IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
+ $client = $this;
+ $response = null;
+ $responses = [];
+
+ try {
+ for(;;) {
+ $response = $client->query($target);
+ $responses[] = $response;
+
+ $server = $response->findNextWhoisServer();
+ if($server === '' || $server === $this->server)
+ break;
+
+ $client = new WhoisClient($server);
+ }
+ } catch(\RuntimeException $ex) {
+ if(empty($responses))
+ throw $ex;
+ }
+
+ return new WhoisResult($target, array_reverse($responses));
+ }
+}
diff --git a/src/Whois/WhoisResponse.php b/src/Whois/WhoisResponse.php
new file mode 100644
index 0000000..b1cd07b
--- /dev/null
+++ b/src/Whois/WhoisResponse.php
@@ -0,0 +1,60 @@
+server;
+ }
+
+ public function getLines(): array {
+ return $this->lines;
+ }
+
+ public function getText(): string {
+ return implode(PHP_EOL, $this->lines);
+ }
+
+ public function findNextWhoisServer(): string {
+ $server = '';
+
+ foreach($this->lines as $line) {
+ $parts = explode(':', strtolower($line), 2);
+ if(in_array(trim($parts[0]), self::WHOIS_SERVER_PREFIXES)) {
+ $server = trim($parts[1]);
+ if(str_contains($server, '://')) {
+ $server = '';
+ continue;
+ }
+ break;
+ }
+ }
+
+ return $this->server === $server ? '' : $server;
+ }
+
+ public function jsonSerialize(): mixed {
+ return [
+ 'server' => $this->server,
+ 'lines' => $this->lines,
+ ];
+ }
+
+ public function __serialize(): array {
+ return [
+ 'server' => $this->server,
+ 'lines' => $this->lines,
+ ];
+ }
+
+ public function __unserialize(array $stored): void {
+ $this->server = $stored['server'];
+ $this->lines = $stored['lines'];
+ }
+}
diff --git a/src/Whois/WhoisResult.php b/src/Whois/WhoisResult.php
new file mode 100644
index 0000000..255a69a
--- /dev/null
+++ b/src/Whois/WhoisResult.php
@@ -0,0 +1,44 @@
+target;
+ }
+
+ public function getResponses(): array {
+ return $this->responses;
+ }
+
+ public function getResponse(int $index): WhoisResponse {
+ return $this->responses[$index];
+ }
+
+ public function getResponseCount(): int {
+ return count($this->responses);
+ }
+
+ public function jsonSerialize(): mixed {
+ return [
+ 'target' => $this->target,
+ 'responses' => $this->responses,
+ ];
+ }
+
+ public function __serialize(): array {
+ return [
+ 'target' => $this->target,
+ 'responses' => $this->responses,
+ ];
+ }
+
+ public function __unserialize(array $stored): void {
+ $this->target = $stored['target'];
+ $this->responses = $stored['responses'];
+ }
+}
diff --git a/tpl/ascii.php b/tpl/ascii.php
index c8c83d0..86522d2 100644
--- a/tpl/ascii.php
+++ b/tpl/ascii.php
@@ -1,5 +1,5 @@
extends('home/master');
+$self->extends('master');
$self->header_title = 'flash.moe / ascii table';
$self->header_minimal = true;
diff --git a/tpl/contact.php b/tpl/contact.php
index 90d6e3a..149b94b 100644
--- a/tpl/contact.php
+++ b/tpl/contact.php
@@ -1,5 +1,5 @@
extends('home/master');
+$self->extends('master');
$self->header_title = 'flash.moe / contact';
diff --git a/tpl/master.php b/tpl/master.php
index adc86f4..4b91f1a 100644
--- a/tpl/master.php
+++ b/tpl/master.php
@@ -43,7 +43,7 @@
=$self->getBlock('container');?>
footer_onload) && is_array($self->footer_onload)): ?>
+