2 Unix SMB/CIFS implementation.
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/>.
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"
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"
37 #define DBGC_CLASS DBGC_DNS
39 uint8_t werr_to_dns_err(WERROR werr
)
41 if (W_ERROR_EQUAL(WERR_OK
, werr
)) {
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
,
75 struct dnsp_DnssrvRpcRecord
**records
,
76 uint16_t *num_records
)
79 struct dnsp_DnssrvRpcRecord
*recs
;
84 recs
= talloc_zero_array(mem_ctx
, struct dnsp_DnssrvRpcRecord
,
87 return WERR_NOT_ENOUGH_MEMORY
;
89 for (ri
= 0; ri
< el
->num_values
; ri
++) {
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
)) {
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
) {
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
);
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
);
132 *num_records
= el
->num_values
;
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
,
143 struct dnsp_DnssrvRpcRecord
**records
,
144 uint16_t *num_records
,
147 static const char * const attrs
[] = {
154 struct ldb_message
*msg
= NULL
;
155 struct ldb_message_element
*el
;
160 if (tombstoned
!= NULL
) {
162 ret
= dsdb_search_one(samdb
, mem_ctx
, &msg
, dn
,
163 LDB_SCOPE_BASE
, attrs
, 0,
164 "(objectClass=dnsNode)");
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");
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
,
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()
219 * Because we are not looking for a tombstone
220 * in this codepath, we just pretend it does
223 return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST
;
227 werr
= dns_common_extract(samdb
, el
, mem_ctx
, records
, num_records
);
229 if (!W_ERROR_IS_OK(werr
)) {
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(
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
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
);
259 DBG_ERR("Unable to allocate ldb_parse_tree\n");
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
);
269 DBG_ERR("Unable to allocate value->data\n");
274 value
->length
= length
;
276 value
->data
[0] = '*';
277 memcpy(&value
->data
[1], name
, size
);
279 memcpy(value
->data
, name
, size
);
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
) {
292 unsigned int labels
= 1;
293 for (x
= 0; x
< name
->length
; x
++) {
294 if (name
->data
[x
] == '.') {
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(
319 const struct ldb_val
*name
= NULL
; /* The DNS name being
321 const char *attr
= NULL
; /* The attribute name */
322 struct ldb_parse_tree
*query
= NULL
; /* The constructed query
324 struct ldb_parse_tree
*wildcard_query
= NULL
; /* The parse tree for the
327 int labels
= 0; /* The number of labels in the name */
329 attr
= ldb_dn_get_rdn_name(dn
);
331 DBG_ERR("Unable to get rdn_name\n");
335 name
= ldb_dn_get_rdn_val(dn
);
337 DBG_ERR("Unable to get domain name value\n");
340 labels
= number_of_labels(name
);
342 query
= ldb_parse_tree(mem_ctx
, BASE
);
344 DBG_ERR("Unable to parse query %s\n", BASE
);
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(
358 struct ldb_parse_tree
*,
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
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(
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);
391 return NULL
; /* Reason will have been logged */
393 wildcard_query
->u
.list
.elements
[l
] = el
;
399 * Scan the list of records matching a dns wildcard query and return the
402 * The best match is either an exact name match, or the longest wild card
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 */
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
) {
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
,
446 struct ldb_message
**msg
)
448 static const char * const attrs
[] = {
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");
465 return LDB_ERR_OPERATIONS_ERROR
;
468 query
= build_wildcard_query(frame
, dn
);
471 return LDB_ERR_OPERATIONS_ERROR
;
474 result
= talloc_zero(mem_ctx
, struct ldb_result
);
475 if (result
== NULL
) {
477 DBG_ERR("Unable to allocate ldb_result\n");
478 return LDB_ERR_OPERATIONS_ERROR
;
481 ret
= ldb_build_search_req_ex(&request
,
490 ldb_search_default_callback
,
492 if (ret
!= LDB_SUCCESS
) {
494 DBG_ERR("ldb_build_search_req_ex returned %d\n", ret
);
498 ret
= ldb_request(samdb
, request
);
499 if (ret
!= LDB_SUCCESS
) {
504 ret
= ldb_wait(request
->handle
, LDB_WAIT_ALL
);
505 if (ret
!= LDB_SUCCESS
) {
510 if (result
->count
== 0) {
512 return LDB_ERR_NO_SUCH_OBJECT
;
515 match
= get_best_match(dn
, result
);
518 return LDB_ERR_OPERATIONS_ERROR
;
521 *msg
= talloc_move(mem_ctx
, &match
);
527 * Lookup a DNS record, will match DNS wild card records if an exact match
530 WERROR
dns_common_wildcard_lookup(struct ldb_context
*samdb
,
533 struct dnsp_DnssrvRpcRecord
**records
,
534 uint16_t *num_records
)
538 struct ldb_message
*msg
= NULL
;
539 struct ldb_message_element
*el
= NULL
;
540 const struct ldb_val
*name
= NULL
;
545 name
= ldb_dn_get_rdn_val(dn
);
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
,
560 werr
= dns_name_check(
562 strlen((const char*)name
->data
),
563 (const char*) name
->data
);
564 if (!W_ERROR_IS_OK(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
,
578 if (!W_ERROR_EQUAL(werr
, WERR_DNS_ERROR_NAME_DOES_NOT_EXIST
)) {
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");
592 return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST
;
595 werr
= dns_common_extract(samdb
, el
, mem_ctx
, records
, num_records
);
597 if (!W_ERROR_IS_OK(werr
)) {
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:
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
)
632 unsigned int labels
= 0;
633 unsigned int label_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
] == '.') {
653 if (labels
> DNS_MAX_LABELS
) {
654 return WERR_DS_INVALID_DN_SYNTAX
;
659 if (label_len
> DNS_MAX_LABEL_LENGTH
) {
660 return WERR_DS_INVALID_DN_SYNTAX
;
668 static WERROR
check_name_list(TALLOC_CTX
*mem_ctx
, uint16_t rec_count
,
669 struct dnsp_DnssrvRpcRecord
*records
)
674 struct dnsp_DnssrvRpcRecord record
;
677 for (i
= 0; i
< rec_count
; i
++) {
680 switch (record
.wType
) {
683 len
= strlen(record
.data
.ns
);
684 werr
= dns_name_check(mem_ctx
, len
, record
.data
.ns
);
687 len
= strlen(record
.data
.cname
);
688 werr
= dns_name_check(mem_ctx
, len
, record
.data
.cname
);
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
)) {
696 len
= strlen(record
.data
.soa
.rname
);
697 werr
= dns_name_check(mem_ctx
, len
, record
.data
.soa
.rname
);
700 len
= strlen(record
.data
.ptr
);
701 werr
= dns_name_check(mem_ctx
, len
, record
.data
.ptr
);
704 len
= strlen(record
.data
.mx
.nameTarget
);
705 werr
= dns_name_check(mem_ctx
, len
, record
.data
.mx
.nameTarget
);
708 len
= strlen(record
.data
.srv
.nameTarget
);
709 werr
= dns_name_check(mem_ctx
, len
,
710 record
.data
.srv
.nameTarget
);
713 * In the default case, the record doesn't have a DN, so it
720 if (!W_ERROR_IS_OK(werr
)) {
728 WERROR
dns_common_replace(struct ldb_context
*samdb
,
733 struct dnsp_DnssrvRpcRecord
*records
,
736 struct ldb_message_element
*el
;
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
);
749 werr
= check_name_list(mem_ctx
, rec_count
, records
);
750 if (!W_ERROR_IS_OK(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
));
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;
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
);
797 if (el
->num_values
== 0) {
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
);
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
;
820 if (was_tombstoned
) {
822 * This is already a tombstoned object.
823 * Just leave it instead of updating the time stamp.
828 tv
= timeval_current();
829 tbs
= (struct dnsp_DnssrvRpcRecord
) {
830 .wType
= DNS_TYPE_TOMBSTONE
,
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
);
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
);
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
);
874 static const size_t fixup
= 'a' - 'A';
880 for (zi
= zl
, ni
= nl
; zi
>= 0; zi
--, ni
--) {
884 /* convert to lower case */
885 if (zc
>= 'A' && zc
<= 'Z') {
888 if (nc
>= 'A' && nc
<= 'Z') {
898 if (name
[ni
] != '.') {
905 *host_part_len
= ni
+1;
910 WERROR
dns_common_name2dn(struct ldb_context
*samdb
,
911 struct dns_server_zone
*zones
,
918 const struct dns_server_zone
*z
;
919 size_t host_part_len
= 0;
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");
934 /* Check non-empty names */
935 werr
= dns_name_check(mem_ctx
, strlen(name
), name
);
936 if (!W_ERROR_IS_OK(werr
)) {
940 for (z
= zones
; z
!= NULL
; z
= z
->next
) {
943 match
= dns_name_match(z
->name
, name
, &host_part_len
);
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=@");
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
);
966 static int dns_common_sort_zones(struct ldb_message
**m1
, struct ldb_message
**m2
)
971 n1
= ldb_msg_find_attr_as_string(*m1
, "name", NULL
);
972 n2
= ldb_msg_find_attr_as_string(*m2
, "name", NULL
);
977 /* If the string lengths are not equal just sort by length */
979 /* If m1 is the larger zone name, return it first */
983 /*TODO: We need to compare DNs here, we want the DomainDNSZones first */
987 NTSTATUS
dns_common_zones(struct ldb_context
*samdb
,
989 struct ldb_dn
*base_dn
,
990 struct dns_server_zone
**zones_ret
)
993 static const char * const attrs
[] = { "name", NULL
};
994 struct ldb_result
*res
;
996 struct dns_server_zone
*new_list
= NULL
;
997 TALLOC_CTX
*frame
= talloc_stackframe();
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)");
1005 /* TODO: this search does not work against windows */
1006 ret
= dsdb_search(samdb
, frame
, &res
, NULL
,
1009 DSDB_SEARCH_SEARCH_ALL_PARTITIONS
,
1010 "(objectClass=dnsZone)");
1012 if (ret
!= LDB_SUCCESS
) {
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
);
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
));
1045 DLIST_ADD_END(new_list
, z
);
1048 *zones_ret
= new_list
;
1050 return NT_STATUS_OK
;