s4:rpc-dnsserver: Implement EnumDirectoryPartition operation
[ddiss/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 #include "dsdb/samdb/samdb.h"
27
28 /* There are only 2 fixed partitions for DNS */
29 struct dnsserver_partition *dnsserver_db_enumerate_partitions(TALLOC_CTX *mem_ctx,
30                                                         struct dnsserver_serverinfo *serverinfo,
31                                                         struct ldb_context *samdb)
32 {
33         struct dnsserver_partition *partitions, *p;
34
35         partitions = NULL;
36
37         /* Domain partition */
38         p = talloc_zero(mem_ctx, struct dnsserver_partition);
39         if (p == NULL) {
40                 goto failed;
41         }
42
43         p->partition_dn = ldb_dn_new(p, samdb, serverinfo->pszDomainDirectoryPartition);
44         if (p->partition_dn == NULL) {
45                 goto failed;
46         }
47
48         p->pszDpFqdn = samdb_dn_to_dns_domain(p, p->partition_dn);
49         p->dwDpFlags = DNS_DP_AUTOCREATED | DNS_DP_DOMAIN_DEFAULT | DNS_DP_ENLISTED;
50         p->is_forest = false;
51
52         DLIST_ADD_END(partitions, p, NULL);
53
54         /* Forest Partition */
55         p = talloc_zero(mem_ctx, struct dnsserver_partition);
56         if (p == NULL) {
57                 goto failed;
58         }
59
60         p->partition_dn = ldb_dn_new(p, samdb, serverinfo->pszForestDirectoryPartition);
61         if (p->partition_dn == NULL) {
62                 goto failed;
63         }
64
65         p->pszDpFqdn = samdb_dn_to_dns_domain(p, p->partition_dn);
66         p->dwDpFlags = DNS_DP_AUTOCREATED | DNS_DP_FOREST_DEFAULT | DNS_DP_ENLISTED;
67         p->is_forest = true;
68
69         DLIST_ADD_END(partitions, p, NULL);
70
71         return partitions;
72
73 failed:
74         return NULL;
75
76 }
77
78
79 /* Search for all dnsZone records */
80 struct dnsserver_zone *dnsserver_db_enumerate_zones(TALLOC_CTX *mem_ctx,
81                                                 struct ldb_context *samdb,
82                                                 struct dnsserver_partition *p)
83 {
84         TALLOC_CTX *tmp_ctx;
85         const char * const attrs[] = {"name", NULL};
86         struct ldb_dn *dn;
87         struct ldb_result *res;
88         struct dnsserver_zone *zones, *z;
89         int i, ret;
90
91         tmp_ctx = talloc_new(mem_ctx);
92         if (tmp_ctx == NULL) {
93                 return NULL;
94         }
95
96         dn = ldb_dn_copy(tmp_ctx, p->partition_dn);
97         if (dn == NULL) {
98                 goto failed;
99         }
100         if (!ldb_dn_add_child_fmt(dn, "CN=MicrosoftDNS")) {
101                 goto failed;
102         }
103
104         ret = ldb_search(samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
105                           attrs, "(objectClass=dnsZone)");
106         if (ret != LDB_SUCCESS) {
107                 DEBUG(0, ("dnsserver: Failed to find DNS Zones in %s\n",
108                         ldb_dn_get_linearized(dn)));
109                 goto failed;
110         }
111
112         zones = NULL;
113         for(i=0; i<res->count; i++) {
114                 char *name;
115                 z = talloc_zero(mem_ctx, struct dnsserver_zone);
116                 if (z == NULL) {
117                         goto failed;
118                 }
119
120                 z->partition = p;
121                 name = talloc_strdup(z,
122                                 ldb_msg_find_attr_as_string(res->msgs[i], "name", NULL));
123                 if (strcmp(name, "RootDNSServers") == 0) {
124                         talloc_free(name);
125                         z->name = talloc_strdup(z, ".");
126                 } else {
127                         z->name = name;
128                 }
129                 z->zone_dn = talloc_steal(z, res->msgs[i]->dn);
130
131                 DLIST_ADD_END(zones, z, NULL);
132                 DEBUG(2, ("dnsserver: Found DNS zone %s\n", z->name));
133         }
134
135         return zones;
136
137 failed:
138         talloc_free(tmp_ctx);
139         return NULL;
140 }
141
142
143 static unsigned int dnsserver_update_soa(TALLOC_CTX *mem_ctx,
144                                 struct ldb_context *samdb,
145                                 struct dnsserver_zone *z)
146 {
147         const char * const attrs[] = { "dnsRecord", NULL };
148         struct ldb_result *res;
149         struct dnsp_DnssrvRpcRecord rec;
150         struct ldb_message_element *el;
151         enum ndr_err_code ndr_err;
152         int ret, i, serial = -1;
153         NTTIME t;
154
155         unix_to_nt_time(&t, time(NULL));
156         t /= 10*1000*1000; /* convert to seconds (NT time is in 100ns units) */
157         t /= 3600;         /* convert to hours */
158
159         ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs,
160                         "(&(objectClass=dnsNode)(name=@))");
161         if (ret != LDB_SUCCESS || res->count == 0) {
162                 return -1;
163         }
164
165         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
166         if (el == NULL) {
167                 return -1;
168         }
169
170         for (i=0; i<el->num_values; i++) {
171                 ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec,
172                                         (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
173                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
174                         continue;
175                 }
176
177                 if (rec.wType == DNS_TYPE_SOA) {
178                         serial = rec.data.soa.serial + 1;
179                         rec.dwSerial = serial;
180                         rec.dwTimeStamp = (uint32_t)t;
181                         rec.data.soa.serial = serial;
182
183                         ndr_err = ndr_push_struct_blob(&el->values[i], mem_ctx, &rec,
184                                         (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
185                         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
186                                 return -1;
187                         }
188                         break;
189                 }
190         }
191
192         if (serial != -1) {
193                 el->flags = LDB_FLAG_MOD_REPLACE;
194                 ret = ldb_modify(samdb, res->msgs[0]);
195                 if (ret != LDB_SUCCESS) {
196                         return -1;
197                 }
198         }
199
200         return serial;
201 }
202
203
204 static WERROR dnsserver_db_do_add_rec(TALLOC_CTX *mem_ctx,
205                                 struct ldb_context *samdb,
206                                 struct ldb_dn *dn,
207                                 struct dnsp_DnssrvRpcRecord *rec)
208 {
209         struct ldb_message *msg;
210         struct ldb_val v;
211         int ret;
212         enum ndr_err_code ndr_err;
213
214         msg = ldb_msg_new(mem_ctx);
215         W_ERROR_HAVE_NO_MEMORY(msg);
216
217         msg->dn = dn;
218         ret = ldb_msg_add_string(msg, "objectClass", "dnsNode");
219         if (ret != LDB_SUCCESS) {
220                 return WERR_NOMEM;
221         }
222
223         if (rec) {
224                 ndr_err = ndr_push_struct_blob(&v, mem_ctx, rec,
225                                 (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
226                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
227                         return WERR_GENERAL_FAILURE;
228                 }
229
230                 ret = ldb_msg_add_value(msg, "dnsRecord", &v, NULL);
231                 if (ret != LDB_SUCCESS) {
232                         return WERR_NOMEM;
233                 }
234         }
235
236         ret = ldb_add(samdb, msg);
237         if (ret != LDB_SUCCESS) {
238                 return WERR_INTERNAL_DB_ERROR;
239         }
240
241         return WERR_OK;
242 }
243
244
245 WERROR dnsserver_db_add_empty_node(TALLOC_CTX *mem_ctx,
246                                         struct ldb_context *samdb,
247                                         struct dnsserver_zone *z,
248                                         const char *name)
249 {
250         const char * const attrs[] = { "name", NULL };
251         struct ldb_result *res;
252         struct ldb_dn *dn;
253         int ret;
254
255         ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_BASE, attrs,
256                         "(&(objectClass=dnsNode)(name=%s))", name);
257         if (ret != LDB_SUCCESS) {
258                 return WERR_INTERNAL_DB_ERROR;
259         }
260
261         if (res->count > 0) {
262                 talloc_free(res);
263                 return WERR_DNS_ERROR_RECORD_ALREADY_EXISTS;
264         }
265
266         dn = ldb_dn_copy(mem_ctx, z->zone_dn);
267         W_ERROR_HAVE_NO_MEMORY(dn);
268
269         if (!ldb_dn_add_child_fmt(dn, "DC=%s", name)) {
270                 return WERR_NOMEM;
271         }
272
273         return dnsserver_db_do_add_rec(mem_ctx, samdb, dn, NULL);
274 }
275
276
277 WERROR dnsserver_db_add_record(TALLOC_CTX *mem_ctx,
278                                         struct ldb_context *samdb,
279                                         struct dnsserver_zone *z,
280                                         const char *name,
281                                         struct DNS_RPC_RECORD *add_record)
282 {
283         const char * const attrs[] = { "dnsRecord", NULL };
284         struct ldb_result *res;
285         struct dnsp_DnssrvRpcRecord *rec;
286         struct ldb_message_element *el;
287         struct ldb_dn *dn;
288         enum ndr_err_code ndr_err;
289         NTTIME t;
290         int ret, i;
291         int serial;
292
293         rec = dns_to_dnsp_copy(mem_ctx, add_record);
294         W_ERROR_HAVE_NO_MEMORY(rec);
295
296         serial = dnsserver_update_soa(mem_ctx, samdb, z);
297         if (serial < 0) {
298                 return WERR_INTERNAL_DB_ERROR;
299         }
300
301         unix_to_nt_time(&t, time(NULL));
302         t /= 10*1000*1000; /* convert to seconds (NT time is in 100ns units) */
303         t /= 3600;         /* convert to hours */
304
305         rec->dwSerial = serial;
306         rec->dwTimeStamp = t;
307
308         ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs,
309                         "(&(objectClass=dnsNode)(name=%s))", name);
310         if (ret != LDB_SUCCESS) {
311                 return WERR_INTERNAL_DB_ERROR;
312         }
313
314         if (res->count == 0) {
315                 dn = dnsserver_name_to_dn(mem_ctx, z, name);
316                 W_ERROR_HAVE_NO_MEMORY(dn);
317
318                 return dnsserver_db_do_add_rec(mem_ctx, samdb, dn, rec);
319         }
320
321         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
322         if (el == NULL) {
323                 ret = ldb_msg_add_empty(res->msgs[0], "dnsRecord", 0, &el);
324                 if (ret != LDB_SUCCESS) {
325                         return WERR_NOMEM;
326                 }
327         }
328
329         for (i=0; i<el->num_values; i++) {
330                 struct dnsp_DnssrvRpcRecord rec2;
331
332                 ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2,
333                                                 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
334                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
335                         return WERR_GENERAL_FAILURE;
336                 }
337
338                 if (dns_record_match(rec, &rec2)) {
339                         break;
340                 }
341         }
342         if (i < el->num_values) {
343                 return WERR_DNS_ERROR_RECORD_ALREADY_EXISTS;
344         }
345         if (i == el->num_values) {
346                 /* adding a new value */
347                 el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values+1);
348                 W_ERROR_HAVE_NO_MEMORY(el->values);
349                 el->num_values++;
350         }
351
352         ndr_err = ndr_push_struct_blob(&el->values[i], mem_ctx, rec,
353                                         (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
354         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
355                 return WERR_GENERAL_FAILURE;
356         }
357
358         el->flags = LDB_FLAG_MOD_REPLACE;
359         ret = ldb_modify(samdb, res->msgs[0]);
360         if (ret != LDB_SUCCESS) {
361                 return WERR_INTERNAL_DB_ERROR;
362         }
363
364         return WERR_OK;
365 }
366
367 WERROR dnsserver_db_update_record(TALLOC_CTX *mem_ctx,
368                                         struct ldb_context *samdb,
369                                         struct dnsserver_zone *z,
370                                         const char *name,
371                                         struct DNS_RPC_RECORD *add_record,
372                                         struct DNS_RPC_RECORD *del_record)
373 {
374         const char * const attrs[] = { "dnsRecord", NULL };
375         struct ldb_result *res;
376         struct dnsp_DnssrvRpcRecord *arec, *drec;
377         struct ldb_message_element *el;
378         enum ndr_err_code ndr_err;
379         NTTIME t;
380         int ret, i;
381         int serial;
382
383         serial = dnsserver_update_soa(mem_ctx, samdb, z);
384         if (serial < 0) {
385                 return WERR_INTERNAL_DB_ERROR;
386         }
387
388         arec = dns_to_dnsp_copy(mem_ctx, add_record);
389         W_ERROR_HAVE_NO_MEMORY(arec);
390
391         drec = dns_to_dnsp_copy(mem_ctx, del_record);
392         W_ERROR_HAVE_NO_MEMORY(drec);
393
394         unix_to_nt_time(&t, time(NULL));
395         t /= 10*1000*1000;
396
397         arec->dwSerial = serial;
398         arec->dwTimeStamp = t;
399
400         ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs,
401                         "(&(objectClass=dnsNode)(name=%s))", name);
402         if (ret != LDB_SUCCESS) {
403                 return WERR_INTERNAL_DB_ERROR;
404         }
405
406         if (res->count == 0) {
407                 return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
408         }
409
410         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
411         if (el == NULL || el->num_values == 0) {
412                 return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
413         }
414
415         for (i=0; i<el->num_values; i++) {
416                 struct dnsp_DnssrvRpcRecord rec2;
417
418                 ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2,
419                                                 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
420                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
421                         return WERR_GENERAL_FAILURE;
422                 }
423
424                 if (dns_record_match(arec, &rec2)) {
425                         break;
426                 }
427         }
428         if (i < el->num_values) {
429                 return WERR_DNS_ERROR_RECORD_ALREADY_EXISTS;
430         }
431
432
433         for (i=0; i<el->num_values; i++) {
434                 struct dnsp_DnssrvRpcRecord rec2;
435
436                 ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2,
437                                                 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
438                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
439                         return WERR_GENERAL_FAILURE;
440                 }
441
442                 if (dns_record_match(drec, &rec2)) {
443                         break;
444                 }
445         }
446         if (i == el->num_values) {
447                 return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
448         }
449
450         ndr_err = ndr_push_struct_blob(&el->values[i], mem_ctx, arec,
451                                         (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
452         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
453                 return WERR_GENERAL_FAILURE;
454         }
455
456         el->flags = LDB_FLAG_MOD_REPLACE;
457         ret = ldb_modify(samdb, res->msgs[0]);
458         if (ret != LDB_SUCCESS) {
459                 return WERR_INTERNAL_DB_ERROR;
460         }
461
462         return WERR_OK;
463 }
464
465 WERROR dnsserver_db_delete_record(TALLOC_CTX *mem_ctx,
466                                         struct ldb_context *samdb,
467                                         struct dnsserver_zone *z,
468                                         const char *name,
469                                         struct DNS_RPC_RECORD *del_record)
470 {
471         const char * const attrs[] = { "dnsRecord", NULL };
472         struct ldb_result *res;
473         struct dnsp_DnssrvRpcRecord *rec;
474         struct ldb_message_element *el;
475         enum ndr_err_code ndr_err;
476         int ret, i;
477         int serial;
478
479         serial = dnsserver_update_soa(mem_ctx, samdb, z);
480         if (serial < 0) {
481                 return WERR_INTERNAL_DB_ERROR;
482         }
483
484         rec = dns_to_dnsp_copy(mem_ctx, del_record);
485         W_ERROR_HAVE_NO_MEMORY(rec);
486
487         ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs,
488                         "(&(objectClass=dnsNode)(name=%s))", name);
489         if (ret != LDB_SUCCESS) {
490                 return WERR_INTERNAL_DB_ERROR;
491         }
492
493         if (res->count == 0) {
494                 return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
495         }
496
497         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
498         if (el == NULL || el->num_values == 0) {
499                 return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
500         }
501
502         for (i=0; i<el->num_values; i++) {
503                 struct dnsp_DnssrvRpcRecord rec2;
504
505                 ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2,
506                                                 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
507                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
508                         return WERR_GENERAL_FAILURE;
509                 }
510
511                 if (dns_record_match(rec, &rec2)) {
512                         break;
513                 }
514         }
515         if (i == el->num_values) {
516                 return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
517         }
518         if (i < el->num_values-1) {
519                 memmove(&el->values[i], &el->values[i+1], sizeof(el->values[0])*((el->num_values-1)-i));
520         }
521         el->num_values--;
522
523         if (el->num_values == 0) {
524                 ret = ldb_delete(samdb, res->msgs[0]->dn);
525         } else {
526                 el->flags = LDB_FLAG_MOD_REPLACE;
527                 ret = ldb_modify(samdb, res->msgs[0]);
528         }
529         if (ret != LDB_SUCCESS) {
530                 return WERR_INTERNAL_DB_ERROR;
531         }
532
533         return WERR_OK;
534 }