Fix the loop unrolling inside resolve_ads().
[samba.git] / source3 / libsmb / namequery.c
index 50fb9f1620590aefde3144b26a9edc0154525328..af76f3f85db08c63cff2e3e13400ae0839ad9339 100644 (file)
@@ -76,9 +76,6 @@ bool saf_store( const char *domain, const char *servername )
                return False;
        }
 
-       if ( !gencache_init() )
-               return False;
-
        key = saf_key( domain );
        expire = time( NULL ) + lp_parm_int(-1, "saf","ttl", SAF_TTL);
 
@@ -108,9 +105,6 @@ bool saf_join_store( const char *domain, const char *servername )
                return False;
        }
 
-       if ( !gencache_init() )
-               return False;
-
        key = saf_join_key( domain );
        expire = time( NULL ) + lp_parm_int(-1, "saf","join ttl", SAFJOIN_TTL);
 
@@ -134,9 +128,6 @@ bool saf_delete( const char *domain )
                return False;
        }
 
-       if ( !gencache_init() )
-               return False;
-
        key = saf_join_key(domain);
        ret = gencache_del(key);
        SAFE_FREE(key);
@@ -171,9 +162,6 @@ char *saf_fetch( const char *domain )
                return NULL;
        }
 
-       if ( !gencache_init() )
-               return False;
-
        key = saf_join_key( domain );
 
        ret = gencache_get( key, &server, &timeout );
