r4777: added a smb_composite_sesssetup() async composite function. This
[gd/samba-autobuild/.git] / source4 / libcli / raw / clitransport.c
index 2d29ba371f1169c010f0a64ce7670591d87a0cd1..55a7e25f723d538dd5225146e76c58fe9c41ca42 100644 (file)
@@ -1,7 +1,8 @@
 /* 
    Unix SMB/CIFS implementation.
    SMB client transport 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 "libcli/raw/libcliraw.h"
+#include "system/time.h"
+#include "dlinklist.h"
+#include "events.h"
+
+
+static void smbcli_transport_process_recv(struct smbcli_transport *transport);
+static void smbcli_transport_process_send(struct smbcli_transport *transport);
 
 /*
   an event has happened on the socket
 */
 static void smbcli_transport_event_handler(struct event_context *ev, struct fd_event *fde, 
-                                       time_t t, uint16_t flags)
+                                          struct timeval t, uint16_t flags)
 {
        struct smbcli_transport *transport = fde->private;
 
-       smbcli_transport_process(transport);
+       if (flags & EVENT_FD_READ) {
+               smbcli_transport_process_recv(transport);
+       }
+       if (flags & EVENT_FD_WRITE) {
+               smbcli_transport_process_send(transport);
+       }
+}
+
+/*
+  destroy a transport
+ */
+static int transport_destructor(void *ptr)
+{
+       struct smbcli_transport *transport = ptr;
+
+       smbcli_transport_dead(transport);
+       event_remove_fd(transport->socket->event.ctx, transport->socket->event.fde);
+       event_remove_timed(transport->socket->event.ctx, transport->socket->event.te);
+       return 0;
 }
 
 /*
@@ -38,57 +65,35 @@ static void smbcli_transport_event_handler(struct event_context *ev, struct fd_e
 struct smbcli_transport *smbcli_transport_init(struct smbcli_socket *sock)
 {
        struct smbcli_transport *transport;
-       struct fd_event fde;
 
-       transport = talloc_named(NULL, sizeof(*transport), "smbcli_transport");
+       transport = talloc_p(sock, struct smbcli_transport);
        if (!transport) return NULL;
 
        ZERO_STRUCTP(transport);
 
-       transport->event.ctx = event_context_init();
-       if (transport->event.ctx == NULL) {
-               talloc_free(transport);
-               return NULL;
-       }
-
-       transport->socket = sock;
+       transport->socket = talloc_reference(transport, sock);
        transport->negotiate.protocol = PROTOCOL_NT1;
        transport->options.use_spnego = lp_use_spnego();
-       transport->negotiate.max_xmit = ~0;
+       transport->options.max_xmit = lp_max_xmit();
+       transport->options.max_mux = lp_maxmux();
+
+       transport->negotiate.max_xmit = transport->options.max_xmit;
        
        smbcli_init_signing(transport);
 
-       transport->socket->reference_count++;
-
        ZERO_STRUCT(transport->called);
 
-       fde.fd = sock->fd;
-       fde.flags = EVENT_FD_READ;
-       fde.handler = smbcli_transport_event_handler;
-       fde.private = transport;
-       fde.ref_count = 1;
+       /* take over event handling from the socket layer - it only
+          handles events up until we are connected */
+       transport->socket->event.fde->handler = smbcli_transport_event_handler;
+       transport->socket->event.fde->private = transport;
+       transport->socket->event.fde->flags = EVENT_FD_READ;
 
-       transport->event.fde = event_add_fd(transport->event.ctx, &fde);
+       talloc_set_destructor(transport, transport_destructor);
 
        return transport;
 }
 
-/*
-  decrease reference count on a transport, and destroy if it becomes
-  zero
-*/
-void smbcli_transport_close(struct smbcli_transport *transport)
-{
-       transport->reference_count--;
-       if (transport->reference_count <= 0) {
-               smbcli_sock_close(transport->socket);
-               event_remove_fd(transport->event.ctx, transport->event.fde);
-               event_remove_timed(transport->event.ctx, transport->event.te);
-               event_context_destroy(transport->event.ctx);
-               talloc_free(transport);
-       }
-}
-
 /*
   mark the transport as dead
 */
