}
}
+bool boolFromJson(const rapidjson::Value& container, const char* key, const bool default_value)
+{
+ if (!container.IsObject()) {
+ throw JsonException("Container was not an object.");
+ }
+ const Value& val = container[key];
+ if (val.IsBool()) {
+ return val.GetBool();
+ } else {
+ return default_value;
+ }
+}
+
string makeStringFromDocument(const Document& doc)
{
StringBuffer output;
std::string stringFromJson(const rapidjson::Value& container, const char* key);
std::string stringFromJson(const rapidjson::Value& container, const char* key, const std::string& default_value);
bool boolFromJson(const rapidjson::Value& container, const char* key);
+bool boolFromJson(const rapidjson::Value& container, const char* key, const bool default_value);
class JsonException : public std::runtime_error
{
throw HttpMethodNotAllowedException();
}
+static void makePtr(const DNSResourceRecord& rr, DNSResourceRecord* ptr) {
+ if (rr.qtype.getCode() == QType::A) {
+ uint32_t ip;
+ if (!IpToU32(rr.content, &ip)) {
+ throw ApiException("PTR: Invalid IP address given");
+ }
+ ptr->qname = (boost::format("%u.%u.%u.%u.in-addr.arpa")
+ % ((ip >> 24) & 0xff)
+ % ((ip >> 16) & 0xff)
+ % ((ip >> 8) & 0xff)
+ % ((ip ) & 0xff)
+ ).str();
+ } else if (rr.qtype.getCode() == QType::AAAA) {
+ ComboAddress ca(rr.content);
+ string tmp;
+ for (int group = 0; group < 8; ++group) {
+ tmp += (boost::format("%04x") % ntohs(ca.sin6.sin6_addr.s6_addr16[group])).str();
+ }
+ ostringstream ss;
+ size_t npos = tmp.size();
+ while (npos--) {
+ ss << tmp[npos] << ".";
+ }
+ ss << "ip6.arpa";
+ ptr->qname = ss.str();
+ } else {
+ throw ApiException("Unsupported PTR source '" + rr.qname + "' type '" + rr.qtype.getName() + "'");
+ }
+
+ ptr->qtype = "PTR";
+ ptr->ttl = rr.ttl;
+ ptr->disabled = rr.disabled;
+ ptr->priority = 0;
+ ptr->content = rr.qname;
+}
+
static void apiServerZoneRRset(HttpRequest* req, HttpResponse* resp) {
if(req->method != "PATCH")
throw HttpMethodNotAllowedException();
else if (changetype == "REPLACE") {
vector<DNSResourceRecord> new_records;
vector<Comment> new_comments;
+ vector<DNSResourceRecord> new_ptrs;
bool replace_records = false;
bool replace_comments = false;
throw ApiException("Record "+rr.qname+" IN "+rr.qtype.getName()+" "+rr.content+": "+e.what());
}
+ if ((rr.qtype.getCode() == QType::A || rr.qtype.getCode() == QType::AAAA) &&
+ boolFromJson(record, "set-ptr", false) == true) {
+ DNSResourceRecord ptr;
+ makePtr(rr, &ptr);
+
+ // verify that there's a zone for the PTR
+ DNSPacket fakePacket;
+ SOAData sd;
+ fakePacket.qtype = QType::PTR;
+ if (!B.getAuth(&fakePacket, &sd, ptr.qname, 0))
+ throw ApiException("Could not find domain for PTR '"+ptr.qname+"' requested for '"+ptr.content+"'");
+
+ ptr.domain_id = sd.domain_id;
+ new_ptrs.push_back(ptr);
+ }
+
new_records.push_back(rr);
}
}
}
}
di.backend->commitTransaction();
+
+ // now the PTRs
+ BOOST_FOREACH(const DNSResourceRecord& rr, new_ptrs) {
+ DNSPacket fakePacket;
+ SOAData sd;
+ sd.db = (DNSBackend *)-1;
+ fakePacket.qtype = QType::PTR;
+
+ if (!B.getAuth(&fakePacket, &sd, rr.qname, 0))
+ throw ApiException("Could not find domain for PTR '"+rr.qname+"' requested for '"+rr.content+"' (while saving)");
+
+ sd.db->startTransaction(rr.qname);
+ if (!sd.db->replaceRRSet(sd.domain_id, rr.qname, rr.qtype, vector<DNSResourceRecord>(1, rr))) {
+ throw ApiException("PTR-Hosting backend does not support editing records.");
+ }
+ sd.db->commitTransaction();
+ }
+
}
else
throw ApiException("Changetype not understood");
self.assertEquals([r for r in data['records'] if r['type'] == 'NS'], payload2['records'])
self.assertEquals(data['comments'], payload['comments'])
+ def test_ZoneAutoPtrIPv4(self):
+ revzone = '0.2.192.in-addr.arpa'
+ self.create_zone(name=revzone)
+ payload, zone = self.create_zone()
+ name = payload['name']
+ # replace with qname mismatch
+ payload = {
+ 'changetype': 'replace',
+ 'name': name,
+ 'type': 'A',
+ 'records': [
+ {
+ "name": name,
+ "type": "A",
+ "priority": 0,
+ "ttl": 3600,
+ "content": '192.2.0.2',
+ "disabled": False,
+ "set-ptr": True
+ }
+ ]
+ }
+ r = self.session.patch(
+ self.url("/servers/localhost/zones/" + name + "/rrset"),
+ data=json.dumps(payload),
+ headers={'content-type': 'application/json'})
+ self.assertSuccessJson(r)
+ r = self.session.get(self.url("/servers/localhost/zones/" + revzone))
+ recs = r.json()['records']
+ print recs
+ revrec = [rec for rec in recs if rec['type'] == 'PTR']
+ self.assertEquals(revrec, [{
+ u'content': name,
+ u'disabled': False,
+ u'ttl': 3600,
+ u'priority': 0,
+ u'type': u'PTR',
+ u'name': u'2.0.2.192.in-addr.arpa'
+ }])
+
+ def test_ZoneAutoPtrIPv6(self):
+ # 2001:DB8::bb:aa
+ revzone = '8.b.d.0.1.0.0.2.ip6.arpa'
+ self.create_zone(name=revzone)
+ payload, zone = self.create_zone()
+ name = payload['name']
+ # replace with qname mismatch
+ payload = {
+ 'changetype': 'replace',
+ 'name': name,
+ 'type': 'AAAA',
+ 'records': [
+ {
+ "name": name,
+ "type": "AAAA",
+ "priority": 0,
+ "ttl": 3600,
+ "content": '2001:DB8::bb:aa',
+ "disabled": False,
+ "set-ptr": True
+ }
+ ]
+ }
+ r = self.session.patch(
+ self.url("/servers/localhost/zones/" + name + "/rrset"),
+ data=json.dumps(payload),
+ headers={'content-type': 'application/json'})
+ self.assertSuccessJson(r)
+ r = self.session.get(self.url("/servers/localhost/zones/" + revzone))
+ recs = r.json()['records']
+ print recs
+ revrec = [rec for rec in recs if rec['type'] == 'PTR']
+ self.assertEquals(revrec, [{
+ u'content': name,
+ u'disabled': False,
+ u'ttl': 3600,
+ u'priority': 0,
+ u'type': u'PTR',
+ u'name': u'a.a.0.0.b.b.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa'
+ }])
+
@unittest.skipIf(not isRecursor(), "Not applicable")
class RecursorZones(ApiTestCase):