s4 dns: Update requests with QCLASS != IN or ALL trigger NOTIMPLEMENTED errors
[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                 }
68                 if (r->rr_class == DNS_QCLASS_NONE) {
69                         if (r->length != 0) {
70                                 return DNS_ERR(FORMAT_ERROR);
71                         }
72                         if (r->rr_type == DNS_QTYPE_ALL) {
73                                 /* TODO: Return this error if the given name exits in this zone */
74                                 return DNS_ERR(YXDOMAIN);
75                         } else {
76                                 /* TODO: Return error if there's an RRset of this type in the zone */
77                                 return DNS_ERR(YXRRSET);
78                         }
79                 }
80                 if (r->rr_class == zone->question_class) {
81                         /* Check if there's a RR with this */
82                         return DNS_ERR(NOT_IMPLEMENTED);
83                 } else {
84                         return DNS_ERR(FORMAT_ERROR);
85                 }
86         }
87
88
89         return WERR_OK;
90 }
91
92 static WERROR update_prescan(const struct dns_name_question *zone,
93                              const struct dns_res_rec *updates, uint16_t count)
94 {
95         const struct dns_res_rec *r;
96         uint16_t i;
97         size_t host_part_len;
98         bool match;
99
100         for (i = 0; i < count; i++) {
101                 r = &updates[i];
102                 match = dns_name_match(zone->name, r->name, &host_part_len);
103                 if (!match) {
104                         return DNS_ERR(NOTZONE);
105                 }
106                 if (zone->question_class == r->rr_class) {
107                         /*TODO: also check for AXFR,MAILA,MAILB  */
108                         if (r->rr_type == DNS_QTYPE_ALL) {
109                                 return DNS_ERR(FORMAT_ERROR);
110                         }
111                 } else if (r->rr_class == DNS_QCLASS_ANY) {
112                         if (r->ttl != 0 || r->length != 0) {
113                                 return DNS_ERR(FORMAT_ERROR);
114                         }
115                 } else if (r->rr_class == DNS_QCLASS_NONE) {
116                         if (r->ttl != 0 || r->rr_type == DNS_QTYPE_ALL) {
117                                 return DNS_ERR(FORMAT_ERROR);
118                         }
119                 } else {
120                         return DNS_ERR(FORMAT_ERROR);
121                 }
122         }
123         return WERR_OK;
124 }
125
126 WERROR dns_server_process_update(struct dns_server *dns,
127                                  TALLOC_CTX *mem_ctx,
128                                  struct dns_name_packet *in,
129                                  struct dns_res_rec *prereqs,     uint16_t prereq_count,
130                                  struct dns_res_rec **updates,    uint16_t *update_count,
131                                  struct dns_res_rec **additional, uint16_t *arcount)
132 {
133         struct dns_name_question *zone;
134         const struct dns_server_zone *z;
135         size_t host_part_len = 0;
136         WERROR werror = DNS_ERR(NOT_IMPLEMENTED);
137         bool update_allowed = false;
138
139         if (in->qdcount != 1) {
140                 return DNS_ERR(FORMAT_ERROR);
141         }
142
143         zone = &in->questions[0];
144
145         if (zone->question_class != DNS_QCLASS_IN &&
146             zone->question_class != DNS_QCLASS_ANY) {
147                 return DNS_ERR(NOT_IMPLEMENTED);
148         }
149
150         if (zone->question_type != DNS_QTYPE_SOA) {
151                 return DNS_ERR(FORMAT_ERROR);
152         }
153
154         DEBUG(0, ("Got a dns update request.\n"));
155
156         for (z = dns->zones; z != NULL; z = z->next) {
157                 bool match;
158
159                 match = dns_name_match(z->name, zone->name, &host_part_len);
160                 if (match) {
161                         break;
162                 }
163         }
164
165         if (z == NULL) {
166                 return DNS_ERR(NOTAUTH);
167         }
168
169         if (host_part_len != 0) {
170                 /* TODO: We need to delegate this one */
171                 return DNS_ERR(NOT_IMPLEMENTED);
172         }
173
174         werror = check_prerequsites(dns, mem_ctx, in, prereqs, prereq_count);
175         W_ERROR_NOT_OK_RETURN(werror);
176
177         /* TODO: Check if update is allowed, we probably want "always",
178          * key-based GSSAPI, key-based bind-style TSIG and "never" as
179          * smb.conf options. */
180         if (!update_allowed) {
181                 return DNS_ERR(REFUSED);
182         }
183
184         werror = update_prescan(in->questions, *updates, *update_count);
185         W_ERROR_NOT_OK_RETURN(werror);
186
187         return werror;
188 }