s4:libcli/raw: copy smbcli_transport_connect_* to clisocket.c
[kai/samba.git] / source4 / libcli / raw / clisocket.c
index cbbd6490bdc5f388a44fb41c54e3f4d1e9305ea9..70a83a493f03c8d317e885766f3282b872438e04 100644 (file)
@@ -8,7 +8,7 @@
    
    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 "events.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"
+#include "libcli/raw/raw_proto.h"
 
 /*
-  this private structure is used during async connection handling
+  send a session request
 */
-struct clisocket_connect {
-       int port_num;
-       int *iports;
-       struct smbcli_socket *sock;
-       const char *dest_host;
-};
+struct smbcli_request *smbcli_transport_connect_send(struct smbcli_transport *transport,
+                                                    struct nbt_name *calling, 
+                                                    struct nbt_name *called)
+{
+       uint8_t *p;
+       struct smbcli_request *req;
+       DATA_BLOB calling_blob, called_blob;
+       TALLOC_CTX *tmp_ctx = talloc_new(transport);
+       NTSTATUS status;
 
+       status = nbt_name_dup(transport, called, &transport->called);
+       if (!NT_STATUS_IS_OK(status)) goto failed;
+       
+       status = nbt_name_to_blob(tmp_ctx, &calling_blob, calling);
+       if (!NT_STATUS_IS_OK(status)) goto failed;
 
-static int smbcli_sock_destructor(void *ptr)
-{
-       struct smbcli_socket *sock = talloc_get_type(ptr, struct smbcli_socket);
+       status = nbt_name_to_blob(tmp_ctx, &called_blob, called);
+       if (!NT_STATUS_IS_OK(status)) goto failed;
 
-       if (sock->event.fde && sock->event.ctx) {
-               event_remove_fd(sock->event.ctx, sock->event.fde);
-       }
-       return 0;
-}
+       /* allocate output buffer */
+       req = smbcli_request_setup_nonsmb(transport, 
+                                         NBT_HDR_SIZE + 
+                                         calling_blob.length + called_blob.length);
+       if (req == NULL) goto failed;
 
-/*
-  create a smbcli_socket context
-*/
-struct smbcli_socket *smbcli_sock_init(TALLOC_CTX *mem_ctx)
-{
-       struct smbcli_socket *sock;
+       /* put in the destination name */
+       p = req->out.buffer + NBT_HDR_SIZE;
+       memcpy(p, called_blob.data, called_blob.length);
+       p += called_blob.length;
 
-       sock = talloc_zero(mem_ctx, struct smbcli_socket);
-       if (!sock) {
-               return NULL;
-       }
+       memcpy(p, calling_blob.data, calling_blob.length);
+       p += calling_blob.length;
 
-       sock->event.ctx = event_context_init(sock);
-       if (sock->event.ctx == NULL) {
-               talloc_free(sock);
-               return NULL;
+       _smb_setlen_nbt(req->out.buffer, PTR_DIFF(p, req->out.buffer) - NBT_HDR_SIZE);
+       SCVAL(req->out.buffer,0,0x81);
+
+       if (!smbcli_request_send(req)) {
+               smbcli_request_destroy(req);
+               goto failed;
        }
 
-       talloc_set_destructor(sock, smbcli_sock_destructor);
+       talloc_free(tmp_ctx);
+       return req;
 
-       return sock;
+failed:
+       talloc_free(tmp_ctx);
+       return NULL;
 }
 
-static NTSTATUS smbcli_sock_connect_one(struct smbcli_socket *sock, 
-                                       const char *hostaddr, int port);
-
 /*
-  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, 
-                                       struct timeval t, uint16_t flags)
+  map a session request error to a NTSTATUS
+ */
+static NTSTATUS map_session_refused_error(uint8_t error)
 {
-       struct smbcli_composite *c = talloc_get_type(fde->private, struct smbcli_composite);
-       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]);
-               if (NT_STATUS_IS_OK(c->status) ||
-                   NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
-                       conn->sock->event.fde->private = c;
-                       return;
-               }
-       }
-
-       c->state = SMBCLI_REQUEST_ERROR;
-       if (c->async.fn) {
-               c->async.fn(c);
+       switch (error) {
+       case 0x80:
+       case 0x81:
+               return NT_STATUS_REMOTE_NOT_LISTENING;
+       case 0x82:
+               return NT_STATUS_RESOURCE_NAME_NOT_FOUND;
+       case 0x83:
+               return NT_STATUS_REMOTE_RESOURCES;
        }
+       return NT_STATUS_UNEXPECTED_IO_ERROR;
 }
 
 
 /*
-  try to connect to the given address/port
+  finish a smbcli_transport_connect()
 */
