r5304: removed lib/socket/socket.h from includes.h
[nivanova/samba-autobuild/.git] / source4 / libcli / raw / clisocket.c
index 566367233379fbb7953867718e64c08b2ef96e75..cbb479ca1a1388e8fb645663088be2ab4f553c1b 100644 (file)
@@ -1,7 +1,9 @@
 /* 
    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
 */
 
 #include "includes.h"
+#include "lib/events/events.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/composite/composite.h"
+#include "lib/socket/socket.h"
+
+/*
+  this private structure is used during async connection handling
+*/
+struct clisocket_connect {
+       int port_num;
+       int *iports;
+       struct smbcli_socket *sock;
+       const char *dest_host;
+};
 
 
 /*
   create a smbcli_socket context
+  The event_ctx is optional - if not supplied one will be created
 */
-struct smbcli_socket *smbcli_sock_init(void)
+struct smbcli_socket *smbcli_sock_init(TALLOC_CTX *mem_ctx, 
+                                      struct event_context *event_ctx)
 {
        struct smbcli_socket *sock;
 
-       sock = talloc_p(NULL, struct smbcli_socket);
+       sock = talloc_zero(mem_ctx, struct smbcli_socket);
        if (!sock) {
                return NULL;
        }
 
-       ZERO_STRUCTP(sock);
-       sock->fd = -1;
-       sock->port = 0;
-       /* 20 second default timeout */
-       sock->timeout = 20000;
-
-       sock->hostname = NULL;
+       if (event_ctx) {
+               sock->event.ctx = talloc_reference(sock, event_ctx);
+       } else {
+               sock->event.ctx = event_context_init(sock);
+       }
+       if (sock->event.ctx == NULL) {
+               talloc_free(sock);
+               return NULL;
+       }
 
        return sock;
 }
 
+static NTSTATUS smbcli_sock_connect_one(struct smbcli_socket *sock, 
+                                       const char *hostaddr, int port, 
+                                       struct composite_context *c);
+
+/*
+  handle socket write events during an async connect. These happen when the OS
+  has either completed the connect() or has returned an error
+*/
+static void smbcli_sock_connect_handler(struct event_context *ev, struct fd_event *fde, 
+                                       uint16_t flags, void *private)
+{
+       struct composite_context *c = talloc_get_type(private, struct composite_context);
+       struct clisocket_connect *conn = talloc_get_type(c->private, struct clisocket_connect);
+       int i;
+       
+       c->status = socket_connect_complete(conn->sock->sock, 0);
+       if (NT_STATUS_IS_OK(c->status)) {
+               socket_set_option(conn->sock->sock, lp_socket_options(), NULL);
+               conn->sock->hostname = talloc_strdup(conn->sock, conn->dest_host);
+               c->state = SMBCLI_REQUEST_DONE;
+               if (c->async.fn) {
+                       c->async.fn(c);
+               }
+               return;
+       }
+
+       /* that port failed - try the next port */
+       for (i=conn->port_num+1;conn->iports[i];i++) {
+               conn->port_num = i;
+               c->status = smbcli_sock_connect_one(conn->sock, 
+                                                   conn->dest_host, 
+                                                   conn->iports[i], c);
+               if (NT_STATUS_IS_OK(c->status) ||
+                   NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+                       return;
+               }
+       }
+
+       c->state = SMBCLI_REQUEST_ERROR;
+       if (c->async.fn) {
+               c->async.fn(c);
+       }
+}
+
+
+/*
+  try to connect to the given address/port
+*/
+static NTSTATUS smbcli_sock_connect_one(struct smbcli_socket *sock, 
+                                       const char *hostaddr, int port,
+                                       struct composite_context *c)
+{
+       NTSTATUS status;
+
+       if (sock->sock) {
+               talloc_free(sock->sock);
+               sock->sock = NULL;
+       }
+       talloc_free(sock->event.fde);
+
+       status = socket_create("ip", SOCKET_TYPE_STREAM, &sock->sock, 0);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       talloc_steal(sock, sock->sock);
+
+       /* we initially look for write - see the man page on
+          non-blocking connect */
+       sock->event.fde = event_add_fd(sock->event.ctx, sock, socket_get_fd(sock->sock), 
+                                      EVENT_FD_WRITE, smbcli_sock_connect_handler, c);
+
+       sock->port = port;
+       set_blocking(socket_get_fd(sock->sock), False);
+
+       return socket_connect(sock->sock, NULL, 0, hostaddr, port, 0);
+}
+                                       
+
 /*
   connect a smbcli_socket context to an IP/port pair
   if port is 0 then choose 445 then 139
+
+  this is the async send side of the interface
 */
