1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  *
  25  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
  26  */
  27 
  28 /*
  29  * NDR heap management. The heap is used for temporary storage by
  30  * both the client and server side library routines.  In order to
  31  * support the different requirements of the various RPCs, the heap
  32  * can grow dynamically if required.  We start with a single block
  33  * and perform sub-allocations from it.  If an RPC requires more space
  34  * we will continue to add it a block at a time.  This means that we
  35  * don't hog lots of memory on every call to support the few times
  36  * that we actually need a lot heap space.
  37  *
  38  * Note that there is no individual free function.  Once space has been
  39  * allocated, it remains allocated until the heap is destroyed.  This
  40  * shouldn't be an issue because the heap is being filled with data to
  41  * be marshalled or unmarshalled and we need it all to be there until
  42  * the point that the entire heap is no longer required.
  43  */
  44 
  45 #include <sys/errno.h>
  46 #include <stdlib.h>
  47 #include <string.h>
  48 #include <strings.h>
  49 #include <sys/uio.h>
  50 
  51 #include <libmlrpc.h>
  52 #include <ndr_wchar.h>
  53 
  54 /*
  55  * Allocate a heap structure and the first heap block.  For many RPC
  56  * operations this will be the only time we need to malloc memory
  57  * in this instance of the heap.  The only point of note here is that
  58  * we put the heap management data in the first block to avoid a
  59  * second malloc. Make sure that sizeof(ndr_heap_t) is smaller
  60  * than NDR_HEAP_BLKSZ.
  61  *
  62  * Note that the heap management data is at the start of the first block.
  63  *
  64  * Returns a pointer to the newly created heap, which is used like an
  65  * opaque handle with the rest of the heap management interface..
  66  */
  67 ndr_heap_t *
  68 ndr_heap_create(void)
  69 {
  70         ndr_heap_t *heap;
  71         char *base;
  72         size_t allocsize = sizeof (ndr_heap_t) + NDR_HEAP_BLKSZ;
  73 
  74         if ((heap = malloc(allocsize)) == NULL)
  75                 return (NULL);
  76 
  77         base = (char *)heap;
  78         bzero(heap, sizeof (ndr_heap_t));
  79 
  80         heap->iovcnt = NDR_HEAP_MAXIOV;
  81         heap->iov = heap->iovec;
  82         heap->iov->iov_base = base;
  83         heap->iov->iov_len = sizeof (ndr_heap_t);
  84         heap->top = base + allocsize;
  85         heap->next = base + sizeof (ndr_heap_t);
  86 
  87         return (heap);
  88 }
  89 
  90 /*
  91  * Deallocate all of the memory associated with a heap.  This is the
  92  * only way to deallocate heap memory, it isn't possible to free the
  93  * space obtained by individual malloc calls.
  94  *
  95  * Note that the first block contains the heap management data, which
  96  * is deleted last.
  97  */
  98 void
  99 ndr_heap_destroy(ndr_heap_t *heap)
 100 {
 101         int i;
 102         char *p;
 103 
 104         if (heap) {
 105                 for (i = 1; i < NDR_HEAP_MAXIOV; ++i) {
 106                         if ((p = heap->iovec[i].iov_base) != NULL)
 107                                 free(p);
 108                 }
 109 
 110                 free(heap);
 111         }
 112 }
 113 
 114 /*
 115  * Allocate space in the specified heap.  All requests are padded, if
 116  * required, to ensure dword alignment.  If the current iov will be
 117  * exceeded, we allocate a new block and setup the next iov.  Otherwise
 118  * all we have to do is move the next pointer and update the current
 119  * iov length.
 120  *
 121  * On success, a pointer to the allocated (dword aligned) area is
 122  * returned.  Otherwise a null pointer is returned.
 123  */
 124 void *
 125 ndr_heap_malloc(ndr_heap_t *heap, unsigned size)
 126 {
 127         char *p;
 128         int incr_size;
 129 
 130         size += NDR_ALIGN4(size);
 131 
 132         if (heap == NULL || size == 0)
 133                 return (NULL);
 134 
 135         p = heap->next;
 136 
 137         if (p + size > heap->top) {
 138                 if ((heap->iovcnt == 0) || ((--heap->iovcnt) == 0))
 139                         return (NULL);
 140 
 141                 incr_size = (size < NDR_HEAP_BLKSZ) ? NDR_HEAP_BLKSZ : size;
 142 
 143                 if ((p = (char *)malloc(incr_size)) == NULL)
 144                         return (NULL);
 145 
 146                 ++heap->iov;
 147                 heap->iov->iov_base = p;
 148                 heap->iov->iov_len = 0;
 149                 heap->top = p + incr_size;
 150         }
 151 
 152         heap->next = p + size;
 153         heap->iov->iov_len += size;
 154         return ((void *)p);
 155 }
 156 
 157 /*
 158  * Convenience function to copy some memory into the heap.
 159  */
 160 void *
 161 ndr_heap_dupmem(ndr_heap_t *heap, const void *mem, size_t len)
 162 {
 163         void *p;
 164 
 165         if (mem == NULL)
 166                 return (NULL);
 167 
 168         if ((p = ndr_heap_malloc(heap, len)) != NULL)
 169                 (void) memcpy(p, mem, len);
 170 
 171         return (p);
 172 }
 173 
 174 /*
 175  * Convenience function to do heap strdup.
 176  */
 177 void *
 178 ndr_heap_strdup(ndr_heap_t *heap, const char *s)
 179 {
 180         int len;
 181         void *p;
 182 
 183         if (s == NULL)
 184                 return (NULL);
 185 
 186         /*
 187          * We don't need to clutter the heap with empty strings.
 188          */
 189         if ((len = strlen(s)) == 0)
 190                 return ("");
 191 
 192         p = ndr_heap_dupmem(heap, s, len+1);
 193 
 194         return (p);
 195 }
 196 
 197 /*
 198  * Make an ndr_mstring_t from a regular string.
 199  */
 200 int
 201 ndr_heap_mstring(ndr_heap_t *heap, const char *s, ndr_mstring_t *out)
 202 {
 203         size_t slen;
 204 
 205         if (s == NULL || out == NULL)
 206                 return (-1);
 207 
 208         /*
 209          * Determine the WC strlen of s
 210          * Was ndr__wcequiv_strlen(s)
 211          */
 212         slen = ndr__mbstowcs(NULL, s, NDR_STRING_MAX);
 213         if (slen == (size_t)-1)
 214                 return (-1);
 215 
 216         out->length = slen * sizeof (ndr_wchar_t);
 217         out->allosize = out->length + sizeof (ndr_wchar_t);
 218 
 219         if ((out->str = ndr_heap_strdup(heap, s)) == NULL)
 220                 return (-1);
 221 
 222         return (0);
 223 }
 224 
 225 /*
 226  * Our regular string marshalling always creates null terminated strings
 227  * but some Windows clients and servers are pedantic about the string
 228  * formats they will accept and require non-null terminated strings.
 229  * This function can be used to build a wide-char, non-null terminated
 230  * string in the heap as a varying/conformant array.  We need to do the
 231  * wide-char conversion here because the marshalling code won't be
 232  * aware that this is really a string.
 233  */
 234 void
 235 ndr_heap_mkvcs(ndr_heap_t *heap, char *s, ndr_vcstr_t *vc)
 236 {
 237         size_t slen;
 238         int mlen;
 239 
 240         /*
 241          * Determine the WC strlen of s
 242          * Was ndr__wcequiv_strlen(s)
 243          */
 244         slen = ndr__mbstowcs(NULL, s, NDR_STRING_MAX);
 245         if (slen == (size_t)-1)
 246                 slen = 0;
 247 
 248         vc->wclen = slen * sizeof (ndr_wchar_t);
 249         vc->wcsize = vc->wclen;
 250 
 251         /*
 252          * alloc one extra wchar for a null
 253          * See slen + 1 arg for mbstowcs
 254          */
 255         mlen = sizeof (ndr_vcs_t) + vc->wcsize + sizeof (ndr_wchar_t);
 256         vc->vcs = ndr_heap_malloc(heap, mlen);
 257 
 258         if (vc->vcs) {
 259                 vc->vcs->vc_first_is = 0;
 260                 vc->vcs->vc_length_is = slen;
 261                 (void) ndr__mbstowcs(vc->vcs->buffer, s, slen + 1);
 262         }
 263 }
 264 
 265 void
 266 ndr_heap_mkvcb(ndr_heap_t *heap, uint8_t *data, uint32_t datalen,
 267     ndr_vcbuf_t *vcbuf)
 268 {
 269         int mlen;
 270 
 271         if (data == NULL || datalen == 0) {
 272                 bzero(vcbuf, sizeof (ndr_vcbuf_t));
 273                 return;
 274         }
 275 
 276         vcbuf->len = datalen;
 277         vcbuf->size = datalen;
 278 
 279         mlen = sizeof (ndr_vcbuf_t) + datalen;
 280 
 281         vcbuf->vcb = ndr_heap_malloc(heap, mlen);
 282 
 283         if (vcbuf->vcb) {
 284                 vcbuf->vcb->vc_first_is = 0;
 285                 vcbuf->vcb->vc_length_is = datalen;
 286                 bcopy(data, vcbuf->vcb->buffer, datalen);
 287         }
 288 }
 289 
 290 /*
 291  * Removed ndr_heap_siddup(), now using ndr_heap_dupmem().
 292  */
 293 
 294 int
 295 ndr_heap_used(ndr_heap_t *heap)
 296 {
 297         int used = 0;
 298         int i;
 299 
 300         for (i = 0; i < NDR_HEAP_MAXIOV; ++i)
 301                 used += heap->iovec[i].iov_len;
 302 
 303         return (used);
 304 }
 305 
 306 int
 307 ndr_heap_avail(ndr_heap_t *heap)
 308 {
 309         int avail;
 310         int count;
 311 
 312         count = (heap->iovcnt == 0) ? 0 : (heap->iovcnt - 1);
 313 
 314         avail = count * NDR_HEAP_BLKSZ;
 315         avail += (heap->top - heap->next);
 316 
 317         return (avail);
 318 }