PTR records provide a reverse mapping from a single IP or set of IP addresses to a domain. For example,
$ dig -x 192.0.2.5 +short
example.org.
The way this works in the DNS system is through the in-addr.arpa. zone. For example
$ dig example.org +short
192.0.2.12
$ dig -x 192.0.2.12
; <<>> DiG 9.9.5-3ubuntu0.1-Ubuntu <<>> -x 192.0.2.12
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 3431
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4000
;; QUESTION SECTION:
;12.55.168.192.in-addr.arpa. IN PTR example.org.
;; AUTHORITY SECTION:
12.55.168.192.in-addr.arpa. 3600 IN NS ns1.example.org.
;; Query time: 40 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Fri Feb 20 19:05:44 UTC 2015
;; MSG SIZE rcvd: 119
In the question section we see the address being requested from the DNS system as 12.55.168.192.in-addr.arpa.. As you can see, the IP address has been reversed in order to function similarly to a domain name where the more specific elements come first. The reversed IP address is then added to the in-addr.arpa. domain, at which point the DNS system can perform a simple look up to find any PTR records that describe what domain name, if any, maps to that IP.
To create a PTR record in Designate, there are two requirements.
- A domain that should be pointed to from the IP
- A in-addr.arpa. zone entry that will receive the actual PTR record
To begin let’s create a zone that we want to return when we do our reverse lookup.
POST /v2/zones HTTP/1.1
Accept: application/json
Content-Type: application/json
{
"name": "example.org.",
"email": "admin@example.org",
"ttl": 3600,
"description": "A great example zone"
}
Here is the JSON response describing the new zone.
HTTP/1.1 202 Accepted
Location: http://127.0.0.1:9001/v2/zones/fe078042-0aa3-4500-a81e-8f328f79bf75
Content-Length: 476
Content-Type: application/json; charset=UTF-8
X-Openstack-Request-Id: req-bfcd0723-624c-4ec2-bbd5-99e985efe8db
Date: Fri, 20 Feb 2015 21:20:28 GMT
Connection: keep-alive
{
"email": "admin@example.org",
"project_id": "noauth-project",
"action": "CREATE",
"version": 1,
"pool_id": "794ccc2c-d751-44fe-b57f-8894c9f5c842",
"created_at": "2015-02-20T21:20:28.000000",
"name": "example.org.",
"id": "fe078042-0aa3-4500-a81e-8f328f79bf75",
"serial": 1424467228,
"ttl": 3600,
"updated_at": null,
"links": {
"self": "http://127.0.0.1:9001/v2/zones/fe078042-0aa3-4500-a81e-8f328f79bf75"
},
"description": "A great example zone",
"status": "PENDING"
}
Note
The status is PENDING. If we make a GET request to the self field in the zone, it will most likely have been processed and updated to ACTIVE.
Now that we have a zone we’d like to use for our reverse DNS lookup, we need to add an in-addr.arpa. zone that includes the IP address we’ll be looking up.
Let’s configure 192.0.2.11 to return our example.org. domain name when we do a reverse look up.
POST /v2/zones HTTP/1.1
Accept: application/json
Content-Type: application/json
{
"name": "11.2.0.192.in-addr.arpa.",
"email": "admin@example.org",
"ttl": 3600,
"description": "A in-addr.arpa. zone for reverse lookups."
}
As you can see, in the name field we’ve reversed our IP address and used that as a subdomain in the in-addr.arpa. zone.
Here is the response.
HTTP/1.1 202 Accepted
Location: http://127.0.0.1:9001/v2/zones/1bed5d24-d487-4410-b813-f1c637db0ba3
Content-Length: 512
Content-Type: application/json; charset=UTF-8
X-Openstack-Request-Id: req-4e691123-045e-4f8e-ae50-b5eabb5af3fa
Date: Fri, 20 Feb 2015 21:35:41 GMT
Connection: keep-alive
{
"email": "admin@example.org",
"project_id": "noauth-project",
"action": "CREATE",
"version": 1,
"pool_id": "794ccc2c-d751-44fe-b57f-8894c9f5c842",
"created_at": "2015-02-20T21:35:41.000000",
"name": "11.2.0.192.in-addr.arpa.",
"id": "1bed5d24-d487-4410-b813-f1c637db0ba3",
"serial": 1424468141,
"ttl": 3600,
"updated_at": null,
"links": {
"self": "http://127.0.0.1:9001/v2/zones/1bed5d24-d487-4410-b813-f1c637db0ba3"
},
"description": "A in-addr.arpa. zone for reverse lookups.",
"status": "PENDING"
}
Now that we have our in-addr.arpa. zone, we add a new PTR record to the zone.
POST /v2/zones/1bed5d24-d487-4410-b813-f1c637db0ba3/recordsets HTTP/1.1
Content-Type: application/json
Accept: application/json
{
"name": "11.2.0.192.in-addr.arpa.",
"description": "A PTR recordset",
"type": "PTR",
"ttl": 3600,
"records": [
"example.org."
]
}
Here is the response.
HTTP/1.1 202 Accepted
Location: http://127.0.0.1:9001/v2/zones/1bed5d24-d487-4410-b813-f1c637db0ba3/recordsets/a3dca24e-3eba-4523-8607-c0ad4b9a9272
Content-Length: 499
Content-Type: application/json; charset=UTF-8
X-Openstack-Request-Id: req-5b7044d0-591a-445a-839f-1403b1455824
Date: Fri, 20 Feb 2015 21:42:45 GMT
Connection: keep-alive
{
"type": "PTR",
"action": "CREATE",
"version": 1,
"created_at": "2015-02-20T21:42:45.000000",
"zone_id": "1bed5d24-d487-4410-b813-f1c637db0ba3",
"name": "11.2.0.192.in-addr.arpa.",
"id": "a3dca24e-3eba-4523-8607-c0ad4b9a9272",
"ttl": 3600,
"records": [
"example.org."
],
"updated_at": null,
"links": {
"self": "http://127.0.0.1:9001/v2/zones/1bed5d24-d487-4410-b813-f1c637db0ba3/recordsets/a3dca24e-3eba-4523-8607-c0ad4b9a9272"
},
"description": "A PTR recordset",
"status": "PENDING"
}
We should now have a correct PTR record assigned in our nameserver that we can test.
Note
As the in-addr.arpa. zone is considered an admin zone, you may need to get admin rights in order to create the necessary subdomains.
Let’s test it out!
$ dig @localhost -x 192.0.2.11
; <<>> DiG 9.9.5-3ubuntu0.1-Ubuntu <<>> @localhost -x 192.0.2.11
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 32832
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;11.2.0.192.in-addr.arpa. IN PTR
;; ANSWER SECTION:
11.2.0.192.in-addr.arpa. 3600 IN PTR example.org.
;; AUTHORITY SECTION:
11.2.0.192.in-addr.arpa. 3600 IN NS ns1.example.org.
;; Query time: 3 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Fri Feb 20 21:45:53 UTC 2015
;; MSG SIZE rcvd: 98
As you can see from the answer section everything worked as expected.
You can add many PTR records to a larger subnet by using a more broadly defined in-addr.arpa. zone. For example, if we wanted to ensure any IP in a subnet resolves to a specific domain.
POST /v2/zones HTTP/1.1
Accept: application/json
Content-Type: application/json
{
"name": "2.0.192.in-addr.arpa.",
"ttl": 3600,
"email": "admin@example.com"
}
We then could use the corresponding domain to create a PTR record for a specific IP.
POST /v2/zones/$domain_uuid/recordsets HTTP/1.1
Accept: application/json
Content-Type: application/json
{
"name": "3.2.0.192.in-addr.arpa.",
"type": "PTR"
"ttl": 3600,
"records": [
"cats.example.com."
]
}
When we do our reverse look, we should see cats.example.com.
$ dig @localhost -x 192.0.2.3 +short
cats.example.com.
Success!
You can further specify in-addr.arpa. zones to chunks of IP addresses by using Classless in-addr.arpa. Delegation. See RFC 2317 for more information.
Note
In BIND9, when creating a new PTR we could skip the zone name. For example, if the zone is 2.0.192.in-addr.arpa., using 12 for the record name is ends up as 12.2.0.192.in-addr.arpa.. In Designate, the name of a record MUST be a complete host name.
Using the V1 REST interface let’s start by creating a domain.
POST /v1/domains HTTP/1.1
Content-Type: application/json
{
"name": "example.com.",
"ttl": 3600,
"email": "admin@example.com"
}
This should return the JSON document describing the new domain.
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 238
Location: http://127.0.0.1:9001/v1/domains/77c4f4aa-b8c9-4df5-af8e-b54e5fcadef7
X-Openstack-Request-Id: req-c3f8478d-1665-4b40-9545-9a856fac17ea
Date: Fri, 20 Feb 2015 19:35:37 GMT
Connection: keep-alive
{
"updated_at": null,
"ttl": 3600,
"serial": 1424460937,
"name": "example.com.",
"id": "77c4f4aa-b8c9-4df5-af8e-b54e5fcadef7",
"email": "admin@example.com",
"description": null,
"created_at": "2015-02-20T19:35:37.000000"
}
Now that we have a domain we want to return when we use our PTR record, we’ll create the in-addr.arpa. domain that will be used when looking up the IP address.
Let’s configure 192.0.2.10 to return our example.com. domain name when we do a reverse look up.
POST /v1/domains HTTP/1.1
Content-Type: application/json
{
"name": "10.2.0.192.in-addr.arpa.",
"ttl": 1200,
"email": "admin@thedns.com"
}
We should get a response like
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 252
Location: http://127.0.0.1:9001/v1/domains/d098abaa-37e3-40e5-b7c5-3794b5a0ec32
X-Openstack-Request-Id: req-bc2b1796-bd11-47a9-bb06-fd6a870a4bc2
Date: Fri, 20 Feb 2015 19:43:15 GMT
Connection: keep-alive
{
"updated_at": null,
"ttl": 1200,
"serial": 1424461395,
"name": "10.2.0.192.in-addr.arpa.",
"id": "d098abaa-37e3-40e5-b7c5-3794b5a0ec32",
"email": "admin@thedns.com",
"description": null,
"created_at": "2015-02-20T19:43:15.000000"
}
We will use this in-addr.arpa. domain to create the actual PTR record.
POST /v1/domains/d098abaa-37e3-40e5-b7c5-3794b5a0ec32/records HTTP/1.1
Content-Type: application/json
{
"name": "10.2.0.192.in-addr.arpa.",
"type": "PTR",
"data": "example.com."
}
Here is the response.
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 315
Location: http://127.0.0.1:9001/v1/domains/d098abaa-37e3-40e5-b7c5-3794b5a0ec32/records/0476ed89-9823-4f8e-a991-79422bc2e490
X-Openstack-Request-Id: req-36588ba6-e91a-4456-9706-8d156ea7cfd2
Date: Fri, 20 Feb 2015 19:48:01 GMT
Connection: keep-alive
{
"updated_at": null,
"type": "PTR",
"ttl": null,
"priority": null,
"name": "11.2.0.192.in-addr.arpa.",
"id": "0476ed89-9823-4f8e-a991-79422bc2e490",
"domain_id": "d098abaa-37e3-40e5-b7c5-3794b5a0ec32",
"description": null,
"data": "example.com.",
"created_at": "2015-02-20T19:48:01.000000"
}
We should now have a correct PTR record assigned in our nameserver that we can test.
We’ll use dig to make sure our reverse lookup is resolving correctly.
$ dig @localhost -x 192.0.2.10 +short
example.com.
It worked!