From: Bert Hubert Date: Sun, 4 Oct 2009 07:42:10 +0000 (+0000) Subject: initial load of the 'MyDNS' compatability backend X-Git-Tag: rec-3.2~121 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=4dad70693039a752f93ee0ee5cbd1c858cad8803;p=pdns initial load of the 'MyDNS' compatability backend git-svn-id: svn://svn.powerdns.com/pdns/trunk/pdns@1418 d19b8d6e-7fed-0310-83ef-9ca221ded41b --- diff --git a/configure.in b/configure.in index bf3a20a14..a9a9d20c1 100644 --- a/configure.in +++ b/configure.in @@ -214,6 +214,9 @@ do gmysql ) needmysql=yes ;; + mydns ) + needmysql=yes + ;; gpgsql ) needpgsql=yes ;; @@ -545,4 +548,4 @@ modules/pipebackend/Makefile modules/oraclebackend/Makefile \ modules/xdbbackend/Makefile modules/odbcbackend/Makefile \ modules/gpgsqlbackend/Makefile modules/ldapbackend/Makefile modules/gsqlitebackend/Makefile modules/gsqlite3backend/Makefile -modules/goraclebackend/Makefile ) +modules/goraclebackend/Makefile modules/mydnsbackend/Makefile) diff --git a/modules/mydnsbackend/Makefile.am b/modules/mydnsbackend/Makefile.am new file mode 100644 index 000000000..5988bf1e1 --- /dev/null +++ b/modules/mydnsbackend/Makefile.am @@ -0,0 +1,7 @@ +AM_CPPFLAGS=@THREADFLAGS@ +INCLUDES=-I@MYSQL_incdir@ +lib_LTLIBRARIES = libmydnsbackend.la +EXTRA_DIST=OBJECTFILES OBJECTLIBS +libmydnsbackend_la_SOURCES=mydnsbackend.cc mydnsbackend.hh +libmydnsbackend_la_LDFLAGS=-module -avoid-version @MYSQL_lib@ -lmysqlclient + diff --git a/modules/mydnsbackend/OBJECTFILES b/modules/mydnsbackend/OBJECTFILES new file mode 100644 index 000000000..1cd6d2168 --- /dev/null +++ b/modules/mydnsbackend/OBJECTFILES @@ -0,0 +1 @@ +mydnsbackend.o diff --git a/modules/mydnsbackend/OBJECTLIBS b/modules/mydnsbackend/OBJECTLIBS new file mode 100644 index 000000000..175432ca4 --- /dev/null +++ b/modules/mydnsbackend/OBJECTLIBS @@ -0,0 +1 @@ +-lmysqlclient -lz diff --git a/modules/mydnsbackend/mydnsbackend.cc b/modules/mydnsbackend/mydnsbackend.cc new file mode 100644 index 000000000..7c89b556c --- /dev/null +++ b/modules/mydnsbackend/mydnsbackend.cc @@ -0,0 +1,357 @@ +/* + * PowerDNS backend module for MyDNS style databases + * Author: Jonathan Oddy (Hostway UK) + * + * The schema used by MyDNS isn't suitable for retrieving results with a single + * query. This means that existing PowerDNS backends are unable to make use of + * the schema without lame hackery (or awful performance.) This module does + * the nasty lookup logic required to make use of the schema, and should be as + * tollerent as MyDNS when it comes to things being fully qualified or not. + * + * A known "bug" is that AXFRs will fail if your rr table contains invalid + * junk. I'm not sure this is really a bug, if you've decided to put free-form + * text in your data for an A record you have bigger issues. + * + * I'd advise avoiding the MyDNS schema if at all possible as the query count + * for even simple lookups is daft. It's quite trivial to craft a request + * that'll require 128 database queries to answer with a servfail! + * + */ + +#include +#include +#include +#include +#include + +using namespace std; + +#include +#include +#include "mydnsbackend.hh" +#include +#include +#include +#include +#include + +#include + +static string backendName="[MyDNSbackend]"; + +MyDNSBackend::MyDNSBackend(const string &suffix) { + setArgPrefix("mydns"+suffix); + + try { + d_db = new SMySQL(getArg("dbname"), + getArg("host"), + getArgAsNum("port"), + getArg("socket"), + getArg("user"), + getArg("password")); + + } + catch(SSqlException &e) { + L<doQuery(query); + } catch (SSqlException &e) { + throw AhuException("Query failed: "+e.txtReason()); + } +} + +bool MyDNSBackend::list(const string &target, int zoneId) { + string query; + string sname; + SSql::row_t rrow; + + d_db->setLog(::arg().mustDo("query-logging")); + + query = "select origin, minimum from "+d_soatable+" where id = "; + ostringstream o; + o<Query(query); + + if(!d_db->getRow(rrow)) { + // No such zone + return false; + } + + d_origin = rrow[0]; + if (d_origin[d_origin.length()-1] == '.') + d_origin.erase(d_origin.length()-1); + d_minimum = atol(rrow[1].c_str()); + + while (d_db->getRow(rrow)) { + L<Query(query); + + d_qname = ""; + return true; + +} + +bool MyDNSBackend::getSOA(const string& name, SOAData& soadata, DNSPacket*) { + string query; + SSql::row_t rrow; + + d_db->setLog(::arg().mustDo("query-logging")); + + if (name.empty()) + return false; + + query = "select id, mbox, serial, ns, refresh, retry, expire, minimum, ttl from "+d_soatable+" where origin = '"; + + if (name.find_first_of("'\\")!=string::npos) + query+=d_db->escape(name); + else + query+=name; + + query+=".' and "+d_soawhere; + + this->Query(query); + + if(!(d_db->getRow(rrow))) { + return false; + } + + soadata.domain_id = atol(rrow[0].c_str()); + soadata.hostmaster = rrow[1]; + soadata.serial = atol(rrow[2].c_str()); + soadata.nameserver = rrow[3]; + soadata.refresh = atol(rrow[4].c_str()); + soadata.retry = atol(rrow[5].c_str()); + soadata.expire = atol(rrow[6].c_str()); + soadata.default_ttl = atol(rrow[7].c_str()); + soadata.ttl = atol(rrow[8].c_str()); + if (soadata.ttl < soadata.default_ttl) { + soadata.ttl = soadata.default_ttl; + } + soadata.db = this; + + while (d_db->getRow(rrow)) { + L<setLog(::arg().mustDo("query-logging")); + + if (qname.empty()) + return; + + // Escape the name, after this point we only want to use it in queries + if (qname.find_first_of("'\\")!=string::npos) + sname=d_db->escape(qname); + else + sname = qname; + sname += "."; + + if (zoneId < 0) { + // First off we need to work out what zone we're working with + // MyDNS records aren't always fully qualified, so we need to work out the zone ID. + + size_t pos; + string sdom; + + pos = 0; + sdom = sname; + while (!sdom.empty() && pos != string::npos) { + query = "select id, origin, minimum from "+d_soatable+" where origin = '"+sdom+"' and "+d_soawhere; + + this->Query(query); + if(d_db->getRow(rrow)) { + zoneId = atol(rrow[0].c_str()); + d_origin = rrow[1]; + if (d_origin[d_origin.length()-1] == '.') + d_origin.erase(d_origin.length()-1); + d_minimum = atol(rrow[2].c_str()); + found = true; + break; + } + + pos = sname.find_first_of(".",pos+1); + sdom = sname.substr(pos+1); + } + + } else { + query = "select origin, minimum from "+d_soatable+" where id = "; + ostringstream o; + o<Query(query); + + if(!d_db->getRow(rrow)) { + throw AhuException("lookup() passed zoneId = "+o.str()+" but no such zone!"); + } + + found = true; + d_origin = rrow[0]; + if (d_origin[d_origin.length()-1] == '.') + d_origin.erase(d_origin.length()-1); + d_minimum = atol(rrow[1].c_str()); + } + + + if (found) { + while (d_db->getRow(rrow)) { + L<escape(host); + + query = "select type, data, aux, ttl, zone from "+d_rrtable+" where zone = "; + ostringstream o; + o<Query(query); + + d_qname = qname; + } + +} + +bool MyDNSBackend::get(DNSResourceRecord &rr) { + if (d_origin.empty()) { + // This happens if lookup() couldn't find the zone + return false; + } + + SSql::row_t rrow; + + if(!d_db->getRow(rrow)) { + return false; + } + + rr.qtype=rrow[0]; + rr.content = rrow[1]; + + if(!d_qname.empty()) { + // use this to distinguish between select with 'name' field (list()) and one without + rr.qname=d_qname; + } else { + rr.qname=rrow[5]; + if (rr.qname[rr.qname.length()-1] == '.') { + rr.qname.erase(rr.qname.length()-1); // Fully qualified, nuke the last . + } else { + if (!rr.qname.empty()) + rr.qname += "."; + rr.qname += d_origin; // Not fully qualified + } + } + + if (rr.qtype == "NS" || rr.qtype == "MX" || rr.qtype == "CNAME" || rr.qtype == "PTR") { + if (rr.content[rr.content.length()-1] == '.') { + rr.content.erase(rr.content.length()-1); // Fully qualified, nuke the last . + } else { + if (!rr.content.empty()) + rr.content += "."; + rr.content += d_origin; + } + } + + rr.priority = atol(rrow[2].c_str()); + rr.ttl = atol(rrow[3].c_str()); + if (rr.ttl < d_minimum) + rr.ttl = d_minimum; + rr.domain_id=atol(rrow[4].c_str()); + + + rr.last_modified=0; + + return true; + +} + +class MyDNSFactory : public BackendFactory { + +public: + MyDNSFactory() : BackendFactory("mydns") {} + + void declareArguments(const string &suffix = "") { + declare(suffix,"dbname","Pdns backend database name to connect to","mydns"); + declare(suffix,"user","Pdns backend user to connect as","powerdns"); + declare(suffix,"host","Pdns backend host to connect to",""); + declare(suffix,"port","Pdns backend host to connect to",""); + declare(suffix,"password","Pdns backend password to connect with",""); + declare(suffix,"socket","Pdns backend socket to connect to",""); + declare(suffix,"rr-table","Name of RR table to use","rr"); + declare(suffix,"soa-table","Name of SOA table to use","soa"); + declare(suffix,"soa-where","Additional WHERE clause for SOA","1 = 1"); + declare(suffix,"rr-where","Additional WHERE clause for RR","1 = 1"); + declare(suffix,"soa-active","Use the active column in the SOA table","yes"); + declare(suffix,"rr-active","Use the active column in the RR table","yes"); + } + + MyDNSBackend *make(const string &suffix = "") { + return new MyDNSBackend(suffix); + } + +}; + +class MyDNSLoader { + +public: + MyDNSLoader() { + BackendMakers().report(new MyDNSFactory()); + L< +#include + +using namespace std; + +#include + +class MyDNSBackend : public DNSBackend +{ +public: + MyDNSBackend(const string &suffix=""); + ~MyDNSBackend(); + MyDNSBackend *parent; + + void lookup(const QType &, const string &qdomain, DNSPacket *p=0, int zoneId=-1); + bool list(const string &target, int domain_id); + bool get(DNSResourceRecord &r); + bool getSOA(const string& name, SOAData& soadata, DNSPacket*); + +private: + void Query(const string& query); + SMySQL *d_db; + + string d_qname; + string d_rrtable; + string d_soatable; + string d_soawhere; + string d_rrwhere; + string d_origin; + unsigned int d_minimum; + +}; +#endif /* MYDNSBACKEND_HH */