Skip to content

Commit

Permalink
Merge 5ba6c30 into 14a3da0
Browse files Browse the repository at this point in the history
  • Loading branch information
Christian Thomas committed Dec 5, 2018
2 parents 14a3da0 + 5ba6c30 commit b91baa6
Show file tree
Hide file tree
Showing 28 changed files with 1,159 additions and 72 deletions.
106 changes: 81 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,17 @@ These resolvers at the least implement the `Resolvers\Interfaces\DNSQuery` inter
- LocalSystem (uses the local PHP dns query function)

```php

$resolver = new Resolvers\GoogleDNS();
// validates hostname
$hostname = Hostname::createFromString('google.com');

// OR

$hostname = 'google.com';

// can query via convenience methods
$records = $resolver->getARecords($hostname); // returns a collection of DNSRecords
$records = $resolver->getARecords($hostname); // returns a collection of DNS A Records

// can also query by any type. Types are a proper object that validate you have the right type
// can also query by any RecordType. Record Types are a proper object that validate you have the right type.
$recordType = DNSRecordType::createAAAA();

// OR
Expand Down Expand Up @@ -117,6 +115,8 @@ $cachedResolver = new Resolvers\Cached($cache, $resolverOfChoice, $TTL);

Take a look in the `src/Entities` to see what's available for you to query by and receive.

For records with extra type data, like SOA, TXT, MX, and NS there is a data attribute on `Entities\DNSRecord` that will be set with the proper type

**Reverse Lookup**

This is offered via a separate `ReverseDNSQuery` interface as it is not common or available for every type of DNS Resolver.
Expand Down Expand Up @@ -152,59 +152,115 @@ christians-mbp:php-dns chthomas$ make repl
Psy Shell v0.9.9 (PHP 7.2.8 — cli) by Justin Hileman
>>> ls
Variables: $cachedResolver, $chainResolver, $cloudFlareResolver, $googleDNSResolver, $IOSubscriber, $localSystemResolver, $stdErr, $stdOut
>>> $records = $chainResolver->getARecords(new Hostname('facebook.com'))
>>> $records = $chainResolver->getARecords('facebook.com')
{
"dns.query.profiled": {
"elapsedSeconds": 0.14085698127746582,
"transactionName": "CloudFlare:facebook.com:A",
"peakMemoryUsage": 9508312
"elapsedSeconds": 0.21915197372436523,
"transactionName": "CloudFlare:facebook.com.:A",
"peakMemoryUsage": 9517288
}
}
{
"dns.queried": {
"resolver": "CloudFlare",
"hostname": "facebook.com",
"hostname": "facebook.com.",
"type": "A",
"records": [
{
"hostname": "facebook.com",
"hostname": "facebook.com.",
"type": "A",
"TTL": 175,
"TTL": 224,
"class": "IN",
"IPAddress": "31.13.71.36"
}
],
"empty": false
}
}
=> RemotelyLiving\PHPDNS\Entities\DNSRecordCollection {#2375}
>>> $records->pickFirst()
=> RemotelyLiving\PHPDNS\Entities\DNSRecord {#2392}
>>> $googleDNSResolver->hasRecord($records->pickFirst())
=> RemotelyLiving\PHPDNS\Entities\DNSRecordCollection {#2370}
>>> $records->pickFirst()->toArray()
=> [
"hostname" => "facebook.com.",
"type" => "A",
"TTL" => 224,
"class" => "IN",
"IPAddress" => "31.13.71.36",
]
>>> $records = $chainResolver->withConsensusResults()->getRecords('facebook.com', 'TXT')
{
"dns.query.profiled": {
"elapsedSeconds": 0.023031949996948242,
"transactionName": "CloudFlare:facebook.com.:TXT",
"peakMemoryUsage": 9615080
}
}
{
"dns.queried": {
"resolver": "CloudFlare",
"hostname": "facebook.com.",
"type": "TXT",
"records": [
{
"hostname": "facebook.com.",
"type": "TXT",
"TTL": 9136,
"class": "IN",
"data": "v=spf1 redirect=_spf.facebook.com"
}
],
"empty": false
}
}
{
"dns.query.profiled": {
"elapsedSeconds": 0.16751790046691895,
"transactionName": "GoogleDNS:facebook.com:A",
"peakMemoryUsage": 9508312
"elapsedSeconds": 0.23299598693847656,
"transactionName": "GoogleDNS:facebook.com.:TXT",
"peakMemoryUsage": 9615080
}
}
{
"dns.queried": {
"resolver": "GoogleDNS",
"hostname": "facebook.com",
"type": "A",
"hostname": "facebook.com.",
"type": "TXT",
"records": [
{
"hostname": "facebook.com",
"type": "A",
"TTL": 234,
"hostname": "facebook.com.",
"type": "TXT",
"TTL": 21121,
"class": "IN",
"IPAddress": "31.13.71.36"
"data": "v=spf1 redirect=_spf.facebook.com"
}
],
"empty": false
}
}
{
"dns.query.profiled": {
"elapsedSeconds": 0.0018258094787597656,
"transactionName": "LocalSystem:facebook.com.:TXT",
"peakMemoryUsage": 9615080
}
}
{
"dns.queried": {
"resolver": "LocalSystem",
"hostname": "facebook.com.",
"type": "TXT",
"records": [
{
"hostname": "facebook.com.",
"type": "TXT",
"TTL": 25982,
"class": "IN",
"data": "v=spf1 redirect=_spf.facebook.com"
}
],
"empty": false
}
}
=> true
=> RemotelyLiving\PHPDNS\Entities\DNSRecordCollection {#2413}
>>> $records->pickFirst()->getData()->getValue()
=> "v=spf1 redirect=_spf.facebook.com"
>>>
```
40 changes: 34 additions & 6 deletions src/Entities/DNSRecord.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,33 +31,48 @@ class DNSRecord extends EntityAbstract implements Arrayable, Serializable
*/
private $class;