@@ -301,7 +289,8 @@ NODE_STATUS_STRUCT *node_status_query(int fd,
 
        p.ip = ((const struct sockaddr_in *)to_ss)->sin_addr;
        p.port = NMB_PORT;
-       p.fd = fd;
+       p.recv_fd = -1;
+       p.send_fd = fd;
        p.timestamp = time(NULL);
        p.packet_type = NMB_PACKET;
 
@@ -451,12 +440,12 @@ static int addr_compare(const struct sockaddr *ss1,
        int num_interfaces = iface_count();
        int i;
 
-       /* Sort IPv6 addresses first. */
+       /* Sort IPv4 addresses first. */
        if (ss1->sa_family != ss2->sa_family) {
                if (ss2->sa_family == AF_INET) {
-                       return -1;
-               } else {
                        return 1;
+               } else {
+                       return -1;
                }
        }
 
@@ -582,7 +571,7 @@ static int remove_duplicate_addrs2(struct ip_service *iplist, int count )
        DEBUG(10,("remove_duplicate_addrs2: "
                        "looking for duplicate address/port pairs\n"));
 
-       /* one loop to remove duplicates */
+       /* One loop to set duplicates to a zero addr. */
        for ( i=0; i<count; i++ ) {
                if ( is_zero_addr((struct sockaddr *)&iplist[i].ss)) {
                        continue;
@@ -596,23 +585,54 @@ static int remove_duplicate_addrs2(struct ip_service *iplist, int count )
                }
        }
 
-       /* 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((struct sockaddr *)&iplist[i].ss) ) {
-                       if (i != count-1) {
-                               memmove(&iplist[i], &iplist[i+1],
-                                       (count - i - 1)*sizeof(iplist[i]));
+       /* Now remove any addresses set to zero above. */
+       for (i = 0; i < count; i++) {
+               while (i < count &&
+                               is_zero_addr((struct sockaddr *)&iplist[i].ss)) {
+                       if (count-i-1>0) {
+                               memmove(&iplist[i],
+                                       &iplist[i+1],
+                                       (count-i-1)*sizeof(struct ip_service));
                        }
                        count--;
-                       continue;
                }
-               i++;
        }
 
        return count;
 }
 
+static bool prioritize_ipv4_list(struct ip_service *iplist, int count)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct ip_service *iplist_new = TALLOC_ARRAY(frame, struct ip_service, count);
+       int i, j;
+
+       if (iplist_new == NULL) {
+               TALLOC_FREE(frame);
+               return false;
+       }
+
+       j = 0;
+
+       /* Copy IPv4 first. */
+       for (i = 0; i < count; i++) {
+               if (iplist[i].ss.ss_family == AF_INET) {
+                       iplist_new[j++] = iplist[i];
+               }
+       }
+
+       /* Copy IPv6. */
+       for (i = 0; i < count; i++) {
+               if (iplist[i].ss.ss_family != AF_INET) {
+                       iplist_new[j++] = iplist[i];
+               }
+       }
+
+       memcpy(iplist, iplist_new, sizeof(struct ip_service)*count);
+       TALLOC_FREE(frame);
+       return true;
+}
+
 /****************************************************************************
  Do a netbios name query to find someones IP.
  Returns an array of IP addresses or NULL if none.
@@ -678,7 +698,8 @@ struct sockaddr_storage *name_query(int fd,
 
        p.ip = ((struct sockaddr_in *)to_ss)->sin_addr;
        p.port = NMB_PORT;
-       p.fd = fd;
+       p.recv_fd = -1;
+       p.send_fd = fd;
        p.timestamp = time(NULL);
        p.packet_type = NMB_PACKET;
 
@@ -827,160 +848,53 @@ struct sockaddr_storage *name_query(int fd,
 }
 
 /********************************************************
- 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
+ Convert an array if struct sockaddr_storage to struct ip_service
  return false on failure.  Port is set to PORT_NONE;
+ pcount is [in/out] - it is the length of ss_list on input,
+ and the length of return_iplist on output as we remove any
+ zero addresses from ss_list.
 *********************************************************/
 
 static bool convert_ss2service(struct ip_service **return_iplist,
                const struct sockaddr_storage *ss_list,
-               int count)
+               int *pcount)
 {
        int i;
+       int orig_count = *pcount;
+       int real_count = 0;
 
-       if ( count==0 || !ss_list )
+       if (orig_count==0 || !ss_list )
                return False;
 
+       /* Filter out zero addrs. */
+       for ( i=0; i<orig_count; i++ ) {
+               if (is_zero_addr((struct sockaddr *)&ss_list[i])) {
+                       continue;
+               }
+               real_count++;
+       }
+       if (real_count==0) {
+               return false;
+       }
+
        /* copy the ip address; port will be PORT_NONE */
-       if ((*return_iplist = SMB_MALLOC_ARRAY(struct ip_service, count)) ==
+       if ((*return_iplist = SMB_MALLOC_ARRAY(struct ip_service, real_count)) ==
                        NULL) {
                DEBUG(0,("convert_ip2service: malloc failed "
-                       "for %d enetries!\n", count ));
+                       "for %d enetries!\n", real_count ));
                return False;
        }
 
-       for ( i=0; i<count; i++ ) {
-               (*return_iplist)[i].ss   = ss_list[i];
-               (*return_iplist)[i].port = PORT_NONE;
+       for ( i=0, real_count = 0; i<orig_count; i++ ) {
+               if (is_zero_addr((struct sockaddr *)&ss_list[i])) {
+                       continue;
+               }
+               (*return_iplist)[real_count].ss   = ss_list[i];
+               (*return_iplist)[real_count].port = PORT_NONE;
+               real_count++;
        }
 
+       *pcount = real_count;
        return true;
 }
 
