Sophie

Sophie

distrib > Mandriva > 2010.1 > i586 > by-pkgid > 563affe035311228f138962d4d47d4fd > files > 23

pdns-3.0.1-0.1mdv2010.2.i586.rpm

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">
<HTML
><HEAD
><TITLE
>Backend writers' guide</TITLE
><META
NAME="GENERATOR"
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
REL="HOME"
TITLE="PowerDNS manual"
HREF="index.html"><LINK
REL="PREVIOUS"
TITLE="How PDNS translates DNS queries into backend queries"
HREF="dns-to-query.html"><LINK
REL="NEXT"
TITLE="Reporting errors"
HREF="backend-error-reporting.html"></HEAD
><BODY
CLASS="APPENDIX"
BGCOLOR="#FFFFFF"
TEXT="#000000"
LINK="#0000FF"
VLINK="#840084"
ALINK="#0000FF"
><DIV
CLASS="NAVHEADER"
><TABLE
SUMMARY="Header navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TH
COLSPAN="3"
ALIGN="center"
>PowerDNS manual</TH
></TR
><TR
><TD
WIDTH="10%"
ALIGN="left"
VALIGN="bottom"
><A
HREF="dns-to-query.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="80%"
ALIGN="center"
VALIGN="bottom"
></TD
><TD
WIDTH="10%"
ALIGN="right"
VALIGN="bottom"
><A
HREF="backend-error-reporting.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
></TABLE
><HR
ALIGN="LEFT"
WIDTH="100%"></DIV
><DIV
CLASS="APPENDIX"
><H1
><A
NAME="BACKEND-WRITERS-GUIDE"
></A
>Appendix C. Backend writers' guide</H1
><P
>      PDNS backends are implemented via a simple yet powerful C++ interface. If your needs are not met by the PipeBackend, you 
      may want to write your own. Before doing any PowerDNS development, please visit <A
HREF="http://wiki.powerdns.com"
TARGET="_top"
>the wiki</A
>.
    </P
><P
>      A backend contains zero DNS logic. It need not look for CNAMES, it need not return NS records unless explicitly asked for, etcetera.
      All DNS logic is contained within PDNS itself - backends should simply return records matching the description asked for. 
    </P
><P
> 
      <DIV
CLASS="WARNING"
><P
></P
><TABLE
CLASS="WARNING"
WIDTH="100%"
BORDER="0"
><TR
><TD
WIDTH="25"
ALIGN="CENTER"
VALIGN="TOP"
><IMG
SRC="../images/warning.gif"
HSPACE="5"
ALT="Warning"></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
><P
>      However, please note that your backend can get queries in aNy CAsE! If your database is case sensitive, like most are (with the notable
      exception of MySQL), you must make sure that you do find answers which differ only in case.
	</P
></TD
></TR
></TABLE
></DIV
>
    </P
><P
> 
      <DIV
CLASS="WARNING"
><P
></P
><TABLE
CLASS="WARNING"
WIDTH="100%"
BORDER="0"
><TR
><TD
WIDTH="25"
ALIGN="CENTER"
VALIGN="TOP"
><IMG
SRC="../images/warning.gif"
HSPACE="5"
ALT="Warning"></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
><P
>	  PowerDNS may instantiate multiple instances of your backend, or destroy existing copies and instantiate new ones. Backend code
	  should therefore be thread-safe with respect to its static data. Additionally, it is wise if instantiation is a fast operation, 
	  with the possible exception of the first construction.
	</P
></TD
></TR
></TABLE
></DIV
>
    </P
><DIV
CLASS="SECT1"
><H1
CLASS="SECT1"
><A
NAME="SIMPLE-BACKENDS"
>C.1. Simple read-only native backends</A
></H1
><P
>	Implementing a backend consists of inheriting from the DNSBackend class. For read-only backends, which do not support slave operation,
	only the following methods are relevant:
	
	<PRE
CLASS="PROGRAMLISTING"
>	class DNSBackend
	{
	public:

	virtual bool lookup(const QType &amp;qtype, const string &amp;qdomain, DNSPacket *pkt_p=0, int zoneId=-1)=0; 
	virtual bool list(int domain_id)=0; 
	virtual bool get(DNSResourceRecord &amp;r)=0;
	virtual bool getSOA(const string &amp;name, SOAData &amp;soadata);
	};
	</PRE
>
      
	Note that the first three methods must be implemented. <CODE
CLASS="FUNCTION"
>getSOA()</CODE
> has a useful default implementation.
      </P
