s4 dns: Implement update record prescan logic
[gd/samba-autobuild/.git] / source4 / dns_server / dns_update.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    DNS server handler for update requests
5
6    Copyright (C) 2010 Kai Blin  <kai@samba.org>
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 "libcli/util/ntstatus.h"
24 #include "librpc/ndr/libndr.h"
25 #include "librpc/gen_ndr/ndr_dns.h"
26 #include "librpc/gen_ndr/ndr_dnsp.h"
27 #include <ldb.h>
28 #include "dsdb/samdb/samdb.h"
29 #include "dsdb/common/util.h"
30 #include "dns_server/dns_server.h"
31
32 static WERROR check_prerequsites(struct dns_server *dns,
33                                  TALLOC_CTX *mem_ctx,
34                                  const struct dns_name_packet *in,
35                                  const struct dns_res_rec *prereqs, uint16_t count)
36 {
37         const struct dns_name_question *zone;
38         size_t host_part_len = 0;
39         uint16_t i;
40
41         zone = in->questions;
42
43         for (i = 0; i < count; i++) {
44                 const struct dns_res_rec *r = &prereqs[i];
45                 bool match;
46
47                 if (r->ttl != 0) {
48                         return DNS_ERR(FORMAT_ERROR);
49                 }
50                 match = dns_name_match(zone->name, r->name, &host_part_len);
51                 if (!match) {
52                         /* TODO: check if we need to echo all prereqs if the
53                          * first fails */
54                         return DNS_ERR(NOTZONE);
55                 }
56                 if (r->rr_class == DNS_QCLASS_ANY) {
57                         if (r->length != 0) {
58                                 return DNS_ERR(FORMAT_ERROR);
59                         }
60                         if (r->rr_type == DNS_QTYPE_ALL) {
61                                 /* TODO: Check if zone has at least one RR */
62                                 return DNS_ERR(NAME_ERROR);
63                         } else {
64                                 /* TODO: Check if RR exists of the specified type */
65                                 return DNS_ERR(NXRRSET);
66                         }
67                         continue;
68                 }
69                 if (r->rr_class == DNS_QCLASS_NONE) {
70                         if (r->length != 0) {
71                                 return DNS_ERR(FORMAT_ERROR);
72                         }
73                         if (r->rr_type == DNS_QTYPE_ALL) {
74                                 /* TODO: Return this error if the given name exits in this zone */
75                                 return DNS_ERR(YXDOMAIN);
76                         } else {
77                                 /* TODO: Return error if there's an RRset of this type in the zone */
78                                 return DNS_ERR(YXRRSET);
79                         }
80                         continue;
81                 }
82                 if (r->rr_class == zone->question_class) {
83                         /* Check if there's a RR with this */
84                         return DNS_ERR(NOT_IMPLEMENTED);
85                 } else {
86                         return DNS_ERR(FORMAT_ERROR);
87                 }
88         }
89
90
91         return WERR_OK;
92 }
93
94 static WERROR update_prescan(const struct dns_name_question *zone,
95                              const struct dns_res_rec *updates, uint16_t count)
96 {
97         const struct dns_res_rec *r;
98         uint16_t i;
99         size_t host_part_len;
100         bool match;
101
102         for (i = 0; i < count; i++) {
103                 r = &updates[i];
104                 match = dns_name_match(zone->name, r->name, &host_part_len);
105                 if (!match) {
106                         return DNS_ERR(NOTZONE);
107                 }
108                 if (zone->question_class == r->rr_class) {
109                         /*TODO: also check for AXFR,MAILA,MAILB  */
110                         if (r->rr_type == DNS_QTYPE_ALL) {
111                                 return DNS_ERR(FORMAT_ERROR);
112                         }
113                 } else if (r->rr_class == DNS_QCLASS_ANY) {
114                         if (r->ttl != 0 || r->length != 0) {
115                                 return DNS_ERR(FORMAT_ERROR);
116                         }
117                 } else if (r->rr_class == DNS_QCLASS_NONE) {
118                         if (r->ttl != 0 || r->rr_type == DNS_QTYPE_ALL) {
119                                 return DNS_ERR(FORMAT_ERROR);
120                         }
121                 } else {
122                         return DNS_ERR(FORMAT_ERROR);
123                 }
124         }
125         return WERR_OK;
126 }
127
128 WERROR dns_server_process_update(struct dns_server *dns,
129                                  TALLOC_CTX *mem_ctx,
130                                  struct dns_name_packet *in,
131                                  const struct dns_res_rec *prereqs, uint16_t prereq_count,
132                                  struct dns_res_rec **updates,      uint16_t *update_count,
133                                  struct dns_res_rec **additional,   uint16_t *arcount)
134 {
135         struct dns_name_question *zone;
136         const struct dns_server_zone *z;
137         size_t host_part_len = 0;
138         WERROR werror = DNS_ERR(NOT_IMPLEMENTED);
139         bool update_allowed = false;
140
141         if (in->qdcount != 1) {
142                 return DNS_ERR(FORMAT_ERROR);
143         }
144
145         zone = in->questions;
146
147         if (zone->question_type != DNS_QTYPE_SOA) {
148                 return DNS_ERR(FORMAT_ERROR);
149         }
150
151         DEBUG(0, ("Got a dns update request.\n"));
152
153         for (z = dns->zones; z != NULL; z = z->next) {
154                 bool match;
155
156                 match = dns_name_match(z->name, zone->name, &host_part_len);
157                 if (match) {
158                         break;
159                 }
160         }
161
162         if (z == NULL) {
163                 return DNS_ERR(NOTAUTH);
164         }
165
166         if (host_part_len != 0) {
167                 /* TODO: We need to delegate this one */
168                 return DNS_ERR(NOT_IMPLEMENTED);
169         }
170
171         werror = check_prerequsites(dns, mem_ctx, in, prereqs, prereq_count);
172         W_ERROR_NOT_OK_RETURN(werror);
173
174         /* TODO: Check if update is allowed, we probably want "always",
175          * key-based GSSAPI, key-based bind-style TSIG and "never" as
176          * smb.conf options. */
177         if (!update_allowed) {
178                 return DNS_ERR(REFUSED);
179         }
180
181         werror = update_prescan(in->questions, *updates, *update_count);
182         W_ERROR_NOT_OK_RETURN(werror);
183
184         return werror;
185 }