Add context for libcli_resolve.
[samba-svnmirror.git] / source / libcli / raw / clisocket.c
index 1004db404018b54ce8e803da76cd9dfc33c31d90..c09104e256b00b2d66c3aa09f086e5786195ba98 100644 (file)
@@ -1,12 +1,14 @@
 /* 
    Unix SMB/CIFS implementation.
+
    SMB client socket context management functions
-   Copyright (C) Andrew Tridgell 1994-2003
+
+   Copyright (C) Andrew Tridgell 1994-2005
    Copyright (C) James Myers 2003 <myersjj@samba.org>
    
    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 2 of the License, or
+   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,
    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, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
+#include "lib/events/events.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/composite/composite.h"
+#include "lib/socket/socket.h"
+#include "libcli/resolve/resolve.h"
+#include "param/param.h"
 
+struct sock_connect_state {
+       struct composite_context *ctx;
+       const char *host_name;
+       int num_ports;
+       uint16_t *ports;
+       struct smbcli_socket *result;
+};
 
 /*
-  create a smbcli_socket context
+  connect a smbcli_socket context to an IP/port pair
+  if port is 0 then choose 445 then 139
 */
-struct smbcli_socket *smbcli_sock_init(void)
+
+static void smbcli_sock_connect_recv_conn(struct composite_context *ctx);
+
+struct composite_context *smbcli_sock_connect_send(TALLOC_CTX *mem_ctx,
+                                                  const char *host_addr,
+                                                  int port,
+                                                  const char *host_name,
+                                                  struct event_context *event_ctx)
 {
-       struct smbcli_socket *sock;
+       struct composite_context *result, *ctx;
+       struct sock_connect_state *state;
 
-       sock = talloc_named(NULL, sizeof(*sock), "smbcli_socket");
-       if (!sock) {
-               return NULL;
-       }
+       result = talloc_zero(mem_ctx, struct composite_context);
+       if (result == NULL) goto failed;
+       result->state = COMPOSITE_STATE_IN_PROGRESS;
 
-       ZERO_STRUCTP(sock);
-       sock->fd = -1;
-       sock->port = 0;
-       /* 20 second default timeout */
-       sock->timeout = 20000;
+       if (event_ctx != NULL) {
+               result->event_ctx = talloc_reference(result, event_ctx);
+       } else {
+               result->event_ctx = event_context_init(result);
+       }
 
-       sock->hostname = NULL;
+       if (result->event_ctx == NULL) goto failed;
 
-       return sock;
-}
+       state = talloc(result, struct sock_connect_state);
+       if (state == NULL) goto failed;
+       state->ctx = result;
+       result->private_data = state;
 
-/*
-  connect a smbcli_socket context to an IP/port pair
-  if port is 0 then choose 445 then 139
-*/
-BOOL smbcli_sock_connect(struct smbcli_socket *sock, struct in_addr *ip, int port)
-{
-       if (getenv("LIBSMB_PROG")) {
-               sock->fd = sock_exec(getenv("LIBSMB_PROG"));
-               return sock->fd != -1;
-       }
+       state->host_name = talloc_strdup(state, host_name);
+       if (state->host_name == NULL) goto failed;
 
        if (port == 0) {
+               const char **ports = lp_smb_ports(global_loadparm);
                int i;
-               const char **ports = lp_smb_ports();
+
+               for (i=0;ports[i];i++) /* noop */ ;
+               if (i == 0) {
+                       DEBUG(3, ("no smb ports defined\n"));
+                       goto failed;
+               }
+               state->num_ports = i;
+               state->ports = talloc_array(state, uint16_t, i);
+               if (state->ports == NULL) goto failed;
                for (i=0;ports[i];i++) {
-                       port = atoi(ports[i]);
-                       if (port != 0 && smbcli_sock_connect(sock, ip, port)) {
-                               return True;
-                       }
+                       state->ports[i] = atoi(ports[i]);
                }
-               return False;
-       }
-
-       sock->dest_ip = *ip;
-       sock->port = port;
-       sock->fd = open_socket_out(SOCK_STREAM,
-                                  &sock->dest_ip,
-                                  sock->port, 
-                                  LONG_CONNECT_TIMEOUT);
-       if (sock->fd == -1) {
-               return False;
+       } else {
+               state->ports = talloc_array(state, uint16_t, 1);
+               if (state->ports == NULL) goto failed;
+               state->num_ports = 1;
+               state->ports[0] = port;
        }
 
-       set_blocking(sock->fd, False);
+       ctx = socket_connect_multi_send(state, host_addr,
+                                       state->num_ports, state->ports,
+                                       lp_resolve_context(global_loadparm),
+                                       state->ctx->event_ctx);
+       if (ctx == NULL) goto failed;
+       ctx->async.fn = smbcli_sock_connect_recv_conn;
+       ctx->async.private_data = state;
+       return result;
 
-       return True;
+failed:
+       talloc_free(result);
+       return NULL;
 }
 