@@ -1053,7 +967,7 @@ NTSTATUS name_resolve_bcast(const char *name,
 success:
 
        status = NT_STATUS_OK;
-       if (!convert_ss2service(return_iplist, ss_list, *return_count) )
+       if (!convert_ss2service(return_iplist, ss_list, return_count) )
                status = NT_STATUS_INVALID_PARAMETER;
 
        SAFE_FREE(ss_list);
@@ -1188,7 +1102,7 @@ NTSTATUS resolve_wins(const char *name,
 success:
 
        status = NT_STATUS_OK;
-       if (!convert_ss2service(return_iplist, ss_list, *return_count))
+       if (!convert_ss2service(return_iplist, ss_list, return_count))
                status = NT_STATUS_INVALID_PARAMETER;
 
        SAFE_FREE(ss_list);
@@ -1336,6 +1250,10 @@ static NTSTATUS resolve_hosts(const char *name, int name_type,
                ZERO_STRUCT(ss);
                memcpy(&ss, res->ai_addr, res->ai_addrlen);
 
+               if (is_zero_addr((struct sockaddr *)&ss)) {
+                       continue;
+               }
+
                *return_count += 1;
 
                *return_iplist = SMB_REALLOC_ARRAY(*return_iplist,
@@ -1369,7 +1287,7 @@ static NTSTATUS resolve_ads(const char *name,
                            struct ip_service **return_iplist,
                            int *return_count)
 {
-       int                     i, j;
+       int                     i;
        NTSTATUS                status;
        TALLOC_CTX              *ctx;
        struct dns_rr_srv       *dcs = NULL;
@@ -1418,7 +1336,11 @@ static NTSTATUS resolve_ads(const char *name,
        }
 
        for (i=0;i<numdcs;i++) {
-               numaddrs += MAX(dcs[i].num_ips,1);
+               if (!dcs[i].ss_s) {
+                       numaddrs += 1;
+               } else {
+                       numaddrs += dcs[i].num_ips;
+               }
        }
 
        if ((*return_iplist = SMB_MALLOC_ARRAY(struct ip_service, numaddrs)) ==
@@ -1432,42 +1354,75 @@ static NTSTATUS resolve_ads(const char *name,
        /* 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 */
 
+       for (i = 0; i < numdcs; i++) {
+               /* If we don't have an IP list for a name, look 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;
+                       /* We need to get all IP addresses here. */
+                       struct addrinfo *res = NULL;
+                       struct addrinfo *p;
+                       int extra_addrs = 0;
+
+                       if (!interpret_string_addr_internal(&res,
+                                               dcs[i].hostname,
+                                               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((struct sockaddr *)&r->ss)) {
-                       (*return_count)++;
+                       /* Add in every IP from the lookup. How
+                          many is that ? */
+                       for (p = res; p; p = p->ai_next) {
+                               if (is_zero_addr((struct sockaddr *)p->ai_addr)) {
+                                       continue;
+                               }
+                               extra_addrs++;
+                       }
+                       if (extra_addrs > 1) {
+                               /* We need to expand the return_iplist array
+                                  as we only budgeted for one address. */
+                               numaddrs += (extra_addrs-1);
+                               *return_iplist = SMB_REALLOC_ARRAY(*return_iplist,
+                                               struct ip_service,
+                                               numaddrs);
+                               if (*return_iplist == NULL) {
+                                       if (res) {
+                                               freeaddrinfo(res);
+                                       }
+                                       talloc_destroy(ctx);
+                                       return NT_STATUS_NO_MEMORY;
+                               }
+                       }
+                       for (p = res; p; p = p->ai_next) {
+                               (*return_iplist)[*return_count].port = dcs[i].port;
+                               memcpy(&(*return_iplist)[*return_count].ss,
+                                               p->ai_addr,
+                                               p->ai_addrlen);
+                               if (is_zero_addr((struct sockaddr *)&(*return_iplist)[*return_count].ss)) {
+                                       continue;
+                               }
+                               (*return_count)++;
+                               /* Should never happen, but still... */
+                               if (*return_count>=numaddrs) {
+                                       break;
+                               }
+                       }
+                       if (res) {
+                               freeaddrinfo(res);
+                       }
+               } else {
+                       /* use all the IP addresses from the SRV sresponse */
+                       int j;
+                       for (j = 0; j < dcs[i].num_ips; j++) {
+                               (*return_iplist)[*return_count].port = dcs[i].port;
+                               (*return_iplist)[*return_count].ss = dcs[i].ss_s[j];
+                               if (is_zero_addr((struct sockaddr *)&(*return_iplist)[*return_count].ss)) {
+                                       continue;
+                               }
+                               (*return_count)++;
+                               /* Should never happen, but still... */
+                               if (*return_count>=numaddrs) {
+                                       break;
+                               }
+                       }
                }
        }
 
@@ -1524,6 +1479,10 @@ NTSTATUS internal_resolve_name(const char *name,
                        SAFE_FREE(*return_iplist);
                        return NT_STATUS_INVALID_PARAMETER;
                }
+               if (is_zero_addr((struct sockaddr *)&(*return_iplist)->ss)) {
+                       SAFE_FREE(*return_iplist);
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
                *return_count = 1;
                return NT_STATUS_OK;
        }
@@ -1531,6 +1490,8 @@ NTSTATUS internal_resolve_name(const char *name,
        /* Check name cache */
 
        if (namecache_fetch(name, name_type, return_iplist, return_count)) {
+               *return_count = remove_duplicate_addrs2(*return_iplist,
+                                       *return_count );
                /* This could be a negative response */
                if (*return_count > 0) {
                        return NT_STATUS_OK;
@@ -1625,10 +1586,7 @@ NTSTATUS internal_resolve_name(const char *name,
        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 );
-       }
+       *return_count = remove_duplicate_addrs2(*return_iplist, *return_count );
 
        /* Save in name cache */
        if ( DEBUGLEVEL >= 100 ) {
@@ -1644,7 +1602,9 @@ NTSTATUS internal_resolve_name(const char *name,
                }
        }
 
-       namecache_store(name, name_type, *return_count, *return_iplist);
+       if (*return_count) {
+               namecache_store(name, name_type, *return_count, *return_iplist);
+       }
 
        /* Display some debugging info */
 
@@ -1676,7 +1636,8 @@ NTSTATUS internal_resolve_name(const char *name,
 
 bool resolve_name(const char *name,
                struct sockaddr_storage *return_ss,
-               int name_type)
+               int name_type,
+               bool prefer_ipv4)
 {
        struct ip_service *ss_list = NULL;
        char *sitename = NULL;
@@ -1693,6 +1654,19 @@ bool resolve_name(const char *name,
                                                  lp_name_resolve_order()))) {
                int i;
 
+               if (prefer_ipv4) {
+                       for (i=0; i<count; i++) {
+                               if (!is_zero_addr((struct sockaddr *)&ss_list[i].ss) &&
+                                               !is_broadcast_addr((struct sockaddr *)&ss_list[i].ss) &&
+                                               (ss_list[i].ss.ss_family == AF_INET)) {
+                                       *return_ss = ss_list[i].ss;
+                                       SAFE_FREE(ss_list);
+                                       SAFE_FREE(sitename);
+                                       return True;
+                               }
+                       }
+               }
+
                /* only return valid addresses for TCP connections */
                for (i=0; i<count; i++) {
                        if (!is_zero_addr((struct sockaddr *)&ss_list[i].ss) &&
@@ -2068,7 +2042,7 @@ static NTSTATUS get_dc_list(const char *domain,
 
                /* explicit lookup; resolve_name() will
                 * handle names & IP addresses */
-               if (resolve_name( name, &name_ss, 0x20 )) {
+               if (resolve_name( name, &name_ss, 0x20, true )) {
                        char addr[INET6_ADDRSTRLEN];
                        print_sockaddr(addr,
                                        sizeof(addr),
@@ -2093,9 +2067,13 @@ static NTSTATUS get_dc_list(const char *domain,
        /* 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 );
+       local_count = remove_duplicate_addrs2(return_iplist, local_count );
+
+       /* For DC's we always prioritize IPv4 due to W2K3 not
+        * supporting LDAP, KRB5 or CLDAP over IPv6. */
+
+       if (local_count && return_iplist) {
+               prioritize_ipv4_list(return_iplist, local_count);
        }
 
        if ( DEBUGLEVEL >= 4 ) {