From e3e16928c057b35b66bc814a507fc79b9e02084c Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 12 Dec 2007 09:42:58 -0800 Subject: Allow cliconnect to loop through multiple ip addresses 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 | 72 +++++++++++++++++++++++++++------------- source3/libsmb/namequery.c | 81 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+), 23 deletions(-) diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c index fdf7491d80..45c202090e 100644 --- a/source3/libsmb/cliconnect.c +++ b/source3/libsmb/cliconnect.c @@ -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; } diff --git a/source3/libsmb/namequery.c b/source3/libsmb/namequery.c index 819147d48f..853fe979b7 100644 --- a/source3/libsmb/namequery.c +++ b/source3/libsmb/namequery.c @@ -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