dc4c4bcd1e11aa0c7b1e63b9950f24cab72e92b9
[gd/samba-autobuild/.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                                 continue;
825                         }
826
827                         result = b9_putnamedrr(state, allnodes, name, &rec);
828                         if (result != ISC_R_SUCCESS) {
829                                 continue;
830                         }
831                 }
832         }
833
834         talloc_free(tmp_ctx);
835
836         return ISC_R_SUCCESS;
837 }
838
839
840 /*
841   start a transaction
842  */
843 _PUBLIC_ isc_result_t dlz_newversion(const char *zone, void *dbdata, void **versionp)
844 {
845         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
846
847         state->log(ISC_LOG_INFO, "samba_dlz: starting transaction on zone %s", zone);
848
849         if (state->transaction_token != NULL) {
850                 state->log(ISC_LOG_INFO, "samba_dlz: transaction already started for zone %s", zone);
851                 return ISC_R_FAILURE;
852         }
853
854         state->transaction_token = talloc_zero(state, int);
855         if (state->transaction_token == NULL) {
856                 return ISC_R_NOMEMORY;
857         }
858
859         if (ldb_transaction_start(state->samdb) != LDB_SUCCESS) {
860                 state->log(ISC_LOG_INFO, "samba_dlz: failed to start a transaction for zone %s", zone);
861                 talloc_free(state->transaction_token);
862                 state->transaction_token = NULL;
863                 return ISC_R_FAILURE;
864         }
865
866         *versionp = (void *)state->transaction_token;
867
868         return ISC_R_SUCCESS;
869 }
870
871 /*
872   end a transaction
873  */
874 _PUBLIC_ void dlz_closeversion(const char *zone, isc_boolean_t commit,
875                                void *dbdata, void **versionp)
876 {
877         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
878
879         if (state->transaction_token != (int *)*versionp) {
880                 state->log(ISC_LOG_INFO, "samba_dlz: transaction not started for zone %s", zone);
881                 return;
882         }
883
884         if (commit) {
885                 if (ldb_transaction_commit(state->samdb) != LDB_SUCCESS) {
886                         state->log(ISC_LOG_INFO, "samba_dlz: failed to commit a transaction for zone %s", zone);
887                         return;
888                 }
889                 state->log(ISC_LOG_INFO, "samba_dlz: committed transaction on zone %s", zone);
890         } else {
891                 if (ldb_transaction_cancel(state->samdb) != LDB_SUCCESS) {
892                         state->log(ISC_LOG_INFO, "samba_dlz: failed to cancel a transaction for zone %s", zone);
893                         return;
894                 }
895                 state->log(ISC_LOG_INFO, "samba_dlz: cancelling transaction on zone %s", zone);
896         }
897
898         talloc_free(state->transaction_token);
899         state->transaction_token = NULL;
900         *versionp = NULL;
901 }
902
903
904 /*
905   see if there is a SOA record for a zone
906  */
907 static bool b9_has_soa(struct dlz_bind9_data *state, struct ldb_dn *dn, const char *zone)
908 {
909         const char *attrs[] = { "dnsRecord", NULL };
910         struct ldb_result *res;
911         struct ldb_message_element *el;
912         TALLOC_CTX *tmp_ctx = talloc_new(state);
913         int ret, i;
914
915         if (!ldb_dn_add_child_fmt(dn, "DC=@,DC=%s", zone)) {
916                 talloc_free(tmp_ctx);
917                 return false;
918         }
919
920         ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
921                          attrs, "objectClass=dnsNode");
922         if (ret != LDB_SUCCESS) {
923                 talloc_free(tmp_ctx);
924                 return false;
925         }
926
927         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
928         if (el == NULL) {
929                 talloc_free(tmp_ctx);
930                 return false;
931         }
932         for (i=0; i<el->num_values; i++) {
933                 struct dnsp_DnssrvRpcRecord rec;
934                 enum ndr_err_code ndr_err;
935
936                 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec,
937                                                (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
938                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
939                         continue;
940                 }
941                 if (rec.wType == DNS_TYPE_SOA) {
942                         talloc_free(tmp_ctx);
943                         return true;
944                 }
945         }
946
947         talloc_free(tmp_ctx);
948         return false;
949 }
950
951 /*
952   configure a writeable zone
953  */
954 _PUBLIC_ isc_result_t dlz_configure(dns_view_t *view, void *dbdata)
955 {
956         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
957         TALLOC_CTX *tmp_ctx;
958         struct ldb_dn *dn;
959         int i;
960
961         state->log(ISC_LOG_INFO, "samba_dlz: starting configure");
962         if (state->writeable_zone == NULL) {
963                 state->log(ISC_LOG_INFO, "samba_dlz: no writeable_zone method available");
964                 return ISC_R_FAILURE;
965         }
966
967         tmp_ctx = talloc_new(state);
968
969         for (i=0; zone_prefixes[i]; i++) {
970                 const char *attrs[] = { "name", NULL };
971                 int j, ret;
972                 struct ldb_result *res;
973
974                 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
975                 if (dn == NULL) {
976                         talloc_free(tmp_ctx);
977                         return ISC_R_NOMEMORY;
978                 }
979
980                 if (!ldb_dn_add_child_fmt(dn, "%s", zone_prefixes[i])) {
981                         talloc_free(tmp_ctx);
982                         return ISC_R_NOMEMORY;
983                 }
984
985                 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
986                                  attrs, "objectClass=dnsZone");
987                 if (ret != LDB_SUCCESS) {
988                         continue;
989                 }
990
991                 for (j=0; j<res->count; j++) {
992                         isc_result_t result;
993                         const char *zone = ldb_msg_find_attr_as_string(res->msgs[j], "name", NULL);
994                         if (zone == NULL) {
995                                 continue;
996                         }
997                         if (!b9_has_soa(state, dn, zone)) {
998                                 continue;
999                         }
1000                         result = state->writeable_zone(view, zone);
1001                         if (result != ISC_R_SUCCESS) {
1002                                 state->log(ISC_LOG_ERROR, "samba_dlz: Failed to configure zone '%s'",
1003                                            zone);
1004                                 talloc_free(tmp_ctx);
1005                                 return result;
1006                         }
1007                         state->log(ISC_LOG_INFO, "samba_dlz: configured writeable zone '%s'", zone);
1008                 }
1009         }
1010
1011         talloc_free(tmp_ctx);
1012         return ISC_R_SUCCESS;
1013 }
1014
1015 /*
1016   authorize a zone update
1017  */
1018 _PUBLIC_ isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
1019                                     const char *type, const char *key, uint32_t keydatalen, uint8_t *keydata,
1020                                     void *dbdata)
1021 {
1022         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1023
1024         state->log(ISC_LOG_INFO, "samba_dlz: allowing update of signer=%s name=%s tcpaddr=%s type=%s key=%s keydatalen=%u",
1025                    signer, name, tcpaddr, type, key, keydatalen);
1026         return true;
1027 }
1028
1029
1030 /*
1031   add a new record
1032  */
1033 static isc_result_t b9_add_record(struct dlz_bind9_data *state, const char *name,
1034                                   struct ldb_dn *dn,
1035                                   struct dnsp_DnssrvRpcRecord *rec)
1036 {
1037         struct ldb_message *msg;
1038         enum ndr_err_code ndr_err;
1039         struct ldb_val v;
1040         int ret;
1041
1042         msg = ldb_msg_new(rec);
1043         if (msg == NULL) {
1044                 return ISC_R_NOMEMORY;
1045         }
1046         msg->dn = dn;
1047         ret = ldb_msg_add_string(msg, "objectClass", "dnsNode");
1048         if (ret != LDB_SUCCESS) {
1049                 return ISC_R_FAILURE;
1050         }
1051
1052         ndr_err = ndr_push_struct_blob(&v, rec, rec, (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
1053         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1054                 return ISC_R_FAILURE;
1055         }
1056         ret = ldb_msg_add_value(msg, "dnsRecord", &v, NULL);
1057         if (ret != LDB_SUCCESS) {
1058                 return ISC_R_FAILURE;
1059         }
1060
1061         ret = ldb_add(state->samdb, msg);
1062         if (ret != LDB_SUCCESS) {
1063                 return ISC_R_FAILURE;
1064         }
1065
1066         return ISC_R_SUCCESS;
1067 }
1068
1069
1070 /*
1071   see if two dns records match
1072  */
1073 static bool b9_record_match(struct dlz_bind9_data *state,
1074                             struct dnsp_DnssrvRpcRecord *rec1, struct dnsp_DnssrvRpcRecord *rec2)
1075 {
1076         if (rec1->wType != rec2->wType) {
1077                 return false;
1078         }
1079         /* see if this type is single valued */
1080         if (b9_single_valued(rec1->wType)) {
1081                 return true;
1082         }
1083
1084         /* see if the data matches */
1085         switch (rec1->wType) {
1086         case DNS_TYPE_A:
1087                 return strcmp(rec1->data.ipv4, rec2->data.ipv4) == 0;
1088         case DNS_TYPE_AAAA:
1089                 return strcmp(rec1->data.ipv6, rec2->data.ipv6) == 0;
1090         case DNS_TYPE_CNAME:
1091                 return strcmp(rec1->data.cname, rec2->data.cname) == 0;
1092         case DNS_TYPE_TXT:
1093                 return strcmp(rec1->data.txt, rec2->data.txt) == 0;
1094         case DNS_TYPE_PTR:
1095                 return strcmp(rec1->data.ptr, rec2->data.ptr) == 0;
1096         case DNS_TYPE_NS:
1097                 return strcmp(rec1->data.ns, rec2->data.ns) == 0;
1098
1099         case DNS_TYPE_SRV:
1100                 return rec1->data.srv.wPriority == rec2->data.srv.wPriority &&
1101                         rec1->data.srv.wWeight  == rec2->data.srv.wWeight &&
1102                         rec1->data.srv.wPort    == rec2->data.srv.wPort &&
1103                         strcmp(rec1->data.srv.nameTarget, rec2->data.srv.nameTarget) == 0;
1104
1105         case DNS_TYPE_MX:
1106                 return rec1->data.mx.wPriority == rec2->data.mx.wPriority &&
1107                         strcmp(rec1->data.mx.nameTarget, rec2->data.mx.nameTarget) == 0;
1108
1109         case DNS_TYPE_HINFO:
1110                 return strcmp(rec1->data.hinfo.cpu, rec2->data.hinfo.cpu) == 0 &&
1111                         strcmp(rec1->data.hinfo.os, rec2->data.hinfo.os) == 0;
1112
1113         case DNS_TYPE_SOA:
1114                 return strcmp(rec1->data.soa.mname, rec2->data.soa.mname) == 0 &&
1115                         strcmp(rec1->data.soa.rname, rec2->data.soa.rname) == 0 &&
1116                         rec1->data.soa.serial == rec2->data.soa.serial &&
1117                         rec1->data.soa.refresh == rec2->data.soa.refresh &&
1118                         rec1->data.soa.retry == rec2->data.soa.retry &&
1119                         rec1->data.soa.expire == rec2->data.soa.expire &&
1120                         rec1->data.soa.minimum == rec2->data.soa.minimum;
1121         default:
1122                 state->log(ISC_LOG_ERROR, "samba b9_putrr: unhandled record type %u",
1123                            rec1->wType);
1124                 break;
1125         }
1126
1127         return false;
1128 }
1129
1130
1131 /*
1132   add or modify a rdataset
1133  */
1134 _PUBLIC_ isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
1135 {
1136         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1137         struct dnsp_DnssrvRpcRecord *rec;
1138         struct ldb_dn *dn;
1139         isc_result_t result;
1140         struct ldb_result *res;
1141         const char *attrs[] = { "dnsRecord", NULL };
1142         int ret, i;
1143         struct ldb_message_element *el;
1144         enum ndr_err_code ndr_err;
1145
1146         if (state->transaction_token != (void*)version) {
1147                 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1148                 return ISC_R_FAILURE;
1149         }
1150
1151         rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
1152         if (rec == NULL) {
1153                 return ISC_R_NOMEMORY;
1154         }
1155
1156         if (!b9_parse(state, rdatastr, rec)) {
1157                 state->log(ISC_LOG_INFO, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
1158                 talloc_free(rec);
1159                 return ISC_R_FAILURE;
1160         }
1161
1162         /* find the DN of the record */
1163         result = b9_find_name_dn(state, name, rec, &dn);
1164         if (result != ISC_R_SUCCESS) {
1165                 talloc_free(rec);
1166                 return result;
1167         }
1168
1169         /* get any existing records */
1170         ret = ldb_search(state->samdb, rec, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1171         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1172                 result = b9_add_record(state, name, dn, rec);
1173                 talloc_free(rec);
1174                 if (result == ISC_R_SUCCESS) {
1175                         state->log(ISC_LOG_ERROR, "samba_dlz: added %s %s", name, rdatastr);
1176                 }
1177                 return result;
1178         }
1179
1180         /* there are existing records. We need to see if this will
1181          * replace a record or add to it
1182          */
1183         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1184         if (el == NULL) {
1185                 state->log(ISC_LOG_ERROR, "samba_dlz: no dnsRecord attribute for %s",
1186                            ldb_dn_get_linearized(dn));
1187                 talloc_free(rec);
1188                 return ISC_R_FAILURE;
1189         }
1190
1191         for (i=0; i<el->num_values; i++) {
1192                 struct dnsp_DnssrvRpcRecord rec2;
1193
1194                 ndr_err = ndr_pull_struct_blob(&el->values[i], rec, &rec2,
1195                                                (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1196                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1197                         state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1198                                    ldb_dn_get_linearized(dn));
1199                         talloc_free(rec);
1200                         return ISC_R_FAILURE;
1201                 }
1202
1203                 if (b9_record_match(state, rec, &rec2)) {
1204                         break;
1205                 }
1206         }
1207         if (i == el->num_values) {
1208                 /* adding a new value */
1209                 el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values+1);
1210                 if (el->values == NULL) {
1211                         talloc_free(rec);
1212                         return ISC_R_NOMEMORY;
1213                 }
1214                 el->num_values++;
1215         }
1216
1217         ndr_err = ndr_push_struct_blob(&el->values[i], rec, rec,
1218                                        (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
1219         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1220                 state->log(ISC_LOG_ERROR, "samba_dlz: failed to push dnsRecord for %s",
1221                            ldb_dn_get_linearized(dn));
1222                 talloc_free(rec);
1223                 return ISC_R_FAILURE;
1224         }
1225
1226         /* modify the record */
1227         el->flags = LDB_FLAG_MOD_REPLACE;
1228         ret = ldb_modify(state->samdb, res->msgs[0]);
1229         if (ret != LDB_SUCCESS) {
1230                 state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
1231                            ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1232                 talloc_free(rec);
1233                 return ISC_R_FAILURE;
1234         }
1235
1236         state->log(ISC_LOG_INFO, "samba_dlz: added rdataset %s '%s'", name, rdatastr);
1237
1238         talloc_free(rec);
1239         return ISC_R_SUCCESS;
1240 }
1241
1242 /*
1243   remove a rdataset
1244  */
1245 _PUBLIC_ isc_result_t dlz_subrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
1246 {
1247         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1248         struct dnsp_DnssrvRpcRecord *rec;
1249         struct ldb_dn *dn;
1250         isc_result_t result;
1251         struct ldb_result *res;
1252         const char *attrs[] = { "dnsRecord", NULL };
1253         int ret, i;
1254         struct ldb_message_element *el;
1255         enum ndr_err_code ndr_err;
1256
1257         if (state->transaction_token != (void*)version) {
1258                 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1259                 return ISC_R_FAILURE;
1260         }
1261
1262         rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
1263         if (rec == NULL) {
1264                 return ISC_R_NOMEMORY;
1265         }
1266
1267         if (!b9_parse(state, rdatastr, rec)) {
1268                 state->log(ISC_LOG_INFO, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
1269                 talloc_free(rec);
1270                 return ISC_R_FAILURE;
1271         }
1272
1273         /* find the DN of the record */
1274         result = b9_find_name_dn(state, name, rec, &dn);
1275         if (result != ISC_R_SUCCESS) {
1276                 talloc_free(rec);
1277                 return result;
1278         }
1279
1280         /* get the existing records */
1281         ret = ldb_search(state->samdb, rec, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1282         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1283                 talloc_free(rec);
1284                 return ISC_R_NOTFOUND;
1285         }
1286
1287         /* there are existing records. We need to see if any match
1288          */
1289         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1290         if (el == NULL || el->num_values == 0) {
1291                 state->log(ISC_LOG_ERROR, "samba_dlz: no dnsRecord attribute for %s",
1292                            ldb_dn_get_linearized(dn));
1293                 talloc_free(rec);
1294                 return ISC_R_FAILURE;
1295         }
1296
1297         for (i=0; i<el->num_values; i++) {
1298                 struct dnsp_DnssrvRpcRecord rec2;
1299
1300                 ndr_err = ndr_pull_struct_blob(&el->values[i], rec, &rec2,
1301                                                (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1302                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1303                         state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1304                                    ldb_dn_get_linearized(dn));
1305                         talloc_free(rec);
1306                         return ISC_R_FAILURE;
1307                 }
1308
1309                 if (b9_record_match(state, rec, &rec2)) {
1310                         break;
1311                 }
1312         }
1313         if (i == el->num_values) {
1314                 talloc_free(rec);
1315                 return ISC_R_NOTFOUND;
1316         }
1317
1318         if (i < el->num_values-1) {
1319                 memmove(&el->values[i], &el->values[i+1], sizeof(el->values[0])*((el->num_values-1)-i));
1320         }
1321         el->num_values--;
1322
1323         if (el->num_values == 0) {
1324                 /* delete the record */
1325                 ret = ldb_delete(state->samdb, dn);
1326         } else {
1327                 /* modify the record */
1328                 el->flags = LDB_FLAG_MOD_REPLACE;
1329                 ret = ldb_modify(state->samdb, res->msgs[0]);
1330         }
1331         if (ret != LDB_SUCCESS) {
1332                 state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
1333                            ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1334                 talloc_free(rec);
1335                 return ISC_R_FAILURE;
1336         }
1337
1338         state->log(ISC_LOG_INFO, "samba_dlz: subtracted rdataset %s '%s'", name, rdatastr);
1339
1340         talloc_free(rec);
1341         return ISC_R_SUCCESS;
1342 }
1343
1344
1345 /*
1346   delete all records of the given type
1347  */
1348 _PUBLIC_ isc_result_t dlz_delrdataset(const char *name, const char *type, void *dbdata, void *version)
1349 {
1350         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1351         TALLOC_CTX *tmp_ctx;
1352         struct ldb_dn *dn;
1353         isc_result_t result;
1354         struct ldb_result *res;
1355         const char *attrs[] = { "dnsRecord", NULL };
1356         int ret, i;
1357         struct ldb_message_element *el;
1358         enum ndr_err_code ndr_err;
1359         enum dns_record_type dns_type;
1360         bool found = false;
1361
1362         if (state->transaction_token != (void*)version) {
1363                 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1364                 return ISC_R_FAILURE;
1365         }
1366
1367         dns_type = b9_dns_type(type);
1368         if (dns_type == DNS_TYPE_ZERO) {
1369                 state->log(ISC_LOG_INFO, "samba_dlz: bad dns type %s in delete", type);
1370                 return ISC_R_FAILURE;
1371         }
1372
1373         tmp_ctx = talloc_new(state);
1374
1375         /* find the DN of the record */
1376         result = b9_find_name_dn(state, name, tmp_ctx, &dn);
1377         if (result != ISC_R_SUCCESS) {
1378                 talloc_free(tmp_ctx);
1379                 return result;
1380         }
1381
1382         /* get the existing records */
1383         ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1384         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1385                 talloc_free(tmp_ctx);
1386                 return ISC_R_NOTFOUND;
1387         }
1388
1389         /* there are existing records. We need to see if any match the type
1390          */
1391         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1392         if (el == NULL || el->num_values == 0) {
1393                 talloc_free(tmp_ctx);
1394                 return ISC_R_NOTFOUND;
1395         }
1396
1397         for (i=0; i<el->num_values; i++) {
1398                 struct dnsp_DnssrvRpcRecord rec2;
1399
1400                 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec2,
1401                                                (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1402                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1403                         state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1404                                    ldb_dn_get_linearized(dn));
1405                         talloc_free(tmp_ctx);
1406                         return ISC_R_FAILURE;
1407                 }
1408
1409                 if (dns_type == rec2.wType) {
1410                         if (i < el->num_values-1) {
1411                                 memmove(&el->values[i], &el->values[i+1],
1412                                         sizeof(el->values[0])*((el->num_values-1)-i));
1413                         }
1414                         el->num_values--;
1415                         i--;
1416                         found = true;
1417                 }
1418         }
1419
1420         if (!found) {
1421                 talloc_free(tmp_ctx);
1422                 return ISC_R_FAILURE;
1423         }
1424
1425         if (el->num_values == 0) {
1426                 /* delete the record */
1427                 ret = ldb_delete(state->samdb, dn);
1428         } else {
1429                 /* modify the record */
1430                 el->flags = LDB_FLAG_MOD_REPLACE;
1431                 ret = ldb_modify(state->samdb, res->msgs[0]);
1432         }
1433         if (ret != LDB_SUCCESS) {
1434                 state->log(ISC_LOG_ERROR, "samba_dlz: failed to delete type %s in %s - %s",
1435                            type, ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1436                 talloc_free(tmp_ctx);
1437                 return ISC_R_FAILURE;
1438         }
1439
1440         state->log(ISC_LOG_INFO, "samba_dlz: deleted rdataset %s of type %s", name, type);
1441
1442         talloc_free(tmp_ctx);
1443         return ISC_R_SUCCESS;
1444 }