4105c1119e048233d18b1a2c862a49feda963d29
[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                         if (zone == NULL) {
1019                                 continue;
1020                         }
1021                         if (!b9_has_soa(state, dn, zone)) {
1022                                 continue;
1023                         }
1024                         result = state->writeable_zone(view, zone);
1025                         if (result != ISC_R_SUCCESS) {
1026                                 state->log(ISC_LOG_ERROR, "samba_dlz: Failed to configure zone '%s'",
1027                                            zone);
1028                                 talloc_free(tmp_ctx);
1029                                 return result;
1030                         }
1031                         state->log(ISC_LOG_INFO, "samba_dlz: configured writeable zone '%s'", zone);
1032                 }
1033         }
1034
1035         talloc_free(tmp_ctx);
1036         return ISC_R_SUCCESS;
1037 }
1038
1039 /*
1040   authorize a zone update
1041  */
1042 _PUBLIC_ isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
1043                                     const char *type, const char *key, uint32_t keydatalen, uint8_t *keydata,
1044                                     void *dbdata)
1045 {
1046         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1047
1048         state->log(ISC_LOG_INFO, "samba_dlz: allowing update of signer=%s name=%s tcpaddr=%s type=%s key=%s keydatalen=%u",
1049                    signer, name, tcpaddr, type, key, keydatalen);
1050         return true;
1051 }
1052
1053
1054 /*
1055   add a new record
1056  */
1057 static isc_result_t b9_add_record(struct dlz_bind9_data *state, const char *name,
1058                                   struct ldb_dn *dn,
1059                                   struct dnsp_DnssrvRpcRecord *rec)
1060 {
1061         struct ldb_message *msg;
1062         enum ndr_err_code ndr_err;
1063         struct ldb_val v;
1064         int ret;
1065
1066         msg = ldb_msg_new(rec);
1067         if (msg == NULL) {
1068                 return ISC_R_NOMEMORY;
1069         }
1070         msg->dn = dn;
1071         ret = ldb_msg_add_string(msg, "objectClass", "dnsNode");
1072         if (ret != LDB_SUCCESS) {
1073                 return ISC_R_FAILURE;
1074         }
1075
1076         ndr_err = ndr_push_struct_blob(&v, rec, rec, (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
1077         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1078                 return ISC_R_FAILURE;
1079         }
1080         ret = ldb_msg_add_value(msg, "dnsRecord", &v, NULL);
1081         if (ret != LDB_SUCCESS) {
1082                 return ISC_R_FAILURE;
1083         }
1084
1085         ret = ldb_add(state->samdb, msg);
1086         if (ret != LDB_SUCCESS) {
1087                 return ISC_R_FAILURE;
1088         }
1089
1090         return ISC_R_SUCCESS;
1091 }
1092
1093 /*
1094   see if two DNS names are the same
1095  */
1096 static bool dns_name_equal(const char *name1, const char *name2)
1097 {
1098         size_t len1 = strlen(name1);
1099         size_t len2 = strlen(name2);
1100         if (name1[len1-1] == '.') len1--;
1101         if (name2[len2-1] == '.') len2--;
1102         if (len1 != len2) {
1103                 return false;
1104         }
1105         return strncasecmp_m(name1, name2, len1) == 0;
1106 }
1107
1108
1109 /*
1110   see if two dns records match
1111  */
1112 static bool b9_record_match(struct dlz_bind9_data *state,
1113                             struct dnsp_DnssrvRpcRecord *rec1, struct dnsp_DnssrvRpcRecord *rec2)
1114 {
1115         if (rec1->wType != rec2->wType) {
1116                 return false;
1117         }
1118         /* see if this type is single valued */
1119         if (b9_single_valued(rec1->wType)) {
1120                 return true;
1121         }
1122
1123         /* see if the data matches */
1124         switch (rec1->wType) {
1125         case DNS_TYPE_A:
1126                 return strcmp(rec1->data.ipv4, rec2->data.ipv4) == 0;
1127         case DNS_TYPE_AAAA:
1128                 return strcmp(rec1->data.ipv6, rec2->data.ipv6) == 0;
1129         case DNS_TYPE_CNAME:
1130                 return dns_name_equal(rec1->data.cname, rec2->data.cname);
1131         case DNS_TYPE_TXT:
1132                 return strcmp(rec1->data.txt, rec2->data.txt) == 0;
1133         case DNS_TYPE_PTR:
1134                 return strcmp(rec1->data.ptr, rec2->data.ptr) == 0;
1135         case DNS_TYPE_NS:
1136                 return dns_name_equal(rec1->data.ns, rec2->data.ns);
1137
1138         case DNS_TYPE_SRV:
1139                 return rec1->data.srv.wPriority == rec2->data.srv.wPriority &&
1140                         rec1->data.srv.wWeight  == rec2->data.srv.wWeight &&
1141                         rec1->data.srv.wPort    == rec2->data.srv.wPort &&
1142                         dns_name_equal(rec1->data.srv.nameTarget, rec2->data.srv.nameTarget);
1143
1144         case DNS_TYPE_MX:
1145                 return rec1->data.mx.wPriority == rec2->data.mx.wPriority &&
1146                         dns_name_equal(rec1->data.mx.nameTarget, rec2->data.mx.nameTarget);
1147
1148         case DNS_TYPE_HINFO:
1149                 return strcmp(rec1->data.hinfo.cpu, rec2->data.hinfo.cpu) == 0 &&
1150                         strcmp(rec1->data.hinfo.os, rec2->data.hinfo.os) == 0;
1151
1152         case DNS_TYPE_SOA:
1153                 return dns_name_equal(rec1->data.soa.mname, rec2->data.soa.mname) &&
1154                         dns_name_equal(rec1->data.soa.rname, rec2->data.soa.rname) &&
1155                         rec1->data.soa.serial == rec2->data.soa.serial &&
1156                         rec1->data.soa.refresh == rec2->data.soa.refresh &&
1157                         rec1->data.soa.retry == rec2->data.soa.retry &&
1158                         rec1->data.soa.expire == rec2->data.soa.expire &&
1159                         rec1->data.soa.minimum == rec2->data.soa.minimum;
1160         default:
1161                 state->log(ISC_LOG_ERROR, "samba b9_putrr: unhandled record type %u",
1162                            rec1->wType);
1163                 break;
1164         }
1165
1166         return false;
1167 }
1168
1169
1170 /*
1171   add or modify a rdataset
1172  */
1173 _PUBLIC_ isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
1174 {
1175         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1176         struct dnsp_DnssrvRpcRecord *rec;
1177         struct ldb_dn *dn;
1178         isc_result_t result;
1179         struct ldb_result *res;
1180         const char *attrs[] = { "dnsRecord", NULL };
1181         int ret, i;
1182         struct ldb_message_element *el;
1183         enum ndr_err_code ndr_err;
1184         NTTIME t;
1185
1186         if (state->transaction_token != (void*)version) {
1187                 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1188                 return ISC_R_FAILURE;
1189         }
1190
1191         rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
1192         if (rec == NULL) {
1193                 return ISC_R_NOMEMORY;
1194         }
1195
1196         unix_to_nt_time(&t, time(NULL));
1197         t /= 10*1000*1000; /* convert to seconds (NT time is in 100ns units) */
1198         t /= 3600;         /* convert to hours */
1199
1200         rec->rank        = DNS_RANK_ZONE;
1201         rec->dwSerial    = state->soa_serial;
1202         rec->dwTimeStamp = (uint32_t)t;
1203
1204         if (!b9_parse(state, rdatastr, rec)) {
1205                 state->log(ISC_LOG_INFO, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
1206                 talloc_free(rec);
1207                 return ISC_R_FAILURE;
1208         }
1209
1210         /* find the DN of the record */
1211         result = b9_find_name_dn(state, name, rec, &dn);
1212         if (result != ISC_R_SUCCESS) {
1213                 talloc_free(rec);
1214                 return result;
1215         }
1216
1217         /* get any existing records */
1218         ret = ldb_search(state->samdb, rec, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1219         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1220                 result = b9_add_record(state, name, dn, rec);
1221                 talloc_free(rec);
1222                 if (result == ISC_R_SUCCESS) {
1223                         state->log(ISC_LOG_ERROR, "samba_dlz: added %s %s", name, rdatastr);
1224                 }
1225                 return result;
1226         }
1227
1228         /* there are existing records. We need to see if this will
1229          * replace a record or add to it
1230          */
1231         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1232         if (el == NULL) {
1233                 state->log(ISC_LOG_ERROR, "samba_dlz: no dnsRecord attribute for %s",
1234                            ldb_dn_get_linearized(dn));
1235                 talloc_free(rec);
1236                 return ISC_R_FAILURE;
1237         }
1238
1239         for (i=0; i<el->num_values; i++) {
1240                 struct dnsp_DnssrvRpcRecord rec2;
1241
1242                 ndr_err = ndr_pull_struct_blob(&el->values[i], rec, &rec2,
1243                                                (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1244                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1245                         state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1246                                    ldb_dn_get_linearized(dn));
1247                         talloc_free(rec);
1248                         return ISC_R_FAILURE;
1249                 }
1250
1251                 if (b9_record_match(state, rec, &rec2)) {
1252                         break;
1253                 }
1254         }
1255         if (i == el->num_values) {
1256                 /* adding a new value */
1257                 el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values+1);
1258                 if (el->values == NULL) {
1259                         talloc_free(rec);
1260                         return ISC_R_NOMEMORY;
1261                 }
1262                 el->num_values++;
1263         }
1264
1265         ndr_err = ndr_push_struct_blob(&el->values[i], rec, rec,
1266                                        (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
1267         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1268                 state->log(ISC_LOG_ERROR, "samba_dlz: failed to push dnsRecord for %s",
1269                            ldb_dn_get_linearized(dn));
1270                 talloc_free(rec);
1271                 return ISC_R_FAILURE;
1272         }
1273
1274         /* modify the record */
1275         el->flags = LDB_FLAG_MOD_REPLACE;
1276         ret = ldb_modify(state->samdb, res->msgs[0]);
1277         if (ret != LDB_SUCCESS) {
1278                 state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
1279                            ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1280                 talloc_free(rec);
1281                 return ISC_R_FAILURE;
1282         }
1283
1284         state->log(ISC_LOG_INFO, "samba_dlz: added rdataset %s '%s'", name, rdatastr);
1285
1286         talloc_free(rec);
1287         return ISC_R_SUCCESS;
1288 }
1289
1290 /*
1291   remove a rdataset
1292  */
1293 _PUBLIC_ isc_result_t dlz_subrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
1294 {
1295         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1296         struct dnsp_DnssrvRpcRecord *rec;
1297         struct ldb_dn *dn;
1298         isc_result_t result;
1299         struct ldb_result *res;
1300         const char *attrs[] = { "dnsRecord", NULL };
1301         int ret, i;
1302         struct ldb_message_element *el;
1303         enum ndr_err_code ndr_err;
1304
1305         if (state->transaction_token != (void*)version) {
1306                 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1307                 return ISC_R_FAILURE;
1308         }
1309
1310         rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
1311         if (rec == NULL) {
1312                 return ISC_R_NOMEMORY;
1313         }
1314
1315         if (!b9_parse(state, rdatastr, rec)) {
1316                 state->log(ISC_LOG_INFO, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
1317                 talloc_free(rec);
1318                 return ISC_R_FAILURE;
1319         }
1320
1321         /* find the DN of the record */
1322         result = b9_find_name_dn(state, name, rec, &dn);
1323         if (result != ISC_R_SUCCESS) {
1324                 talloc_free(rec);
1325                 return result;
1326         }
1327
1328         /* get the existing records */
1329         ret = ldb_search(state->samdb, rec, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1330         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1331                 talloc_free(rec);
1332                 return ISC_R_NOTFOUND;
1333         }
1334
1335         /* there are existing records. We need to see if any match
1336          */
1337         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1338         if (el == NULL || el->num_values == 0) {
1339                 state->log(ISC_LOG_ERROR, "samba_dlz: no dnsRecord attribute for %s",
1340                            ldb_dn_get_linearized(dn));
1341                 talloc_free(rec);
1342                 return ISC_R_FAILURE;
1343         }
1344
1345         for (i=0; i<el->num_values; i++) {
1346                 struct dnsp_DnssrvRpcRecord rec2;
1347
1348                 ndr_err = ndr_pull_struct_blob(&el->values[i], rec, &rec2,
1349                                                (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1350                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1351                         state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1352                                    ldb_dn_get_linearized(dn));
1353                         talloc_free(rec);
1354                         return ISC_R_FAILURE;
1355                 }
1356
1357                 if (b9_record_match(state, rec, &rec2)) {
1358                         break;
1359                 }
1360         }
1361         if (i == el->num_values) {
1362                 talloc_free(rec);
1363                 return ISC_R_NOTFOUND;
1364         }
1365
1366         if (i < el->num_values-1) {
1367                 memmove(&el->values[i], &el->values[i+1], sizeof(el->values[0])*((el->num_values-1)-i));
1368         }
1369         el->num_values--;
1370
1371         if (el->num_values == 0) {
1372                 /* delete the record */
1373                 ret = ldb_delete(state->samdb, dn);
1374         } else {
1375                 /* modify the record */
1376                 el->flags = LDB_FLAG_MOD_REPLACE;
1377                 ret = ldb_modify(state->samdb, res->msgs[0]);
1378         }
1379         if (ret != LDB_SUCCESS) {
1380                 state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
1381                            ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1382                 talloc_free(rec);
1383                 return ISC_R_FAILURE;
1384         }
1385
1386         state->log(ISC_LOG_INFO, "samba_dlz: subtracted rdataset %s '%s'", name, rdatastr);
1387
1388         talloc_free(rec);
1389         return ISC_R_SUCCESS;
1390 }
1391
1392
1393 /*
1394   delete all records of the given type
1395  */
1396 _PUBLIC_ isc_result_t dlz_delrdataset(const char *name, const char *type, void *dbdata, void *version)
1397 {
1398         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1399         TALLOC_CTX *tmp_ctx;
1400         struct ldb_dn *dn;
1401         isc_result_t result;
1402         struct ldb_result *res;
1403         const char *attrs[] = { "dnsRecord", NULL };
1404         int ret, i;
1405         struct ldb_message_element *el;
1406         enum ndr_err_code ndr_err;
1407         enum dns_record_type dns_type;
1408         bool found = false;
1409
1410         if (state->transaction_token != (void*)version) {
1411                 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1412                 return ISC_R_FAILURE;
1413         }
1414
1415         if (!b9_dns_type(type, &dns_type)) {
1416                 state->log(ISC_LOG_INFO, "samba_dlz: bad dns type %s in delete", type);
1417                 return ISC_R_FAILURE;
1418         }
1419
1420         tmp_ctx = talloc_new(state);
1421
1422         /* find the DN of the record */
1423         result = b9_find_name_dn(state, name, tmp_ctx, &dn);
1424         if (result != ISC_R_SUCCESS) {
1425                 talloc_free(tmp_ctx);
1426                 return result;
1427         }
1428
1429         /* get the existing records */
1430         ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1431         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1432                 talloc_free(tmp_ctx);
1433                 return ISC_R_NOTFOUND;
1434         }
1435
1436         /* there are existing records. We need to see if any match the type
1437          */
1438         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1439         if (el == NULL || el->num_values == 0) {
1440                 talloc_free(tmp_ctx);
1441                 return ISC_R_NOTFOUND;
1442         }
1443
1444         for (i=0; i<el->num_values; i++) {
1445                 struct dnsp_DnssrvRpcRecord rec2;
1446
1447                 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec2,
1448                                                (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1449                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1450                         state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1451                                    ldb_dn_get_linearized(dn));
1452                         talloc_free(tmp_ctx);
1453                         return ISC_R_FAILURE;
1454                 }
1455
1456                 if (dns_type == rec2.wType) {
1457                         if (i < el->num_values-1) {
1458                                 memmove(&el->values[i], &el->values[i+1],
1459                                         sizeof(el->values[0])*((el->num_values-1)-i));
1460                         }
1461                         el->num_values--;
1462                         i--;
1463                         found = true;
1464                 }
1465         }
1466
1467         if (!found) {
1468                 talloc_free(tmp_ctx);
1469                 return ISC_R_FAILURE;
1470         }
1471
1472         if (el->num_values == 0) {
1473                 /* delete the record */
1474                 ret = ldb_delete(state->samdb, dn);
1475         } else {
1476                 /* modify the record */
1477                 el->flags = LDB_FLAG_MOD_REPLACE;
1478                 ret = ldb_modify(state->samdb, res->msgs[0]);
1479         }
1480         if (ret != LDB_SUCCESS) {
1481                 state->log(ISC_LOG_ERROR, "samba_dlz: failed to delete type %s in %s - %s",
1482                            type, ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1483                 talloc_free(tmp_ctx);
1484                 return ISC_R_FAILURE;
1485         }
1486
1487         state->log(ISC_LOG_INFO, "samba_dlz: deleted rdataset %s of type %s", name, type);
1488
1489         talloc_free(tmp_ctx);
1490         return ISC_R_SUCCESS;
1491 }