r12558: Support [flag(NULLTERM)] on [charset()] arrays
[samba.git] / source4 / librpc / ndr / ndr_string.c
index 42316a8003c8b2213fb33288f82e29e9f463a51c..fcc529cbd10855066a26eaa3a2263984a756ac2e 100644 (file)
@@ -79,18 +79,19 @@ NTSTATUS ndr_pull_string(struct ndr_pull *ndr, int ndr_flags, const char **s)
                                              "Bad string lengths len1=%u ofs=%u len2=%u\n", 
                                              len1, ofs, len2);
                }
-               if (len2 == 0) {
-                       *s = talloc_strdup(ndr, "");
-                       break;
-               }
                NDR_PULL_NEED_BYTES(ndr, (len2 + c_len_term)*byte_mul);
-               ret = convert_string_talloc(ndr, chset, CH_UNIX, 
-                                           ndr->data+ndr->offset, 
-                                           (len2 + c_len_term)*byte_mul,
-                                           (void **)&as);
-               if (ret == -1) {
-                       return ndr_pull_error(ndr, NDR_ERR_CHARCNV, 
-                                             "Bad character conversion");
+               if (len2 == 0) {
+                       as = talloc_strdup(ndr->current_mem_ctx, "");
+               } else {
+                       ret = convert_string_talloc(ndr->current_mem_ctx,
+                                                   chset, CH_UNIX, 
+                                                   ndr->data+ndr->offset, 
+                                                   (len2 + c_len_term)*byte_mul,
+                                                   (void **)&as);
+                       if (ret == -1) {
+                               return ndr_pull_error(ndr, NDR_ERR_CHARCNV, 
+                                                     "Bad character conversion");
+                       }
                }
                NDR_CHECK(ndr_pull_advance(ndr, (len2 + c_len_term)*byte_mul));
 
@@ -125,16 +126,17 @@ NTSTATUS ndr_pull_string(struct ndr_pull *ndr, int ndr_flags, const char **s)
                NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &len1));
                NDR_PULL_NEED_BYTES(ndr, (len1 + c_len_term)*byte_mul);
                if (len1 == 0) {
-                       *s = talloc_strdup(ndr, "");
-                       break;
-               }
-               ret = convert_string_talloc(ndr, chset, CH_UNIX, 
-                                           ndr->data+ndr->offset, 
-                                           (len1 + c_len_term)*byte_mul,
-                                           (void **)&as);
-               if (ret == -1) {
-                       return ndr_pull_error(ndr, NDR_ERR_CHARCNV, 
-                                             "Bad character conversion");
+                       as = talloc_strdup(ndr->current_mem_ctx, "");
+               } else {
+                       ret = convert_string_talloc(ndr->current_mem_ctx,
+                                                   chset, CH_UNIX, 
+                                                   ndr->data+ndr->offset, 
+                                                   (len1 + c_len_term)*byte_mul,
+                                                   (void **)&as);
+                       if (ret == -1) {
+                               return ndr_pull_error(ndr, NDR_ERR_CHARCNV, 
+                                                     "Bad character conversion");
+                       }
                }
                NDR_CHECK(ndr_pull_advance(ndr, (len1 + c_len_term)*byte_mul));
 
@@ -162,16 +164,17 @@ NTSTATUS ndr_pull_string(struct ndr_pull *ndr, int ndr_flags, const char **s)
                NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &len1));
                NDR_PULL_NEED_BYTES(ndr, (len1 + c_len_term)*byte_mul);
                if (len1 == 0) {
-                       *s = talloc_strdup(ndr, "");
-                       break;
-               }
-               ret = convert_string_talloc(ndr, chset, CH_UNIX, 
-                                           ndr->data+ndr->offset, 
-                                           (len1 + c_len_term)*byte_mul,
-                                           (void **)&as);
-               if (ret == -1) {
-                       return ndr_pull_error(ndr, NDR_ERR_CHARCNV, 
-                                             "Bad character conversion");
+                       as = talloc_strdup(ndr->current_mem_ctx, "");
+               } else {
+                       ret = convert_string_talloc(ndr->current_mem_ctx,
+                                                   chset, CH_UNIX, 
+                                                   ndr->data+ndr->offset, 
+                                                   (len1 + c_len_term)*byte_mul,
+                                                   (void **)&as);
+                       if (ret == -1) {
+                               return ndr_pull_error(ndr, NDR_ERR_CHARCNV, 
+                                                     "Bad character conversion");
+                       }
                }
                NDR_CHECK(ndr_pull_advance(ndr, (len1 + c_len_term)*byte_mul));
 