-static NTSTATUS smbcli_sock_connect_one(struct smbcli_socket *sock, 
-                                       const char *hostaddr, int port)
+NTSTATUS smbcli_transport_connect_recv(struct smbcli_request *req)
 {
-       struct fd_event fde;
        NTSTATUS status;
 
-       if (sock->sock) {
-               talloc_free(sock->sock);
-               sock->sock = NULL;
+       if (!smbcli_request_receive(req)) {
+               smbcli_request_destroy(req);
+               return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
        }
 
-       if (sock->event.fde) {
-               event_remove_fd(sock->event.ctx, sock->event.fde);
-               sock->event.fde = NULL;
+       switch (CVAL(req->in.buffer,0)) {
+       case 0x82:
+               status = NT_STATUS_OK;
+               break;
+       case 0x83:
+               status = map_session_refused_error(CVAL(req->in.buffer,4));
+               break;
+       case 0x84:
+               DEBUG(1,("Warning: session retarget not supported\n"));
+               status = NT_STATUS_NOT_SUPPORTED;
+               break;
+       default:
+               status = NT_STATUS_UNEXPECTED_IO_ERROR;
+               break;
        }
 
-       status = socket_create("ip", SOCKET_TYPE_STREAM, &sock->sock, 0);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
+       smbcli_request_destroy(req);
+       return status;
+}
 
-       talloc_steal(sock, sock->sock);
 
-       /* we initially look for write - see the man page on
-          non-blocking connect */
-       fde.fd = socket_get_fd(sock->sock);
-       fde.flags = EVENT_FD_WRITE;
-       fde.handler = smbcli_sock_connect_handler;
-       fde.private = sock;
+/*
+  send a session request (if needed)
+*/
+bool smbcli_transport_connect(struct smbcli_transport *transport,
+                             struct nbt_name *calling, 
+                             struct nbt_name *called)
+{
+       struct smbcli_request *req;
+       NTSTATUS status;
 
-       sock->event.fde = event_add_fd(sock->event.ctx, &fde);
-       sock->port = port;
-       set_blocking(fde.fd, False);
+       if (transport->socket->port == 445) {
+               return true;
+       }
 
-       return socket_connect(sock->sock, NULL, 0, hostaddr, port, 0);
+       req = smbcli_transport_connect_send(transport, 
+                                           calling, called);
+       status = smbcli_transport_connect_recv(req);
+       return NT_STATUS_IS_OK(status);
 }
-                                       
+
+struct sock_connect_state {
+       struct composite_context *ctx;
+       const char *host_name;
+       int num_ports;
+       uint16_t *ports;
+       const char *socket_options;
+       struct smbcli_socket *result;
+};
 
 /*
   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
 */
-struct smbcli_composite *smbcli_sock_connect_send(struct smbcli_socket *sock, 
-                                                 const char *host_addr, int port)
+
+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,
+                                                  const char **ports,
+                                                  const char *host_name,
+                                                  struct resolve_context *resolve_ctx,
+                                                  struct tevent_context *event_ctx,
+                                                  const char *socket_options)
 {
-       struct smbcli_composite *c;
-       struct clisocket_connect *conn;
+       struct composite_context *result, *ctx;
+       struct sock_connect_state *state;
        int i;
 
-       c = talloc_zero(sock, struct smbcli_composite);
-       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) {
-               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++) {
-                       conn->iports[i] = atoi(ports[i]);
-               }
-               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;
+       result = talloc_zero(mem_ctx, struct composite_context);
+       if (result == NULL) goto failed;
+       result->state = COMPOSITE_STATE_IN_PROGRESS;
+
+       result->event_ctx = event_ctx;
+       if (result->event_ctx == NULL) goto failed;
+
+       state = talloc(result, struct sock_connect_state);
+       if (state == NULL) goto failed;
+       state->ctx = result;
+       result->private_data = state;
+
+       state->host_name = talloc_strdup(state, host_name);
+       if (state->host_name == NULL) goto failed;
+
+       state->num_ports = str_list_length(ports);
+       state->ports = talloc_array(state, uint16_t, state->num_ports);
+       if (state->ports == NULL) goto failed;
+       for (i=0;ports[i];i++) {
+               state->ports[i] = atoi(ports[i]);
        }
+       state->socket_options = talloc_reference(state, socket_options);
 
-       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]);
-               if (NT_STATUS_IS_OK(c->status) ||
-                   NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
-                       sock->event.fde->private = c;
-                       return c;
-               }
+       if (!host_addr) {
+               host_addr = host_name;
        }
 
-       c->state = SMBCLI_REQUEST_ERROR;
-       return c;
-       
+       ctx = socket_connect_multi_send(state, host_addr,
+                                       state->num_ports, state->ports,
+                                       resolve_ctx,
+                                       state->ctx->event_ctx);
+       if (ctx == NULL) goto failed;
+       ctx->async.fn = smbcli_sock_connect_recv_conn;
+       ctx->async.private_data = state;
+       return result;
+
 failed:
-       talloc_free(c);
+       talloc_free(result);
        return NULL;
 }
 
+static void smbcli_sock_connect_recv_conn(struct composite_context *ctx)
+{
+       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, state->socket_options, 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 = state->ctx->event_ctx;
+       if (composite_nomem(state->result->event.ctx, state->ctx)) return;
+
+       composite_done(state->ctx);
+}
+
 /*
   finish a smbcli_sock_connect_send() operation
 */
