Rewrote flash.moe whois.

This commit is contained in:
flash 2022-09-27 01:58:32 +00:00
parent e49c940032
commit 6bbbc7a385
18 changed files with 391 additions and 1185 deletions

View file

@ -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();
})();

View file

@ -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;
}

View file

@ -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();

View file

@ -1,110 +0,0 @@
<?php
ini_set('display_errors', 'on');
error_reporting(-1);
require_once __DIR__ . '/whois-php/src/Whois/WhoisException.php';
require_once __DIR__ . '/whois-php/src/Whois/Servers.php';
require_once __DIR__ . '/whois-php/src/Whois/Result.php';
require_once __DIR__ . '/whois-php/src/Whois/Client.php';
define('FMWHOIS_PREFIX', 'fm:whois:domain:');
$domain = isset($_GET['domain']) && is_string($_GET['domain'])
? idn_to_ascii(mb_strtolower($_GET['domain']), IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46)
: '';
$domainHash = hash('sha256', $domain);
if(!empty($domain)) {
$redis = new Redis;
$redis->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));
}
?>
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>flash.moe whois</title>
<link href="style.css" rel="stylesheet" type="text/css"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
</head>
<body>
<div class="container">
<div class="header">
<div class="title">flash.moe <span style="color: #4a3650;">whois</span></div>
<div class="nav">
<a href="/">Home</a>
<!--<a href="https://github.com/flashwave/whois-php">Library</a>-->
</div>
</div>
<form class="lookup-form" method="get" action="">
<input class="lookup-form-input" type="text" name="domain" placeholder="Enter a domain or IP address to look up!" value="<?=htmlentities($domain);?>" id="lookup-input"/>
<input class="lookup-form-submit" type="submit" value="Look up" id="lookup-submit"/>
</form>
<div class="result" id="lookup-result"><?=htmlentities($responseText);?></div>
<div class="copy">
&copy; <a href="https://flash.moe">flashwave</a> 2013-<?=date('Y');?>
</div>
</div>
<script>var whoisUrl = '<?=$_SERVER["PHP_SELF"];?>';</script>
<script src="./script.js" type="text/javascript" charset="utf-8"></script>
</body>
</html>

View file

@ -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();
}

View file

@ -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;
}

View file

@ -1,234 +0,0 @@
<?php
namespace Whois;
class Client
{
/**
* Container for the Servers class.
*
* @var Servers
*/
private $servers;
/**
* Constructor.
*
* @param Servers $servers Class containing the Whois servers.
*/
public function __construct(Servers $servers = null)
{
// Check if $servers is null
if ($servers == null) {
// Assign the standard Servers class
$servers = Servers::class;
}
// Apply the servers
$this->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;
}
}

View file

@ -1,34 +0,0 @@
<?php
namespace Whois;
class Result
{
/**
* The target whois domain/ip.
*
* @var string
*/
public $target = '';
/**
* The amount of whois results.
*
* @var int
*/
public $count = 0;
/**
* The results in order from last to first
*
* @var array
*/
public $responses = [];
/**
* The result type.
* Possible values are empty, domain and ip.
*
* @var string
*/
public $type = 'empty';
}

View file

@ -1,612 +0,0 @@
<?php
namespace Whois;
class Servers
{
public $tld = [
'abogado' => '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',
];
}

View file

@ -1,8 +0,0 @@
<?php
namespace Whois;
use Exception;
class WhoisException extends Exception
{
}

57
src/Whois/WhoisClient.php Normal file
View file

@ -0,0 +1,57 @@
<?php
namespace Makai\Whois;
class WhoisClient {
public const ROOT = 'whois.iana.org';
public const PORT = 43;
public function __construct(
private string $server = self::ROOT
) {}
public function getServer(): string {
return $this->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));
}
}

View file

@ -0,0 +1,60 @@
<?php
namespace Makai\Whois;
class WhoisResponse implements \JsonSerializable {
private const WHOIS_SERVER_PREFIXES = ['refer', 'whois', 'registrar whois server'];
public function __construct(
private string $server,
private array $lines
) {}
public function getServer(): string {
return $this->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'];
}
}

44
src/Whois/WhoisResult.php Normal file
View file

@ -0,0 +1,44 @@
<?php
namespace Makai\Whois;
class WhoisResult implements \JsonSerializable {
public function __construct(
private string $target,
private array $responses
) {}
public function getTarget(): string {
return $this->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'];
}
}

View file

@ -1,5 +1,5 @@
<?php
$self->extends('home/master');
$self->extends('master');
$self->header_title = 'flash.moe / ascii table';
$self->header_minimal = true;

View file

@ -1,5 +1,5 @@
<?php
$self->extends('home/master');
$self->extends('master');
$self->header_title = 'flash.moe / contact';

View file

@ -43,7 +43,7 @@
<?=$self->getBlock('container');?>
</div>
<div class="footer">
<div class="footer-text">&copy; flashwave 2010-<?=date('Y');?> - <?=($self->footer_quotes[array_rand($self->footer_quotes)]);?></div>
<div class="footer-text">&copy; flashwave <?=($self->footer_copy_start ?? '2010');?>-<?=($self->footer_copy_end ?? date('Y'));?> - <?=($self->footer_quotes[array_rand($self->footer_quotes)]);?></div>
</div>
<?php if(isset($self->footer_onload) && is_array($self->footer_onload)): ?>
<script type="text/javascript">

View file

@ -1,5 +1,5 @@
<?php
$self->extends('home/master');
$self->extends('master');
$self->header_title = 'flash.moe / projects';

23
tpl/whois.php Normal file
View file

@ -0,0 +1,23 @@
<?php
$self->extends('master');
$self->header_title = 'flash.moe / whois';
$self->header_logo_flash = 'flash.moe&nbsp;';
$self->header_logo_wave = 'whois';
$self->header_minimal = true;
$self->footer_copy_start = '2013';
$self->block('container', function($self) {
?>
<div class="whois-container">
<form class="whois-lookup-form" method="get" action="">
<input class="whois-lookup-form-input" type="text" name="domain" placeholder="Enter a domain or IP address to look up!" value="" id="lookup-input"/>
<input class="whois-lookup-form-submit" type="submit" value="Look up" id="lookup-submit"/>
</form>
<div class="whois-result" id="lookup-result"></div>
</div>
<script type="text/javascript" src="/assets/2021whois.js"></script>
<?php
});