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/>.
24 #include "param/param.h"
25 #include "dsdb/samdb/samdb.h"
26 #include "dsdb/common/util.h"
27 #include "auth/session.h"
28 #include "gen_ndr/ndr_dnsp.h"
29 #include "lib/cmdline/popt_common.h"
30 #include "dlz_bind9.h"
32 struct dlz_bind9_data {
33 struct ldb_context *samdb;
34 struct tevent_context *ev_ctx;
35 struct loadparm_context *lp;
37 /* helper functions from the dlz_dlopen driver */
38 void (*log)(int level, const char *fmt, ...);
39 isc_result_t (*putrr)(dns_sdlzlookup_t *handle, const char *type,
40 dns_ttl_t ttl, const char *data);
41 isc_result_t (*putnamedrr)(dns_sdlzlookup_t *handle, const char *name,
42 const char *type, dns_ttl_t ttl, const char *data);
46 return the version of the API
48 _PUBLIC_ int dlz_version(unsigned int *flags)
50 return DLZ_DLOPEN_VERSION;
54 remember a helper function from the bind9 dlz_dlopen driver
56 static void b9_add_helper(struct dlz_bind9_data *state, const char *helper_name, void *ptr)
58 if (strcmp(helper_name, "log") == 0) {
61 if (strcmp(helper_name, "putrr") == 0) {
64 if (strcmp(helper_name, "putnamedrr") == 0) {
65 state->putnamedrr = ptr;
70 format a record for bind9
72 static bool b9_format(struct dlz_bind9_data *state,
74 struct dnsp_DnssrvRpcRecord *rec,
75 const char **type, const char **data)
80 *data = rec->data.ipv4;
85 *data = rec->data.ipv6;
90 *data = rec->data.cname;
95 *data = rec->data.txt;
100 *data = rec->data.ptr;
105 *data = talloc_asprintf(mem_ctx, "%u %u %u %s",
106 rec->data.srv.wPriority,
107 rec->data.srv.wWeight,
109 rec->data.srv.nameTarget);
114 *data = talloc_asprintf(mem_ctx, "%u %s",
115 rec->data.srv.wPriority,
116 rec->data.srv.nameTarget);
121 *data = talloc_asprintf(mem_ctx, "%s %s",
128 *data = rec->data.ns;
133 *data = talloc_asprintf(mem_ctx, "%s %s %u %u %u %u %u",
136 rec->data.soa.serial,
137 rec->data.soa.refresh,
139 rec->data.soa.expire,
140 rec->data.soa.minimum);
144 state->log(ISC_LOG_ERROR, "samba b9_putrr: unhandled record type %u",
153 send a resource recond to bind9
155 static isc_result_t b9_putrr(struct dlz_bind9_data *state,
156 void *handle, struct dnsp_DnssrvRpcRecord *rec,
160 const char *type, *data;
161 TALLOC_CTX *tmp_ctx = talloc_new(state);
163 if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
164 return ISC_R_FAILURE;
168 talloc_free(tmp_ctx);
169 return ISC_R_NOMEMORY;
174 for (i=0; types[i]; i++) {
175 if (strcmp(types[i], type) == 0) break;
177 if (types[i] == NULL) {
179 return ISC_R_SUCCESS;
183 /* FIXME: why does dlz insist on all TTL values being the same
184 for the same name? */
185 result = state->putrr(handle, type, /* rec->dwTtlSeconds */ 900, data);
186 if (result != ISC_R_SUCCESS) {
187 state->log(ISC_LOG_ERROR, "Failed to put rr");
189 talloc_free(tmp_ctx);
195 send a named resource recond to bind9
197 static isc_result_t b9_putnamedrr(struct dlz_bind9_data *state,
198 void *handle, const char *name,
199 struct dnsp_DnssrvRpcRecord *rec)
202 const char *type, *data;
203 TALLOC_CTX *tmp_ctx = talloc_new(state);
205 if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
206 return ISC_R_FAILURE;
210 talloc_free(tmp_ctx);
211 return ISC_R_NOMEMORY;
214 /* FIXME: why does dlz insist on all TTL values being the same
215 for the same name? */
216 result = state->putnamedrr(handle, name, type, /* rec->dwTtlSeconds */ 900, data);
217 if (result != ISC_R_SUCCESS) {
218 state->log(ISC_LOG_ERROR, "Failed to put named rr '%s'", name);
220 talloc_free(tmp_ctx);
228 static isc_result_t parse_options(struct dlz_bind9_data *state,
229 unsigned int argc, char *argv[])
233 struct poptOption long_options[] = {
238 pc = poptGetContext("dlz_bind9", argc, (const char **)argv, long_options,
239 POPT_CONTEXT_KEEP_FIRST);
241 while ((opt = poptGetNextOpt(pc)) != -1) {
244 state->log(ISC_LOG_ERROR, "Invalid option %s: %s",
245 poptBadOption(pc, 0), poptStrerror(opt));
246 return ISC_R_FAILURE;
250 return ISC_R_SUCCESS;
255 called to initialise the driver
257 _PUBLIC_ isc_result_t dlz_create(const char *dlzname,
258 unsigned int argc, char *argv[],
259 void *driverarg, void **dbdata, ...)
261 struct dlz_bind9_data *state;
262 const char *helper_name;
270 state = talloc_zero(NULL, struct dlz_bind9_data);
272 return ISC_R_NOMEMORY;
275 tmp_ctx = talloc_new(state);
277 /* fill in the helper functions */
278 va_start(ap, dbdata);
279 while ((helper_name = va_arg(ap, const char *)) != NULL) {
280 b9_add_helper(state, helper_name, va_arg(ap, void*));
284 result = parse_options(state, argc, argv);
285 if (result != ISC_R_SUCCESS) {
289 state->lp = loadparm_init_global(true);
290 if (state->lp == NULL) {
291 result = ISC_R_NOMEMORY;
295 state->ev_ctx = tevent_context_init(state);
296 if (state->ev_ctx == NULL) {
297 result = ISC_R_NOMEMORY;
301 state->samdb = ldb_init(state, state->ev_ctx);
302 if (state->samdb == NULL) {
303 state->log(ISC_LOG_ERROR, "samba dlz_bind9: Failed to create ldb");
304 result = ISC_R_FAILURE;
308 url = talloc_asprintf(tmp_ctx, "ldapi://%s",
309 private_path(tmp_ctx, state->lp, "ldap_priv/ldapi"));
311 result = ISC_R_NOMEMORY;
315 ret = ldb_connect(state->samdb, url, 0, NULL);
317 state->log(ISC_LOG_ERROR, "samba dlz_bind9: Failed to connect to %s - %s",
318 url, ldb_errstring(state->samdb));
319 result = ISC_R_FAILURE;
323 dn = ldb_get_default_basedn(state->samdb);
325 state->log(ISC_LOG_ERROR, "samba dlz_bind9: Unable to get basedn for %s - %s",
326 url, ldb_errstring(state->samdb));
327 result = ISC_R_FAILURE;
331 state->log(ISC_LOG_INFO, "samba dlz_bind9: started for DN %s",
332 ldb_dn_get_linearized(dn));
336 talloc_free(tmp_ctx);
337 return ISC_R_SUCCESS;
347 _PUBLIC_ void dlz_destroy(void *driverarg, void *dbdata)
349 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
350 state->log(ISC_LOG_INFO, "samba dlz_bind9: shutting down");
356 see if we handle a given zone
358 _PUBLIC_ isc_result_t dlz_findzonedb(void *driverarg, void *dbdata, const char *name)
360 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
361 if (strcasecmp(lpcfg_dnsdomain(state->lp), name) == 0) {
362 return ISC_R_SUCCESS;
364 return ISC_R_NOTFOUND;
371 _PUBLIC_ isc_result_t dlz_lookup_types(struct dlz_bind9_data *state,
372 const char *zone, const char *name,
373 void *driverarg, dns_sdlzlookup_t *lookup,
377 TALLOC_CTX *tmp_ctx = talloc_new(state);
378 const char *attrs[] = { "dnsRecord", NULL };
380 struct ldb_result *res;
381 struct ldb_message_element *el;
383 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
385 talloc_free(tmp_ctx);
386 return ISC_R_NOMEMORY;
389 if (!ldb_dn_add_child_fmt(dn, "DC=%s,DC=%s,CN=MicrosoftDNS,DC=DomainDnsZones",
391 talloc_free(tmp_ctx);
392 return ISC_R_NOMEMORY;
395 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
396 attrs, "objectClass=dnsNode");
397 if (ret != LDB_SUCCESS) {
398 talloc_free(tmp_ctx);
399 return ISC_R_NOTFOUND;
402 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
403 if (el == NULL || el->num_values == 0) {
404 state->log(ISC_LOG_INFO, "failed to find %s",
405 ldb_dn_get_linearized(dn));
406 talloc_free(tmp_ctx);
407 return ISC_R_NOTFOUND;
410 for (i=0; i<el->num_values; i++) {
411 struct dnsp_DnssrvRpcRecord rec;
412 enum ndr_err_code ndr_err;
415 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec,
416 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
417 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
418 state->log(ISC_LOG_ERROR, "samba dlz_bind9: failed to parse dnsRecord for %s",
419 ldb_dn_get_linearized(dn));
420 talloc_free(tmp_ctx);
421 return ISC_R_FAILURE;
424 result = b9_putrr(state, lookup, &rec, types);
425 if (result != ISC_R_SUCCESS) {
426 talloc_free(tmp_ctx);
431 talloc_free(tmp_ctx);
432 return ISC_R_SUCCESS;
438 _PUBLIC_ isc_result_t dlz_lookup(const char *zone, const char *name, void *driverarg,
439 void *dbdata, dns_sdlzlookup_t *lookup)
441 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
442 return dlz_lookup_types(state, zone, name, driverarg, lookup, NULL);
447 see if a zone transfer is allowed
449 _PUBLIC_ isc_result_t dlz_allowzonexfr(void *driverarg, void *dbdata, const char *name,
452 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
454 if (strcasecmp(lpcfg_dnsdomain(state->lp), name) == 0) {
455 /* TODO: check an ACL here? client is the IP of the requester */
456 state->log(ISC_LOG_INFO, "samba dlz_bind9: allowing zone transfer for '%s' by '%s'",
458 return ISC_R_SUCCESS;
460 return ISC_R_NOTFOUND;
464 perform a zone transfer
466 _PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *driverarg, void *dbdata,
467 dns_sdlzallnodes_t *allnodes)
469 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
470 const char *attrs[] = { "dnsRecord", NULL };
473 struct ldb_result *res;
474 TALLOC_CTX *tmp_ctx = talloc_new(state);
477 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
479 talloc_free(tmp_ctx);
480 return ISC_R_NOMEMORY;
483 if (!ldb_dn_add_child_fmt(dn, "DC=%s,CN=MicrosoftDNS,DC=DomainDnsZones", zone)) {
484 talloc_free(tmp_ctx);
485 return ISC_R_NOMEMORY;
488 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
489 attrs, "objectClass=dnsNode");
490 if (ret != LDB_SUCCESS) {
491 talloc_free(tmp_ctx);
492 return ISC_R_NOTFOUND;
495 for (i=0; i<res->count; i++) {
496 struct ldb_message_element *el;
497 TALLOC_CTX *el_ctx = talloc_new(tmp_ctx);
498 const char *rdn, *name;
499 const struct ldb_val *v;
501 el = ldb_msg_find_element(res->msgs[i], "dnsRecord");
502 if (el == NULL || el->num_values == 0) {
503 state->log(ISC_LOG_INFO, "failed to find dnsRecord for %s",
504 ldb_dn_get_linearized(dn));
509 v = ldb_dn_get_rdn_val(res->msgs[i]->dn);
511 state->log(ISC_LOG_INFO, "failed to find RDN for %s",
512 ldb_dn_get_linearized(dn));
517 rdn = talloc_strndup(el_ctx, (char *)v->data, v->length);
519 talloc_free(tmp_ctx);
520 return ISC_R_NOMEMORY;
523 if (strcmp(rdn, "@") == 0) {
526 name = talloc_asprintf(el_ctx, "%s.%s", rdn, zone);
529 talloc_free(tmp_ctx);
530 return ISC_R_NOMEMORY;
533 for (j=0; j<el->num_values; j++) {
534 struct dnsp_DnssrvRpcRecord rec;
535 enum ndr_err_code ndr_err;
538 ndr_err = ndr_pull_struct_blob(&el->values[j], el_ctx, &rec,
539 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
540 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
541 state->log(ISC_LOG_ERROR, "samba dlz_bind9: failed to parse dnsRecord for %s",
542 ldb_dn_get_linearized(dn));
547 result = b9_putnamedrr(state, allnodes, name, &rec);
548 if (result != ISC_R_SUCCESS) {
555 talloc_free(tmp_ctx);
557 return ISC_R_SUCCESS;