-
-/****************************************************************************
- mark the socket as dead
-****************************************************************************/
-void smbcli_sock_dead(struct smbcli_socket *sock)
+static void smbcli_sock_connect_recv_conn(struct composite_context *ctx)
 {
-       if (sock->fd != -1) {
-               close(sock->fd);
-               sock->fd = -1;
-       }
+       struct sock_connect_state *state =
+               talloc_get_type(ctx->async.private_data,
+                               struct sock_connect_state);
+       struct socket_context *sock;
+       uint16_t port;
+
+       state->ctx->status = socket_connect_multi_recv(ctx, state, &sock,
+                                                      &port);
+       if (!composite_is_ok(state->ctx)) return;
+
+       state->ctx->status =
+               socket_set_option(sock, lp_socket_options(global_loadparm), NULL);
+       if (!composite_is_ok(state->ctx)) return;
+
+
+       state->result = talloc_zero(state, struct smbcli_socket);
+       if (composite_nomem(state->result, state->ctx)) return;
+
+       state->result->sock = talloc_steal(state->result, sock);
+       state->result->port = port;
+       state->result->hostname = talloc_steal(sock, state->host_name);
+
+       state->result->event.ctx =
+               talloc_reference(state->result, state->ctx->event_ctx);
+       if (composite_nomem(state->result->event.ctx, state->ctx)) return;
+
+       composite_done(state->ctx);
 }
 
