summaryrefslogtreecommitdiff
path: root/source4/dns_server/dlz_bind9.c
diff options
context:
space:
mode:
Diffstat (limited to 'source4/dns_server/dlz_bind9.c')
-rw-r--r--source4/dns_server/dlz_bind9.c523
1 files changed, 523 insertions, 0 deletions
diff --git a/source4/dns_server/dlz_bind9.c b/source4/dns_server/dlz_bind9.c
new file mode 100644
index 0000000000..0260a79eea
--- /dev/null
+++ b/source4/dns_server/dlz_bind9.c
@@ -0,0 +1,523 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ bind9 dlz driver for Samba
+
+ Copyright (C) 2010 Andrew Tridgell
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "talloc.h"
+#include "param/param.h"
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/common/util.h"
+#include "auth/session.h"
+#include "gen_ndr/ndr_dnsp.h"
+#include "dlz_bind9.h"
+
+struct dlz_bind9_data {
+ struct ldb_context *samdb;
+ struct tevent_context *ev_ctx;
+ struct loadparm_context *lp;
+
+ /* helper functions from the dlz_dlopen driver */
+ void (*log)(int level, const char *fmt, ...);
+ isc_result_t (*putrr)(dns_sdlzlookup_t *handle, const char *type,
+ dns_ttl_t ttl, const char *data);
+ isc_result_t (*putnamedrr)(dns_sdlzlookup_t *handle, const char *name,
+ const char *type, dns_ttl_t ttl, const char *data);
+};
+
+/*
+ return the version of the API
+ */
+_PUBLIC_ int dlz_version(void)
+{
+ return DLZ_DLOPEN_VERSION;
+}
+
+/*
+ remember a helper function from the bind9 dlz_dlopen driver
+ */
+static void b9_add_helper(struct dlz_bind9_data *state, const char *helper_name, void *ptr)
+{
+ if (strcmp(helper_name, "log") == 0) {
+ state->log = ptr;
+ }
+ if (strcmp(helper_name, "putrr") == 0) {
+ state->putrr = ptr;
+ }
+ if (strcmp(helper_name, "putnamedrr") == 0) {
+ state->putnamedrr = ptr;
+ }
+}
+
+/*
+ format a record for bind9
+ */
+static bool b9_format(struct dlz_bind9_data *state,
+ TALLOC_CTX *mem_ctx,
+ struct dnsp_DnssrvRpcRecord *rec,
+ const char **type, const char **data)
+{
+ switch (rec->wType) {
+ case DNS_TYPE_A:
+ *type = "a";
+ *data = rec->data.ipv4;
+ break;
+
+ case DNS_TYPE_AAAA:
+ *type = "aaaa";
+ *data = rec->data.ipv6;
+ break;
+
+ case DNS_TYPE_CNAME:
+ *type = "cname";
+ *data = rec->data.cname;
+ break;
+
+ case DNS_TYPE_TXT:
+ *type = "txt";
+ *data = rec->data.txt;
+ break;
+
+ case DNS_TYPE_PTR:
+ *type = "ptr";
+ *data = rec->data.ptr;
+ break;
+
+ case DNS_TYPE_SRV:
+ *type = "srv";
+ *data = talloc_asprintf(mem_ctx, "%u %u %u %s",
+ rec->data.srv.wPriority,
+ rec->data.srv.wWeight,
+ rec->data.srv.wPort,
+ rec->data.srv.nameTarget);
+ break;
+
+ case DNS_TYPE_MX:
+ *type = "mx";
+ *data = talloc_asprintf(mem_ctx, "%u %s",
+ rec->data.srv.wPriority,
+ rec->data.srv.nameTarget);
+ break;
+
+ case DNS_TYPE_HINFO:
+ *type = "hinfo";
+ *data = talloc_asprintf(mem_ctx, "%s %s",
+ rec->data.hinfo.cpu,
+ rec->data.hinfo.os);
+ break;
+
+ case DNS_TYPE_NS:
+ *type = "ns";
+ *data = rec->data.ns;
+ break;
+
+ case DNS_TYPE_SOA:
+ *type = "soa";
+ *data = talloc_asprintf(mem_ctx, "%s %s %u %u %u %u %u",
+ rec->data.soa.mname,
+ rec->data.soa.rname,
+ rec->data.soa.serial,
+ rec->data.soa.refresh,
+ rec->data.soa.retry,
+ rec->data.soa.expire,
+ rec->data.soa.minimum);
+ break;
+
+ default:
+ state->log(ISC_LOG_ERROR, "samba b9_putrr: unhandled record type %u",
+ rec->wType);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ send a resource recond to bind9
+ */
+static isc_result_t b9_putrr(struct dlz_bind9_data *state,
+ void *handle, struct dnsp_DnssrvRpcRecord *rec,
+ const char **types)
+{
+ isc_result_t result;
+ const char *type, *data;
+ TALLOC_CTX *tmp_ctx = talloc_new(state);
+
+ if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
+ return ISC_R_FAILURE;
+ }
+
+ if (data == NULL) {
+ talloc_free(tmp_ctx);
+ return ISC_R_NOMEMORY;
+ }
+
+ if (types) {
+ int i;
+ for (i=0; types[i]; i++) {
+ if (strcmp(types[i], type) == 0) break;
+ }
+ if (types[i] == NULL) {
+ /* skip it */
+ return ISC_R_SUCCESS;
+ }
+ }
+
+ /* FIXME: why does dlz insist on all TTL values being the same
+ for the same name? */
+ result = state->putrr(handle, type, /* rec->dwTtlSeconds */ 900, data);
+ if (result != ISC_R_SUCCESS) {
+ state->log(ISC_LOG_ERROR, "Failed to put rr");
+ }
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+
+/*
+ send a named resource recond to bind9
+ */
+static isc_result_t b9_putnamedrr(struct dlz_bind9_data *state,
+ void *handle, const char *name,
+ struct dnsp_DnssrvRpcRecord *rec)
+{
+ isc_result_t result;
+ const char *type, *data;
+ TALLOC_CTX *tmp_ctx = talloc_new(state);
+
+ if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
+ return ISC_R_FAILURE;
+ }
+
+ if (data == NULL) {
+ talloc_free(tmp_ctx);
+ return ISC_R_NOMEMORY;
+ }
+
+ /* FIXME: why does dlz insist on all TTL values being the same
+ for the same name? */
+ result = state->putnamedrr(handle, name, type, /* rec->dwTtlSeconds */ 900, data);
+ if (result != ISC_R_SUCCESS) {
+ state->log(ISC_LOG_ERROR, "Failed to put named rr '%s'", name);
+ }
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+
+/*
+ called to initialise the driver
+ */
+_PUBLIC_ isc_result_t dlz_create(const char *dlzname,
+ unsigned int argc, char *argv[],
+ void *driverarg, void **dbdata, ...)
+{
+ struct dlz_bind9_data *state;
+ const char *helper_name;
+ va_list ap;
+ isc_result_t result;
+ const char *url;
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+ struct ldb_dn *dn;
+
+ state = talloc_zero(NULL, struct dlz_bind9_data);
+ if (state == NULL) {
+ return ISC_R_NOMEMORY;
+ }
+
+ tmp_ctx = talloc_new(state);
+
+ /* fill in the helper functions */
+ va_start(ap, dbdata);
+ while ((helper_name = va_arg(ap, const char *)) != NULL) {
+ b9_add_helper(state, helper_name, va_arg(ap, void*));
+ }
+ va_end(ap);
+
+ state->lp = loadparm_init_global(true);
+ if (state->lp == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto failed;
+ }
+
+ state->ev_ctx = tevent_context_init(state);
+ if (state->ev_ctx == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto failed;
+ }
+
+ state->samdb = ldb_init(state, state->ev_ctx);
+ if (state->samdb == NULL) {
+ state->log(ISC_LOG_ERROR, "samba dlz_bind9: Failed to create ldb");
+ result = ISC_R_FAILURE;
+ goto failed;
+ }
+
+ url = talloc_asprintf(tmp_ctx, "ldapi://%s",
+ private_path(tmp_ctx, state->lp, "ldap_priv/ldapi"));
+ if (url == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto failed;
+ }
+
+ ret = ldb_connect(state->samdb, url, 0, NULL);
+ if (ret == -1) {
+ state->log(ISC_LOG_ERROR, "samba dlz_bind9: Failed to connect to %s - %s",
+ url, ldb_errstring(state->samdb));
+ result = ISC_R_FAILURE;
+ goto failed;
+ }
+
+ dn = ldb_get_default_basedn(state->samdb);
+ if (dn == NULL) {
+ state->log(ISC_LOG_ERROR, "samba dlz_bind9: Unable to get basedn for %s - %s",
+ url, ldb_errstring(state->samdb));
+ result = ISC_R_FAILURE;
+ goto failed;
+ }
+
+ state->log(ISC_LOG_INFO, "samba dlz_bind9: started for DN %s",
+ ldb_dn_get_linearized(dn));
+
+ *dbdata = state;
+
+ talloc_free(tmp_ctx);
+ return ISC_R_SUCCESS;
+
+failed:
+ talloc_free(state);
+ return result;
+}
+
+/*
+ shutdown the backend
+ */
+_PUBLIC_ void dlz_destroy(void *driverarg, void *dbdata)
+{
+ struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
+ state->log(ISC_LOG_INFO, "samba dlz_bind9: shutting down");
+ talloc_free(state);
+}
+
+
+/*
+ see if we handle a given zone
+ */
+_PUBLIC_ isc_result_t dlz_findzonedb(void *driverarg, void *dbdata, const char *name)
+{
+ struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
+ if (strcasecmp(lpcfg_dnsdomain(state->lp), name) == 0) {
+ return ISC_R_SUCCESS;
+ }
+ return ISC_R_NOTFOUND;
+}
+
+
+/*
+ lookup one record
+ */
+_PUBLIC_ isc_result_t dlz_lookup_types(struct dlz_bind9_data *state,
+ const char *zone, const char *name,
+ void *driverarg, dns_sdlzlookup_t *lookup,
+ const char **types)
+{
+ struct ldb_dn *dn;
+ TALLOC_CTX *tmp_ctx = talloc_new(state);
+ const char *attrs[] = { "dnsRecord", NULL };
+ int ret, i;
+ struct ldb_result *res;
+ struct ldb_message_element *el;
+
+ dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
+ if (dn == NULL) {
+ talloc_free(tmp_ctx);
+ return ISC_R_NOMEMORY;
+ }
+
+ if (!ldb_dn_add_child_fmt(dn, "DC=%s,DC=%s,CN=MicrosoftDNS,DC=DomainDnsZones",
+ name, zone)) {
+ talloc_free(tmp_ctx);
+ return ISC_R_NOMEMORY;
+ }
+
+ ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
+ attrs, "objectClass=dnsNode");
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ISC_R_NOTFOUND;
+ }
+
+ el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
+ if (el == NULL || el->num_values == 0) {
+ state->log(ISC_LOG_INFO, "failed to find %s",
+ ldb_dn_get_linearized(dn));
+ talloc_free(tmp_ctx);
+ return ISC_R_NOTFOUND;
+ }
+
+ for (i=0; i<el->num_values; i++) {
+ struct dnsp_DnssrvRpcRecord rec;
+ enum ndr_err_code ndr_err;
+ isc_result_t result;
+
+ ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec,
+ (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ state->log(ISC_LOG_ERROR, "samba dlz_bind9: failed to parse dnsRecord for %s",
+ ldb_dn_get_linearized(dn));
+ talloc_free(tmp_ctx);
+ return ISC_R_FAILURE;
+ }
+
+ result = b9_putrr(state, lookup, &rec, types);
+ if (result != ISC_R_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+ }
+
+ talloc_free(tmp_ctx);
+ return ISC_R_SUCCESS;
+}
+
+/*
+ lookup one record
+ */
+_PUBLIC_ isc_result_t dlz_lookup(const char *zone, const char *name, void *driverarg,
+ void *dbdata, dns_sdlzlookup_t *lookup)
+{
+ struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
+ return dlz_lookup_types(state, zone, name, driverarg, lookup, NULL);
+}
+
+
+/*
+ see if a zone transfer is allowed
+ */
+_PUBLIC_ isc_result_t dlz_allowzonexfr(void *driverarg, void *dbdata, const char *name,
+ const char *client)
+{
+ struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
+
+ if (strcasecmp(lpcfg_dnsdomain(state->lp), name) == 0) {
+ /* TODO: check an ACL here? client is the IP of the requester */
+ state->log(ISC_LOG_INFO, "samba dlz_bind9: allowing zone transfer for '%s' by '%s'",
+ name, client);
+ return ISC_R_SUCCESS;
+ }
+ return ISC_R_NOTFOUND;
+}
+
+/*
+ perform a zone transfer
+ */
+_PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *driverarg, void *dbdata,
+ dns_sdlzallnodes_t *allnodes)
+{
+ struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
+ const char *attrs[] = { "dnsRecord", NULL };
+ int ret, i, j;
+ struct ldb_dn *dn;
+ struct ldb_result *res;
+ TALLOC_CTX *tmp_ctx = talloc_new(state);
+
+
+ dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
+ if (dn == NULL) {
+ talloc_free(tmp_ctx);
+ return ISC_R_NOMEMORY;
+ }
+
+ if (!ldb_dn_add_child_fmt(dn, "DC=%s,CN=MicrosoftDNS,DC=DomainDnsZones", zone)) {
+ talloc_free(tmp_ctx);
+ return ISC_R_NOMEMORY;
+ }
+
+ ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
+ attrs, "objectClass=dnsNode");
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ISC_R_NOTFOUND;
+ }
+
+ for (i=0; i<res->count; i++) {
+ struct ldb_message_element *el;
+ TALLOC_CTX *el_ctx = talloc_new(tmp_ctx);
+ const char *rdn, *name;
+ const struct ldb_val *v;
+
+ el = ldb_msg_find_element(res->msgs[i], "dnsRecord");
+ if (el == NULL || el->num_values == 0) {
+ state->log(ISC_LOG_INFO, "failed to find dnsRecord for %s",
+ ldb_dn_get_linearized(dn));
+ talloc_free(el_ctx);
+ continue;
+ }
+
+ v = ldb_dn_get_rdn_val(res->msgs[i]->dn);
+ if (v == NULL) {
+ state->log(ISC_LOG_INFO, "failed to find RDN for %s",
+ ldb_dn_get_linearized(dn));
+ talloc_free(el_ctx);
+ continue;
+ }
+
+ rdn = talloc_strndup(el_ctx, (char *)v->data, v->length);
+ if (rdn == NULL) {
+ talloc_free(tmp_ctx);
+ return ISC_R_NOMEMORY;
+ }
+
+ if (strcmp(rdn, "@") == 0) {
+ name = zone;
+ } else {
+ name = talloc_asprintf(el_ctx, "%s.%s", rdn, zone);
+ }
+ if (name == NULL) {
+ talloc_free(tmp_ctx);
+ return ISC_R_NOMEMORY;
+ }
+
+ for (j=0; j<el->num_values; j++) {
+ struct dnsp_DnssrvRpcRecord rec;
+ enum ndr_err_code ndr_err;
+ isc_result_t result;
+
+ ndr_err = ndr_pull_struct_blob(&el->values[j], el_ctx, &rec,
+ (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ state->log(ISC_LOG_ERROR, "samba dlz_bind9: failed to parse dnsRecord for %s",
+ ldb_dn_get_linearized(dn));
+ talloc_free(el_ctx);
+ continue;
+ }
+
+ result = b9_putnamedrr(state, allnodes, name, &rec);
+ if (result != ISC_R_SUCCESS) {
+ talloc_free(el_ctx);
+ continue;
+ }
+ }
+ }
+
+ talloc_free(tmp_ctx);
+
+ return ISC_R_SUCCESS;
+}