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