Add an assert to track if we try to use an rpc_context after it has been destroyed
[deb_libnfs.git] / lib / pdu.c
index d7426ced53c82a50748b243d130b09ad2c1dab06..abb19f23b11b1df49020b28c7b4c2a8b8c9edaf3 100644 (file)
--- a/lib/pdu.c
+++ b/lib/pdu.c
@@ -25,6 +25,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <assert.h>
 #include <errno.h>
 #include <rpc/rpc.h>
 #include <rpc/xdr.h>
@@ -39,9 +40,7 @@ struct rpc_pdu *rpc_allocate_pdu(struct rpc_context *rpc, int program, int versi
        struct rpc_pdu *pdu;
        struct rpc_msg msg;
 
-       if (rpc == NULL) {
-               return NULL;
-       }
+       assert(rpc->magic == RPC_CONTEXT_MAGIC);
 
        pdu = malloc(sizeof(struct rpc_pdu));
        if (pdu == NULL) {
@@ -80,8 +79,10 @@ struct rpc_pdu *rpc_allocate_pdu(struct rpc_context *rpc, int program, int versi
        return pdu;
 }
 
-void rpc_free_pdu(struct rpc_context *rpc _U_, struct rpc_pdu *pdu)
+void rpc_free_pdu(struct rpc_context *rpc, struct rpc_pdu *pdu)
 {
+       assert(rpc->magic == RPC_CONTEXT_MAGIC);
+
        if (pdu->outdata.data != NULL) {
                free(pdu->outdata.data);
                pdu->outdata.data = NULL;
@@ -103,6 +104,8 @@ int rpc_queue_pdu(struct rpc_context *rpc, struct rpc_pdu *pdu)
 {
        int size, recordmarker;
 
+       assert(rpc->magic == RPC_CONTEXT_MAGIC);
+
        size = xdr_getpos(&pdu->xdr);
 
        /* for udp we dont queue, we just send it straight away */
@@ -141,11 +144,6 @@ int rpc_get_pdu_size(char *buf)
 
        size = ntohl(*(uint32_t *)buf);
 
-       if ((size & 0x80000000) == 0) {
-               /* cant handle oncrpc fragments */
-               return -1;
-       }
-
        return (size & 0x7fffffff) + 4;
 }
 
@@ -153,6 +151,8 @@ static int rpc_process_reply(struct rpc_context *rpc, struct rpc_pdu *pdu, XDR *
 {
        struct rpc_msg msg;
 
+       assert(rpc->magic == RPC_CONTEXT_MAGIC);
+
        memset(&msg, 0, sizeof(struct rpc_msg));
        msg.acpted_rply.ar_verf = _null_auth;
        if (pdu->xdr_decode_bufsize > 0) {
@@ -214,8 +214,11 @@ int rpc_process_pdu(struct rpc_context *rpc, char *buf, int size)
 {
        struct rpc_pdu *pdu;
        XDR xdr;
-       int pos, recordmarker;
+       int pos, recordmarker = 0;
        unsigned int xid;
+       char *reasbuf = NULL;
+
+       assert(rpc->magic == RPC_CONTEXT_MAGIC);
 
        memset(&xdr, 0, sizeof(XDR));
 
@@ -226,11 +229,50 @@ int rpc_process_pdu(struct rpc_context *rpc, char *buf, int size)
                        xdr_destroy(&xdr);
                        return -1;
                }
+               if (!(recordmarker&0x80000000)) {
+                       xdr_destroy(&xdr);
+                       if (rpc_add_fragment(rpc, buf+4, size-4) != 0) {
+                               rpc_set_error(rpc, "Failed to queue fragment for reassembly.");
+                               return -1;
+                       }
+                       return 0;
+               }
        }
+
+       /* reassembly */
+       if (recordmarker != 0 && rpc->fragments != NULL) {
+               struct rpc_fragment *fragment;
+               uint64_t total = size - 4;
+               char *ptr;
+
+               xdr_destroy(&xdr);
+               for (fragment = rpc->fragments; fragment; fragment = fragment->next) {
+                       total += fragment->size;
+               }
+
+               reasbuf = malloc(total);
+               if (reasbuf == NULL) {
+                       rpc_set_error(rpc, "Failed to reassemble PDU");
+                       rpc_free_all_fragments(rpc);
+                       return -1;
+               }
+               ptr = reasbuf;
+               for (fragment = rpc->fragments; fragment; fragment = fragment->next) {
+                       memcpy(ptr, fragment->data, fragment->size);
+                       ptr += fragment->size;
+               }
+               memcpy(ptr, buf + 4, size - 4);
+               xdrmem_create(&xdr, reasbuf, total, XDR_DECODE);
+               rpc_free_all_fragments(rpc);
+       }
+
        pos = xdr_getpos(&xdr);
        if (xdr_int(&xdr, (int *)&xid) == 0) {
                rpc_set_error(rpc, "xdr_int reading xid failed");
                xdr_destroy(&xdr);
+               if (reasbuf != NULL) {
+                       free(reasbuf);
+               }
                return -1;
        }
        xdr_setpos(&xdr, pos);
@@ -249,10 +291,16 @@ int rpc_process_pdu(struct rpc_context *rpc, char *buf, int size)
                if (rpc->is_udp == 0 || rpc->is_broadcast == 0) {
                        rpc_free_pdu(rpc, pdu);
                }
+               if (reasbuf != NULL) {
+                       free(reasbuf);
+               }
                return 0;
        }
        rpc_set_error(rpc, "No matching pdu found for xid:%d", xid);
        xdr_destroy(&xdr);
+       if (reasbuf != NULL) {
+               free(reasbuf);
+       }
        return -1;
 }