2014-06-13 19:17:49 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace Addr;
|
|
|
|
|
|
|
|
use Alert\Reactor;
|
|
|
|
|
|
|
|
class Resolver
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* @var Reactor
|
|
|
|
*/
|
|
|
|
private $reactor;
|
|
|
|
|
|
|
|
/**
|
2014-06-15 01:47:15 +02:00
|
|
|
* @var NameValidator
|
2014-06-13 19:17:49 +02:00
|
|
|
*/
|
2014-06-15 01:47:15 +02:00
|
|
|
private $nameValidator;
|
2014-06-13 19:17:49 +02:00
|
|
|
|
2014-06-15 01:47:15 +02:00
|
|
|
/**
|
|
|
|
* @var Client
|
|
|
|
*/
|
|
|
|
private $client;
|
2014-06-13 19:17:49 +02:00
|
|
|
|
2014-06-15 01:47:15 +02:00
|
|
|
/**
|
|
|
|
* @var Cache
|
|
|
|
*/
|
|
|
|
private $cache;
|
2014-06-13 19:17:49 +02:00
|
|
|
|
2014-06-15 01:47:15 +02:00
|
|
|
/**
|
|
|
|
* @var HostsFile
|
|
|
|
*/
|
|
|
|
private $hostsFile;
|
2014-06-13 19:17:49 +02:00
|
|
|
|
2014-06-16 04:28:59 +02:00
|
|
|
/**
|
|
|
|
* @var callable
|
|
|
|
*/
|
|
|
|
private $cacheStoreCallback;
|
|
|
|
|
2014-06-13 19:17:49 +02:00
|
|
|
/**
|
2014-06-15 01:47:15 +02:00
|
|
|
* Constructor
|
|
|
|
*
|
2014-06-13 19:17:49 +02:00
|
|
|
* @param Reactor $reactor
|
2014-06-15 01:47:15 +02:00
|
|
|
* @param NameValidator $nameValidator
|
|
|
|
* @param Client $client
|
2014-06-13 19:17:49 +02:00
|
|
|
* @param Cache $cache
|
2014-06-15 01:47:15 +02:00
|
|
|
* @param HostsFile $hostsFile
|
2014-06-13 19:17:49 +02:00
|
|
|
*/
|
2014-06-15 01:47:15 +02:00
|
|
|
public function __construct(
|
|
|
|
Reactor $reactor,
|
|
|
|
NameValidator $nameValidator,
|
|
|
|
Client $client = null,
|
|
|
|
Cache $cache = null,
|
|
|
|
HostsFile $hostsFile = null
|
|
|
|
) {
|
2014-06-13 19:17:49 +02:00
|
|
|
$this->reactor = $reactor;
|
2014-06-15 01:47:15 +02:00
|
|
|
$this->nameValidator = $nameValidator;
|
|
|
|
$this->client = $client;
|
|
|
|
$this->cache = $cache;
|
|
|
|
$this->hostsFile = $hostsFile;
|
2014-06-16 04:28:59 +02:00
|
|
|
|
|
|
|
if ($cache) {
|
|
|
|
$this->cacheStoreCallback = [$cache, 'store'];
|
|
|
|
}
|
2014-06-13 19:17:49 +02:00
|
|
|
}
|
|
|
|
|
2014-06-16 18:47:51 +02:00
|
|
|
/**
|
|
|
|
* Verify the supplied domain name is in a valid format
|
|
|
|
*
|
|
|
|
* @param $name
|
|
|
|
* @param $callback
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
private function validateName($name, $callback)
|
|
|
|
{
|
|
|
|
if (!$this->nameValidator->validate($name)) {
|
|
|
|
$this->reactor->immediately(function() use($callback) {
|
|
|
|
call_user_func($callback, null, ResolutionErrors::ERR_INVALID_NAME);
|
|
|
|
});
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-06-13 19:17:49 +02:00
|
|
|
/**
|
2014-06-15 01:47:15 +02:00
|
|
|
* Check if a supplied name is an IP address and resolve immediately
|
|
|
|
*
|
|
|
|
* @param string $name
|
|
|
|
* @param callable $callback
|
|
|
|
* @return bool
|
2014-06-13 19:17:49 +02:00
|
|
|
*/
|
2014-06-15 01:47:15 +02:00
|
|
|
private function resolveAsIPAddress($name, $callback)
|
2014-06-13 19:17:49 +02:00
|
|
|
{
|
2014-06-15 01:47:15 +02:00
|
|
|
if (filter_var($name, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
|
|
|
$this->reactor->immediately(function() use($callback, $name) {
|
|
|
|
call_user_func($callback, $name, AddressModes::INET4_ADDR);
|
|
|
|
});
|
2014-06-13 19:17:49 +02:00
|
|
|
|
2014-06-15 01:47:15 +02:00
|
|
|
return true;
|
|
|
|
} else if (filter_var($name, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
|
|
|
$this->reactor->immediately(function() use($callback, $name) {
|
|
|
|
call_user_func($callback, $name, AddressModes::INET6_ADDR);
|
|
|
|
});
|
2014-06-13 19:17:49 +02:00
|
|
|
|
2014-06-15 01:47:15 +02:00
|
|
|
return true;
|
2014-06-13 19:17:49 +02:00
|
|
|
}
|
2014-06-15 01:47:15 +02:00
|
|
|
|
|
|
|
return false;
|
2014-06-13 19:17:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-06-15 01:47:15 +02:00
|
|
|
* Resolve a name in the hosts file
|
2014-06-13 19:17:49 +02:00
|
|
|
*
|
|
|
|
* @param string $name
|
2014-06-15 01:47:15 +02:00
|
|
|
* @param int $mode
|
|
|
|
* @param callable $callback
|
|
|
|
* @return bool
|
2014-06-13 19:17:49 +02:00
|
|
|
*/
|
2014-06-15 01:47:15 +02:00
|
|
|
private function resolveInHostsFile($name, $mode, $callback)
|
2014-06-13 19:17:49 +02:00
|
|
|
{
|
2014-06-15 01:47:15 +02:00
|
|
|
/* localhost should resolve regardless of whether we have a hosts file
|
|
|
|
also the Windows hosts file no longer contains this record */
|
|
|
|
if ($name === 'localhost') {
|
|
|
|
if ($mode & AddressModes::PREFER_INET6) {
|
|
|
|
$this->reactor->immediately(function() use($callback) {
|
|
|
|
call_user_func($callback, '::1', AddressModes::INET6_ADDR);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
$this->reactor->immediately(function() use($callback) {
|
|
|
|
call_user_func($callback, '127.0.0.1', AddressModes::INET4_ADDR);
|
|
|
|
});
|
2014-06-13 19:17:49 +02:00
|
|
|
}
|
2014-06-15 01:47:15 +02:00
|
|
|
|
|
|
|
return true;
|
2014-06-13 19:17:49 +02:00
|
|
|
}
|
|
|
|
|
2014-06-15 01:47:15 +02:00
|
|
|
if (!$this->hostsFile || null === $result = $this->hostsFile->resolve($name, $mode)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
list($addr, $type) = $result;
|
|
|
|
$this->reactor->immediately(function() use($callback, $addr, $type) {
|
|
|
|
call_user_func($callback, $addr, $type);
|
|
|
|
});
|
|
|
|
|
|
|
|
return true;
|
2014-06-13 19:17:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-06-15 01:47:15 +02:00
|
|
|
* Resolve a name in the cache
|
2014-06-13 19:17:49 +02:00
|
|
|
*
|
|
|
|
* @param string $name
|
|
|
|
* @param int $mode
|
2014-06-15 01:47:15 +02:00
|
|
|
* @param callable $callback
|
2014-06-13 19:17:49 +02:00
|
|
|
* @return bool
|
|
|
|
*/
|
2014-06-15 01:47:15 +02:00
|
|
|
private function resolveInCache($name, $mode, $callback)
|
2014-06-13 19:17:49 +02:00
|
|
|
{
|
2014-06-15 01:47:15 +02:00
|
|
|
if ($this->cache && null !== $result = $this->cache->resolve($name, $mode)) {
|
|
|
|
list($addr, $type) = $result;
|
|
|
|
$this->reactor->immediately(function() use($callback, $addr, $type) {
|
|
|
|
call_user_func($callback, $addr, $type);
|
2014-06-13 19:17:49 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-06-15 01:47:15 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Resolve a name from a server
|
|
|
|
*
|
|
|
|
* @param string $name
|
|
|
|
* @param int $mode
|
|
|
|
* @param callable $callback
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
private function resolveFromServer($name, $mode, $callback)
|
|
|
|
{
|
|
|
|
if (!$this->client) {
|
|
|
|
$this->reactor->immediately(function() use($callback) {
|
|
|
|
call_user_func($callback, null, ResolutionErrors::ERR_NO_RECORD);
|
2014-06-13 19:17:49 +02:00
|
|
|
});
|
|
|
|
|
2014-06-15 01:47:15 +02:00
|
|
|
return;
|
2014-06-13 19:17:49 +02:00
|
|
|
}
|
|
|
|
|
2014-06-16 04:28:59 +02:00
|
|
|
$this->client->resolve($name, $mode, $callback, $this->cacheStoreCallback);
|
2014-06-13 19:17:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-06-15 01:47:15 +02:00
|
|
|
* Resolve a name
|
|
|
|
*
|
2014-06-13 19:17:49 +02:00
|
|
|
* @param string $name
|
|
|
|
* @param callable $callback
|
2014-06-15 01:47:15 +02:00
|
|
|
* @param int $mode
|
2014-06-13 19:17:49 +02:00
|
|
|
*/
|
2014-06-15 01:47:15 +02:00
|
|
|
public function resolve($name, callable $callback, $mode = 3)
|
2014-06-13 19:17:49 +02:00
|
|
|
{
|
2014-06-15 01:47:15 +02:00
|
|
|
if ($this->resolveAsIPAddress($name, $callback)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-16 18:47:51 +02:00
|
|
|
$name = strtolower($name);
|
|
|
|
if (!$this->validateName($name, $callback)) {
|
2014-06-15 01:47:15 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->resolveInHostsFile($name, $mode, $callback) || $this->resolveInCache($name, $mode, $callback)) {
|
2014-06-13 19:17:49 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-15 01:47:15 +02:00
|
|
|
$this->resolveFromServer($name, $mode, $callback);
|
2014-06-13 19:17:49 +02:00
|
|
|
}
|
|
|
|
}
|