-NTSTATUS smbcli_sock_connect_recv(struct smbcli_composite *c)
+NTSTATUS smbcli_sock_connect_recv(struct composite_context *c,
+                                 TALLOC_CTX *mem_ctx,
+                                 struct smbcli_socket **result)
 {
-       NTSTATUS status;
-       status = smb_composite_wait(c);
+       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;
 }
@@ -242,28 +275,31 @@ NTSTATUS smbcli_sock_connect_recv(struct smbcli_composite *c)
 
   sync version of the function
 */
-NTSTATUS smbcli_sock_connect(struct smbcli_socket *sock, const char *host_addr, int port)
+NTSTATUS smbcli_sock_connect(TALLOC_CTX *mem_ctx,
+                            const char *host_addr, const char **ports,
+                            const char *host_name,
+                            struct resolve_context *resolve_ctx,
+                            struct tevent_context *event_ctx,
+                                const char *socket_options,
+                            struct smbcli_socket **result)
 {
-       struct smbcli_composite *c;
-
-       c = smbcli_sock_connect_send(sock, host_addr, port);
-       if (c == NULL) {
-               return NT_STATUS_NO_MEMORY;
-       }
-
-       return smbcli_sock_connect_recv(c);
+       struct composite_context *c =
+               smbcli_sock_connect_send(mem_ctx, host_addr, ports, host_name,
+                                        resolve_ctx,
+                                        event_ctx, socket_options);
+       return smbcli_sock_connect_recv(c, mem_ctx, result);
 }
 
 
 /****************************************************************************
  mark the socket as dead
 ****************************************************************************/
-void smbcli_sock_dead(struct smbcli_socket *sock)
+_PUBLIC_ void smbcli_sock_dead(struct smbcli_socket *sock)
 {
-       if (sock->sock != NULL) {
-               talloc_free(sock->sock);
-               sock->sock = NULL;
-       }
+       talloc_free(sock->event.fde);
+       sock->event.fde = NULL;
+       talloc_free(sock->sock);
+       sock->sock = NULL;
 }
 
 /****************************************************************************
@@ -274,66 +310,39 @@ void smbcli_sock_set_options(struct smbcli_socket *sock, const char *options)
        socket_set_option(sock->sock, options, NULL);
 }
 
-/****************************************************************************
- Write to socket. Return amount written.
-****************************************************************************/
-ssize_t smbcli_sock_write(struct smbcli_socket *sock, const uint8_t *data, size_t len)
-{
-       NTSTATUS status;
-       DATA_BLOB blob;
-       size_t nsent;
-
-       if (sock->sock == NULL) {
-               errno = EIO;
-               return -1;
-       }
-
-       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, uint8_t *data, size_t len)
-{
-       NTSTATUS status;
-       size_t nread;
-
-       if (sock->sock == NULL) {
-               errno = EIO;
-               return -1;
-       }
-
-       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)
+_PUBLIC_ struct smbcli_socket *smbcli_sock_connect_byname(const char *host, const char **ports,
+                                                TALLOC_CTX *mem_ctx,
+                                                struct resolve_context *resolve_ctx,
+                                                struct tevent_context *event_ctx,
+                                                const char *socket_options)
 {
        int name_type = NBT_NAME_SERVER;
        const char *address;
        NTSTATUS status;
        struct nbt_name nbt_name;
        char *name, *p;
+       TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+       struct smbcli_socket *result;
 
-       name = talloc_strdup(sock, host);
+       if (event_ctx == NULL) {
+               DEBUG(0, ("Invalid NULL event context passed in as parameter\n"));
+               return NULL;
+       }
+
+       if (tmp_ctx == NULL) {
+               DEBUG(0, ("talloc_new failed\n"));
+               return NULL;
+       }
+
+       name = talloc_strdup(tmp_ctx, host);
+       if (name == NULL) {
+               DEBUG(0, ("talloc_strdup failed\n"));
+               talloc_free(tmp_ctx);
+               return NULL;
+       }
 
        /* allow hostnames of the form NAME#xx and do a netbios lookup */
        if ((p = strchr(name, '#'))) {
@@ -341,18 +350,26 @@ BOOL smbcli_sock_connect_byname(struct smbcli_socket *sock, const char *host, in
                *p = 0;
        }
 
-       nbt_name.name = name;
-       nbt_name.type = name_type;
-       nbt_name.scope = NULL;
+       make_nbt_name(&nbt_name, host, name_type);
        
-       status = resolve_name(&nbt_name, sock, &address);
+       status = resolve_name_ex(resolve_ctx, 0, 0, &nbt_name, tmp_ctx, &address, event_ctx);
        if (!NT_STATUS_IS_OK(status)) {
-               return False;
+               talloc_free(tmp_ctx);
+               return NULL;
        }
 
-       sock->hostname = name;
+       status = smbcli_sock_connect(mem_ctx, address, ports, name, resolve_ctx,
+                                    event_ctx, 
+                                        socket_options, &result);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(9, ("smbcli_sock_connect failed: %s\n",
+                         nt_errstr(status)));
+               talloc_free(tmp_ctx);
+               return NULL;
+       }
 
-       status = smbcli_sock_connect(sock, address, port);
+       talloc_free(tmp_ctx);
 
-       return NT_STATUS_IS_OK(status);
+       return result;
 }