-BOOL smbcli_sock_connect(struct smbcli_socket *sock, struct in_addr *ip, int port)
+struct composite_context *smbcli_sock_connect_send(struct smbcli_socket *sock, 
+                                                 const char *host_addr, int port)
 {
-       if (getenv("LIBSMB_PROG")) {
-               sock->fd = sock_exec(getenv("LIBSMB_PROG"));
-               return sock->fd != -1;
-       }
+       struct composite_context *c;
+       struct clisocket_connect *conn;
+       int i;
 
+       c = talloc_zero(sock, struct composite_context);
+       if (c == NULL) return NULL;
+
+       c->event_ctx = sock->event.ctx;
+
+       conn = talloc(c, struct clisocket_connect);
+       if (conn == NULL) goto failed;
+
+       conn->sock = sock;
+
+       /* work out what ports we will try */
        if (port == 0) {
-               int i;
                const char **ports = lp_smb_ports();
+               for (i=0;ports[i];i++) /* noop */ ;
+               conn->iports = talloc_array(c, int, i+1);
+               if (conn->iports == NULL) goto failed;
                for (i=0;ports[i];i++) {
-                       port = atoi(ports[i]);
-                       if (port != 0 && smbcli_sock_connect(sock, ip, port)) {
-                               return True;
-                       }
+                       conn->iports[i] = atoi(ports[i]);
                }
-               return False;
+               conn->iports[i] = 0;
+       } else {
+               conn->iports = talloc_array(c, int, 2);
+               if (conn->iports == NULL) goto failed;
+               conn->iports[0] = port;
+               conn->iports[1] = 0;
        }
 
-       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;
+       conn->dest_host = talloc_strdup(c, host_addr);
+       if (conn->dest_host == NULL) goto failed;
+
+       c->private = conn;
+       c->state = SMBCLI_REQUEST_SEND;
+
+       /* startup the connect process for each port in turn until one
+          succeeds or tells us that it is pending */
+       for (i=0;conn->iports[i];i++) {
+               conn->port_num = i;
+               conn->sock->port = conn->iports[i];
+               c->status = smbcli_sock_connect_one(sock, 
+                                                   conn->dest_host, 
+                                                   conn->iports[i], c);
+               if (NT_STATUS_IS_OK(c->status) ||
+                   NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+                       return c;
+               }
        }
 
-       set_blocking(sock->fd, False);
-       set_socket_options(sock->fd, lp_socket_options());
+       c->state = SMBCLI_REQUEST_ERROR;
+       return c;
+       
+failed:
+       talloc_free(c);
+       return NULL;
+}
 
-       return True;
+/*
+  finish a smbcli_sock_connect_send() operation
+*/
+NTSTATUS smbcli_sock_connect_recv(struct composite_context *c)
+{
+       NTSTATUS status;
+       status = composite_wait(c);
+       talloc_free(c);
+       return status;
 }
 
