373047765b2deb864da10515a6b5fb8f5856f323
[obnox/samba/samba-obnox.git] / libcli / dns / dns_hosts_file.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    read a file containing DNS names, types and IP addresses
5
6    Copyright (C) Andrew Tridgell 1994-1998
7    Copyright (C) Jeremy Allison 2007
8    Copyright (C) Andrew Bartlett 2009-2011
9
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.
14
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.
19
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/>.
22 */
23
24 /* The purpose of this file is to read the file generated by the samba_dnsupdate script */
25
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"
33
34 #ifdef strcasecmp
35 #undef strcasecmp
36 #endif
37
38 /********************************************************
39  Start parsing the dns_hosts_file file.
40 *********************************************************/
41
42 static XFILE *startdns_hosts_file(const char *fname)
43 {
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;
50         }
51         return fp;
52 }
53
54 /********************************************************
55  Parse the next line in the dns_hosts_file file.
56 *********************************************************/
57
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)
61 {
62         char line[1024];
63
64         *pp_name = NULL;
65         *pp_name_type = NULL;
66         *pp_next_name = NULL;
67         *p_port = 0;
68
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;
75
76                 const char *ptr;
77                 int count = 0;
78
79                 if (!fgets_slash(line,sizeof(line),fp)) {
80                         continue;
81                 }
82
83                 if (*line == '#') {
84                         continue;
85                 }
86
87                 ptr = line;
88
89                 if (next_token_talloc(ctx, &ptr, &name_type, NULL))
90                         ++count;
91                 if (count == 0) {
92                         continue;
93                 }
94                 if (next_token_talloc(ctx, &ptr, &name, NULL))
95                         ++count;
96                 if ((strcasecmp(name_type, "A") == 0) ||
97                     (strcasecmp(name_type, "AAAA") == 0))
98                 {
99                         if (next_token_talloc(ctx, &ptr, &ip, NULL))
100                                 ++count;
101                 } else if (name_type && strcasecmp(name_type, "SRV") == 0) {
102                         if (next_token_talloc(ctx, &ptr, &next_name, NULL))
103                                 ++count;
104                         if (next_token_talloc(ctx, &ptr, &port, NULL))
105                                 ++count;
106                 } else if (name_type && strcasecmp(name_type, "CNAME") == 0) {
107                         if (next_token_talloc(ctx, &ptr, &next_name, NULL))
108                                 ++count;
109                 } else if (name_type && strcasecmp(name_type, "NS") == 0) {
110                         if (next_token_talloc(ctx, &ptr, &next_name, NULL))
111                                 ++count;
112                 }
113
114                 if ((strcasecmp(name_type, "A") == 0) ||
115                     (strcasecmp(name_type, "AAAA") == 0))
116                 {
117                         if (count != 3) {
118                                 DEBUG(0,("getdns_hosts_fileent: Ill formed hosts A[AAA] record [%s]\n",
119                                          line));
120                                 continue;
121                         }
122                         DEBUG(4, ("getdns_hosts_fileent: host entry: %s %s %s\n",
123                                   name_type, name, ip));
124                         if (!interpret_string_addr(pss, ip, AI_NUMERICHOST)) {
125                                 DEBUG(0,("getdns_hosts_fileent: invalid address "
126                                          "%s.\n", ip));
127                         }
128
129                 } else if (strcasecmp(name_type, "SRV") == 0) {
130                         if (count != 4) {
131                                 DEBUG(0,("getdns_hosts_fileent: Ill formed hosts SRV record [%s]\n",
132                                          line));
133                                 continue;
134                         }
135                         *p_port = strtoul(port, NULL, 10);
136                         if (*p_port == UINT32_MAX) {
137                                 DEBUG(0, ("getdns_hosts_fileent: Ill formed hosts SRV record [%s] (invalid port: %s)\n",
138                                           line, port));
139                                 continue;
140                         }
141                         DEBUG(4, ("getdns_hosts_fileent: host entry: %s %s %s %u\n",
142                                   name_type, name, next_name, (unsigned int)*p_port));
143                         *pp_next_name = talloc_strdup(ctx, next_name);
144                         if (!*pp_next_name) {
145                                 return false;
146                         }
147                 } else if (strcasecmp(name_type, "CNAME") == 0) {
148                         if (count != 3) {
149                                 DEBUG(0,("getdns_hosts_fileent: Ill formed hosts CNAME record [%s]\n",
150                                          line));
151                                 continue;
152                         }
153                         DEBUG(4, ("getdns_hosts_fileent: host entry: %s %s %s\n",
154                                   name_type, name, next_name));
155                         *pp_next_name = talloc_strdup(ctx, next_name);
156                         if (!*pp_next_name) {
157                                 return false;
158                         }
159                 } else if (strcasecmp(name_type, "NS") == 0) {
160                         if (count != 3) {
161                                 DEBUG(0,("getdns_hosts_fileent: Ill formed hosts NS record [%s]\n",
162                                          line));
163                                 continue;
164                         }
165                         DEBUG(4, ("getdns_hosts_fileent: NS entry: %s %s %s\n",
166                                   name_type, name, next_name));
167                         continue;
168                 } else {
169                         DEBUG(0,("getdns_hosts_fileent: unknown type %s\n", name_type));
170                         continue;
171                 }
172
173                 *pp_name = talloc_strdup(ctx, name);
174                 if (!*pp_name) {
175                         return false;
176                 }
177
178                 *pp_name_type = talloc_strdup(ctx, name_type);
179                 if (!*pp_name_type) {
180                         return false;
181                 }
182                 return true;
183         }
184
185         return false;
186 }
187
188 /********************************************************
189  Finish parsing the dns_hosts_file file.
190 *********************************************************/
191
192 static void enddns_hosts_file(XFILE *fp)
193 {
194         x_fclose(fp);
195 }
196
197 /********************************************************
198  Resolve via "dns_hosts" method.
199 *********************************************************/
200
201 static NTSTATUS resolve_dns_hosts_file_as_dns_rr_recurse(const char *dns_hosts_file,
202                                                          const char *name, bool srv_lookup,
203                                                          int level, uint32_t port,
204                                                          TALLOC_CTX *mem_ctx,
205                                                          struct dns_rr_srv **return_rr,
206                                                          int *return_count)
207 {
208         /*
209          * "dns_hosts" means parse the local dns_hosts file.
210          */
211
212         XFILE *fp;
213         char *host_name = NULL;
214         char *name_type = NULL;
215         char *next_name = NULL;
216         struct sockaddr_storage return_ss;
217         uint32_t srv_port;
218         NTSTATUS status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
219         TALLOC_CTX *ctx = NULL;
220         TALLOC_CTX *ip_list_ctx = NULL;
221         struct dns_rr_srv *rr = NULL;
222
223         *return_rr = NULL;
224
225         /* Don't recurse forever, even on our own flat files */
226         if (level > 11) {
227                 DEBUG(0, ("resolve_dns_hosts_file recursion limit reached looking up %s!\n", name));
228                 return status;
229         }
230
231         *return_count = 0;
232
233         DEBUG(3,("resolve_dns_hosts: (%d) "
234                  "Attempting %s dns_hosts lookup for name %s\n",
235                  level, srv_lookup ? "SRV" : "A[AAA]", name));
236
237         fp = startdns_hosts_file(dns_hosts_file);
238
239         if ( fp == NULL )
240                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
241
242         ip_list_ctx = talloc_new(mem_ctx);
243         if (!ip_list_ctx) {
244                 enddns_hosts_file(fp);
245                 return NT_STATUS_NO_MEMORY;
246         }
247
248         ctx = talloc_new(ip_list_ctx);
249         if (!ctx) {
250                 talloc_free(ip_list_ctx);
251                 enddns_hosts_file(fp);
252                 return NT_STATUS_NO_MEMORY;
253         }
254
255         while (getdns_hosts_fileent(ctx, fp, &host_name, &name_type, &next_name, &return_ss, &srv_port)) {
256                 if (!strequal(name, host_name)) {
257                         /* continue at the bottom of the loop */
258                 } else if (srv_lookup) {
259                         if (strcasecmp(name_type, "SRV") == 0) {
260                                 NTSTATUS status_recurse;
261                                 struct dns_rr_srv *tmp_rr;
262                                 int tmp_count = 0;
263                                 /* we only accept one host name per SRV entry */
264                                 status_recurse
265                                         = resolve_dns_hosts_file_as_dns_rr_recurse(dns_hosts_file, next_name,
266                                                                                      false,
267                                                                                      level + 1, srv_port,
268                                                                                      ip_list_ctx, &tmp_rr,
269                                                                                      &tmp_count);
270                                 if (NT_STATUS_EQUAL(status_recurse, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
271                                         /* Don't fail on a dangling SRV record */
272                                 } else if (!NT_STATUS_IS_OK(status_recurse)) {
273                                         enddns_hosts_file(fp);
274                                         talloc_free(ip_list_ctx);
275                                         return status_recurse;
276                                 } else if (tmp_count != 1) {
277                                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
278                                 } else {
279                                         status = status_recurse;
280                                         rr = talloc_realloc(ip_list_ctx, rr, struct dns_rr_srv, (*return_count) + 1);
281                                         if (!rr) {
282                                                 enddns_hosts_file(fp);
283                                                 return NT_STATUS_NO_MEMORY;
284                                         }
285                                         talloc_steal(rr, tmp_rr);
286                                         rr[*return_count] = *tmp_rr;
287                                         *return_count = (*return_count) + 1;
288                                 }
289                         }
290                 } else if (strcasecmp(name_type, "CNAME") == 0) {
291                         /* we only accept one host name per CNAME */
292                         enddns_hosts_file(fp);
293                         status = resolve_dns_hosts_file_as_dns_rr_recurse(dns_hosts_file, next_name, false,
294                                                                           level + 1, port,
295                                                                           mem_ctx, return_rr, return_count);
296                         talloc_free(ip_list_ctx);
297                         return status;
298                 } else if ((strcasecmp(name_type, "A") == 0) ||
299                            (strcasecmp(name_type, "AAAA") == 0))
300                 {
301                         if (*return_count == 0) {
302                                 /* We are happy to keep looking for other possible A record matches */
303                                 rr = talloc_zero(ip_list_ctx,
304                                                   struct dns_rr_srv);
305
306                                 if (rr == NULL) {
307                                         TALLOC_FREE(ctx);
308                                         enddns_hosts_file(fp);
309                                         DEBUG(3,("resolve_dns_hosts: talloc_realloc fail !\n"));
310                                         return NT_STATUS_NO_MEMORY;
311                                 }
312
313                                 rr->hostname = talloc_strdup(rr, host_name);
314
315                                 if (rr->hostname == 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;
320                                 }
321                                 rr->port = port;
322
323                                 *return_count = 1;
324                         }
325
326                         /* Set the specified port (possibly from a SRV lookup) into the structure we return */
327                         set_sockaddr_port((struct sockaddr *)&return_ss, port);
328
329                         /* We are happy to keep looking for other possible A record matches */
330                         rr->ss_s = talloc_realloc(rr, rr->ss_s,
331                                                   struct sockaddr_storage,
332                                                   rr->num_ips + 1);
333
334                         if (rr->ss_s == NULL) {
335                                 TALLOC_FREE(ctx);
336                                 enddns_hosts_file(fp);
337                                 DEBUG(3,("resolve_dns_hosts: talloc_realloc fail !\n"));
338                                 return NT_STATUS_NO_MEMORY;
339                         }
340
341                         rr->ss_s[rr->num_ips] = return_ss;
342                         rr->num_ips += 1;
343
344                         /* we found something */
345                         status = NT_STATUS_OK;
346                 }
347
348                 TALLOC_FREE(ctx);
349                 ctx = talloc_new(mem_ctx);
350                 if (!ctx) {
351                         enddns_hosts_file(fp);
352                         return NT_STATUS_NO_MEMORY;
353                 }
354         }
355
356         *return_rr = talloc_steal(mem_ctx, rr);
357         TALLOC_FREE(ip_list_ctx);
358         enddns_hosts_file(fp);
359         return status;
360 }
361
362 /********************************************************
363  Resolve via "dns_hosts_file" method, returning a list of sockaddr_storage values
364 *********************************************************/
365
366 NTSTATUS resolve_dns_hosts_file_as_sockaddr(const char *dns_hosts_file,
367                                             const char *name, bool srv_lookup,
368                                             TALLOC_CTX *mem_ctx,
369                                             struct sockaddr_storage **return_iplist,
370                                             int *return_count)
371 {
372         NTSTATUS status;
373         struct dns_rr_srv *dns_rr = NULL;
374         int i, j, rr_count = 0;
375
376         *return_iplist = NULL;
377         *return_count = 0;
378
379         status = resolve_dns_hosts_file_as_dns_rr_recurse(dns_hosts_file, name, srv_lookup,
380                                                           0, 0,
381                                                           mem_ctx, &dns_rr, &rr_count);
382         if (!NT_STATUS_IS_OK(status)) {
383                 DEBUG(3,("resolve_dns_hosts (sockaddr): "
384                          "failed to obtain %s result records for for name %s: %s\n",
385                          srv_lookup ? "SRV" : "A", name, nt_errstr(status)));
386                 return status;
387         }
388
389         for (i=0; i < rr_count; i++) {
390                 *return_iplist = talloc_realloc(mem_ctx, *return_iplist, struct sockaddr_storage, *return_count + dns_rr[i].num_ips);
391                 if (!*return_iplist) {
392                         return NT_STATUS_NO_MEMORY;
393                 }
394                 for (j=0; j < dns_rr[i].num_ips; j++) {
395                         (*return_iplist)[*return_count] = dns_rr[i].ss_s[j];
396                         *return_count = *return_count + 1;
397                 }
398         }
399         DEBUG(3,("resolve_dns_hosts (sockaddr): "
400                 "Found %d results for for name %s\n",
401                  *return_count, name));
402
403         return NT_STATUS_OK;
404 }
405
406 /********************************************************
407  Resolve via "dns_hosts_file" method, returning struct dns_rr_srv
408 *********************************************************/
409
410 NTSTATUS resolve_dns_hosts_file_as_dns_rr(const char *dns_hosts_file,
411                                             const char *name, bool srv_lookup,
412                                             TALLOC_CTX *mem_ctx,
413                                             struct dns_rr_srv **return_rr,
414                                             int *return_count)
415 {
416         NTSTATUS status;
417         *return_rr = NULL;
418         *return_count = 0;
419
420         status = resolve_dns_hosts_file_as_dns_rr_recurse(dns_hosts_file, name, srv_lookup,
421                                                           0, 0,
422                                                           mem_ctx, return_rr, return_count);
423
424         if (NT_STATUS_IS_OK(status)) {
425                 DEBUG(3,("resolve_dns_hosts (dns_rr): "
426                          "Found %d %s result records for for name %s\n",
427                          *return_count, srv_lookup ? "SRV" : "A[AAA]", name));
428         } else {
429                 DEBUG(3,("resolve_dns_hosts (dns_rr): "
430                          "failed to obtain %s result records for for name %s: %s\n",
431                          srv_lookup ? "SRV" : "A[AAA]", name, nt_errstr(status)));
432         }
433         return status;
434 }