2 Unix SMB/CIFS implementation.
6 Copyright (C) 2010 Kai Blin
7 Copyright (C) 2014 Stefan Metzmacher
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "libcli/util/ntstatus.h"
25 #include "libcli/util/werror.h"
26 #include "librpc/ndr/libndr.h"
27 #include "librpc/gen_ndr/ndr_dns.h"
28 #include "librpc/gen_ndr/ndr_dnsp.h"
30 #include "dsdb/samdb/samdb.h"
31 #include "dsdb/common/util.h"
32 #include "dns_server/dnsserver_common.h"
35 #define DBGC_CLASS DBGC_DNS
37 uint8_t werr_to_dns_err(WERROR werr
)
39 if (W_ERROR_EQUAL(WERR_OK
, werr
)) {
41 } else if (W_ERROR_EQUAL(DNS_ERR(FORMAT_ERROR
), werr
)) {
42 return DNS_RCODE_FORMERR
;
43 } else if (W_ERROR_EQUAL(DNS_ERR(SERVER_FAILURE
), werr
)) {
44 return DNS_RCODE_SERVFAIL
;
45 } else if (W_ERROR_EQUAL(DNS_ERR(NAME_ERROR
), werr
)) {
46 return DNS_RCODE_NXDOMAIN
;
47 } else if (W_ERROR_EQUAL(WERR_DNS_ERROR_NAME_DOES_NOT_EXIST
, werr
)) {
48 return DNS_RCODE_NXDOMAIN
;
49 } else if (W_ERROR_EQUAL(DNS_ERR(NOT_IMPLEMENTED
), werr
)) {
50 return DNS_RCODE_NOTIMP
;
51 } else if (W_ERROR_EQUAL(DNS_ERR(REFUSED
), werr
)) {
52 return DNS_RCODE_REFUSED
;
53 } else if (W_ERROR_EQUAL(DNS_ERR(YXDOMAIN
), werr
)) {
54 return DNS_RCODE_YXDOMAIN
;
55 } else if (W_ERROR_EQUAL(DNS_ERR(YXRRSET
), werr
)) {
56 return DNS_RCODE_YXRRSET
;
57 } else if (W_ERROR_EQUAL(DNS_ERR(NXRRSET
), werr
)) {
58 return DNS_RCODE_NXRRSET
;
59 } else if (W_ERROR_EQUAL(DNS_ERR(NOTAUTH
), werr
)) {
60 return DNS_RCODE_NOTAUTH
;
61 } else if (W_ERROR_EQUAL(DNS_ERR(NOTZONE
), werr
)) {
62 return DNS_RCODE_NOTZONE
;
63 } else if (W_ERROR_EQUAL(DNS_ERR(BADKEY
), werr
)) {
64 return DNS_RCODE_BADKEY
;
66 DEBUG(5, ("No mapping exists for %s\n", win_errstr(werr
)));
67 return DNS_RCODE_SERVFAIL
;
70 WERROR
dns_common_extract(const struct ldb_message_element
*el
,
72 struct dnsp_DnssrvRpcRecord
**records
,
73 uint16_t *num_records
)
76 struct dnsp_DnssrvRpcRecord
*recs
;
81 recs
= talloc_zero_array(mem_ctx
, struct dnsp_DnssrvRpcRecord
,
86 for (ri
= 0; ri
< el
->num_values
; ri
++) {
87 struct ldb_val
*v
= &el
->values
[ri
];
88 enum ndr_err_code ndr_err
;
90 ndr_err
= ndr_pull_struct_blob(v
, recs
, &recs
[ri
],
91 (ndr_pull_flags_fn_t
)ndr_pull_dnsp_DnssrvRpcRecord
);
92 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
94 DEBUG(0, ("Failed to grab dnsp_DnssrvRpcRecord\n"));
95 return DNS_ERR(SERVER_FAILURE
);
99 *num_records
= el
->num_values
;
103 WERROR
dns_common_lookup(struct ldb_context
*samdb
,
106 struct dnsp_DnssrvRpcRecord
**records
,
107 uint16_t *num_records
,
110 static const char * const attrs
[] = {
117 struct ldb_message
*msg
= NULL
;
118 struct ldb_message_element
*el
;
123 if (tombstoned
!= NULL
) {
125 ret
= dsdb_search_one(samdb
, mem_ctx
, &msg
, dn
,
126 LDB_SCOPE_BASE
, attrs
, 0,
127 "(objectClass=dnsNode)");
129 ret
= dsdb_search_one(samdb
, mem_ctx
, &msg
, dn
,
130 LDB_SCOPE_BASE
, attrs
, 0,
131 "(&(objectClass=dnsNode)(!(dNSTombstoned=TRUE)))");
133 if (ret
== LDB_ERR_NO_SUCH_OBJECT
) {
134 return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST
;
136 if (ret
!= LDB_SUCCESS
) {
137 /* TODO: we need to check if there's a glue record we need to
138 * create a referral to */
139 return DNS_ERR(NAME_ERROR
);
142 if (tombstoned
!= NULL
) {
143 *tombstoned
= ldb_msg_find_attr_as_bool(msg
,
144 "dNSTombstoned", false);
147 el
= ldb_msg_find_element(msg
, "dnsRecord");
150 if (tombstoned
!= NULL
) {
151 struct dnsp_DnssrvRpcRecord
*recs
;
153 * records produced by older Samba releases
154 * keep dnsNode objects without dnsRecord and
155 * without setting dNSTombstoned=TRUE.
157 * We just pretend they're tombstones.
159 recs
= talloc_array(mem_ctx
,
160 struct dnsp_DnssrvRpcRecord
,
165 recs
[0] = (struct dnsp_DnssrvRpcRecord
) {
166 .wType
= DNS_TYPE_TOMBSTONE
,
168 * A value of timestamp != 0
169 * indicated that the object was already
170 * a tombstone, this will be used
171 * in dns_common_replace()
181 return DNS_ERR(NAME_ERROR
);
184 werr
= dns_common_extract(el
, mem_ctx
, records
, num_records
);
186 if (!W_ERROR_IS_OK(werr
)) {
193 static int rec_cmp(const struct dnsp_DnssrvRpcRecord
*r1
,
194 const struct dnsp_DnssrvRpcRecord
*r2
)
196 if (r1
->wType
!= r2
->wType
) {
198 * The records are sorted with higher types first
200 return r2
->wType
- r1
->wType
;
204 * Then we need to sort from the oldest to newest timestamp
206 return r1
->dwTimeStamp
- r2
->dwTimeStamp
;
209 WERROR
dns_common_replace(struct ldb_context
*samdb
,
214 struct dnsp_DnssrvRpcRecord
*records
,
217 struct ldb_message_element
*el
;
220 struct ldb_message
*msg
= NULL
;
221 bool was_tombstoned
= false;
222 bool become_tombstoned
= false;
224 msg
= ldb_msg_new(mem_ctx
);
225 W_ERROR_HAVE_NO_MEMORY(msg
);
229 ret
= ldb_msg_add_empty(msg
, "dnsRecord", LDB_FLAG_MOD_REPLACE
, &el
);
230 if (ret
!= LDB_SUCCESS
) {
231 return DNS_ERR(SERVER_FAILURE
);
235 * we have at least one value,
236 * which might be used for the tombstone marker
238 el
->values
= talloc_zero_array(el
, struct ldb_val
, MAX(1, rec_count
));
240 W_ERROR_HAVE_NO_MEMORY(el
->values
);
243 * We store a sorted list with the high wType values first
244 * that's what windows does. It also simplifies the
245 * filtering of DNS_TYPE_TOMBSTONE records
247 TYPESAFE_QSORT(records
, rec_count
, rec_cmp
);
250 for (i
= 0; i
< rec_count
; i
++) {
251 struct ldb_val
*v
= &el
->values
[el
->num_values
];
252 enum ndr_err_code ndr_err
;
254 if (records
[i
].wType
== DNS_TYPE_TOMBSTONE
) {
255 if (records
[i
].data
.timestamp
!= 0) {
256 was_tombstoned
= true;
261 records
[i
].dwSerial
= serial
;
262 ndr_err
= ndr_push_struct_blob(v
, el
->values
, &records
[i
],
263 (ndr_push_flags_fn_t
)ndr_push_dnsp_DnssrvRpcRecord
);
264 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
265 DEBUG(0, ("Failed to push dnsp_DnssrvRpcRecord\n"));
266 return DNS_ERR(SERVER_FAILURE
);
272 if (el
->num_values
== 0) {
276 ret
= ldb_msg_add_string(msg
, "objectClass", "dnsNode");
277 if (ret
!= LDB_SUCCESS
) {
278 return DNS_ERR(SERVER_FAILURE
);
281 ret
= ldb_add(samdb
, msg
);
282 if (ret
!= LDB_SUCCESS
) {
283 return DNS_ERR(SERVER_FAILURE
);
289 if (el
->num_values
== 0) {
290 struct dnsp_DnssrvRpcRecord tbs
;
291 struct ldb_val
*v
= &el
->values
[el
->num_values
];
292 enum ndr_err_code ndr_err
;
295 if (was_tombstoned
) {
297 * This is already a tombstoned object.
298 * Just leave it instead of updating the time stamp.
303 tv
= timeval_current();
304 tbs
= (struct dnsp_DnssrvRpcRecord
) {
305 .wType
= DNS_TYPE_TOMBSTONE
,
307 .data
.timestamp
= timeval_to_nttime(&tv
),
310 ndr_err
= ndr_push_struct_blob(v
, el
->values
, &tbs
,
311 (ndr_push_flags_fn_t
)ndr_push_dnsp_DnssrvRpcRecord
);
312 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
313 DEBUG(0, ("Failed to push dnsp_DnssrvRpcRecord\n"));
314 return DNS_ERR(SERVER_FAILURE
);
318 become_tombstoned
= true;
321 if (was_tombstoned
|| become_tombstoned
) {
322 ret
= ldb_msg_add_empty(msg
, "dNSTombstoned",
323 LDB_FLAG_MOD_REPLACE
, NULL
);
324 if (ret
!= LDB_SUCCESS
) {
325 return DNS_ERR(SERVER_FAILURE
);
328 ret
= ldb_msg_add_fmt(msg
, "dNSTombstoned", "%s",
329 become_tombstoned
? "TRUE" : "FALSE");
330 if (ret
!= LDB_SUCCESS
) {
331 return DNS_ERR(SERVER_FAILURE
);
335 ret
= ldb_modify(samdb
, msg
);
336 if (ret
!= LDB_SUCCESS
) {
337 NTSTATUS nt
= dsdb_ldb_err_to_ntstatus(ret
);
338 return ntstatus_to_werror(nt
);