/**
* @var \RemotelyLiving\PHPDNS\Entities\DataAbstract|null
*/
private $data;

public function __construct(
DNSRecordType $recordType,
Hostname $hostname,
int $ttl,
IPAddress $IPAddress = null,
string $class = 'IN'
string $class = 'IN',
DataAbstract $data = null
) {
$this->recordType = $recordType;
$this->hostname = $hostname;
$this->TTL = $ttl;
$this->IPAddress = $IPAddress;
$this->class = $class;
$this->data = $data;
}

public static function createFromPrimitives(
string $recordType,
string $hostname,
int $ttl,
string $IPAddress = null,
string $class = 'IN'
string $class = 'IN',
string $data = null
) : DNSRecord {
$type = DNSRecordType::createFromString($recordType);
$hostname = Hostname::createFromString($hostname);
$data = ($data !== null)
? DataAbstract::createFromTypeAndString($type, $data)
: null;

return new static(
DNSRecordType::createFromString($recordType),
Hostname::createFromString($hostname),
$type,
$hostname,
$ttl,
$IPAddress ? IPAddress::createFromString($IPAddress) : null,
$class
$class,
$data
);
}

Expand Down Expand Up @@ -86,6 +101,11 @@ public function getClass(): string
return $this->class;
}

public function getData(): ?DataAbstract
{
return $this->data;
}

public function toArray(): array
{
$formatted = [
Expand All @@ -99,14 +119,19 @@ public function toArray(): array
$formatted['IPAddress'] = (string)$this->IPAddress;
}

if ($this->data) {
$formatted['data'] = (string)$this->data;
}

return $formatted;
}

public function equals(DNSRecord $record): bool
{
return $this->hostname->equals($record->getHostname())
&& $this->recordType->equals($record->getType())
&& (string) $this->IPAddress === (string) $record->getIPAddress(); // could be null
&& (string)$this->data === (string)$record->getData() // could be null
&& (string)$this->IPAddress === (string)$record->getIPAddress(); // could be null
}

