7e18165e5f5d83bbc7b64908c6edd26981b248ca
[kai/samba.git] / source4 / dns_server / dlz_bind9.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    bind9 dlz driver for Samba
5
6    Copyright (C) 2010 Andrew Tridgell
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "talloc.h"
24 #include "param/param.h"
25 #include "lib/events/events.h"
26 #include "dsdb/samdb/samdb.h"
27 #include "dsdb/common/util.h"
28 #include "auth/session.h"
29 #include "auth/gensec/gensec.h"
30 #include "gen_ndr/ndr_dnsp.h"
31 #include "lib/cmdline/popt_common.h"
32 #include "lib/cmdline/popt_credentials.h"
33 #include "ldb_module.h"
34 #include "dlz_minimal.h"
35
36 struct dlz_bind9_data {
37         struct ldb_context *samdb;
38         struct tevent_context *ev_ctx;
39         struct loadparm_context *lp;
40         int *transaction_token;
41
42         /* helper functions from the dlz_dlopen driver */
43         void (*log)(int level, const char *fmt, ...);
44         isc_result_t (*putrr)(dns_sdlzlookup_t *handle, const char *type,
45                               dns_ttl_t ttl, const char *data);
46         isc_result_t (*putnamedrr)(dns_sdlzlookup_t *handle, const char *name,
47                                    const char *type, dns_ttl_t ttl, const char *data);
48         isc_result_t (*writeable_zone)(dns_view_t *view, const char *zone_name);
49 };
50
51
52 static const char *zone_prefixes[] = {
53         "CN=MicrosoftDNS,DC=DomainDnsZones",
54         "CN=MicrosoftDNS,DC=ForestDnsZones",
55         NULL
56 };
57
58 /*
59   return the version of the API
60  */
61 _PUBLIC_ int dlz_version(unsigned int *flags)
62 {
63         return DLZ_DLOPEN_VERSION;
64 }
65
66 /*
67    remember a helper function from the bind9 dlz_dlopen driver
68  */
69 static void b9_add_helper(struct dlz_bind9_data *state, const char *helper_name, void *ptr)
70 {
71         if (strcmp(helper_name, "log") == 0) {
72                 state->log = ptr;
73         }
74         if (strcmp(helper_name, "putrr") == 0) {
75                 state->putrr = ptr;
76         }
77         if (strcmp(helper_name, "putnamedrr") == 0) {
78                 state->putnamedrr = ptr;
79         }
80         if (strcmp(helper_name, "writeable_zone") == 0) {
81                 state->writeable_zone = ptr;
82         }
83 }
84
85 /*
86   format a record for bind9
87  */
88 static bool b9_format(struct dlz_bind9_data *state,
89                       TALLOC_CTX *mem_ctx,
90                       struct dnsp_DnssrvRpcRecord *rec,
91                       const char **type, const char **data)
92 {
93         switch (rec->wType) {
94         case DNS_TYPE_A:
95                 *type = "a";
96                 *data = rec->data.ipv4;
97                 break;
98
99         case DNS_TYPE_AAAA:
100                 *type = "aaaa";
101                 *data = rec->data.ipv6;
102                 break;
103
104         case DNS_TYPE_CNAME:
105                 *type = "cname";
106                 *data = rec->data.cname;
107                 break;
108
109         case DNS_TYPE_TXT:
110                 *type = "txt";
111                 *data = rec->data.txt;
112                 break;
113
114         case DNS_TYPE_PTR:
115                 *type = "ptr";
116                 *data = rec->data.ptr;
117                 break;
118
119         case DNS_TYPE_SRV:
120                 *type = "srv";
121                 *data = talloc_asprintf(mem_ctx, "%u %u %u %s",
122                                         rec->data.srv.wPriority,
123                                         rec->data.srv.wWeight,
124                                         rec->data.srv.wPort,
125                                         rec->data.srv.nameTarget);
126                 break;
127
128         case DNS_TYPE_MX:
129                 *type = "mx";
130                 *data = talloc_asprintf(mem_ctx, "%u %s",
131                                         rec->data.mx.wPriority,
132                                         rec->data.mx.nameTarget);
133                 break;
134
135         case DNS_TYPE_HINFO:
136                 *type = "hinfo";
137                 *data = talloc_asprintf(mem_ctx, "%s %s",
138                                         rec->data.hinfo.cpu,
139                                         rec->data.hinfo.os);
140                 break;
141
142         case DNS_TYPE_NS:
143                 *type = "ns";
144                 *data = rec->data.ns;
145                 break;
146
147         case DNS_TYPE_SOA:
148                 *type = "soa";
149                 *data = talloc_asprintf(mem_ctx, "%s %s %u %u %u %u %u",
150                                         rec->data.soa.mname,
151                                         rec->data.soa.rname,
152                                         rec->data.soa.serial,
153                                         rec->data.soa.refresh,
154                                         rec->data.soa.retry,
155                                         rec->data.soa.expire,
156                                         rec->data.soa.minimum);
157                 break;
158
159         default:
160                 state->log(ISC_LOG_ERROR, "samba b9_putrr: unhandled record type %u",
161                            rec->wType);
162                 return false;
163         }
164
165         return true;
166 }
167
168 static const struct {
169         enum dns_record_type dns_type;
170         const char *typestr;
171         bool single_valued;
172 } dns_typemap[] = {
173         { DNS_TYPE_A,     "A"     , false},
174         { DNS_TYPE_AAAA,  "AAAA"  , false},
175         { DNS_TYPE_CNAME, "CNAME" , true},
176         { DNS_TYPE_TXT,   "TXT"   , false},
177         { DNS_TYPE_PTR,   "PTR"   , false},
178         { DNS_TYPE_SRV,   "SRV"   , false},
179         { DNS_TYPE_MX,    "MX"    , false},
180         { DNS_TYPE_HINFO, "HINFO" , false},
181         { DNS_TYPE_NS,    "NS"    , false},
182         { DNS_TYPE_SOA,   "SOA"   , true},
183 };
184
185
186 /*
187   see if a DNS type is single valued
188  */
189 static bool b9_single_valued(enum dns_record_type dns_type)
190 {
191         int i;
192         for (i=0; i<ARRAY_SIZE(dns_typemap); i++) {
193                 if (dns_typemap[i].dns_type == dns_type) {
194                         return dns_typemap[i].single_valued;
195                 }
196         }
197         return false;
198 }
199
200 /*
201   see if a DNS type is single valued
202  */
203 static enum dns_record_type b9_dns_type(const char *type)
204 {
205         int i;
206         for (i=0; i<ARRAY_SIZE(dns_typemap); i++) {
207                 if (strcasecmp(dns_typemap[i].typestr, type) == 0) {
208                         return dns_typemap[i].dns_type;
209                 }
210         }
211         return DNS_TYPE_ZERO;
212 }
213
214
215 #define DNS_PARSE_STR(ret, str, sep, saveptr) do {      \
216         (ret) = strtok_r(str, sep, &saveptr); \
217         if ((ret) == NULL) return false; \
218         } while (0)
219
220 #define DNS_PARSE_UINT(ret, str, sep, saveptr) do {  \
221         char *istr = strtok_r(str, sep, &saveptr); \
222         if ((istr) == NULL) return false; \
223         (ret) = strtoul(istr, NULL, 10); \
224         } while (0)
225
226 /*
227   parse a record from bind9
228  */
229 static bool b9_parse(struct dlz_bind9_data *state,
230                      const char *rdatastr,
231                      struct dnsp_DnssrvRpcRecord *rec)
232 {
233         char *full_name, *dclass, *type;
234         char *str, *saveptr=NULL;
235         int i;
236
237         str = talloc_strdup(rec, rdatastr);
238         if (str == NULL) {
239                 return false;
240         }
241
242         /* parse the SDLZ string form */
243         DNS_PARSE_STR(full_name, str, "\t", saveptr);
244         DNS_PARSE_UINT(rec->dwTtlSeconds, NULL, "\t", saveptr);
245         DNS_PARSE_STR(dclass, NULL, "\t", saveptr);
246         DNS_PARSE_STR(type, NULL, "\t", saveptr);
247
248         /* construct the record */
249         for (i=0; i<ARRAY_SIZE(dns_typemap); i++) {
250                 if (strcasecmp(type, dns_typemap[i].typestr) == 0) {
251                         rec->wType = dns_typemap[i].dns_type;
252                         break;
253                 }
254         }
255         if (i == ARRAY_SIZE(dns_typemap)) {
256                 state->log(ISC_LOG_ERROR, "samba_dlz: unsupported record type '%s' for '%s'",
257                            type, full_name);
258                 return false;
259         }
260
261         switch (rec->wType) {
262         case DNS_TYPE_A:
263                 DNS_PARSE_STR(rec->data.ipv4, NULL, " ", saveptr);
264                 break;
265
266         case DNS_TYPE_AAAA:
267                 DNS_PARSE_STR(rec->data.ipv6, NULL, " ", saveptr);
268                 break;
269
270         case DNS_TYPE_CNAME:
271                 DNS_PARSE_STR(rec->data.cname, NULL, " ", saveptr);
272                 break;
273
274         case DNS_TYPE_TXT:
275                 DNS_PARSE_STR(rec->data.txt, NULL, "\t", saveptr);
276                 break;
277
278         case DNS_TYPE_PTR:
279                 DNS_PARSE_STR(rec->data.ptr, NULL, " ", saveptr);
280                 break;
281
282         case DNS_TYPE_SRV:
283                 DNS_PARSE_UINT(rec->data.srv.wPriority, NULL, " ", saveptr);
284                 DNS_PARSE_UINT(rec->data.srv.wWeight, NULL, " ", saveptr);
285                 DNS_PARSE_UINT(rec->data.srv.wPort, NULL, " ", saveptr);
286                 DNS_PARSE_STR(rec->data.srv.nameTarget, NULL, " ", saveptr);
287                 break;
288
289         case DNS_TYPE_MX:
290                 DNS_PARSE_UINT(rec->data.mx.wPriority, NULL, " ", saveptr);
291                 DNS_PARSE_STR(rec->data.mx.nameTarget, NULL, " ", saveptr);
292                 break;
293
294         case DNS_TYPE_HINFO:
295                 DNS_PARSE_STR(rec->data.hinfo.cpu, NULL, " ", saveptr);
296                 DNS_PARSE_STR(rec->data.hinfo.os, NULL, " ", saveptr);
297                 break;
298
299         case DNS_TYPE_NS:
300                 DNS_PARSE_STR(rec->data.ns, NULL, " ", saveptr);
301                 break;
302
303         case DNS_TYPE_SOA:
304                 DNS_PARSE_STR(rec->data.soa.mname, NULL, " ", saveptr);
305                 DNS_PARSE_STR(rec->data.soa.rname, NULL, " ", saveptr);
306                 DNS_PARSE_UINT(rec->data.soa.serial, NULL, " ", saveptr);
307                 DNS_PARSE_UINT(rec->data.soa.refresh, NULL, " ", saveptr);
308                 DNS_PARSE_UINT(rec->data.soa.retry, NULL, " ", saveptr);
309                 DNS_PARSE_UINT(rec->data.soa.expire, NULL, " ", saveptr);
310                 DNS_PARSE_UINT(rec->data.soa.minimum, NULL, " ", saveptr);
311                 break;
312
313         default:
314                 state->log(ISC_LOG_ERROR, "samba b9_parse: unhandled record type %u",
315                            rec->wType);
316                 return false;
317         }
318
319         /* we should be at the end of the buffer now */
320         if (strtok_r(NULL, "\t ", &saveptr) != NULL) {
321                 state->log(ISC_LOG_ERROR, "samba b9_parse: expected data at end of string for '%s'");
322                 return false;
323         }
324
325         return true;
326 }
327
328 /*
329   send a resource recond to bind9
330  */
331 static isc_result_t b9_putrr(struct dlz_bind9_data *state,
332                              void *handle, struct dnsp_DnssrvRpcRecord *rec,
333                              const char **types)
334 {
335         isc_result_t result;
336         const char *type, *data;
337         TALLOC_CTX *tmp_ctx = talloc_new(state);
338
339         if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
340                 return ISC_R_FAILURE;
341         }
342
343         if (data == NULL) {
344                 talloc_free(tmp_ctx);
345                 return ISC_R_NOMEMORY;
346         }
347
348         if (types) {
349                 int i;
350                 for (i=0; types[i]; i++) {
351                         if (strcmp(types[i], type) == 0) break;
352                 }
353                 if (types[i] == NULL) {
354                         /* skip it */
355                         return ISC_R_SUCCESS;
356                 }
357         }
358
359         result = state->putrr(handle, type, rec->dwTtlSeconds, data);
360         if (result != ISC_R_SUCCESS) {
361                 state->log(ISC_LOG_ERROR, "Failed to put rr");
362         }
363         talloc_free(tmp_ctx);
364         return result;
365 }
366
367
368 /*
369   send a named resource recond to bind9
370  */
371 static isc_result_t b9_putnamedrr(struct dlz_bind9_data *state,
372                                   void *handle, const char *name,
373                                   struct dnsp_DnssrvRpcRecord *rec)
374 {
375         isc_result_t result;
376         const char *type, *data;
377         TALLOC_CTX *tmp_ctx = talloc_new(state);
378
379         if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
380                 return ISC_R_FAILURE;
381         }
382
383         if (data == NULL) {
384                 talloc_free(tmp_ctx);
385                 return ISC_R_NOMEMORY;
386         }
387
388         result = state->putnamedrr(handle, name, type, rec->dwTtlSeconds, data);
389         if (result != ISC_R_SUCCESS) {
390                 state->log(ISC_LOG_ERROR, "Failed to put named rr '%s'", name);
391         }
392         talloc_free(tmp_ctx);
393         return result;
394 }
395
396 struct b9_options {
397         const char *url;
398 };
399
400 /*
401    parse options
402  */
403 static isc_result_t parse_options(struct dlz_bind9_data *state,
404                                   unsigned int argc, char *argv[],
405                                   struct b9_options *options)
406 {
407         int opt;
408         poptContext pc;
409         struct poptOption long_options[] = {
410                 { "url",       'H', POPT_ARG_STRING, &options->url, 0, "database URL", "URL" },
411                 { NULL }
412         };
413         struct poptOption **popt_options;
414         int ret;
415
416         popt_options = ldb_module_popt_options(state->samdb);
417         (*popt_options) = long_options;
418
419         ret = ldb_modules_hook(state->samdb, LDB_MODULE_HOOK_CMDLINE_OPTIONS);
420         if (ret != LDB_SUCCESS) {
421                 state->log(ISC_LOG_ERROR, "dlz samba: failed cmdline hook");
422                 return ISC_R_FAILURE;
423         }
424
425         pc = poptGetContext("dlz_bind9", argc, (const char **)argv, *popt_options,
426                             POPT_CONTEXT_KEEP_FIRST);
427
428         while ((opt = poptGetNextOpt(pc)) != -1) {
429                 switch (opt) {
430                 default:
431                         state->log(ISC_LOG_ERROR, "dlz samba: Invalid option %s: %s",
432                                    poptBadOption(pc, 0), poptStrerror(opt));
433                         return ISC_R_FAILURE;
434                 }
435         }
436
437         ret = ldb_modules_hook(state->samdb, LDB_MODULE_HOOK_CMDLINE_PRECONNECT);
438         if (ret != LDB_SUCCESS) {
439                 state->log(ISC_LOG_ERROR, "dlz samba: failed cmdline preconnect");
440                 return ISC_R_FAILURE;
441         }
442
443         return ISC_R_SUCCESS;
444 }
445
446
447 /*
448   called to initialise the driver
449  */
450 _PUBLIC_ isc_result_t dlz_create(const char *dlzname,
451                                  unsigned int argc, char *argv[],
452                                  void **dbdata, ...)
453 {
454         struct dlz_bind9_data *state;
455         const char *helper_name;
456         va_list ap;
457         isc_result_t result;
458         TALLOC_CTX *tmp_ctx;
459         int ret;
460         struct ldb_dn *dn;
461         struct b9_options options;
462
463         ZERO_STRUCT(options);
464
465         state = talloc_zero(NULL, struct dlz_bind9_data);
466         if (state == NULL) {
467                 return ISC_R_NOMEMORY;
468         }
469
470         tmp_ctx = talloc_new(state);
471
472         /* fill in the helper functions */
473         va_start(ap, dbdata);
474         while ((helper_name = va_arg(ap, const char *)) != NULL) {
475                 b9_add_helper(state, helper_name, va_arg(ap, void*));
476         }
477         va_end(ap);
478
479         state->ev_ctx = s4_event_context_init(state);
480         if (state->ev_ctx == NULL) {
481                 result = ISC_R_NOMEMORY;
482                 goto failed;
483         }
484
485         state->samdb = ldb_init(state, state->ev_ctx);
486         if (state->samdb == NULL) {
487                 state->log(ISC_LOG_ERROR, "samba_dlz: Failed to create ldb");
488                 result = ISC_R_FAILURE;
489                 goto failed;
490         }
491
492         result = parse_options(state, argc, argv, &options);
493         if (result != ISC_R_SUCCESS) {
494                 goto failed;
495         }
496
497         state->lp = loadparm_init_global(true);
498         if (state->lp == NULL) {
499                 result = ISC_R_NOMEMORY;
500                 goto failed;
501         }
502
503         if (options.url == NULL) {
504                 options.url = talloc_asprintf(tmp_ctx, "ldapi://%s",
505                                               private_path(tmp_ctx, state->lp, "ldap_priv/ldapi"));
506                 if (options.url == NULL) {
507                         result = ISC_R_NOMEMORY;
508                         goto failed;
509                 }
510         }
511
512         ret = ldb_connect(state->samdb, options.url, 0, NULL);
513         if (ret == -1) {
514                 state->log(ISC_LOG_ERROR, "samba_dlz: Failed to connect to %s - %s",
515                            options.url, ldb_errstring(state->samdb));
516                 result = ISC_R_FAILURE;
517                 goto failed;
518         }
519
520         ret = ldb_modules_hook(state->samdb, LDB_MODULE_HOOK_CMDLINE_POSTCONNECT);
521         if (ret != LDB_SUCCESS) {
522                 state->log(ISC_LOG_ERROR, "samba_dlz: Failed postconnect for %s - %s",
523                            options.url, ldb_errstring(state->samdb));
524                 result = ISC_R_FAILURE;
525                 goto failed;
526         }
527
528         dn = ldb_get_default_basedn(state->samdb);
529         if (dn == NULL) {
530                 state->log(ISC_LOG_ERROR, "samba_dlz: Unable to get basedn for %s - %s",
531                            options.url, ldb_errstring(state->samdb));
532                 result = ISC_R_FAILURE;
533                 goto failed;
534         }
535
536         state->log(ISC_LOG_INFO, "samba_dlz: started for DN %s",
537                    ldb_dn_get_linearized(dn));
538
539         *dbdata = state;
540
541         talloc_free(tmp_ctx);
542         return ISC_R_SUCCESS;
543
544 failed:
545         talloc_free(state);
546         return result;
547 }
548
549 /*
550   shutdown the backend
551  */
552 _PUBLIC_ void dlz_destroy(void *dbdata)
553 {
554         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
555         state->log(ISC_LOG_INFO, "samba_dlz: shutting down");
556         talloc_free(state);
557 }
558
559
560 /*
561   return the base DN for a zone
562  */
563 static isc_result_t b9_find_zone_dn(struct dlz_bind9_data *state, const char *zone_name,
564                                     TALLOC_CTX *mem_ctx, struct ldb_dn **zone_dn)
565 {
566         int ret;
567         TALLOC_CTX *tmp_ctx = talloc_new(state);
568         const char *attrs[] = { NULL };
569         int i;
570
571         for (i=0; zone_prefixes[i]; i++) {
572                 struct ldb_dn *dn;
573                 struct ldb_result *res;
574
575                 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
576                 if (dn == NULL) {
577                         talloc_free(tmp_ctx);
578                         return ISC_R_NOMEMORY;
579                 }
580
581                 if (!ldb_dn_add_child_fmt(dn, "DC=%s,%s", zone_name, zone_prefixes[i])) {
582                         talloc_free(tmp_ctx);
583                         return ISC_R_NOMEMORY;
584                 }
585
586                 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsZone");
587                 if (ret == LDB_SUCCESS) {
588                         if (zone_dn != NULL) {
589                                 *zone_dn = talloc_steal(mem_ctx, dn);
590                         }
591                         talloc_free(tmp_ctx);
592                         return ISC_R_SUCCESS;
593                 }
594                 talloc_free(dn);
595         }
596
597         talloc_free(tmp_ctx);
598         return ISC_R_NOTFOUND;
599 }
600
601
602 /*
603   return the DN for a name. The record does not need to exist, but the
604   zone must exist
605  */
606 static isc_result_t b9_find_name_dn(struct dlz_bind9_data *state, const char *name,
607                                     TALLOC_CTX *mem_ctx, struct ldb_dn **dn)
608 {
609         const char *p;
610
611         /* work through the name piece by piece, until we find a zone */
612         for (p=name; p; ) {
613                 isc_result_t result;
614                 result = b9_find_zone_dn(state, p, mem_ctx, dn);
615                 if (result == ISC_R_SUCCESS) {
616                         /* we found a zone, now extend the DN to get
617                          * the full DN
618                          */
619                         bool ret;
620                         if (p == name) {
621                                 ret = ldb_dn_add_child_fmt(*dn, "DC=@");
622                         } else {
623                                 ret = ldb_dn_add_child_fmt(*dn, "DC=%.*s", (int)(p-name)-1, name);
624                         }
625                         if (!ret) {
626                                 talloc_free(*dn);
627                                 return ISC_R_NOMEMORY;
628                         }
629                         return ISC_R_SUCCESS;
630                 }
631                 p = strchr(p, '.');
632                 if (p == NULL) {
633                         break;
634                 }
635                 p++;
636         }
637         return ISC_R_NOTFOUND;
638 }
639
640
641 /*
642   see if we handle a given zone
643  */
644 _PUBLIC_ isc_result_t dlz_findzonedb(void *dbdata, const char *name)
645 {
646         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
647         return b9_find_zone_dn(state, name, NULL, NULL);
648 }
649
650
651 /*
652   lookup one record
653  */
654 static isc_result_t dlz_lookup_types(struct dlz_bind9_data *state,
655                                      const char *zone, const char *name,
656                                      dns_sdlzlookup_t *lookup,
657                                      const char **types)
658 {
659         TALLOC_CTX *tmp_ctx = talloc_new(state);
660         const char *attrs[] = { "dnsRecord", NULL };
661         int ret, i;
662         struct ldb_result *res;
663         struct ldb_message_element *el;
664         struct ldb_dn *dn;
665
666         for (i=0; zone_prefixes[i]; i++) {
667                 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
668                 if (dn == NULL) {
669                         talloc_free(tmp_ctx);
670                         return ISC_R_NOMEMORY;
671                 }
672
673                 if (!ldb_dn_add_child_fmt(dn, "DC=%s,DC=%s,%s", name, zone, zone_prefixes[i])) {
674                         talloc_free(tmp_ctx);
675                         return ISC_R_NOMEMORY;
676                 }
677
678                 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
679                                  attrs, "objectClass=dnsNode");
680                 if (ret == LDB_SUCCESS) {
681                         break;
682                 }
683         }
684         if (ret != LDB_SUCCESS) {
685                 talloc_free(tmp_ctx);
686                 return ISC_R_NOTFOUND;
687         }
688
689         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
690         if (el == NULL || el->num_values == 0) {
691                 talloc_free(tmp_ctx);
692                 return ISC_R_NOTFOUND;
693         }
694
695         for (i=0; i<el->num_values; i++) {
696                 struct dnsp_DnssrvRpcRecord rec;
697                 enum ndr_err_code ndr_err;
698                 isc_result_t result;
699
700                 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec,
701                                                (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
702                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
703                         state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
704                                    ldb_dn_get_linearized(dn));
705                         talloc_free(tmp_ctx);
706                         return ISC_R_FAILURE;
707                 }
708
709                 result = b9_putrr(state, lookup, &rec, types);
710                 if (result != ISC_R_SUCCESS) {
711                         talloc_free(tmp_ctx);
712                         return result;
713                 }
714         }
715
716         talloc_free(tmp_ctx);
717         return ISC_R_SUCCESS;
718 }
719
720 /*
721   lookup one record
722  */
723 _PUBLIC_ isc_result_t dlz_lookup(const char *zone, const char *name,
724                                  void *dbdata, dns_sdlzlookup_t *lookup)
725 {
726         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
727         return dlz_lookup_types(state, zone, name, lookup, NULL);
728 }
729
730
731 /*
732   see if a zone transfer is allowed
733  */
734 _PUBLIC_ isc_result_t dlz_allowzonexfr(void *dbdata, const char *name, const char *client)
735 {
736         /* just say yes for all our zones for now */
737         return dlz_findzonedb(dbdata, name);
738 }
739
740 /*
741   perform a zone transfer
742  */
743 _PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *dbdata,
744                                    dns_sdlzallnodes_t *allnodes)
745 {
746         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
747         const char *attrs[] = { "dnsRecord", NULL };
748         int ret, i, j;
749         struct ldb_dn *dn;
750         struct ldb_result *res;
751         TALLOC_CTX *tmp_ctx = talloc_new(state);
752
753         for (i=0; zone_prefixes[i]; i++) {
754                 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
755                 if (dn == NULL) {
756                         talloc_free(tmp_ctx);
757                         return ISC_R_NOMEMORY;
758                 }
759
760                 if (!ldb_dn_add_child_fmt(dn, "DC=%s,%s", zone, zone_prefixes[i])) {
761                         talloc_free(tmp_ctx);
762                         return ISC_R_NOMEMORY;
763                 }
764
765                 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
766                                  attrs, "objectClass=dnsNode");
767                 if (ret == LDB_SUCCESS) {
768                         break;
769                 }
770         }
771         if (ret != LDB_SUCCESS) {
772                 talloc_free(tmp_ctx);
773                 return ISC_R_NOTFOUND;
774         }
775
776         for (i=0; i<res->count; i++) {
777                 struct ldb_message_element *el;
778                 TALLOC_CTX *el_ctx = talloc_new(tmp_ctx);
779                 const char *rdn, *name;
780                 const struct ldb_val *v;
781
782                 el = ldb_msg_find_element(res->msgs[i], "dnsRecord");
783                 if (el == NULL || el->num_values == 0) {
784                         state->log(ISC_LOG_INFO, "failed to find dnsRecord for %s",
785                                    ldb_dn_get_linearized(dn));
786                         talloc_free(el_ctx);
787                         continue;
788                 }
789
790                 v = ldb_dn_get_rdn_val(res->msgs[i]->dn);
791                 if (v == NULL) {
792                         state->log(ISC_LOG_INFO, "failed to find RDN for %s",
793                                    ldb_dn_get_linearized(dn));
794                         talloc_free(el_ctx);
795                         continue;
796                 }
797
798                 rdn = talloc_strndup(el_ctx, (char *)v->data, v->length);
799                 if (rdn == NULL) {
800                         talloc_free(tmp_ctx);
801                         return ISC_R_NOMEMORY;
802                 }
803
804                 if (strcmp(rdn, "@") == 0) {
805                         name = zone;
806                 } else {
807                         name = talloc_asprintf(el_ctx, "%s.%s", rdn, zone);
808                 }
809                 if (name == NULL) {
810                         talloc_free(tmp_ctx);
811                         return ISC_R_NOMEMORY;
812                 }
813
814                 for (j=0; j<el->num_values; j++) {
815                         struct dnsp_DnssrvRpcRecord rec;
816                         enum ndr_err_code ndr_err;
817                         isc_result_t result;
818
819                         ndr_err = ndr_pull_struct_blob(&el->values[j], el_ctx, &rec,
820                                                        (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
821                         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
822                                 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
823                                            ldb_dn_get_linearized(dn));
824                                 talloc_free(el_ctx);
825                                 continue;
826                         }
827
828                         result = b9_putnamedrr(state, allnodes, name, &rec);
829                         if (result != ISC_R_SUCCESS) {
830                                 talloc_free(el_ctx);
831                                 continue;
832                         }
833                 }
834         }
835
836         talloc_free(tmp_ctx);
837
838         return ISC_R_SUCCESS;
839 }
840
841
842 /*
843   start a transaction
844  */
845 _PUBLIC_ isc_result_t dlz_newversion(const char *zone, void *dbdata, void **versionp)
846 {
847         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
848
849         state->log(ISC_LOG_INFO, "samba_dlz: starting transaction on zone %s", zone);
850
851         if (state->transaction_token != NULL) {
852                 state->log(ISC_LOG_INFO, "samba_dlz: transaction already started for zone %s", zone);
853                 return ISC_R_FAILURE;
854         }
855
856         state->transaction_token = talloc_zero(state, int);
857         if (state->transaction_token == NULL) {
858                 return ISC_R_NOMEMORY;
859         }
860
861         if (ldb_transaction_start(state->samdb) != LDB_SUCCESS) {
862                 state->log(ISC_LOG_INFO, "samba_dlz: failed to start a transaction for zone %s", zone);
863                 talloc_free(state->transaction_token);
864                 state->transaction_token = NULL;
865                 return ISC_R_FAILURE;
866         }
867
868         *versionp = (void *)state->transaction_token;
869
870         return ISC_R_SUCCESS;
871 }
872
873 /*
874   end a transaction
875  */
876 _PUBLIC_ void dlz_closeversion(const char *zone, isc_boolean_t commit,
877                                void *dbdata, void **versionp)
878 {
879         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
880
881         if (state->transaction_token != (int *)*versionp) {
882                 state->log(ISC_LOG_INFO, "samba_dlz: transaction not started for zone %s", zone);
883                 return;
884         }
885
886         if (commit) {
887                 if (ldb_transaction_commit(state->samdb) != LDB_SUCCESS) {
888                         state->log(ISC_LOG_INFO, "samba_dlz: failed to commit a transaction for zone %s", zone);
889                         return;
890                 }
891                 state->log(ISC_LOG_INFO, "samba_dlz: committed transaction on zone %s", zone);
892         } else {
893                 if (ldb_transaction_cancel(state->samdb) != LDB_SUCCESS) {
894                         state->log(ISC_LOG_INFO, "samba_dlz: failed to cancel a transaction for zone %s", zone);
895                         return;
896                 }
897                 state->log(ISC_LOG_INFO, "samba_dlz: cancelling transaction on zone %s", zone);
898         }
899
900         talloc_free(state->transaction_token);
901         state->transaction_token = NULL;
902         *versionp = NULL;
903 }
904
905
906 /*
907   see if there is a SOA record for a zone
908  */
909 static bool b9_has_soa(struct dlz_bind9_data *state, struct ldb_dn *dn, const char *zone)
910 {
911         const char *attrs[] = { "dnsRecord", NULL };
912         struct ldb_result *res;
913         struct ldb_message_element *el;
914         TALLOC_CTX *tmp_ctx = talloc_new(state);
915         int ret, i;
916
917         if (!ldb_dn_add_child_fmt(dn, "DC=@,DC=%s", zone)) {
918                 talloc_free(tmp_ctx);
919                 return false;
920         }
921
922         ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
923                          attrs, "objectClass=dnsNode");
924         if (ret != LDB_SUCCESS) {
925                 talloc_free(tmp_ctx);
926                 return false;
927         }
928
929         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
930         if (el == NULL) {
931                 talloc_free(tmp_ctx);
932                 return false;
933         }
934         for (i=0; i<el->num_values; i++) {
935                 struct dnsp_DnssrvRpcRecord rec;
936                 enum ndr_err_code ndr_err;
937
938                 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec,
939                                                (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
940                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
941                         continue;
942                 }
943                 if (rec.wType == DNS_TYPE_SOA) {
944                         talloc_free(tmp_ctx);
945                         return true;
946                 }
947         }
948
949         talloc_free(tmp_ctx);
950         return false;
951 }
952
953 /*
954   configure a writeable zone
955  */
956 _PUBLIC_ isc_result_t dlz_configure(dns_view_t *view, void *dbdata)
957 {
958         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
959         TALLOC_CTX *tmp_ctx;
960         struct ldb_dn *dn;
961         int i;
962
963         state->log(ISC_LOG_INFO, "samba_dlz: starting configure");
964         if (state->writeable_zone == NULL) {
965                 state->log(ISC_LOG_INFO, "samba_dlz: no writeable_zone method available");
966                 return ISC_R_FAILURE;
967         }
968
969         tmp_ctx = talloc_new(state);
970
971         for (i=0; zone_prefixes[i]; i++) {
972                 const char *attrs[] = { "name", NULL };
973                 int j, ret;
974                 struct ldb_result *res;
975
976                 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
977                 if (dn == NULL) {
978                         talloc_free(tmp_ctx);
979                         return ISC_R_NOMEMORY;
980                 }
981
982                 if (!ldb_dn_add_child_fmt(dn, "%s", zone_prefixes[i])) {
983                         talloc_free(tmp_ctx);
984                         return ISC_R_NOMEMORY;
985                 }
986
987                 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
988                                  attrs, "objectClass=dnsZone");
989                 if (ret != LDB_SUCCESS) {
990                         continue;
991                 }
992
993                 for (j=0; j<res->count; j++) {
994                         isc_result_t result;
995                         const char *zone = ldb_msg_find_attr_as_string(res->msgs[j], "name", NULL);
996                         if (zone == NULL) {
997                                 continue;
998                         }
999                         if (!b9_has_soa(state, dn, zone)) {
1000                                 continue;
1001                         }
1002                         result = state->writeable_zone(view, zone);
1003                         if (result != ISC_R_SUCCESS) {
1004                                 state->log(ISC_LOG_ERROR, "samba_dlz: Failed to configure zone '%s'",
1005                                            zone);
1006                                 talloc_free(tmp_ctx);
1007                                 return result;
1008                         }
1009                         state->log(ISC_LOG_INFO, "samba_dlz: configured writeable zone '%s'", zone);
1010                 }
1011         }
1012
1013         talloc_free(tmp_ctx);
1014         return ISC_R_SUCCESS;
1015 }
1016
1017 /*
1018   authorize a zone update
1019  */
1020 _PUBLIC_ isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
1021                                     const char *type, const char *key, uint32_t keydatalen, uint8_t *keydata,
1022                                     void *dbdata)
1023 {
1024         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1025
1026         state->log(ISC_LOG_INFO, "samba_dlz: allowing update of signer=%s name=%s tcpaddr=%s type=%s key=%s keydatalen=%u",
1027                    signer, name, tcpaddr, type, key, keydatalen);
1028         return true;
1029 }
1030
1031
1032 /*
1033   add a new record
1034  */
1035 static isc_result_t b9_add_record(struct dlz_bind9_data *state, const char *name,
1036                                   struct ldb_dn *dn,
1037                                   struct dnsp_DnssrvRpcRecord *rec)
1038 {
1039         struct ldb_message *msg;
1040         enum ndr_err_code ndr_err;
1041         struct ldb_val v;
1042         int ret;
1043
1044         msg = ldb_msg_new(rec);
1045         if (msg == NULL) {
1046                 return ISC_R_NOMEMORY;
1047         }
1048         msg->dn = dn;
1049         ret = ldb_msg_add_string(msg, "objectClass", "dnsNode");
1050         if (ret != LDB_SUCCESS) {
1051                 return ISC_R_FAILURE;
1052         }
1053
1054         ndr_err = ndr_push_struct_blob(&v, rec, rec, (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
1055         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1056                 return ISC_R_FAILURE;
1057         }
1058         ret = ldb_msg_add_value(msg, "dnsRecord", &v, NULL);
1059         if (ret != LDB_SUCCESS) {
1060                 return ISC_R_FAILURE;
1061         }
1062
1063         ret = ldb_add(state->samdb, msg);
1064         if (ret != LDB_SUCCESS) {
1065                 return ISC_R_FAILURE;
1066         }
1067
1068         return ISC_R_SUCCESS;
1069 }
1070
1071
1072 /*
1073   see if two dns records match
1074  */
1075 static bool b9_record_match(struct dlz_bind9_data *state,
1076                             struct dnsp_DnssrvRpcRecord *rec1, struct dnsp_DnssrvRpcRecord *rec2)
1077 {
1078         if (rec1->wType != rec2->wType) {
1079                 return false;
1080         }
1081         /* see if this type is single valued */
1082         if (b9_single_valued(rec1->wType)) {
1083                 return true;
1084         }
1085
1086         /* see if the data matches */
1087         switch (rec1->wType) {
1088         case DNS_TYPE_A:
1089                 return strcmp(rec1->data.ipv4, rec2->data.ipv4) == 0;
1090         case DNS_TYPE_AAAA:
1091                 return strcmp(rec1->data.ipv6, rec2->data.ipv6) == 0;
1092         case DNS_TYPE_CNAME:
1093                 return strcmp(rec1->data.cname, rec2->data.cname) == 0;
1094         case DNS_TYPE_TXT:
1095                 return strcmp(rec1->data.txt, rec2->data.txt) == 0;
1096         case DNS_TYPE_PTR:
1097                 return strcmp(rec1->data.ptr, rec2->data.ptr) == 0;
1098         case DNS_TYPE_NS:
1099                 return strcmp(rec1->data.ns, rec2->data.ns) == 0;
1100
1101         case DNS_TYPE_SRV:
1102                 return rec1->data.srv.wPriority == rec2->data.srv.wPriority &&
1103                         rec1->data.srv.wWeight  == rec2->data.srv.wWeight &&
1104                         rec1->data.srv.wPort    == rec2->data.srv.wPort &&
1105                         strcmp(rec1->data.srv.nameTarget, rec2->data.srv.nameTarget) == 0;
1106
1107         case DNS_TYPE_MX:
1108                 return rec1->data.mx.wPriority == rec2->data.mx.wPriority &&
1109                         strcmp(rec1->data.mx.nameTarget, rec2->data.mx.nameTarget) == 0;
1110
1111         case DNS_TYPE_HINFO:
1112                 return strcmp(rec1->data.hinfo.cpu, rec2->data.hinfo.cpu) == 0 &&
1113                         strcmp(rec1->data.hinfo.os, rec2->data.hinfo.os) == 0;
1114
1115         case DNS_TYPE_SOA:
1116                 return strcmp(rec1->data.soa.mname, rec2->data.soa.mname) == 0 &&
1117                         strcmp(rec1->data.soa.rname, rec2->data.soa.rname) == 0 &&
1118                         rec1->data.soa.serial == rec2->data.soa.serial &&
1119                         rec1->data.soa.refresh == rec2->data.soa.refresh &&
1120                         rec1->data.soa.retry == rec2->data.soa.retry &&
1121                         rec1->data.soa.expire == rec2->data.soa.expire &&
1122                         rec1->data.soa.minimum == rec2->data.soa.minimum;
1123         default:
1124                 state->log(ISC_LOG_ERROR, "samba b9_putrr: unhandled record type %u",
1125                            rec1->wType);
1126                 break;
1127         }
1128
1129         return false;
1130 }
1131
1132
1133 /*
1134   add or modify a rdataset
1135  */
1136 _PUBLIC_ isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
1137 {
1138         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1139         struct dnsp_DnssrvRpcRecord *rec;
1140         struct ldb_dn *dn;
1141         isc_result_t result;
1142         struct ldb_result *res;
1143         const char *attrs[] = { "dnsRecord", NULL };
1144         int ret, i;
1145         struct ldb_message_element *el;
1146         enum ndr_err_code ndr_err;
1147
1148         if (state->transaction_token != (void*)version) {
1149                 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1150                 return ISC_R_FAILURE;
1151         }
1152
1153         rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
1154         if (rec == NULL) {
1155                 return ISC_R_NOMEMORY;
1156         }
1157
1158         if (!b9_parse(state, rdatastr, rec)) {
1159                 state->log(ISC_LOG_INFO, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
1160                 talloc_free(rec);
1161                 return ISC_R_FAILURE;
1162         }
1163
1164         /* find the DN of the record */
1165         result = b9_find_name_dn(state, name, rec, &dn);
1166         if (result != ISC_R_SUCCESS) {
1167                 talloc_free(rec);
1168                 return result;
1169         }
1170
1171         /* get any existing records */
1172         ret = ldb_search(state->samdb, rec, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1173         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1174                 result = b9_add_record(state, name, dn, rec);
1175                 talloc_free(rec);
1176                 if (result == ISC_R_SUCCESS) {
1177                         state->log(ISC_LOG_ERROR, "samba_dlz: added %s %s", name, rdatastr);
1178                 }
1179                 return result;
1180         }
1181
1182         /* there are existing records. We need to see if this will
1183          * replace a record or add to it
1184          */
1185         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1186         if (el == NULL) {
1187                 state->log(ISC_LOG_ERROR, "samba_dlz: no dnsRecord attribute for %s",
1188                            ldb_dn_get_linearized(dn));
1189                 talloc_free(rec);
1190                 return ISC_R_FAILURE;
1191         }
1192
1193         for (i=0; i<el->num_values; i++) {
1194                 struct dnsp_DnssrvRpcRecord rec2;
1195
1196                 ndr_err = ndr_pull_struct_blob(&el->values[i], rec, &rec2,
1197                                                (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1198                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1199                         state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1200                                    ldb_dn_get_linearized(dn));
1201                         talloc_free(rec);
1202                         return ISC_R_FAILURE;
1203                 }
1204
1205                 if (b9_record_match(state, rec, &rec2)) {
1206                         break;
1207                 }
1208         }
1209         if (i == el->num_values) {
1210                 /* adding a new value */
1211                 el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values+1);
1212                 if (el->values == NULL) {
1213                         talloc_free(rec);
1214                         return ISC_R_NOMEMORY;
1215                 }
1216                 el->num_values++;
1217         }
1218
1219         ndr_err = ndr_push_struct_blob(&el->values[i], rec, rec,
1220                                        (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
1221         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1222                 state->log(ISC_LOG_ERROR, "samba_dlz: failed to push dnsRecord for %s",
1223                            ldb_dn_get_linearized(dn));
1224                 talloc_free(rec);
1225                 return ISC_R_FAILURE;
1226         }
1227
1228         /* modify the record */
1229         el->flags = LDB_FLAG_MOD_REPLACE;
1230         ret = ldb_modify(state->samdb, res->msgs[0]);
1231         if (ret != LDB_SUCCESS) {
1232                 state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
1233                            ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1234                 talloc_free(rec);
1235                 return ISC_R_FAILURE;
1236         }
1237
1238         state->log(ISC_LOG_INFO, "samba_dlz: added rdataset %s '%s'", name, rdatastr);
1239
1240         talloc_free(rec);
1241         return ISC_R_SUCCESS;
1242 }
1243
1244 /*
1245   remove a rdataset
1246  */
1247 _PUBLIC_ isc_result_t dlz_subrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
1248 {
1249         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1250         struct dnsp_DnssrvRpcRecord *rec;
1251         struct ldb_dn *dn;
1252         isc_result_t result;
1253         struct ldb_result *res;
1254         const char *attrs[] = { "dnsRecord", NULL };
1255         int ret, i;
1256         struct ldb_message_element *el;
1257         enum ndr_err_code ndr_err;
1258
1259         if (state->transaction_token != (void*)version) {
1260                 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1261                 return ISC_R_FAILURE;
1262         }
1263
1264         rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
1265         if (rec == NULL) {
1266                 return ISC_R_NOMEMORY;
1267         }
1268
1269         if (!b9_parse(state, rdatastr, rec)) {
1270                 state->log(ISC_LOG_INFO, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
1271                 talloc_free(rec);
1272                 return ISC_R_FAILURE;
1273         }
1274
1275         /* find the DN of the record */
1276         result = b9_find_name_dn(state, name, rec, &dn);
1277         if (result != ISC_R_SUCCESS) {
1278                 talloc_free(rec);
1279                 return result;
1280         }
1281
1282         /* get the existing records */
1283         ret = ldb_search(state->samdb, rec, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1284         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1285                 talloc_free(rec);
1286                 return ISC_R_NOTFOUND;
1287         }
1288
1289         /* there are existing records. We need to see if any match
1290          */
1291         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1292         if (el == NULL || el->num_values == 0) {
1293                 state->log(ISC_LOG_ERROR, "samba_dlz: no dnsRecord attribute for %s",
1294                            ldb_dn_get_linearized(dn));
1295                 talloc_free(rec);
1296                 return ISC_R_FAILURE;
1297         }
1298
1299         for (i=0; i<el->num_values; i++) {
1300                 struct dnsp_DnssrvRpcRecord rec2;
1301
1302                 ndr_err = ndr_pull_struct_blob(&el->values[i], rec, &rec2,
1303                                                (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1304                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1305                         state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1306                                    ldb_dn_get_linearized(dn));
1307                         talloc_free(rec);
1308                         return ISC_R_FAILURE;
1309                 }
1310
1311                 if (b9_record_match(state, rec, &rec2)) {
1312                         break;
1313                 }
1314         }
1315         if (i == el->num_values) {
1316                 talloc_free(rec);
1317                 return ISC_R_NOTFOUND;
1318         }
1319
1320         if (i < el->num_values-1) {
1321                 memmove(&el->values[i], &el->values[i+1], sizeof(el->values[0])*((el->num_values-1)-i));
1322         }
1323         el->num_values--;
1324
1325         if (el->num_values == 0) {
1326                 /* delete the record */
1327                 ret = ldb_delete(state->samdb, dn);
1328         } else {
1329                 /* modify the record */
1330                 el->flags = LDB_FLAG_MOD_REPLACE;
1331                 ret = ldb_modify(state->samdb, res->msgs[0]);
1332         }
1333         if (ret != LDB_SUCCESS) {
1334                 state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
1335                            ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1336                 talloc_free(rec);
1337                 return ISC_R_FAILURE;
1338         }
1339
1340         state->log(ISC_LOG_INFO, "samba_dlz: subtracted rdataset %s '%s'", name, rdatastr);
1341
1342         talloc_free(rec);
1343         return ISC_R_SUCCESS;
1344 }
1345
1346
1347 /*
1348   delete all records of the given type
1349  */
1350 _PUBLIC_ isc_result_t dlz_delrdataset(const char *name, const char *type, void *dbdata, void *version)
1351 {
1352         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1353         TALLOC_CTX *tmp_ctx;
1354         struct ldb_dn *dn;
1355         isc_result_t result;
1356         struct ldb_result *res;
1357         const char *attrs[] = { "dnsRecord", NULL };
1358         int ret, i;
1359         struct ldb_message_element *el;
1360         enum ndr_err_code ndr_err;
1361         enum dns_record_type dns_type;
1362         bool found = false;
1363
1364         if (state->transaction_token != (void*)version) {
1365                 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1366                 return ISC_R_FAILURE;
1367         }
1368
1369         dns_type = b9_dns_type(type);
1370         if (dns_type == DNS_TYPE_ZERO) {
1371                 state->log(ISC_LOG_INFO, "samba_dlz: bad dns type %s in delete", type);
1372                 return ISC_R_FAILURE;
1373         }
1374
1375         tmp_ctx = talloc_new(state);
1376
1377         /* find the DN of the record */
1378         result = b9_find_name_dn(state, name, tmp_ctx, &dn);
1379         if (result != ISC_R_SUCCESS) {
1380                 talloc_free(tmp_ctx);
1381                 return result;
1382         }
1383
1384         /* get the existing records */
1385         ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1386         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1387                 talloc_free(tmp_ctx);
1388                 return ISC_R_NOTFOUND;
1389         }
1390
1391         /* there are existing records. We need to see if any match the type
1392          */
1393         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1394         if (el == NULL || el->num_values == 0) {
1395                 talloc_free(tmp_ctx);
1396                 return ISC_R_NOTFOUND;
1397         }
1398
1399         for (i=0; i<el->num_values; i++) {
1400                 struct dnsp_DnssrvRpcRecord rec2;
1401
1402                 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec2,
1403                                                (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1404                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1405                         state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1406                                    ldb_dn_get_linearized(dn));
1407                         talloc_free(tmp_ctx);
1408                         return ISC_R_FAILURE;
1409                 }
1410
1411                 if (dns_type == rec2.wType) {
1412                         if (i < el->num_values-1) {
1413                                 memmove(&el->values[i], &el->values[i+1],
1414                                         sizeof(el->values[0])*((el->num_values-1)-i));
1415                         }
1416                         el->num_values--;
1417                         i--;
1418                         found = true;
1419                 }
1420         }
1421
1422         if (!found) {
1423                 talloc_free(tmp_ctx);
1424                 return ISC_R_FAILURE;
1425         }
1426
1427         if (el->num_values == 0) {
1428                 /* delete the record */
1429                 ret = ldb_delete(state->samdb, dn);
1430         } else {
1431                 /* modify the record */
1432                 el->flags = LDB_FLAG_MOD_REPLACE;
1433                 ret = ldb_modify(state->samdb, res->msgs[0]);
1434         }
1435         if (ret != LDB_SUCCESS) {
1436                 state->log(ISC_LOG_ERROR, "samba_dlz: failed to delete type %s in %s - %s",
1437                            type, ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1438                 talloc_free(tmp_ctx);
1439                 return ISC_R_FAILURE;
1440         }
1441
1442         state->log(ISC_LOG_INFO, "samba_dlz: deleted rdataset %s of type %s", name, type);
1443
1444         talloc_free(tmp_ctx);
1445         return ISC_R_SUCCESS;
1446 }