-/****************************************************************************
- reduce socket reference count - if it becomes zero then close
-****************************************************************************/
-void smbcli_sock_close(struct smbcli_socket *sock)
+/*
+  finish a smbcli_sock_connect_send() operation
+*/
+NTSTATUS smbcli_sock_connect_recv(struct composite_context *c,
+                                 TALLOC_CTX *mem_ctx,
+                                 struct smbcli_socket **result)
 {
-       sock->reference_count--;
-       if (sock->reference_count <= 0) {
-               smbcli_sock_dead(sock);
+       NTSTATUS status = composite_wait(c);
+       if (NT_STATUS_IS_OK(status)) {
+               struct sock_connect_state *state =
+                       talloc_get_type(c->private_data,
+                                       struct sock_connect_state);
+               *result = talloc_steal(mem_ctx, state->result);
        }
+       talloc_free(c);
+       return status;
 }
 
-/****************************************************************************
- Set socket options on a open connection.
-****************************************************************************/
-void smbcli_sock_set_options(struct smbcli_socket *sock, const char *options)
+/*
+  connect a smbcli_socket context to an IP/port pair
+  if port is 0 then choose the ports listed in smb.conf (normally 445 then 139)
+
+  sync version of the function
+*/
+NTSTATUS smbcli_sock_connect(TALLOC_CTX *mem_ctx,
+                            const char *host_addr, int port,
+                            const char *host_name,
+                            struct event_context *event_ctx,
+                            struct smbcli_socket **result)
 {
-       set_socket_options(sock->fd, options);
+       struct composite_context *c =
+               smbcli_sock_connect_send(mem_ctx, host_addr, port, host_name,
+                                        event_ctx);
+       return smbcli_sock_connect_recv(c, mem_ctx, result);
 }
 
+
 /****************************************************************************
- Write to socket. Return amount written.
+ mark the socket as dead
 ****************************************************************************/
-ssize_t smbcli_sock_write(struct smbcli_socket *sock, const char *data, size_t len)
+void smbcli_sock_dead(struct smbcli_socket *sock)
 {
-       if (sock->fd == -1) {
-               errno = EIO;
-               return -1;
-       }
-
-       return write_data(sock->fd, data, len);
+       talloc_free(sock->event.fde);
+       sock->event.fde = NULL;
+       talloc_free(sock->sock);
+       sock->sock = NULL;
 }
 
-
 /****************************************************************************
- Read from socket. return amount read
+ Set socket options on a open connection.
 ****************************************************************************/
-ssize_t smbcli_sock_read(struct smbcli_socket *sock, char *data, size_t len)
+void smbcli_sock_set_options(struct smbcli_socket *sock, const char *options)
 {
-       if (sock->fd == -1) {
-               errno = EIO;
-               return -1;
-       }
-
-       return read_data(sock->fd, data, len);
+       socket_set_option(sock->sock, options, NULL);
 }
 
 /****************************************************************************
 resolve a hostname and connect 
 ****************************************************************************/
-BOOL smbcli_sock_connect_byname(struct smbcli_socket *sock, const char *host, int port)
+struct smbcli_socket *smbcli_sock_connect_byname(const char *host, int port,
+                                                TALLOC_CTX *mem_ctx,
+                                                struct resolve_context *resolve_ctx,
+                                                struct event_context *event_ctx)
 {
-       int name_type = 0x20;
-       struct in_addr ip;
+       int name_type = NBT_NAME_SERVER;
+       const char *address;
+       NTSTATUS status;
+       struct nbt_name nbt_name;
        char *name, *p;
-       BOOL ret;
+       TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+       struct smbcli_socket *result;
 
-       if (getenv("LIBSMB_PROG")) {
-               sock->fd = sock_exec(getenv("LIBSMB_PROG"));
-               return sock->fd != -1;
+       if (tmp_ctx == NULL) {
+               DEBUG(0, ("talloc_new failed\n"));
+               return NULL;
        }
 
-       name = talloc_strdup(sock, host);
+       name = talloc_strdup(tmp_ctx, host);
+       if (name == NULL) {
+               DEBUG(0, ("talloc_strdup failed\n"));
+               talloc_free(tmp_ctx);
+               return NULL;
+       }
+
+       if (event_ctx == NULL) {
+               event_ctx = event_context_init(mem_ctx);
+       }
+
+       if (event_ctx == NULL) {
+               DEBUG(0, ("event_context_init failed\n"));
+               talloc_free(tmp_ctx);
+               return NULL;
+       }
 
        /* allow hostnames of the form NAME#xx and do a netbios lookup */
        if ((p = strchr(name, '#'))) {
@@ -164,18 +239,25 @@ BOOL smbcli_sock_connect_byname(struct smbcli_socket *sock, const char *host, in
                *p = 0;
        }
 
-       if (!resolve_name(name, name, &ip, name_type)) {
-               talloc_free(name);
-               return False;
+       make_nbt_name(&nbt_name, host, name_type);
+       
+       status = resolve_name(resolve_ctx, &nbt_name, tmp_ctx, &address, event_ctx);
+       if (!NT_STATUS_IS_OK(status)) {
+               talloc_free(tmp_ctx);
+               return NULL;
        }
 
-       ret = smbcli_sock_connect(sock, &ip, port);
+       status = smbcli_sock_connect(mem_ctx, address, port, name, event_ctx,
+                                    &result);
 
-       if (ret) {
-               sock->hostname = talloc_steal(sock, name);
-       } else {
-               talloc_free(name);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(9, ("smbcli_sock_connect failed: %s\n",
+                         nt_errstr(status)));
+               talloc_free(tmp_ctx);
+               return NULL;
        }
 
-       return ret;
+       talloc_free(tmp_ctx);
+
+       return result;
 }