d_ws = new WebServer(arg()["webserver-address"], arg().asNum("webserver-port"));
d_ws->setApiKey(arg()["api-key"]);
d_ws->setPassword(arg()["webserver-password"]);
+ d_ws->setLogLevel(arg()["webserver-loglevel"]);
NetmaskGroup acl;
acl.toMasks(::arg()["webserver-allow-from"]);
ret<<"Total queries: "<<S.read("udp-queries")<<". Question/answer latency: "<<S.read("latency")/1000.0<<"ms</p><br>"<<endl;
if(req->getvars["ring"].empty()) {
- vector<string>entries=S.listRings();
- for(vector<string>::const_iterator i=entries.begin();i!=entries.end();++i)
- printtable(ret,*i,S.getRingTitle(*i));
+ auto entries = S.listRings();
+ for(const auto &i: entries) {
+ printtable(ret, i, S.getRingTitle(i));
+ }
printvars(ret);
if(arg().mustDo("webserver-print-arguments"))
out["uptime"] = std::to_string(time(0) - s_starttime);
}
+boost::optional<uint64_t> productServerStatisticsFetch(const std::string& name)
+{
+ try {
+ // ::read() calls ::exists() which throws a PDNSException when the key does not exist
+ return S.read(name);
+ }
+ catch(...) {
+ return boost::none;
+ }
+}
+
static void validateGatheredRRType(const DNSResourceRecord& rr) {
if (rr.qtype.getCode() == QType::OPT || rr.qtype.getCode() == QType::TSIG) {
throw ApiException("RRset "+rr.qname.toString()+" IN "+rr.qtype.getName()+": invalid type given");
}
}
-static void gatherRecords(const Json container, const DNSName& qname, const QType qtype, const int ttl, vector<DNSResourceRecord>& new_records, vector<DNSResourceRecord>& new_ptrs) {
+static void gatherRecords(const string& logprefix, const Json container, const DNSName& qname, const QType qtype, const int ttl, vector<DNSResourceRecord>& new_records, vector<DNSResourceRecord>& new_ptrs) {
UeberBackend B;
DNSResourceRecord rr;
rr.qname = qname;
if ((rr.qtype.getCode() == QType::A || rr.qtype.getCode() == QType::AAAA) &&
boolFromJson(record, "set-ptr", false) == true) {
+
+ g_log<<Logger::Warning<<logprefix<<"API call uses deprecated set-ptr feature, please remove it"<<endl;
+
DNSResourceRecord ptr;
makePtr(rr, &ptr);
string master = value.string_value();
if (master.empty())
throw ApiException("Master can not be an empty string");
+ try {
+ ComboAddress m(master);
+ } catch (const PDNSException &e) {
+ throw ApiException("Master (" + master + ") is not an IP address: " + e.reason);
+ }
zonemaster.push_back(master);
}
}
if (rrset["records"].is_array()) {
int ttl = intFromJson(rrset, "ttl");
- gatherRecords(rrset, qname, qtype, ttl, new_records, new_ptrs);
+ gatherRecords(req->logprefix, rrset, qname, qtype, ttl, new_records, new_ptrs);
}
if (rrset["comments"].is_array()) {
gatherComments(rrset, qname, qtype, new_comments);
domains.push_back(di);
}
} else {
- B.getAllDomains(&domains, true); // incl. disabled
+ try {
+ B.getAllDomains(&domains, true); // incl. disabled
+ } catch(const PDNSException &e) {
+ throw HttpInternalServerErrorException("Could not retrieve all domain information: " + e.reason);
+ }
}
Json::array doc;
UeberBackend B;
DomainInfo di;
- if (!B.getDomainInfo(zonename, di)) {
- throw HttpNotFoundException();
+ try {
+ if (!B.getDomainInfo(zonename, di)) {
+ throw HttpNotFoundException();
+ }
+ } catch(const PDNSException &e) {
+ throw HttpInternalServerErrorException("Could not retrieve Domain Info: " + e.reason);
}
if(req->method == "PUT") {
if(!di.backend->deleteDomain(zonename))
throw ApiException("Deleting domain '"+zonename.toString()+"' failed: backend delete failed/unsupported");
+ // clear caches
+ DNSSECKeeper dk(&B);
+ dk.clearCaches(zonename);
+ purgeAuthCaches(zonename.toString() + "$");
+
// empty body on success
resp->body = "";
resp->status = 204; // No Content: declare that the zone is gone now
// ttl shouldn't be part of DELETE, and it shouldn't be required if we don't get new records.
int ttl = intFromJson(rrset, "ttl");
// new_ptrs is merged.
- gatherRecords(rrset, qname, qtype, ttl, new_records, new_ptrs);
+ gatherRecords(req->logprefix, rrset, qname, qtype, ttl, new_records, new_ptrs);
for(DNSResourceRecord& rr : new_records) {
rr.domain_id = di.id;
di.backend->lookup(QType(QType::ANY), qname);
DNSResourceRecord rr;
while (di.backend->get(rr)) {
- if (qtype.getCode() == 0) {
+ if (rr.qtype.getCode() == 0) {
ent_present = true;
+ /* that's fine, we will override it */
+ continue;
}
if (qtype.getCode() != rr.qtype.getCode()
&& (exclusiveEntryTypes.count(qtype.getCode()) != 0
string q = req->getvars["q"];
string sMax = req->getvars["max"];
+ string sObjectType = req->getvars["object_type"];
+
int maxEnts = 100;
int ents = 0;
+ // the following types of data can be searched for using the api
+ enum class ObjectType
+ {
+ ALL,
+ ZONE,
+ RECORD,
+ COMMENT
+ } objectType;
+
if (q.empty())
throw ApiException("Query q can't be blank");
if (!sMax.empty())
if (maxEnts < 1)
throw ApiException("Maximum entries must be larger than 0");
+ if (sObjectType.empty())
+ objectType = ObjectType::ALL;
+ else if (sObjectType == "all")
+ objectType = ObjectType::ALL;
+ else if (sObjectType == "zone")
+ objectType = ObjectType::ZONE;
+ else if (sObjectType == "record")
+ objectType = ObjectType::RECORD;
+ else if (sObjectType == "comment")
+ objectType = ObjectType::COMMENT;
+ else
+ throw ApiException("object_type must be one of the following options: all, zone, record, comment");
+
SimpleMatch sm(q,true);
UeberBackend B;
vector<DomainInfo> domains;
for(const DomainInfo di: domains)
{
- if (ents < maxEnts && sm.match(di.zone)) {
+ if ((objectType == ObjectType::ALL || objectType == ObjectType::ZONE) && ents < maxEnts && sm.match(di.zone)) {
doc.push_back(Json::object {
{ "object_type", "zone" },
{ "zone_id", apiZoneNameToId(di.zone) },
zoneIdZone[di.id] = di; // populate cache
}
- if (B.searchRecords(q, maxEnts, result_rr))
+ if ((objectType == ObjectType::ALL || objectType == ObjectType::RECORD) && B.searchRecords(q, maxEnts, result_rr))
{
for(const DNSResourceRecord& rr: result_rr)
{
}
}
- if (B.searchComments(q, maxEnts, result_c))
+ if ((objectType == ObjectType::ALL || objectType == ObjectType::COMMENT) && B.searchComments(q, maxEnts, result_c))
{
for(const Comment &c: result_c)
{