@@ -195,16 +198,17 @@ NTSTATUS ndr_pull_string(struct ndr_pull *ndr, int ndr_flags, const char **s)
                NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &len3));
                NDR_PULL_NEED_BYTES(ndr, (len3 + c_len_term)*byte_mul);
                if (len3 == 0) {
-                       *s = talloc_strdup(ndr, "");
-                       break;
-               }
-               ret = convert_string_talloc(ndr, chset, CH_UNIX, 
-                                           ndr->data+ndr->offset, 
-                                           (len3 + c_len_term)*byte_mul,
-                                           (void **)&as);
-               if (ret == -1) {
-                       return ndr_pull_error(ndr, NDR_ERR_CHARCNV, 
-                                             "Bad character conversion");
+                       as = talloc_strdup(ndr->current_mem_ctx, "");
+               } else {
+                       ret = convert_string_talloc(ndr->current_mem_ctx,
+                                                   chset, CH_UNIX, 
+                                                   ndr->data+ndr->offset, 
+                                                   (len3 + c_len_term)*byte_mul,
+                                                   (void **)&as);
+                       if (ret == -1) {
+                               return ndr_pull_error(ndr, NDR_ERR_CHARCNV, 
+                                                     "Bad character conversion");
+                       }
                }
                NDR_CHECK(ndr_pull_advance(ndr, (len3 + c_len_term)*byte_mul));
 
@@ -226,16 +230,17 @@ NTSTATUS ndr_pull_string(struct ndr_pull *ndr, int ndr_flags, const char **s)
                NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &len3));
                NDR_PULL_NEED_BYTES(ndr, len3);
                if (len3 == 0) {
-                       *s = talloc_strdup(ndr, "");
-                       break;
-               }
-               ret = convert_string_talloc(ndr, chset, CH_UNIX, 
-                                           ndr->data+ndr->offset, 
-                                           len3,
-                                           (void **)&as);
-               if (ret == -1) {
-                       return ndr_pull_error(ndr, NDR_ERR_CHARCNV, 
-                                             "Bad character conversion");
+                       as = talloc_strdup(ndr->current_mem_ctx, "");
+               } else {
+                       ret = convert_string_talloc(ndr->current_mem_ctx,
+                                                   chset, CH_UNIX, 
+                                                   ndr->data+ndr->offset, 
+                                                   len3,
+                                                   (void **)&as);
+                       if (ret == -1) {
+                               return ndr_pull_error(ndr, NDR_ERR_CHARCNV, 
+                                                     "Bad character conversion");
+                       }
                }
                NDR_CHECK(ndr_pull_advance(ndr, len3));
                *s = as;
@@ -247,7 +252,8 @@ NTSTATUS ndr_pull_string(struct ndr_pull *ndr, int ndr_flags, const char **s)
                } else {
                        len1 = utf16_len_n(ndr->data+ndr->offset, ndr->data_size - ndr->offset);
                }
