dns: server side implementation of record aging
[Samba.git] / source4 / dns_server / dnsserver_common.c
blob2551240fca48961f2ca0eaa0d33334c0e6c1c960
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 "rpc_server/dnsserver/dnsserver.h"
35 #include "lib/util/dlinklist.h"
37 #undef DBGC_CLASS
38 #define DBGC_CLASS DBGC_DNS
40 uint8_t werr_to_dns_err(WERROR werr)
42 if (W_ERROR_EQUAL(WERR_OK, werr)) {
43 return DNS_RCODE_OK;
44 } else if (W_ERROR_EQUAL(DNS_ERR(FORMAT_ERROR), werr)) {
45 return DNS_RCODE_FORMERR;
46 } else if (W_ERROR_EQUAL(DNS_ERR(SERVER_FAILURE), werr)) {
47 return DNS_RCODE_SERVFAIL;
48 } else if (W_ERROR_EQUAL(DNS_ERR(NAME_ERROR), werr)) {
49 return DNS_RCODE_NXDOMAIN;
50 } else if (W_ERROR_EQUAL(WERR_DNS_ERROR_NAME_DOES_NOT_EXIST, werr)) {
51 return DNS_RCODE_NXDOMAIN;
52 } else if (W_ERROR_EQUAL(DNS_ERR(NOT_IMPLEMENTED), werr)) {
53 return DNS_RCODE_NOTIMP;
54 } else if (W_ERROR_EQUAL(DNS_ERR(REFUSED), werr)) {
55 return DNS_RCODE_REFUSED;
56 } else if (W_ERROR_EQUAL(DNS_ERR(YXDOMAIN), werr)) {
57 return DNS_RCODE_YXDOMAIN;
58 } else if (W_ERROR_EQUAL(DNS_ERR(YXRRSET), werr)) {
59 return DNS_RCODE_YXRRSET;
60 } else if (W_ERROR_EQUAL(DNS_ERR(NXRRSET), werr)) {
61 return DNS_RCODE_NXRRSET;
62 } else if (W_ERROR_EQUAL(DNS_ERR(NOTAUTH), werr)) {
63 return DNS_RCODE_NOTAUTH;
64 } else if (W_ERROR_EQUAL(DNS_ERR(NOTZONE), werr)) {
65 return DNS_RCODE_NOTZONE;
66 } else if (W_ERROR_EQUAL(DNS_ERR(BADKEY), werr)) {
67 return DNS_RCODE_BADKEY;
69 DEBUG(5, ("No mapping exists for %s\n", win_errstr(werr)));
70 return DNS_RCODE_SERVFAIL;
73 WERROR dns_common_extract(struct ldb_context *samdb,
74 const struct ldb_message_element *el,
75 TALLOC_CTX *mem_ctx,
76 struct dnsp_DnssrvRpcRecord **records,
77 uint16_t *num_records)
79 uint16_t ri;
80 struct dnsp_DnssrvRpcRecord *recs;
82 *records = NULL;
83 *num_records = 0;
85 recs = talloc_zero_array(mem_ctx, struct dnsp_DnssrvRpcRecord,
86 el->num_values);
87 if (recs == NULL) {
88 return WERR_NOT_ENOUGH_MEMORY;
90 for (ri = 0; ri < el->num_values; ri++) {
91 bool am_rodc;
92 int ret;
93 const char *dnsHostName = NULL;
94 struct ldb_val *v = &el->values[ri];
95 enum ndr_err_code ndr_err;
96 ndr_err = ndr_pull_struct_blob(v, recs, &recs[ri],
97 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
98 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
99 TALLOC_FREE(recs);
100 DEBUG(0, ("Failed to grab dnsp_DnssrvRpcRecord\n"));
101 return DNS_ERR(SERVER_FAILURE);
105 * In AD, except on an RODC (where we should list a random RWDC,
106 * we should over-stamp the MNAME with our own hostname
108 if (recs[ri].wType != DNS_TYPE_SOA) {
109 continue;
112 ret = samdb_rodc(samdb, &am_rodc);
113 if (ret != LDB_SUCCESS) {
114 DEBUG(0, ("Failed to confirm we are not an RODC: %s\n",
115 ldb_errstring(samdb)));
116 return DNS_ERR(SERVER_FAILURE);
119 if (am_rodc) {
120 continue;
123 ret = samdb_dns_host_name(samdb, &dnsHostName);
124 if (ret != LDB_SUCCESS || dnsHostName == NULL) {
125 DEBUG(0, ("Failed to get dnsHostName from rootDSE"));
126 return DNS_ERR(SERVER_FAILURE);
129 recs[ri].data.soa.mname = talloc_strdup(recs, dnsHostName);
132 *records = recs;
133 *num_records = el->num_values;
134 return WERR_OK;
138 * Lookup a DNS record, performing an exact match.
139 * i.e. DNS wild card records are not considered.
141 WERROR dns_common_lookup(struct ldb_context *samdb,
142 TALLOC_CTX *mem_ctx,
143 struct ldb_dn *dn,
144 struct dnsp_DnssrvRpcRecord **records,
145 uint16_t *num_records,
146 bool *tombstoned)
148 static const char * const attrs[] = {
149 "dnsRecord",
150 "dNSTombstoned",
151 NULL
153 int ret;
154 WERROR werr;
155 struct ldb_message *msg = NULL;
156 struct ldb_message_element *el;
158 *records = NULL;
159 *num_records = 0;
161 if (tombstoned != NULL) {
162 *tombstoned = false;
163 ret = dsdb_search_one(samdb, mem_ctx, &msg, dn,
164 LDB_SCOPE_BASE, attrs, 0,
165 "(objectClass=dnsNode)");
166 } else {
167 ret = dsdb_search_one(samdb, mem_ctx, &msg, dn,
168 LDB_SCOPE_BASE, attrs, 0,
169 "(&(objectClass=dnsNode)(!(dNSTombstoned=TRUE)))");
171 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
172 return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
174 if (ret != LDB_SUCCESS) {
175 /* TODO: we need to check if there's a glue record we need to
176 * create a referral to */
177 return DNS_ERR(NAME_ERROR);
180 if (tombstoned != NULL) {
181 *tombstoned = ldb_msg_find_attr_as_bool(msg,
182 "dNSTombstoned", false);
185 el = ldb_msg_find_element(msg, "dnsRecord");
186 if (el == NULL) {
187 TALLOC_FREE(msg);
189 * records produced by older Samba releases
190 * keep dnsNode objects without dnsRecord and
191 * without setting dNSTombstoned=TRUE.
193 * We just pretend they're tombstones.
195 if (tombstoned != NULL) {
196 struct dnsp_DnssrvRpcRecord *recs;
197 recs = talloc_array(mem_ctx,
198 struct dnsp_DnssrvRpcRecord,
200 if (recs == NULL) {
201 return WERR_NOT_ENOUGH_MEMORY;
203 recs[0] = (struct dnsp_DnssrvRpcRecord) {
204 .wType = DNS_TYPE_TOMBSTONE,
206 * A value of timestamp != 0
207 * indicated that the object was already
208 * a tombstone, this will be used
209 * in dns_common_replace()
211 .data.timestamp = 1,
214 *tombstoned = true;
215 *records = recs;
216 *num_records = 1;
217 return WERR_OK;
218 } else {
220 * Because we are not looking for a tombstone
221 * in this codepath, we just pretend it does
222 * not exist at all.
224 return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
228 werr = dns_common_extract(samdb, el, mem_ctx, records, num_records);
229 TALLOC_FREE(msg);
230 if (!W_ERROR_IS_OK(werr)) {
231 return werr;
234 return WERR_OK;
238 * Build an ldb_parse_tree node for an equality check
240 * Note: name is assumed to have been validated by dns_name_check
241 * so will be zero terminated and of a reasonable size.
243 static struct ldb_parse_tree *build_equality_operation(
244 TALLOC_CTX *mem_ctx,
245 bool add_asterix, /* prepend an '*' to the name */
246 const uint8_t *name, /* the value being matched */
247 const char *attr, /* the attribute to check name against */
248 size_t size) /* length of name */
251 struct ldb_parse_tree *el = NULL; /* Equality node being built */
252 struct ldb_val *value = NULL; /* Value the attr will be compared
253 with */
254 size_t length = 0; /* calculated length of the value
255 including option '*' prefix and
256 '\0' string terminator */
258 el = talloc(mem_ctx, struct ldb_parse_tree);
259 if (el == NULL) {
260 DBG_ERR("Unable to allocate ldb_parse_tree\n");
261 return NULL;
264 el->operation = LDB_OP_EQUALITY;
265 el->u.equality.attr = talloc_strdup(mem_ctx, attr);
266 value = &el->u.equality.value;
267 length = (add_asterix) ? size + 2 : size + 1;
268 value->data = talloc_zero_array(el, uint8_t, length);
269 if (el == NULL) {
270 DBG_ERR("Unable to allocate value->data\n");
271 TALLOC_FREE(el);
272 return NULL;
275 value->length = length;
276 if (add_asterix) {
277 value->data[0] = '*';
278 memcpy(&value->data[1], name, size);
279 } else {
280 memcpy(value->data, name, size);
282 return el;
286 * Determine the number of levels in name
287 * essentially the number of '.'s in the name + 1
289 * name is assumed to have been validated by dns_name_check
291 static unsigned int number_of_labels(const struct ldb_val *name) {
292 int x = 0;
293 unsigned int labels = 1;
294 for (x = 0; x < name->length; x++) {
295 if (name->data[x] == '.') {
296 labels++;
299 return labels;
302 * Build a query that matches the target name, and any possible
303 * DNS wild card entries
305 * Builds a parse tree equivalent to the example query.
307 * x.y.z -> (|(name=x.y.z)(name=\2a.y.z)(name=\2a.z)(name=\2a))
309 * The attribute 'name' is used as this is what the LDB index is on
310 * (the RDN, being 'dc' in this use case, does not have an index in
311 * the AD schema).
313 * Returns NULL if unable to build the query.
315 * The first component of the DN is assumed to be the name being looked up
316 * and also that it has been validated by dns_name_check
319 #define BASE "(&(objectClass=dnsNode)(!(dNSTombstoned=TRUE))(|(a=b)(c=d)))"
320 static struct ldb_parse_tree *build_wildcard_query(
321 TALLOC_CTX *mem_ctx,
322 struct ldb_dn *dn)
324 const struct ldb_val *name = NULL; /* The DNS name being
325 queried */
326 const char *attr = "name"; /* The attribute name */
327 struct ldb_parse_tree *query = NULL; /* The constructed query
328 parse tree*/
329 struct ldb_parse_tree *wildcard_query = NULL; /* The parse tree for the
330 name and wild card
331 entries */
332 int labels = 0; /* The number of labels in the name */
334 name = ldb_dn_get_rdn_val(dn);
335 if (name == NULL) {
336 DBG_ERR("Unable to get domain name value\n");
337 return NULL;
339 labels = number_of_labels(name);
341 query = ldb_parse_tree(mem_ctx, BASE);
342 if (query == NULL) {
343 DBG_ERR("Unable to parse query %s\n", BASE);
344 return NULL;
348 * The 3rd element of BASE is a place holder which is replaced with
349 * the actual wild card query
351 wildcard_query = query->u.list.elements[2];
352 TALLOC_FREE(wildcard_query->u.list.elements);
354 wildcard_query->u.list.num_elements = labels + 1;
355 wildcard_query->u.list.elements = talloc_array(
356 wildcard_query,
357 struct ldb_parse_tree *,
358 labels + 1);
360 * Build the wild card query
363 int x = 0; /* current character in the name */
364 int l = 0; /* current equality operator index in elements */
365 struct ldb_parse_tree *el = NULL; /* Equality operator being
366 built */
367 bool add_asterix = true; /* prepend an '*' to the value */
368 for (l = 0, x = 0; l < labels && x < name->length; l++) {
369 unsigned int size = name->length - x;
370 add_asterix = (name->data[x] == '.');
371 el = build_equality_operation(
372 mem_ctx,
373 add_asterix,
374 &name->data[x],
375 attr,
376 size);
377 if (el == NULL) {
378 return NULL; /* Reason will have been logged */
380 wildcard_query->u.list.elements[l] = el;
382 /* skip to the start of the next label */
383 for (;x < name->length && name->data[x] != '.'; x++);
386 /* Add the base level "*" only query */
387 el = build_equality_operation(mem_ctx, true, NULL, attr, 0);
388 if (el == NULL) {
389 TALLOC_FREE(query);
390 return NULL; /* Reason will have been logged */
392 wildcard_query->u.list.elements[l] = el;
394 return query;
398 * Scan the list of records matching a dns wildcard query and return the
399 * best match.
401 * The best match is either an exact name match, or the longest wild card
402 * entry returned
404 * i.e. name = a.b.c candidates *.b.c, *.c, - *.b.c would be selected
405 * name = a.b.c candidates a.b.c, *.b.c, *.c - a.b.c would be selected
407 static struct ldb_message *get_best_match(struct ldb_dn *dn,
408 struct ldb_result *result)
410 int matched = 0; /* Index of the current best match in result */
411 size_t length = 0; /* The length of the current candidate */
412 const struct ldb_val *target = NULL; /* value we're looking for */
413 const struct ldb_val *candidate = NULL; /* current candidate value */
414 int x = 0;
416 target = ldb_dn_get_rdn_val(dn);
417 for(x = 0; x < result->count; x++) {
418 candidate = ldb_dn_get_rdn_val(result->msgs[x]->dn);
419 if (strncasecmp((char *) target->data,
420 (char *) candidate->data,
421 target->length) == 0) {
422 /* Exact match stop searching and return */
423 return result->msgs[x];
425 if (candidate->length > length) {
426 matched = x;
427 length = candidate->length;
430 return result->msgs[matched];
434 * Look up a DNS entry, if an exact match does not exist, return the
435 * closest matching DNS wildcard entry if available
437 * Returns: LDB_ERR_NO_SUCH_OBJECT If no matching record exists
438 * LDB_ERR_OPERATIONS_ERROR If the query fails
439 * LDB_SUCCESS If a matching record was retrieved
442 static int dns_wildcard_lookup(struct ldb_context *samdb,
443 TALLOC_CTX *mem_ctx,
444 struct ldb_dn *dn,
445 struct ldb_message **msg)
447 static const char * const attrs[] = {
448 "dnsRecord",
449 "dNSTombstoned",
450 NULL
452 struct ldb_dn *parent = NULL; /* The parent dn */
453 struct ldb_result *result = NULL; /* Results of the search */
454 int ret; /* Return code */
455 struct ldb_parse_tree *query = NULL; /* The query to run */
456 struct ldb_request *request = NULL; /* LDB request for the query op */
457 struct ldb_message *match = NULL; /* the best matching DNS record */
458 TALLOC_CTX *frame = talloc_stackframe();
460 parent = ldb_dn_get_parent(frame, dn);
461 if (parent == NULL) {
462 DBG_ERR("Unable to extract parent from dn\n");
463 TALLOC_FREE(frame);
464 return LDB_ERR_OPERATIONS_ERROR;
467 query = build_wildcard_query(frame, dn);
468 if (query == NULL) {
469 TALLOC_FREE(frame);
470 return LDB_ERR_OPERATIONS_ERROR;
473 result = talloc_zero(mem_ctx, struct ldb_result);
474 if (result == NULL) {
475 TALLOC_FREE(frame);
476 DBG_ERR("Unable to allocate ldb_result\n");
477 return LDB_ERR_OPERATIONS_ERROR;
480 ret = ldb_build_search_req_ex(&request,
481 samdb,
482 frame,
483 parent,
484 LDB_SCOPE_ONELEVEL,
485 query,
486 attrs,
487 NULL,
488 result,
489 ldb_search_default_callback,
490 NULL);
491 if (ret != LDB_SUCCESS) {
492 TALLOC_FREE(frame);
493 DBG_ERR("ldb_build_search_req_ex returned %d\n", ret);
494 return ret;
497 ret = ldb_request(samdb, request);
498 if (ret != LDB_SUCCESS) {
499 TALLOC_FREE(frame);
500 return ret;
503 ret = ldb_wait(request->handle, LDB_WAIT_ALL);
504 if (ret != LDB_SUCCESS) {
505 TALLOC_FREE(frame);
506 return ret;
509 if (result->count == 0) {
510 TALLOC_FREE(frame);
511 return LDB_ERR_NO_SUCH_OBJECT;
514 match = get_best_match(dn, result);
515 if (match == NULL) {
516 TALLOC_FREE(frame);
517 return LDB_ERR_OPERATIONS_ERROR;
520 *msg = talloc_move(mem_ctx, &match);
521 TALLOC_FREE(frame);
522 return LDB_SUCCESS;
526 * Lookup a DNS record, will match DNS wild card records if an exact match
527 * is not found.
529 WERROR dns_common_wildcard_lookup(struct ldb_context *samdb,
530 TALLOC_CTX *mem_ctx,
531 struct ldb_dn *dn,
532 struct dnsp_DnssrvRpcRecord **records,
533 uint16_t *num_records)
535 int ret;
536 WERROR werr;
537 struct ldb_message *msg = NULL;
538 struct ldb_message_element *el = NULL;
539 const struct ldb_val *name = NULL;
541 *records = NULL;
542 *num_records = 0;
544 name = ldb_dn_get_rdn_val(dn);
545 if (name == NULL) {
546 return DNS_ERR(NAME_ERROR);
549 /* Don't look for a wildcard for @ */
550 if (name->length == 1 && name->data[0] == '@') {
551 return dns_common_lookup(samdb,
552 mem_ctx,
554 records,
555 num_records,
556 NULL);
559 werr = dns_name_check(
560 mem_ctx,
561 strlen((const char*)name->data),
562 (const char*) name->data);
563 if (!W_ERROR_IS_OK(werr)) {
564 return werr;
568 * Do a point search first, then fall back to a wildcard
569 * lookup if it does not exist
571 werr = dns_common_lookup(samdb,
572 mem_ctx,
574 records,
575 num_records,
576 NULL);
577 if (!W_ERROR_EQUAL(werr, WERR_DNS_ERROR_NAME_DOES_NOT_EXIST)) {
578 return werr;
581 ret = dns_wildcard_lookup(samdb, mem_ctx, dn, &msg);
582 if (ret == LDB_ERR_OPERATIONS_ERROR) {
583 return DNS_ERR(SERVER_FAILURE);
585 if (ret != LDB_SUCCESS) {
586 return DNS_ERR(NAME_ERROR);
589 el = ldb_msg_find_element(msg, "dnsRecord");
590 if (el == NULL) {
591 return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
594 werr = dns_common_extract(samdb, el, mem_ctx, records, num_records);
595 TALLOC_FREE(msg);
596 if (!W_ERROR_IS_OK(werr)) {
597 return werr;
600 return WERR_OK;
603 static int rec_cmp(const struct dnsp_DnssrvRpcRecord *r1,
604 const struct dnsp_DnssrvRpcRecord *r2)
606 if (r1->wType != r2->wType) {
608 * The records are sorted with higher types first
610 return r2->wType - r1->wType;
614 * Then we need to sort from the oldest to newest timestamp
616 return r1->dwTimeStamp - r2->dwTimeStamp;
620 * Check for valid DNS names. These are names which:
621 * - are non-empty
622 * - do not start with a dot
623 * - do not have any empty labels
624 * - have no more than 127 labels
625 * - are no longer than 253 characters
626 * - none of the labels exceed 63 characters
628 WERROR dns_name_check(TALLOC_CTX *mem_ctx, size_t len, const char *name)
630 size_t i;
631 unsigned int labels = 0;
632 unsigned int label_len = 0;
634 if (len == 0) {
635 return WERR_DS_INVALID_DN_SYNTAX;
638 if (len > 1 && name[0] == '.') {
639 return WERR_DS_INVALID_DN_SYNTAX;
642 if ((len - 1) > DNS_MAX_DOMAIN_LENGTH) {
643 return WERR_DS_INVALID_DN_SYNTAX;
646 for (i = 0; i < len - 1; i++) {
647 if (name[i] == '.' && name[i+1] == '.') {
648 return WERR_DS_INVALID_DN_SYNTAX;
650 if (name[i] == '.') {
651 labels++;
652 if (labels > DNS_MAX_LABELS) {
653 return WERR_DS_INVALID_DN_SYNTAX;
655 label_len = 0;
656 } else {
657 label_len++;
658 if (label_len > DNS_MAX_LABEL_LENGTH) {
659 return WERR_DS_INVALID_DN_SYNTAX;
664 return WERR_OK;
667 static WERROR check_name_list(TALLOC_CTX *mem_ctx, uint16_t rec_count,
668 struct dnsp_DnssrvRpcRecord *records)
670 WERROR werr;
671 uint16_t i;
672 size_t len;
673 struct dnsp_DnssrvRpcRecord record;
675 werr = WERR_OK;
676 for (i = 0; i < rec_count; i++) {
677 record = records[i];
679 switch (record.wType) {
681 case DNS_TYPE_NS:
682 len = strlen(record.data.ns);
683 werr = dns_name_check(mem_ctx, len, record.data.ns);
684 break;
685 case DNS_TYPE_CNAME:
686 len = strlen(record.data.cname);
687 werr = dns_name_check(mem_ctx, len, record.data.cname);
688 break;
689 case DNS_TYPE_SOA:
690 len = strlen(record.data.soa.mname);
691 werr = dns_name_check(mem_ctx, len, record.data.soa.mname);
692 if (!W_ERROR_IS_OK(werr)) {
693 break;
695 len = strlen(record.data.soa.rname);
696 werr = dns_name_check(mem_ctx, len, record.data.soa.rname);
697 break;
698 case DNS_TYPE_PTR:
699 len = strlen(record.data.ptr);
700 werr = dns_name_check(mem_ctx, len, record.data.ptr);
701 break;
702 case DNS_TYPE_MX:
703 len = strlen(record.data.mx.nameTarget);
704 werr = dns_name_check(mem_ctx, len, record.data.mx.nameTarget);
705 break;
706 case DNS_TYPE_SRV:
707 len = strlen(record.data.srv.nameTarget);
708 werr = dns_name_check(mem_ctx, len,
709 record.data.srv.nameTarget);
710 break;
712 * In the default case, the record doesn't have a DN, so it
713 * must be ok.
715 default:
716 break;
719 if (!W_ERROR_IS_OK(werr)) {
720 return werr;
724 return WERR_OK;
727 WERROR dns_get_zone_properties(struct ldb_context *samdb,
728 TALLOC_CTX *mem_ctx,
729 struct ldb_dn *zone_dn,
730 struct dnsserver_zoneinfo *zoneinfo)
733 int ret, i;
734 struct dnsp_DnsProperty *prop = NULL;
735 struct ldb_message_element *element = NULL;
736 const char *const attrs[] = {"dNSProperty", NULL};
737 struct ldb_result *res = NULL;
738 enum ndr_err_code err;
740 ret = ldb_search(samdb,
741 mem_ctx,
742 &res,
743 zone_dn,
744 LDB_SCOPE_BASE,
745 attrs,
746 "(objectClass=dnsZone)");
747 if (ret != LDB_SUCCESS) {
748 DBG_ERR("dnsserver: Failed to find DNS zone: %s\n",
749 ldb_dn_get_linearized(zone_dn));
750 return DNS_ERR(SERVER_FAILURE);
753 element = ldb_msg_find_element(res->msgs[0], "dNSProperty");
754 if (element == NULL) {
755 return DNS_ERR(NOTZONE);
758 for (i = 0; i < element->num_values; i++) {
759 prop = talloc_zero(mem_ctx, struct dnsp_DnsProperty);
760 if (prop == NULL) {
761 return WERR_NOT_ENOUGH_MEMORY;
763 err = ndr_pull_struct_blob(
764 &(element->values[i]),
765 mem_ctx,
766 prop,
767 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnsProperty);
768 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
769 return DNS_ERR(SERVER_FAILURE);
772 switch (prop->id) {
773 case DSPROPERTY_ZONE_AGING_STATE:
774 zoneinfo->fAging = prop->data.aging_enabled;
775 break;
776 case DSPROPERTY_ZONE_NOREFRESH_INTERVAL:
777 zoneinfo->dwNoRefreshInterval =
778 prop->data.norefresh_hours;
779 break;
780 case DSPROPERTY_ZONE_REFRESH_INTERVAL:
781 zoneinfo->dwRefreshInterval = prop->data.refresh_hours;
782 break;
783 case DSPROPERTY_ZONE_ALLOW_UPDATE:
784 zoneinfo->fAllowUpdate = prop->data.allow_update_flag;
785 break;
786 case DSPROPERTY_ZONE_AGING_ENABLED_TIME:
787 zoneinfo->dwAvailForScavengeTime =
788 prop->data.next_scavenging_cycle_hours;
789 break;
790 case DSPROPERTY_ZONE_SCAVENGING_SERVERS:
791 zoneinfo->aipScavengeServers->AddrCount =
792 prop->data.servers.addrCount;
793 zoneinfo->aipScavengeServers->AddrArray =
794 prop->data.servers.addr;
795 break;
796 case DSPROPERTY_ZONE_EMPTY:
797 case DSPROPERTY_ZONE_TYPE:
798 case DSPROPERTY_ZONE_SECURE_TIME:
799 case DSPROPERTY_ZONE_DELETED_FROM_HOSTNAME:
800 case DSPROPERTY_ZONE_MASTER_SERVERS:
801 case DSPROPERTY_ZONE_AUTO_NS_SERVERS:
802 case DSPROPERTY_ZONE_DCPROMO_CONVERT:
803 case DSPROPERTY_ZONE_SCAVENGING_SERVERS_DA:
804 case DSPROPERTY_ZONE_MASTER_SERVERS_DA:
805 case DSPROPERTY_ZONE_NS_SERVERS_DA:
806 case DSPROPERTY_ZONE_NODE_DBFLAGS:
807 break;
811 return WERR_OK;
814 WERROR dns_common_replace(struct ldb_context *samdb,
815 TALLOC_CTX *mem_ctx,
816 struct ldb_dn *dn,
817 bool needs_add,
818 uint32_t serial,
819 struct dnsp_DnssrvRpcRecord *records,
820 uint16_t rec_count)
822 struct ldb_message_element *el;
823 uint16_t i;
824 int ret;
825 WERROR werr;
826 struct ldb_message *msg = NULL;
827 bool was_tombstoned = false;
828 bool become_tombstoned = false;
829 struct ldb_dn *zone_dn = NULL;
830 struct dnsserver_zoneinfo *zoneinfo = NULL;
831 NTTIME t;
833 msg = ldb_msg_new(mem_ctx);
834 W_ERROR_HAVE_NO_MEMORY(msg);
836 msg->dn = dn;
838 zone_dn = ldb_dn_copy(mem_ctx, dn);
839 if (zone_dn == NULL) {
840 return WERR_NOT_ENOUGH_MEMORY;
842 if (!ldb_dn_remove_child_components(zone_dn, 1)) {
843 return DNS_ERR(SERVER_FAILURE);
845 zoneinfo = talloc(mem_ctx, struct dnsserver_zoneinfo);
846 if (zoneinfo == NULL) {
847 return WERR_NOT_ENOUGH_MEMORY;
849 werr = dns_get_zone_properties(samdb, mem_ctx, zone_dn, zoneinfo);
850 if (W_ERROR_EQUAL(DNS_ERR(NOTZONE), werr)) {
852 * We only got zoneinfo for aging so if we didn't find any
853 * properties then just disable aging and keep going.
855 zoneinfo->fAging = 0;
856 } else if (!W_ERROR_IS_OK(werr)) {
857 return werr;
860 werr = check_name_list(mem_ctx, rec_count, records);
861 if (!W_ERROR_IS_OK(werr)) {
862 return werr;
865 ret = ldb_msg_add_empty(msg, "dnsRecord", LDB_FLAG_MOD_REPLACE, &el);
866 if (ret != LDB_SUCCESS) {
867 return DNS_ERR(SERVER_FAILURE);
871 * we have at least one value,
872 * which might be used for the tombstone marker
874 el->values = talloc_zero_array(el, struct ldb_val, MAX(1, rec_count));
875 if (rec_count > 0) {
876 W_ERROR_HAVE_NO_MEMORY(el->values);
879 * We store a sorted list with the high wType values first
880 * that's what windows does. It also simplifies the
881 * filtering of DNS_TYPE_TOMBSTONE records
883 TYPESAFE_QSORT(records, rec_count, rec_cmp);
886 for (i = 0; i < rec_count; i++) {
887 struct ldb_val *v = &el->values[el->num_values];
888 enum ndr_err_code ndr_err;
890 if (records[i].wType == DNS_TYPE_TOMBSTONE) {
891 if (records[i].data.timestamp != 0) {
892 was_tombstoned = true;
894 continue;
897 if (zoneinfo->fAging == 1 && records[i].dwTimeStamp != 0) {
898 unix_to_nt_time(&t, time(NULL));
899 t /= 10 * 1000 * 1000;
900 t /= 3600;
901 if (t - records[i].dwTimeStamp >
902 zoneinfo->dwNoRefreshInterval) {
903 records[i].dwTimeStamp = t;
907 records[i].dwSerial = serial;
908 ndr_err = ndr_push_struct_blob(v, el->values, &records[i],
909 (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
910 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
911 DEBUG(0, ("Failed to push dnsp_DnssrvRpcRecord\n"));
912 return DNS_ERR(SERVER_FAILURE);
914 el->num_values++;
917 if (needs_add) {
918 if (el->num_values == 0) {
919 return WERR_OK;
922 ret = ldb_msg_add_string(msg, "objectClass", "dnsNode");
923 if (ret != LDB_SUCCESS) {
924 return DNS_ERR(SERVER_FAILURE);
927 ret = ldb_add(samdb, msg);
928 if (ret != LDB_SUCCESS) {
929 return DNS_ERR(SERVER_FAILURE);
932 return WERR_OK;
935 if (el->num_values == 0) {
936 struct dnsp_DnssrvRpcRecord tbs;
937 struct ldb_val *v = &el->values[el->num_values];
938 enum ndr_err_code ndr_err;
939 struct timeval tv;
941 if (was_tombstoned) {
943 * This is already a tombstoned object.
944 * Just leave it instead of updating the time stamp.
946 return WERR_OK;
949 tv = timeval_current();
950 tbs = (struct dnsp_DnssrvRpcRecord) {
951 .wType = DNS_TYPE_TOMBSTONE,
952 .dwSerial = serial,
953 .data.timestamp = timeval_to_nttime(&tv),
956 ndr_err = ndr_push_struct_blob(v, el->values, &tbs,
957 (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
958 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
959 DEBUG(0, ("Failed to push dnsp_DnssrvRpcRecord\n"));
960 return DNS_ERR(SERVER_FAILURE);
962 el->num_values++;
964 become_tombstoned = true;
967 if (was_tombstoned || become_tombstoned) {
968 ret = ldb_msg_add_empty(msg, "dNSTombstoned",
969 LDB_FLAG_MOD_REPLACE, NULL);
970 if (ret != LDB_SUCCESS) {
971 return DNS_ERR(SERVER_FAILURE);
974 ret = ldb_msg_add_fmt(msg, "dNSTombstoned", "%s",
975 become_tombstoned ? "TRUE" : "FALSE");
976 if (ret != LDB_SUCCESS) {
977 return DNS_ERR(SERVER_FAILURE);
981 ret = ldb_modify(samdb, msg);
982 if (ret != LDB_SUCCESS) {
983 NTSTATUS nt = dsdb_ldb_err_to_ntstatus(ret);
984 return ntstatus_to_werror(nt);
987 return WERR_OK;
990 bool dns_name_match(const char *zone, const char *name, size_t *host_part_len)
992 size_t zl = strlen(zone);
993 size_t nl = strlen(name);
994 ssize_t zi, ni;
995 static const size_t fixup = 'a' - 'A';
997 if (zl > nl) {
998 return false;
1001 for (zi = zl, ni = nl; zi >= 0; zi--, ni--) {
1002 char zc = zone[zi];
1003 char nc = name[ni];
1005 /* convert to lower case */
1006 if (zc >= 'A' && zc <= 'Z') {
1007 zc += fixup;
1009 if (nc >= 'A' && nc <= 'Z') {
1010 nc += fixup;
1013 if (zc != nc) {
1014 return false;
1018 if (ni >= 0) {
1019 if (name[ni] != '.') {
1020 return false;
1023 ni--;
1026 *host_part_len = ni+1;
1028 return true;
1031 WERROR dns_common_name2dn(struct ldb_context *samdb,
1032 struct dns_server_zone *zones,
1033 TALLOC_CTX *mem_ctx,
1034 const char *name,
1035 struct ldb_dn **_dn)
1037 struct ldb_dn *base;
1038 struct ldb_dn *dn;
1039 const struct dns_server_zone *z;
1040 size_t host_part_len = 0;
1041 WERROR werr;
1043 if (name == NULL) {
1044 return DNS_ERR(FORMAT_ERROR);
1047 if (strcmp(name, "") == 0) {
1048 base = ldb_get_default_basedn(samdb);
1049 dn = ldb_dn_copy(mem_ctx, base);
1050 ldb_dn_add_child_fmt(dn, "DC=@,DC=RootDNSServers,CN=MicrosoftDNS,CN=System");
1051 *_dn = dn;
1052 return WERR_OK;
1055 /* Check non-empty names */
1056 werr = dns_name_check(mem_ctx, strlen(name), name);
1057 if (!W_ERROR_IS_OK(werr)) {
1058 return werr;
1061 for (z = zones; z != NULL; z = z->next) {
1062 bool match;
1064 match = dns_name_match(z->name, name, &host_part_len);
1065 if (match) {
1066 break;
1070 if (z == NULL) {
1071 return DNS_ERR(NAME_ERROR);
1074 if (host_part_len == 0) {
1075 dn = ldb_dn_copy(mem_ctx, z->dn);
1076 ldb_dn_add_child_fmt(dn, "DC=@");
1077 *_dn = dn;
1078 return WERR_OK;
1081 dn = ldb_dn_copy(mem_ctx, z->dn);
1082 ldb_dn_add_child_fmt(dn, "DC=%*.*s", (int)host_part_len, (int)host_part_len, name);
1083 *_dn = dn;
1084 return WERR_OK;
1087 static int dns_common_sort_zones(struct ldb_message **m1, struct ldb_message **m2)
1089 const char *n1, *n2;
1090 size_t l1, l2;
1092 n1 = ldb_msg_find_attr_as_string(*m1, "name", NULL);
1093 n2 = ldb_msg_find_attr_as_string(*m2, "name", NULL);
1095 l1 = strlen(n1);
1096 l2 = strlen(n2);
1098 /* If the string lengths are not equal just sort by length */
1099 if (l1 != l2) {
1100 /* If m1 is the larger zone name, return it first */
1101 return l2 - l1;
1104 /*TODO: We need to compare DNs here, we want the DomainDNSZones first */
1105 return 0;
1108 NTSTATUS dns_common_zones(struct ldb_context *samdb,
1109 TALLOC_CTX *mem_ctx,
1110 struct ldb_dn *base_dn,
1111 struct dns_server_zone **zones_ret)
1113 int ret;
1114 static const char * const attrs[] = { "name", NULL};
1115 struct ldb_result *res;
1116 int i;
1117 struct dns_server_zone *new_list = NULL;
1118 TALLOC_CTX *frame = talloc_stackframe();
1120 if (base_dn) {
1121 /* This search will work against windows */
1122 ret = dsdb_search(samdb, frame, &res,
1123 base_dn, LDB_SCOPE_SUBTREE,
1124 attrs, 0, "(objectClass=dnsZone)");
1125 } else {
1126 /* TODO: this search does not work against windows */
1127 ret = dsdb_search(samdb, frame, &res, NULL,
1128 LDB_SCOPE_SUBTREE,
1129 attrs,
1130 DSDB_SEARCH_SEARCH_ALL_PARTITIONS,
1131 "(objectClass=dnsZone)");
1133 if (ret != LDB_SUCCESS) {
1134 TALLOC_FREE(frame);
1135 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1138 TYPESAFE_QSORT(res->msgs, res->count, dns_common_sort_zones);
1140 for (i=0; i < res->count; i++) {
1141 struct dns_server_zone *z;
1143 z = talloc_zero(mem_ctx, struct dns_server_zone);
1144 if (z == NULL) {
1145 TALLOC_FREE(frame);
1146 return NT_STATUS_NO_MEMORY;
1149 z->name = ldb_msg_find_attr_as_string(res->msgs[i], "name", NULL);
1150 talloc_steal(z, z->name);
1151 z->dn = talloc_move(z, &res->msgs[i]->dn);
1153 * Ignore the RootDNSServers zone and zones that we don't support yet
1154 * RootDNSServers should never be returned (Windows DNS server don't)
1155 * ..TrustAnchors should never be returned as is, (Windows returns
1156 * TrustAnchors) and for the moment we don't support DNSSEC so we'd better
1157 * not return this zone.
1159 if ((strcmp(z->name, "RootDNSServers") == 0) ||
1160 (strcmp(z->name, "..TrustAnchors") == 0))
1162 DEBUG(10, ("Ignoring zone %s\n", z->name));
1163 talloc_free(z);
1164 continue;
1166 DLIST_ADD_END(new_list, z);
1169 *zones_ret = new_list;
1170 TALLOC_FREE(frame);
1171 return NT_STATUS_OK;
1175 see if two DNS names are the same
1177 bool dns_name_equal(const char *name1, const char *name2)
1179 size_t len1 = strlen(name1);
1180 size_t len2 = strlen(name2);
1182 if (len1 > 0 && name1[len1 - 1] == '.') {
1183 len1--;
1185 if (len2 > 0 && name2[len2 - 1] == '.') {
1186 len2--;
1188 if (len1 != len2) {
1189 return false;
1191 return strncasecmp(name1, name2, len1) == 0;