selftest: Add a basic test of samba_upgradedns
[Samba.git] / libcli / dns / dns_hosts_file.c
blob94d1d9704a3b58d8437dfab85f56d9b601a8526e
1 /*
2 Unix SMB/CIFS implementation.
4 read a file containing DNS names, types and IP addresses
6 Copyright (C) Andrew Tridgell 1994-1998
7 Copyright (C) Jeremy Allison 2007
8 Copyright (C) Andrew Bartlett 2009-2011
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 /* The purpose of this file is to read the file generated by the samba_dnsupdate script */
26 #include "includes.h"
27 #include "lib/util/xfile.h"
28 #include "lib/util/util_net.h"
29 #include "system/filesys.h"
30 #include "system/network.h"
31 #include "libcli/nbt/libnbt.h"
32 #include "libcli/dns/dns.h"
34 #ifdef strcasecmp
35 #undef strcasecmp
36 #endif
38 /********************************************************
39 Start parsing the dns_hosts_file file.
40 *********************************************************/
42 static XFILE *startdns_hosts_file(const char *fname)
44 XFILE *fp = x_fopen(fname,O_RDONLY, 0);
45 if (!fp) {
46 DEBUG(4,("startdns_hosts_file: Can't open dns_hosts_file file %s. "
47 "Error was %s\n",
48 fname, strerror(errno)));
49 return NULL;
51 return fp;
54 /********************************************************
55 Parse the next line in the dns_hosts_file file.
56 *********************************************************/
58 static bool getdns_hosts_fileent(TALLOC_CTX *ctx, XFILE *fp, char **pp_name, char **pp_name_type,
59 char **pp_next_name,
60 struct sockaddr_storage *pss, uint32_t *p_port)
62 char line[1024];
64 *pp_name = NULL;
65 *pp_name_type = NULL;
66 *pp_next_name = NULL;
67 *p_port = 0;
69 while(!x_feof(fp) && !x_ferror(fp)) {
70 char *name_type = NULL;
71 char *name = NULL;
72 char *next_name = NULL;
73 char *ip = NULL;
74 char *port = NULL;
76 const char *ptr;
77 int count = 0;
79 if (!fgets_slash(line,sizeof(line),fp)) {
80 continue;
83 if (*line == '#') {
84 continue;
87 ptr = line;
89 if (next_token_talloc(ctx, &ptr, &name_type, NULL))
90 ++count;
91 if (next_token_talloc(ctx, &ptr, &name, NULL))
92 ++count;
93 if (name_type && strcasecmp(name_type, "A") == 0) {
94 if (next_token_talloc(ctx, &ptr, &ip, NULL))
95 ++count;
96 } else if (name_type && strcasecmp(name_type, "SRV") == 0) {
97 if (next_token_talloc(ctx, &ptr, &next_name, NULL))
98 ++count;
99 if (next_token_talloc(ctx, &ptr, &port, NULL))
100 ++count;
101 } else if (name_type && strcasecmp(name_type, "CNAME") == 0) {
102 if (next_token_talloc(ctx, &ptr, &next_name, NULL))
103 ++count;
105 if (count <= 0)
106 continue;
108 if (strcasecmp(name_type, "A") == 0) {
109 if (count != 3) {
110 DEBUG(0,("getdns_hosts_fileent: Ill formed hosts A record [%s]\n",
111 line));
112 continue;
114 DEBUG(4, ("getdns_hosts_fileent: host entry: %s %s %s\n",
115 name_type, name, ip));
116 if (!interpret_string_addr(pss, ip, AI_NUMERICHOST)) {
117 DEBUG(0,("getdns_hosts_fileent: invalid address "
118 "%s.\n", ip));
121 } else if (strcasecmp(name_type, "SRV") == 0) {
122 if (count != 4) {
123 DEBUG(0,("getdns_hosts_fileent: Ill formed hosts SRV record [%s]\n",
124 line));
125 continue;
127 *p_port = strtoul(port, NULL, 10);
128 if (*p_port == UINT32_MAX) {
129 DEBUG(0, ("getdns_hosts_fileent: Ill formed hosts SRV record [%s] (invalid port: %s)\n",
130 line, port));
131 continue;
133 DEBUG(4, ("getdns_hosts_fileent: host entry: %s %s %s %u\n",
134 name_type, name, next_name, (unsigned int)*p_port));
135 *pp_next_name = talloc_strdup(ctx, next_name);
136 if (!*pp_next_name) {
137 return false;
139 } else if (strcasecmp(name_type, "CNAME") == 0) {
140 if (count != 3) {
141 DEBUG(0,("getdns_hosts_fileent: Ill formed hosts CNAME record [%s]\n",
142 line));
143 continue;
145 DEBUG(4, ("getdns_hosts_fileent: host entry: %s %s %s\n",
146 name_type, name, next_name));
147 *pp_next_name = talloc_strdup(ctx, next_name);
148 if (!*pp_next_name) {
149 return false;
151 } else {
152 DEBUG(0,("getdns_hosts_fileent: unknown type %s\n", name_type));
153 continue;
156 *pp_name = talloc_strdup(ctx, name);
157 if (!*pp_name) {
158 return false;
161 *pp_name_type = talloc_strdup(ctx, name_type);
162 if (!*pp_name_type) {
163 return false;
165 return true;
168 return false;
171 /********************************************************
172 Finish parsing the dns_hosts_file file.
173 *********************************************************/
175 static void enddns_hosts_file(XFILE *fp)
177 x_fclose(fp);
180 /********************************************************
181 Resolve via "dns_hosts" method.
182 *********************************************************/
184 static NTSTATUS resolve_dns_hosts_file_as_dns_rr_recurse(const char *dns_hosts_file,
185 const char *name, bool srv_lookup,
186 int level, uint32_t port,
187 TALLOC_CTX *mem_ctx,
188 struct dns_rr_srv **return_rr,
189 int *return_count)
192 * "dns_hosts" means parse the local dns_hosts file.
195 XFILE *fp;
196 char *host_name = NULL;
197 char *name_type = NULL;
198 char *next_name = NULL;
199 struct sockaddr_storage return_ss;
200 uint32_t srv_port;
201 NTSTATUS status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
202 TALLOC_CTX *ctx = NULL;
203 TALLOC_CTX *ip_list_ctx = NULL;
204 struct dns_rr_srv *rr = NULL;
206 *return_rr = NULL;
208 /* Don't recurse forever, even on our own flat files */
209 if (level > 11) {
210 DEBUG(0, ("resolve_dns_hosts_file recursion limit reached looking up %s!\n", name));
211 return status;
214 *return_count = 0;
216 DEBUG(3,("resolve_dns_hosts: (%d) "
217 "Attempting %s dns_hosts lookup for name %s\n",
218 level, srv_lookup ? "SRV" : "A", name));
220 fp = startdns_hosts_file(dns_hosts_file);
222 if ( fp == NULL )
223 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
225 ip_list_ctx = talloc_new(mem_ctx);
226 if (!ip_list_ctx) {
227 enddns_hosts_file(fp);
228 return NT_STATUS_NO_MEMORY;
231 ctx = talloc_new(ip_list_ctx);
232 if (!ctx) {
233 talloc_free(ip_list_ctx);
234 enddns_hosts_file(fp);
235 return NT_STATUS_NO_MEMORY;
238 while (getdns_hosts_fileent(ctx, fp, &host_name, &name_type, &next_name, &return_ss, &srv_port)) {
239 if (!strequal(name, host_name)) {
240 /* continue at the bottom of the loop */
241 } else if (srv_lookup) {
242 if (strcasecmp(name_type, "SRV") == 0) {
243 NTSTATUS status_recurse;
244 struct dns_rr_srv *tmp_rr;
245 int tmp_count = 0;
246 /* we only accept one host name per SRV entry */
247 status_recurse
248 = resolve_dns_hosts_file_as_dns_rr_recurse(dns_hosts_file, next_name,
249 false,
250 level + 1, srv_port,
251 ip_list_ctx, &tmp_rr,
252 &tmp_count);
253 if (NT_STATUS_EQUAL(status_recurse, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
254 /* Don't fail on a dangling SRV record */
255 } else if (!NT_STATUS_IS_OK(status_recurse)) {
256 enddns_hosts_file(fp);
257 talloc_free(ip_list_ctx);
258 return status_recurse;
259 } else if (tmp_count != 1) {
260 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
261 } else {
262 status = status_recurse;
263 rr = talloc_realloc(ip_list_ctx, rr, struct dns_rr_srv, (*return_count) + 1);
264 if (!rr) {
265 enddns_hosts_file(fp);
266 return NT_STATUS_NO_MEMORY;
268 talloc_steal(rr, tmp_rr);
269 rr[*return_count] = *tmp_rr;
270 *return_count = (*return_count) + 1;
273 } else if (strcasecmp(name_type, "CNAME") == 0) {
274 /* we only accept one host name per CNAME */
275 enddns_hosts_file(fp);
276 status = resolve_dns_hosts_file_as_dns_rr_recurse(dns_hosts_file, next_name, false,
277 level + 1, port,
278 mem_ctx, return_rr, return_count);
279 talloc_free(ip_list_ctx);
280 return status;
281 } else if (strcasecmp(name_type, "A") == 0) {
282 if (*return_count == 0) {
283 /* We are happy to keep looking for other possible A record matches */
284 rr = talloc_zero(ip_list_ctx,
285 struct dns_rr_srv);
287 if (rr == NULL) {
288 TALLOC_FREE(ctx);
289 enddns_hosts_file(fp);
290 DEBUG(3,("resolve_dns_hosts: talloc_realloc fail !\n"));
291 return NT_STATUS_NO_MEMORY;
294 rr->hostname = talloc_strdup(rr, host_name);
296 if (rr->hostname == NULL) {
297 TALLOC_FREE(ctx);
298 enddns_hosts_file(fp);
299 DEBUG(3,("resolve_dns_hosts: talloc_realloc fail !\n"));
300 return NT_STATUS_NO_MEMORY;
302 rr->port = port;
304 *return_count = 1;
307 /* Set the specified port (possibly from a SRV lookup) into the structure we return */
308 set_sockaddr_port((struct sockaddr *)&return_ss, port);
310 /* We are happy to keep looking for other possible A record matches */
311 rr->ss_s = talloc_realloc(rr, rr->ss_s,
312 struct sockaddr_storage,
313 rr->num_ips + 1);
315 if (rr->ss_s == NULL) {
316 TALLOC_FREE(ctx);
317 enddns_hosts_file(fp);
318 DEBUG(3,("resolve_dns_hosts: talloc_realloc fail !\n"));
319 return NT_STATUS_NO_MEMORY;
322 rr->ss_s[rr->num_ips] = return_ss;
323 rr->num_ips += 1;
325 /* we found something */
326 status = NT_STATUS_OK;
329 TALLOC_FREE(ctx);
330 ctx = talloc_new(mem_ctx);
331 if (!ctx) {
332 enddns_hosts_file(fp);
333 return NT_STATUS_NO_MEMORY;
337 *return_rr = talloc_steal(mem_ctx, rr);
338 TALLOC_FREE(ip_list_ctx);
339 enddns_hosts_file(fp);
340 return status;
343 /********************************************************
344 Resolve via "dns_hosts_file" method, returning a list of sockaddr_storage values
345 *********************************************************/
347 NTSTATUS resolve_dns_hosts_file_as_sockaddr(const char *dns_hosts_file,
348 const char *name, bool srv_lookup,
349 TALLOC_CTX *mem_ctx,
350 struct sockaddr_storage **return_iplist,
351 int *return_count)
353 NTSTATUS status;
354 struct dns_rr_srv *dns_rr = NULL;
355 int i, j, rr_count = 0;
357 *return_iplist = NULL;
358 *return_count = 0;
360 status = resolve_dns_hosts_file_as_dns_rr_recurse(dns_hosts_file, name, srv_lookup,
361 0, 0,
362 mem_ctx, &dns_rr, &rr_count);
363 if (!NT_STATUS_IS_OK(status)) {
364 DEBUG(3,("resolve_dns_hosts (sockaddr): "
365 "failed to obtain %s result records for for name %s: %s\n",
366 srv_lookup ? "SRV" : "A", name, nt_errstr(status)));
367 return status;
370 for (i=0; i < rr_count; i++) {
371 *return_iplist = talloc_realloc(mem_ctx, *return_iplist, struct sockaddr_storage, *return_count + dns_rr[i].num_ips);
372 if (!*return_iplist) {
373 return NT_STATUS_NO_MEMORY;
375 for (j=0; j < dns_rr[i].num_ips; j++) {
376 (*return_iplist)[*return_count] = dns_rr[i].ss_s[j];
377 *return_count = *return_count + 1;
380 DEBUG(3,("resolve_dns_hosts (sockaddr): "
381 "Found %d results for for name %s\n",
382 *return_count, name));
384 return NT_STATUS_OK;
387 /********************************************************
388 Resolve via "dns_hosts_file" method, returning struct dns_rr_srv
389 *********************************************************/
391 NTSTATUS resolve_dns_hosts_file_as_dns_rr(const char *dns_hosts_file,
392 const char *name, bool srv_lookup,
393 TALLOC_CTX *mem_ctx,
394 struct dns_rr_srv **return_rr,
395 int *return_count)
397 NTSTATUS status;
398 *return_rr = NULL;
399 *return_count = 0;
401 status = resolve_dns_hosts_file_as_dns_rr_recurse(dns_hosts_file, name, srv_lookup,
402 0, 0,
403 mem_ctx, return_rr, return_count);
405 if (NT_STATUS_IS_OK(status)) {
406 DEBUG(3,("resolve_dns_hosts (dns_rr): "
407 "Found %d %s result records for for name %s\n",
408 *return_count, srv_lookup ? "SRV" : "A", name));
409 } else {
410 DEBUG(3,("resolve_dns_hosts (dns_rr): "
411 "failed to obtain %s result records for for name %s: %s\n",
412 srv_lookup ? "SRV" : "A", name, nt_errstr(status)));
414 return status;