s4-dns: added basic DLZ writeable support to dlz_bind9
[samba.git] / source4 / dns_server / dlz_bind9.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    bind9 dlz driver for Samba
5
6    Copyright (C) 2010 Andrew Tridgell
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "talloc.h"
24 #include "param/param.h"
25 #include "lib/events/events.h"
26 #include "dsdb/samdb/samdb.h"
27 #include "dsdb/common/util.h"
28 #include "auth/session.h"
29 #include "auth/gensec/gensec.h"
30 #include "gen_ndr/ndr_dnsp.h"
31 #include "lib/cmdline/popt_common.h"
32 #include "lib/cmdline/popt_credentials.h"
33 #include "ldb_module.h"
34 #include "dlz_minimal.h"
35
36 struct dlz_bind9_data {
37         struct ldb_context *samdb;
38         struct tevent_context *ev_ctx;
39         struct loadparm_context *lp;
40         bool transaction_started;
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.srv.wPriority,
132                                         rec->data.srv.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 /*
169   parse a record from bind9
170  */
171 static bool b9_parse(struct dlz_bind9_data *state,
172                      TALLOC_CTX *mem_ctx,
173                      struct dnsp_DnssrvRpcRecord *rec,
174                      const char **type, const char **data)
175 {
176         return false;
177 }
178
179 /*
180   send a resource recond to bind9
181  */
182 static isc_result_t b9_putrr(struct dlz_bind9_data *state,
183                              void *handle, struct dnsp_DnssrvRpcRecord *rec,
184                              const char **types)
185 {
186         isc_result_t result;
187         const char *type, *data;
188         TALLOC_CTX *tmp_ctx = talloc_new(state);
189
190         if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
191                 return ISC_R_FAILURE;
192         }
193
194         if (data == NULL) {
195                 talloc_free(tmp_ctx);
196                 return ISC_R_NOMEMORY;
197         }
198
199         if (types) {
200                 int i;
201                 for (i=0; types[i]; i++) {
202                         if (strcmp(types[i], type) == 0) break;
203                 }
204                 if (types[i] == NULL) {
205                         /* skip it */
206                         return ISC_R_SUCCESS;
207                 }
208         }
209
210         result = state->putrr(handle, type, rec->dwTtlSeconds, data);
211         if (result != ISC_R_SUCCESS) {
212                 state->log(ISC_LOG_ERROR, "Failed to put rr");
213         }
214         talloc_free(tmp_ctx);
215         return result;
216 }
217
218
219 /*
220   send a named resource recond to bind9
221  */
222 static isc_result_t b9_putnamedrr(struct dlz_bind9_data *state,
223                                   void *handle, const char *name,
224                                   struct dnsp_DnssrvRpcRecord *rec)
225 {
226         isc_result_t result;
227         const char *type, *data;
228         TALLOC_CTX *tmp_ctx = talloc_new(state);
229
230         if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
231                 return ISC_R_FAILURE;
232         }
233
234         if (data == NULL) {
235                 talloc_free(tmp_ctx);
236                 return ISC_R_NOMEMORY;
237         }
238
239         result = state->putnamedrr(handle, name, type, rec->dwTtlSeconds, data);
240         if (result != ISC_R_SUCCESS) {
241                 state->log(ISC_LOG_ERROR, "Failed to put named rr '%s'", name);
242         }
243         talloc_free(tmp_ctx);
244         return result;
245 }
246
247 struct b9_options {
248         const char *url;
249 };
250
251 /*
252    parse options
253  */
254 static isc_result_t parse_options(struct dlz_bind9_data *state,
255                                   unsigned int argc, char *argv[],
256                                   struct b9_options *options)
257 {
258         int opt;
259         poptContext pc;
260         struct poptOption long_options[] = {
261                 { "url",       'H', POPT_ARG_STRING, &options->url, 0, "database URL", "URL" },
262                 { NULL }
263         };
264         struct poptOption **popt_options;
265         int ret;
266
267         popt_options = ldb_module_popt_options(state->samdb);
268         (*popt_options) = long_options;
269
270         ret = ldb_modules_hook(state->samdb, LDB_MODULE_HOOK_CMDLINE_OPTIONS);
271         if (ret != LDB_SUCCESS) {
272                 state->log(ISC_LOG_ERROR, "dlz samba: failed cmdline hook");
273                 return ISC_R_FAILURE;
274         }
275
276         pc = poptGetContext("dlz_bind9", argc, (const char **)argv, *popt_options,
277                             POPT_CONTEXT_KEEP_FIRST);
278
279         while ((opt = poptGetNextOpt(pc)) != -1) {
280                 switch (opt) {
281                 default:
282                         state->log(ISC_LOG_ERROR, "dlz samba: Invalid option %s: %s",
283                                    poptBadOption(pc, 0), poptStrerror(opt));
284                         return ISC_R_FAILURE;
285                 }
286         }
287
288         ret = ldb_modules_hook(state->samdb, LDB_MODULE_HOOK_CMDLINE_PRECONNECT);
289         if (ret != LDB_SUCCESS) {
290                 state->log(ISC_LOG_ERROR, "dlz samba: failed cmdline preconnect");
291                 return ISC_R_FAILURE;
292         }
293
294         return ISC_R_SUCCESS;
295 }
296
297
298 /*
299   called to initialise the driver
300  */
301 _PUBLIC_ isc_result_t dlz_create(const char *dlzname,
302                                  unsigned int argc, char *argv[],
303                                  void **dbdata, ...)
304 {
305         struct dlz_bind9_data *state;
306         const char *helper_name;
307         va_list ap;
308         isc_result_t result;
309         TALLOC_CTX *tmp_ctx;
310         int ret;
311         struct ldb_dn *dn;
312         struct b9_options options;
313
314         ZERO_STRUCT(options);
315
316         state = talloc_zero(NULL, struct dlz_bind9_data);
317         if (state == NULL) {
318                 return ISC_R_NOMEMORY;
319         }
320
321         tmp_ctx = talloc_new(state);
322
323         /* fill in the helper functions */
324         va_start(ap, dbdata);
325         while ((helper_name = va_arg(ap, const char *)) != NULL) {
326                 b9_add_helper(state, helper_name, va_arg(ap, void*));
327         }
328         va_end(ap);
329
330         state->ev_ctx = s4_event_context_init(state);
331         if (state->ev_ctx == NULL) {
332                 result = ISC_R_NOMEMORY;
333                 goto failed;
334         }
335
336         state->samdb = ldb_init(state, state->ev_ctx);
337         if (state->samdb == NULL) {
338                 state->log(ISC_LOG_ERROR, "samba dlz_bind9: Failed to create ldb");
339                 result = ISC_R_FAILURE;
340                 goto failed;
341         }
342
343         result = parse_options(state, argc, argv, &options);
344         if (result != ISC_R_SUCCESS) {
345                 goto failed;
346         }
347
348         state->lp = loadparm_init_global(true);
349         if (state->lp == NULL) {
350                 result = ISC_R_NOMEMORY;
351                 goto failed;
352         }
353
354         if (options.url == NULL) {
355                 options.url = talloc_asprintf(tmp_ctx, "ldapi://%s",
356                                               private_path(tmp_ctx, state->lp, "ldap_priv/ldapi"));
357                 if (options.url == NULL) {
358                         result = ISC_R_NOMEMORY;
359                         goto failed;
360                 }
361         }
362
363         ret = ldb_connect(state->samdb, options.url, 0, NULL);
364         if (ret == -1) {
365                 state->log(ISC_LOG_ERROR, "samba dlz_bind9: Failed to connect to %s - %s",
366                            options.url, ldb_errstring(state->samdb));
367                 result = ISC_R_FAILURE;
368                 goto failed;
369         }
370
371         ret = ldb_modules_hook(state->samdb, LDB_MODULE_HOOK_CMDLINE_POSTCONNECT);
372         if (ret != LDB_SUCCESS) {
373                 state->log(ISC_LOG_ERROR, "samba dlz_bind9: Failed postconnect for %s - %s",
374                            options.url, ldb_errstring(state->samdb));
375                 result = ISC_R_FAILURE;
376                 goto failed;
377         }
378
379         dn = ldb_get_default_basedn(state->samdb);
380         if (dn == NULL) {
381                 state->log(ISC_LOG_ERROR, "samba dlz_bind9: Unable to get basedn for %s - %s",
382                            options.url, ldb_errstring(state->samdb));
383                 result = ISC_R_FAILURE;
384                 goto failed;
385         }
386
387         state->log(ISC_LOG_INFO, "samba dlz_bind9: started for DN %s",
388                    ldb_dn_get_linearized(dn));
389
390         *dbdata = state;
391
392         talloc_free(tmp_ctx);
393         return ISC_R_SUCCESS;
394
395 failed:
396         talloc_free(state);
397         return result;
398 }
399
400 /*
401   shutdown the backend
402  */
403 _PUBLIC_ void dlz_destroy(void *dbdata)
404 {
405         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
406         state->log(ISC_LOG_INFO, "samba dlz_bind9: shutting down");
407         talloc_free(state);
408 }
409
410
411 /*
412   see if we handle a given zone
413  */
414 _PUBLIC_ isc_result_t dlz_findzonedb(void *dbdata, const char *name)
415 {
416         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
417         int ret;
418         TALLOC_CTX *tmp_ctx = talloc_new(state);
419         const char *attrs[] = { NULL };
420         int i;
421
422         for (i=0; zone_prefixes[i]; i++) {
423                 struct ldb_dn *dn;
424                 struct ldb_result *res;
425
426                 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
427                 if (dn == NULL) {
428                         talloc_free(tmp_ctx);
429                         return ISC_R_NOMEMORY;
430                 }
431
432                 if (!ldb_dn_add_child_fmt(dn, "DC=%s,%s", name, zone_prefixes[i])) {
433                         talloc_free(tmp_ctx);
434                         return ISC_R_NOMEMORY;
435                 }
436
437                 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsZone");
438                 if (ret == LDB_SUCCESS) {
439                         talloc_free(tmp_ctx);
440                         return ISC_R_SUCCESS;
441                 }
442                 talloc_free(dn);
443         }
444
445         talloc_free(tmp_ctx);
446         return ISC_R_NOTFOUND;
447 }
448
449
450 /*
451   lookup one record
452  */
453 static isc_result_t dlz_lookup_types(struct dlz_bind9_data *state,
454                                      const char *zone, const char *name,
455                                      dns_sdlzlookup_t *lookup,
456                                      const char **types)
457 {
458         TALLOC_CTX *tmp_ctx = talloc_new(state);
459         const char *attrs[] = { "dnsRecord", NULL };
460         int ret, i;
461         struct ldb_result *res;
462         struct ldb_message_element *el;
463         struct ldb_dn *dn;
464
465         for (i=0; zone_prefixes[i]; i++) {
466                 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
467                 if (dn == NULL) {
468                         talloc_free(tmp_ctx);
469                         return ISC_R_NOMEMORY;
470                 }
471
472                 if (!ldb_dn_add_child_fmt(dn, "DC=%s,DC=%s,%s", name, zone, zone_prefixes[i])) {
473                         talloc_free(tmp_ctx);
474                         return ISC_R_NOMEMORY;
475                 }
476
477                 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
478                                  attrs, "objectClass=dnsNode");
479                 if (ret == LDB_SUCCESS) {
480                         break;
481                 }
482         }
483         if (ret != LDB_SUCCESS) {
484                 talloc_free(tmp_ctx);
485                 return ISC_R_NOTFOUND;
486         }
487
488         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
489         if (el == NULL || el->num_values == 0) {
490                 talloc_free(tmp_ctx);
491                 return ISC_R_NOTFOUND;
492         }
493
494         for (i=0; i<el->num_values; i++) {
495                 struct dnsp_DnssrvRpcRecord rec;
496                 enum ndr_err_code ndr_err;
497                 isc_result_t result;
498
499                 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec,
500                                                (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
501                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
502                         state->log(ISC_LOG_ERROR, "samba dlz_bind9: failed to parse dnsRecord for %s",
503                                    ldb_dn_get_linearized(dn));
504                         talloc_free(tmp_ctx);
505                         return ISC_R_FAILURE;
506                 }
507
508                 result = b9_putrr(state, lookup, &rec, types);
509                 if (result != ISC_R_SUCCESS) {
510                         talloc_free(tmp_ctx);
511                         return result;
512                 }
513         }
514
515         talloc_free(tmp_ctx);
516         return ISC_R_SUCCESS;
517 }
518
519 /*
520   lookup one record
521  */
522 _PUBLIC_ isc_result_t dlz_lookup(const char *zone, const char *name,
523                                  void *dbdata, dns_sdlzlookup_t *lookup)
524 {
525         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
526         return dlz_lookup_types(state, zone, name, lookup, NULL);
527 }
528
529
530 /*
531   see if a zone transfer is allowed
532  */
533 _PUBLIC_ isc_result_t dlz_allowzonexfr(void *dbdata, const char *name, const char *client)
534 {
535         /* just say yes for all our zones for now */
536         return dlz_findzonedb(dbdata, name);
537 }
538
539 /*
540   perform a zone transfer
541  */
542 _PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *dbdata,
543                                    dns_sdlzallnodes_t *allnodes)
544 {
545         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
546         const char *attrs[] = { "dnsRecord", NULL };
547         int ret, i, j;
548         struct ldb_dn *dn;
549         struct ldb_result *res;
550         TALLOC_CTX *tmp_ctx = talloc_new(state);
551
552         for (i=0; zone_prefixes[i]; i++) {
553                 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
554                 if (dn == NULL) {
555                         talloc_free(tmp_ctx);
556                         return ISC_R_NOMEMORY;
557                 }
558
559                 if (!ldb_dn_add_child_fmt(dn, "DC=%s,%s", zone, zone_prefixes[i])) {
560                         talloc_free(tmp_ctx);
561                         return ISC_R_NOMEMORY;
562                 }
563
564                 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
565                                  attrs, "objectClass=dnsNode");
566                 if (ret == LDB_SUCCESS) {
567                         break;
568                 }
569         }
570         if (ret != LDB_SUCCESS) {
571                 talloc_free(tmp_ctx);
572                 return ISC_R_NOTFOUND;
573         }
574
575         for (i=0; i<res->count; i++) {
576                 struct ldb_message_element *el;
577                 TALLOC_CTX *el_ctx = talloc_new(tmp_ctx);
578                 const char *rdn, *name;
579                 const struct ldb_val *v;
580
581                 el = ldb_msg_find_element(res->msgs[i], "dnsRecord");
582                 if (el == NULL || el->num_values == 0) {
583                         state->log(ISC_LOG_INFO, "failed to find dnsRecord for %s",
584                                    ldb_dn_get_linearized(dn));
585                         talloc_free(el_ctx);
586                         continue;
587                 }
588
589                 v = ldb_dn_get_rdn_val(res->msgs[i]->dn);
590                 if (v == NULL) {
591                         state->log(ISC_LOG_INFO, "failed to find RDN for %s",
592                                    ldb_dn_get_linearized(dn));
593                         talloc_free(el_ctx);
594                         continue;
595                 }
596
597                 rdn = talloc_strndup(el_ctx, (char *)v->data, v->length);
598                 if (rdn == NULL) {
599                         talloc_free(tmp_ctx);
600                         return ISC_R_NOMEMORY;
601                 }
602
603                 if (strcmp(rdn, "@") == 0) {
604                         name = zone;
605                 } else {
606                         name = talloc_asprintf(el_ctx, "%s.%s", rdn, zone);
607                 }
608                 if (name == NULL) {
609                         talloc_free(tmp_ctx);
610                         return ISC_R_NOMEMORY;
611                 }
612
613                 for (j=0; j<el->num_values; j++) {
614                         struct dnsp_DnssrvRpcRecord rec;
615                         enum ndr_err_code ndr_err;
616                         isc_result_t result;
617
618                         ndr_err = ndr_pull_struct_blob(&el->values[j], el_ctx, &rec,
619                                                        (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
620                         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
621                                 state->log(ISC_LOG_ERROR, "samba dlz_bind9: failed to parse dnsRecord for %s",
622                                            ldb_dn_get_linearized(dn));
623                                 talloc_free(el_ctx);
624                                 continue;
625                         }
626
627                         result = b9_putnamedrr(state, allnodes, name, &rec);
628                         if (result != ISC_R_SUCCESS) {
629                                 talloc_free(el_ctx);
630                                 continue;
631                         }
632                 }
633         }
634
635         talloc_free(tmp_ctx);
636
637         return ISC_R_SUCCESS;
638 }
639
640
641 /*
642   start a transaction
643  */
644 _PUBLIC_ isc_result_t dlz_newversion(const char *zone, void *dbdata, void **versionp)
645 {
646         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
647
648         state->log(ISC_LOG_INFO, "samba dlz_bind9: starting transaction on zone %s", zone);
649
650         if (state->transaction_started) {
651                 state->log(ISC_LOG_INFO, "samba dlz_bind9: transaction already started for zone %s", zone);
652                 return ISC_R_FAILURE;
653         }
654
655         state->transaction_started = true;
656
657         *versionp = (void *) &state->transaction_started;
658
659         return ISC_R_SUCCESS;
660 }
661
662 /*
663   end a transaction
664  */
665 _PUBLIC_ void dlz_closeversion(const char *zone, isc_boolean_t commit,
666                                void *dbdata, void **versionp)
667 {
668         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
669
670         if (!state->transaction_started) {
671                 state->log(ISC_LOG_INFO, "samba dlz_bind9: transaction not started for zone %s", zone);
672                 *versionp = NULL;
673                 return;
674         }
675
676         state->transaction_started = false;
677
678         *versionp = NULL;
679
680         if (commit) {
681                 state->log(ISC_LOG_INFO, "samba dlz_bind9: committing transaction on zone %s", zone);
682         } else {
683                 state->log(ISC_LOG_INFO, "samba dlz_bind9: cancelling transaction on zone %s", zone);
684         }
685 }
686
687
688 /*
689   see if there is a SOA record for a zone
690  */
691 static bool b9_has_soa(struct dlz_bind9_data *state, struct ldb_dn *dn, const char *zone)
692 {
693         const char *attrs[] = { "dnsRecord", NULL };
694         struct ldb_result *res;
695         struct ldb_message_element *el;
696         TALLOC_CTX *tmp_ctx = talloc_new(state);
697         int ret, i;
698
699         if (!ldb_dn_add_child_fmt(dn, "DC=@,DC=%s", zone)) {
700                 talloc_free(tmp_ctx);
701                 return false;
702         }
703
704         ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
705                          attrs, "objectClass=dnsNode");
706         if (ret != LDB_SUCCESS) {
707                 talloc_free(tmp_ctx);
708                 return false;
709         }
710
711         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
712         if (el == NULL) {
713                 talloc_free(tmp_ctx);
714                 return false;
715         }
716         for (i=0; i<el->num_values; i++) {
717                 struct dnsp_DnssrvRpcRecord rec;
718                 enum ndr_err_code ndr_err;
719
720                 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec,
721                                                (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
722                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
723                         continue;
724                 }
725                 if (rec.wType == DNS_TYPE_SOA) {
726                         talloc_free(tmp_ctx);
727                         return true;
728                 }
729         }
730
731         talloc_free(tmp_ctx);
732         return false;
733 }
734
735 /*
736   configure a writeable zone
737  */
738 _PUBLIC_ isc_result_t dlz_configure(dns_view_t *view, void *dbdata)
739 {
740         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
741         TALLOC_CTX *tmp_ctx;
742         struct ldb_dn *dn;
743         int i;
744
745         state->log(ISC_LOG_INFO, "samba dlz_bind9: starting configure");
746         if (state->writeable_zone == NULL) {
747                 state->log(ISC_LOG_INFO, "samba dlz_bind9: no writeable_zone method available");
748                 return ISC_R_FAILURE;
749         }
750
751         tmp_ctx = talloc_new(state);
752
753         for (i=0; zone_prefixes[i]; i++) {
754                 const char *attrs[] = { "name", NULL };
755                 int j, ret;
756                 struct ldb_result *res;
757
758                 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
759                 if (dn == NULL) {
760                         talloc_free(tmp_ctx);
761                         return ISC_R_NOMEMORY;
762                 }
763
764                 if (!ldb_dn_add_child_fmt(dn, "%s", zone_prefixes[i])) {
765                         talloc_free(tmp_ctx);
766                         return ISC_R_NOMEMORY;
767                 }
768
769                 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
770                                  attrs, "objectClass=dnsZone");
771                 if (ret != LDB_SUCCESS) {
772                         continue;
773                 }
774
775                 for (j=0; j<res->count; j++) {
776                         isc_result_t result;
777                         const char *zone = ldb_msg_find_attr_as_string(res->msgs[j], "name", NULL);
778                         if (zone == NULL) {
779                                 continue;
780                         }
781                         if (!b9_has_soa(state, dn, zone)) {
782                                 continue;
783                         }
784                         result = state->writeable_zone(view, zone);
785                         if (result != ISC_R_SUCCESS) {
786                                 state->log(ISC_LOG_ERROR, "samba dlz_bind9: Failed to configure zone '%s'",
787                                            zone);
788                                 talloc_free(tmp_ctx);
789                                 return result;
790                         }
791                         state->log(ISC_LOG_INFO, "samba dlz_bind9: configured writeable zone '%s'", zone);
792                 }
793         }
794
795         talloc_free(tmp_ctx);
796         return ISC_R_SUCCESS;
797 }
798
799 /*
800   authorize a zone update
801  */
802 _PUBLIC_ isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
803                                     const char *type, const char *key, uint32_t keydatalen, uint8_t *keydata,
804                                     void *dbdata)
805 {
806         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
807
808         state->log(ISC_LOG_INFO, "samba dlz_bind9: allowing update of signer=%s name=%s tcpaddr=%s type=%s key=%s keydatalen=%u",
809                    signer, name, tcpaddr, type, key, keydatalen);
810         return true;
811 }
812
813
814 _PUBLIC_ isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
815 {
816         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
817
818         if (version != (void *) &state->transaction_started) {
819                 return ISC_R_FAILURE;
820         }
821
822         state->log(ISC_LOG_INFO, "samba dlz_bind9: adding rdataset %s '%s'", name, rdatastr);
823
824         return ISC_R_SUCCESS;
825 }
826
827 _PUBLIC_ isc_result_t dlz_subrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
828 {
829         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
830
831         if (version != (void *) &state->transaction_started) {
832                 return ISC_R_FAILURE;
833         }
834
835         state->log(ISC_LOG_INFO, "samba dlz_bind9: subtracting rdataset %s '%s'", name, rdatastr);
836
837         return ISC_R_SUCCESS;
838 }
839
840
841 _PUBLIC_ isc_result_t dlz_delrdataset(const char *name, void *dbdata, void *version)
842 {
843         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
844
845         if (version != (void *) &state->transaction_started) {
846                 return ISC_R_FAILURE;
847         }
848
849         state->log(ISC_LOG_INFO, "samba dlz_bind9: deleting rdataset %s", name);
850
851         return ISC_R_SUCCESS;
852 }