+/*
+  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)
 
-/****************************************************************************
- mark the socket as dead
-****************************************************************************/
-void smbcli_sock_dead(struct smbcli_socket *sock)
+  sync version of the function
+*/
+NTSTATUS smbcli_sock_connect(struct smbcli_socket *sock, const char *host_addr, int port)
 {
-       if (sock->fd != -1) {
-               close(sock->fd);
-               sock->fd = -1;
+       struct composite_context *c;
+
+       c = smbcli_sock_connect_send(sock, host_addr, port);
+       if (c == NULL) {
+               return NT_STATUS_NO_MEMORY;
        }
+
+       return smbcli_sock_connect_recv(c);
 }
 
+
 /****************************************************************************
- reduce socket reference count - if it becomes zero then close
+ mark the socket as dead
 ****************************************************************************/
-void smbcli_sock_close(struct smbcli_socket *sock)
+void smbcli_sock_dead(struct smbcli_socket *sock)
 {
-       sock->reference_count--;
-       if (sock->reference_count <= 0) {
-               smbcli_sock_dead(sock);
+       if (sock->sock != NULL) {
+               talloc_free(sock->sock);
+               sock->sock = NULL;
        }
 }
 
@@ -112,50 +258,67 @@ void smbcli_sock_close(struct smbcli_socket *sock)
 ****************************************************************************/
 void smbcli_sock_set_options(struct smbcli_socket *sock, const char *options)
 {
-       set_socket_options(sock->fd, options);
+       socket_set_option(sock->sock, options, NULL);
 }
 
 /****************************************************************************
  Write to socket. Return amount written.
 ****************************************************************************/
-ssize_t smbcli_sock_write(struct smbcli_socket *sock, const char *data, size_t len)
+ssize_t smbcli_sock_write(struct smbcli_socket *sock, const uint8_t *data, size_t len)
 {
-       if (sock->fd == -1) {
+       NTSTATUS status;
+       DATA_BLOB blob;
+       size_t nsent;
+
+       if (sock->sock == NULL) {
                errno = EIO;
                return -1;
        }
 
-       return write(sock->fd, data, len);
+       blob.data = discard_const(data);
+       blob.length = len;
+
+       status = socket_send(sock->sock, &blob, &nsent, 0);
+       if (NT_STATUS_IS_ERR(status)) {
+               return -1;
+       }
+
+       return nsent;
 }
 
 
 /****************************************************************************
  Read from socket. return amount read
 ****************************************************************************/
-ssize_t smbcli_sock_read(struct smbcli_socket *sock, char *data, size_t len)
+ssize_t smbcli_sock_read(struct smbcli_socket *sock, uint8_t *data, size_t len)
 {
-       if (sock->fd == -1) {
+       NTSTATUS status;
+       size_t nread;
+
+       if (sock->sock == NULL) {
                errno = EIO;
                return -1;
        }
 
-       return read(sock->fd, data, len);
+       status = socket_recv(sock->sock, data, len, &nread, 0);
+       if (NT_STATUS_IS_ERR(status)) {
+               return -1;
+       }
+
+       return nread;
 }
 
+
 /****************************************************************************
 resolve a hostname and connect 
 ****************************************************************************/
 BOOL smbcli_sock_connect_byname(struct smbcli_socket *sock, const char *host, int port)
 {
-       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;
-
-       if (getenv("LIBSMB_PROG")) {
-               sock->fd = sock_exec(getenv("LIBSMB_PROG"));
-               return sock->fd != -1;
-       }
 
        name = talloc_strdup(sock, host);
 
@@ -165,18 +328,18 @@ 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);
+       nbt_name.name = name;
+       nbt_name.type = name_type;
+       nbt_name.scope = NULL;
+       
+       status = resolve_name(&nbt_name, sock, &address);
+       if (!NT_STATUS_IS_OK(status)) {
                return False;
        }
 
-       ret = smbcli_sock_connect(sock, &ip, port);
+       sock->hostname = name;
 
-       if (ret) {
-               sock->hostname = talloc_steal(sock, name);
-       } else {
-               talloc_free(name);
-       }
+       status = smbcli_sock_connect(sock, address, port);
 
-       return ret;
+       return NT_STATUS_IS_OK(status);
 }