selftest/Samba4: use "server signing = on" for now
[Samba/gebeck_regimport.git] / source4 / dns_server / dlz_bind9.c
blobe37a66e2d3490aeea8feb942721b8d37fd7408f3
1 /*
2 Unix SMB/CIFS implementation.
4 bind9 dlz driver for Samba
6 Copyright (C) 2010 Andrew Tridgell
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.
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.
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/>.
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"
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;
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);
53 static const char *zone_prefixes[] = {
54 "CN=MicrosoftDNS,DC=DomainDnsZones",
55 "CN=MicrosoftDNS,DC=ForestDnsZones",
56 "CN=MicrosoftDNS,CN=System",
57 NULL
61 return the version of the API
63 _PUBLIC_ int dlz_version(unsigned int *flags)
65 return DLZ_DLOPEN_VERSION;
69 remember a helper function from the bind9 dlz_dlopen driver
71 static void b9_add_helper(struct dlz_bind9_data *state, const char *helper_name, void *ptr)
73 if (strcmp(helper_name, "log") == 0) {
74 state->log = ptr;
76 if (strcmp(helper_name, "putrr") == 0) {
77 state->putrr = ptr;
79 if (strcmp(helper_name, "putnamedrr") == 0) {
80 state->putnamedrr = ptr;
82 if (strcmp(helper_name, "writeable_zone") == 0) {
83 state->writeable_zone = ptr;
88 format a record for bind9
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)
95 switch (rec->wType) {
96 case DNS_TYPE_A:
97 *type = "a";
98 *data = rec->data.ipv4;
99 break;
101 case DNS_TYPE_AAAA:
102 *type = "aaaa";
103 *data = rec->data.ipv6;
104 break;
106 case DNS_TYPE_CNAME:
107 *type = "cname";
108 *data = rec->data.cname;
109 break;
111 case DNS_TYPE_TXT:
112 *type = "txt";
113 *data = rec->data.txt;
114 break;
116 case DNS_TYPE_PTR:
117 *type = "ptr";
118 *data = rec->data.ptr;
119 break;
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;
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;
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;
144 case DNS_TYPE_NS:
145 *type = "ns";
146 *data = rec->data.ns;
147 break;
149 case DNS_TYPE_SOA: {
150 const char *mname;
151 *type = "soa";
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
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;
162 mname = strlower_talloc(mem_ctx, mname);
163 if (mname == NULL) {
164 return false;
167 state->soa_serial = rec->data.soa.serial;
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;
180 default:
181 state->log(ISC_LOG_ERROR, "samba b9_putrr: unhandled record type %u",
182 rec->wType);
183 return false;
186 return true;
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},
208 see if a DNS type is single valued
210 static bool b9_single_valued(enum dns_record_type dns_type)
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;
218 return false;
222 see if a DNS type is single valued
224 static bool b9_dns_type(const char *type, enum dns_record_type *dtype)
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;
233 return false;
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)
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)
249 parse a record from bind9
251 static bool b9_parse(struct dlz_bind9_data *state,
252 const char *rdatastr,
253 struct dnsp_DnssrvRpcRecord *rec)
255 char *full_name, *dclass, *type;
256 char *str, *saveptr=NULL;
257 int i;
259 str = talloc_strdup(rec, rdatastr);
260 if (str == NULL) {
261 return false;
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);
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;
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;
283 switch (rec->wType) {
284 case DNS_TYPE_A:
285 DNS_PARSE_STR(rec->data.ipv4, NULL, " ", saveptr);
286 break;
288 case DNS_TYPE_AAAA:
289 DNS_PARSE_STR(rec->data.ipv6, NULL, " ", saveptr);
290 break;
292 case DNS_TYPE_CNAME:
293 DNS_PARSE_STR(rec->data.cname, NULL, " ", saveptr);
294 break;
296 case DNS_TYPE_TXT:
297 DNS_PARSE_STR(rec->data.txt, NULL, "\t", saveptr);
298 break;
300 case DNS_TYPE_PTR:
301 DNS_PARSE_STR(rec->data.ptr, NULL, " ", saveptr);
302 break;
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;
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;
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;
321 case DNS_TYPE_NS:
322 DNS_PARSE_STR(rec->data.ns, NULL, " ", saveptr);
323 break;
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;
335 default:
336 state->log(ISC_LOG_ERROR, "samba b9_parse: unhandled record type %u",
337 rec->wType);
338 return false;
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;
347 return true;
351 send a resource recond to bind9
353 static isc_result_t b9_putrr(struct dlz_bind9_data *state,
354 void *handle, struct dnsp_DnssrvRpcRecord *rec,
355 const char **types)
357 isc_result_t result;
358 const char *type, *data;
359 TALLOC_CTX *tmp_ctx = talloc_new(state);
361 if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
362 return ISC_R_FAILURE;
365 if (data == NULL) {
366 talloc_free(tmp_ctx);
367 return ISC_R_NOMEMORY;
370 if (types) {
371 int i;
372 for (i=0; types[i]; i++) {
373 if (strcmp(types[i], type) == 0) break;
375 if (types[i] == NULL) {
376 /* skip it */
377 return ISC_R_SUCCESS;
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");
385 talloc_free(tmp_ctx);
386 return result;
391 send a named resource recond to bind9
393 static isc_result_t b9_putnamedrr(struct dlz_bind9_data *state,
394 void *handle, const char *name,
395 struct dnsp_DnssrvRpcRecord *rec)
397 isc_result_t result;
398 const char *type, *data;
399 TALLOC_CTX *tmp_ctx = talloc_new(state);
401 if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
402 return ISC_R_FAILURE;
405 if (data == NULL) {
406 talloc_free(tmp_ctx);
407 return ISC_R_NOMEMORY;
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);
414 talloc_free(tmp_ctx);
415 return result;
418 struct b9_options {
419 const char *url;
423 parse options
425 static isc_result_t parse_options(struct dlz_bind9_data *state,
426 unsigned int argc, char *argv[],
427 struct b9_options *options)
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 }
435 struct poptOption **popt_options;
436 int ret;
438 fault_setup_disable();
440 popt_options = ldb_module_popt_options(state->samdb);
441 (*popt_options) = long_options;
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;
449 pc = poptGetContext("dlz_bind9", argc, (const char **)argv, *popt_options,
450 POPT_CONTEXT_KEEP_FIRST);
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;
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;
467 return ISC_R_SUCCESS;
472 called to initialise the driver
474 _PUBLIC_ isc_result_t dlz_create(const char *dlzname,
475 unsigned int argc, char *argv[],
476 void **dbdata, ...)
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;
487 ZERO_STRUCT(options);
489 state = talloc_zero(NULL, struct dlz_bind9_data);
490 if (state == NULL) {
491 return ISC_R_NOMEMORY;
494 tmp_ctx = talloc_new(state);
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*));
501 va_end(ap);
503 state->ev_ctx = s4_event_context_init(state);
504 if (state->ev_ctx == NULL) {
505 result = ISC_R_NOMEMORY;
506 goto failed;
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;
516 result = parse_options(state, argc, argv, &options);
517 if (result != ISC_R_SUCCESS) {
518 goto failed;
521 state->lp = loadparm_init_global(true);
522 if (state->lp == NULL) {
523 result = ISC_R_NOMEMORY;
524 goto failed;
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;
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;
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;
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;
560 state->log(ISC_LOG_INFO, "samba_dlz: started for DN %s",
561 ldb_dn_get_linearized(dn));
563 *dbdata = state;
565 talloc_free(tmp_ctx);
566 return ISC_R_SUCCESS;
568 failed:
569 talloc_free(state);
570 return result;
574 shutdown the backend
576 _PUBLIC_ void dlz_destroy(void *dbdata)
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);
585 return the base DN for a zone
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)
590 int ret;
591 TALLOC_CTX *tmp_ctx = talloc_new(state);
592 const char *attrs[] = { NULL };
593 int i;
595 for (i=0; zone_prefixes[i]; i++) {
596 struct ldb_dn *dn;
597 struct ldb_result *res;
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;
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;
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);
615 talloc_free(tmp_ctx);
616 return ISC_R_SUCCESS;
618 talloc_free(dn);
621 talloc_free(tmp_ctx);
622 return ISC_R_NOTFOUND;
627 return the DN for a name. The record does not need to exist, but the
628 zone must exist
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)
633 const char *p;
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
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);
649 if (!ret) {
650 talloc_free(*dn);
651 return ISC_R_NOMEMORY;
653 return ISC_R_SUCCESS;
655 p = strchr(p, '.');
656 if (p == NULL) {
657 break;
659 p++;
661 return ISC_R_NOTFOUND;
666 see if we handle a given zone
668 _PUBLIC_ isc_result_t dlz_findzonedb(void *dbdata, const char *name)
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);
676 lookup one record
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)
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;
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;
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;
702 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
703 attrs, "objectClass=dnsNode");
704 if (ret == LDB_SUCCESS) {
705 break;
708 if (ret != LDB_SUCCESS) {
709 talloc_free(tmp_ctx);
710 return ISC_R_NOTFOUND;
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;
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;
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;
733 result = b9_putrr(state, lookup, &rec, types);
734 if (result != ISC_R_SUCCESS) {
735 talloc_free(tmp_ctx);
736 return result;
740 talloc_free(tmp_ctx);
741 return ISC_R_SUCCESS;
745 lookup one record
747 _PUBLIC_ isc_result_t dlz_lookup(const char *zone, const char *name,
748 void *dbdata, dns_sdlzlookup_t *lookup)
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);
756 see if a zone transfer is allowed
758 _PUBLIC_ isc_result_t dlz_allowzonexfr(void *dbdata, const char *name, const char *client)
760 /* just say yes for all our zones for now */
761 return dlz_findzonedb(dbdata, name);
765 perform a zone transfer
767 _PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *dbdata,
768 dns_sdlzallnodes_t *allnodes)
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);
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;
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;
789 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
790 attrs, "objectClass=dnsNode");
791 if (ret == LDB_SUCCESS) {
792 break;
795 if (ret != LDB_SUCCESS) {
796 talloc_free(tmp_ctx);
797 return ISC_R_NOTFOUND;
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;
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;
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;
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;
828 if (strcmp(rdn, "@") == 0) {
829 name = zone;
830 } else {
831 name = talloc_asprintf(el_ctx, "%s.%s", rdn, zone);
833 if (name == NULL) {
834 talloc_free(tmp_ctx);
835 return ISC_R_NOMEMORY;
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;
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;
851 result = b9_putnamedrr(state, allnodes, name, &rec);
852 if (result != ISC_R_SUCCESS) {
853 continue;
858 talloc_free(tmp_ctx);
860 return ISC_R_SUCCESS;
865 start a transaction
867 _PUBLIC_ isc_result_t dlz_newversion(const char *zone, void *dbdata, void **versionp)
869 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
871 state->log(ISC_LOG_INFO, "samba_dlz: starting transaction on zone %s", zone);
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;
878 state->transaction_token = talloc_zero(state, int);
879 if (state->transaction_token == NULL) {
880 return ISC_R_NOMEMORY;
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;
890 *versionp = (void *)state->transaction_token;
892 return ISC_R_SUCCESS;
896 end a transaction
898 _PUBLIC_ void dlz_closeversion(const char *zone, isc_boolean_t commit,
899 void *dbdata, void **versionp)
901 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
903 if (state->transaction_token != (int *)*versionp) {
904 state->log(ISC_LOG_INFO, "samba_dlz: transaction not started for zone %s", zone);
905 return;
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;
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;
919 state->log(ISC_LOG_INFO, "samba_dlz: cancelling transaction on zone %s", zone);
922 talloc_free(state->transaction_token);
923 state->transaction_token = NULL;
924 *versionp = NULL;
929 see if there is a SOA record for a zone
931 static bool b9_has_soa(struct dlz_bind9_data *state, struct ldb_dn *dn, const char *zone)
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;
939 if (!ldb_dn_add_child_fmt(dn, "DC=@,DC=%s", zone)) {
940 talloc_free(tmp_ctx);
941 return false;
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;
951 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
952 if (el == NULL) {
953 talloc_free(tmp_ctx);
954 return false;
956 for (i=0; i<el->num_values; i++) {
957 struct dnsp_DnssrvRpcRecord rec;
958 enum ndr_err_code ndr_err;
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;
965 if (rec.wType == DNS_TYPE_SOA) {
966 talloc_free(tmp_ctx);
967 return true;
971 talloc_free(tmp_ctx);
972 return false;
976 configure a writeable zone
978 _PUBLIC_ isc_result_t dlz_configure(dns_view_t *view, void *dbdata)
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;
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;
991 tmp_ctx = talloc_new(state);
993 for (i=0; zone_prefixes[i]; i++) {
994 const char *attrs[] = { "name", NULL };
995 int j, ret;
996 struct ldb_result *res;
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;
1004 if (!ldb_dn_add_child_fmt(dn, "%s", zone_prefixes[i])) {
1005 talloc_free(tmp_ctx);
1006 return ISC_R_NOMEMORY;
1009 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
1010 attrs, "objectClass=dnsZone");
1011 if (ret != LDB_SUCCESS) {
1012 continue;
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;
1020 if (zone == NULL) {
1021 continue;
1023 zone_dn = ldb_dn_copy(tmp_ctx, dn);
1024 if (zone_dn == NULL) {
1025 talloc_free(tmp_ctx);
1026 return ISC_R_NOMEMORY;
1029 if (!b9_has_soa(state, zone_dn, zone)) {
1030 continue;
1032 result = state->writeable_zone(view, zone);
1033 if (result != ISC_R_SUCCESS) {
1034 state->log(ISC_LOG_ERROR, "samba_dlz: Failed to configure zone '%s'",
1035 zone);
1036 talloc_free(tmp_ctx);
1037 return result;
1039 state->log(ISC_LOG_INFO, "samba_dlz: configured writeable zone '%s'", zone);
1043 talloc_free(tmp_ctx);
1044 return ISC_R_SUCCESS;
1048 authorize a zone update
1050 _PUBLIC_ isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
1051 const char *type, const char *key, uint32_t keydatalen, uint8_t *keydata,
1052 void *dbdata)
1054 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1056 state->log(ISC_LOG_INFO, "samba_dlz: allowing update of signer=%s name=%s tcpaddr=%s type=%s key=%s keydatalen=%u",
1057 signer, name, tcpaddr, type, key, keydatalen);
1058 return true;
1063 add a new record
1065 static isc_result_t b9_add_record(struct dlz_bind9_data *state, const char *name,
1066 struct ldb_dn *dn,
1067 struct dnsp_DnssrvRpcRecord *rec)
1069 struct ldb_message *msg;
1070 enum ndr_err_code ndr_err;
1071 struct ldb_val v;
1072 int ret;
1074 msg = ldb_msg_new(rec);
1075 if (msg == NULL) {
1076 return ISC_R_NOMEMORY;
1078 msg->dn = dn;
1079 ret = ldb_msg_add_string(msg, "objectClass", "dnsNode");
1080 if (ret != LDB_SUCCESS) {
1081 return ISC_R_FAILURE;
1084 ndr_err = ndr_push_struct_blob(&v, rec, rec, (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
1085 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1086 return ISC_R_FAILURE;
1088 ret = ldb_msg_add_value(msg, "dnsRecord", &v, NULL);
1089 if (ret != LDB_SUCCESS) {
1090 return ISC_R_FAILURE;
1093 ret = ldb_add(state->samdb, msg);
1094 if (ret != LDB_SUCCESS) {
1095 return ISC_R_FAILURE;
1098 return ISC_R_SUCCESS;
1102 see if two DNS names are the same
1104 static bool dns_name_equal(const char *name1, const char *name2)
1106 size_t len1 = strlen(name1);
1107 size_t len2 = strlen(name2);
1108 if (name1[len1-1] == '.') len1--;
1109 if (name2[len2-1] == '.') len2--;
1110 if (len1 != len2) {
1111 return false;
1113 return strncasecmp_m(name1, name2, len1) == 0;
1118 see if two dns records match
1120 static bool b9_record_match(struct dlz_bind9_data *state,
1121 struct dnsp_DnssrvRpcRecord *rec1, struct dnsp_DnssrvRpcRecord *rec2)
1123 if (rec1->wType != rec2->wType) {
1124 return false;
1126 /* see if this type is single valued */
1127 if (b9_single_valued(rec1->wType)) {
1128 return true;
1131 /* see if the data matches */
1132 switch (rec1->wType) {
1133 case DNS_TYPE_A:
1134 return strcmp(rec1->data.ipv4, rec2->data.ipv4) == 0;
1135 case DNS_TYPE_AAAA:
1136 return strcmp(rec1->data.ipv6, rec2->data.ipv6) == 0;
1137 case DNS_TYPE_CNAME:
1138 return dns_name_equal(rec1->data.cname, rec2->data.cname);
1139 case DNS_TYPE_TXT:
1140 return strcmp(rec1->data.txt, rec2->data.txt) == 0;
1141 case DNS_TYPE_PTR:
1142 return strcmp(rec1->data.ptr, rec2->data.ptr) == 0;
1143 case DNS_TYPE_NS:
1144 return dns_name_equal(rec1->data.ns, rec2->data.ns);
1146 case DNS_TYPE_SRV:
1147 return rec1->data.srv.wPriority == rec2->data.srv.wPriority &&
1148 rec1->data.srv.wWeight == rec2->data.srv.wWeight &&
1149 rec1->data.srv.wPort == rec2->data.srv.wPort &&
1150 dns_name_equal(rec1->data.srv.nameTarget, rec2->data.srv.nameTarget);
1152 case DNS_TYPE_MX:
1153 return rec1->data.mx.wPriority == rec2->data.mx.wPriority &&
1154 dns_name_equal(rec1->data.mx.nameTarget, rec2->data.mx.nameTarget);
1156 case DNS_TYPE_HINFO:
1157 return strcmp(rec1->data.hinfo.cpu, rec2->data.hinfo.cpu) == 0 &&
1158 strcmp(rec1->data.hinfo.os, rec2->data.hinfo.os) == 0;
1160 case DNS_TYPE_SOA:
1161 return dns_name_equal(rec1->data.soa.mname, rec2->data.soa.mname) &&
1162 dns_name_equal(rec1->data.soa.rname, rec2->data.soa.rname) &&
1163 rec1->data.soa.serial == rec2->data.soa.serial &&
1164 rec1->data.soa.refresh == rec2->data.soa.refresh &&
1165 rec1->data.soa.retry == rec2->data.soa.retry &&
1166 rec1->data.soa.expire == rec2->data.soa.expire &&
1167 rec1->data.soa.minimum == rec2->data.soa.minimum;
1168 default:
1169 state->log(ISC_LOG_ERROR, "samba b9_putrr: unhandled record type %u",
1170 rec1->wType);
1171 break;
1174 return false;
1179 add or modify a rdataset
1181 _PUBLIC_ isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
1183 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1184 struct dnsp_DnssrvRpcRecord *rec;
1185 struct ldb_dn *dn;
1186 isc_result_t result;
1187 struct ldb_result *res;
1188 const char *attrs[] = { "dnsRecord", NULL };
1189 int ret, i;
1190 struct ldb_message_element *el;
1191 enum ndr_err_code ndr_err;
1192 NTTIME t;
1194 if (state->transaction_token != (void*)version) {
1195 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1196 return ISC_R_FAILURE;
1199 rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
1200 if (rec == NULL) {
1201 return ISC_R_NOMEMORY;
1204 unix_to_nt_time(&t, time(NULL));
1205 t /= 10*1000*1000; /* convert to seconds (NT time is in 100ns units) */
1206 t /= 3600; /* convert to hours */
1208 rec->rank = DNS_RANK_ZONE;
1209 rec->dwSerial = state->soa_serial;
1210 rec->dwTimeStamp = (uint32_t)t;
1212 if (!b9_parse(state, rdatastr, rec)) {
1213 state->log(ISC_LOG_INFO, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
1214 talloc_free(rec);
1215 return ISC_R_FAILURE;
1218 /* find the DN of the record */
1219 result = b9_find_name_dn(state, name, rec, &dn);
1220 if (result != ISC_R_SUCCESS) {
1221 talloc_free(rec);
1222 return result;
1225 /* get any existing records */
1226 ret = ldb_search(state->samdb, rec, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1227 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1228 result = b9_add_record(state, name, dn, rec);
1229 talloc_free(rec);
1230 if (result == ISC_R_SUCCESS) {
1231 state->log(ISC_LOG_ERROR, "samba_dlz: added %s %s", name, rdatastr);
1233 return result;
1236 /* there are existing records. We need to see if this will
1237 * replace a record or add to it
1239 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1240 if (el == NULL) {
1241 state->log(ISC_LOG_ERROR, "samba_dlz: no dnsRecord attribute for %s",
1242 ldb_dn_get_linearized(dn));
1243 talloc_free(rec);
1244 return ISC_R_FAILURE;
1247 for (i=0; i<el->num_values; i++) {
1248 struct dnsp_DnssrvRpcRecord rec2;
1250 ndr_err = ndr_pull_struct_blob(&el->values[i], rec, &rec2,
1251 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1252 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1253 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1254 ldb_dn_get_linearized(dn));
1255 talloc_free(rec);
1256 return ISC_R_FAILURE;
1259 if (b9_record_match(state, rec, &rec2)) {
1260 break;
1263 if (i == el->num_values) {
1264 /* adding a new value */
1265 el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values+1);
1266 if (el->values == NULL) {
1267 talloc_free(rec);
1268 return ISC_R_NOMEMORY;
1270 el->num_values++;
1273 ndr_err = ndr_push_struct_blob(&el->values[i], rec, rec,
1274 (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
1275 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1276 state->log(ISC_LOG_ERROR, "samba_dlz: failed to push dnsRecord for %s",
1277 ldb_dn_get_linearized(dn));
1278 talloc_free(rec);
1279 return ISC_R_FAILURE;
1282 /* modify the record */
1283 el->flags = LDB_FLAG_MOD_REPLACE;
1284 ret = ldb_modify(state->samdb, res->msgs[0]);
1285 if (ret != LDB_SUCCESS) {
1286 state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
1287 ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1288 talloc_free(rec);
1289 return ISC_R_FAILURE;
1292 state->log(ISC_LOG_INFO, "samba_dlz: added rdataset %s '%s'", name, rdatastr);
1294 talloc_free(rec);
1295 return ISC_R_SUCCESS;
1299 remove a rdataset
1301 _PUBLIC_ isc_result_t dlz_subrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
1303 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1304 struct dnsp_DnssrvRpcRecord *rec;
1305 struct ldb_dn *dn;
1306 isc_result_t result;
1307 struct ldb_result *res;
1308 const char *attrs[] = { "dnsRecord", NULL };
1309 int ret, i;
1310 struct ldb_message_element *el;
1311 enum ndr_err_code ndr_err;
1313 if (state->transaction_token != (void*)version) {
1314 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1315 return ISC_R_FAILURE;
1318 rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
1319 if (rec == NULL) {
1320 return ISC_R_NOMEMORY;
1323 if (!b9_parse(state, rdatastr, rec)) {
1324 state->log(ISC_LOG_INFO, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
1325 talloc_free(rec);
1326 return ISC_R_FAILURE;
1329 /* find the DN of the record */
1330 result = b9_find_name_dn(state, name, rec, &dn);
1331 if (result != ISC_R_SUCCESS) {
1332 talloc_free(rec);
1333 return result;
1336 /* get the existing records */
1337 ret = ldb_search(state->samdb, rec, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1338 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1339 talloc_free(rec);
1340 return ISC_R_NOTFOUND;
1343 /* there are existing records. We need to see if any match
1345 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1346 if (el == NULL || el->num_values == 0) {
1347 state->log(ISC_LOG_ERROR, "samba_dlz: no dnsRecord attribute for %s",
1348 ldb_dn_get_linearized(dn));
1349 talloc_free(rec);
1350 return ISC_R_FAILURE;
1353 for (i=0; i<el->num_values; i++) {
1354 struct dnsp_DnssrvRpcRecord rec2;
1356 ndr_err = ndr_pull_struct_blob(&el->values[i], rec, &rec2,
1357 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1358 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1359 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1360 ldb_dn_get_linearized(dn));
1361 talloc_free(rec);
1362 return ISC_R_FAILURE;
1365 if (b9_record_match(state, rec, &rec2)) {
1366 break;
1369 if (i == el->num_values) {
1370 talloc_free(rec);
1371 return ISC_R_NOTFOUND;
1374 if (i < el->num_values-1) {
1375 memmove(&el->values[i], &el->values[i+1], sizeof(el->values[0])*((el->num_values-1)-i));
1377 el->num_values--;
1379 if (el->num_values == 0) {
1380 /* delete the record */
1381 ret = ldb_delete(state->samdb, dn);
1382 } else {
1383 /* modify the record */
1384 el->flags = LDB_FLAG_MOD_REPLACE;
1385 ret = ldb_modify(state->samdb, res->msgs[0]);
1387 if (ret != LDB_SUCCESS) {
1388 state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
1389 ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1390 talloc_free(rec);
1391 return ISC_R_FAILURE;
1394 state->log(ISC_LOG_INFO, "samba_dlz: subtracted rdataset %s '%s'", name, rdatastr);
1396 talloc_free(rec);
1397 return ISC_R_SUCCESS;
1402 delete all records of the given type
1404 _PUBLIC_ isc_result_t dlz_delrdataset(const char *name, const char *type, void *dbdata, void *version)
1406 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1407 TALLOC_CTX *tmp_ctx;
1408 struct ldb_dn *dn;
1409 isc_result_t result;
1410 struct ldb_result *res;
1411 const char *attrs[] = { "dnsRecord", NULL };
1412 int ret, i;
1413 struct ldb_message_element *el;
1414 enum ndr_err_code ndr_err;
1415 enum dns_record_type dns_type;
1416 bool found = false;
1418 if (state->transaction_token != (void*)version) {
1419 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1420 return ISC_R_FAILURE;
1423 if (!b9_dns_type(type, &dns_type)) {
1424 state->log(ISC_LOG_INFO, "samba_dlz: bad dns type %s in delete", type);
1425 return ISC_R_FAILURE;
1428 tmp_ctx = talloc_new(state);
1430 /* find the DN of the record */
1431 result = b9_find_name_dn(state, name, tmp_ctx, &dn);
1432 if (result != ISC_R_SUCCESS) {
1433 talloc_free(tmp_ctx);
1434 return result;
1437 /* get the existing records */
1438 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1439 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1440 talloc_free(tmp_ctx);
1441 return ISC_R_NOTFOUND;
1444 /* there are existing records. We need to see if any match the type
1446 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1447 if (el == NULL || el->num_values == 0) {
1448 talloc_free(tmp_ctx);
1449 return ISC_R_NOTFOUND;
1452 for (i=0; i<el->num_values; i++) {
1453 struct dnsp_DnssrvRpcRecord rec2;
1455 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec2,
1456 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1457 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1458 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1459 ldb_dn_get_linearized(dn));
1460 talloc_free(tmp_ctx);
1461 return ISC_R_FAILURE;
1464 if (dns_type == rec2.wType) {
1465 if (i < el->num_values-1) {
1466 memmove(&el->values[i], &el->values[i+1],
1467 sizeof(el->values[0])*((el->num_values-1)-i));
1469 el->num_values--;
1470 i--;
1471 found = true;
1475 if (!found) {
1476 talloc_free(tmp_ctx);
1477 return ISC_R_FAILURE;
1480 if (el->num_values == 0) {
1481 /* delete the record */
1482 ret = ldb_delete(state->samdb, dn);
1483 } else {
1484 /* modify the record */
1485 el->flags = LDB_FLAG_MOD_REPLACE;
1486 ret = ldb_modify(state->samdb, res->msgs[0]);
1488 if (ret != LDB_SUCCESS) {
1489 state->log(ISC_LOG_ERROR, "samba_dlz: failed to delete type %s in %s - %s",
1490 type, ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1491 talloc_free(tmp_ctx);
1492 return ISC_R_FAILURE;
1495 state->log(ISC_LOG_INFO, "samba_dlz: deleted rdataset %s of type %s", name, type);
1497 talloc_free(tmp_ctx);
1498 return ISC_R_SUCCESS;