Allow cliconnect to loop through multiple ip addresses
authorJeremy Allison <jra@samba.org>
Wed, 12 Dec 2007 17:42:58 +0000 (09:42 -0800)
committerJeremy Allison <jra@samba.org>
Wed, 12 Dec 2007 17:42:58 +0000 (09:42 -0800)
for a server. We should have been doing this for a while,
but it's more critical with IPv6.
Original patch fixed up by James.
Jeremy.
(This used to be commit 5c7f7629a97ef0929e00e52f1fae4386c984000b)

source3/libsmb/cliconnect.c
source3/libsmb/namequery.c

index fdf7491d806eaea48a2c6a7c0040fb1b6b03f1fc..45c202090e2db3a49423f384c706c0089c21da76 100644 (file)
@@ -1424,7 +1424,11 @@ NTSTATUS cli_connect(struct cli_state *cli,
 
 {
        int name_type = 0x20;
-       char *p;
+       TALLOC_CTX *frame = talloc_stackframe();
+       unsigned int num_addrs = 0;
+       unsigned int i = 0;
+       struct sockaddr_storage *ss_arr = NULL;
+       char *p = NULL;
 
        /* reasonable default hostname */
        if (!host) {
@@ -1440,44 +1444,66 @@ NTSTATUS cli_connect(struct cli_state *cli,
        }
 
        if (!dest_ss || is_zero_addr(dest_ss)) {
-                if (!resolve_name(cli->desthost, &cli->dest_ss, name_type)) {
+               NTSTATUS status =resolve_name_list(frame,
+                                       cli->desthost,
+                                       name_type,
+                                       &ss_arr,
+                                       &num_addrs);
+               if (!NT_STATUS_IS_OK(status)) {
+                       TALLOC_FREE(frame);
                        return NT_STATUS_BAD_NETWORK_NAME;
                 }
-               if (dest_ss) {
-                       *dest_ss = cli->dest_ss;
-               }
        } else {
-               cli->dest_ss = *dest_ss;
+               num_addrs = 1;
+               ss_arr = TALLOC_P(frame, struct sockaddr_storage);
+               if (!ss_arr) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_MEMORY;
+               }
+               *ss_arr = *dest_ss;
        }
 
-       if (getenv("LIBSMB_PROG")) {
-               cli->fd = sock_exec(getenv("LIBSMB_PROG"));
-       } else {
-               /* try 445 first, then 139 */
-               uint16_t port = cli->port?cli->port:445;
-               cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ss,
-                                         port, cli->timeout);
-               if (cli->fd == -1 && cli->port == 0) {
-                       port = 139;
+       for (i = 0; i < num_addrs; i++) {
+               cli->dest_ss = ss_arr[i];
+               if (getenv("LIBSMB_PROG")) {
+                       cli->fd = sock_exec(getenv("LIBSMB_PROG"));
+               } else {
+                       /* try 445 first, then 139 */
+                       uint16_t port = cli->port?cli->port:445;
                        cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ss,
                                                  port, cli->timeout);
+                       if (cli->fd == -1 && cli->port == 0) {
+                               port = 139;
+                               cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ss,
+                                                         port, cli->timeout);
+                       }
+                       if (cli->fd != -1) {
+                               cli->port = port;
+                       }
                }
-               if (cli->fd != -1) {
-                       cli->port = port;
+               if (cli->fd == -1) {
+                       char addr[INET6_ADDRSTRLEN];
+                       print_sockaddr(addr, sizeof(addr), &ss_arr[i]);
+                       DEBUG(2,("Error connecting to %s (%s)\n",
+                                dest_ss?addr:host,strerror(errno)));
+               } else {
+                       /* Exit from loop on first connection. */
+                       break;
                }
        }
+
        if (cli->fd == -1) {
-               char addr[INET6_ADDRSTRLEN];
-               if (dest_ss) {
-                       print_sockaddr(addr, sizeof(addr), dest_ss);
-               }
-               DEBUG(1,("Error connecting to %s (%s)\n",
-                        dest_ss?addr:host,strerror(errno)));
+               TALLOC_FREE(frame);
                return map_nt_error_from_unix(errno);
        }
 
+       if (dest_ss) {
+               *dest_ss = cli->dest_ss;
+       }
+
        set_socket_options(cli->fd, lp_socket_options());
 
+       TALLOC_FREE(frame);
        return NT_STATUS_OK;
 }
 
index 819147d48f6c373662fb0d093261e6597857104a..853fe979b7704677fbb97287cc020a6d3c8ff5c4 100644 (file)
@@ -1653,6 +1653,87 @@ bool resolve_name(const char *name,
        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.
 *********************************************************/