s4-dns: Update serial number for zone on dns updates
[rusty/samba.git] / source4 / rpc_server / dnsserver / dnsdb.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    DNS Server
5
6    Copyright (C) Amitay Isaacs 2011
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "dnsserver.h"
24 #include "lib/util/dlinklist.h"
25 #include "librpc/gen_ndr/ndr_dnsp.h"
26
27 struct dnsserver_zone *dnsserver_db_enumerate_zones(TALLOC_CTX *mem_ctx,
28                                                 struct ldb_context *samdb,
29                                                 bool is_forest)
30 {
31         TALLOC_CTX *tmp_ctx;
32         const char *domain_prefix = "DC=DomainDnsZones";
33         const char *forest_prefix = "DC=ForestDnsZones";
34         const char *prefix;
35         const char * const attrs[] = {"name", NULL};
36         struct ldb_dn *dn_base, *partition_dn, *dn;
37         struct ldb_result *res;
38         struct dnsserver_zone *zones, *z;
39         int i, ret;
40
41         tmp_ctx = talloc_new(mem_ctx);
42         if (tmp_ctx == NULL) {
43                 return NULL;
44         }
45
46         if (is_forest) {
47                 dn_base = ldb_get_root_basedn(samdb);
48         } else {
49                 dn_base = ldb_get_default_basedn(samdb);
50         }
51
52         partition_dn = ldb_dn_copy(tmp_ctx, dn_base);
53         if (partition_dn == NULL) {
54                 goto failed;
55         }
56
57         if (is_forest) {
58                 prefix = forest_prefix;
59         } else {
60                 prefix = domain_prefix;
61         }
62
63         if (!ldb_dn_add_child_fmt(partition_dn, "%s", prefix)) {
64                 goto failed;
65         }
66
67         dn = ldb_dn_copy(tmp_ctx, partition_dn);
68         if (dn == NULL) {
69                 goto failed;
70         }
71         if (!ldb_dn_add_child_fmt(dn, "CN=MicrosoftDNS")) {
72                 goto failed;
73         }
74
75         ret = ldb_search(samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
76                           attrs, "(&(objectClass=dnsZone)(!(name=RootDNSServers)))");
77         if (ret != LDB_SUCCESS) {
78                 DEBUG(0, ("dnsserver: Failed to find DNS Zones in %s\n",
79                         ldb_dn_get_linearized(dn)));
80                 goto failed;
81         }
82
83         zones = NULL;
84         for(i=0; i<res->count; i++) {
85                 z = talloc_zero(mem_ctx, struct dnsserver_zone);
86                 if (z == NULL) {
87                         goto failed;
88                 }
89
90                 z->name = talloc_strdup(z,
91                                 ldb_msg_find_attr_as_string(res->msgs[i], "name", NULL));
92                 DEBUG(2, ("dnsserver: Found DNS zone %s\n", z->name));
93                 z->zone_dn = talloc_steal(z, res->msgs[i]->dn);
94                 z->partition_dn = talloc_steal(z, partition_dn);
95
96                 DLIST_ADD_END(zones, z, NULL);
97         }
98
99         return zones;
100
101 failed:
102         talloc_free(tmp_ctx);
103         return NULL;
104 }
105
106
107 static unsigned int dnsserver_update_soa(TALLOC_CTX *mem_ctx,
108                                 struct ldb_context *samdb,
109                                 struct dnsserver_zone *z)
110 {
111         const char * const attrs[] = { "dnsRecord", NULL };
112         struct ldb_result *res;
113         struct dnsp_DnssrvRpcRecord rec;
114         struct ldb_message_element *el;
115         enum ndr_err_code ndr_err;
116         int ret, i, serial = -1;
117         NTTIME t;
118
119         unix_to_nt_time(&t, time(NULL));
120         t /= 10*1000*1000; /* convert to seconds (NT time is in 100ns units) */
121         t /= 3600;         /* convert to hours */
122
123         ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs,
124                         "(&(objectClass=dnsNode)(name=@))");
125         if (ret != LDB_SUCCESS || res->count == 0) {
126                 return -1;
127         }
128
129         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
130         if (el == NULL) {
131                 return -1;
132         }
133
134         for (i=0; i<el->num_values; i++) {
135                 ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec,
136                                         (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
137                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
138                         continue;
139                 }
140
141                 if (rec.wType == DNS_TYPE_SOA) {
142                         serial = rec.data.soa.serial + 1;
143                         rec.dwSerial = serial;
144                         rec.dwTimeStamp = (uint32_t)t;
145                         rec.data.soa.serial = serial;
146
147                         ndr_err = ndr_push_struct_blob(&el->values[i], mem_ctx, &rec,
148                                         (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
149                         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
150                                 return -1;
151                         }
152                         break;
153                 }
154         }
155
156         if (serial != -1) {
157                 el->flags = LDB_FLAG_MOD_REPLACE;
158                 ret = ldb_modify(samdb, res->msgs[0]);
159                 if (ret != LDB_SUCCESS) {
160                         return -1;
161                 }
162         }
163
164         return serial;
165 }
166
167
168 static WERROR dnsserver_db_do_add_rec(TALLOC_CTX *mem_ctx,
169                                 struct ldb_context *samdb,
170                                 struct ldb_dn *dn,
171                                 struct dnsp_DnssrvRpcRecord *rec)
172 {
173         struct ldb_message *msg;
174         struct ldb_val v;
175         int ret;
176         enum ndr_err_code ndr_err;
177
178         msg = ldb_msg_new(mem_ctx);
179         W_ERROR_HAVE_NO_MEMORY(msg);
180
181         msg->dn = dn;
182         ret = ldb_msg_add_string(msg, "objectClass", "dnsNode");
183         if (ret != LDB_SUCCESS) {
184                 return WERR_NOMEM;
185         }
186
187         if (rec) {
188                 ndr_err = ndr_push_struct_blob(&v, mem_ctx, rec,
189                                 (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
190                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
191                         return WERR_GENERAL_FAILURE;
192                 }
193
194                 ret = ldb_msg_add_value(msg, "dnsRecord", &v, NULL);
195                 if (ret != LDB_SUCCESS) {
196                         return WERR_NOMEM;
197                 }
198         }
199
200         ret = ldb_add(samdb, msg);
201         if (ret != LDB_SUCCESS) {
202                 return WERR_INTERNAL_DB_ERROR;
203         }
204
205         return WERR_OK;
206 }
207
208
209 WERROR dnsserver_db_add_empty_node(TALLOC_CTX *mem_ctx,
210                                         struct ldb_context *samdb,
211                                         struct dnsserver_zone *z,
212                                         const char *name)
213 {
214         const char * const attrs[] = { "name", NULL };
215         struct ldb_result *res;
216         struct ldb_dn *dn;
217         int ret;
218
219         ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_BASE, attrs,
220                         "(&(objectClass=dnsNode)(name=%s))", name);
221         if (ret != LDB_SUCCESS) {
222                 return WERR_INTERNAL_DB_ERROR;
223         }
224
225         if (res->count > 0) {
226                 talloc_free(res);
227                 return WERR_DNS_ERROR_RECORD_ALREADY_EXISTS;
228         }
229
230         dn = ldb_dn_copy(mem_ctx, z->zone_dn);
231         W_ERROR_HAVE_NO_MEMORY(dn);
232
233         if (!ldb_dn_add_child_fmt(dn, "DC=%s", name)) {
234                 return WERR_NOMEM;
235         }
236
237         return dnsserver_db_do_add_rec(mem_ctx, samdb, dn, NULL);
238 }
239
240
241 WERROR dnsserver_db_add_record(TALLOC_CTX *mem_ctx,
242                                         struct ldb_context *samdb,
243                                         struct dnsserver_zone *z,
244                                         const char *name,
245                                         struct DNS_RPC_RECORD *add_record)
246 {
247         const char * const attrs[] = { "dnsRecord", NULL };
248         struct ldb_result *res;
249         struct dnsp_DnssrvRpcRecord *rec;
250         struct ldb_message_element *el;
251         struct ldb_dn *dn;
252         enum ndr_err_code ndr_err;
253         NTTIME t;
254         int ret, i;
255         int serial;
256
257         rec = dns_to_dnsp_copy(mem_ctx, add_record);
258         W_ERROR_HAVE_NO_MEMORY(rec);
259
260         serial = dnsserver_update_soa(mem_ctx, samdb, z);
261         if (serial < 0) {
262                 return WERR_INTERNAL_DB_ERROR;
263         }
264
265         unix_to_nt_time(&t, time(NULL));
266         t /= 10*1000*1000; /* convert to seconds (NT time is in 100ns units) */
267         t /= 3600;         /* convert to hours */
268
269         rec->dwSerial = serial;
270         rec->dwTimeStamp = t;
271
272         ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs,
273                         "(&(objectClass=dnsNode)(name=%s))", name);
274         if (ret != LDB_SUCCESS) {
275                 return WERR_INTERNAL_DB_ERROR;
276         }
277
278         if (res->count == 0) {
279                 dn = dnsserver_name_to_dn(mem_ctx, z, name);
280                 W_ERROR_HAVE_NO_MEMORY(dn);
281
282                 return dnsserver_db_do_add_rec(mem_ctx, samdb, dn, rec);
283         }
284
285         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
286         if (el == NULL) {
287                 ret = ldb_msg_add_empty(res->msgs[0], "dnsRecord", 0, &el);
288                 if (ret != LDB_SUCCESS) {
289                         return WERR_NOMEM;
290                 }
291         }
292
293         for (i=0; i<el->num_values; i++) {
294                 struct dnsp_DnssrvRpcRecord rec2;
295
296                 ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2,
297                                                 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
298                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
299                         return WERR_GENERAL_FAILURE;
300                 }
301
302                 if (dns_record_match(rec, &rec2)) {
303                         break;
304                 }
305         }
306         if (i < el->num_values) {
307                 return WERR_DNS_ERROR_RECORD_ALREADY_EXISTS;
308         }
309         if (i == el->num_values) {
310                 /* adding a new value */
311                 el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values+1);
312                 W_ERROR_HAVE_NO_MEMORY(el->values);
313                 el->num_values++;
314         }
315
316         ndr_err = ndr_push_struct_blob(&el->values[i], mem_ctx, rec,
317                                         (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
318         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
319                 return WERR_GENERAL_FAILURE;
320         }
321
322         el->flags = LDB_FLAG_MOD_REPLACE;
323         ret = ldb_modify(samdb, res->msgs[0]);
324         if (ret != LDB_SUCCESS) {
325                 return WERR_INTERNAL_DB_ERROR;
326         }
327
328         return WERR_OK;
329 }
330
331 WERROR dnsserver_db_update_record(TALLOC_CTX *mem_ctx,
332                                         struct ldb_context *samdb,
333                                         struct dnsserver_zone *z,
334                                         const char *name,
335                                         struct DNS_RPC_RECORD *add_record,
336                                         struct DNS_RPC_RECORD *del_record)
337 {
338         const char * const attrs[] = { "dnsRecord", NULL };
339         struct ldb_result *res;
340         struct dnsp_DnssrvRpcRecord *arec, *drec;
341         struct ldb_message_element *el;
342         enum ndr_err_code ndr_err;
343         NTTIME t;
344         int ret, i;
345         int serial;
346
347         serial = dnsserver_update_soa(mem_ctx, samdb, z);
348         if (serial < 0) {
349                 return WERR_INTERNAL_DB_ERROR;
350         }
351
352         arec = dns_to_dnsp_copy(mem_ctx, add_record);
353         W_ERROR_HAVE_NO_MEMORY(arec);
354
355         drec = dns_to_dnsp_copy(mem_ctx, del_record);
356         W_ERROR_HAVE_NO_MEMORY(drec);
357
358         unix_to_nt_time(&t, time(NULL));
359         t /= 10*1000*1000;
360
361         arec->dwSerial = serial;
362         arec->dwTimeStamp = t;
363
364         ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs,
365                         "(&(objectClass=dnsNode)(name=%s))", name);
366         if (ret != LDB_SUCCESS) {
367                 return WERR_INTERNAL_DB_ERROR;
368         }
369
370         if (res->count == 0) {
371                 return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
372         }
373
374         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
375         if (el == NULL || el->num_values == 0) {
376                 return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
377         }
378
379         for (i=0; i<el->num_values; i++) {
380                 struct dnsp_DnssrvRpcRecord rec2;
381
382                 ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2,
383                                                 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
384                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
385                         return WERR_GENERAL_FAILURE;
386                 }
387
388                 if (dns_record_match(arec, &rec2)) {
389                         break;
390                 }
391         }
392         if (i < el->num_values) {
393                 return WERR_DNS_ERROR_RECORD_ALREADY_EXISTS;
394         }
395
396
397         for (i=0; i<el->num_values; i++) {
398                 struct dnsp_DnssrvRpcRecord rec2;
399
400                 ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2,
401                                                 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
402                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
403                         return WERR_GENERAL_FAILURE;
404                 }
405
406                 if (dns_record_match(drec, &rec2)) {
407                         break;
408                 }
409         }
410         if (i == el->num_values) {
411                 return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
412         }
413
414         ndr_err = ndr_push_struct_blob(&el->values[i], mem_ctx, arec,
415                                         (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
416         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
417                 return WERR_GENERAL_FAILURE;
418         }
419
420         el->flags = LDB_FLAG_MOD_REPLACE;
421         ret = ldb_modify(samdb, res->msgs[0]);
422         if (ret != LDB_SUCCESS) {
423                 return WERR_INTERNAL_DB_ERROR;
424         }
425
426         return WERR_OK;
427 }
428
429 WERROR dnsserver_db_delete_record(TALLOC_CTX *mem_ctx,
430                                         struct ldb_context *samdb,
431                                         struct dnsserver_zone *z,
432                                         const char *name,
433                                         struct DNS_RPC_RECORD *del_record)
434 {
435         const char * const attrs[] = { "dnsRecord", NULL };
436         struct ldb_result *res;
437         struct dnsp_DnssrvRpcRecord *rec;
438         struct ldb_message_element *el;
439         enum ndr_err_code ndr_err;
440         int ret, i;
441         int serial;
442
443         serial = dnsserver_update_soa(mem_ctx, samdb, z);
444         if (serial < 0) {
445                 return WERR_INTERNAL_DB_ERROR;
446         }
447
448         rec = dns_to_dnsp_copy(mem_ctx, del_record);
449         W_ERROR_HAVE_NO_MEMORY(rec);
450
451         ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs,
452                         "(&(objectClass=dnsNode)(name=%s))", name);
453         if (ret != LDB_SUCCESS) {
454                 return WERR_INTERNAL_DB_ERROR;
455         }
456
457         if (res->count == 0) {
458                 return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
459         }
460
461         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
462         if (el == NULL || el->num_values == 0) {
463                 return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
464         }
465
466         for (i=0; i<el->num_values; i++) {
467                 struct dnsp_DnssrvRpcRecord rec2;
468
469                 ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2,
470                                                 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
471                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
472                         return WERR_GENERAL_FAILURE;
473                 }
474
475                 if (dns_record_match(rec, &rec2)) {
476                         break;
477                 }
478         }
479         if (i == el->num_values) {
480                 return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
481         }
482         if (i < el->num_values-1) {
483                 memmove(&el->values[i], &el->values[i+1], sizeof(el->values[0])*((el->num_values-1)-i));
484         }
485         el->num_values--;
486
487         if (el->num_values == 0) {
488                 ret = ldb_delete(samdb, res->msgs[0]->dn);
489         } else {
490                 el->flags = LDB_FLAG_MOD_REPLACE;
491                 ret = ldb_modify(samdb, res->msgs[0]);
492         }
493         if (ret != LDB_SUCCESS) {
494                 return WERR_INTERNAL_DB_ERROR;
495         }
496
497         return WERR_OK;
498 }