><P
>	The semantics are simple. Each instance of your class only handles one (1) query at a time. There is no need for locking as PDNS guarantees
	that your backend will never be called reentrantly. 
      </P
><P
>	Some examples, a more formal specification is down below. A normal lookup starts like this:
	
	<PRE
CLASS="PROGRAMLISTING"
>        YourBackend yb;
	yb.lookup(QType::CNAME,"www.powerdns.com");
	</PRE
>
      
	Your class should now do everything to start this query. Perform as much preparation as possible - handling errors at this stage is better for PDNS 
	than doing so later on. A real error should be reported by throwing an exception. 
      </P
><P
>	PDNS will then call the <CODE
CLASS="FUNCTION"
>get()</CODE
> method to get <B
CLASS="COMMAND"
>DNSResourceRecord</B
>s back. The following code illustrates
	a typical query:
	
      <PRE
CLASS="PROGRAMLISTING"
>	yb.lookup(QType::CNAME,"www.powerdns.com");

	DNSResourceRecord rr;
	while(yb.get(rr))
   	  cout&lt;&lt;"Found cname pointing to '"+rr.content+"'"&lt;&lt;endl;
	}
	</PRE
>
      </P
><P
>	Each zone starts with a Start of Authority (SOA) record. This record is special so many backends will choose to implement it
	specially. The default <CODE
CLASS="FUNCTION"
>getSOA()</CODE
> method performs a regular lookup on your backend to figure out the SOA, 
	so if you have no special treatment for SOA records, where is no need to implement your own <CODE
CLASS="FUNCTION"
>getSOA()</CODE
>.
      </P
><P
>	Besides direct queries, PDNS also needs to be able to list a zone, to do zone transfers for example. Each zone has an id which should be
	unique within the backend. To list all records belonging to a zone id, the <CODE
CLASS="FUNCTION"
>list()</CODE
> method is used. Conveniently,
	the domain_id is also available in the <B
CLASS="COMMAND"
>SOAData</B
> structure.
      </P
><P
>	The following lists the contents of a zone called "powerdns.com".
      
	<PRE
CLASS="PROGRAMLISTING"
>	SOAData sd;
	if(!yb.getSOA("powerdns.com",sd))  // are we authoritative over powerdns.com?
	  return RCode::NotAuth;           // no

	yb.list(sd.domain_id); 
	while(yb.get(rr))
   	  cout&lt;&lt;rr.qname&lt;&lt;"\t IN "&lt;&lt;rr.qtype.getName()&lt;&lt;"\t"&lt;&lt;rr.content&lt;&lt;endl;
	</PRE
>
      </P
><P
>	Please note that when so called 'fancy records' (see <A
HREF="fancy-records.html"
>Chapter 14</A
>) are enabled, a backend can receive
	wildcard lookups. These have a % as the first character of the qdomain in lookup. 
      </P
><DIV
CLASS="SECT2"
><H2
CLASS="SECT2"
><A
NAME="AEN5636"
>C.1.1. A sample minimal backend</A
></H2
><P
>	  This backend only knows about the host "random.powerdns.com", and furthermore, only about its A record:
	  
	  <PRE
CLASS="PROGRAMLISTING"
>/* FIRST PART */
class RandomBackend : public DNSBackend
{
public:
  bool list(int id) {
    return false; // we don't support AXFR
  }
    
  void lookup(const QType &amp;type, const string &amp;qdomain, DNSPacket *p, int zoneId)
  {
    if(type.getCode()!=QType::A || qdomain!="random.powerdns.com")  // we only know about random.powerdns.com A
      d_answer="";                                                  // no answer
    else {
      ostringstream os;
      os&lt;&lt;random()%256&lt;&lt;"."&lt;&lt;random()%256&lt;&lt;"."&lt;&lt;random()%256&lt;&lt;"."&lt;&lt;random()%256;
      d_answer=os.str();                                           // our random ip address
    }
  }

  bool get(DNSResourceRecord &amp;rr)
  {
    if(!d_answer.empty()) {
      rr.qname="random.powerdns.com";                               // fill in details
      rr.qtype=QType::A;                                            // A record
      rr.ttl=86400;                                                 // 1 day
      rr.content=d_answer;

      d_answer="";                                                  // this was the last answer
      
      return true;
    }
    return false;                                                   // no more data
  }
  
private:
  string d_answer;
};

/* SECOND PART */

class RandomFactory : public BackendFactory
{
public:
  RandomFactory() : BackendFactory("random") {}

