/* Unix SMB/CIFS implementation. read a file containing DNS names, types and IP addresses Copyright (C) Andrew Tridgell 1994-1998 Copyright (C) Jeremy Allison 2007 Copyright (C) Andrew Bartlett 2009-2011 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* The purpose of this file is to read the file generated by the samba_dnsupdate script */ #include "includes.h" #include "lib/util/xfile.h" #include "lib/util/util_net.h" #include "system/filesys.h" #include "system/network.h" #include "libcli/nbt/libnbt.h" #include "libcli/dns/dns.h" #ifdef strcasecmp #undef strcasecmp #endif /******************************************************** Start parsing the dns_hosts_file file. *********************************************************/ static XFILE *startdns_hosts_file(const char *fname) { XFILE *fp = x_fopen(fname,O_RDONLY, 0); if (!fp) { DEBUG(4,("startdns_hosts_file: Can't open dns_hosts_file file %s. " "Error was %s\n", fname, strerror(errno))); return NULL; } return fp; } /******************************************************** Parse the next line in the dns_hosts_file file. *********************************************************/ static bool getdns_hosts_fileent(TALLOC_CTX *ctx, XFILE *fp, char **pp_name, char **pp_name_type, char **pp_next_name, struct sockaddr_storage *pss, uint32_t *p_port) { char line[1024]; *pp_name = NULL; *pp_name_type = NULL; *pp_next_name = NULL; *p_port = 0; while(!x_feof(fp) && !x_ferror(fp)) { char *name_type = NULL; char *name = NULL; char *next_name = NULL; char *ip = NULL; char *port = NULL; const char *ptr; int count = 0; if (!fgets_slash(line,sizeof(line),fp)) { continue; } if (*line == '#') { continue; } ptr = line; if (next_token_talloc(ctx, &ptr, &name_type, NULL)) ++count; if (next_token_talloc(ctx, &ptr, &name, NULL)) ++count; if (name_type && strcasecmp(name_type, "A") == 0) { if (next_token_talloc(ctx, &ptr, &ip, NULL)) ++count; } else if (name_type && strcasecmp(name_type, "SRV") == 0) { if (next_token_talloc(ctx, &ptr, &next_name, NULL)) ++count; if (next_token_talloc(ctx, &ptr, &port, NULL)) ++count; } else if (name_type && strcasecmp(name_type, "CNAME") == 0) { if (next_token_talloc(ctx, &ptr, &next_name, NULL)) ++count; } if (count <= 0) continue; if (strcasecmp(name_type, "A") == 0) { if (count != 3) { DEBUG(0,("getdns_hosts_fileent: Ill formed hosts A record [%s]\n", line)); continue; } DEBUG(4, ("getdns_hosts_fileent: host entry: %s %s %s\n", name_type, name, ip)); if (!interpret_string_addr(pss, ip, AI_NUMERICHOST)) { DEBUG(0,("getdns_hosts_fileent: invalid address " "%s.\n", ip)); } } else if (strcasecmp(name_type, "SRV") == 0) { if (count != 4) { DEBUG(0,("getdns_hosts_fileent: Ill formed hosts SRV record [%s]\n", line)); continue; } *p_port = strtoul(port, NULL, 10); if (*p_port == UINT32_MAX) { DEBUG(0, ("getdns_hosts_fileent: Ill formed hosts SRV record [%s] (invalid port: %s)\n", line, port)); continue; } DEBUG(4, ("getdns_hosts_fileent: host entry: %s %s %s %u\n", name_type, name, next_name, (unsigned int)*p_port)); *pp_next_name = talloc_strdup(ctx, next_name); if (!*pp_next_name) { return false; } } else if (strcasecmp(name_type, "CNAME") == 0) { if (count != 3) { DEBUG(0,("getdns_hosts_fileent: Ill formed hosts CNAME record [%s]\n", line)); continue; } DEBUG(4, ("getdns_hosts_fileent: host entry: %s %s %s\n", name_type, name, next_name)); *pp_next_name = talloc_strdup(ctx, next_name); if (!*pp_next_name) { return false; } } else { DEBUG(0,("getdns_hosts_fileent: unknown type %s\n", name_type)); continue; } *pp_name = talloc_strdup(ctx, name); if (!*pp_name) { return false; } *pp_name_type = talloc_strdup(ctx, name_type); if (!*pp_name_type) { return false; } return true; } return false; } /******************************************************** Finish parsing the dns_hosts_file file. *********************************************************/ static void enddns_hosts_file(XFILE *fp) { x_fclose(fp); } /******************************************************** Resolve via "dns_hosts" method. *********************************************************/ static NTSTATUS resolve_dns_hosts_file_as_dns_rr_recurse(const char *dns_hosts_file, const char *name, bool srv_lookup, int level, uint32_t port, TALLOC_CTX *mem_ctx, struct dns_rr_srv **return_rr, int *return_count) { /* * "dns_hosts" means parse the local dns_hosts file. */ XFILE *fp; char *host_name = NULL; char *name_type = NULL; char *next_name = NULL; struct sockaddr_storage return_ss; uint32_t srv_port; NTSTATUS status = NT_STATUS_OBJECT_NAME_NOT_FOUND; TALLOC_CTX *ctx = NULL; TALLOC_CTX *ip_list_ctx = NULL; struct dns_rr_srv *rr = NULL; *return_rr = NULL; /* Don't recurse forever, even on our own flat files */ if (level > 11) { DEBUG(0, ("resolve_dns_hosts_file recursion limit reached looking up %s!\n", name)); return status; } *return_count = 0; DEBUG(3,("resolve_dns_hosts: (%d) " "Attempting %s dns_hosts lookup for name %s\n", level, srv_lookup ? "SRV" : "A", name)); fp = startdns_hosts_file(dns_hosts_file); if ( fp == NULL ) return NT_STATUS_OBJECT_NAME_NOT_FOUND; ip_list_ctx = talloc_new(mem_ctx); if (!ip_list_ctx) { enddns_hosts_file(fp); return NT_STATUS_NO_MEMORY; } ctx = talloc_new(ip_list_ctx); if (!ctx) { talloc_free(ip_list_ctx); enddns_hosts_file(fp); return NT_STATUS_NO_MEMORY; } while (getdns_hosts_fileent(ctx, fp, &host_name, &name_type, &next_name, &return_ss, &srv_port)) { if (!strequal(name, host_name)) { /* continue at the bottom of the loop */ } else if (srv_lookup) { if (strcasecmp(name_type, "SRV") == 0) { NTSTATUS status_recurse; struct dns_rr_srv *tmp_rr; int tmp_count = 0; /* we only accept one host name per SRV entry */ status_recurse = resolve_dns_hosts_file_as_dns_rr_recurse(dns_hosts_file, next_name, false, level + 1, srv_port, ip_list_ctx, &tmp_rr, &tmp_count); if (NT_STATUS_EQUAL(status_recurse, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { /* Don't fail on a dangling SRV record */ } else if (!NT_STATUS_IS_OK(status_recurse)) { enddns_hosts_file(fp); talloc_free(ip_list_ctx); return status_recurse; } else if (tmp_count != 1) { status = NT_STATUS_OBJECT_NAME_NOT_FOUND; } else { status = status_recurse; rr = talloc_realloc(ip_list_ctx, rr, struct dns_rr_srv, (*return_count) + 1); if (!rr) { enddns_hosts_file(fp); return NT_STATUS_NO_MEMORY; } talloc_steal(rr, tmp_rr); rr[*return_count] = *tmp_rr; *return_count = (*return_count) + 1; } } } else if (strcasecmp(name_type, "CNAME") == 0) { /* we only accept one host name per CNAME */ enddns_hosts_file(fp); status = resolve_dns_hosts_file_as_dns_rr_recurse(dns_hosts_file, next_name, false, level + 1, port, mem_ctx, return_rr, return_count); talloc_free(ip_list_ctx); return status; } else if (strcasecmp(name_type, "A") == 0) { if (*return_count == 0) { /* We are happy to keep looking for other possible A record matches */ rr = talloc_zero(ip_list_ctx, struct dns_rr_srv); if (rr == NULL) { TALLOC_FREE(ctx); enddns_hosts_file(fp); DEBUG(3,("resolve_dns_hosts: talloc_realloc fail !\n")); return NT_STATUS_NO_MEMORY; } rr->hostname = talloc_strdup(rr, host_name); if (rr->hostname == NULL) { TALLOC_FREE(ctx); enddns_hosts_file(fp); DEBUG(3,("resolve_dns_hosts: talloc_realloc fail !\n")); return NT_STATUS_NO_MEMORY; } rr->port = port; *return_count = 1; } /* Set the specified port (possibly from a SRV lookup) into the structure we return */ set_sockaddr_port((struct sockaddr *)&return_ss, port); /* We are happy to keep looking for other possible A record matches */ rr->ss_s = talloc_realloc(rr, rr->ss_s, struct sockaddr_storage, rr->num_ips + 1); if (rr->ss_s == NULL) { TALLOC_FREE(ctx); enddns_hosts_file(fp); DEBUG(3,("resolve_dns_hosts: talloc_realloc fail !\n")); return NT_STATUS_NO_MEMORY; } rr->ss_s[rr->num_ips] = return_ss; rr->num_ips += 1; /* we found something */ status = NT_STATUS_OK; } TALLOC_FREE(ctx); ctx = talloc_new(mem_ctx); if (!ctx) { enddns_hosts_file(fp); return NT_STATUS_NO_MEMORY; } } *return_rr = talloc_steal(mem_ctx, rr); TALLOC_FREE(ip_list_ctx); enddns_hosts_file(fp); return status; } /******************************************************** Resolve via "dns_hosts_file" method, returning a list of sockaddr_storage values *********************************************************/ NTSTATUS resolve_dns_hosts_file_as_sockaddr(const char *dns_hosts_file, const char *name, bool srv_lookup, TALLOC_CTX *mem_ctx, struct sockaddr_storage **return_iplist, int *return_count) { NTSTATUS status; struct dns_rr_srv *dns_rr = NULL; int i, j, rr_count = 0; *return_iplist = NULL; *return_count = 0; status = resolve_dns_hosts_file_as_dns_rr_recurse(dns_hosts_file, name, srv_lookup, 0, 0, mem_ctx, &dns_rr, &rr_count); if (!NT_STATUS_IS_OK(status)) { DEBUG(3,("resolve_dns_hosts (sockaddr): " "failed to obtain %s result records for for name %s: %s\n", srv_lookup ? "SRV" : "A", name, nt_errstr(status))); return status; } for (i=0; i < rr_count; i++) { *return_iplist = talloc_realloc(mem_ctx, *return_iplist, struct sockaddr_storage, *return_count + dns_rr[i].num_ips); if (!*return_iplist) { return NT_STATUS_NO_MEMORY; } for (j=0; j < dns_rr[i].num_ips; j++) { (*return_iplist)[*return_count] = dns_rr[i].ss_s[j]; *return_count = *return_count + 1; } } DEBUG(3,("resolve_dns_hosts (sockaddr): " "Found %d results for for name %s\n", *return_count, name)); return NT_STATUS_OK; } /******************************************************** Resolve via "dns_hosts_file" method, returning struct dns_rr_srv *********************************************************/ NTSTATUS resolve_dns_hosts_file_as_dns_rr(const char *dns_hosts_file, const char *name, bool srv_lookup, TALLOC_CTX *mem_ctx, struct dns_rr_srv **return_rr, int *return_count) { NTSTATUS status; *return_rr = NULL; *return_count = 0; status = resolve_dns_hosts_file_as_dns_rr_recurse(dns_hosts_file, name, srv_lookup, 0, 0, mem_ctx, return_rr, return_count); if (NT_STATUS_IS_OK(status)) { DEBUG(3,("resolve_dns_hosts (dns_rr): " "Found %d %s result records for for name %s\n", *return_count, srv_lookup ? "SRV" : "A", name)); } else { DEBUG(3,("resolve_dns_hosts (dns_rr): " "failed to obtain %s result records for for name %s: %s\n", srv_lookup ? "SRV" : "A", name, nt_errstr(status))); } return status; }