-               ret = convert_string_talloc(ndr, chset, CH_UNIX, 
+               ret = convert_string_talloc(ndr->current_mem_ctx,
+                                           chset, CH_UNIX, 
                                            ndr->data+ndr->offset, 
                                            len1,
                                            (void **)&as);
@@ -263,7 +269,8 @@ NTSTATUS ndr_pull_string(struct ndr_pull *ndr, int ndr_flags, const char **s)
        case LIBNDR_FLAG_STR_FIXLEN32:
                len1 = (flags & LIBNDR_FLAG_STR_FIXLEN32)?32:15;
                NDR_PULL_NEED_BYTES(ndr, len1*byte_mul);
-               ret = convert_string_talloc(ndr, chset, CH_UNIX, 
+               ret = convert_string_talloc(ndr->current_mem_ctx,
+                                           chset, CH_UNIX, 
                                            ndr->data+ndr->offset, 
                                            len1*byte_mul,
                                            (void **)&as);
@@ -530,13 +537,17 @@ NTSTATUS ndr_pull_string_array(struct ndr_pull *ndr, int ndr_flags, const char *
        }
 
        for (count = 0;; count++) {
+               TALLOC_CTX *tmp_ctx;
                const char *s = NULL;
-               a = talloc_realloc(ndr, a, const char *, count + 2);
+               a = talloc_realloc(ndr->current_mem_ctx, a, const char *, count + 2);
                NT_STATUS_HAVE_NO_MEMORY(a);
                a[count]   = NULL;
                a[count+1]   = NULL;
 
+               tmp_ctx = ndr->current_mem_ctx;
+               ndr->current_mem_ctx = a;
                NDR_CHECK(ndr_pull_string(ndr, ndr_flags, &s));
+               ndr->current_mem_ctx = tmp_ctx;
                if (strcmp("", s)==0) {
                        a[count] = NULL;
                        break;
@@ -590,42 +601,60 @@ void ndr_print_string_array(struct ndr_print *ndr, const char *name, const char
 }
 
 /* Return number of elements in a string including the last (zeroed) element */
-uint32_t ndr_string_length(void *_var, uint32_t element_size)
+uint32_t ndr_string_length(const void *_var, uint32_t element_size)
 {
        uint32_t i;
        uint8_t zero[4] = {0,0,0,0};
-       char *var = _var;
+       const char *var = _var;
 
        for (i = 0; memcmp(var+i*element_size,zero,element_size) != 0; i++);
 
        return i+1;
 }
 
-NTSTATUS ndr_check_string_terminator(struct ndr_pull *ndr, const void *_var, uint32_t count, uint32_t element_size)
+NTSTATUS ndr_check_string_terminator(struct ndr_pull *ndr, uint32_t count, uint32_t element_size)
 {
-       const char *var = _var;
        uint32_t i;
+       struct ndr_pull_save save_offset;
 
-       var += element_size*(count-1);
+       ndr_pull_save(ndr, &save_offset);
+       ndr_pull_advance(ndr, (count - 1) * element_size);
+       NDR_PULL_NEED_BYTES(ndr, element_size);
 
        for (i = 0; i < element_size; i++) {
-                if (var[i] != 0) {
-                       return NT_STATUS_UNSUCCESSFUL;
+                if (ndr->data[ndr->offset+i] != 0) {
+                       ndr_pull_restore(ndr, &save_offset);
+
+                       return ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE, "String terminator not present or outside string boundaries");
                 }
        }
 
-       return NT_STATUS_OK;
+       ndr_pull_restore(ndr, &save_offset);
 
+       return NT_STATUS_OK;
 }
 
-NTSTATUS ndr_pull_charset(struct ndr_pull *ndr, int ndr_flags, char **var, uint32_t length, uint8_t byte_mul, int chset)
+NTSTATUS ndr_pull_charset(struct ndr_pull *ndr, int ndr_flags, const char **var, uint32_t length, uint8_t byte_mul, int chset)
 {
        int ret;
+       if (length == 0) {
+               *var = talloc_strdup(ndr->current_mem_ctx, "");
+               return NT_STATUS_OK;
+       }
+
        NDR_PULL_NEED_BYTES(ndr, length*byte_mul);
-       ret = convert_string_talloc(ndr, chset, CH_UNIX, 
+
+       if (ndr->flags & LIBNDR_FLAG_STR_NULLTERM) {
+               /* Explicitly ignore the return value here. An array that 
+                * is not zero-terminated is considered a warning only, not fatal */
+               ndr_check_string_terminator(ndr, length, byte_mul);
+       }
+       
+       ret = convert_string_talloc(ndr->current_mem_ctx,
+                                   chset, CH_UNIX, 
                                    ndr->data+ndr->offset, 
                                    length*byte_mul,
-                                   (void **)var);
+                                   discard_const_p(void *, var));
        if (ret == -1) {
                return ndr_pull_error(ndr, NDR_ERR_CHARCNV, 
                                      "Bad character conversion");
@@ -637,16 +666,33 @@ NTSTATUS ndr_pull_charset(struct ndr_pull *ndr, int ndr_flags, char **var, uint3
 
 NTSTATUS ndr_push_charset(struct ndr_push *ndr, int ndr_flags, const char *var, uint32_t length, uint8_t byte_mul, int chset)
 {
-       ssize_t ret;
-       NDR_PUSH_NEED_BYTES(ndr, byte_mul*length);
+       ssize_t ret, required;
+
+       required = byte_mul * length;
+       
+       NDR_PUSH_NEED_BYTES(ndr, required);
        ret = convert_string(CH_UNIX, chset, 
-                            var, length,
-                            ndr->data+ndr->offset, byte_mul*length);
+                            var, strlen(var),
+                            ndr->data+ndr->offset, required);
        if (ret == -1) {
                return ndr_push_error(ndr, NDR_ERR_CHARCNV, 
                                      "Bad character conversion");
        }
-       ndr->offset += ret;
+
+       /* Make sure the remaining part of the string is filled with zeroes */
+       if (ret < required) {
+               memset(ndr->data+ndr->offset+ret, 0, required-ret);
+       }
+
+       ndr->offset += required;
 
        return NT_STATUS_OK;
 }
+
+/* Return number of elements in a string in the specified charset */
+uint32_t ndr_charset_length(const void *var, int chset)
+{
+       /* FIXME: Treat special chars special here, taking chset into account */
+       /* Also include 0 byte */
+       return strlen(var)+1;
+}