@@ -125,7 +130,7 @@ void smbcli_transport_dead(struct smbcli_transport *transport)
 */
 static void smbcli_transport_write_enable(struct smbcli_transport *transport)
 {
-       transport->event.fde->flags |= EVENT_FD_WRITE;
+       transport->socket->event.fde->flags |= EVENT_FD_WRITE;
 }
 
 /*
@@ -133,17 +138,17 @@ static void smbcli_transport_write_enable(struct smbcli_transport *transport)
 */
 static void smbcli_transport_write_disable(struct smbcli_transport *transport)
 {
-       transport->event.fde->flags &= ~EVENT_FD_WRITE;
+       transport->socket->event.fde->flags &= ~EVENT_FD_WRITE;
 }
 
-/****************************************************************************
-send a session request (if appropriate)
-****************************************************************************/
-BOOL smbcli_transport_connect(struct smbcli_transport *transport,
-                          struct nmb_name *calling, 
-                          struct nmb_name *called)
+/*
+  send a session request
+*/
+struct smbcli_request *smbcli_transport_connect_send(struct smbcli_transport *transport,
+                                                    struct nmb_name *calling, 
+                                                    struct nmb_name *called)
 {
-       char *p;
+       uint8_t *p;
        int len = NBT_HDR_SIZE;
        struct smbcli_request *req;
 
@@ -151,45 +156,103 @@ BOOL smbcli_transport_connect(struct smbcli_transport *transport,
                transport->called = *called;
        }
 
-       /* 445 doesn't have session request */
-       if (transport->socket->port == 445) {
-               return True;
-       }
-
        /* allocate output buffer */
-       req = smbcli_request_setup_nonsmb(transport, NBT_HDR_SIZE + 2*nbt_mangled_name_len());
+       req = smbcli_request_setup_nonsmb(transport, 
+                                         NBT_HDR_SIZE + 2*nbt_mangled_name_len());
+       if (req == NULL) return NULL;
 
        /* put in the destination name */
        p = req->out.buffer + NBT_HDR_SIZE;
-       name_mangle(called->name, p, called->name_type);
-       len += name_len(p);
+       name_mangle(called->name, (char *)p, called->name_type);
+       len += name_len((char *)p);
 
        /* and my name */
        p = req->out.buffer+len;
-       name_mangle(calling->name, p, calling->name_type);
-       len += name_len(p);
+       name_mangle(calling->name, (char *)p, calling->name_type);
+       len += name_len((char *)p);
 
        _smb_setlen(req->out.buffer,len-4);
        SCVAL(req->out.buffer,0,0x81);
 
-       if (!smbcli_request_send(req) ||
-           !smbcli_request_receive(req)) {
+       if (!smbcli_request_send(req)) {
                smbcli_request_destroy(req);
-               return False;
+               return NULL;
        }
-       
-       if (CVAL(req->in.buffer,0) != 0x82) {
-               transport->error.etype = ETYPE_NBT;
-               transport->error.e.nbt_error = CVAL(req->in.buffer,4);
+
+       return req;
+}
+
+/*
+  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()
+*/
+NTSTATUS smbcli_transport_connect_recv(struct smbcli_request *req)
+{
+       NTSTATUS status;
+
+       if (!smbcli_request_receive(req)) {
                smbcli_request_destroy(req);
-               return False;
+               return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+       }
+
+       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;
        }
 
        smbcli_request_destroy(req);
-       return True;
+       return status;
 }
 
 
+/*
+  send a session request (if needed)
+*/
+BOOL smbcli_transport_connect(struct smbcli_transport *transport,
+                             struct nmb_name *calling, 
+                             struct nmb_name *called)
+{
+       struct smbcli_request *req;
+       NTSTATUS status;
+
+       if (transport->socket->port == 445) {
+               return True;
+       }
+
+       req = smbcli_transport_connect_send(transport, 
+                                           calling, called);
+       status = smbcli_transport_connect_recv(req);
+       return NT_STATUS_IS_OK(status);
+}
+
 /****************************************************************************
 get next mid in sequence
 ****************************************************************************/
@@ -220,35 +283,35 @@ again:
 }
 
 static void idle_handler(struct event_context *ev, 
-                        struct timed_event *te, time_t t)
+                        struct timed_event *te, struct timeval t)
 {
        struct smbcli_transport *transport = te->private;
-       te->next_event = t + transport->idle.period;
+       te->next_event = timeval_add(&te->next_event, 0, transport->idle.period);
        transport->idle.func(transport, transport->idle.private);
 }
 
 /*
   setup the idle handler for a transport
-  the period is in seconds
+  the period is in microseconds
 */
 void smbcli_transport_idle_handler(struct smbcli_transport *transport, 
-                               void (*idle_func)(struct smbcli_transport *, void *),
-                               uint_t period,
-                               void *private)
+                                  void (*idle_func)(struct smbcli_transport *, void *),
+                                  uint64_t period,
+                                  void *private)
 {
        struct timed_event te;
        transport->idle.func = idle_func;
        transport->idle.private = private;
        transport->idle.period = period;
 
-       if (transport->event.te != NULL) {
-               event_remove_timed(transport->event.ctx, transport->event.te);
+       if (transport->socket->event.te != NULL) {
+               event_remove_timed(transport->socket->event.ctx, transport->socket->event.te);
        }
 
-       te.next_event = time(NULL) + period;
+       te.next_event = timeval_current_ofs(0, period);
        te.handler = idle_handler;
        te.private = transport;
-       transport->event.te = event_add_timed(transport->event.ctx, &te);
+       transport->socket->event.te = event_add_timed(transport->socket->event.ctx, &te);
 }
 
 /*
@@ -265,6 +328,7 @@ static void smbcli_transport_process_send(struct smbcli_transport *transport)
                                return;
                        }
                        smbcli_transport_dead(transport);
+                       return;
                }
                req->out.buffer += ret;
                req->out.size -= ret;
@@ -340,7 +404,8 @@ static void smbcli_transport_finish_recv(struct smbcli_transport *transport)
        }
 
        if (!req) {
-               DEBUG(1,("Discarding unmatched reply with mid %d\n", mid));
+               DEBUG(1,("Discarding unmatched reply with mid %d op %d\n", 
+                        mid, CVAL(hdr, HDR_COM)));
                goto error;
        }
 
@@ -401,6 +466,7 @@ static void smbcli_transport_finish_recv(struct smbcli_transport *transport)
                transport->error.etype = ETYPE_SOCKET;
                transport->error.e.socket_error = SOCKET_READ_BAD_SIG;
                req->state = SMBCLI_REQUEST_ERROR;
+               req->status = NT_STATUS_ACCESS_DENIED;
                goto error;
        };
 
@@ -436,14 +502,7 @@ static void smbcli_transport_process_recv(struct smbcli_transport *transport)
                                    transport->recv_buffer.header + 
                                    transport->recv_buffer.received,
                                    NBT_HDR_SIZE - transport->recv_buffer.received);
-               if (ret == 0) {
-                       smbcli_transport_dead(transport);
-                       return;
-               }
                if (ret == -1) {
-                       if (errno == EINTR || errno == EAGAIN) {
-                               return;
-                       }
                        smbcli_transport_dead(transport);
                        return;
                }
@@ -453,8 +512,8 @@ static void smbcli_transport_process_recv(struct smbcli_transport *transport)
                if (transport->recv_buffer.received == NBT_HDR_SIZE) {
                        /* we've got a full header */
                        transport->recv_buffer.req_size = smb_len(transport->recv_buffer.header) + NBT_HDR_SIZE;
-                       transport->recv_buffer.buffer = talloc(transport,
-                                                              NBT_HDR_SIZE+transport->recv_buffer.req_size);
+                       transport->recv_buffer.buffer = talloc_size(transport,
+                                                                   NBT_HDR_SIZE+transport->recv_buffer.req_size);
                        if (transport->recv_buffer.buffer == NULL) {
                                smbcli_transport_dead(transport);
                                return;
@@ -471,9 +530,6 @@ static void smbcli_transport_process_recv(struct smbcli_transport *transport)
                                    transport->recv_buffer.req_size - 
                                    transport->recv_buffer.received);
                if (ret == -1) {
-                       if (errno == EINTR || errno == EAGAIN) {
-                               return;
-                       }
                        smbcli_transport_dead(transport);
                        return;
                }
@@ -494,7 +550,7 @@ BOOL smbcli_transport_process(struct smbcli_transport *transport)
 {
        smbcli_transport_process_send(transport);
        smbcli_transport_process_recv(transport);
-       if (transport->socket->fd == -1) {
+       if (transport->socket->sock == NULL) {
                return False;
        }
        return True;
@@ -508,7 +564,7 @@ BOOL smbcli_transport_process(struct smbcli_transport *transport)
 void smbcli_transport_send(struct smbcli_request *req)
 {
        /* check if the transport is dead */
-       if (req->transport->socket->fd == -1) {
+       if (req->transport->socket->sock == NULL) {
                req->state = SMBCLI_REQUEST_ERROR;
                req->status = NT_STATUS_NET_WRITE_FAULT;
                return;