added fragmentation support on receive for dcerpc packets. I have
authorAndrew Tridgell <tridge@samba.org>
Tue, 4 Nov 2003 02:28:08 +0000 (02:28 +0000)
committerAndrew Tridgell <tridge@samba.org>
Tue, 4 Nov 2003 02:28:08 +0000 (02:28 +0000)
successfully used SourceData with 200M of data in rpcecho
(This used to be commit a9aa7954fe84c925bb158af8b73aa71b7ea84e2b)

source4/libcli/raw/rawdcerpc.c
source4/libcli/rpc/dcerpc.c
source4/torture/rpc/echo.c

index 4a5159948d6c69f3c278ee2dcce0d0948846ce11..d548129ab17acf745336a6ce6b89bd9c60a8be07 100644 (file)
@@ -51,6 +51,7 @@ struct cli_request *dcerpc_raw_send(struct dcerpc_pipe *p, DATA_BLOB *blob)
        return req;
 }
 
+
 NTSTATUS dcerpc_raw_recv(struct dcerpc_pipe *p, 
                         struct cli_request *req,
                         TALLOC_CTX *mem_ctx,
@@ -58,14 +59,78 @@ NTSTATUS dcerpc_raw_recv(struct dcerpc_pipe *p,
 {
        struct smb_trans2 trans;
        NTSTATUS status;
+       uint16 frag_length;
+       DATA_BLOB payload;
 
        status = smb_raw_trans_recv(req, mem_ctx, &trans);
-       if (!NT_STATUS_IS_OK(status)) {
+       /* STATUS_BUFFER_OVERFLOW means that there is more data
+          available via SMBreadX */
+       if (!NT_STATUS_IS_OK(status) && 
+           !NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
                return status;
        }
 
+       payload = trans.out.data;
+
+       if (trans.out.data.length < 16 || 
+           !NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
+               goto done;
+       }
+
+       /* we might have recieved a partial fragment, in which case we
+          need to pull the rest of it */
+       frag_length = SVAL(payload.data, 8);
+       if (frag_length <= payload.length) {
+               goto done;
+       }
+
+       /* make sure the payload can hold the whole fragment */
+       payload.data = talloc_realloc(mem_ctx, payload.data, frag_length);
+       if (!payload.data) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* the rest of the data is available via SMBreadX */
+       while (frag_length > payload.length) {
+               uint32 n;
+               union smb_read io;
+
+               n = frag_length - payload.length;
+               if (n > 0xFF00) {
+                       n = 0xFF00;
+               }
+
+               io.generic.level = RAW_READ_READX;
+               io.readx.in.fnum = p->fnum;
+               io.readx.in.mincnt = n;
+               io.readx.in.maxcnt = n;
+               io.readx.in.offset = 0;
+               io.readx.in.remaining = 0;
+               io.readx.out.data = payload.data + payload.length;
+               status = smb_raw_read(p->tree, &io);
+               if (!NT_STATUS_IS_OK(status) &&
+                   !NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
+                       break;
+               }
+               
+               n = io.readx.out.nread;
+               if (n == 0) {
+                       status = NT_STATUS_UNSUCCESSFUL;
+                       break;
+               }
+               
+               payload.length += n;
+
+               /* if the SMBreadX returns NT_STATUS_OK then there
+                  isn't any more data to be read */
+               if (NT_STATUS_IS_OK(status)) {
+                       break;
+               }
+       }
+
+done:
        if (blob) {
-               *blob = trans.out.data;
+               *blob = payload;
        }
 
        return status;
@@ -81,3 +146,75 @@ NTSTATUS dcerpc_raw_packet(struct dcerpc_pipe *p,
        return dcerpc_raw_recv(p, req, mem_ctx, reply_blob);
 }
              
+
+/* 
+   retrieve a secondary pdu from a pipe 
+*/
+NTSTATUS dcerpc_raw_packet_secondary(struct dcerpc_pipe *p, 
+                                    TALLOC_CTX *mem_ctx,
+                                    DATA_BLOB *blob)
+{
+       union smb_read io;
+       uint32 n = 0x2000;
+       uint32 frag_length;
+       NTSTATUS status;
+
+       *blob = data_blob_talloc(mem_ctx, NULL, n);
+       if (!blob->data) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       io.generic.level = RAW_READ_READX;
+       io.readx.in.fnum = p->fnum;
+       io.readx.in.mincnt = n;
+       io.readx.in.maxcnt = n;
+       io.readx.in.offset = 0;
+       io.readx.in.remaining = 0;
+       io.readx.out.data = blob->data;
+
+       status = smb_raw_read(p->tree, &io);
+       if (!NT_STATUS_IS_OK(status) &&
+           !NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
+               return status;
+       }
+
+       blob->length = io.readx.out.nread;
+
+       if (blob->length < 16) {
+               return status;
+       }
+
+       frag_length = SVAL(blob->data, 8);
+       if (frag_length <= blob->length) {
+               return status;
+       }
+
+       blob->data = talloc_realloc(mem_ctx, blob->data, frag_length);
+       if (!blob->data) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       while (frag_length > blob->length &&
+              NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
+
+               n = frag_length - blob->length;
+               if (n > 0xFF00) {
+                       n = 0xFF00;
+               }
+
+               io.readx.in.mincnt = n;
+               io.readx.in.maxcnt = n;
+               io.readx.out.data = blob->data + blob->length;
+               status = smb_raw_read(p->tree, &io);
+
+               if (!NT_STATUS_IS_OK(status) &&
+                   !NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
+                       return status;
+               }
+               
+               n = io.readx.out.nread;
+               blob->length += n;
+       }
+
+       return status;
+}
index b62071875574880a6e1fd1a2f96d940f029a683d..d4f21f969ad13277ea291e9280ba4e8e050ed12f 100644 (file)
@@ -620,7 +620,7 @@ NTSTATUS cli_dcerpc_request(struct dcerpc_pipe *p,
        
        struct dcerpc_packet pkt;
        NTSTATUS status;
-       DATA_BLOB blob_in, blob_out;
+       DATA_BLOB blob_in, blob_out, payload;
 
        init_dcerpc_hdr(&pkt.hdr);
 
@@ -647,11 +647,57 @@ NTSTATUS cli_dcerpc_request(struct dcerpc_pipe *p,
        }
 
        if (pkt.hdr.ptype != DCERPC_PKT_RESPONSE) {
-               status = NT_STATUS_UNSUCCESSFUL;
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       if (!(pkt.hdr.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
+               /* something is badly wrong! */
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       payload = pkt.out.response.stub_data;
+
+       /* continue receiving fragments */
+       while (!(pkt.hdr.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
+               uint32 length;
+
+               status = dcerpc_raw_packet_secondary(p, mem_ctx, &blob_out);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+
+               status = dcerpc_pull(&blob_out, mem_ctx, &pkt);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+
+               if (pkt.hdr.pfc_flags & DCERPC_PFC_FLAG_FIRST) {
+                       /* start of another packet!? */
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+
+               if (pkt.hdr.ptype != DCERPC_PKT_RESPONSE) {
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+
+               length = pkt.out.response.stub_data.length;
+
+               payload.data = talloc_realloc(mem_ctx, 
+                                             payload.data, 
+                                             payload.length + length);
+               if (!payload.data) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               memcpy(payload.data + payload.length,
+                      pkt.out.response.stub_data.data,
+                      length);
+
+               payload.length += length;
        }
 
        if (stub_data_out) {
-               *stub_data_out = pkt.out.response.stub_data;
+               *stub_data_out = payload;
        }
 
        return status;
index 48b004782cffb92c389951975b3dd75798b0c6ad..834eb1f67813e9a66a6ac8452d8ccdec8f8245c1 100644 (file)
@@ -69,7 +69,7 @@ static BOOL test_echodata(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx)
                                         &len_out,
                                         &data_out);
        if (!NT_STATUS_IS_OK(status)) {
-               printf("EchoData(%d) failed\n", len);
+               printf("EchoData(%d) failed - %s\n", len, nt_errstr(status));
                return False;
        }
        printf("EchoData(%d) returned %d bytes\n", len, len_out);
@@ -92,7 +92,7 @@ static BOOL test_sourcedata(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx)
        int i;
        NTSTATUS status;
        char *data_out;
-       int len = 100;
+       int len = 200000;
        int len_out;
 
        printf("\nTesting SourceData\n");
@@ -102,13 +102,14 @@ static BOOL test_sourcedata(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx)
                                           &len_out,
                                           &data_out);
        if (!NT_STATUS_IS_OK(status)) {
-               printf("SourceData(%d) failed\n", len);
+               printf("SourceData(%d) failed - %s\n", len, nt_errstr(status));
                return False;
        }
        printf("SourceData(%d) returned %d bytes\n", len, len_out);
 
        for (i=0;i<len;i++) {
-               if (data_out[i] != (i & 0xFF)) {
+               unsigned char *v = (unsigned char *)data_out;
+               if (v[i] != (i & 0xFF)) {
                        printf("bad data 0x%x at %d\n", data_out[i], i);
                        return False;
                }
@@ -138,7 +139,7 @@ static BOOL test_sinkdata(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx)
                                         len,
                                         data_in);
        if (!NT_STATUS_IS_OK(status)) {
-               printf("SinkData(%d) failed\n", len);
+               printf("SinkData(%d) failed - %s\n", len, nt_errstr(status));
                return False;
        }
 
@@ -159,6 +160,11 @@ BOOL torture_rpc_echo(int dummy)
                return False;
        }
 
+       if (!test_sourcedata(p, mem_ctx)) {
+               ret = False;
+       }
+       return ret;
+
        if (!test_addone(p, mem_ctx)) {
                ret = False;
        }