summaryrefslogtreecommitdiff
path: root/source3/libsmb/namequery.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/libsmb/namequery.c')
-rw-r--r--source3/libsmb/namequery.c2145
1 files changed, 2145 insertions, 0 deletions
diff --git a/source3/libsmb/namequery.c b/source3/libsmb/namequery.c
new file mode 100644
index 0000000000..24d7ee1a9c
--- /dev/null
+++ b/source3/libsmb/namequery.c
@@ -0,0 +1,2145 @@
+/*
+ Unix SMB/CIFS implementation.
+ name query routines
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Jeremy Allison 2007.
+
+ 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/>.
+*/
+
+#include "includes.h"
+
+/* nmbd.c sets this to True. */
+bool global_in_nmbd = False;
+
+/****************************
+ * SERVER AFFINITY ROUTINES *
+ ****************************/
+
+ /* Server affinity is the concept of preferring the last domain
+ controller with whom you had a successful conversation */
+
+/****************************************************************************
+****************************************************************************/
+#define SAFKEY_FMT "SAF/DOMAIN/%s"
+#define SAF_TTL 900
+
+static char *saf_key(const char *domain)
+{
+ char *keystr;
+
+ asprintf_strupper_m(&keystr, SAFKEY_FMT, domain);
+
+ return keystr;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+bool saf_store( const char *domain, const char *servername )
+{
+ char *key;
+ time_t expire;
+ bool ret = False;
+
+ if ( !domain || !servername ) {
+ DEBUG(2,("saf_store: "
+ "Refusing to store empty domain or servername!\n"));
+ return False;
+ }
+
+ if ( (strlen(domain) == 0) || (strlen(servername) == 0) ) {
+ DEBUG(0,("saf_store: "
+ "refusing to store 0 length domain or servername!\n"));
+ return False;
+ }
+
+ if ( !gencache_init() )
+ return False;
+
+ key = saf_key( domain );
+ expire = time( NULL ) + SAF_TTL;
+
+ DEBUG(10,("saf_store: domain = [%s], server = [%s], expire = [%u]\n",
+ domain, servername, (unsigned int)expire ));
+
+ ret = gencache_set( key, servername, expire );
+
+ SAFE_FREE( key );
+
+ return ret;
+}
+
+bool saf_delete( const char *domain )
+{
+ char *key;
+ bool ret = False;
+
+ if ( !domain ) {
+ DEBUG(2,("saf_delete: Refusing to delete empty domain\n"));
+ return False;
+ }
+
+ if ( !gencache_init() )
+ return False;
+
+ key = saf_key(domain);
+ ret = gencache_del(key);
+
+ if (ret) {
+ DEBUG(10,("saf_delete: domain = [%s]\n", domain ));
+ }
+
+ SAFE_FREE( key );
+
+ return ret;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+char *saf_fetch( const char *domain )
+{
+ char *server = NULL;
+ time_t timeout;
+ bool ret = False;
+ char *key = NULL;
+
+ if ( !domain || strlen(domain) == 0) {
+ DEBUG(2,("saf_fetch: Empty domain name!\n"));
+ return NULL;
+ }
+
+ if ( !gencache_init() )
+ return False;
+
+ key = saf_key( domain );
+
+ ret = gencache_get( key, &server, &timeout );
+
+ SAFE_FREE( key );
+
+ if ( !ret ) {
+ DEBUG(5,("saf_fetch: failed to find server for \"%s\" domain\n",
+ domain ));
+ } else {
+ DEBUG(5,("saf_fetch: Returning \"%s\" for \"%s\" domain\n",
+ server, domain ));
+ }
+
+ return server;
+}
+
+/****************************************************************************
+ Generate a random trn_id.
+****************************************************************************/
+
+static int generate_trn_id(void)
+{
+ uint16 id;
+
+ generate_random_buffer((uint8 *)&id, sizeof(id));
+
+ return id % (unsigned)0x7FFF;
+}
+
+/****************************************************************************
+ Parse a node status response into an array of structures.
+****************************************************************************/
+
+static NODE_STATUS_STRUCT *parse_node_status(char *p,
+ int *num_names,
+ struct node_status_extra *extra)
+{
+ NODE_STATUS_STRUCT *ret;
+ int i;
+
+ *num_names = CVAL(p,0);
+
+ if (*num_names == 0)
+ return NULL;
+
+ ret = SMB_MALLOC_ARRAY(NODE_STATUS_STRUCT,*num_names);
+ if (!ret)
+ return NULL;
+
+ p++;
+ for (i=0;i< *num_names;i++) {
+ StrnCpy(ret[i].name,p,15);
+ trim_char(ret[i].name,'\0',' ');
+ ret[i].type = CVAL(p,15);
+ ret[i].flags = p[16];
+ p += 18;
+ DEBUG(10, ("%s#%02x: flags = 0x%02x\n", ret[i].name,
+ ret[i].type, ret[i].flags));
+ }
+ /*
+ * Also, pick up the MAC address ...
+ */
+ if (extra) {
+ memcpy(&extra->mac_addr, p, 6); /* Fill in the mac addr */
+ }
+ return ret;
+}
+
+
+/****************************************************************************
+ Do a NBT node status query on an open socket and return an array of
+ structures holding the returned names or NULL if the query failed.
+**************************************************************************/
+
+NODE_STATUS_STRUCT *node_status_query(int fd,
+ struct nmb_name *name,
+ const struct sockaddr_storage *to_ss,
+ int *num_names,
+ struct node_status_extra *extra)
+{
+ bool found=False;
+ int retries = 2;
+ int retry_time = 2000;
+ struct timeval tval;
+ struct packet_struct p;
+ struct packet_struct *p2;
+ struct nmb_packet *nmb = &p.packet.nmb;
+ NODE_STATUS_STRUCT *ret;
+
+ ZERO_STRUCT(p);
+
+ if (to_ss->ss_family != AF_INET) {
+ /* Can't do node status to IPv6 */
+ return NULL;
+ }
+ nmb->header.name_trn_id = generate_trn_id();
+ nmb->header.opcode = 0;
+ nmb->header.response = false;
+ nmb->header.nm_flags.bcast = false;
+ nmb->header.nm_flags.recursion_available = false;
+ nmb->header.nm_flags.recursion_desired = false;
+ nmb->header.nm_flags.trunc = false;
+ nmb->header.nm_flags.authoritative = false;
+ nmb->header.rcode = 0;
+ nmb->header.qdcount = 1;
+ nmb->header.ancount = 0;
+ nmb->header.nscount = 0;
+ nmb->header.arcount = 0;
+ nmb->question.question_name = *name;
+ nmb->question.question_type = 0x21;
+ nmb->question.question_class = 0x1;
+
+ p.ip = ((const struct sockaddr_in *)to_ss)->sin_addr;
+ p.port = NMB_PORT;
+ p.fd = fd;
+ p.timestamp = time(NULL);
+ p.packet_type = NMB_PACKET;
+
+ GetTimeOfDay(&tval);
+
+ if (!send_packet(&p))
+ return NULL;
+
+ retries--;
+
+ while (1) {
+ struct timeval tval2;
+ GetTimeOfDay(&tval2);
+ if (TvalDiff(&tval,&tval2) > retry_time) {
+ if (!retries)
+ break;
+ if (!found && !send_packet(&p))
+ return NULL;
+ GetTimeOfDay(&tval);
+ retries--;
+ }
+
+ if ((p2=receive_nmb_packet(fd,90,nmb->header.name_trn_id))) {
+ struct nmb_packet *nmb2 = &p2->packet.nmb;
+ debug_nmb_packet(p2);
+
+ if (nmb2->header.opcode != 0 ||
+ nmb2->header.nm_flags.bcast ||
+ nmb2->header.rcode ||
+ !nmb2->header.ancount ||
+ nmb2->answers->rr_type != 0x21) {
+ /* XXXX what do we do with this? could be a
+ redirect, but we'll discard it for the
+ moment */
+ free_packet(p2);
+ continue;
+ }
+
+ ret = parse_node_status(&nmb2->answers->rdata[0],
+ num_names, extra);
+ free_packet(p2);
+ return ret;
+ }
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Find the first type XX name in a node status reply - used for finding
+ a servers name given its IP. Return the matched name in *name.
+**************************************************************************/
+
+bool name_status_find(const char *q_name,
+ int q_type,
+ int type,
+ const struct sockaddr_storage *to_ss,
+ fstring name)
+{
+ char addr[INET6_ADDRSTRLEN];
+ struct sockaddr_storage ss;
+ NODE_STATUS_STRUCT *status = NULL;
+ struct nmb_name nname;
+ int count, i;
+ int sock;
+ bool result = false;
+
+ if (lp_disable_netbios()) {
+ DEBUG(5,("name_status_find(%s#%02x): netbios is disabled\n",
+ q_name, q_type));
+ return False;
+ }
+
+ print_sockaddr(addr, sizeof(addr), to_ss);
+
+ DEBUG(10, ("name_status_find: looking up %s#%02x at %s\n", q_name,
+ q_type, addr));
+
+ /* Check the cache first. */
+
+ if (namecache_status_fetch(q_name, q_type, type, to_ss, name)) {
+ return True;
+ }
+
+ if (to_ss->ss_family != AF_INET) {
+ /* Can't do node status to IPv6 */
+ return false;
+ }
+
+ if (!interpret_string_addr(&ss, lp_socket_address(),
+ AI_NUMERICHOST|AI_PASSIVE)) {
+ zero_addr(&ss);
+ }
+
+ sock = open_socket_in(SOCK_DGRAM, 0, 3, &ss, True);
+ if (sock == -1)
+ goto done;
+
+ /* W2K PDC's seem not to respond to '*'#0. JRA */
+ make_nmb_name(&nname, q_name, q_type);
+ status = node_status_query(sock, &nname, to_ss, &count, NULL);
+ close(sock);
+ if (!status)
+ goto done;
+
+ for (i=0;i<count;i++) {
+ if (status[i].type == type)
+ break;
+ }
+ if (i == count)
+ goto done;
+
+ pull_ascii_nstring(name, sizeof(fstring), status[i].name);
+
+ /* Store the result in the cache. */
+ /* but don't store an entry for 0x1c names here. Here we have
+ a single host and DOMAIN<0x1c> names should be a list of hosts */
+
+ if ( q_type != 0x1c ) {
+ namecache_status_store(q_name, q_type, type, to_ss, name);
+ }
+
+ result = true;
+
+ done:
+ SAFE_FREE(status);
+
+ DEBUG(10, ("name_status_find: name %sfound", result ? "" : "not "));
+
+ if (result)
+ DEBUGADD(10, (", name %s ip address is %s", name, addr));
+
+ DEBUG(10, ("\n"));
+
+ return result;
+}
+
+/*
+ comparison function used by sort_addr_list
+*/
+
+static int addr_compare(const struct sockaddr_storage *ss1,
+ const struct sockaddr_storage *ss2)
+{
+ int max_bits1=0, max_bits2=0;
+ int num_interfaces = iface_count();
+ int i;
+
+ /* Sort IPv6 addresses first. */
+ if (ss1->ss_family != ss2->ss_family) {
+ if (ss2->ss_family == AF_INET) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+
+ /* Here we know both addresses are of the same
+ * family. */
+
+ for (i=0;i<num_interfaces;i++) {
+ const struct sockaddr_storage *pss = iface_n_bcast(i);
+ unsigned char *p_ss1 = NULL;
+ unsigned char *p_ss2 = NULL;
+ unsigned char *p_if = NULL;
+ size_t len = 0;
+ int bits1, bits2;
+
+ if (pss->ss_family != ss1->ss_family) {
+ /* Ignore interfaces of the wrong type. */
+ continue;
+ }
+ if (pss->ss_family == AF_INET) {
+ p_if = (unsigned char *)
+ &((const struct sockaddr_in *)pss)->sin_addr;
+ p_ss1 = (unsigned char *)
+ &((const struct sockaddr_in *)ss1)->sin_addr;
+ p_ss2 = (unsigned char *)
+ &((const struct sockaddr_in *)ss2)->sin_addr;
+ len = 4;
+ }
+#if defined(HAVE_IPV6)
+ if (pss->ss_family == AF_INET6) {
+ p_if = (unsigned char *)
+ &((const struct sockaddr_in6 *)pss)->sin6_addr;
+ p_ss1 = (unsigned char *)
+ &((const struct sockaddr_in6 *)ss1)->sin6_addr;
+ p_ss2 = (unsigned char *)
+ &((const struct sockaddr_in6 *)ss2)->sin6_addr;
+ len = 16;
+ }
+#endif
+ if (!p_ss1 || !p_ss2 || !p_if || len == 0) {
+ continue;
+ }
+ bits1 = matching_len_bits(p_ss1, p_if, len);
+ bits2 = matching_len_bits(p_ss2, p_if, len);
+ max_bits1 = MAX(bits1, max_bits1);
+ max_bits2 = MAX(bits2, max_bits2);
+ }
+
+ /* Bias towards directly reachable IPs */
+ if (iface_local(ss1)) {
+ if (ss1->ss_family == AF_INET) {
+ max_bits1 += 32;
+ } else {
+ max_bits1 += 128;
+ }
+ }
+ if (iface_local(ss2)) {
+ if (ss2->ss_family == AF_INET) {
+ max_bits2 += 32;
+ } else {
+ max_bits2 += 128;
+ }
+ }
+ return max_bits2 - max_bits1;
+}
+
+/*******************************************************************
+ compare 2 ldap IPs by nearness to our interfaces - used in qsort
+*******************************************************************/
+
+int ip_service_compare(struct ip_service *ss1, struct ip_service *ss2)
+{
+ int result;
+
+ if ((result = addr_compare(&ss1->ss, &ss2->ss)) != 0) {
+ return result;
+ }
+
+ if (ss1->port > ss2->port) {
+ return 1;
+ }
+
+ if (ss1->port < ss2->port) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ sort an IP list so that names that are close to one of our interfaces
+ are at the top. This prevents the problem where a WINS server returns an IP
+ that is not reachable from our subnet as the first match
+*/
+
+static void sort_addr_list(struct sockaddr_storage *sslist, int count)
+{
+ if (count <= 1) {
+ return;
+ }
+
+ qsort(sslist, count, sizeof(struct sockaddr_storage),
+ QSORT_CAST addr_compare);
+}
+
+static void sort_service_list(struct ip_service *servlist, int count)
+{
+ if (count <= 1) {
+ return;
+ }
+
+ qsort(servlist, count, sizeof(struct ip_service),
+ QSORT_CAST ip_service_compare);
+}
+
+/**********************************************************************
+ Remove any duplicate address/port pairs in the list
+ *********************************************************************/
+
+static int remove_duplicate_addrs2(struct ip_service *iplist, int count )
+{
+ int i, j;
+
+ DEBUG(10,("remove_duplicate_addrs2: "
+ "looking for duplicate address/port pairs\n"));
+
+ /* one loop to remove duplicates */
+ for ( i=0; i<count; i++ ) {
+ if ( is_zero_addr(&iplist[i].ss)) {
+ continue;
+ }
+
+ for ( j=i+1; j<count; j++ ) {
+ if (addr_equal(&iplist[i].ss, &iplist[j].ss) &&
+ iplist[i].port == iplist[j].port) {
+ zero_addr(&iplist[j].ss);
+ }
+ }
+ }
+
+ /* one loop to clean up any holes we left */
+ /* first ip should never be a zero_ip() */
+ for (i = 0; i<count; ) {
+ if (is_zero_addr(&iplist[i].ss) ) {
+ if (i != count-1) {
+ memmove(&iplist[i], &iplist[i+1],
+ (count - i - 1)*sizeof(iplist[i]));
+ }
+ count--;
+ continue;
+ }
+ i++;
+ }
+
+ return count;
+}
+
+/****************************************************************************
+ Do a netbios name query to find someones IP.
+ Returns an array of IP addresses or NULL if none.
+ *count will be set to the number of addresses returned.
+ *timed_out is set if we failed by timing out
+****************************************************************************/
+
+struct sockaddr_storage *name_query(int fd,
+ const char *name,
+ int name_type,
+ bool bcast,
+ bool recurse,
+ const struct sockaddr_storage *to_ss,
+ int *count,
+ int *flags,
+ bool *timed_out)
+{
+ bool found=false;
+ int i, retries = 3;
+ int retry_time = bcast?250:2000;
+ struct timeval tval;
+ struct packet_struct p;
+ struct packet_struct *p2;
+ struct nmb_packet *nmb = &p.packet.nmb;
+ struct sockaddr_storage *ss_list = NULL;
+
+ if (lp_disable_netbios()) {
+ DEBUG(5,("name_query(%s#%02x): netbios is disabled\n",
+ name, name_type));
+ return NULL;
+ }
+
+ if (to_ss->ss_family != AF_INET) {
+ return NULL;
+ }
+
+ if (timed_out) {
+ *timed_out = false;
+ }
+
+ memset((char *)&p,'\0',sizeof(p));
+ (*count) = 0;
+ (*flags) = 0;
+
+ nmb->header.name_trn_id = generate_trn_id();
+ nmb->header.opcode = 0;
+ nmb->header.response = false;
+ nmb->header.nm_flags.bcast = bcast;
+ nmb->header.nm_flags.recursion_available = false;
+ nmb->header.nm_flags.recursion_desired = recurse;
+ nmb->header.nm_flags.trunc = false;
+ nmb->header.nm_flags.authoritative = false;
+ nmb->header.rcode = 0;
+ nmb->header.qdcount = 1;
+ nmb->header.ancount = 0;
+ nmb->header.nscount = 0;
+ nmb->header.arcount = 0;
+
+ make_nmb_name(&nmb->question.question_name,name,name_type);
+
+ nmb->question.question_type = 0x20;
+ nmb->question.question_class = 0x1;
+
+ p.ip = ((struct sockaddr_in *)to_ss)->sin_addr;
+ p.port = NMB_PORT;
+ p.fd = fd;
+ p.timestamp = time(NULL);
+ p.packet_type = NMB_PACKET;
+
+ GetTimeOfDay(&tval);
+
+ if (!send_packet(&p))
+ return NULL;
+
+ retries--;
+
+ while (1) {
+ struct timeval tval2;
+
+ GetTimeOfDay(&tval2);
+ if (TvalDiff(&tval,&tval2) > retry_time) {
+ if (!retries)
+ break;
+ if (!found && !send_packet(&p))
+ return NULL;
+ GetTimeOfDay(&tval);
+ retries--;
+ }
+
+ if ((p2=receive_nmb_packet(fd,90,nmb->header.name_trn_id))) {
+ struct nmb_packet *nmb2 = &p2->packet.nmb;
+ debug_nmb_packet(p2);
+
+ /* If we get a Negative Name Query Response from a WINS
+ * server, we should report it and give up.
+ */
+ if( 0 == nmb2->header.opcode /* A query response */
+ && !(bcast) /* from a WINS server */
+ && nmb2->header.rcode /* Error returned */
+ ) {
+
+ if( DEBUGLVL( 3 ) ) {
+ /* Only executed if DEBUGLEVEL >= 3 */
+ dbgtext( "Negative name query "
+ "response, rcode 0x%02x: ",
+ nmb2->header.rcode );
+ switch( nmb2->header.rcode ) {
+ case 0x01:
+ dbgtext( "Request "
+ "was invalidly formatted.\n" );
+ break;
+ case 0x02:
+ dbgtext( "Problem with NBNS, "
+ "cannot process name.\n");
+ break;
+ case 0x03:
+ dbgtext( "The name requested "
+ "does not exist.\n" );
+ break;
+ case 0x04:
+ dbgtext( "Unsupported request "
+ "error.\n" );
+ break;
+ case 0x05:
+ dbgtext( "Query refused "
+ "error.\n" );
+ break;
+ default:
+ dbgtext( "Unrecognized error "
+ "code.\n" );
+ break;
+ }
+ }
+ free_packet(p2);
+ return( NULL );
+ }
+
+ if (nmb2->header.opcode != 0 ||
+ nmb2->header.nm_flags.bcast ||
+ nmb2->header.rcode ||
+ !nmb2->header.ancount) {
+ /*
+ * XXXX what do we do with this? Could be a
+ * redirect, but we'll discard it for the
+ * moment.
+ */
+ free_packet(p2);
+ continue;
+ }
+
+ ss_list = SMB_REALLOC_ARRAY(ss_list,
+ struct sockaddr_storage,
+ (*count) +
+ nmb2->answers->rdlength/6);
+
+ if (!ss_list) {
+ DEBUG(0,("name_query: Realloc failed.\n"));
+ free_packet(p2);
+ return NULL;
+ }
+
+ DEBUG(2,("Got a positive name query response "
+ "from %s ( ",
+ inet_ntoa(p2->ip)));
+
+ for (i=0;i<nmb2->answers->rdlength/6;i++) {
+ struct in_addr ip;
+ putip((char *)&ip,&nmb2->answers->rdata[2+i*6]);
+ in_addr_to_sockaddr_storage(&ss_list[(*count)],
+ ip);
+ DEBUGADD(2,("%s ",inet_ntoa(ip)));
+ (*count)++;
+ }
+ DEBUGADD(2,(")\n"));
+
+ found=true;
+ retries=0;
+ /* We add the flags back ... */
+ if (nmb2->header.response)
+ (*flags) |= NM_FLAGS_RS;
+ if (nmb2->header.nm_flags.authoritative)
+ (*flags) |= NM_FLAGS_AA;
+ if (nmb2->header.nm_flags.trunc)
+ (*flags) |= NM_FLAGS_TC;
+ if (nmb2->header.nm_flags.recursion_desired)
+ (*flags) |= NM_FLAGS_RD;
+ if (nmb2->header.nm_flags.recursion_available)
+ (*flags) |= NM_FLAGS_RA;
+ if (nmb2->header.nm_flags.bcast)
+ (*flags) |= NM_FLAGS_B;
+ free_packet(p2);
+ /*
+ * If we're doing a unicast lookup we only
+ * expect one reply. Don't wait the full 2
+ * seconds if we got one. JRA.
+ */
+ if(!bcast && found)
+ break;
+ }
+ }
+
+ /* only set timed_out if we didn't fund what we where looking for*/
+
+ if ( !found && timed_out ) {
+ *timed_out = true;
+ }
+
+ /* sort the ip list so we choose close servers first if possible */
+ sort_addr_list(ss_list, *count);
+
+ return ss_list;
+}
+
+/********************************************************
+ Start parsing the lmhosts file.
+*********************************************************/
+
+XFILE *startlmhosts(const char *fname)
+{
+ XFILE *fp = x_fopen(fname,O_RDONLY, 0);
+ if (!fp) {
+ DEBUG(4,("startlmhosts: Can't open lmhosts file %s. "
+ "Error was %s\n",
+ fname, strerror(errno)));
+ return NULL;
+ }
+ return fp;
+}
+
+/********************************************************
+ Parse the next line in the lmhosts file.
+*********************************************************/
+
+bool getlmhostsent(TALLOC_CTX *ctx, XFILE *fp, char **pp_name, int *name_type,
+ struct sockaddr_storage *pss)
+{
+ char line[1024];
+
+ *pp_name = NULL;
+
+ while(!x_feof(fp) && !x_ferror(fp)) {
+ char *ip = NULL;
+ char *flags = NULL;
+ char *extra = NULL;
+ char *name = NULL;
+ const char *ptr;
+ char *ptr1 = NULL;
+ int count = 0;
+
+ *name_type = -1;
+
+ if (!fgets_slash(line,sizeof(line),fp)) {
+ continue;
+ }
+
+ if (*line == '#') {
+ continue;
+ }
+
+ ptr = line;
+
+ if (next_token_talloc(ctx, &ptr, &ip, NULL))
+ ++count;
+ if (next_token_talloc(ctx, &ptr, &name, NULL))
+ ++count;
+ if (next_token_talloc(ctx, &ptr, &flags, NULL))
+ ++count;
+ if (next_token_talloc(ctx, &ptr, &extra, NULL))
+ ++count;
+
+ if (count <= 0)
+ continue;
+
+ if (count > 0 && count < 2) {
+ DEBUG(0,("getlmhostsent: Ill formed hosts line [%s]\n",
+ line));
+ continue;
+ }
+
+ if (count >= 4) {
+ DEBUG(0,("getlmhostsent: too many columns "
+ "in lmhosts file (obsolete syntax)\n"));
+ continue;
+ }
+
+ if (!flags) {
+ flags = talloc_strdup(ctx, "");
+ if (!flags) {
+ continue;
+ }
+ }
+
+ DEBUG(4, ("getlmhostsent: lmhost entry: %s %s %s\n",
+ ip, name, flags));
+
+ if (strchr_m(flags,'G') || strchr_m(flags,'S')) {
+ DEBUG(0,("getlmhostsent: group flag "
+ "in lmhosts ignored (obsolete)\n"));
+ continue;
+ }
+
+ if (!interpret_string_addr(pss, ip, AI_NUMERICHOST)) {
+ DEBUG(0,("getlmhostsent: invalid address "
+ "%s.\n", ip));
+ }
+
+ /* Extra feature. If the name ends in '#XX',
+ * where XX is a hex number, then only add that name type. */
+ if((ptr1 = strchr_m(name, '#')) != NULL) {
+ char *endptr;
+ ptr1++;
+
+ *name_type = (int)strtol(ptr1, &endptr, 16);
+ if(!*ptr1 || (endptr == ptr1)) {
+ DEBUG(0,("getlmhostsent: invalid name "
+ "%s containing '#'.\n", name));
+ continue;
+ }
+
+ *(--ptr1) = '\0'; /* Truncate at the '#' */
+ }
+
+ *pp_name = talloc_strdup(ctx, name);
+ if (!*pp_name) {
+ return false;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+/********************************************************
+ Finish parsing the lmhosts file.
+*********************************************************/
+
+void endlmhosts(XFILE *fp)
+{
+ x_fclose(fp);
+}
+
+/********************************************************
+ convert an array if struct sockaddr_storage to struct ip_service
+ return false on failure. Port is set to PORT_NONE;
+*********************************************************/
+
+static bool convert_ss2service(struct ip_service **return_iplist,
+ const struct sockaddr_storage *ss_list,
+ int count)
+{
+ int i;
+
+ if ( count==0 || !ss_list )
+ return False;
+
+ /* copy the ip address; port will be PORT_NONE */
+ if ((*return_iplist = SMB_MALLOC_ARRAY(struct ip_service, count)) ==
+ NULL) {
+ DEBUG(0,("convert_ip2service: malloc failed "
+ "for %d enetries!\n", count ));
+ return False;
+ }
+
+ for ( i=0; i<count; i++ ) {
+ (*return_iplist)[i].ss = ss_list[i];
+ (*return_iplist)[i].port = PORT_NONE;
+ }
+
+ return true;
+}
+
+/********************************************************
+ Resolve via "bcast" method.
+*********************************************************/
+
+NTSTATUS name_resolve_bcast(const char *name,
+ int name_type,
+ struct ip_service **return_iplist,
+ int *return_count)
+{
+ int sock, i;
+ int num_interfaces = iface_count();
+ struct sockaddr_storage *ss_list;
+ struct sockaddr_storage ss;
+ NTSTATUS status;
+
+ if (lp_disable_netbios()) {
+ DEBUG(5,("name_resolve_bcast(%s#%02x): netbios is disabled\n",
+ name, name_type));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ *return_iplist = NULL;
+ *return_count = 0;
+
+ /*
+ * "bcast" means do a broadcast lookup on all the local interfaces.
+ */
+
+ DEBUG(3,("name_resolve_bcast: Attempting broadcast lookup "
+ "for name %s<0x%x>\n", name, name_type));
+
+ if (!interpret_string_addr(&ss, lp_socket_address(),
+ AI_NUMERICHOST|AI_PASSIVE)) {
+ zero_addr(&ss);
+ }
+
+ sock = open_socket_in( SOCK_DGRAM, 0, 3, &ss, true );
+ if (sock == -1) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ set_socket_options(sock,"SO_BROADCAST");
+ /*
+ * Lookup the name on all the interfaces, return on
+ * the first successful match.
+ */
+ for( i = num_interfaces-1; i >= 0; i--) {
+ const struct sockaddr_storage *pss = iface_n_bcast(i);
+ int flags;
+
+ /* Done this way to fix compiler error on IRIX 5.x */
+ if (!pss) {
+ continue;
+ }
+ ss_list = name_query(sock, name, name_type, true,
+ true, pss, return_count, &flags, NULL);
+ if (ss_list) {
+ goto success;
+ }
+ }
+
+ /* failed - no response */
+
+ close(sock);
+ return NT_STATUS_UNSUCCESSFUL;
+
+success:
+
+ status = NT_STATUS_OK;
+ if (!convert_ss2service(return_iplist, ss_list, *return_count) )
+ status = NT_STATUS_INVALID_PARAMETER;
+
+ SAFE_FREE(ss_list);
+ close(sock);
+ return status;
+}
+
+/********************************************************
+ Resolve via "wins" method.
+*********************************************************/
+
+NTSTATUS resolve_wins(const char *name,
+ int name_type,
+ struct ip_service **return_iplist,
+ int *return_count)
+{
+ int sock, t, i;
+ char **wins_tags;
+ struct sockaddr_storage src_ss, *ss_list = NULL;
+ struct in_addr src_ip;
+ NTSTATUS status;
+
+ if (lp_disable_netbios()) {
+ DEBUG(5,("resolve_wins(%s#%02x): netbios is disabled\n",
+ name, name_type));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ *return_iplist = NULL;
+ *return_count = 0;
+
+ DEBUG(3,("resolve_wins: Attempting wins lookup for name %s<0x%x>\n",
+ name, name_type));
+
+ if (wins_srv_count() < 1) {
+ DEBUG(3,("resolve_wins: WINS server resolution selected "
+ "and no WINS servers listed.\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* we try a lookup on each of the WINS tags in turn */
+ wins_tags = wins_srv_tags();
+
+ if (!wins_tags) {
+ /* huh? no tags?? give up in disgust */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* the address we will be sending from */
+ if (!interpret_string_addr(&src_ss, lp_socket_address(),
+ AI_NUMERICHOST|AI_PASSIVE)) {
+ zero_addr(&src_ss);
+ }
+
+ if (src_ss.ss_family != AF_INET) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr, sizeof(addr), &src_ss);
+ DEBUG(3,("resolve_wins: cannot receive WINS replies "
+ "on IPv6 address %s\n",
+ addr));
+ wins_srv_tags_free(wins_tags);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ src_ip = ((struct sockaddr_in *)&src_ss)->sin_addr;
+
+ /* in the worst case we will try every wins server with every
+ tag! */
+ for (t=0; wins_tags && wins_tags[t]; t++) {
+ int srv_count = wins_srv_count_tag(wins_tags[t]);
+ for (i=0; i<srv_count; i++) {
+ struct sockaddr_storage wins_ss;
+ struct in_addr wins_ip;
+ int flags;
+ bool timed_out;
+
+ wins_ip = wins_srv_ip_tag(wins_tags[t], src_ip);
+
+ if (global_in_nmbd && ismyip_v4(wins_ip)) {
+ /* yikes! we'll loop forever */
+ continue;
+ }
+
+ /* skip any that have been unresponsive lately */
+ if (wins_srv_is_dead(wins_ip, src_ip)) {
+ continue;
+ }
+
+ DEBUG(3,("resolve_wins: using WINS server %s "
+ "and tag '%s'\n",
+ inet_ntoa(wins_ip), wins_tags[t]));
+
+ sock = open_socket_in(SOCK_DGRAM, 0, 3, &src_ss, true);
+ if (sock == -1) {
+ continue;
+ }
+
+ in_addr_to_sockaddr_storage(&wins_ss, wins_ip);
+ ss_list = name_query(sock,
+ name,
+ name_type,
+ false,
+ true,
+ &wins_ss,
+ return_count,
+ &flags,
+ &timed_out);
+
+ /* exit loop if we got a list of addresses */
+
+ if (ss_list)
+ goto success;
+
+ close(sock);
+
+ if (timed_out) {
+ /* Timed out wating for WINS server to respond.
+ * Mark it dead. */
+ wins_srv_died(wins_ip, src_ip);
+ } else {
+ /* The name definately isn't in this
+ group of WINS servers.
+ goto the next group */
+ break;
+ }
+ }
+ }
+
+ wins_srv_tags_free(wins_tags);
+ return NT_STATUS_NO_LOGON_SERVERS;
+
+success:
+
+ status = NT_STATUS_OK;
+ if (!convert_ss2service(return_iplist, ss_list, *return_count))
+ status = NT_STATUS_INVALID_PARAMETER;
+
+ SAFE_FREE(ss_list);
+ wins_srv_tags_free(wins_tags);
+ close(sock);
+
+ return status;
+}
+
+/********************************************************
+ Resolve via "lmhosts" method.
+*********************************************************/
+
+static NTSTATUS resolve_lmhosts(const char *name, int name_type,
+ struct ip_service **return_iplist,
+ int *return_count)
+{
+ /*
+ * "lmhosts" means parse the local lmhosts file.
+ */
+
+ XFILE *fp;
+ char *lmhost_name = NULL;
+ int name_type2;
+ struct sockaddr_storage return_ss;
+ NTSTATUS status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ TALLOC_CTX *ctx = NULL;
+
+ *return_iplist = NULL;
+ *return_count = 0;
+
+ DEBUG(3,("resolve_lmhosts: "
+ "Attempting lmhosts lookup for name %s<0x%x>\n",
+ name, name_type));
+
+ fp = startlmhosts(get_dyn_LMHOSTSFILE());
+
+ if ( fp == NULL )
+ return NT_STATUS_NO_SUCH_FILE;
+
+ ctx = talloc_init("resolve_lmhosts");
+ if (!ctx) {
+ endlmhosts(fp);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ while (getlmhostsent(ctx, fp, &lmhost_name, &name_type2, &return_ss)) {
+
+ if (!strequal(name, lmhost_name)) {
+ TALLOC_FREE(lmhost_name);
+ continue;
+ }
+
+ if ((name_type2 != -1) && (name_type != name_type2)) {
+ TALLOC_FREE(lmhost_name);
+ continue;
+ }
+
+ *return_iplist = SMB_REALLOC_ARRAY((*return_iplist),
+ struct ip_service,
+ (*return_count)+1);
+
+ if ((*return_iplist) == NULL) {
+ TALLOC_FREE(ctx);
+ endlmhosts(fp);
+ DEBUG(3,("resolve_lmhosts: malloc fail !\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*return_iplist)[*return_count].ss = return_ss;
+ (*return_iplist)[*return_count].port = PORT_NONE;
+ *return_count += 1;
+
+ /* we found something */
+ status = NT_STATUS_OK;
+
+ /* Multiple names only for DC lookup */
+ if (name_type != 0x1c)
+ break;
+ }
+
+ TALLOC_FREE(ctx);
+ endlmhosts(fp);
+ return status;
+}
+
+
+/********************************************************
+ Resolve via "hosts" method.
+*********************************************************/
+
+static NTSTATUS resolve_hosts(const char *name, int name_type,
+ struct ip_service **return_iplist,
+ int *return_count)
+{
+ /*
+ * "host" means do a localhost, or dns lookup.
+ */
+ struct addrinfo hints;
+ struct addrinfo *ailist = NULL;
+ struct addrinfo *res = NULL;
+ int ret = -1;
+ int i = 0;
+
+ if ( name_type != 0x20 && name_type != 0x0) {
+ DEBUG(5, ("resolve_hosts: not appropriate "
+ "for name type <0x%x>\n",
+ name_type));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ *return_iplist = NULL;
+ *return_count = 0;
+
+ DEBUG(3,("resolve_hosts: Attempting host lookup for name %s<0x%x>\n",
+ name, name_type));
+
+ ZERO_STRUCT(hints);
+ /* By default make sure it supports TCP. */
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_ADDRCONFIG;
+
+#if !defined(HAVE_IPV6)
+ /* Unless we have IPv6, we really only want IPv4 addresses back. */
+ hints.ai_family = AF_INET;
+#endif
+
+ ret = getaddrinfo(name,
+ NULL,
+ &hints,
+ &ailist);
+ if (ret) {
+ DEBUG(3,("resolve_hosts: getaddrinfo failed for name %s [%s]\n",
+ name,
+ gai_strerror(ret) ));
+ }
+
+ for (res = ailist; res; res = res->ai_next) {
+ struct sockaddr_storage ss;
+
+ if (!res->ai_addr || res->ai_addrlen == 0) {
+ continue;
+ }
+
+ ZERO_STRUCT(ss);
+ memcpy(&ss, res->ai_addr, res->ai_addrlen);
+
+ *return_count += 1;
+
+ *return_iplist = SMB_REALLOC_ARRAY(*return_iplist,
+ struct ip_service,
+ *return_count);
+ if (!*return_iplist) {
+ DEBUG(3,("resolve_hosts: malloc fail !\n"));
+ freeaddrinfo(ailist);
+ return NT_STATUS_NO_MEMORY;
+ }
+ (*return_iplist)[i].ss = ss;
+ (*return_iplist)[i].port = PORT_NONE;
+ i++;
+ }
+ if (ailist) {
+ freeaddrinfo(ailist);
+ }
+ if (*return_count) {
+ return NT_STATUS_OK;
+ }
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/********************************************************
+ Resolve via "ADS" method.
+*********************************************************/
+
+static NTSTATUS resolve_ads(const char *name,
+ int name_type,
+ const char *sitename,
+ struct ip_service **return_iplist,
+ int *return_count)
+{
+ int i, j;
+ NTSTATUS status;
+ TALLOC_CTX *ctx;
+ struct dns_rr_srv *dcs = NULL;
+ int numdcs = 0;
+ int numaddrs = 0;
+
+ if ((name_type != 0x1c) && (name_type != KDC_NAME_TYPE) &&
+ (name_type != 0x1b)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if ( (ctx = talloc_init("resolve_ads")) == NULL ) {
+ DEBUG(0,("resolve_ads: talloc_init() failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* The DNS code needs fixing to find IPv6 addresses... JRA. */
+
+ switch (name_type) {
+ case 0x1b:
+ DEBUG(5,("resolve_ads: Attempting to resolve "
+ "PDC for %s using DNS\n", name));
+ status = ads_dns_query_pdc(ctx, name, &dcs, &numdcs);
+ break;
+
+ case 0x1c:
+ DEBUG(5,("resolve_ads: Attempting to resolve "
+ "DCs for %s using DNS\n", name));
+ status = ads_dns_query_dcs(ctx, name, sitename, &dcs,
+ &numdcs);
+ break;
+ case KDC_NAME_TYPE:
+ DEBUG(5,("resolve_ads: Attempting to resolve "
+ "KDCs for %s using DNS\n", name));
+ status = ads_dns_query_kdcs(ctx, name, sitename, &dcs,
+ &numdcs);
+ break;
+ default:
+ status = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if ( !NT_STATUS_IS_OK( status ) ) {
+ talloc_destroy(ctx);
+ return status;
+ }
+
+ for (i=0;i<numdcs;i++) {
+ numaddrs += MAX(dcs[i].num_ips,1);
+ }
+
+ if ((*return_iplist = SMB_MALLOC_ARRAY(struct ip_service, numaddrs)) ==
+ NULL ) {
+ DEBUG(0,("resolve_ads: malloc failed for %d entries\n",
+ numaddrs ));
+ talloc_destroy(ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* now unroll the list of IP addresses */
+
+ *return_count = 0;
+ i = 0;
+ j = 0;
+ while ( i < numdcs && (*return_count<numaddrs) ) {
+ struct ip_service *r = &(*return_iplist)[*return_count];
+
+ r->port = dcs[i].port;
+
+ /* If we don't have an IP list for a name, lookup it up */
+
+ if (!dcs[i].ss_s) {
+ interpret_string_addr(&r->ss, dcs[i].hostname, 0);
+ i++;
+ j = 0;
+ } else {
+ /* use the IP addresses from the SRV sresponse */
+
+ if ( j >= dcs[i].num_ips ) {
+ i++;
+ j = 0;
+ continue;
+ }
+
+ r->ss = dcs[i].ss_s[j];
+ j++;
+ }
+
+ /* make sure it is a valid IP. I considered checking the
+ * negative connection cache, but this is the wrong place
+ * for it. Maybe only as a hack. After think about it, if
+ * all of the IP addresses returned from DNS are dead, what
+ * hope does a netbios name lookup have ? The standard reason
+ * for falling back to netbios lookups is that our DNS server
+ * doesn't know anything about the DC's -- jerry */
+
+ if (!is_zero_addr(&r->ss)) {
+ (*return_count)++;
+ }
+ }
+
+ talloc_destroy(ctx);
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Internal interface to resolve a name into an IP address.
+ Use this function if the string is either an IP address, DNS
+ or host name or NetBIOS name. This uses the name switch in the
+ smb.conf to determine the order of name resolution.
+
+ Added support for ip addr/port to support ADS ldap servers.
+ the only place we currently care about the port is in the
+ resolve_hosts() when looking up DC's via SRV RR entries in DNS
+**********************************************************************/
+
+NTSTATUS internal_resolve_name(const char *name,
+ int name_type,
+ const char *sitename,
+ struct ip_service **return_iplist,
+ int *return_count,
+ const char *resolve_order)
+{
+ char *tok;
+ const char *ptr;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ int i;
+ TALLOC_CTX *frame = NULL;
+
+ *return_iplist = NULL;
+ *return_count = 0;
+
+ DEBUG(10, ("internal_resolve_name: looking up %s#%x (sitename %s)\n",
+ name, name_type, sitename ? sitename : NULL));
+
+ if (is_ipaddress(name)) {
+ if ((*return_iplist = SMB_MALLOC_P(struct ip_service)) ==
+ NULL) {
+ DEBUG(0,("internal_resolve_name: malloc fail !\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* ignore the port here */
+ (*return_iplist)->port = PORT_NONE;
+
+ /* if it's in the form of an IP address then get the lib to interpret it */
+ if (!interpret_string_addr(&(*return_iplist)->ss,
+ name, AI_NUMERICHOST)) {
+ DEBUG(1,("internal_resolve_name: interpret_string_addr "
+ "failed on %s\n",
+ name));
+ SAFE_FREE(*return_iplist);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ *return_count = 1;
+ return NT_STATUS_OK;
+ }
+
+ /* Check name cache */
+
+ if (namecache_fetch(name, name_type, return_iplist, return_count)) {
+ /* This could be a negative response */
+ if (*return_count > 0) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ /* set the name resolution order */
+
+ if (strcmp( resolve_order, "NULL") == 0) {
+ DEBUG(8,("internal_resolve_name: all lookups disabled\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!resolve_order[0]) {
+ ptr = "host";
+ } else {
+ ptr = resolve_order;
+ }
+
+ /* iterate through the name resolution backends */
+
+ frame = talloc_stackframe();
+ while (next_token_talloc(frame, &ptr, &tok, LIST_SEP)) {
+ if((strequal(tok, "host") || strequal(tok, "hosts"))) {
+ status = resolve_hosts(name, name_type, return_iplist,
+ return_count);
+ if (NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ } else if(strequal( tok, "kdc")) {
+ /* deal with KDC_NAME_TYPE names here.
+ * This will result in a SRV record lookup */
+ status = resolve_ads(name, KDC_NAME_TYPE, sitename,
+ return_iplist, return_count);
+ if (NT_STATUS_IS_OK(status)) {
+ /* Ensure we don't namecache
+ * this with the KDC port. */
+ name_type = KDC_NAME_TYPE;
+ goto done;
+ }
+ } else if(strequal( tok, "ads")) {
+ /* deal with 0x1c and 0x1b names here.
+ * This will result in a SRV record lookup */
+ status = resolve_ads(name, name_type, sitename,
+ return_iplist, return_count);
+ if (NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ } else if(strequal( tok, "lmhosts")) {
+ status = resolve_lmhosts(name, name_type,
+ return_iplist, return_count);
+ if (NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ } else if(strequal( tok, "wins")) {
+ /* don't resolve 1D via WINS */
+ if (name_type != 0x1D) {
+ status = resolve_wins(name, name_type,
+ return_iplist,
+ return_count);
+ if (NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ }
+ } else if(strequal( tok, "bcast")) {
+ status = name_resolve_bcast(name, name_type,
+ return_iplist,
+ return_count);
+ if (NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ } else {
+ DEBUG(0,("resolve_name: unknown name switch type %s\n",
+ tok));
+ }
+ }
+
+ /* All of the resolve_* functions above have returned false. */
+
+ TALLOC_FREE(frame);
+ SAFE_FREE(*return_iplist);
+ *return_count = 0;
+
+ return NT_STATUS_UNSUCCESSFUL;
+
+ done:
+
+ /* Remove duplicate entries. Some queries, notably #1c (domain
+ controllers) return the PDC in iplist[0] and then all domain
+ controllers including the PDC in iplist[1..n]. Iterating over
+ the iplist when the PDC is down will cause two sets of timeouts. */
+
+ if ( *return_count ) {
+ *return_count = remove_duplicate_addrs2(*return_iplist,
+ *return_count );
+ }
+
+ /* Save in name cache */
+ if ( DEBUGLEVEL >= 100 ) {
+ for (i = 0; i < *return_count && DEBUGLEVEL == 100; i++) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr, sizeof(addr),
+ &(*return_iplist)[i].ss);
+ DEBUG(100, ("Storing name %s of type %d (%s:%d)\n",
+ name,
+ name_type,
+ addr,
+ (*return_iplist)[i].port));
+ }
+ }
+
+ namecache_store(name, name_type, *return_count, *return_iplist);
+
+ /* Display some debugging info */
+
+ if ( DEBUGLEVEL >= 10 ) {
+ DEBUG(10, ("internal_resolve_name: returning %d addresses: ",
+ *return_count));
+
+ for (i = 0; i < *return_count; i++) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr, sizeof(addr),
+ &(*return_iplist)[i].ss);
+ DEBUGADD(10, ("%s:%d ",
+ addr,
+ (*return_iplist)[i].port));
+ }
+ DEBUG(10, ("\n"));
+ }
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/********************************************************
+ Internal interface to resolve a name into one IP address.
+ Use this function if the string is either an IP address, DNS
+ or host name or NetBIOS name. This uses the name switch in the
+ smb.conf to determine the order of name resolution.
+*********************************************************/
+
+bool resolve_name(const char *name,
+ struct sockaddr_storage *return_ss,
+ int name_type)
+{
+ struct ip_service *ss_list = NULL;
+ char *sitename = NULL;
+ int count = 0;
+
+ if (is_ipaddress(name)) {
+ return interpret_string_addr(return_ss, name, AI_NUMERICHOST);
+ }
+
+ sitename = sitename_fetch(lp_realm()); /* wild guess */
+
+ if (NT_STATUS_IS_OK(internal_resolve_name(name, name_type, sitename,
+ &ss_list, &count,
+ lp_name_resolve_order()))) {
+ int i;
+
+ /* only return valid addresses for TCP connections */
+ for (i=0; i<count; i++) {
+ if (!is_zero_addr(&ss_list[i].ss) &&
+ !is_broadcast_addr(&ss_list[i].ss)) {
+ *return_ss = ss_list[i].ss;
+ SAFE_FREE(ss_list);
+ SAFE_FREE(sitename);
+ return True;
+ }
+ }
+ }
+
+ SAFE_FREE(ss_list);
+ SAFE_FREE(sitename);
+ return False;
+}
+
+/********************************************************
+ Internal interface to resolve a name into a list of IP addresses.
+ Use this function if the string is either an IP address, DNS
+ or host name or NetBIOS name. This uses the name switch in the
+ smb.conf to determine the order of name resolution.
+*********************************************************/
+
+NTSTATUS resolve_name_list(TALLOC_CTX *ctx,
+ const char *name,
+ int name_type,
+ struct sockaddr_storage **return_ss_arr,
+ unsigned int *p_num_entries)
+{
+ struct ip_service *ss_list = NULL;
+ char *sitename = NULL;
+ int count = 0;
+ int i;
+ unsigned int num_entries;
+ NTSTATUS status;
+
+ *p_num_entries = 0;
+ *return_ss_arr = NULL;
+
+ if (is_ipaddress(name)) {
+ *return_ss_arr = TALLOC_P(ctx, struct sockaddr_storage);
+ if (!*return_ss_arr) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (!interpret_string_addr(*return_ss_arr, name, AI_NUMERICHOST)) {
+ TALLOC_FREE(*return_ss_arr);
+ return NT_STATUS_BAD_NETWORK_NAME;
+ }
+ *p_num_entries = 1;
+ return NT_STATUS_OK;
+ }
+
+ sitename = sitename_fetch(lp_realm()); /* wild guess */
+
+ status = internal_resolve_name(name, name_type, sitename,
+ &ss_list, &count,
+ lp_name_resolve_order());
+ SAFE_FREE(sitename);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* only return valid addresses for TCP connections */
+ for (i=0, num_entries = 0; i<count; i++) {
+ if (!is_zero_addr(&ss_list[i].ss) &&
+ !is_broadcast_addr(&ss_list[i].ss)) {
+ num_entries++;
+ }
+ }
+ if (num_entries == 0) {
+ SAFE_FREE(ss_list);
+ return NT_STATUS_BAD_NETWORK_NAME;
+ }
+
+ *return_ss_arr = TALLOC_ARRAY(ctx,
+ struct sockaddr_storage,
+ num_entries);
+ if (!(*return_ss_arr)) {
+ SAFE_FREE(ss_list);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0, num_entries = 0; i<count; i++) {
+ if (!is_zero_addr(&ss_list[i].ss) &&
+ !is_broadcast_addr(&ss_list[i].ss)) {
+ (*return_ss_arr)[num_entries++] = ss_list[i].ss;
+ }
+ }
+
+ status = NT_STATUS_OK;
+ *p_num_entries = num_entries;
+
+ SAFE_FREE(ss_list);
+ return NT_STATUS_OK;
+}
+
+/********************************************************
+ Find the IP address of the master browser or DMB for a workgroup.
+*********************************************************/
+
+bool find_master_ip(const char *group, struct sockaddr_storage *master_ss)
+{
+ struct ip_service *ip_list = NULL;
+ int count = 0;
+ NTSTATUS status;
+
+ if (lp_disable_netbios()) {
+ DEBUG(5,("find_master_ip(%s): netbios is disabled\n", group));
+ return false;
+ }
+
+ status = internal_resolve_name(group, 0x1D, NULL, &ip_list, &count,
+ lp_name_resolve_order());
+ if (NT_STATUS_IS_OK(status)) {
+ *master_ss = ip_list[0].ss;
+ SAFE_FREE(ip_list);
+ return true;
+ }
+
+ status = internal_resolve_name(group, 0x1B, NULL, &ip_list, &count,
+ lp_name_resolve_order());
+ if (NT_STATUS_IS_OK(status)) {
+ *master_ss = ip_list[0].ss;
+ SAFE_FREE(ip_list);
+ return true;
+ }
+
+ SAFE_FREE(ip_list);
+ return false;
+}
+
+/********************************************************
+ Get the IP address list of the primary domain controller
+ for a domain.
+*********************************************************/
+
+bool get_pdc_ip(const char *domain, struct sockaddr_storage *pss)
+{
+ struct ip_service *ip_list = NULL;
+ int count = 0;
+ NTSTATUS status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+
+ /* Look up #1B name */
+
+ if (lp_security() == SEC_ADS) {
+ status = internal_resolve_name(domain, 0x1b, NULL, &ip_list,
+ &count, "ads");
+ }
+
+ if (!NT_STATUS_IS_OK(status) || count == 0) {
+ status = internal_resolve_name(domain, 0x1b, NULL, &ip_list,
+ &count,
+ lp_name_resolve_order());
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ }
+
+ /* if we get more than 1 IP back we have to assume it is a
+ multi-homed PDC and not a mess up */
+
+ if ( count > 1 ) {
+ DEBUG(6,("get_pdc_ip: PDC has %d IP addresses!\n", count));
+ sort_service_list(ip_list, count);
+ }
+
+ *pss = ip_list[0].ss;
+ SAFE_FREE(ip_list);
+ return true;
+}
+
+/* Private enum type for lookups. */
+
+enum dc_lookup_type { DC_NORMAL_LOOKUP, DC_ADS_ONLY, DC_KDC_ONLY };
+
+/********************************************************
+ Get the IP address list of the domain controllers for
+ a domain.
+*********************************************************/
+
+static NTSTATUS get_dc_list(const char *domain,
+ const char *sitename,
+ struct ip_service **ip_list,
+ int *count,
+ enum dc_lookup_type lookup_type,
+ bool *ordered)
+{
+ char *resolve_order = NULL;
+ char *saf_servername = NULL;
+ char *pserver = NULL;
+ const char *p;
+ char *port_str = NULL;
+ int port;
+ char *name;
+ int num_addresses = 0;
+ int local_count, i, j;
+ struct ip_service *return_iplist = NULL;
+ struct ip_service *auto_ip_list = NULL;
+ bool done_auto_lookup = false;
+ int auto_count = 0;
+ NTSTATUS status;
+ TALLOC_CTX *ctx = talloc_init("get_dc_list");
+
+ *ip_list = NULL;
+ *count = 0;
+
+ if (!ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *ordered = False;
+
+ /* if we are restricted to solely using DNS for looking
+ up a domain controller, make sure that host lookups
+ are enabled for the 'name resolve order'. If host lookups
+ are disabled and ads_only is True, then set the string to
+ NULL. */
+
+ resolve_order = talloc_strdup(ctx, lp_name_resolve_order());
+ if (!resolve_order) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ strlower_m(resolve_order);
+ if (lookup_type == DC_ADS_ONLY) {
+ if (strstr( resolve_order, "host")) {
+ resolve_order = talloc_strdup(ctx, "ads");
+
+ /* DNS SRV lookups used by the ads resolver
+ are already sorted by priority and weight */
+ *ordered = true;
+ } else {
+ resolve_order = talloc_strdup(ctx, "NULL");
+ }
+ } else if (lookup_type == DC_KDC_ONLY) {
+ /* DNS SRV lookups used by the ads/kdc resolver
+ are already sorted by priority and weight */
+ *ordered = true;
+ resolve_order = talloc_strdup(ctx, "kdc");
+ }
+ if (!resolve_order) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ /* fetch the server we have affinity for. Add the
+ 'password server' list to a search for our domain controllers */
+
+ saf_servername = saf_fetch( domain);
+
+ if (strequal(domain, lp_workgroup()) || strequal(domain, lp_realm())) {
+ pserver = talloc_asprintf(NULL, "%s, %s",
+ saf_servername ? saf_servername : "",
+ lp_passwordserver());
+ } else {
+ pserver = talloc_asprintf(NULL, "%s, *",
+ saf_servername ? saf_servername : "");
+ }
+
+ SAFE_FREE(saf_servername);
+ if (!pserver) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ /* if we are starting from scratch, just lookup DOMAIN<0x1c> */
+
+ if (!*pserver ) {
+ DEBUG(10,("get_dc_list: no preferred domain controllers.\n"));
+ status = internal_resolve_name(domain, 0x1C, sitename, ip_list,
+ count, resolve_order);
+ goto out;
+ }
+
+ DEBUG(3,("get_dc_list: preferred server list: \"%s\"\n", pserver ));
+
+ /*
+ * if '*' appears in the "password server" list then add
+ * an auto lookup to the list of manually configured
+ * DC's. If any DC is listed by name, then the list should be
+ * considered to be ordered
+ */
+
+ p = pserver;
+ while (next_token_talloc(ctx, &p, &name, LIST_SEP)) {
+ if (!done_auto_lookup && strequal(name, "*")) {
+ status = internal_resolve_name(domain, 0x1C, sitename,
+ &auto_ip_list,
+ &auto_count,
+ resolve_order);
+ if (NT_STATUS_IS_OK(status)) {
+ num_addresses += auto_count;
+ }
+ done_auto_lookup = true;
+ DEBUG(8,("Adding %d DC's from auto lookup\n",
+ auto_count));
+ } else {
+ num_addresses++;
+ }
+ }
+
+ /* if we have no addresses and haven't done the auto lookup, then
+ just return the list of DC's. Or maybe we just failed. */
+
+ if ((num_addresses == 0)) {
+ if (done_auto_lookup) {
+ DEBUG(4,("get_dc_list: no servers found\n"));
+ status = NT_STATUS_NO_LOGON_SERVERS;
+ goto out;
+ }
+ status = internal_resolve_name(domain, 0x1C, sitename, ip_list,
+ count, resolve_order);
+ goto out;
+ }
+
+ if ((return_iplist = SMB_MALLOC_ARRAY(struct ip_service,
+ num_addresses)) == NULL) {
+ DEBUG(3,("get_dc_list: malloc fail !\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ p = pserver;
+ local_count = 0;
+
+ /* fill in the return list now with real IP's */
+
+ while ((local_count<num_addresses) &&
+ next_token_talloc(ctx, &p, &name, LIST_SEP)) {
+ struct sockaddr_storage name_ss;
+
+ /* copy any addersses from the auto lookup */
+
+ if (strequal(name, "*")) {
+ for (j=0; j<auto_count; j++) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr,
+ sizeof(addr),
+ &auto_ip_list[j].ss);
+ /* Check for and don't copy any
+ * known bad DC IP's. */
+ if(!NT_STATUS_IS_OK(check_negative_conn_cache(
+ domain,
+ addr))) {
+ DEBUG(5,("get_dc_list: "
+ "negative entry %s removed "
+ "from DC list\n",
+ addr));
+ continue;
+ }
+ return_iplist[local_count].ss =
+ auto_ip_list[j].ss;
+ return_iplist[local_count].port =
+ auto_ip_list[j].port;
+ local_count++;
+ }
+ continue;
+ }
+
+ /* added support for address:port syntax for ads
+ * (not that I think anyone will ever run the LDAP
+ * server in an AD domain on something other than
+ * port 389 */
+
+ port = (lp_security() == SEC_ADS) ? LDAP_PORT : PORT_NONE;
+ if ((port_str=strchr(name, ':')) != NULL) {
+ *port_str = '\0';
+ port_str++;
+ port = atoi(port_str);
+ }
+
+ /* explicit lookup; resolve_name() will
+ * handle names & IP addresses */
+ if (resolve_name( name, &name_ss, 0x20 )) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr,
+ sizeof(addr),
+ &name_ss);
+
+ /* Check for and don't copy any known bad DC IP's. */
+ if( !NT_STATUS_IS_OK(check_negative_conn_cache(domain,
+ addr)) ) {
+ DEBUG(5,("get_dc_list: negative entry %s "
+ "removed from DC list\n",
+ name ));
+ continue;
+ }
+
+ return_iplist[local_count].ss = name_ss;
+ return_iplist[local_count].port = port;
+ local_count++;
+ *ordered = true;
+ }
+ }
+
+ /* need to remove duplicates in the list if we have any
+ explicit password servers */
+
+ if (local_count) {
+ local_count = remove_duplicate_addrs2(return_iplist,
+ local_count );
+ }
+
+ if ( DEBUGLEVEL >= 4 ) {
+ DEBUG(4,("get_dc_list: returning %d ip addresses "
+ "in an %sordered list\n",
+ local_count,
+ *ordered ? "":"un"));
+ DEBUG(4,("get_dc_list: "));
+ for ( i=0; i<local_count; i++ ) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr,
+ sizeof(addr),
+ &return_iplist[i].ss);
+ DEBUGADD(4,("%s:%d ", addr, return_iplist[i].port ));
+ }
+ DEBUGADD(4,("\n"));
+ }
+
+ *ip_list = return_iplist;
+ *count = local_count;
+
+ status = ( *count != 0 ? NT_STATUS_OK : NT_STATUS_NO_LOGON_SERVERS );
+
+ out:
+
+ if (!NT_STATUS_IS_OK(status)) {
+ SAFE_FREE(return_iplist);
+ *ip_list = NULL;
+ *count = 0;
+ }
+
+ SAFE_FREE(auto_ip_list);
+ TALLOC_FREE(ctx);
+ return status;
+}
+
+/*********************************************************************
+ Small wrapper function to get the DC list and sort it if neccessary.
+*********************************************************************/
+
+NTSTATUS get_sorted_dc_list( const char *domain,
+ const char *sitename,
+ struct ip_service **ip_list,
+ int *count,
+ bool ads_only )
+{
+ bool ordered;
+ NTSTATUS status;
+ enum dc_lookup_type lookup_type = DC_NORMAL_LOOKUP;
+
+ *ip_list = NULL;
+ *count = 0;
+
+ DEBUG(8,("get_sorted_dc_list: attempting lookup "
+ "for name %s (sitename %s) using [%s]\n",
+ domain,
+ sitename ? sitename : "NULL",
+ (ads_only ? "ads" : lp_name_resolve_order())));
+
+ if (ads_only) {
+ lookup_type = DC_ADS_ONLY;
+ }
+
+ status = get_dc_list(domain, sitename, ip_list,
+ count, lookup_type, &ordered);
+ if (!NT_STATUS_IS_OK(status)) {
+ SAFE_FREE(*ip_list);
+ *count = 0;
+ return status;
+ }
+
+ /* only sort if we don't already have an ordered list */
+ if (!ordered) {
+ sort_service_list(*ip_list, *count);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Get the KDC list - re-use all the logic in get_dc_list.
+*********************************************************************/
+
+NTSTATUS get_kdc_list( const char *realm,
+ const char *sitename,
+ struct ip_service **ip_list,
+ int *count)
+{
+ bool ordered;
+ NTSTATUS status;
+
+ *count = 0;
+ *ip_list = NULL;
+
+ status = get_dc_list(realm, sitename, ip_list,
+ count, DC_KDC_ONLY, &ordered);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ SAFE_FREE(*ip_list);
+ *count = 0;
+ return status;
+ }
+
+ /* only sort if we don't already have an ordered list */
+ if ( !ordered ) {
+ sort_service_list(*ip_list, *count);
+ }
+
+ return NT_STATUS_OK;
+}