dns_server: Do not look for a wildcard for @
[Samba.git] / source4 / dns_server / dnsserver_common.c
blob199e9d513d686d16f515316f1586a7c3df86467c
1 /*
2 Unix SMB/CIFS implementation.
4 DNS server utils
6 Copyright (C) 2010 Kai Blin
7 Copyright (C) 2014 Stefan Metzmacher
8 Copyright (C) 2015 Andrew Bartlett
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "includes.h"
25 #include "libcli/util/ntstatus.h"
26 #include "libcli/util/werror.h"
27 #include "librpc/ndr/libndr.h"
28 #include "librpc/gen_ndr/ndr_dns.h"
29 #include "librpc/gen_ndr/ndr_dnsp.h"
30 #include <ldb.h>
31 #include "dsdb/samdb/samdb.h"
32 #include "dsdb/common/util.h"
33 #include "dns_server/dnsserver_common.h"
34 #include "lib/util/dlinklist.h"
36 #undef DBGC_CLASS
37 #define DBGC_CLASS DBGC_DNS
39 uint8_t werr_to_dns_err(WERROR werr)
41 if (W_ERROR_EQUAL(WERR_OK, werr)) {
42 return DNS_RCODE_OK;
43 } else if (W_ERROR_EQUAL(DNS_ERR(FORMAT_ERROR), werr)) {
44 return DNS_RCODE_FORMERR;
45 } else if (W_ERROR_EQUAL(DNS_ERR(SERVER_FAILURE), werr)) {
46 return DNS_RCODE_SERVFAIL;
47 } else if (W_ERROR_EQUAL(DNS_ERR(NAME_ERROR), werr)) {
48 return DNS_RCODE_NXDOMAIN;
49 } else if (W_ERROR_EQUAL(WERR_DNS_ERROR_NAME_DOES_NOT_EXIST, werr)) {
50 return DNS_RCODE_NXDOMAIN;
51 } else if (W_ERROR_EQUAL(DNS_ERR(NOT_IMPLEMENTED), werr)) {
52 return DNS_RCODE_NOTIMP;
53 } else if (W_ERROR_EQUAL(DNS_ERR(REFUSED), werr)) {
54 return DNS_RCODE_REFUSED;
55 } else if (W_ERROR_EQUAL(DNS_ERR(YXDOMAIN), werr)) {
56 return DNS_RCODE_YXDOMAIN;
57 } else if (W_ERROR_EQUAL(DNS_ERR(YXRRSET), werr)) {
58 return DNS_RCODE_YXRRSET;
59 } else if (W_ERROR_EQUAL(DNS_ERR(NXRRSET), werr)) {
60 return DNS_RCODE_NXRRSET;
61 } else if (W_ERROR_EQUAL(DNS_ERR(NOTAUTH), werr)) {
62 return DNS_RCODE_NOTAUTH;
63 } else if (W_ERROR_EQUAL(DNS_ERR(NOTZONE), werr)) {
64 return DNS_RCODE_NOTZONE;
65 } else if (W_ERROR_EQUAL(DNS_ERR(BADKEY), werr)) {
66 return DNS_RCODE_BADKEY;
68 DEBUG(5, ("No mapping exists for %s\n", win_errstr(werr)));
69 return DNS_RCODE_SERVFAIL;
72 WERROR dns_common_extract(struct ldb_context *samdb,
73 const struct ldb_message_element *el,
74 TALLOC_CTX *mem_ctx,
75 struct dnsp_DnssrvRpcRecord **records,
76 uint16_t *num_records)
78 uint16_t ri;
79 struct dnsp_DnssrvRpcRecord *recs;
81 *records = NULL;
82 *num_records = 0;
84 recs = talloc_zero_array(mem_ctx, struct dnsp_DnssrvRpcRecord,
85 el->num_values);
86 if (recs == NULL) {
87 return WERR_NOT_ENOUGH_MEMORY;
89 for (ri = 0; ri < el->num_values; ri++) {
90 bool am_rodc;
91 int ret;
92 const char *dnsHostName = NULL;
93 struct ldb_val *v = &el->values[ri];
94 enum ndr_err_code ndr_err;
95 ndr_err = ndr_pull_struct_blob(v, recs, &recs[ri],
96 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
97 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
98 TALLOC_FREE(recs);
99 DEBUG(0, ("Failed to grab dnsp_DnssrvRpcRecord\n"));
100 return DNS_ERR(SERVER_FAILURE);
104 * In AD, except on an RODC (where we should list a random RWDC,
105 * we should over-stamp the MNAME with our own hostname
107 if (recs[ri].wType != DNS_TYPE_SOA) {
108 continue;
111 ret = samdb_rodc(samdb, &am_rodc);
112 if (ret != LDB_SUCCESS) {
113 DEBUG(0, ("Failed to confirm we are not an RODC: %s\n",
114 ldb_errstring(samdb)));
115 return DNS_ERR(SERVER_FAILURE);
118 if (am_rodc) {
119 continue;
122 ret = samdb_dns_host_name(samdb, &dnsHostName);
123 if (ret != LDB_SUCCESS || dnsHostName == NULL) {
124 DEBUG(0, ("Failed to get dnsHostName from rootDSE"));
125 return DNS_ERR(SERVER_FAILURE);
128 recs[ri].data.soa.mname = talloc_strdup(recs, dnsHostName);
131 *records = recs;
132 *num_records = el->num_values;
133 return WERR_OK;
137 * Lookup a DNS record, performing an exact match.
138 * i.e. DNS wild card records are not considered.
140 WERROR dns_common_lookup(struct ldb_context *samdb,
141 TALLOC_CTX *mem_ctx,
142 struct ldb_dn *dn,
143 struct dnsp_DnssrvRpcRecord **records,
144 uint16_t *num_records,
145 bool *tombstoned)
147 static const char * const attrs[] = {
148 "dnsRecord",
149 "dNSTombstoned",
150 NULL
152 int ret;
153 WERROR werr;
154 struct ldb_message *msg = NULL;
155 struct ldb_message_element *el;
157 *records = NULL;
158 *num_records = 0;
160 if (tombstoned != NULL) {
161 *tombstoned = false;
162 ret = dsdb_search_one(samdb, mem_ctx, &msg, dn,
163 LDB_SCOPE_BASE, attrs, 0,
164 "(objectClass=dnsNode)");
165 } else {
166 ret = dsdb_search_one(samdb, mem_ctx, &msg, dn,
167 LDB_SCOPE_BASE, attrs, 0,
168 "(&(objectClass=dnsNode)(!(dNSTombstoned=TRUE)))");
170 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
171 return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
173 if (ret != LDB_SUCCESS) {
174 /* TODO: we need to check if there's a glue record we need to
175 * create a referral to */
176 return DNS_ERR(NAME_ERROR);
179 if (tombstoned != NULL) {
180 *tombstoned = ldb_msg_find_attr_as_bool(msg,
181 "dNSTombstoned", false);
184 el = ldb_msg_find_element(msg, "dnsRecord");
185 if (el == NULL) {
186 TALLOC_FREE(msg);
188 * records produced by older Samba releases
189 * keep dnsNode objects without dnsRecord and
190 * without setting dNSTombstoned=TRUE.
192 * We just pretend they're tombstones.
194 if (tombstoned != NULL) {
195 struct dnsp_DnssrvRpcRecord *recs;
196 recs = talloc_array(mem_ctx,
197 struct dnsp_DnssrvRpcRecord,
199 if (recs == NULL) {
200 return WERR_NOT_ENOUGH_MEMORY;
202 recs[0] = (struct dnsp_DnssrvRpcRecord) {
203 .wType = DNS_TYPE_TOMBSTONE,
205 * A value of timestamp != 0
206 * indicated that the object was already
207 * a tombstone, this will be used
208 * in dns_common_replace()
210 .data.timestamp = 1,
213 *tombstoned = true;
214 *records = recs;
215 *num_records = 1;
216 return WERR_OK;
217 } else {
219 * Because we are not looking for a tombstone
220 * in this codepath, we just pretend it does
221 * not exist at all.
223 return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
227 werr = dns_common_extract(samdb, el, mem_ctx, records, num_records);
228 TALLOC_FREE(msg);
229 if (!W_ERROR_IS_OK(werr)) {
230 return werr;
233 return WERR_OK;
237 * Build an ldb_parse_tree node for an equality check
239 * Note: name is assumed to have been validated by dns_name_check
240 * so will be zero terminated and of a reasonable size.
242 static struct ldb_parse_tree *build_equality_operation(
243 TALLOC_CTX *mem_ctx,
244 bool add_asterix, /* prepend an '*' to the name */
245 const uint8_t *name, /* the value being matched */
246 const char *attr, /* the attribute to check name against */
247 size_t size) /* length of name */
250 struct ldb_parse_tree *el = NULL; /* Equality node being built */
251 struct ldb_val *value = NULL; /* Value the attr will be compared
252 with */
253 size_t length = 0; /* calculated length of the value
254 including option '*' prefix and
255 '\0' string terminator */
257 el = talloc(mem_ctx, struct ldb_parse_tree);
258 if (el == NULL) {
259 DBG_ERR("Unable to allocate ldb_parse_tree\n");
260 return NULL;
263 el->operation = LDB_OP_EQUALITY;
264 el->u.equality.attr = talloc_strdup(mem_ctx, attr);
265 value = &el->u.equality.value;
266 length = (add_asterix) ? size + 2 : size + 1;
267 value->data = talloc_zero_array(el, uint8_t, length);
268 if (el == NULL) {
269 DBG_ERR("Unable to allocate value->data\n");
270 TALLOC_FREE(el);
271 return NULL;
274 value->length = length;
275 if (add_asterix) {
276 value->data[0] = '*';
277 memcpy(&value->data[1], name, size);
278 } else {
279 memcpy(value->data, name, size);
281 return el;
285 * Determine the number of levels in name
286 * essentially the number of '.'s in the name + 1
288 * name is assumed to have been validated by dns_name_check
290 static unsigned int number_of_labels(const struct ldb_val *name) {
291 int x = 0;
292 unsigned int labels = 1;
293 for (x = 0; x < name->length; x++) {
294 if (name->data[x] == '.') {
295 labels++;
298 return labels;
301 * Build a query that matches the target name, and any possible
302 * DNS wild card entries
304 * Builds a parse tree equivalent to the example query.
306 * x.y.z -> (|(name=x.y.z)(name=\2a.y.z)(name=\2a.z)(name=\2a))
308 * Returns NULL if unable to build the query.
310 * The first component of the DN is assumed to be the name being looked up
311 * and also that it has been validated by dns_name_check
314 #define BASE "(&(objectClass=dnsNode)(!(dNSTombstoned=TRUE))(|(a=b)(c=d)))"
315 static struct ldb_parse_tree *build_wildcard_query(
316 TALLOC_CTX *mem_ctx,
317 struct ldb_dn *dn)
319 const struct ldb_val *name = NULL; /* The DNS name being
320 queried */
321 const char *attr = NULL; /* The attribute name */
322 struct ldb_parse_tree *query = NULL; /* The constructed query
323 parse tree*/
324 struct ldb_parse_tree *wildcard_query = NULL; /* The parse tree for the
325 name and wild card
326 entries */
327 int labels = 0; /* The number of labels in the name */
329 attr = ldb_dn_get_rdn_name(dn);
330 if (attr == NULL) {
331 DBG_ERR("Unable to get rdn_name\n");
332 return NULL;
335 name = ldb_dn_get_rdn_val(dn);
336 if (name == NULL) {
337 DBG_ERR("Unable to get domain name value\n");
338 return NULL;
340 labels = number_of_labels(name);
342 query = ldb_parse_tree(mem_ctx, BASE);
343 if (query == NULL) {
344 DBG_ERR("Unable to parse query %s\n", BASE);
345 return NULL;
349 * The 3rd element of BASE is a place holder which is replaced with
350 * the actual wild card query
352 wildcard_query = query->u.list.elements[2];
353 TALLOC_FREE(wildcard_query->u.list.elements);
355 wildcard_query->u.list.num_elements = labels + 1;
356 wildcard_query->u.list.elements = talloc_array(
357 wildcard_query,
358 struct ldb_parse_tree *,
359 labels + 1);
361 * Build the wild card query
364 int x = 0; /* current character in the name */
365 int l = 0; /* current equality operator index in elements */
366 struct ldb_parse_tree *el = NULL; /* Equality operator being
367 built */
368 bool add_asterix = true; /* prepend an '*' to the value */
369 for (l = 0, x = 0; l < labels && x < name->length; l++) {
370 unsigned int size = name->length - x;
371 add_asterix = (name->data[x] == '.');
372 el = build_equality_operation(
373 mem_ctx,
374 add_asterix,
375 &name->data[x],
376 attr,
377 size);
378 if (el == NULL) {
379 return NULL; /* Reason will have been logged */
381 wildcard_query->u.list.elements[l] = el;
383 /* skip to the start of the next label */
384 for (;x < name->length && name->data[x] != '.'; x++);
387 /* Add the base level "*" only query */
388 el = build_equality_operation(mem_ctx, true, NULL, attr, 0);
389 if (el == NULL) {
390 TALLOC_FREE(query);
391 return NULL; /* Reason will have been logged */
393 wildcard_query->u.list.elements[l] = el;
395 return query;
399 * Scan the list of records matching a dns wildcard query and return the
400 * best match.
402 * The best match is either an exact name match, or the longest wild card
403 * entry returned
405 * i.e. name = a.b.c candidates *.b.c, *.c, - *.b.c would be selected
406 * name = a.b.c candidates a.b.c, *.b.c, *.c - a.b.c would be selected
408 static struct ldb_message *get_best_match(struct ldb_dn *dn,
409 struct ldb_result *result)
411 int matched = 0; /* Index of the current best match in result */
412 size_t length = 0; /* The length of the current candidate */
413 const struct ldb_val *target = NULL; /* value we're looking for */
414 const struct ldb_val *candidate = NULL; /* current candidate value */
415 int x = 0;
417 target = ldb_dn_get_rdn_val(dn);
418 for(x = 0; x < result->count; x++) {
419 candidate = ldb_dn_get_rdn_val(result->msgs[x]->dn);
420 if (strncasecmp((char *) target->data,
421 (char *) candidate->data,
422 target->length) == 0) {
423 /* Exact match stop searching and return */
424 return result->msgs[x];
426 if (candidate->length > length) {
427 matched = x;
428 length = candidate->length;
431 return result->msgs[matched];
435 * Look up a DNS entry, if an exact match does not exist, return the
436 * closest matching DNS wildcard entry if available
438 * Returns: LDB_ERR_NO_SUCH_OBJECT If no matching record exists
439 * LDB_ERR_OPERATIONS_ERROR If the query fails
440 * LDB_SUCCESS If a matching record was retrieved
443 static int dns_wildcard_lookup(struct ldb_context *samdb,
444 TALLOC_CTX *mem_ctx,
445 struct ldb_dn *dn,
446 struct ldb_message **msg)
448 static const char * const attrs[] = {
449 "dnsRecord",
450 "dNSTombstoned",
451 NULL
453 struct ldb_dn *parent = NULL; /* The parent dn */
454 struct ldb_result *result = NULL; /* Results of the search */
455 int ret; /* Return code */
456 struct ldb_parse_tree *query = NULL; /* The query to run */
457 struct ldb_request *request = NULL; /* LDB request for the query op */
458 struct ldb_message *match = NULL; /* the best matching DNS record */
459 TALLOC_CTX *frame = talloc_stackframe();
461 parent = ldb_dn_get_parent(frame, dn);
462 if (parent == NULL) {
463 DBG_ERR("Unable to extract parent from dn\n");
464 TALLOC_FREE(frame);
465 return LDB_ERR_OPERATIONS_ERROR;
468 query = build_wildcard_query(frame, dn);
469 if (query == NULL) {
470 TALLOC_FREE(frame);
471 return LDB_ERR_OPERATIONS_ERROR;
474 result = talloc_zero(mem_ctx, struct ldb_result);
475 if (result == NULL) {
476 TALLOC_FREE(frame);
477 DBG_ERR("Unable to allocate ldb_result\n");
478 return LDB_ERR_OPERATIONS_ERROR;
481 ret = ldb_build_search_req_ex(&request,
482 samdb,
483 frame,
484 parent,
485 LDB_SCOPE_ONELEVEL,
486 query,
487 attrs,
488 NULL,
489 result,
490 ldb_search_default_callback,
491 NULL);
492 if (ret != LDB_SUCCESS) {
493 TALLOC_FREE(frame);
494 DBG_ERR("ldb_build_search_req_ex returned %d\n", ret);
495 return ret;
498 ret = ldb_request(samdb, request);
499 if (ret != LDB_SUCCESS) {
500 TALLOC_FREE(frame);
501 return ret;
504 ret = ldb_wait(request->handle, LDB_WAIT_ALL);
505 if (ret != LDB_SUCCESS) {
506 TALLOC_FREE(frame);
507 return ret;
510 if (result->count == 0) {
511 TALLOC_FREE(frame);
512 return LDB_ERR_NO_SUCH_OBJECT;
515 match = get_best_match(dn, result);
516 if (match == NULL) {
517 TALLOC_FREE(frame);
518 return LDB_ERR_OPERATIONS_ERROR;
521 *msg = talloc_move(mem_ctx, &match);
522 TALLOC_FREE(frame);
523 return LDB_SUCCESS;
527 * Lookup a DNS record, will match DNS wild card records if an exact match
528 * is not found.
530 WERROR dns_common_wildcard_lookup(struct ldb_context *samdb,
531 TALLOC_CTX *mem_ctx,
532 struct ldb_dn *dn,
533 struct dnsp_DnssrvRpcRecord **records,
534 uint16_t *num_records)
536 int ret;
537 WERROR werr;
538 struct ldb_message *msg = NULL;
539 struct ldb_message_element *el = NULL;
540 const struct ldb_val *name = NULL;
542 *records = NULL;
543 *num_records = 0;
545 name = ldb_dn_get_rdn_val(dn);
546 if (name == NULL) {
547 return DNS_ERR(NAME_ERROR);
550 /* Don't look for a wildcard for @ */
551 if (name->length == 1 && name->data[0] == '@') {
552 return dns_common_lookup(samdb,
553 mem_ctx,
555 records,
556 num_records,
557 NULL);
560 werr = dns_name_check(
561 mem_ctx,
562 strlen((const char*)name->data),
563 (const char*) name->data);
564 if (!W_ERROR_IS_OK(werr)) {
565 return werr;
569 * Do a point search first, then fall back to a wildcard
570 * lookup if it does not exist
572 werr = dns_common_lookup(samdb,
573 mem_ctx,
575 records,
576 num_records,
577 NULL);
578 if (!W_ERROR_EQUAL(werr, WERR_DNS_ERROR_NAME_DOES_NOT_EXIST)) {
579 return werr;
582 ret = dns_wildcard_lookup(samdb, mem_ctx, dn, &msg);
583 if (ret == LDB_ERR_OPERATIONS_ERROR) {
584 return DNS_ERR(SERVER_FAILURE);
586 if (ret != LDB_SUCCESS) {
587 return DNS_ERR(NAME_ERROR);
590 el = ldb_msg_find_element(msg, "dnsRecord");
591 if (el == NULL) {
592 return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
595 werr = dns_common_extract(samdb, el, mem_ctx, records, num_records);
596 TALLOC_FREE(msg);
597 if (!W_ERROR_IS_OK(werr)) {
598 return werr;
601 return WERR_OK;
604 static int rec_cmp(const struct dnsp_DnssrvRpcRecord *r1,
605 const struct dnsp_DnssrvRpcRecord *r2)
607 if (r1->wType != r2->wType) {
609 * The records are sorted with higher types first
611 return r2->wType - r1->wType;
615 * Then we need to sort from the oldest to newest timestamp
617 return r1->dwTimeStamp - r2->dwTimeStamp;
621 * Check for valid DNS names. These are names which:
622 * - are non-empty
623 * - do not start with a dot
624 * - do not have any empty labels
625 * - have no more than 127 labels
626 * - are no longer than 253 characters
627 * - none of the labels exceed 63 characters
629 WERROR dns_name_check(TALLOC_CTX *mem_ctx, size_t len, const char *name)
631 size_t i;
632 unsigned int labels = 0;
633 unsigned int label_len = 0;
635 if (len == 0) {
636 return WERR_DS_INVALID_DN_SYNTAX;
639 if (len > 1 && name[0] == '.') {
640 return WERR_DS_INVALID_DN_SYNTAX;
643 if ((len - 1) > DNS_MAX_DOMAIN_LENGTH) {
644 return WERR_DS_INVALID_DN_SYNTAX;
647 for (i = 0; i < len - 1; i++) {
648 if (name[i] == '.' && name[i+1] == '.') {
649 return WERR_DS_INVALID_DN_SYNTAX;
651 if (name[i] == '.') {
652 labels++;
653 if (labels > DNS_MAX_LABELS) {
654 return WERR_DS_INVALID_DN_SYNTAX;
656 label_len = 0;
657 } else {
658 label_len++;
659 if (label_len > DNS_MAX_LABEL_LENGTH) {
660 return WERR_DS_INVALID_DN_SYNTAX;
665 return WERR_OK;
668 static WERROR check_name_list(TALLOC_CTX *mem_ctx, uint16_t rec_count,
669 struct dnsp_DnssrvRpcRecord *records)
671 WERROR werr;
672 uint16_t i;
673 size_t len;
674 struct dnsp_DnssrvRpcRecord record;
676 werr = WERR_OK;
677 for (i = 0; i < rec_count; i++) {
678 record = records[i];
680 switch (record.wType) {
682 case DNS_TYPE_NS:
683 len = strlen(record.data.ns);
684 werr = dns_name_check(mem_ctx, len, record.data.ns);
685 break;
686 case DNS_TYPE_CNAME:
687 len = strlen(record.data.cname);
688 werr = dns_name_check(mem_ctx, len, record.data.cname);
689 break;
690 case DNS_TYPE_SOA:
691 len = strlen(record.data.soa.mname);
692 werr = dns_name_check(mem_ctx, len, record.data.soa.mname);
693 if (!W_ERROR_IS_OK(werr)) {
694 break;
696 len = strlen(record.data.soa.rname);
697 werr = dns_name_check(mem_ctx, len, record.data.soa.rname);
698 break;
699 case DNS_TYPE_PTR:
700 len = strlen(record.data.ptr);
701 werr = dns_name_check(mem_ctx, len, record.data.ptr);
702 break;
703 case DNS_TYPE_MX:
704 len = strlen(record.data.mx.nameTarget);
705 werr = dns_name_check(mem_ctx, len, record.data.mx.nameTarget);
706 break;
707 case DNS_TYPE_SRV:
708 len = strlen(record.data.srv.nameTarget);
709 werr = dns_name_check(mem_ctx, len,
710 record.data.srv.nameTarget);
711 break;
713 * In the default case, the record doesn't have a DN, so it
714 * must be ok.
716 default:
717 break;
720 if (!W_ERROR_IS_OK(werr)) {
721 return werr;
725 return WERR_OK;
728 WERROR dns_common_replace(struct ldb_context *samdb,
729 TALLOC_CTX *mem_ctx,
730 struct ldb_dn *dn,
731 bool needs_add,
732 uint32_t serial,
733 struct dnsp_DnssrvRpcRecord *records,
734 uint16_t rec_count)
736 struct ldb_message_element *el;
737 uint16_t i;
738 int ret;
739 WERROR werr;
740 struct ldb_message *msg = NULL;
741 bool was_tombstoned = false;
742 bool become_tombstoned = false;
744 msg = ldb_msg_new(mem_ctx);
745 W_ERROR_HAVE_NO_MEMORY(msg);
747 msg->dn = dn;
749 werr = check_name_list(mem_ctx, rec_count, records);
750 if (!W_ERROR_IS_OK(werr)) {
751 return werr;
754 ret = ldb_msg_add_empty(msg, "dnsRecord", LDB_FLAG_MOD_REPLACE, &el);
755 if (ret != LDB_SUCCESS) {
756 return DNS_ERR(SERVER_FAILURE);
760 * we have at least one value,
761 * which might be used for the tombstone marker
763 el->values = talloc_zero_array(el, struct ldb_val, MAX(1, rec_count));
764 if (rec_count > 0) {
765 W_ERROR_HAVE_NO_MEMORY(el->values);
768 * We store a sorted list with the high wType values first
769 * that's what windows does. It also simplifies the
770 * filtering of DNS_TYPE_TOMBSTONE records
772 TYPESAFE_QSORT(records, rec_count, rec_cmp);
775 for (i = 0; i < rec_count; i++) {
776 struct ldb_val *v = &el->values[el->num_values];
777 enum ndr_err_code ndr_err;
779 if (records[i].wType == DNS_TYPE_TOMBSTONE) {
780 if (records[i].data.timestamp != 0) {
781 was_tombstoned = true;
783 continue;
786 records[i].dwSerial = serial;
787 ndr_err = ndr_push_struct_blob(v, el->values, &records[i],
788 (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
789 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
790 DEBUG(0, ("Failed to push dnsp_DnssrvRpcRecord\n"));
791 return DNS_ERR(SERVER_FAILURE);
793 el->num_values++;
796 if (needs_add) {
797 if (el->num_values == 0) {
798 return WERR_OK;
801 ret = ldb_msg_add_string(msg, "objectClass", "dnsNode");
802 if (ret != LDB_SUCCESS) {
803 return DNS_ERR(SERVER_FAILURE);
806 ret = ldb_add(samdb, msg);
807 if (ret != LDB_SUCCESS) {
808 return DNS_ERR(SERVER_FAILURE);
811 return WERR_OK;
814 if (el->num_values == 0) {
815 struct dnsp_DnssrvRpcRecord tbs;
816 struct ldb_val *v = &el->values[el->num_values];
817 enum ndr_err_code ndr_err;
818 struct timeval tv;
820 if (was_tombstoned) {
822 * This is already a tombstoned object.
823 * Just leave it instead of updating the time stamp.
825 return WERR_OK;
828 tv = timeval_current();
829 tbs = (struct dnsp_DnssrvRpcRecord) {
830 .wType = DNS_TYPE_TOMBSTONE,
831 .dwSerial = serial,
832 .data.timestamp = timeval_to_nttime(&tv),
835 ndr_err = ndr_push_struct_blob(v, el->values, &tbs,
836 (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
837 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
838 DEBUG(0, ("Failed to push dnsp_DnssrvRpcRecord\n"));
839 return DNS_ERR(SERVER_FAILURE);
841 el->num_values++;
843 become_tombstoned = true;
846 if (was_tombstoned || become_tombstoned) {
847 ret = ldb_msg_add_empty(msg, "dNSTombstoned",
848 LDB_FLAG_MOD_REPLACE, NULL);
849 if (ret != LDB_SUCCESS) {
850 return DNS_ERR(SERVER_FAILURE);
853 ret = ldb_msg_add_fmt(msg, "dNSTombstoned", "%s",
854 become_tombstoned ? "TRUE" : "FALSE");
855 if (ret != LDB_SUCCESS) {
856 return DNS_ERR(SERVER_FAILURE);
860 ret = ldb_modify(samdb, msg);
861 if (ret != LDB_SUCCESS) {
862 NTSTATUS nt = dsdb_ldb_err_to_ntstatus(ret);
863 return ntstatus_to_werror(nt);
866 return WERR_OK;
869 bool dns_name_match(const char *zone, const char *name, size_t *host_part_len)
871 size_t zl = strlen(zone);
872 size_t nl = strlen(name);
873 ssize_t zi, ni;
874 static const size_t fixup = 'a' - 'A';
876 if (zl > nl) {
877 return false;
880 for (zi = zl, ni = nl; zi >= 0; zi--, ni--) {
881 char zc = zone[zi];
882 char nc = name[ni];
884 /* convert to lower case */
885 if (zc >= 'A' && zc <= 'Z') {
886 zc += fixup;
888 if (nc >= 'A' && nc <= 'Z') {
889 nc += fixup;
892 if (zc != nc) {
893 return false;
897 if (ni >= 0) {
898 if (name[ni] != '.') {
899 return false;
902 ni--;
905 *host_part_len = ni+1;
907 return true;
910 WERROR dns_common_name2dn(struct ldb_context *samdb,
911 struct dns_server_zone *zones,
912 TALLOC_CTX *mem_ctx,
913 const char *name,
914 struct ldb_dn **_dn)
916 struct ldb_dn *base;
917 struct ldb_dn *dn;
918 const struct dns_server_zone *z;
919 size_t host_part_len = 0;
920 WERROR werr;
922 if (name == NULL) {
923 return DNS_ERR(FORMAT_ERROR);
926 if (strcmp(name, "") == 0) {
927 base = ldb_get_default_basedn(samdb);
928 dn = ldb_dn_copy(mem_ctx, base);
929 ldb_dn_add_child_fmt(dn, "DC=@,DC=RootDNSServers,CN=MicrosoftDNS,CN=System");
930 *_dn = dn;
931 return WERR_OK;
934 /* Check non-empty names */
935 werr = dns_name_check(mem_ctx, strlen(name), name);
936 if (!W_ERROR_IS_OK(werr)) {
937 return werr;
940 for (z = zones; z != NULL; z = z->next) {
941 bool match;
943 match = dns_name_match(z->name, name, &host_part_len);
944 if (match) {
945 break;
949 if (z == NULL) {
950 return DNS_ERR(NAME_ERROR);
953 if (host_part_len == 0) {
954 dn = ldb_dn_copy(mem_ctx, z->dn);
955 ldb_dn_add_child_fmt(dn, "DC=@");
956 *_dn = dn;
957 return WERR_OK;
960 dn = ldb_dn_copy(mem_ctx, z->dn);
961 ldb_dn_add_child_fmt(dn, "DC=%*.*s", (int)host_part_len, (int)host_part_len, name);
962 *_dn = dn;
963 return WERR_OK;
966 static int dns_common_sort_zones(struct ldb_message **m1, struct ldb_message **m2)
968 const char *n1, *n2;
969 size_t l1, l2;
971 n1 = ldb_msg_find_attr_as_string(*m1, "name", NULL);
972 n2 = ldb_msg_find_attr_as_string(*m2, "name", NULL);
974 l1 = strlen(n1);
975 l2 = strlen(n2);
977 /* If the string lengths are not equal just sort by length */
978 if (l1 != l2) {
979 /* If m1 is the larger zone name, return it first */
980 return l2 - l1;
983 /*TODO: We need to compare DNs here, we want the DomainDNSZones first */
984 return 0;
987 NTSTATUS dns_common_zones(struct ldb_context *samdb,
988 TALLOC_CTX *mem_ctx,
989 struct ldb_dn *base_dn,
990 struct dns_server_zone **zones_ret)
992 int ret;
993 static const char * const attrs[] = { "name", NULL};
994 struct ldb_result *res;
995 int i;
996 struct dns_server_zone *new_list = NULL;
997 TALLOC_CTX *frame = talloc_stackframe();
999 if (base_dn) {
1000 /* This search will work against windows */
1001 ret = dsdb_search(samdb, frame, &res,
1002 base_dn, LDB_SCOPE_SUBTREE,
1003 attrs, 0, "(objectClass=dnsZone)");
1004 } else {
1005 /* TODO: this search does not work against windows */
1006 ret = dsdb_search(samdb, frame, &res, NULL,
1007 LDB_SCOPE_SUBTREE,
1008 attrs,
1009 DSDB_SEARCH_SEARCH_ALL_PARTITIONS,
1010 "(objectClass=dnsZone)");
1012 if (ret != LDB_SUCCESS) {
1013 TALLOC_FREE(frame);
1014 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1017 TYPESAFE_QSORT(res->msgs, res->count, dns_common_sort_zones);
1019 for (i=0; i < res->count; i++) {
1020 struct dns_server_zone *z;
1022 z = talloc_zero(mem_ctx, struct dns_server_zone);
1023 if (z == NULL) {
1024 TALLOC_FREE(frame);
1025 return NT_STATUS_NO_MEMORY;
1028 z->name = ldb_msg_find_attr_as_string(res->msgs[i], "name", NULL);
1029 talloc_steal(z, z->name);
1030 z->dn = talloc_move(z, &res->msgs[i]->dn);
1032 * Ignore the RootDNSServers zone and zones that we don't support yet
1033 * RootDNSServers should never be returned (Windows DNS server don't)
1034 * ..TrustAnchors should never be returned as is, (Windows returns
1035 * TrustAnchors) and for the moment we don't support DNSSEC so we'd better
1036 * not return this zone.
1038 if ((strcmp(z->name, "RootDNSServers") == 0) ||
1039 (strcmp(z->name, "..TrustAnchors") == 0))
1041 DEBUG(10, ("Ignoring zone %s\n", z->name));
1042 talloc_free(z);
1043 continue;
1045 DLIST_ADD_END(new_list, z);
1048 *zones_ret = new_list;
1049 TALLOC_FREE(frame);
1050 return NT_STATUS_OK;