  DNSBackend *make(const string &amp;suffix)
  {
    return new RandomBackend();
  }
};

/* THIRD PART */

class RandomLoader
{
public:
  RandomLoader()
  {
    BackendMakers().report(new RandomFactory);
    
    L&lt;&lt;Logger::Info&lt;&lt;" [RandomBackend] This is the randombackend ("__DATE__", "__TIME__") reporting"&lt;&lt;endl;
  }  
};

static RandomLoader randomloader;
	</PRE
>	  
        This simple backend can be used as an 'overlay'. In other words, it only knows about a single record, another loaded backend would have
        to know about the SOA and NS records and such. But nothing prevents us from loading it without another backend.
      </P
><P
>        The first part of the code contains the actual logic and should be pretty straightforward. The second part is a boilerplate
        'factory' class which PDNS calls to create randombackend instances. Note that a 'suffix' parameter is passed. Real life backends
        also declare parameters for the configuration file; these get the 'suffix' appended to them. Note that the "random" in the 
        constructor denotes the name by which the backend will be known.
      </P
><P
>        The third part registers the RandomFactory with PDNS. This is a simple C++ trick which makes sure that this function
        is called on execution of the binary or when loading the dynamic module.
      </P
><P
>	  Please note that a RandomBackend is actually in most PDNS releases. By default it lives on random.example.com, but you can change
	  that by setting <B
CLASS="COMMAND"
>random-hostname</B
>.
	</P
><P
>	  NOTE: this simple backend neglects to handle case properly!
	</P
></DIV
><DIV
CLASS="SECT2"
><H2
CLASS="SECT2"
><A
NAME="AEN5645"
>C.1.2. Interface definition</A
></H2
><P
>	Classes:
	<DIV
CLASS="TABLE"
><A
NAME="AEN5648"
></A
><P
><B
>Table C-1. DNSResourceRecord class</B
></P
><TABLE
BORDER="1"
CLASS="CALSTABLE"
><COL><COL><TBODY
><TR
><TD
>QType qtype</TD
><TD
>QType of this record</TD
></TR
><TR
><TD
>string qname</TD
><TD
>name of this record</TD
></TR
><TR
><TD
>string content</TD
><TD
>ASCII representation of right hand side</TD
></TR
><TR
><TD
>u_int16_t priority</TD
><TD
>priority of an MX record.</TD
></TR
><TR
><TD
>u_int32_t ttl</TD
><TD
>Time To Live of this record</TD
></TR
><TR
><TD
>int domain_id</TD
><TD
>ID of the domain this record belongs to</TD
></TR
><TR
><TD
>time_t last_modified</TD
><TD
>If unzero, last time_t this record was changed</TD
></TR
></TBODY
></TABLE
></DIV
>
      </P
><P
>	<DIV
CLASS="TABLE"
><A
NAME="AEN5674"
></A
><P
><B
>Table C-2. SOAData struct</B
></P
><TABLE
BORDER="1"
CLASS="CALSTABLE"
><COL><COL><TBODY
><TR
><TD
>string nameserver</TD
><TD
>Name of the master nameserver of this zone</TD
></TR
><TR
><TD
>string hostmaster</TD
><TD
>Hostmaster of this domain. May contain an @</TD
></TR
><TR
><TD
>u_int32_t serial</TD
><TD
>Serial number of this zone</TD
></TR
><TR
><TD
>u_int32_t refresh</TD
><TD
>How often this zone should be refreshed</TD
></TR
><TR
><TD
>u_int32_t retry</TD
><TD
>How often a failed zone pull should be retried.</TD
></TR
><TR
><TD
>u_int32_t expire</TD
><TD
>If zone pulls failed for this long, retire records</TD
></TR
><TR
><TD
>u_int32_t default_ttl</TD
><TD
>Difficult</TD
></TR
><TR
><TD
>int domain_id</TD
><TD
>The ID of the domain within this backend. Must be filled!</TD
></TR
><TR
><TD
>DNSBackend *db</TD
><TD
>Pointer to the backend that feels authoritative for a domain and can act as a slave</TD
></TR
></TBODY
></TABLE
></DIV
>
      </P
><P
>	Methods:
	<P
></P
><DIV
CLASS="VARIABLELIST"
><DL
><DT
>void lookup(const QType &amp;qtype, const string &amp;qdomain, DNSPacket *pkt=0, int zoneId=-1)</DT
><DD
><P
>		This function is used to initiate a straight lookup for a record of name 'qdomain' and type 'qtype'.
		A QType can be converted into an integer by invoking its <CODE
