tdb: Add another overflow check to tdb_expand_adjust
[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 (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;
104                 }
105                 if (count <= 0)
106                         continue;
107
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;
113                         }
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));
119                         }
120
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;
126                         }
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;
132                         }
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;
138                         }
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;
144                         }
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;
150                         }
151                 } else {
152                         DEBUG(0,("getdns_hosts_fileent: unknown type %s\n", name_type));
153                         continue;
154                 }
155
156                 *pp_name = talloc_strdup(ctx, name);
157                 if (!*pp_name) {
158                         return false;
159                 }
160
161                 *pp_name_type = talloc_strdup(ctx, name_type);
162                 if (!*pp_name_type) {
163                         return false;
164                 }
165                 return true;
166         }
167
168         return false;
169 }
170
171 /********************************************************
172  Finish parsing the dns_hosts_file file.
173 *********************************************************/
174
175 static void enddns_hosts_file(XFILE *fp)
176 {
177         x_fclose(fp);
178 }
179
180 /********************************************************
181  Resolve via "dns_hosts" method.
182 *********************************************************/
183
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)
190 {
191         /*
192          * "dns_hosts" means parse the local dns_hosts file.
193          */
194
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;
205
206         *return_rr = NULL;
207
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;
212         }
213
214         *return_count = 0;
215
216         DEBUG(3,("resolve_dns_hosts: (%d) "
217                  "Attempting %s dns_hosts lookup for name %s\n",
218                  level, srv_lookup ? "SRV" : "A", name));
219
220         fp = startdns_hosts_file(dns_hosts_file);
221
222         if ( fp == NULL )
223                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
224
225         ip_list_ctx = talloc_new(mem_ctx);
226         if (!ip_list_ctx) {
227                 enddns_hosts_file(fp);
228                 return NT_STATUS_NO_MEMORY;
229         }
230
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;
236         }
237
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;
267                                         }
268                                         talloc_steal(rr, tmp_rr);
269                                         rr[*return_count] = *tmp_rr;
270                                         *return_count = (*return_count) + 1;
271                                 }
272                         }
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);
286
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;
292                                 }
293
294                                 rr->hostname = talloc_strdup(rr, host_name);
295
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;
301                                 }
302                                 rr->port = port;
303
304                                 *return_count = 1;
305                         }
306
307                         /* Set the specified port (possibly from a SRV lookup) into the structure we return */
308                         set_sockaddr_port((struct sockaddr *)&return_ss, port);
309
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);
314
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;
320                         }
321
322                         rr->ss_s[rr->num_ips] = return_ss;
323                         rr->num_ips += 1;
324
325                         /* we found something */
326                         status = NT_STATUS_OK;
327                 }
328
329                 TALLOC_FREE(ctx);
330                 ctx = talloc_new(mem_ctx);
331                 if (!ctx) {
332                         enddns_hosts_file(fp);
333                         return NT_STATUS_NO_MEMORY;
334                 }
335         }
336
337         *return_rr = talloc_steal(mem_ctx, rr);
338         TALLOC_FREE(ip_list_ctx);
339         enddns_hosts_file(fp);
340         return status;
341 }
342
343 /********************************************************
344  Resolve via "dns_hosts_file" method, returning a list of sockaddr_storage values
345 *********************************************************/
346
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)
352 {
353         NTSTATUS status;
354         struct dns_rr_srv *dns_rr = NULL;
355         int i, j, rr_count = 0;
356
357         *return_iplist = NULL;
358         *return_count = 0;
359
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;
368         }
369
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;
374                 }
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;
378                 }
379         }
380         DEBUG(3,("resolve_dns_hosts (sockaddr): "
381                 "Found %d results for for name %s\n",
382                  *return_count, name));
383
384         return NT_STATUS_OK;
385 }
386
387 /********************************************************
388  Resolve via "dns_hosts_file" method, returning struct dns_rr_srv
389 *********************************************************/
390
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)
396 {
397         NTSTATUS status;
398         *return_rr = NULL;
399         *return_count = 0;
400
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);
404
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)));
413         }
414         return status;
415 }