s4:libcli/raw: copy smbcli_transport_connect_* to clisocket.c
[kai/samba.git] / source4 / libcli / raw / clisocket.c
index f0e05085c446862ea43e6fe62a1b2c82f08a36dd..70a83a493f03c8d317e885766f3282b872438e04 100644 (file)
@@ -1,12 +1,14 @@
 /* 
    Unix SMB/CIFS implementation.
+
    SMB client socket context management functions
-   Copyright (C) Andrew Tridgell 1994-1998
+
+   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"
+#include "libcli/raw/raw_proto.h"
+
+/*
+  send a session request
+*/
+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;
+
+       status = nbt_name_to_blob(tmp_ctx, &called_blob, called);
+       if (!NT_STATUS_IS_OK(status)) goto failed;
+
+       /* allocate output buffer */
+       req = smbcli_request_setup_nonsmb(transport, 
+                                         NBT_HDR_SIZE + 
+                                         calling_blob.length + called_blob.length);
+       if (req == NULL) goto failed;
+
+       /* put in the destination name */
+       p = req->out.buffer + NBT_HDR_SIZE;
+       memcpy(p, called_blob.data, called_blob.length);
+       p += called_blob.length;
 
+       memcpy(p, calling_blob.data, calling_blob.length);
+       p += calling_blob.length;
+
+       _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_free(tmp_ctx);
+       return req;
+
+failed:
+       talloc_free(tmp_ctx);
+       return NULL;
+}
 
 /*
-  create a cli_socket context
+  map a session request error to a NTSTATUS
+ */
+static NTSTATUS map_session_refused_error(uint8_t error)
+{
+       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;
+}
+
+
+/*
+  finish a smbcli_transport_connect()
 */
-struct cli_socket *cli_sock_init(void)
+NTSTATUS smbcli_transport_connect_recv(struct smbcli_request *req)
 {
-       struct cli_socket *sock;
-       TALLOC_CTX *mem_ctx;
+       NTSTATUS status;
 
-       mem_ctx = talloc_init("cli_socket");
-       if (!mem_ctx) return NULL;
+       if (!smbcli_request_receive(req)) {
+               smbcli_request_destroy(req);
+               return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+       }
 
-       sock = talloc_zero(mem_ctx, sizeof(*sock));
-       if (!sock) {
-               talloc_destroy(mem_ctx);
-               return 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;
        }
 
-       sock->mem_ctx = mem_ctx;
-       sock->fd = -1;
-       sock->port = 445;
-       /* 20 second default timeout */
-       sock->timeout = 20000;
+       smbcli_request_destroy(req);
+       return status;
+}
+
+
+/*
+  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;
+
+       if (transport->socket->port == 445) {
+               return true;
+       }
 
-       return sock;
+       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 cli_socket context to an IP/port pair
+  connect a smbcli_socket context to an IP/port pair
   if port is 0 then choose 445 then 139
 */
-BOOL cli_sock_connect(struct cli_socket *sock, struct in_addr *ip, 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)
 {
-       if (getenv("LIBSMB_PROG")) {
-               sock->fd = sock_exec(getenv("LIBSMB_PROG"));
-               return sock->fd != -1;
+       struct composite_context *result, *ctx;
+       struct sock_connect_state *state;
+       int i;
+
+       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);
 
-       if (port == 0) {
-               return cli_sock_connect(sock, ip, 445) ||
-                       cli_sock_connect(sock, ip, 139);
+       if (!host_addr) {
+               host_addr = host_name;
        }
 
-       sock->dest_ip = *ip;
-       sock->port = port;
-       sock->fd = open_socket_out(SOCK_STREAM,
-                                  &sock->dest_ip,
-                                  sock->port, 
-                                  LONG_CONNECT_TIMEOUT);
-       return (sock->fd != -1);
+       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(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;
 
-/****************************************************************************
- reduce socket reference count - if it becomes zero then close
-****************************************************************************/
-void cli_sock_close(struct cli_socket *sock)
+       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 composite_context *c,
+                                 TALLOC_CTX *mem_ctx,
+                                 struct smbcli_socket **result)
 {
-       sock->reference_count--;
-       if (sock->reference_count <= 0 && sock->fd != -1) {
-               close(sock->fd);
-               sock->fd = -1;
+       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 cli_sock_set_options(struct cli_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, 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)
 {
-       set_socket_options(sock->fd, options);
+       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);
 }
 
+
 /****************************************************************************
- Write to socket. Return amount written.
+ mark the socket as dead
 ****************************************************************************/
-ssize_t cli_sock_write(struct cli_socket *sock, const char *data, size_t len)
+_PUBLIC_ void smbcli_sock_dead(struct smbcli_socket *sock)
 {
-       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 cli_sock_read(struct cli_socket *sock, char *data, size_t len)
+void smbcli_sock_set_options(struct smbcli_socket *sock, const char *options)
 {
-       return read_data(sock->fd, data, len);
+       socket_set_option(sock->sock, options, NULL);
 }
 
 /****************************************************************************
 resolve a hostname and connect 
 ****************************************************************************/
-BOOL cli_sock_connect_byname(struct cli_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 = 0x20;
-       struct in_addr ip;
-       TALLOC_CTX *mem_ctx;
+       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;
 
-       if (getenv("LIBSMB_PROG")) {
-               sock->fd = sock_exec(getenv("LIBSMB_PROG"));
-               return sock->fd != -1;
+       if (event_ctx == NULL) {
+               DEBUG(0, ("Invalid NULL event context passed in as parameter\n"));
+               return NULL;
        }
 
-       mem_ctx = talloc_init("cli_sock_connect_byname");
-       if (!mem_ctx) return False;
+       if (tmp_ctx == NULL) {
+               DEBUG(0, ("talloc_new failed\n"));
+               return NULL;
+       }
 
-       name = talloc_strdup(mem_ctx, host);
+       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, '#'))) {
@@ -137,12 +350,26 @@ BOOL cli_sock_connect_byname(struct cli_socket *sock, const char *host, int port
                *p = 0;
        }
 
-       if (!resolve_name(mem_ctx, name, &ip, name_type)) {
-               talloc_destroy(mem_ctx);
-               return False;
+       make_nbt_name(&nbt_name, host, name_type);
+       
+       status = resolve_name_ex(resolve_ctx, 0, 0, &nbt_name, tmp_ctx, &address, event_ctx);
+       if (!NT_STATUS_IS_OK(status)) {
+               talloc_free(tmp_ctx);
+               return NULL;
+       }
+
+       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;
        }
 
-       talloc_destroy(mem_ctx);
+       talloc_free(tmp_ctx);
 
-       return cli_sock_connect(sock, &ip, port);
+       return result;
 }