s3:winbind: Remove no longer used domain's private_data pointer
[Samba.git] / source4 / dns_server / dnsserver_common.c
blob71aeee8169dc0264065b1052e29292b651df900d
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"
36 #include "system/network.h"
38 #undef DBGC_CLASS
39 #define DBGC_CLASS DBGC_DNS
41 #undef strncasecmp
43 uint8_t werr_to_dns_err(WERROR werr)
45 if (W_ERROR_EQUAL(WERR_OK, werr)) {
46 return DNS_RCODE_OK;
47 } else if (W_ERROR_EQUAL(DNS_ERR(FORMAT_ERROR), werr)) {
48 return DNS_RCODE_FORMERR;
49 } else if (W_ERROR_EQUAL(DNS_ERR(SERVER_FAILURE), werr)) {
50 return DNS_RCODE_SERVFAIL;
51 } else if (W_ERROR_EQUAL(DNS_ERR(NAME_ERROR), werr)) {
52 return DNS_RCODE_NXDOMAIN;
53 } else if (W_ERROR_EQUAL(WERR_DNS_ERROR_NAME_DOES_NOT_EXIST, werr)) {
54 return DNS_RCODE_NXDOMAIN;
55 } else if (W_ERROR_EQUAL(DNS_ERR(NOT_IMPLEMENTED), werr)) {
56 return DNS_RCODE_NOTIMP;
57 } else if (W_ERROR_EQUAL(DNS_ERR(REFUSED), werr)) {
58 return DNS_RCODE_REFUSED;
59 } else if (W_ERROR_EQUAL(DNS_ERR(YXDOMAIN), werr)) {
60 return DNS_RCODE_YXDOMAIN;
61 } else if (W_ERROR_EQUAL(DNS_ERR(YXRRSET), werr)) {
62 return DNS_RCODE_YXRRSET;
63 } else if (W_ERROR_EQUAL(DNS_ERR(NXRRSET), werr)) {
64 return DNS_RCODE_NXRRSET;
65 } else if (W_ERROR_EQUAL(DNS_ERR(NOTAUTH), werr)) {
66 return DNS_RCODE_NOTAUTH;
67 } else if (W_ERROR_EQUAL(DNS_ERR(NOTZONE), werr)) {
68 return DNS_RCODE_NOTZONE;
69 } else if (W_ERROR_EQUAL(DNS_ERR(BADKEY), werr)) {
70 return DNS_RCODE_BADKEY;
72 DEBUG(5, ("No mapping exists for %s\n", win_errstr(werr)));
73 return DNS_RCODE_SERVFAIL;
76 WERROR dns_common_extract(struct ldb_context *samdb,
77 const struct ldb_message_element *el,
78 TALLOC_CTX *mem_ctx,
79 struct dnsp_DnssrvRpcRecord **records,
80 uint16_t *num_records)
82 uint16_t ri;
83 struct dnsp_DnssrvRpcRecord *recs;
85 *records = NULL;
86 *num_records = 0;
88 recs = talloc_zero_array(mem_ctx, struct dnsp_DnssrvRpcRecord,
89 el->num_values);
90 if (recs == NULL) {
91 return WERR_NOT_ENOUGH_MEMORY;
93 for (ri = 0; ri < el->num_values; ri++) {
94 bool am_rodc;
95 int ret;
96 const char *dnsHostName = NULL;
97 struct ldb_val *v = &el->values[ri];
98 enum ndr_err_code ndr_err;
99 ndr_err = ndr_pull_struct_blob(v, recs, &recs[ri],
100 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
101 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
102 TALLOC_FREE(recs);
103 DEBUG(0, ("Failed to grab dnsp_DnssrvRpcRecord\n"));
104 return DNS_ERR(SERVER_FAILURE);
108 * In AD, except on an RODC (where we should list a random RWDC,
109 * we should over-stamp the MNAME with our own hostname
111 if (recs[ri].wType != DNS_TYPE_SOA) {
112 continue;
115 ret = samdb_rodc(samdb, &am_rodc);
116 if (ret != LDB_SUCCESS) {
117 DEBUG(0, ("Failed to confirm we are not an RODC: %s\n",
118 ldb_errstring(samdb)));
119 return DNS_ERR(SERVER_FAILURE);
122 if (am_rodc) {
123 continue;
126 ret = samdb_dns_host_name(samdb, &dnsHostName);
127 if (ret != LDB_SUCCESS || dnsHostName == NULL) {
128 DEBUG(0, ("Failed to get dnsHostName from rootDSE"));
129 return DNS_ERR(SERVER_FAILURE);
132 recs[ri].data.soa.mname = talloc_strdup(recs, dnsHostName);
135 *records = recs;
136 *num_records = el->num_values;
137 return WERR_OK;
141 * Lookup a DNS record, performing an exact match.
142 * i.e. DNS wild card records are not considered.
144 WERROR dns_common_lookup(struct ldb_context *samdb,
145 TALLOC_CTX *mem_ctx,
146 struct ldb_dn *dn,
147 struct dnsp_DnssrvRpcRecord **records,
148 uint16_t *num_records,
149 bool *tombstoned)
151 const struct timeval start = timeval_current();
152 static const char * const attrs[] = {
153 "dnsRecord",
154 "dNSTombstoned",
155 NULL
157 int ret;
158 WERROR werr = WERR_OK;
159 struct ldb_message *msg = NULL;
160 struct ldb_message_element *el;
162 *records = NULL;
163 *num_records = 0;
165 if (tombstoned != NULL) {
166 *tombstoned = false;
167 ret = dsdb_search_one(samdb, mem_ctx, &msg, dn,
168 LDB_SCOPE_BASE, attrs, 0,
169 "(objectClass=dnsNode)");
170 } else {
171 ret = dsdb_search_one(samdb, mem_ctx, &msg, dn,
172 LDB_SCOPE_BASE, attrs, 0,
173 "(&(objectClass=dnsNode)(!(dNSTombstoned=TRUE)))");
175 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
176 werr = WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
177 goto exit;
179 if (ret != LDB_SUCCESS) {
180 /* TODO: we need to check if there's a glue record we need to
181 * create a referral to */
182 werr = DNS_ERR(NAME_ERROR);
183 goto exit;
186 if (tombstoned != NULL) {
187 *tombstoned = ldb_msg_find_attr_as_bool(msg,
188 "dNSTombstoned", false);
191 el = ldb_msg_find_element(msg, "dnsRecord");
192 if (el == NULL) {
193 TALLOC_FREE(msg);
195 * records produced by older Samba releases
196 * keep dnsNode objects without dnsRecord and
197 * without setting dNSTombstoned=TRUE.
199 * We just pretend they're tombstones.
201 if (tombstoned != NULL) {
202 struct dnsp_DnssrvRpcRecord *recs;
203 recs = talloc_array(mem_ctx,
204 struct dnsp_DnssrvRpcRecord,
206 if (recs == NULL) {
207 werr = WERR_NOT_ENOUGH_MEMORY;
208 goto exit;
210 recs[0] = (struct dnsp_DnssrvRpcRecord) {
211 .wType = DNS_TYPE_TOMBSTONE,
213 * A value of timestamp != 0
214 * indicated that the object was already
215 * a tombstone, this will be used
216 * in dns_common_replace()
218 .data.EntombedTime = 1,
221 *tombstoned = true;
222 *records = recs;
223 *num_records = 1;
224 werr = WERR_OK;
225 goto exit;
226 } else {
228 * Because we are not looking for a tombstone
229 * in this codepath, we just pretend it does
230 * not exist at all.
232 werr = WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
233 goto exit;
237 werr = dns_common_extract(samdb, el, mem_ctx, records, num_records);
238 TALLOC_FREE(msg);
239 if (!W_ERROR_IS_OK(werr)) {
240 goto exit;
243 werr = WERR_OK;
244 exit:
245 DNS_COMMON_LOG_OPERATION(
246 win_errstr(werr),
247 &start,
248 NULL,
249 dn == NULL ? NULL : ldb_dn_get_linearized(dn),
250 NULL);
251 return werr;
255 * Build an ldb_parse_tree node for an equality check
257 * Note: name is assumed to have been validated by dns_name_check
258 * so will be zero terminated and of a reasonable size.
260 static struct ldb_parse_tree *build_equality_operation(
261 TALLOC_CTX *mem_ctx,
262 bool add_asterix, /* prepend an '*' to the name */
263 const uint8_t *name, /* the value being matched */
264 const char *attr, /* the attribute to check name against */
265 size_t size) /* length of name */
268 struct ldb_parse_tree *el = NULL; /* Equality node being built */
269 struct ldb_val *value = NULL; /* Value the attr will be compared
270 with */
271 size_t length = 0; /* calculated length of the value
272 including option '*' prefix and
273 '\0' string terminator */
275 el = talloc(mem_ctx, struct ldb_parse_tree);
276 if (el == NULL) {
277 DBG_ERR("Unable to allocate ldb_parse_tree\n");
278 return NULL;
281 el->operation = LDB_OP_EQUALITY;
282 el->u.equality.attr = talloc_strdup(mem_ctx, attr);
283 value = &el->u.equality.value;
284 length = (add_asterix) ? size + 2 : size + 1;
285 value->data = talloc_zero_array(el, uint8_t, length);
286 if (value->data == NULL) {
287 DBG_ERR("Unable to allocate value->data\n");
288 TALLOC_FREE(el);
289 return NULL;
292 value->length = length;
293 if (add_asterix) {
294 value->data[0] = '*';
295 if (name != NULL) {
296 memcpy(&value->data[1], name, size);
298 } else if (name != NULL) {
299 memcpy(value->data, name, size);
301 return el;
305 * Determine the number of levels in name
306 * essentially the number of '.'s in the name + 1
308 * name is assumed to have been validated by dns_name_check
310 static unsigned int number_of_labels(const struct ldb_val *name) {
311 int x = 0;
312 unsigned int labels = 1;
313 for (x = 0; x < name->length; x++) {
314 if (name->data[x] == '.') {
315 labels++;
318 return labels;
321 * Build a query that matches the target name, and any possible
322 * DNS wild card entries
324 * Builds a parse tree equivalent to the example query.
326 * x.y.z -> (|(name=x.y.z)(name=\2a.y.z)(name=\2a.z)(name=\2a))
328 * The attribute 'name' is used as this is what the LDB index is on
329 * (the RDN, being 'dc' in this use case, does not have an index in
330 * the AD schema).
332 * Returns NULL if unable to build the query.
334 * The first component of the DN is assumed to be the name being looked up
335 * and also that it has been validated by dns_name_check
338 #define BASE "(&(objectClass=dnsNode)(!(dNSTombstoned=TRUE))(|(a=b)(c=d)))"
339 static struct ldb_parse_tree *build_wildcard_query(
340 TALLOC_CTX *mem_ctx,
341 struct ldb_dn *dn)
343 const struct ldb_val *name = NULL; /* The DNS name being
344 queried */
345 const char *attr = "name"; /* The attribute name */
346 struct ldb_parse_tree *query = NULL; /* The constructed query
347 parse tree*/
348 struct ldb_parse_tree *wildcard_query = NULL; /* The parse tree for the
349 name and wild card
350 entries */
351 int labels = 0; /* The number of labels in the name */
353 name = ldb_dn_get_rdn_val(dn);
354 if (name == NULL) {
355 DBG_ERR("Unable to get domain name value\n");
356 return NULL;
358 labels = number_of_labels(name);
360 query = ldb_parse_tree(mem_ctx, BASE);
361 if (query == NULL) {
362 DBG_ERR("Unable to parse query %s\n", BASE);
363 return NULL;
367 * The 3rd element of BASE is a place holder which is replaced with
368 * the actual wild card query
370 wildcard_query = query->u.list.elements[2];
371 TALLOC_FREE(wildcard_query->u.list.elements);
373 wildcard_query->u.list.num_elements = labels + 1;
374 wildcard_query->u.list.elements = talloc_array(
375 wildcard_query,
376 struct ldb_parse_tree *,
377 labels + 1);
379 * Build the wild card query
382 int x = 0; /* current character in the name */
383 int l = 0; /* current equality operator index in elements */
384 struct ldb_parse_tree *el = NULL; /* Equality operator being
385 built */
386 bool add_asterix = true; /* prepend an '*' to the value */
387 for (l = 0, x = 0; l < labels && x < name->length; l++) {
388 unsigned int size = name->length - x;
389 add_asterix = (name->data[x] == '.');
390 el = build_equality_operation(
391 mem_ctx,
392 add_asterix,
393 &name->data[x],
394 attr,
395 size);
396 if (el == NULL) {
397 return NULL; /* Reason will have been logged */
399 wildcard_query->u.list.elements[l] = el;
401 /* skip to the start of the next label */
402 x++;
403 for (;x < name->length && name->data[x] != '.'; x++);
406 /* Add the base level "*" only query */
407 el = build_equality_operation(mem_ctx, true, NULL, attr, 0);
408 if (el == NULL) {
409 TALLOC_FREE(query);
410 return NULL; /* Reason will have been logged */
412 wildcard_query->u.list.elements[l] = el;
414 return query;
418 * Scan the list of records matching a dns wildcard query and return the
419 * best match.
421 * The best match is either an exact name match, or the longest wild card
422 * entry returned
424 * i.e. name = a.b.c candidates *.b.c, *.c, - *.b.c would be selected
425 * name = a.b.c candidates a.b.c, *.b.c, *.c - a.b.c would be selected
427 static struct ldb_message *get_best_match(struct ldb_dn *dn,
428 struct ldb_result *result)
430 int matched = 0; /* Index of the current best match in result */
431 size_t length = 0; /* The length of the current candidate */
432 const struct ldb_val *target = NULL; /* value we're looking for */
433 const struct ldb_val *candidate = NULL; /* current candidate value */
434 int x = 0;
436 target = ldb_dn_get_rdn_val(dn);
437 for(x = 0; x < result->count; x++) {
438 candidate = ldb_dn_get_rdn_val(result->msgs[x]->dn);
439 if (strncasecmp((char *) target->data,
440 (char *) candidate->data,
441 target->length) == 0) {
442 /* Exact match stop searching and return */
443 return result->msgs[x];
445 if (candidate->length > length) {
446 matched = x;
447 length = candidate->length;
450 return result->msgs[matched];
454 * Look up a DNS entry, if an exact match does not exist, return the
455 * closest matching DNS wildcard entry if available
457 * Returns: LDB_ERR_NO_SUCH_OBJECT If no matching record exists
458 * LDB_ERR_OPERATIONS_ERROR If the query fails
459 * LDB_SUCCESS If a matching record was retrieved
462 static int dns_wildcard_lookup(struct ldb_context *samdb,
463 TALLOC_CTX *mem_ctx,
464 struct ldb_dn *dn,
465 struct ldb_message **msg)
467 static const char * const attrs[] = {
468 "dnsRecord",
469 "dNSTombstoned",
470 NULL
472 struct ldb_dn *parent = NULL; /* The parent dn */
473 struct ldb_result *result = NULL; /* Results of the search */
474 int ret; /* Return code */
475 struct ldb_parse_tree *query = NULL; /* The query to run */
476 struct ldb_request *request = NULL; /* LDB request for the query op */
477 struct ldb_message *match = NULL; /* the best matching DNS record */
478 TALLOC_CTX *frame = talloc_stackframe();
480 parent = ldb_dn_get_parent(frame, dn);
481 if (parent == NULL) {
482 DBG_ERR("Unable to extract parent from dn\n");
483 TALLOC_FREE(frame);
484 return LDB_ERR_OPERATIONS_ERROR;
487 query = build_wildcard_query(frame, dn);
488 if (query == NULL) {
489 TALLOC_FREE(frame);
490 return LDB_ERR_OPERATIONS_ERROR;
493 result = talloc_zero(mem_ctx, struct ldb_result);
494 if (result == NULL) {
495 TALLOC_FREE(frame);
496 DBG_ERR("Unable to allocate ldb_result\n");
497 return LDB_ERR_OPERATIONS_ERROR;
500 ret = ldb_build_search_req_ex(&request,
501 samdb,
502 frame,
503 parent,
504 LDB_SCOPE_SUBTREE,
505 query,
506 attrs,
507 NULL,
508 result,
509 ldb_search_default_callback,
510 NULL);
511 if (ret != LDB_SUCCESS) {
512 TALLOC_FREE(frame);
513 DBG_ERR("ldb_build_search_req_ex returned %d\n", ret);
514 return ret;
517 ret = ldb_request(samdb, request);
518 if (ret != LDB_SUCCESS) {
519 TALLOC_FREE(frame);
520 return ret;
523 ret = ldb_wait(request->handle, LDB_WAIT_ALL);
524 if (ret != LDB_SUCCESS) {
525 TALLOC_FREE(frame);
526 return ret;
529 if (result->count == 0) {
530 TALLOC_FREE(frame);
531 return LDB_ERR_NO_SUCH_OBJECT;
534 match = get_best_match(dn, result);
535 if (match == NULL) {
536 TALLOC_FREE(frame);
537 return LDB_ERR_OPERATIONS_ERROR;
540 *msg = talloc_move(mem_ctx, &match);
541 TALLOC_FREE(frame);
542 return LDB_SUCCESS;
546 * Lookup a DNS record, will match DNS wild card records if an exact match
547 * is not found.
549 WERROR dns_common_wildcard_lookup(struct ldb_context *samdb,
550 TALLOC_CTX *mem_ctx,
551 struct ldb_dn *dn,
552 struct dnsp_DnssrvRpcRecord **records,
553 uint16_t *num_records)
555 const struct timeval start = timeval_current();
556 int ret;
557 WERROR werr = WERR_OK;
558 struct ldb_message *msg = NULL;
559 struct ldb_message_element *el = NULL;
560 const struct ldb_val *name = NULL;
562 *records = NULL;
563 *num_records = 0;
565 name = ldb_dn_get_rdn_val(dn);
566 if (name == NULL) {
567 werr = DNS_ERR(NAME_ERROR);
568 goto exit;
571 /* Don't look for a wildcard for @ */
572 if (name->length == 1 && name->data[0] == '@') {
573 werr = dns_common_lookup(samdb,
574 mem_ctx,
576 records,
577 num_records,
578 NULL);
579 goto exit;
582 werr = dns_name_check(
583 mem_ctx,
584 strlen((const char*)name->data),
585 (const char*) name->data);
586 if (!W_ERROR_IS_OK(werr)) {
587 goto exit;
591 * Do a point search first, then fall back to a wildcard
592 * lookup if it does not exist
594 werr = dns_common_lookup(samdb,
595 mem_ctx,
597 records,
598 num_records,
599 NULL);
600 if (!W_ERROR_EQUAL(werr, WERR_DNS_ERROR_NAME_DOES_NOT_EXIST)) {
601 goto exit;
604 ret = dns_wildcard_lookup(samdb, mem_ctx, dn, &msg);
605 if (ret == LDB_ERR_OPERATIONS_ERROR) {
606 werr = DNS_ERR(SERVER_FAILURE);
607 goto exit;
609 if (ret != LDB_SUCCESS) {
610 werr = DNS_ERR(NAME_ERROR);
611 goto exit;
614 el = ldb_msg_find_element(msg, "dnsRecord");
615 if (el == NULL) {
616 werr = WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
617 goto exit;
620 werr = dns_common_extract(samdb, el, mem_ctx, records, num_records);
621 TALLOC_FREE(msg);
622 if (!W_ERROR_IS_OK(werr)) {
623 goto exit;
626 werr = WERR_OK;
627 exit:
628 DNS_COMMON_LOG_OPERATION(
629 win_errstr(werr),
630 &start,
631 NULL,
632 dn == NULL ? NULL : ldb_dn_get_linearized(dn),
633 NULL);
634 return werr;
637 static int rec_cmp(const struct dnsp_DnssrvRpcRecord *r1,
638 const struct dnsp_DnssrvRpcRecord *r2)
640 if (r1->wType != r2->wType) {
642 * The records are sorted with higher types first,
643 * which puts tombstones (type 0) last.
645 return r2->wType - r1->wType;
648 * Then we need to sort from the oldest to newest timestamp.
650 * Note that dwTimeStamp == 0 (never expiring) records come first,
651 * then the ones whose expiry is soonest.
653 return r1->dwTimeStamp - r2->dwTimeStamp;
657 * Check for valid DNS names. These are names which:
658 * - are non-empty
659 * - do not start with a dot
660 * - do not have any empty labels
661 * - have no more than 127 labels
662 * - are no longer than 253 characters
663 * - none of the labels exceed 63 characters
665 WERROR dns_name_check(TALLOC_CTX *mem_ctx, size_t len, const char *name)
667 size_t i;
668 unsigned int labels = 0;
669 unsigned int label_len = 0;
671 if (len == 0) {
672 return WERR_DS_INVALID_DN_SYNTAX;
675 if (len > 1 && name[0] == '.') {
676 return WERR_DS_INVALID_DN_SYNTAX;
679 if ((len - 1) > DNS_MAX_DOMAIN_LENGTH) {
680 return WERR_DS_INVALID_DN_SYNTAX;
683 for (i = 0; i < len - 1; i++) {
684 if (name[i] == '.' && name[i+1] == '.') {
685 return WERR_DS_INVALID_DN_SYNTAX;
687 if (name[i] == '.') {
688 labels++;
689 if (labels > DNS_MAX_LABELS) {
690 return WERR_DS_INVALID_DN_SYNTAX;
692 label_len = 0;
693 } else {
694 label_len++;
695 if (label_len > DNS_MAX_LABEL_LENGTH) {
696 return WERR_DS_INVALID_DN_SYNTAX;
701 return WERR_OK;
704 static WERROR check_name_list(TALLOC_CTX *mem_ctx, uint16_t rec_count,
705 struct dnsp_DnssrvRpcRecord *records)
707 WERROR werr;
708 uint16_t i;
709 size_t len;
710 struct dnsp_DnssrvRpcRecord record;
712 werr = WERR_OK;
713 for (i = 0; i < rec_count; i++) {
714 record = records[i];
716 switch (record.wType) {
718 case DNS_TYPE_NS:
719 len = strlen(record.data.ns);
720 werr = dns_name_check(mem_ctx, len, record.data.ns);
721 break;
722 case DNS_TYPE_CNAME:
723 len = strlen(record.data.cname);
724 werr = dns_name_check(mem_ctx, len, record.data.cname);
725 break;
726 case DNS_TYPE_SOA:
727 len = strlen(record.data.soa.mname);
728 werr = dns_name_check(mem_ctx, len, record.data.soa.mname);
729 if (!W_ERROR_IS_OK(werr)) {
730 break;
732 len = strlen(record.data.soa.rname);
733 werr = dns_name_check(mem_ctx, len, record.data.soa.rname);
734 break;
735 case DNS_TYPE_PTR:
736 len = strlen(record.data.ptr);
737 werr = dns_name_check(mem_ctx, len, record.data.ptr);
738 break;
739 case DNS_TYPE_MX:
740 len = strlen(record.data.mx.nameTarget);
741 werr = dns_name_check(mem_ctx, len, record.data.mx.nameTarget);
742 break;
743 case DNS_TYPE_SRV:
744 len = strlen(record.data.srv.nameTarget);
745 werr = dns_name_check(mem_ctx, len,
746 record.data.srv.nameTarget);
747 break;
749 * In the default case, the record doesn't have a DN, so it
750 * must be ok.
752 default:
753 break;
756 if (!W_ERROR_IS_OK(werr)) {
757 return werr;
761 return WERR_OK;
764 bool dns_name_is_static(struct dnsp_DnssrvRpcRecord *records,
765 uint16_t rec_count)
767 int i = 0;
768 for (i = 0; i < rec_count; i++) {
769 if (records[i].wType == DNS_TYPE_TOMBSTONE) {
770 continue;
773 if (records[i].wType == DNS_TYPE_SOA ||
774 records[i].dwTimeStamp == 0) {
775 return true;
778 return false;
782 * Helper function to copy a dnsp_ip4_array struct to an IP4_ARRAY struct.
783 * The new structure and it's data are allocated on the supplied talloc context
785 static struct IP4_ARRAY *copy_ip4_array(TALLOC_CTX *ctx,
786 const char *name,
787 struct dnsp_ip4_array array)
790 struct IP4_ARRAY *ip4_array = NULL;
791 unsigned int i;
793 ip4_array = talloc_zero(ctx, struct IP4_ARRAY);
794 if (ip4_array == NULL) {
795 DBG_ERR("Out of memory copying property [%s]\n", name);
796 return NULL;
799 ip4_array->AddrCount = array.addrCount;
800 if (ip4_array->AddrCount == 0) {
801 return ip4_array;
804 ip4_array->AddrArray =
805 talloc_array(ip4_array, uint32_t, ip4_array->AddrCount);
806 if (ip4_array->AddrArray == NULL) {
807 TALLOC_FREE(ip4_array);
808 DBG_ERR("Out of memory copying property [%s] values\n", name);
809 return NULL;
812 for (i = 0; i < ip4_array->AddrCount; i++) {
813 ip4_array->AddrArray[i] = array.addrArray[i];
816 return ip4_array;
819 bool dns_zoneinfo_load_zone_property(struct dnsserver_zoneinfo *zoneinfo,
820 struct dnsp_DnsProperty *prop)
822 switch (prop->id) {
823 case DSPROPERTY_ZONE_TYPE:
824 zoneinfo->dwZoneType = prop->data.zone_type;
825 break;
826 case DSPROPERTY_ZONE_ALLOW_UPDATE:
827 zoneinfo->fAllowUpdate = prop->data.allow_update_flag;
828 break;
829 case DSPROPERTY_ZONE_NOREFRESH_INTERVAL:
830 zoneinfo->dwNoRefreshInterval = prop->data.norefresh_hours;
831 break;
832 case DSPROPERTY_ZONE_REFRESH_INTERVAL:
833 zoneinfo->dwRefreshInterval = prop->data.refresh_hours;
834 break;
835 case DSPROPERTY_ZONE_AGING_STATE:
836 zoneinfo->fAging = prop->data.aging_enabled;
837 break;
838 case DSPROPERTY_ZONE_SCAVENGING_SERVERS:
839 zoneinfo->aipScavengeServers = copy_ip4_array(
840 zoneinfo, "ZONE_SCAVENGING_SERVERS", prop->data.servers);
841 if (zoneinfo->aipScavengeServers == NULL) {
842 return false;
844 break;
845 case DSPROPERTY_ZONE_AGING_ENABLED_TIME:
846 zoneinfo->dwAvailForScavengeTime =
847 prop->data.next_scavenging_cycle_hours;
848 break;
849 case DSPROPERTY_ZONE_MASTER_SERVERS:
850 zoneinfo->aipLocalMasters = copy_ip4_array(
851 zoneinfo, "ZONE_MASTER_SERVERS", prop->data.master_servers);
852 if (zoneinfo->aipLocalMasters == NULL) {
853 return false;
855 break;
856 case DSPROPERTY_ZONE_EMPTY:
857 case DSPROPERTY_ZONE_SECURE_TIME:
858 case DSPROPERTY_ZONE_DELETED_FROM_HOSTNAME:
859 case DSPROPERTY_ZONE_AUTO_NS_SERVERS:
860 case DSPROPERTY_ZONE_DCPROMO_CONVERT:
861 case DSPROPERTY_ZONE_SCAVENGING_SERVERS_DA:
862 case DSPROPERTY_ZONE_MASTER_SERVERS_DA:
863 case DSPROPERTY_ZONE_NS_SERVERS_DA:
864 case DSPROPERTY_ZONE_NODE_DBFLAGS:
865 break;
867 return true;
869 WERROR dns_get_zone_properties(struct ldb_context *samdb,
870 TALLOC_CTX *mem_ctx,
871 struct ldb_dn *zone_dn,
872 struct dnsserver_zoneinfo *zoneinfo)
875 int ret, i;
876 struct dnsp_DnsProperty *prop = NULL;
877 struct ldb_message_element *element = NULL;
878 const char *const attrs[] = {"dNSProperty", NULL};
879 struct ldb_result *res = NULL;
880 enum ndr_err_code err;
882 ret = ldb_search(samdb,
883 mem_ctx,
884 &res,
885 zone_dn,
886 LDB_SCOPE_BASE,
887 attrs,
888 "(objectClass=dnsZone)");
889 if (ret != LDB_SUCCESS) {
890 DBG_ERR("dnsserver: Failed to find DNS zone: %s\n",
891 ldb_dn_get_linearized(zone_dn));
892 return DNS_ERR(SERVER_FAILURE);
895 element = ldb_msg_find_element(res->msgs[0], "dNSProperty");
896 if (element == NULL) {
897 return DNS_ERR(NOTZONE);
900 for (i = 0; i < element->num_values; i++) {
901 bool valid_property;
902 prop = talloc_zero(mem_ctx, struct dnsp_DnsProperty);
903 if (prop == NULL) {
904 return WERR_NOT_ENOUGH_MEMORY;
906 err = ndr_pull_struct_blob(
907 &(element->values[i]),
908 mem_ctx,
909 prop,
910 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnsProperty);
911 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
913 * If we can't pull it, then there is no valid
914 * data to load into the zone, so ignore this
915 * as Micosoft does. Windows can load an
916 * invalid property with a zero length into
917 * the dnsProperty attribute.
919 continue;
922 valid_property =
923 dns_zoneinfo_load_zone_property(zoneinfo, prop);
924 if (!valid_property) {
925 return DNS_ERR(SERVER_FAILURE);
929 return WERR_OK;
932 WERROR dns_common_replace(struct ldb_context *samdb,
933 TALLOC_CTX *mem_ctx,
934 struct ldb_dn *dn,
935 bool needs_add,
936 uint32_t serial,
937 struct dnsp_DnssrvRpcRecord *records,
938 uint16_t rec_count)
940 const struct timeval start = timeval_current();
941 struct ldb_message_element *el;
942 uint16_t i;
943 int ret;
944 WERROR werr;
945 struct ldb_message *msg = NULL;
946 bool was_tombstoned = false;
947 bool become_tombstoned = false;
948 struct ldb_dn *zone_dn = NULL;
949 struct dnsserver_zoneinfo *zoneinfo = NULL;
950 uint32_t t;
952 msg = ldb_msg_new(mem_ctx);
953 W_ERROR_HAVE_NO_MEMORY(msg);
955 msg->dn = dn;
957 zone_dn = ldb_dn_copy(mem_ctx, dn);
958 if (zone_dn == NULL) {
959 werr = WERR_NOT_ENOUGH_MEMORY;
960 goto exit;
962 if (!ldb_dn_remove_child_components(zone_dn, 1)) {
963 werr = DNS_ERR(SERVER_FAILURE);
964 goto exit;
966 zoneinfo = talloc(mem_ctx, struct dnsserver_zoneinfo);
967 if (zoneinfo == NULL) {
968 werr = WERR_NOT_ENOUGH_MEMORY;
969 goto exit;
971 werr = dns_get_zone_properties(samdb, mem_ctx, zone_dn, zoneinfo);
972 if (W_ERROR_EQUAL(DNS_ERR(NOTZONE), werr)) {
974 * We only got zoneinfo for aging so if we didn't find any
975 * properties then just disable aging and keep going.
977 zoneinfo->fAging = 0;
978 } else if (!W_ERROR_IS_OK(werr)) {
979 goto exit;
982 werr = check_name_list(mem_ctx, rec_count, records);
983 if (!W_ERROR_IS_OK(werr)) {
984 goto exit;
987 ret = ldb_msg_add_empty(msg, "dnsRecord", LDB_FLAG_MOD_REPLACE, &el);
988 if (ret != LDB_SUCCESS) {
989 werr = DNS_ERR(SERVER_FAILURE);
990 goto exit;
994 * we have at least one value,
995 * which might be used for the tombstone marker
997 el->values = talloc_zero_array(el, struct ldb_val, MAX(1, rec_count));
998 if (el->values == NULL) {
999 werr = WERR_NOT_ENOUGH_MEMORY;
1000 goto exit;
1003 if (rec_count > 1) {
1005 * We store a sorted list with the high wType values first
1006 * that's what windows does. It also simplifies the
1007 * filtering of DNS_TYPE_TOMBSTONE records
1009 TYPESAFE_QSORT(records, rec_count, rec_cmp);
1012 for (i = 0; i < rec_count; i++) {
1013 struct ldb_val *v = &el->values[el->num_values];
1014 enum ndr_err_code ndr_err;
1016 if (records[i].wType == DNS_TYPE_TOMBSTONE) {
1018 * There are two things that could be going on here.
1020 * 1. We use a tombstone with EntombedTime == 0 for
1021 * passing deletion messages through the stack, and
1022 * this is the place we filter them out to perform
1023 * that deletion.
1025 * 2. This node is tombstoned, with no records except
1026 * for a single tombstone, and it is just waiting to
1027 * disappear. In this case, unless the caller has
1028 * added a record, rec_count should be 1, and
1029 * el->num_values will end up at 0, and we will make
1030 * no changes. But if the caller has added a record,
1031 * we need to un-tombstone the node.
1033 * It is not possible to add an explicit tombstone
1034 * record.
1036 if (records[i].data.EntombedTime != 0) {
1037 was_tombstoned = true;
1039 continue;
1042 if (zoneinfo->fAging == 1 && records[i].dwTimeStamp != 0) {
1043 t = unix_to_dns_timestamp(time(NULL));
1044 if (t - records[i].dwTimeStamp >
1045 zoneinfo->dwNoRefreshInterval) {
1046 records[i].dwTimeStamp = t;
1050 records[i].dwSerial = serial;
1051 ndr_err = ndr_push_struct_blob(v, el->values, &records[i],
1052 (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
1053 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1054 DEBUG(0, ("Failed to push dnsp_DnssrvRpcRecord\n"));
1055 werr = DNS_ERR(SERVER_FAILURE);
1056 goto exit;
1058 el->num_values++;
1061 if (needs_add) {
1063 * This is a new dnsNode, which simplifies everything as we
1064 * know there is nothing to delete or change. We add the
1065 * records and get out.
1067 if (el->num_values == 0) {
1068 werr = WERR_OK;
1069 goto exit;
1072 ret = ldb_msg_add_string(msg, "objectClass", "dnsNode");
1073 if (ret != LDB_SUCCESS) {
1074 werr = DNS_ERR(SERVER_FAILURE);
1075 goto exit;
1078 ret = ldb_add(samdb, msg);
1079 if (ret != LDB_SUCCESS) {
1080 werr = DNS_ERR(SERVER_FAILURE);
1081 goto exit;
1084 werr = WERR_OK;
1085 goto exit;
1088 if (el->num_values == 0) {
1090 * We get here if there are no records or all the records were
1091 * tombstones.
1093 struct dnsp_DnssrvRpcRecord tbs;
1094 struct ldb_val *v = &el->values[el->num_values];
1095 enum ndr_err_code ndr_err;
1096 struct timeval tv;
1098 if (was_tombstoned) {
1100 * This is already a tombstoned object.
1101 * Just leave it instead of updating the time stamp.
1103 werr = WERR_OK;
1104 goto exit;
1107 tv = timeval_current();
1108 tbs = (struct dnsp_DnssrvRpcRecord) {
1109 .wType = DNS_TYPE_TOMBSTONE,
1110 .dwSerial = serial,
1111 .data.EntombedTime = timeval_to_nttime(&tv),
1114 ndr_err = ndr_push_struct_blob(v, el->values, &tbs,
1115 (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
1116 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1117 DEBUG(0, ("Failed to push dnsp_DnssrvRpcRecord\n"));
1118 werr = DNS_ERR(SERVER_FAILURE);
1119 goto exit;
1121 el->num_values++;
1123 become_tombstoned = true;
1126 if (was_tombstoned || become_tombstoned) {
1127 ret = ldb_msg_add_empty(msg, "dNSTombstoned",
1128 LDB_FLAG_MOD_REPLACE, NULL);
1129 if (ret != LDB_SUCCESS) {
1130 werr = DNS_ERR(SERVER_FAILURE);
1131 goto exit;
1134 ret = ldb_msg_add_fmt(msg, "dNSTombstoned", "%s",
1135 become_tombstoned ? "TRUE" : "FALSE");
1136 if (ret != LDB_SUCCESS) {
1137 werr = DNS_ERR(SERVER_FAILURE);
1138 goto exit;
1142 ret = ldb_modify(samdb, msg);
1143 if (ret != LDB_SUCCESS) {
1144 NTSTATUS nt = dsdb_ldb_err_to_ntstatus(ret);
1145 werr = ntstatus_to_werror(nt);
1146 goto exit;
1149 werr = WERR_OK;
1150 exit:
1151 talloc_free(msg);
1152 DNS_COMMON_LOG_OPERATION(
1153 win_errstr(werr),
1154 &start,
1155 NULL,
1156 dn == NULL ? NULL : ldb_dn_get_linearized(dn),
1157 NULL);
1158 return werr;
1161 bool dns_name_match(const char *zone, const char *name, size_t *host_part_len)
1163 size_t zl = strlen(zone);
1164 size_t nl = strlen(name);
1165 ssize_t zi, ni;
1166 static const size_t fixup = 'a' - 'A';
1168 if (zl > nl) {
1169 return false;
1172 for (zi = zl, ni = nl; zi >= 0; zi--, ni--) {
1173 char zc = zone[zi];
1174 char nc = name[ni];
1176 /* convert to lower case */
1177 if (zc >= 'A' && zc <= 'Z') {
1178 zc += fixup;
1180 if (nc >= 'A' && nc <= 'Z') {
1181 nc += fixup;
1184 if (zc != nc) {
1185 return false;
1189 if (ni >= 0) {
1190 if (name[ni] != '.') {
1191 return false;
1194 ni--;
1197 *host_part_len = ni+1;
1199 return true;
1202 WERROR dns_common_name2dn(struct ldb_context *samdb,
1203 struct dns_server_zone *zones,
1204 TALLOC_CTX *mem_ctx,
1205 const char *name,
1206 struct ldb_dn **_dn)
1208 struct ldb_dn *base;
1209 struct ldb_dn *dn;
1210 const struct dns_server_zone *z;
1211 size_t host_part_len = 0;
1212 struct ldb_val host_part;
1213 WERROR werr;
1214 bool ok;
1215 const char *casefold = NULL;
1217 if (name == NULL) {
1218 return DNS_ERR(FORMAT_ERROR);
1221 if (strcmp(name, "") == 0) {
1222 base = ldb_get_default_basedn(samdb);
1223 dn = ldb_dn_copy(mem_ctx, base);
1224 ok = ldb_dn_add_child_fmt(dn,
1225 "DC=@,DC=RootDNSServers,CN=MicrosoftDNS,CN=System");
1226 if (ok == false) {
1227 TALLOC_FREE(dn);
1228 return WERR_NOT_ENOUGH_MEMORY;
1231 *_dn = dn;
1232 return WERR_OK;
1235 /* Check non-empty names */
1236 werr = dns_name_check(mem_ctx, strlen(name), name);
1237 if (!W_ERROR_IS_OK(werr)) {
1238 return werr;
1241 for (z = zones; z != NULL; z = z->next) {
1242 bool match;
1244 match = dns_name_match(z->name, name, &host_part_len);
1245 if (match) {
1246 break;
1250 if (z == NULL) {
1251 return DNS_ERR(NAME_ERROR);
1254 if (host_part_len == 0) {
1255 dn = ldb_dn_copy(mem_ctx, z->dn);
1256 ok = ldb_dn_add_child_fmt(dn, "DC=@");
1257 if (! ok) {
1258 TALLOC_FREE(dn);
1259 return WERR_NOT_ENOUGH_MEMORY;
1261 *_dn = dn;
1262 return WERR_OK;
1265 dn = ldb_dn_copy(mem_ctx, z->dn);
1266 if (dn == NULL) {
1267 TALLOC_FREE(dn);
1268 return WERR_NOT_ENOUGH_MEMORY;
1271 host_part = data_blob_const(name, host_part_len);
1273 ok = ldb_dn_add_child_val(dn, "DC", host_part);
1275 if (ok == false) {
1276 TALLOC_FREE(dn);
1277 return WERR_NOT_ENOUGH_MEMORY;
1281 * Check the new DN here for validity, so as to catch errors
1282 * early
1284 ok = ldb_dn_validate(dn);
1285 if (ok == false) {
1286 TALLOC_FREE(dn);
1287 return DNS_ERR(NAME_ERROR);
1291 * The value from this check is saved in the DN, and doing
1292 * this here allows an easy return here.
1294 casefold = ldb_dn_get_casefold(dn);
1295 if (casefold == NULL) {
1296 TALLOC_FREE(dn);
1297 return DNS_ERR(NAME_ERROR);
1300 *_dn = dn;
1301 return WERR_OK;
1306 see if two dns records match
1310 bool dns_record_match(struct dnsp_DnssrvRpcRecord *rec1,
1311 struct dnsp_DnssrvRpcRecord *rec2)
1313 int i;
1314 struct in6_addr rec1_in_addr6;
1315 struct in6_addr rec2_in_addr6;
1317 if (rec1->wType != rec2->wType) {
1318 return false;
1321 /* see if the data matches */
1322 switch (rec1->wType) {
1323 case DNS_TYPE_A:
1324 return strcmp(rec1->data.ipv4, rec2->data.ipv4) == 0;
1325 case DNS_TYPE_AAAA: {
1326 int ret;
1328 ret = inet_pton(AF_INET6, rec1->data.ipv6, &rec1_in_addr6);
1329 if (ret != 1) {
1330 return false;
1332 ret = inet_pton(AF_INET6, rec2->data.ipv6, &rec2_in_addr6);
1333 if (ret != 1) {
1334 return false;
1337 return memcmp(&rec1_in_addr6, &rec2_in_addr6, sizeof(rec1_in_addr6)) == 0;
1339 case DNS_TYPE_CNAME:
1340 return dns_name_equal(rec1->data.cname, rec2->data.cname);
1341 case DNS_TYPE_TXT:
1342 if (rec1->data.txt.count != rec2->data.txt.count) {
1343 return false;
1345 for (i = 0; i < rec1->data.txt.count; i++) {
1346 if (strcmp(rec1->data.txt.str[i], rec2->data.txt.str[i]) != 0) {
1347 return false;
1350 return true;
1351 case DNS_TYPE_PTR:
1352 return dns_name_equal(rec1->data.ptr, rec2->data.ptr);
1353 case DNS_TYPE_NS:
1354 return dns_name_equal(rec1->data.ns, rec2->data.ns);
1356 case DNS_TYPE_SRV:
1357 return rec1->data.srv.wPriority == rec2->data.srv.wPriority &&
1358 rec1->data.srv.wWeight == rec2->data.srv.wWeight &&
1359 rec1->data.srv.wPort == rec2->data.srv.wPort &&
1360 dns_name_equal(rec1->data.srv.nameTarget, rec2->data.srv.nameTarget);
1362 case DNS_TYPE_MX:
1363 return rec1->data.mx.wPriority == rec2->data.mx.wPriority &&
1364 dns_name_equal(rec1->data.mx.nameTarget, rec2->data.mx.nameTarget);
1366 case DNS_TYPE_SOA:
1367 return dns_name_equal(rec1->data.soa.mname, rec2->data.soa.mname) &&
1368 dns_name_equal(rec1->data.soa.rname, rec2->data.soa.rname) &&
1369 rec1->data.soa.serial == rec2->data.soa.serial &&
1370 rec1->data.soa.refresh == rec2->data.soa.refresh &&
1371 rec1->data.soa.retry == rec2->data.soa.retry &&
1372 rec1->data.soa.expire == rec2->data.soa.expire &&
1373 rec1->data.soa.minimum == rec2->data.soa.minimum;
1374 case DNS_TYPE_TOMBSTONE:
1375 return true;
1376 default:
1377 break;
1380 return false;
1384 static int dns_common_sort_zones(struct ldb_message **m1, struct ldb_message **m2)
1386 const char *n1, *n2;
1387 size_t l1, l2;
1389 n1 = ldb_msg_find_attr_as_string(*m1, "name", NULL);
1390 n2 = ldb_msg_find_attr_as_string(*m2, "name", NULL);
1391 if (n1 == NULL || n2 == NULL) {
1392 if (n1 != NULL) {
1393 return -1;
1394 } else if (n2 != NULL) {
1395 return 1;
1396 } else {
1397 return 0;
1400 l1 = strlen(n1);
1401 l2 = strlen(n2);
1403 /* If the string lengths are not equal just sort by length */
1404 if (l1 != l2) {
1405 /* If m1 is the larger zone name, return it first */
1406 return l2 - l1;
1409 /*TODO: We need to compare DNs here, we want the DomainDNSZones first */
1410 return 0;
1413 NTSTATUS dns_common_zones(struct ldb_context *samdb,
1414 TALLOC_CTX *mem_ctx,
1415 struct ldb_dn *base_dn,
1416 struct dns_server_zone **zones_ret)
1418 const struct timeval start = timeval_current();
1419 int ret;
1420 static const char * const attrs[] = { "name", NULL};
1421 struct ldb_result *res;
1422 int i;
1423 struct dns_server_zone *new_list = NULL;
1424 TALLOC_CTX *frame = talloc_stackframe();
1425 NTSTATUS result = NT_STATUS_OK;
1427 if (base_dn) {
1428 /* This search will work against windows */
1429 ret = dsdb_search(samdb, frame, &res,
1430 base_dn, LDB_SCOPE_SUBTREE,
1431 attrs, 0, "(objectClass=dnsZone)");
1432 } else {
1433 /* TODO: this search does not work against windows */
1434 ret = dsdb_search(samdb, frame, &res, NULL,
1435 LDB_SCOPE_SUBTREE,
1436 attrs,
1437 DSDB_SEARCH_SEARCH_ALL_PARTITIONS,
1438 "(objectClass=dnsZone)");
1440 if (ret != LDB_SUCCESS) {
1441 TALLOC_FREE(frame);
1442 result = NT_STATUS_INTERNAL_DB_CORRUPTION;
1443 goto exit;
1446 TYPESAFE_QSORT(res->msgs, res->count, dns_common_sort_zones);
1448 for (i=0; i < res->count; i++) {
1449 struct dns_server_zone *z;
1451 z = talloc_zero(mem_ctx, struct dns_server_zone);
1452 if (z == NULL) {
1453 TALLOC_FREE(frame);
1454 result = NT_STATUS_NO_MEMORY;
1455 goto exit;
1458 z->name = ldb_msg_find_attr_as_string(res->msgs[i], "name", NULL);
1459 talloc_steal(z, z->name);
1460 z->dn = talloc_move(z, &res->msgs[i]->dn);
1462 * Ignore the RootDNSServers zone and zones that we don't support yet
1463 * RootDNSServers should never be returned (Windows DNS server don't)
1464 * ..TrustAnchors should never be returned as is, (Windows returns
1465 * TrustAnchors) and for the moment we don't support DNSSEC so we'd better
1466 * not return this zone.
1468 if ((strcmp(z->name, "RootDNSServers") == 0) ||
1469 (strcmp(z->name, "..TrustAnchors") == 0))
1471 DEBUG(10, ("Ignoring zone %s\n", z->name));
1472 talloc_free(z);
1473 continue;
1475 DLIST_ADD_END(new_list, z);
1478 *zones_ret = new_list;
1479 TALLOC_FREE(frame);
1480 result = NT_STATUS_OK;
1481 exit:
1482 DNS_COMMON_LOG_OPERATION(
1483 nt_errstr(result),
1484 &start,
1485 NULL,
1486 base_dn == NULL ? NULL : ldb_dn_get_linearized(base_dn),
1487 NULL);
1488 return result;
1492 see if two DNS names are the same
1494 bool dns_name_equal(const char *name1, const char *name2)
1496 size_t len1 = strlen(name1);
1497 size_t len2 = strlen(name2);
1499 if (len1 > 0 && name1[len1 - 1] == '.') {
1500 len1--;
1502 if (len2 > 0 && name2[len2 - 1] == '.') {
1503 len2--;
1505 if (len1 != len2) {
1506 return false;
1508 return strncasecmp(name1, name2, len1) == 0;
1513 * Convert unix time to a DNS timestamp
1514 * uint32 hours in the NTTIME epoch
1516 * This uses unix_to_nt_time() which can return special flag NTTIMEs like
1517 * UINT64_MAX (0xFFF...) or NTTIME_MAX (0x7FF...), which will convert to
1518 * distant future timestamps; or 0 as a flag value, meaning a 1601 timestamp,
1519 * which is used to indicate a record does not expire.
1521 * As we don't generally check for these special values in NTTIME conversions,
1522 * we also don't check here, but for the benefit of people encountering these
1523 * timestamps and searching for their origin, here is a list:
1525 ** TIME_T_MAX
1527 * Even if time_t is 32 bit, this will become NTTIME_MAX (a.k.a INT64_MAX,
1528 * 0x7fffffffffffffff) in 100ns units. That translates to 256204778 hours
1529 * since 1601, which converts back to 9223372008000000000 or
1530 * 0x7ffffff9481f1000. It will present as 30828-09-14 02:00:00, around 48
1531 * minutes earlier than NTTIME_MAX.
1533 ** 0, the start of the unix epoch, 1970-01-01 00:00:00
1535 * This is converted into 0 in the Windows epoch, 1601-01-01 00:00:00 which is
1536 * clearly the same whether you count in 100ns units or hours. In DNS record
1537 * timestamps this is a flag meaning the record will never expire.
1539 ** (time_t)-1, such as what *might* mean 1969-12-31 23:59:59
1541 * This becomes (NTTIME)-1ULL a.k.a. UINT64_MAX, 0xffffffffffffffff thence
1542 * 512409557 in hours since 1601. That in turn is 0xfffffffaf2028800 or
1543 * 18446744052000000000 in NTTIME (rounded to the hour), which might be
1544 * presented as -21709551616 or -0x50dfd7800, because NTITME is not completely
1545 * dedicated to being unsigned. If it gets shown as a year, it will be around
1546 * 60055.
1548 ** Other negative time_t values (e.g. 1969-05-29).
1550 * The meaning of these is somewhat undefined, but in any case they will
1551 * translate perfectly well into the same dates in NTTIME.
1553 ** Notes
1555 * There are dns timestamps that exceed the range of NTTIME (up to 488356 AD),
1556 * but it is not possible for this function to produce them.
1558 * It is plausible that it was at midnight on 1601-01-01, in London, that
1559 * Shakespeare wrote:
1561 * The time is out of joint. O cursed spite
1562 * That ever I was born to set it right!
1564 * and this is why we have this epoch and time zone.
1566 uint32_t unix_to_dns_timestamp(time_t t)
1568 NTTIME nt;
1569 unix_to_nt_time(&nt, t);
1570 nt /= NTTIME_TO_HOURS;
1571 return (uint32_t) nt;
1575 * Convert a DNS timestamp into NTTIME.
1577 * Because DNS timestamps cover a longer time period than NTTIME, and these
1578 * would wrap to an arbitrary NTTIME, we saturate at NTTIME_MAX and return an
1579 * error in this case.
1581 NTSTATUS dns_timestamp_to_nt_time(NTTIME *_nt, uint32_t t)
1583 NTTIME nt = t;
1584 if (nt > NTTIME_MAX / NTTIME_TO_HOURS) {
1585 *_nt = NTTIME_MAX;
1586 return NT_STATUS_INTEGER_OVERFLOW;
1588 *_nt = nt * NTTIME_TO_HOURS;
1589 return NT_STATUS_OK;