Home » Networking » Net_DNS » Manual
Provides methods for parsing and manipulating DNS packets as returned from DNS servers and provides communication methods for sending and receiving DNS packets.
Introduction
Introduction – General purpose DNS querying and packet parsing
Contributors
- Eric Kilfoil
- Sara Golemon
- Thomas V.V. Cox
- PEAR End-Users - Thanks
Description
The Net_DNS class provides DNS packet manipulation methods and the ability to send and receive packets to/from nameservers. Any type of DNS packet can be created, including RFC 2136 style DNS UPDATE packets.
The current list of supported RR types are:
- A
- AAAA
- A
- CNAME
- HINFO
- MX
- NAPTR
- NS
- PTR
- SOA
- SRV
- TSIG
- TXT
Class Summary Net_DNS
Class Summary Net_DNS – DNS Packet manipulation class
DNS Packet manipulation class
This class includes sub-classses that provide methods for creating, parsing, sending, and receving DNS nameserver packets.
This class is only a container class and generally should never be constructed. It contains constants defined in RFC 1035 with methods for converting between the integer and the name values of such constants.
For information on sending a basic query to a nameserver for a specific record, see Net_DNS_Resolver::query().
Class Summary Net_DNS_Resolver
Class Summary Net_DNS_Resolver – Provides a resolver implementation to handle DNS queries
Net_DNS_Resolver
The resolver class a high level interface for communicating with DNS servers. It uses the UNIX system stub resolver configuration files (if available) to configure the resolver. The resolver can also be configured manually by overriding the system configuration. This is done by setting the appropriate values in the Net_DNS_Resolver object. See Net_DNS_Resolver::query() for more information on the resolver configuration.
Net_DNS_Resolver::query()
Net_DNS_Resolver::query() – Queries a nameserver and returns a response
Synopsis
require_once('Net/DNS.php');
Net_DNS_Packet Net_DNS_Resolver::query (
string $hostname
, string $type = 'A'
, string $class = 'IN'
)
Description
-
hostname- The name to lookup (eg. www.php.net) -
type- The record type to query -
class- The zone class to query
Constructs a DNS query packet and forwards the query to a nameserver configured in the system stub resolver (ie. /etc/resolv.conf). When a response is received from the DNS server, a fully populated Net_DNS_Packet object is returned that represents the response.
The resolver object contains many properties that control that behaviour of the resolver. Some of these settings are automatically read from the system resolver configuration if available. On Linux/UNIX based systems, this includes /etc/resolv.conf as well as various environment variables. This configuration is done at the time of object instantiation and can be overridden by setting the appropriate object properties.
This function will only return a Net_DNS_Packet if the ANSWER section contains resource records. Specifically, if the ANCOUNT variable in the DNS packet header is 0, query() will return 0 (note: 0, not FALSE). If you are expecting a packet without resource records in the ANSWER section, use Net_DNS_Resolver::rawQuery(). This is useful when doing manual recursion.
For a description of the returned RR data object, see Net_DNS_RR.
Resolver Configuration Object Properties:
- array $nameservers An array of nameserver IP addresses that should be queried.
- int $port The port on which nameservers should be queried. The default is 53.
- array $domain The domain in which the resolver client host resides.
- array $searchlist An array of strings containingg domains to apply to unqualified hosts passed to the resolver.
- int $retry The number of seconds between retransmission of unaswered queries
- int $retrans The number of times unanswered requests should be retried
- int $recurse Sets the value of the RD (recursion desired) bit in the header. If the RD bit is set to 0, the server will not perform recursion on the request.
- int $usevc Whether or not to use TCP (Virtual Circuits) instead of UDP If set to 0, UDP will be used unless TCP is required. TCP is required for questions or responses greater than 512 bytes.
- int $debug If set to TRUE (non-zero), debugging code will be displayed as the resolver makes the request.
Environment Variables:
- RES_NAMESERVERS Space separated list of nameserver IP addresses to query
- RES_SEARCHLIST Space separated list of domain names to add to unqualified search requests.
- LOCALDOMAIN The name of the domain
- RES_OPTIONS A space separated list of options formatted as: optionname:value If the value is ommited, the value defaults to 1 (true). [optionname] corresponds to an object property.
Example
Using Net_DNS_Resolver::query()
<?php
require_once 'Net/DNS.php';
$resolver = new Net_DNS_Resolver();
$response = $resolver->query('example.com');
if ($response) {
foreach ($response->answer as $rr) {
$rr->display();
}
}
?>
Output:
example.com. 129808 IN A 192.0.34.166
The following example shows a DNS query for an MX record. Note that the IP address for the mail exchanger listed within the zone is returned with the response in the additional section. The second exchanger (that is not inside this zone) is not listed. To receive this address, you must perform another query specifically for the A record using the returned hostname.
Using Net_DNS_Resolver::query() to look up an MX record
<?php
require_once 'Net/DNS.php';
$resolver = new Net_DNS_Resolver();
$response = $resolver->query('php.net', 'MX');
if ($response) {
foreach ($response->answer as $rr) {
$rr->display();
}
if (count($response->additional)) {
foreach ($response->additional as $rr) {
$rr->display();
}
}
}
?>
Output:
php.net. 86121 IN MX 15 smtp.osuosl.org.
php.net. 86121 IN MX 5 osu1.php.net.
osu1.php.net. 86121 IN A 140.211.166.39
The next example shows a more complex query with debugging information enabled. Note that the usevc option is set to TRUE. This forces the resolver to use TCP instead of UDP. This can be seen in the debug output on the send_tcp() line.
Using Net_DNS_Resolver::query() with specific nameservers and options
<?php
require_once 'Net/DNS.php';
$resolver = new Net_DNS_Resolver();
$resolver->debug = 1; // Turn on debugging output to show the query
$resolver->usevc = 1; // Force the use of TCP instead of UDP
$resolver->nameservers = array( // Set the IP addresses
'198.41.0.4', // of the nameservers
'192.228.79.201' // to query.
);
$response = $resolver->query('example.com');
if (! $response) {
echo "\n";
echo "ANCOUNT is 0, therefore the query() 'failed'\n";
echo "See Net_DNS_Resolver::rawQuery() to receive this packet\n";
}
?>
Output:
;; query(example.com, A, IN)
;; send_tcp(198.41.0.4:53)
;; sending 29 bytes
;; received 517 bytes
;; HEADER SECTION
;; id = 58298
;; qr = 1 opcode = QUERY aa = 0 tc = 0 rd = 1
;; ra = 0 rcode = NOERROR
;; qdcount = 1 ancount = 0 nscount = 13 arcount = 15
;; QUESTION SECTION (1 record)
;;
;example.com. IN A
;; ANSWER SECTION (0 records)
;; AUTHORITY SECTION (13 records)
com. 172800 IN NS A.GTLD-SERVERS.NET.
com. 172800 IN NS G.GTLD-SERVERS.NET.
com. 172800 IN NS H.GTLD-SERVERS.NET.
com. 172800 IN NS C.GTLD-SERVERS.NET.
com. 172800 IN NS I.GTLD-SERVERS.NET.
com. 172800 IN NS B.GTLD-SERVERS.NET.
com. 172800 IN NS D.GTLD-SERVERS.NET.
com. 172800 IN NS L.GTLD-SERVERS.NET.
com. 172800 IN NS F.GTLD-SERVERS.NET.
com. 172800 IN NS J.GTLD-SERVERS.NET.
com. 172800 IN NS K.GTLD-SERVERS.NET.
com. 172800 IN NS E.GTLD-SERVERS.NET.
com. 172800 IN NS M.GTLD-SERVERS.NET.
;; ADDITIONAL SECTION (15 records)
A.GTLD-SERVERS.NET. 172800 IN AAAA 2001:503:a83e::2:30
A.GTLD-SERVERS.NET. 172800 IN A 192.5.6.30
G.GTLD-SERVERS.NET. 172800 IN A 192.42.93.30
H.GTLD-SERVERS.NET. 172800 IN A 192.54.112.30
C.GTLD-SERVERS.NET. 172800 IN A 192.26.92.30
I.GTLD-SERVERS.NET. 172800 IN A 192.43.172.30
B.GTLD-SERVERS.NET. 172800 IN AAAA 2001:503:231d::2:30
B.GTLD-SERVERS.NET. 172800 IN A 192.33.14.30
D.GTLD-SERVERS.NET. 172800 IN A 192.31.80.30
L.GTLD-SERVERS.NET. 172800 IN A 192.41.162.30
F.GTLD-SERVERS.NET. 172800 IN A 192.35.51.30
J.GTLD-SERVERS.NET. 172800 IN A 192.48.79.30
K.GTLD-SERVERS.NET. 172800 IN A 192.52.178.30
E.GTLD-SERVERS.NET. 172800 IN A 192.12.94.30
M.GTLD-SERVERS.NET. 172800 IN A 192.55.83.30
ANCOUNT is 0, therefore the query() 'failed'
See Net_DNS_Resolver::rawQuery() to receive this packet
Note
This function can not be called statically.
Net_DNS_Resolver::rawQuery()
Net_DNS_Resolver::rawQuery() – Queries a nameserver and returns a response
Synopsis
require_once('Net/DNS.php');
Net_DNS_Packet Net_DNS_Resolver::rawQuery (
string $hostname
, string $type = 'A'
, string $class = 'IN'
)
Description
-
hostname- The name to lookup (eg. www.php.net) -
type- The record type to query -
class- The zone class to query
The Net_DNS_Resolver::rawQuery() function performs a DNS query similar to the Net_DNS_Resolver::query() function; however, rawQuery() will return any response from the nameserver. This is useful when the response packet may or may not contain any resource records in the "ANSWER" section.
rawQuery() uses the same resolver configuration used by Net_DNS_Resolver::query().
For a description of the returned RR data object, see Net_DNS_RR.
Example
Using Net_DNS_Resolver::rawQuery()
<?php
require_once 'Net/DNS.php';
$resolver = new Net_DNS_Resolver();
$response = $resolver->rawQuery('example.com');
if ($response) {
if (count($response->answer)) {
foreach ($response->answer as $rr) {
$rr->display();
}
}
}
?>
Output:
example.com. 129808 IN A 192.0.34.166
Note
This function can not be called statically.
Net_DNS_Resolver::axfr()
Net_DNS_Resolver::axfr() – Performs a zone transfer from a nameserver
Synopsis
require_once('Net/DNS.php');
Net_DNS_Packet Net_DNS_Resolver::axfr (
string $dname
, string $class = 'IN'
, boolean $old = FALSE
)
Description
-
dname- The domain name (zone name) to transfer -
class- The zone class to transfer -
old- Only used for backwards compatibility with previous version of Net_DNS
axfr() attempts a zone transfer from the nameservers specified in the Net_DNS_Resolver->nameservers array. Net_DNS_Resolver::axfr() uses the same resolver configuration as the Net_DNS_Resolver::query() method.
Most public nameservers will not allow a zone transfer by default. A zone transfer will provide a full list of DNS resource records inside of a zone file. A zone transfer will always use TCP instead of UDP queries.
For a description of the returned RR data object, see Net_DNS_RR.
Example
Failed Net_DNS_Resolver::axfr() query
<?php
require_once 'Net/DNS.php';
$resolver = new Net_DNS_Resolver();
$resolver->debug = 1;
$response = $resolver->axfr('example.com');
print_r($response);
if (count($response) == 0) {
echo "\n";
echo "AXFR Failed\n";
}
?>
Output:
;; axfr_start(example.com, IN)
;; query(example.com, AXFR, IN)
;; axfr_start(192.168.0.254:53)
;; sending 29 bytes
;; read_tcp: expecting 2 bytes
;; read_tcp: received 2 bytes
;; read_tcp: expecting 29 bytes
;; read_tcp: received 29 bytes
;; received 29bytes
;; HEADER SECTION
;; id = 29190
;; qr = 1 opcode = QUERY aa = 0 tc = 0 rd = 1
;; ra = 1 rcode = NOTAUTH
;; qdcount = 1 ancount = 0 nscount = 0 arcount = 0
;; QUESTION SECTION (1 record)
;;
;example.com. IN AXFR
;; ANSWER SECTION (0 records)
;; AUTHORITY SECTION (0 records)
;; ADDITIONAL SECTION (0 records)
Array
(
)
AXFR Failed
In the following example, debugging has been turned on and the nameserver has been configured to allow zone transfers. The most important item of note in this example is the SOA record at the beginning and end of the returned records. When a name server sends a zone transfer, the first record sent is the SOA record. The zone transfer is considered complete when the name server sends the SOA record again. This behaviour can be seen in the debug output. The resulting array returned by axfr() does not return the final SOA record.
Succesful Net_DNS_Resolver::axfr() query
<?php
require_once 'Net/DNS.php';
$resolver = new Net_DNS_Resolver();
$resolver->debug = 1;
$response = $resolver->axfr('my.example.com');
echo "\n\nThe following resource records were returned from the nameserver:\n";
if (count($response)) {
foreach ($response as $rr) {
$rr->display();
}
}
?>
Output:
;; axfr_start(my.example.com, IN)
;; query(my.example.com, AXFR, IN)
;; axfr_start(192.168.0.254:53)
;; sending 32 bytes
;; read_tcp: expecting 2 bytes
;; read_tcp: received 2 bytes
;; read_tcp: expecting 262 bytes
;; read_tcp: received 262 bytes
;; received 262bytes
;; HEADER SECTION
;; id = 21220
;; qr = 1 opcode = QUERY aa = 1 tc = 0 rd = 0
;; ra = 1 rcode = NOERROR
;; qdcount = 1 ancount = 10 nscount = 0 arcount = 0
;; QUESTION SECTION (1 record)
;;
;my.example.com. IN AXFR
;; ANSWER SECTION (10 records)
my.example.com. 300 IN SOA ns1.my.example.com. hostmaster.my.example.com. 103 3600 1800 2592000 300
my.example.com. 300 IN NS ns1.my.example.com.
my.example.com. 300 IN MX 10 mx1.my.example.com.
my.example.com. 300 IN MX 20 mx2.my.example.com.
my.example.com. 300 IN A 192.168.0.1
mx1.my.example.com. 300 IN A 192.168.0.2
mx2.my.example.com. 300 IN A 192.168.0.3
server.my.example.com. 300 IN CNAME www.my.example.com.
www.my.example.com. 300 IN A 192.168.0.1
my.example.com. 300 IN SOA ns1.my.example.com. hostmaster.my.example.com. 103 3600 1800 2592000 300
;; AUTHORITY SECTION (0 records)
;; ADDITIONAL SECTION (0 records)
The following resource records were returned from the nameserver:
my.example.com. 300 IN SOA ns1.my.example.com. hostmaster.my.example.com. 103 3600 1800 2592000 300
my.example.com. 300 IN NS ns1.my.example.com.
my.example.com. 300 IN MX 10 mx1.my.example.com.
my.example.com. 300 IN MX 20 mx2.my.example.com.
my.example.com. 300 IN A 192.168.0.1
mx1.my.example.com. 300 IN A 192.168.0.2
mx2.my.example.com. 300 IN A 192.168.0.3
server.my.example.com. 300 IN CNAME www.my.example.com.
www.my.example.com. 300 IN A 192.168.0.1
Note
This function can not be called statically.
Class Summary Net_DNS_Packet
Class Summary Net_DNS_Packet – Provides an object abstraction of a DNS packet
Net_DNS_Packet
The Net_DNS_Packet class provides methods for creating a DNS packet suitable for sending to a nameserver. It also provides the methods of parsing a response from a nameserver into a Net_DNS_Packet object.
A standard DNS packet is made up of five main pieces:
- Header
- Question Section
- Answer Section
- Authority Section
- Additional Section
Net_DNS defines the DNS packet header as a Net_DNS_Header object. Net_DNS_Question defines the question section of a packet object. The remaining three sections should be arrays of Net_DNS_RR objects.
Class Summary Net_DNS_RR
Class Summary Net_DNS_RR – Provides resource record parsing and creation
Net_DNS_RR
The Net_DNS_RR class provides methods of parsing resource records returned by a nameserver, creation of resource record objects to be sent to a nameserver, and the ability to access each component of an RR as an object property.
Each resource record object MUST contain the following properties:
name- The DNS name of the RRtype- The RR typeclass- The RR class (normally IN)ttl- The RR time to liverdlength- The amount of data (in bytes) of the rdata sectionrdata- The data (uncompressed) of the right hand side of the RR
If type is a supported RR type,
it will automatically be decompressed and/or decoded
into its appropriate property values. The values inside
of an RR object will vary based on the type of RR.
Supported RR types:
-
A
-
string
address- IPv4 style address
-
string
-
AAAA
-
string
address- IPv6 style address
-
string
-
CNAME
-
string
cname- The canonical name of the queried host
-
string
-
HINFO
-
string
cpu- The host CPU type -
string
os- Type host operating system
-
string
-
MX
-
integer
preference- The MX preference (lower takes priority -
string
exchange- The name of the mail exchange host
-
integer
-
NAPTR
-
unknown
order- unknown -
unknown
preference- unknown -
unknown
flags- unknown -
unknown
services- unknown -
unknown
regex- unknown -
unknown
replacement- unknown
-
unknown
-
NS
-
string
nsdname- The name of the NS record nameserver
-
string
-
PTR
-
string
ptrdname- The name for the queried IP address
-
string
-
SOA
-
string
mname- Master nameserver hostname as specified in the SOA record - not neccessarily accurate -
string
rname- Email address of the person responsible for the zone - not neccessarily accurate -
string
serial- The serial number (version number) of the retrieved zone -
string
refresh- The length of time before the zone should be refresed -
string
retry- The length of time between retries for slave servers to refresh the zone -
string
expire- The length of time before slave servers should consider the data invalid without refreshing the zone data. -
string
minimum- The default TTL for RRs inside of the zone that are not otherwise specified.
-
string
-
SRV
-
unknown
preference- unknown -
unknown
weight- unknown -
unknown
port- unknown -
unknown
target- unknown
-
unknown
-
TSIG
-
integer
time_signed- The time the signature has was created -
integer
fudge- The time offset that is acceptable between the client and the server -
integer
mac_size- The size of the following signature data -
string/binary
mac- The data containing the has sent by the client/server -
integer
original_id- The ID sent to or received from the server identifying this query. -
integer
error- The value of the error calculating or verifying the signature. -
integer
other_len- The amount of data (in bytes) additional to the signature -
string
other_data- Additional data required by the nameserver for the signature. -
string
key- The key shared by the client and the server to validate authorization.
-
integer
-
TXT
-
string
text- The text defined in the record
-
string
Examples -- DNS Updates
Examples -- DNS Updates – RFC 2136 DNS Updates
RFC 2136 DNS Updates
By constructing a specially formatted DNS packet and sending it to a nameserver, a dynamic DNS update can be performed very easily. RFC 2136 defines some variations of a DNS packet that are used by Net_DNS to request an update. The nameserver must first be configured to accept DNS updates, a key must be established between the client and server, and most importantly, the PHP mhash extension must be included for processing the MD5 digest of the key.
The format of the DNS packet is very specific and requires in-depth knowledge of how DNS updates work. Net_DNS currently does not support any abstraction of these specially formatted packets. Packets must currently be crafted and sent manually. The full formatting of update packets can be found in RFC 2136.
For a complete listing of all of the fields that are available for DNS updates, please review RFC2136 (http://www.ietf.org/rfc/rfc2136.txt). The following examples show the simplest forms of a DNS update.
Sending a dynamic DNS update packet to a nameserver
<?php
require_once 'Net/DNS.php';
$resolver = new Net_DNS_Resolver();
// We should only send the request to the master server
// accepting DNS updates for the zone.
$resolver->nameservers = array('192.168.0.254');
// We must manually construct the DNS packet for a DNS update
// First we must instantiate the packet object
$packet = new Net_DNS_Packet();
// Create the header for the update packet. Most of the defaults are
// acceptable, but we must set the header OPCODE to "UPDATE"
$packet->header = new Net_DNS_Header();
$packet->header->id = $resolver->nextid();
$packet->header->qr = 0;
$packet->header->opcode = "UPDATE";
// As specified in RFC2136, the question section becomes the "ZONE" section.
// This specifies the zone for which we are requesting a change. This
// reflects the zone configuration as specified in the nameserver
// configuration.
$packet->question[0] = new Net_DNS_Question('example.com', "SOA", "IN");
// The "ANSWER" section of the packet becomes the "PREREQUISITE" section of
// the packet. Section 2.4 of RFC2136 defines the possible values for this
// section. As show below, without any prerequisites the nameserver will
// attempt to perform the update unconditionally.
$packet->answer = array();
// The AUTHORITY section becomes the "UPDATE" section of the DNS packet. The
// UPDATE section is a collection of resource record updates that should be
// modified in one form or another. RRs can be deleted or added based on the
// values passed in the RR object. These values are specified in RFC2136 but
// are summarized here:
//
// Adding an RR
// A complete RR object with a non-zero TTL is considered an addition.
//
// Deleting an RR
// A complete RR object with a zero TTL is considered an deletion. An RR that
// matches (exactly) with all values except for the TTL will be removed.
//
// Deleting an RRset
// A complete RR object with a zero TTL and a type of ANY is considered a
// deletion of all RRs with the specified name and type. An RR that matches
// (exactly) with all values except for the TTL and the TYPE will be removed.
//
// Deleting all RRsets for a name
// A complete RR object with a zero TTL, a type of ANY, and a class of ANY is
// considered a deletion of all RRs with the specified name. Any RR that
// matches the name section of the query will be removed.
//
// The following specification will delete the RR that has a name of
// "example.com", class of "IN", type of "A", and an address of
// "192.0.34.166".
$rrDelete =& Net_DNS_RR::factory("example.com. 0 IN A 192.0.34.166");
//
// The following specification will add an RR that has a name of example.com,
// a TTL of 1 hour, a class of "IN", type of "A", and an address of
// "192.0.34.155"). Note that the only difference between this RR and the
// previous RR is the value of the TTL.
$rrAdd =& Net_DNS_RR::factory("example.com. 3600 IN A 192.0.34.166");
//
// The RR modifications are added to the authority (UPDATE) section of the DNS
// packet.
$packet->authority[0] = $rrDelete;
$packet->authority[1] = $rrAdd;
//
// The signature must be present in any packet sent to a nameserver that
// requires authentication. The TSIG RR is added to the additional section of
// the DNS packet.
$tsig =& Net_DNS_RR::factory("keyname.as.specified.in.server. TSIG ThisIsMyKey");
$packet->additional = array($tsig);
// Net_DNS does not automatically calculate the number of records stored in
// each section. This calculation must be done manually.
$packet->header->qdcount = count($packet->question);
$packet->header->ancount = count($packet->answer);
$packet->header->nscount = count($packet->authority);
$packet->header->arcount = count($packet->additional);
//
// After creating your packet, you must send it to the name server for
// processing. DNS updates must use the send_tcp() method:
$response = $resolver->send_tcp($packet, $packet->data());
//
// The response from the server will vary. If the update was successfuly, the
// server will have a response code of "NOERROR". Any other error types will
// be reported in the response packet's header "rcode" variable.
if ($response->header->rcode != "NOERROR") {
return($response->header->rcode);
}
?>