Make rpc_read() match the control flow normally used in Samba
[jra/samba/.git] / source3 / rpc_client / cli_pipe.c
index f0c2f6709efa8a0622fd41fc45737058b4089567..fbd9b45a3a0884542f6b2e22eb3c4821d18714c8 100644 (file)
@@ -176,15 +176,15 @@ static uint32 get_rpc_call_id(void)
  Read from a RPC named pipe
  ********************************************************************/
 static NTSTATUS rpc_read_np(struct cli_state *cli, const char *pipe_name,
-                           int fnum, char *buf, off_t offset, size_t size,
+                           int fnum, char *buf, size_t size,
                            ssize_t *pnum_read)
 {
        ssize_t num_read;
 
-       num_read = cli_read(cli, fnum, buf, offset, size);
+       num_read = cli_read(cli, fnum, buf, 0, size);
 
-       DEBUG(5,("rpc_read_np: num_read = %d, read offset: %u, to read: %u\n",
-               (int)num_read, (unsigned int)offset, (unsigned int)size));
+       DEBUG(5,("rpc_read_np: num_read = %d, to read: %u\n", (int)num_read,
+               (unsigned int)size));
 
        /*
        * A dos error of ERRDOS/ERRmoredata is not an error.
@@ -223,66 +223,71 @@ static NTSTATUS rpc_read_np(struct cli_state *cli, const char *pipe_name,
        return NT_STATUS_OK;
 }
 
+/*
+ * Realloc pdu to have a least "size" bytes
+ */
+
+static bool rpc_grow_buffer(prs_struct *pdu, size_t size)
+{
+       size_t extra_size;
+
+       if (prs_data_size(pdu) >= size) {
+               return true;
+       }
+
+       extra_size = size - prs_data_size(pdu);
+
+       if (!prs_force_grow(pdu, extra_size)) {
+               DEBUG(0, ("rpc_grow_buffer: Failed to grow parse struct by "
+                         "%d bytes.\n", (int)extra_size));
+               return false;
+       }
+
+       DEBUG(5, ("rpc_grow_buffer: grew buffer by %d bytes to %u\n",
+                 (int)extra_size, prs_data_size(pdu)));
+       return true;
+}
+
 
 /*******************************************************************
  Use SMBreadX to get rest of one fragment's worth of rpc data.
- Will expand the current_pdu struct to the correct size.
+ Reads the whole size or give an error message
  ********************************************************************/
 
 static NTSTATUS rpc_read(struct rpc_pipe_client *cli,
                        prs_struct *current_pdu,
-                       uint32 data_to_read,
-                       uint32 *current_pdu_offset)
+                       size_t size,
+                       uint32 current_pdu_offset)
 {
-       size_t size = (size_t)cli->max_recv_frag;
-       uint32 stream_offset = 0;
        ssize_t num_read = 0;
        char *pdata;
-       ssize_t extra_data_size = ((ssize_t)*current_pdu_offset) + ((ssize_t)data_to_read) - (ssize_t)prs_data_size(current_pdu);
-
-       DEBUG(5,("rpc_read: data_to_read: %u current_pdu offset: %u extra_data_size: %d\n",
-               (unsigned int)data_to_read, (unsigned int)*current_pdu_offset, (int)extra_data_size ));
 
-       /*
-        * Grow the buffer if needed to accommodate the data to be read.
-        */
+       DEBUG(5, ("rpc_read: data_to_read: %u current_pdu offset: %d\n",
+                 (unsigned int)size, (unsigned int)current_pdu_offset));
 
-       if (extra_data_size > 0) {
-               if(!prs_force_grow(current_pdu, (uint32)extra_data_size)) {
-                       DEBUG(0,("rpc_read: Failed to grow parse struct by %d bytes.\n", (int)extra_data_size ));
-                       return NT_STATUS_NO_MEMORY;
-               }
-               DEBUG(5,("rpc_read: grew buffer by %d bytes to %u\n", (int)extra_data_size, prs_data_size(current_pdu) ));
-       }
-
-       pdata = prs_data_p(current_pdu) + *current_pdu_offset;
+       pdata = prs_data_p(current_pdu) + current_pdu_offset;
 
-       do {
+       while (num_read < size) {
+               ssize_t thistime = 0;
                NTSTATUS status;
 
-               /* read data using SMBreadX */
-               if (size > (size_t)data_to_read) {
-                       size = (size_t)data_to_read;
-               }
-
                switch (cli->transport_type) {
                case NCACN_NP:
                        status = rpc_read_np(cli->trans.np.cli,
                                             cli->trans.np.pipe_name,
-                                            cli->trans.np.fnum, pdata,
-                                            (off_t)stream_offset, size,
-                                            &num_read);
+                                            cli->trans.np.fnum,
+                                            pdata + num_read,
+                                            size - num_read, &thistime);
                        break;
                case NCACN_IP_TCP:
                case NCACN_UNIX_STREAM:
                        status = NT_STATUS_OK;
-                       num_read = sys_read(cli->trans.sock.fd, pdata, size);
-                       if (num_read == -1) {
+                       thistime = sys_read(cli->trans.sock.fd,
+                                           pdata + num_read,
+                                           size - num_read);
+                       if (thistime == -1) {
                                status = map_nt_error_from_unix(errno);
                        }
-                       if (num_read == 0) {
-                               status = NT_STATUS_END_OF_FILE;
-                       }
                        break;
                default:
                        DEBUG(0, ("unknown transport type %d\n",
@@ -290,17 +295,18 @@ static NTSTATUS rpc_read(struct rpc_pipe_client *cli,
                        return NT_STATUS_INTERNAL_ERROR;
                }
 
-               data_to_read -= num_read;
-               stream_offset += num_read;
-               pdata += num_read;
+               if (thistime == 0) {
+                       status = NT_STATUS_END_OF_FILE;
+               }
 
-       } while (num_read > 0 && data_to_read > 0);
-       /* && err == (0x80000000 | STATUS_BUFFER_OVERFLOW)); */
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+
+               num_read += thistime;
+
+       }
 
-       /*
-        * Update the current offset into current_pdu by the amount read.
-        */
-       *current_pdu_offset += stream_offset;
        return NT_STATUS_OK;
 }
 
@@ -316,11 +322,16 @@ static NTSTATUS cli_pipe_get_current_pdu(struct rpc_pipe_client *cli, RPC_HDR *p
 
        /* Ensure we have at least RPC_HEADER_LEN worth of data to parse. */
        if (current_pdu_len < RPC_HEADER_LEN) {
-               /* rpc_read expands the current_pdu struct as neccessary. */
-               ret = rpc_read(cli, current_pdu, RPC_HEADER_LEN - current_pdu_len, &current_pdu_len);
+               if (!rpc_grow_buffer(current_pdu, RPC_HEADER_LEN)) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+               ret = rpc_read(cli, current_pdu,
+                              RPC_HEADER_LEN - current_pdu_len,
+                              current_pdu_len);
                if (!NT_STATUS_IS_OK(ret)) {
                        return ret;
                }
+               current_pdu_len = RPC_HEADER_LEN;
        }
 
        /* This next call sets the endian bit correctly in current_pdu. */
@@ -330,19 +341,26 @@ static NTSTATUS cli_pipe_get_current_pdu(struct rpc_pipe_client *cli, RPC_HDR *p
                return NT_STATUS_BUFFER_TOO_SMALL;
        }
 
+       if (prhdr->frag_len > cli->max_recv_frag) {
+               DEBUG(0, ("cli_pipe_get_current_pdu: Server sent fraglen %d,"
+                         " we only allow %d\n", (int)prhdr->frag_len,
+                         (int)cli->max_recv_frag));
+               return NT_STATUS_BUFFER_TOO_SMALL;
+       }
+
        /* Ensure we have frag_len bytes of data. */
        if (current_pdu_len < prhdr->frag_len) {
-               /* rpc_read expands the current_pdu struct as neccessary. */
-               ret = rpc_read(cli, current_pdu, (uint32)prhdr->frag_len - current_pdu_len, &current_pdu_len);
+               if (!rpc_grow_buffer(current_pdu, prhdr->frag_len)) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+               ret = rpc_read(cli, current_pdu,
+                              (uint32)prhdr->frag_len - current_pdu_len,
+                              current_pdu_len);
                if (!NT_STATUS_IS_OK(ret)) {
                        return ret;
                }
        }
 
-       if (current_pdu_len < prhdr->frag_len) {
-               return NT_STATUS_BUFFER_TOO_SMALL;
-       }
-
        return NT_STATUS_OK;
 }
 
@@ -833,6 +851,32 @@ static NTSTATUS cli_pipe_reset_current_pdu(struct rpc_pipe_client *cli, RPC_HDR
        return NT_STATUS_OK;
 }
 
+/****************************************************************************
+ Call a remote api on an arbitrary pipe.  takes param, data and setup buffers.
+****************************************************************************/
+
+static bool cli_api_pipe(struct cli_state *cli, const char *pipe_name,
+                        uint16 *setup, uint32 setup_count,
+                        uint32 max_setup_count,
+                        char *params, uint32 param_count,
+                        uint32 max_param_count,
+                        char *data, uint32 data_count,
+                        uint32 max_data_count,
+                        char **rparam, uint32 *rparam_count,
+                        char **rdata, uint32 *rdata_count)
+{
+       cli_send_trans(cli, SMBtrans,
+                 pipe_name,
+                 0,0,                         /* fid, flags */
+                 setup, setup_count, max_setup_count,
+                 params, param_count, max_param_count,
+                 data, data_count, max_data_count);
+
+       return (cli_receive_trans(cli, SMBtrans,
+                            rparam, (unsigned int *)rparam_count,
+                            rdata, (unsigned int *)rdata_count));
+}
+
 /****************************************************************************
  Send data on an rpc pipe via trans. The prs_struct data must be the last
  pdu fragment of an NDR data stream.
@@ -2618,9 +2662,8 @@ static NTSTATUS rpc_pipe_open_tcp_port(TALLOC_CTX *mem_ctx, const char *host,
                goto fail;
        }
 
-       result->trans.sock.fd = open_socket_out(SOCK_STREAM, &addr, port, 60);
-       if (result->trans.sock.fd == -1) {
-               status = map_nt_error_from_unix(errno);
+       status = open_socket_out(&addr, port, 60, &result->trans.sock.fd);
+       if (!NT_STATUS_IS_OK(status)) {
                goto fail;
        }
 
@@ -2898,6 +2941,9 @@ static NTSTATUS rpc_pipe_open_np(struct cli_state *cli,
        result->srv_name_slash = talloc_asprintf_strupper_m(
                result, "\\\\%s", result->desthost);
 
+       result->max_xmit_frag = RPC_MAX_PDU_FRAG_LEN;
+       result->max_recv_frag = RPC_MAX_PDU_FRAG_LEN;
+
        if ((result->desthost == NULL) || (result->srv_name_slash == NULL)) {
                TALLOC_FREE(result);
                return NT_STATUS_NO_MEMORY;
@@ -2979,6 +3025,9 @@ NTSTATUS cli_rpc_pipe_open_noauth(struct cli_state *cli,
 
        auth->user_name = talloc_strdup(auth, cli->user_name);
        auth->domain = talloc_strdup(auth, cli->domain);
+       auth->user_session_key = data_blob_talloc(auth,
+               cli->user_session_key.data,
+               cli->user_session_key.length);
 
        if ((auth->user_name == NULL) || (auth->domain == NULL)) {
                TALLOC_FREE(result);
@@ -3443,6 +3492,10 @@ NTSTATUS cli_get_session_key(TALLOC_CTX *mem_ctx,
                                cli->auth->a_u.kerberos_auth->session_key.length);
                        break;
                case PIPE_AUTH_TYPE_NONE:
+                       *session_key = data_blob_talloc(mem_ctx,
+                               cli->auth->user_session_key.data,
+                               cli->auth->user_session_key.length);
+                       break;
                default:
                        return NT_STATUS_NO_USER_SESSION_KEY;
        }