CLASS="FUNCTION"
>getCode()</CODE
> method and into
		a string with the <CODE
CLASS="FUNCTION"
>getCode()</CODE
>.
	      </P
><P
>		  The original question may or may not be passed in the pointer p. If it is, you can retrieve (from 1.99.11 onwards) 
		  information about who asked the question with the <CODE
CLASS="FUNCTION"
>getRemote(DNSPacket *)</CODE
> method. Alternatively, 
		  <CODE
CLASS="FUNCTION"
>bool getRemote(struct sockaddr *sa, socklen_t *len)</CODE
> is available.
		</P
><P
>		  Note that <B
CLASS="COMMAND"
>qdomain</B
> can be of any case and that your backend should make sure it is in effect case 
		  insensitive. Furthermore, the case of the original question should be retained in answers returned by <CODE
CLASS="FUNCTION"
>get()</CODE
>!
		</P
><P
>		Finally, the domain_id might also be passed indicating that only answers from the indicated zone need apply. This
		can both be used as a restriction or as a possible speedup, hinting your backend where the answer might be found.
	      </P
><P
>		If initiated succesfully, as indicated by returning <B
CLASS="COMMAND"
>true</B
>, answers should be made available over the
		<CODE
CLASS="FUNCTION"
>get()</CODE
> method.
		</P
><P
>		  Should throw an AhuException if an error occured accessing the database. Returning otherwise indicates that the query 
		  was started succesfully. If it is known that no data is available, no exception should be thrown! An exception indicates
		  that the backend considers itself broken - not that no answers are available for a question.
		</P
><P
>		  It is legal to return here, and have the first call to <CODE
CLASS="FUNCTION"
>get()</CODE
> return false. This is interpreted as 'no data'
		</P
></DD
><DT
>bool list(int domain_id)</DT
><DD
><P
>		Initiates a list of the indicated domain. Records should then be made available via the <CODE
CLASS="FUNCTION"
>get()</CODE
> method. 
		Need not include the SOA record. If it is, PDNS will not get confused.
	      </P
><P
>		Should return false if the backend does not consider itself authoritative for this zone.
		Should throw an AhuException if an error occured accessing the database. Returning true indicates that data is or should be available.
	      </P
></DD
><DT
>bool get(DNSResourceRecord &amp;rr)</DT
><DD
><P
>		Request a DNSResourceRecord from a query started by <CODE
CLASS="FUNCTION"
>get()</CODE
> of <CODE
CLASS="FUNCTION"
>list()</CODE
>. If this functions returns 
		<B
CLASS="COMMAND"
>true</B
>, <B
CLASS="COMMAND"
>rr</B
> has been filled with data. When it returns false, no more data is available, 
		and <B
CLASS="COMMAND"
>rr</B
> does not contain new data. A backend should make sure that it either fills out all fields of the 
		DNSResourceRecord or resets them to their default values.
	      </P
><P
>		  The qname field of the DNSResourceRecord should be filled out with the exact <CODE
CLASS="FUNCTION"
>qdomain</CODE
> passed to lookup, preserving
		  its case. So if a query for 'CaSe.yourdomain.com' comes in and your database contains dat afor 'case.yourdomain.com', the qname field of rr
		  should contin 'CaSe.yourdomain.com'!
		</P
><P
>		Should throw an AhuException in case a database error occurred.
	      </P
></DD
><DT
>bool getSOA(const string &amp;name, SOAData &amp;soadata)</DT
><DD
><P
>		If the backend considers itself authoritative over domain <CODE
CLASS="FUNCTION"
>name</CODE
>, this method should fill out
		the passed <B
CLASS="COMMAND"
>SOAData</B
> structure and return a positive number. If the backend is functioning correctly, but
		does not consider itself authoritative, it should return 0. In case of errors, an AhuException should be thrown.
	      </P
></DD
></DL
></DIV
>
	</P
></DIV
></DIV
></DIV
><DIV
CLASS="NAVFOOTER"
><HR
ALIGN="LEFT"
WIDTH="100%"><TABLE
SUMMARY="Footer navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
><A
HREF="dns-to-query.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="index.html"
ACCESSKEY="H"
>Home</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
><A
HREF="backend-error-reporting.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
>How PDNS translates DNS queries into backend queries</TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
>&nbsp;</TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
>Reporting errors</TD
></TR
></TABLE
></DIV
></BODY
></HTML
>