]> git.ipfire.org Git - thirdparty/pdns.git/blob - docs/markdown/appendix/backend-writers-guide.md
Merge branch 'master' of github.com:PowerDNS/pdns
[thirdparty/pdns.git] / docs / markdown / appendix / backend-writers-guide.md
1 # Backend writers' guide
2 PowerDNS backends are implemented via a simple yet powerful C++ interface.
3 If your needs are not met by the PipeBackend, you may want to write your
4 own. Before doing any PowerDNS development, please visit [the
5 wiki](http://wiki.powerdns.com). Also please read [this blog
6 post](http://blog.powerdns.com/2015/06/23/what-is-a-powerdns-backend-and-how-do-i-make-it-send-an-nxdomain/)
7 which has a FAQ and several pictures that help explain what a backend is.
8
9 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.
10
11 **Warning**: 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.
12
13 **Warning**: 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.
14
15 ## Notes
16 Besides regular query types, the DNS also knows the 'ANY' query type. When a server receives a question for this ANY type, it should reply with all record types available.
17
18 Backends should therefore implement being able to answer 'ANY' queries in this way, and supply all record types they have when they receive such an 'ANY' query. This is reflected in the sample script above, which for every qtype answers if the type matches, or if the query is for 'ANY'.
19
20 However, since backends need to implement the ANY query anyhow, PowerDNS makes use of this. Since almost all DNS queries internally need to be translated first into a CNAME query and then into the actual query, possibly followed by a SOA or NS query (this is how DNS works internally), it makes sense for PowerDNS to speed this up, and just ask the ANY query of a backend.
21
22 When it has done so, it gets the data about SOA, CNAME and NS records in one go. This speeds things up tremendously.
23
24 The upshot of the above is that for any backend, including the PIPE backend, implementing the ANY query is NOT optional. And in fact, a backend may see almost exclusively ANY queries. This is not a bug.
25
26 ## Simple read-only native backends
27 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:
28
29 ```
30 class DNSBackend
31 {
32 public:
33
34 virtual void lookup(const QType &qtype, const string &qdomain, DNSPacket *pkt_p=0, int zoneId=-1)=0;
35 virtual bool list(const string &target, int domain_id)=0;
36 virtual bool get(DNSResourceRecord &r)=0;
37 virtual bool getSOA(const string &name, SOAData &soadata, DNSPacket *p=0);
38 };
39 ```
40
41 Note that the first three methods must be implemented. `getSOA()` has a useful default implementation.
42
43 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.
44
45 **Note**: Queries for wildcard names should be answered literally, without expansion. So, if a backend gets a question for "*.powerdns.com", it should only answer with data if there is an actual "*.powerdns.com" name
46
47 Some examples, a more formal specification is down below. A normal lookup starts like this:
48
49 ```
50 YourBackend yb;
51 yb.lookup(QType::CNAME,"www.powerdns.com");
52 ```
53
54 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.
55
56 PDNS will then call the `get()` method to get `DNSResourceRecord`s back. The following code illustrates a typical query:
57
58 ```
59 yb.lookup(QType::CNAME,"www.powerdns.com");
60
61 DNSResourceRecord rr;
62 while(yb.get(rr))
63 cout<<"Found cname pointing to '"+rr.content+"'"<<endl;
64 }
65 ```
66
67 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 `getSOA()` 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 `getSOA()`.
68
69 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 `list()` method is used. Conveniently, the domain\_id is also available in the `SOAData` structure.
70
71 The following lists the contents of a zone called "powerdns.com".
72
73 ```
74 SOAData sd;
75 if(!yb.getSOA("powerdns.com",sd)) // are we authoritative over powerdns.com?
76 return RCode::NotAuth; // no
77
78 yb.list(sd.domain_id);
79 while(yb.get(rr))
80 cout<<rr.qname<<"\t IN "<<rr.qtype.getName()<<"\t"<<rr.content<<endl;
81 ```
82
83 ## A sample minimal backend
84
85 This backend only knows about the host "random.powerdns.com", and furthermore, only about its A record:
86
87 ```
88 /* FIRST PART */
89 class RandomBackend : public DNSBackend
90 {
91 public:
92 bool list(const string &target, int id)
93 {
94 return false; // we don't support AXFR
95 }
96
97 void lookup(const QType &type, const string &qdomain, DNSPacket *p, int zoneId)
98 {
99 if(type.getCode()!=QType::A || qdomain!="random.powerdns.com") // we only know about random.powerdns.com A
100 d_answer=""; // no answer
101 else {
102 ostringstream os;
103 os<<random()%256<<"."<<random()%256<<"."<<random()%256<<"."<<random()%256;
104 d_answer=os.str(); // our random ip address
105 }
106 }
107
108 bool get(DNSResourceRecord &rr)
109 {
110 if(!d_answer.empty()) {
111 rr.qname="random.powerdns.com"; // fill in details
112 rr.qtype=QType::A; // A record
113 rr.ttl=86400; // 1 day
114 rr.content=d_answer;
115
116 d_answer=""; // this was the last answer
117
118 return true;
119 }
120 return false; // no more data
121 }
122
123 private:
124 string d_answer;
125 };
126
127 /* SECOND PART */
128
129 class RandomFactory : public BackendFactory
130 {
131 public:
132 RandomFactory() : BackendFactory("random") {}
133
134 DNSBackend *make(const string &suffix)
135 {
136 return new RandomBackend();
137 }
138 };
139
140 /* THIRD PART */
141
142 class RandomLoader
143 {
144 public:
145 RandomLoader()
146 {
147 BackendMakers().report(new RandomFactory);
148 L << Logger::Info << "[randombackend] This is the random backend version " VERSION " reporting" << endl;
149 }
150 };
151
152 static RandomLoader randomloader;
153 ```
154
155 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.
156
157 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.
158
159 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.
160
161 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 [`random-hostname`](../authoritative/backend-random.md#random-hostname).
162
163 **Note**: this simple backend neglects to handle case properly!
164
165 ## Interface definition
166
167 ### Classes
168
169 #### DNSResourceRecord
170 |&nbsp;|&nbsp;|
171 |:--|:--|
172 |QType qtype|QType of this record|
173 |string qname|name of this record|
174 |string content|ASCII representation of right hand side|
175 |uint32\_t ttl|Time To Live of this record|
176 |int domain\_id| ID of the domain this record belongs to|
177 |time\_t last\_modified| If unzero, last time\_t this record was changed|
178 |bool auth| Used for DNSSEC operations. See [DNSSEC](../authoritative/dnssec.md) and more specifically the [Migration](../authoritative/dnssec.md#migration) section. It is also useful to check out the `rectifyZone()` in pdnssec.cc|
179 |bool disabled|If set, this record is not to be served to DNS clients. Backends should not make these records available to PowerDNS unless indicated otherwise.|
180
181 #### SOAData
182 |&nbsp;|&nbsp;|
183 |:--|:--|
184 |string nameserver|Name of the master nameserver of this zone|
185 |string hostmaster|Hostmaster of this domain. May contain an @|
186 |u\_int32\_t serial|Serial number of this zone|
187 |u\_int32\_t refresh|How often this zone should be refreshed|
188 |u\_int32\_t retry|How often a failed zone pull should be retried.|
189 |u\_int32\_t expire|If zone pulls failed for this long, retire records|
190 |u\_int32\_t default\_ttl|Difficult|
191 |int domain\_id|The ID of the domain within this backend. Must be filled!|
192 |DNSBackend *db|Pointer to the backend that feels authoritative for a domain and can act as a slave|
193
194 ### Methods
195 #### `void lookup(const QType &qtype, const string &qdomain, DNSPacket *pkt=0, int zoneId=-1)`
196 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 `getCode()` method and into a string with the `getCode()`.
197
198 The original question may or may not be passed in the pointer pkt. If it is, you can retrieve information about who asked the question with the `pkt->getRemote()` method.
199
200 Note that **qdomain** 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 `get()`!
201
202 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.
203
204 If initiated successfully, as indicated by returning **true**, answers should be made available over the `get()` method.
205
206 Should throw an PDNSException if an error occurred accessing the database. Returning otherwise indicates that the query was started successfully. 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.
207
208 It is legal to return here, and have the first call to `get()` return false. This is interpreted as 'no data'.
209
210 #### `bool list(int domain_id, bool include_disabled=false)`
211 Initiates a list of the indicated domain. Records should then be made available via the `get()` method. Need not include the SOA record. If it is, PDNS will not get confused. If include\_disabled is given as true, records that are configured but should not be served to DNS clients must also be made available.
212
213 Should return false if the backend does not consider itself authoritative for this zone. Should throw an PDNSException if an error occurred accessing the database. Returning true indicates that data is or should be available.
214
215 #### `bool get(DNSResourceRecord &rr)`
216 Request a DNSResourceRecord from a query started by `get()` of `list()`. If this functions returns **true**, **rr** has been filled with data. When it returns false, no more data is available, and **rr** 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.
217
218 The qname field of the DNSResourceRecord should be filled out with the exact `qdomain` passed to lookup, preserving its case. So if a query for 'CaSe.yourdomain.com' comes in and your database contains data for 'case.yourdomain.com', the qname field of rr should contain 'CaSe.yourdomain.com'!
219
220 Should throw an PDNSException in case a database error occurred.
221
222 #### `bool getSOA(const string &name, SOAData &soadata)`
223 If the backend considers itself authoritative over domain `name`, this method should fill out the passed **SOAData** 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 PDNSException should be thrown.
224
225 ## Reporting errors
226 To report errors, the Logger class is available which works mostly like an iostream. Example usage is as shown above in the RandomBackend. Note that it is very important that each line is ended with **endl** as your message won't be visible otherwise.
227
228 To indicate the importance of an error, the standard syslog errorlevels are available. They can be set by outputting `Logger::Critical`, `Logger::Error`, `Logger::Warning`, `Logger::Notice`, `Logger::Info` or `Logger::Debug` to `L`, in descending order of graveness.
229
230 ## Declaring and reading configuration details
231 It is highly likely that a backend needs configuration details. On launch, these parameters need to be declared with PDNS so it knows it should accept them in the configuration file and on the command line. Furthermore, they will be listed in the output of `--help`.
232
233 Declaring arguments is done by implementing the member function `declareArguments()` in the factory class of your backend. PDNS will call this method after launching the backend.
234
235 In the `declareArguments()` method, the function `declare()` is available. The exact definitions:
236
237 ### `void declareArguments(const string &suffix="")`
238 This method is called to allow a backend to register configurable parameters. The suffix is the sub-name of this module. There is no need to touch this suffix, just pass it on to the declare method.
239
240 ### `void declare(const string &suffix, const string &param, const string &explanation, const string &value)`
241 The suffix is passed to your method, and can be passed on to declare. **param** is the name of your parameter. **explanation** is what will appear in the output of --help. Furthermore, a default value can be supplied in the **value** parameter.
242
243 A sample implementation:
244
245 ```
246 void declareArguments(const string &suffix)
247 {
248 declare(suffix,"dbname","Pdns backend database name to connect to","powerdns");
249 declare(suffix,"user","Pdns backend user to connect as","powerdns");
250 declare(suffix,"host","Pdns backend host to connect to","");
251 declare(suffix,"password","Pdns backend password to connect with","");
252 }
253 ```
254
255 After the arguments have been declared, they can be accessed from your backend using the `mustDo()`, `getArg()` and `getArgAsNum()` methods. The are defined as follows in the DNSBackend class:
256
257 ### `void setArgPrefix(const string &prefix)`
258 Must be called before any of the other accessing functions are used. Typical usage is '`setArgPrefix("mybackend"+suffix)`' in the constructor of a backend.
259
260 ### `bool mustDo(const string &key)`
261 Returns true if the variable `key` is set to anything but 'no'.
262
263 ### `const string& getArg(const string &key)`
264 Returns the exact value of a parameter.
265
266 ### `int getArgAsNum(const string &key)`
267 Returns the numerical value of a parameter. Uses `atoi()` internally
268
269 Sample usage from the BindBackend, using the [`bind-example-zones`](../authoritative/backend-bind.md#bind-example-zones) and [`bind-config`](../authoritative/backend-bind.md#bind-config) parameters.
270
271 ```
272 if(mustDo("example-zones")) {
273 insert(0,"www.example.com","A","192.0.2.4");
274 /* ... */
275 }
276
277 if(!getArg("config").empty()) {
278 BindParser BP;
279
280 BP.parse(getArg("config"));
281 }
282
283 ```
284
285 ## Read/write slave-capable backends
286 The backends above are 'natively capable' in that they contain all data relevant for a domain and do not pull in data from other nameservers. To enable storage of information, a backend must be able to do more.
287
288 Before diving into the details of the implementation some theory is in order. Slave domains are pulled from the master. PDNS needs to know for which domains it is to be a slave, and for each slave domain, what the IP address of the master is.
289
290 A slave zone is pulled from a master, after which it is 'fresh', but this is only temporary. In the SOA record of a zone there is a field which specifies the 'refresh' interval. After that interval has elapsed, the slave nameserver needs to check at the master ff the serial number there is higher than what is stored in the backend locally.
291
292 If this is the case, PDNS dubs the domain 'stale', and schedules a transfer of data from the remote. This transfer remains scheduled until the serial numbers remote and locally are identical again.
293
294 This theory is implemented by the `getUnfreshSlaveInfos` method, which is called on all backends periodically. This method fills a vector of **SlaveDomain**s with domains that are unfresh and possibly stale.
295
296 PDNS then retrieves the SOA of those domains remotely and locally and creates a list of stale domains. For each of these domains, PDNS starts a zone transfer to resynchronise. Because zone transfers can fail, it is important that the interface to the backend allows for transaction semantics because a zone might otherwise be left in a halfway updated situation.
297
298 The following excerpt from the DNSBackend shows the relevant functions:
299
300 ```
301 class DNSBackend {
302 public:
303 /* ... */
304 virtual bool getDomainInfo(const string &domain, DomainInfo &di);
305 virtual bool isMaster(const string &name, const string &ip);
306 virtual bool startTransaction(const string &qname, int id);
307 virtual bool commitTransaction();
308 virtual bool abortTransaction();
309 virtual bool feedRecord(const DNSResourceRecord &rr, string *ordername=0);
310 virtual void getUnfreshSlaveInfos(vector<DomainInfo>* domains);
311 virtual void setFresh(uint32_t id);
312 /* ... */
313 }
314 ```
315
316 The mentioned DomainInfo struct looks like this:
317
318 ### DomainInfo struct
319 |&nbsp;|&nbsp;|
320 |:--|:--|
321 |uint32\_t id|ID of this zone within this backend|
322 |string master|IP address of the master of this domain, if any|
323 |uint32\_t serial|Serial number of this zone|
324 |uint32\_t notified\_serial|Last serial number of this zone that slaves have seen|
325 |time\_t last\_check|Last time this zone was checked over at the master for changes|
326 |enum {Master,Slave,Native} kind|Type of zone|
327 |DNSBackend *backend|Pointer to the backend that feels authoritative for a domain and can act as a slave|
328
329 These functions all have a default implementation that returns false - which explains that these methods can be omitted in simple backends. Furthermore, unlike with simple backends, a slave capable backend must make sure that the 'DNSBackend *db' field of the SOAData record is filled out correctly - it is used to determine which backend will house this zone.
330
331 ### `bool isMaster(const string &name, const string &ip)`
332 If a backend considers itself a slave for the domain **name** and if the IP address in **ip** is indeed a master, it should return true. False otherwise. This is a first line of checks to guard against reloading a domain unnecessarily.
333
334 ### `void getUnfreshSlaveInfos(vector\<DomainInfo\>* domains)`
335 When called, the backend should examine its list of slave domains and add any unfresh ones to the domains vector.
336
337 ### `bool getDomainInfo(const string &name, DomainInfo & di)`
338 This is like `getUnfreshSlaveInfos`, but for a specific domain. If the backend considers itself authoritative for the named zone, `di` should be filled out, and 'true' be returned. Otherwise return false.
339
340 ### `bool startTransaction(const string &qname, int id)`
341 When called, the backend should start a transaction that can be committed or rolled back atomically later on. In SQL terms, this function should **BEGIN** a transaction and **DELETE** all records.
342
343 ### `bool feedRecord(const DNSResourceRecord &rr, string *ordername)`
344 Insert this record.
345
346 ### `bool commitTransaction()`
347 Make the changes effective. In SQL terms, execute **COMMIT**.
348
349 ### `bool abortTransaction()`
350 Abort changes. In SQL terms, execute **ABORT**.
351
352 ### `bool setFresh()`
353 Indicate that a domain has either been updated or refreshed without the need for a retransfer. This causes the domain to vanish from the vector modified by `getUnfreshSlaveInfos()`.
354
355 PDNS will always call `startTransaction()` before making calls to `feedRecord()`. Although it is likely that `abortTransaction()` will be called in case of problems, backends should also be prepared to abort from their destructor.
356
357 The actual code in PDNS is currently (1.99.9):
358
359 ```
360 Resolver resolver;
361 resolver.axfr(remote,domain.c_str());
362
363 db->startTransaction(domain, domain_id);
364 L<<Logger::Error<<"AXFR started for '"<<domain<<"'"<<endl;
365 Resolver::res_t recs;
366
367 while(resolver.axfrChunk(recs)) {
368 for(Resolver::res_t::const_iterator i=recs.begin();i!=recs.end();++i) {
369 db->feedRecord(*i);
370 }
371 }
372 db->commitTransaction();
373 db->setFresh(domain_id);
374 L<<Logger::Error<<"AXFR done for '"<<domain<<"'"<<endl;
375 ```
376
377 ## Supermaster/Superslave capability
378
379 A backend that wants to act as a 'superslave' for a master should implement the following method:
380
381 ```
382 class DNSBackend
383 {
384 virtual bool superMasterBackend(const string &ip, const string &domain, const vector<DNSResourceRecord>&nsset, string *account, DNSBackend **db)
385 };
386 ```
387
388 This function gets called with the IP address of the potential supermaster, the domain it is sending a notification for and the set of NS records for this domain at that IP address.
389
390 Using the supplied data, the backend needs to determine if this is a bonafide 'supernotification' which should be honoured. If it decides that it should, the supplied pointer to 'account' needs to be filled with the configured name of the supermaster (if accounting is desired), and the db needs to be filled with a pointer to your backend.
391
392 Supermaster/superslave is a complicated concept, if this is all unclear see the [Supermaster and Superslave](../authoritative/modes-of-operation.md#supermaster-automatic-provisioning-of-slaves) documentation.
393
394 ## Read/write master-capable backends
395 In order to be a useful master for a domain, notifies must be sent out whenever a domain is changed. Periodically, PDNS queries backends for domains that may have changed, and sends out notifications for slave nameservers.
396
397 In order to do so, PDNS calls the `getUpdatedMasters()` method. Like the `getUnfreshSlaveInfos()` function mentioned above, this should add changed domain names to the vector passed.
398
399 The following excerpt from the DNSBackend shows the relevant functions:
400
401 ```
402 class DNSBackend {
403 public:
404 /* ... */
405 virtual void getUpdatedMasters(vector<DomainInfo>* domains);
406 virtual void setNotified(uint32_t id, uint32_t serial);
407 /* ... */
408 }
409 ```
410
411 These functions all have a default implementation that returns false - which explains that these methods can be omitted in simple backends. Furthermore, unlike with simple backends, a slave capable backend must make sure that the 'DNSBackend *db' field of the SOAData record is filled out correctly - it is used to determine which backend will house this zone.
412
413 ### `void getUpdatedMasters(vector<DomainInfo>* domains)`
414 When called, the backend should examine its list of master domains and add any changed ones to the DomainInfo vector
415
416 ### `bool setNotified(uint32_t domain_id, uint32_t serial)`
417 Indicate that notifications have been queued for this domain and that it need not be considered 'updated' anymore
418
419 ## DNS update support
420 To make your backend DNS update compatible, it needs to implement a number of new functions and functions already used for slave-operation. The new functions are not DNS update specific and might be used for other update/remove functionality at a later stage.
421
422 ```
423 class DNSBackend {
424 public:
425 /* ... */
426 virtual bool startTransaction(const string &qname, int id);
427 virtual bool commitTransaction();
428 virtual bool abortTransaction();
429 virtual bool feedRecord(const DNSResourceRecord &rr, string *ordername);
430 virtual bool replaceRRSet(uint32_t domain_id, const string& qname, const QType& qt, const vector<DNSResourceRecord>& rrset)
431 virtual bool listSubZone(const string &zone, int domain_id);
432 /* ... */
433 }
434 ```
435
436 ### `virtual bool startTransaction(const string &qname, int id)`
437 See [above](#bool-starttransactionconst-string-qname-int-id). Please note that this function now receives a negative number (-1), which indicates that the current zone data should NOT be deleted.
438
439 ### `virtual bool commitTransaction()`
440 See [above](#bool-committransaction)
441
442 ### `virtual bool abortTransaction()`
443 See [above](#bool-aborttransaction). Method is called when an exception is received.
444
445 ### `virtual bool feedRecord(const DNSResourceRecord &rr, string *ordername)`
446 See [above](#bool-feedrecordconst-dnsresourcerecord-rr-string-ordername). Please keep in mind that the zone is not empty because `startTransaction()` was called different.
447
448 ### `virtual bool listSubZone(const string &name, int domain\_id)`
449 This method is needed for rectification of a zone after NS-records have been added. For DNSSEC, we need to know which records are below the currently added record. `listSubZone()` is used like `list()` which means PowerDNS will call `get()` after this method. The default SQL query looks something like this:
450
451 ```
452 // First %s is 'sub.zone.com', second %s is '*.sub.zone.com'
453 select content,ttl,prio,type,domain_id,name from records where (name='%s' OR name like '%s') and domain_id=%d
454 ```
455
456 The method is not only used when adding records, but also to correct ENT-records in powerdns. Make sure it returns every record in the tree below the given record.
457
458 ### `virtual bool replaceRRSet(uint32_t domain_id, const string& qname, const QType& qt, const vector<DNSResourceRecord>& rrset)`
459 This method should remove all the records with `qname` of type `qt`. `qt` might also be ANY, which means all the records with that `qname` need to be removed. After removal, the records in `rrset` must be added to the zone. `rrset` can be empty in which case the method is used to remove a RRset.
460
461 # DNS update support
462 To make your backend DNS update compatible, it needs to implement a number of new functions and functions already used for slave-operation. The new functions are not DNS update specific and might be used for other update/remove functionality at a later stage.
463
464 ```
465 class DNSBackend {
466 public:
467 /* ... */
468 virtual bool startTransaction(const string &qname, int id);
469 virtual bool commitTransaction();
470 virtual bool abortTransaction();
471 virtual bool feedRecord(const DNSResourceRecord &rr, string *ordername);
472 virtual bool replaceRRSet(uint32_t domain_id, const string& qname, const QType& qt, const vector<DNSResourceRecord>& rrset)
473 virtual bool listSubZone(const string &zone, int domain_id);
474 /* ... */
475 }
476 ```
477
478 ## `virtual bool startTransaction(const string &qname, int id);`
479 See [Read/write slave-capable backends](#read-write-slave-capable-backends). Please note that this function now receives a negative number (-1), which indicates that the current zone data should NOT be deleted.
480
481 ## `virtual bool commitTransaction();`
482 See [Read/write slave-capable backends](#read-write-slave-capable-backends).
483
484 ## `virtual bool abortTransaction();`
485 See [Read/write slave-capable backends](#read-write-slave-capable-backends). Method is called when an exception is received.
486
487 ## `virtual bool feedRecord(const DNSResourceRecord &rr, string *ordername);`
488 See [Read/write slave-capable backends](#read-write-slave-capable-backends). Please keep in mind that the zone is not empty because `startTransaction()` was called different.
489
490 virtual bool listSubZone(const string &name, int domain\_id);
491 This method is needed for rectification of a zone after NS-records have been added. For DNSSEC, we need to know which records are below the currently added record. `listSubZone()` is used like `list()` which means PowerDNS will call `get()` after this method. The default SQL query looks something like this:
492
493 ```
494 // First %s is 'sub.zone.com', second %s is '*.sub.zone.com'
495 select content,ttl,prio,type,domain_id,name from records where (name='%s' OR name like '%s') and domain_id=%d
496 ```
497
498 The method is not only used when adding records, but also to correct ENT-records in powerdns. Make sure it returns every record in the tree below the given record.
499
500 ## virtual bool replaceRRSet(uint32\_t domain\_id, const string& qname, const QType& qt, const vector\<DNSResourceRecord\>& rrset);
501
502 This method should remove all the records with `qname` of type `qt`. `qt` might also be ANY, which means all the records with that `qname` need to be removed. After removal, the records in `rrset` must be added to the zone. `rrset` can be empty in which case the method is used to remove a RRset.