public function serialize(): string
Expand All @@ -124,5 +149,8 @@ public function unserialize($record): void
$this->TTL = (int) $unserialized['TTL'];
$this->IPAddress = $rawIPAddres ? IPAddress::createFromString($rawIPAddres) : null;
$this->class = $unserialized['class'];
$this->data = (isset($unserialized['data']))
? DataAbstract::createFromTypeAndString($this->recordType, $unserialized['data'])
: null;
}
}
5 changes: 2 additions & 3 deletions src/Entities/DNSRecordCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public function withUniqueValuesExcluded(): self
{
return $this->filterValues(function (DNSRecord $candidateRecord, DNSRecordCollection $remaining): bool {
return $remaining->has($candidateRecord);
});
})->withUniqueValues();
}

public function withUniqueValues(): self
Expand All @@ -140,8 +140,7 @@ private function filterValues(callable $eval): self

/** @var \RemotelyLiving\PHPDNS\Entities\DNSRecord $record */
while ($record = array_shift($records)) {
$remaining = new self(...$records);
if ($eval($record, $remaining)) {
if ($eval($record, new self(...$records))) {
$filtered[] = $record;
}
}
Expand Down
49 changes: 49 additions & 0 deletions src/Entities/DataAbstract.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php
namespace RemotelyLiving\PHPDNS\Entities;

use RemotelyLiving\PHPDNS\Entities\Interfaces\Arrayable;
use RemotelyLiving\PHPDNS\Entities\Interfaces\Serializable;
use RemotelyLiving\PHPDNS\Exceptions\InvalidArgumentException;

abstract class DataAbstract implements Arrayable, Serializable
{
abstract public function __toString();

public function equals(DataAbstract $dataAbstract): bool
{
return (string)$this === (string)$dataAbstract;
}

public static function createFromTypeAndString(DNSRecordType $recordType, string $data): self
{
if ($recordType->isA(DNSRecordType::TYPE_TXT)) {
return new TXTData($data);
}

if ($recordType->isA(DNSRecordType::TYPE_MX)) {
$exploded = explode(' ', $data);

return new MXData(new Hostname($exploded[1]), (int)$exploded[0]);
}

if ($recordType->isA(DNSRecordType::TYPE_SOA)) {
$exploded = explode(' ', $data);

return new SOAData(
new Hostname($exploded[0]),
new Hostname($exploded[1]),
(int)$exploded[2] ?? 0,
(int)$exploded[3] ?? 0,
(int)$exploded[4] ?? 0,
(int)$exploded[5] ?? 0,
(int)$exploded[6] ?? 0
);
}

if ($recordType->isA(DNSRecordType::TYPE_NS)) {
return new NSData(new Hostname($data));
}

throw new InvalidArgumentException("{$data} could not be created with type {$recordType}");
}
}
13 changes: 12 additions & 1 deletion src/Entities/Hostname.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Hostname extends EntityAbstract

public function __construct(string $hostname)
{
$hostname = self::punyCode(mb_strtolower(trim($hostname)));
$hostname = $this->normalizeHostName($hostname);

if ((bool)filter_var($hostname, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) === false) {
throw new InvalidArgumentException("{$hostname} is not a valid hostname");
Expand Down Expand Up @@ -55,4 +55,15 @@ private static function punyCode(string $hostname): string
{
return (string)idn_to_ascii($hostname, IDNA_ERROR_PUNYCODE, INTL_IDNA_VARIANT_UTS46);
}

private function normalizeHostName(string $hostname): string
{
$hostname = self::punyCode(mb_strtolower(trim($hostname)));

if (substr($hostname, -1) !== '.') {
return "{$hostname}.";
}

return $hostname;
}
}

0 comments on commit b91baa6

Please sign in to comment.