2 Unix SMB/CIFS implementation.
6 Copyright (C) Andrew Tridgell 2003
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 this provides the core routines for NDR parsing functions
26 see http://www.opengroup.org/onlinepubs/9629399/chap14.htm for details
31 #include "dlinklist.h"
33 #define NDR_BASE_MARSHALL_SIZE 1024
36 work out the number of bytes needed to align on a n byte boundary
38 size_t ndr_align_size(uint32_t offset, size_t n)
40 if ((offset & (n-1)) == 0) return 0;
41 return n - (offset & (n-1));
45 initialise a ndr parse structure from a data blob
47 struct ndr_pull *ndr_pull_init_blob(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
51 ndr = talloc_zero(mem_ctx, struct ndr_pull);
52 if (!ndr) return NULL;
53 ndr->current_mem_ctx = mem_ctx;
55 ndr->data = blob->data;
56 ndr->data_size = blob->length;
62 advance by 'size' bytes
64 NTSTATUS ndr_pull_advance(struct ndr_pull *ndr, uint32_t size)
67 if (ndr->offset > ndr->data_size) {
68 return ndr_pull_error(ndr, NDR_ERR_BUFSIZE,
69 "ndr_pull_advance by %u failed",
76 set the parse offset to 'ofs'
78 static NTSTATUS ndr_pull_set_offset(struct ndr_pull *ndr, uint32_t ofs)
81 if (ndr->offset > ndr->data_size) {
82 return ndr_pull_error(ndr, NDR_ERR_BUFSIZE,
83 "ndr_pull_set_offset %u failed",
89 /* save the offset/size of the current ndr state */
90 void ndr_pull_save(struct ndr_pull *ndr, struct ndr_pull_save *save)
92 save->offset = ndr->offset;
93 save->data_size = ndr->data_size;
96 /* restore the size/offset of a ndr structure */
97 void ndr_pull_restore(struct ndr_pull *ndr, struct ndr_pull_save *save)
99 ndr->offset = save->offset;
100 ndr->data_size = save->data_size;
104 /* create a ndr_push structure, ready for some marshalling */
105 struct ndr_push *ndr_push_init_ctx(TALLOC_CTX *mem_ctx)
107 struct ndr_push *ndr;
109 ndr = talloc_zero(mem_ctx, struct ndr_push);
115 ndr->alloc_size = NDR_BASE_MARSHALL_SIZE;
116 ndr->data = talloc_array(ndr, uint8_t, ndr->alloc_size);
125 /* create a ndr_push structure, ready for some marshalling */
126 struct ndr_push *ndr_push_init(void)
128 return ndr_push_init_ctx(NULL);
131 /* free a ndr_push structure */
132 void ndr_push_free(struct ndr_push *ndr)
138 /* return a DATA_BLOB structure for the current ndr_push marshalled data */
139 DATA_BLOB ndr_push_blob(struct ndr_push *ndr)
142 blob.data = ndr->data;
143 blob.length = ndr->offset;
144 if (ndr->alloc_size > ndr->offset) {
145 ndr->data[ndr->offset] = 0;
152 expand the available space in the buffer to 'size'
154 NTSTATUS ndr_push_expand(struct ndr_push *ndr, uint32_t size)
156 if (ndr->alloc_size > size) {
160 ndr->alloc_size += NDR_BASE_MARSHALL_SIZE;
161 if (size+1 > ndr->alloc_size) {
162 ndr->alloc_size = size+1;
164 ndr->data = talloc_realloc(ndr, ndr->data, uint8_t, ndr->alloc_size);
166 return ndr_push_error(ndr, NDR_ERR_ALLOC, "Failed to push_expand to %u",
173 void ndr_print_debug_helper(struct ndr_print *ndr, const char *format, ...) _PRINTF_ATTRIBUTE(2,3)
179 va_start(ap, format);
180 vasprintf(&s, format, ap);
183 for (i=0;i<ndr->depth;i++) {
187 DEBUG(0,("%s\n", s));
192 a useful helper function for printing idl structures via DEBUG()
194 void ndr_print_debug(ndr_print_fn_t fn, const char *name, void *ptr)
196 struct ndr_print *ndr;
198 ndr = talloc_zero(NULL, struct ndr_print);
200 ndr->print = ndr_print_debug_helper;
208 a useful helper function for printing idl unions via DEBUG()
210 void ndr_print_union_debug(ndr_print_fn_t fn, const char *name, uint32_t level, void *ptr)
212 struct ndr_print *ndr;
214 ndr = talloc_zero(NULL, struct ndr_print);
216 ndr->print = ndr_print_debug_helper;
219 ndr_print_set_switch_value(ndr, ptr, level);
225 a useful helper function for printing idl function calls via DEBUG()
227 void ndr_print_function_debug(ndr_print_function_t fn, const char *name, int flags, void *ptr)
229 struct ndr_print *ndr;
231 ndr = talloc_zero(NULL, struct ndr_print);
233 ndr->print = ndr_print_debug_helper;
236 fn(ndr, name, flags, ptr);
240 void ndr_set_flags(uint32_t *pflags, uint32_t new_flags)
242 /* the big/little endian flags are inter-dependent */
243 if (new_flags & LIBNDR_FLAG_LITTLE_ENDIAN) {
244 (*pflags) &= ~LIBNDR_FLAG_BIGENDIAN;
246 if (new_flags & LIBNDR_FLAG_BIGENDIAN) {
247 (*pflags) &= ~LIBNDR_FLAG_LITTLE_ENDIAN;
249 if (new_flags & LIBNDR_FLAG_REMAINING) {
250 (*pflags) &= ~LIBNDR_ALIGN_FLAGS;
252 if (new_flags & LIBNDR_ALIGN_FLAGS) {
253 (*pflags) &= ~LIBNDR_FLAG_REMAINING;
255 (*pflags) |= new_flags;
258 static NTSTATUS ndr_map_error(enum ndr_err_code err)
261 case NDR_ERR_BUFSIZE:
262 return NT_STATUS_BUFFER_TOO_SMALL;
264 return NT_STATUS_INTERNAL_ERROR;
266 return NT_STATUS_NO_MEMORY;
267 case NDR_ERR_ARRAY_SIZE:
268 return NT_STATUS_ARRAY_BOUNDS_EXCEEDED;
273 /* we should map all error codes to different status codes */
274 return NT_STATUS_INVALID_PARAMETER;
278 return and possibly log an NDR error
280 NTSTATUS ndr_pull_error(struct ndr_pull *ndr,
281 enum ndr_err_code err, const char *format, ...) _PRINTF_ATTRIBUTE(3,4)
286 va_start(ap, format);
287 vasprintf(&s, format, ap);
290 DEBUG(3,("ndr_pull_error(%u): %s\n", err, s));
294 return ndr_map_error(err);
298 return and possibly log an NDR error
300 NTSTATUS ndr_push_error(struct ndr_push *ndr, enum ndr_err_code err, const char *format, ...) _PRINTF_ATTRIBUTE(3,4)
305 va_start(ap, format);
306 vasprintf(&s, format, ap);
309 DEBUG(3,("ndr_push_error(%u): %s\n", err, s));
313 return ndr_map_error(err);
317 handle subcontext buffers, which in midl land are user-marshalled, but
318 we use magic in pidl to make them easier to cope with
320 NTSTATUS ndr_pull_subcontext_start(struct ndr_pull *ndr,
321 struct ndr_pull **_subndr,
325 struct ndr_pull *subndr;
326 uint32_t r_content_size;
328 switch (header_size) {
330 uint32_t content_size = ndr->data_size - ndr->offset;
332 content_size = size_is;
334 r_content_size = content_size;
339 uint16_t content_size;
340 NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &content_size));
341 if (size_is >= 0 && size_is != content_size) {
342 return ndr_pull_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PULL) size_is(%d) mismatch content_size %d",
343 (int)size_is, (int)content_size);
345 r_content_size = content_size;
350 uint32_t content_size;
351 NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &content_size));
352 if (size_is >= 0 && size_is != content_size) {
353 return ndr_pull_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PULL) size_is(%d) mismatch content_size %d",
354 (int)size_is, (int)content_size);
356 r_content_size = content_size;
360 return ndr_pull_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PULL) header_size %d",
364 NDR_PULL_NEED_BYTES(ndr, r_content_size);
366 subndr = talloc_zero(ndr, struct ndr_pull);
367 NT_STATUS_HAVE_NO_MEMORY(subndr);
368 subndr->flags = ndr->flags;
369 subndr->current_mem_ctx = ndr->current_mem_ctx;
371 subndr->data = ndr->data + ndr->offset;
373 subndr->data_size = r_content_size;
379 NTSTATUS ndr_pull_subcontext_end(struct ndr_pull *ndr,
380 struct ndr_pull *subndr,
387 } else if (header_size > 0) {
388 advance = subndr->data_size;
390 advance = subndr->offset;
392 NDR_CHECK(ndr_pull_advance(ndr, advance));
396 NTSTATUS ndr_push_subcontext_start(struct ndr_push *ndr,
397 struct ndr_push **_subndr,
401 struct ndr_push *subndr;
403 subndr = ndr_push_init_ctx(ndr);
404 NT_STATUS_HAVE_NO_MEMORY(subndr);
405 subndr->flags = ndr->flags;
412 push a subcontext header
414 NTSTATUS ndr_push_subcontext_end(struct ndr_push *ndr,
415 struct ndr_push *subndr,
420 ssize_t padding_len = size_is - subndr->offset;
421 if (padding_len > 0) {
422 NDR_CHECK(ndr_push_zero(subndr, padding_len));
423 } else if (padding_len < 0) {
424 return ndr_push_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PUSH) content_size %d is larger than size_is(%d)",
425 (int)subndr->offset, (int)size_is);
429 switch (header_size) {
434 NDR_CHECK(ndr_push_uint16(ndr, NDR_SCALARS, subndr->offset));
438 NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, subndr->offset));
442 return ndr_push_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext header size %d",
446 NDR_CHECK(ndr_push_bytes(ndr, subndr->data, subndr->offset));
451 store a token in the ndr context, for later retrieval
453 NTSTATUS ndr_token_store(TALLOC_CTX *mem_ctx,
454 struct ndr_token_list **list,
458 struct ndr_token_list *tok;
459 tok = talloc(mem_ctx, struct ndr_token_list);
461 return NT_STATUS_NO_MEMORY;
465 DLIST_ADD((*list), tok);
470 retrieve a token from a ndr context, using cmp_fn to match the tokens
472 NTSTATUS ndr_token_retrieve_cmp_fn(struct ndr_token_list **list, const void *key, uint32_t *v,
473 comparison_fn_t _cmp_fn, BOOL _remove_tok)
475 struct ndr_token_list *tok;
476 for (tok=*list;tok;tok=tok->next) {
477 if (_cmp_fn && _cmp_fn(tok->key,key)==0) goto found;
478 else if (!_cmp_fn && tok->key == key) goto found;
480 return ndr_map_error(NDR_ERR_TOKEN);
484 DLIST_REMOVE((*list), tok);
491 retrieve a token from a ndr context
493 NTSTATUS ndr_token_retrieve(struct ndr_token_list **list, const void *key, uint32_t *v)
495 return ndr_token_retrieve_cmp_fn(list, key, v, NULL, True);
499 peek at but don't removed a token from a ndr context
501 uint32_t ndr_token_peek(struct ndr_token_list **list, const void *key)
505 status = ndr_token_retrieve_cmp_fn(list, key, &v, NULL, False);
506 if (NT_STATUS_IS_OK(status)) return v;
511 pull an array size field and add it to the array_size_list token list
513 NTSTATUS ndr_pull_array_size(struct ndr_pull *ndr, const void *p)
516 NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &size));
517 return ndr_token_store(ndr, &ndr->array_size_list, p, size);
521 get the stored array size field
523 uint32_t ndr_get_array_size(struct ndr_pull *ndr, const void *p)
525 return ndr_token_peek(&ndr->array_size_list, p);
529 check the stored array size field
531 NTSTATUS ndr_check_array_size(struct ndr_pull *ndr, void *p, uint32_t size)
534 stored = ndr_token_peek(&ndr->array_size_list, p);
535 if (stored != size) {
536 return ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE,
537 "Bad array size - got %u expected %u\n",
544 pull an array length field and add it to the array_length_list token list
546 NTSTATUS ndr_pull_array_length(struct ndr_pull *ndr, const void *p)
548 uint32_t length, offset;
549 NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &offset));
551 return ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE,
552 "non-zero array offset %u\n", offset);
554 NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &length));
555 return ndr_token_store(ndr, &ndr->array_length_list, p, length);
559 get the stored array length field
561 uint32_t ndr_get_array_length(struct ndr_pull *ndr, const void *p)
563 return ndr_token_peek(&ndr->array_length_list, p);
567 check the stored array length field
569 NTSTATUS ndr_check_array_length(struct ndr_pull *ndr, void *p, uint32_t length)
572 stored = ndr_token_peek(&ndr->array_length_list, p);
573 if (stored != length) {
574 return ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE,
575 "Bad array length - got %u expected %u\n",
584 NTSTATUS ndr_push_set_switch_value(struct ndr_push *ndr, const void *p, uint32_t val)
586 return ndr_token_store(ndr, &ndr->switch_list, p, val);
589 NTSTATUS ndr_pull_set_switch_value(struct ndr_pull *ndr, const void *p, uint32_t val)
591 return ndr_token_store(ndr, &ndr->switch_list, p, val);
594 NTSTATUS ndr_print_set_switch_value(struct ndr_print *ndr, const void *p, uint32_t val)
596 return ndr_token_store(ndr, &ndr->switch_list, p, val);
600 retrieve a switch value
602 uint32_t ndr_push_get_switch_value(struct ndr_push *ndr, const void *p)
604 return ndr_token_peek(&ndr->switch_list, p);
607 uint32_t ndr_pull_get_switch_value(struct ndr_pull *ndr, const void *p)
609 return ndr_token_peek(&ndr->switch_list, p);
612 uint32_t ndr_print_get_switch_value(struct ndr_print *ndr, const void *p)
614 return ndr_token_peek(&ndr->switch_list, p);
618 pull a struct from a blob using NDR
620 NTSTATUS ndr_pull_struct_blob(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p,
621 ndr_pull_flags_fn_t fn)
623 struct ndr_pull *ndr;
624 ndr = ndr_pull_init_blob(blob, mem_ctx);
626 return NT_STATUS_NO_MEMORY;
628 return fn(ndr, NDR_SCALARS|NDR_BUFFERS, p);
632 pull a struct from a blob using NDR - failing if all bytes are not consumed
634 NTSTATUS ndr_pull_struct_blob_all(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p,
635 ndr_pull_flags_fn_t fn)
637 struct ndr_pull *ndr;
640 ndr = ndr_pull_init_blob(blob, mem_ctx);
642 return NT_STATUS_NO_MEMORY;
644 status = fn(ndr, NDR_SCALARS|NDR_BUFFERS, p);
645 if (!NT_STATUS_IS_OK(status)) return status;
646 if (ndr->offset != ndr->data_size) {
647 return NT_STATUS_BUFFER_TOO_SMALL;
653 pull a union from a blob using NDR, given the union discriminator
655 NTSTATUS ndr_pull_union_blob(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p,
656 uint32_t level, ndr_pull_flags_fn_t fn)
658 struct ndr_pull *ndr;
661 ndr = ndr_pull_init_blob(blob, mem_ctx);
663 return NT_STATUS_NO_MEMORY;
665 ndr_pull_set_switch_value(ndr, p, level);
666 status = fn(ndr, NDR_SCALARS|NDR_BUFFERS, p);
667 if (!NT_STATUS_IS_OK(status)) return status;
668 if (ndr->offset != ndr->data_size) {
669 return NT_STATUS_BUFFER_TOO_SMALL;
675 push a struct to a blob using NDR
677 NTSTATUS ndr_push_struct_blob(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, const void *p,
678 ndr_push_flags_fn_t fn)
681 struct ndr_push *ndr;
682 ndr = ndr_push_init_ctx(mem_ctx);
684 return NT_STATUS_NO_MEMORY;
686 status = fn(ndr, NDR_SCALARS|NDR_BUFFERS, p);
687 if (!NT_STATUS_IS_OK(status)) {
691 *blob = ndr_push_blob(ndr);
697 push a union to a blob using NDR
699 NTSTATUS ndr_push_union_blob(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p,
700 uint32_t level, ndr_push_flags_fn_t fn)
703 struct ndr_push *ndr;
704 ndr = ndr_push_init_ctx(mem_ctx);
706 return NT_STATUS_NO_MEMORY;
708 ndr_push_set_switch_value(ndr, p, level);
709 status = fn(ndr, NDR_SCALARS|NDR_BUFFERS, p);
710 if (!NT_STATUS_IS_OK(status)) {
714 *blob = ndr_push_blob(ndr);
720 generic ndr_size_*() handler for structures
722 size_t ndr_size_struct(const void *p, int flags, ndr_push_flags_fn_t push)
724 struct ndr_push *ndr;
728 /* avoid recursion */
729 if (flags & LIBNDR_FLAG_NO_NDR_SIZE) return 0;
731 ndr = ndr_push_init_ctx(NULL);
733 ndr->flags |= flags | LIBNDR_FLAG_NO_NDR_SIZE;
734 status = push(ndr, NDR_SCALARS|NDR_BUFFERS, discard_const(p));
735 if (!NT_STATUS_IS_OK(status)) {
744 generic ndr_size_*() handler for unions
746 size_t ndr_size_union(const void *p, int flags, uint32_t level, ndr_push_flags_fn_t push)
748 struct ndr_push *ndr;
752 /* avoid recursion */
753 if (flags & LIBNDR_FLAG_NO_NDR_SIZE) return 0;
755 ndr = ndr_push_init_ctx(NULL);
757 ndr->flags |= flags | LIBNDR_FLAG_NO_NDR_SIZE;
758 ndr_push_set_switch_value(ndr, p, level);
759 status = push(ndr, NDR_SCALARS|NDR_BUFFERS, p);
760 if (!NT_STATUS_IS_OK(status)) {
769 get the current base for relative pointers for the push
771 uint32_t ndr_push_get_relative_base_offset(struct ndr_push *ndr)
773 return ndr->relative_base_offset;
777 restore the old base for relative pointers for the push
779 void ndr_push_restore_relative_base_offset(struct ndr_push *ndr, uint32_t offset)
781 ndr->relative_base_offset = offset;
785 setup the current base for relative pointers for the push
786 called in the NDR_SCALAR stage
788 NTSTATUS ndr_push_setup_relative_base_offset1(struct ndr_push *ndr, const void *p, uint32_t offset)
790 ndr->relative_base_offset = offset;
791 return ndr_token_store(ndr, &ndr->relative_base_list, p, offset);
795 setup the current base for relative pointers for the push
796 called in the NDR_BUFFERS stage
798 NTSTATUS ndr_push_setup_relative_base_offset2(struct ndr_push *ndr, const void *p)
800 return ndr_token_retrieve(&ndr->relative_base_list, p, &ndr->relative_base_offset);
804 push a relative object - stage1
805 this is called during SCALARS processing
807 NTSTATUS ndr_push_relative_ptr1(struct ndr_push *ndr, const void *p)
810 NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, 0));
813 NDR_CHECK(ndr_push_align(ndr, 4));
814 NDR_CHECK(ndr_token_store(ndr, &ndr->relative_list, p, ndr->offset));
815 return ndr_push_uint32(ndr, NDR_SCALARS, 0xFFFFFFFF);
819 push a relative object - stage2
820 this is called during buffers processing
822 NTSTATUS ndr_push_relative_ptr2(struct ndr_push *ndr, const void *p)
824 struct ndr_push_save save;
825 uint32_t ptr_offset = 0xFFFFFFFF;
829 ndr_push_save(ndr, &save);
830 NDR_CHECK(ndr_token_retrieve(&ndr->relative_list, p, &ptr_offset));
831 if (ptr_offset > ndr->offset) {
832 return ndr_push_error(ndr, NDR_ERR_BUFSIZE,
833 "ndr_push_relative_ptr2 ptr_offset(%u) > ndr->offset(%u)",
834 ptr_offset, ndr->offset);
836 ndr->offset = ptr_offset;
837 if (save.offset < ndr->relative_base_offset) {
838 return ndr_push_error(ndr, NDR_ERR_BUFSIZE,
839 "ndr_push_relative_ptr2 save.offset(%u) < ndr->relative_base_offset(%u)",
840 save.offset, ndr->relative_base_offset);
842 NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, save.offset - ndr->relative_base_offset));
843 ndr_push_restore(ndr, &save);
848 get the current base for relative pointers for the pull
850 uint32_t ndr_pull_get_relative_base_offset(struct ndr_pull *ndr)
852 return ndr->relative_base_offset;
856 restore the old base for relative pointers for the pull
858 void ndr_pull_restore_relative_base_offset(struct ndr_pull *ndr, uint32_t offset)
860 ndr->relative_base_offset = offset;
864 setup the current base for relative pointers for the pull
865 called in the NDR_SCALAR stage
867 NTSTATUS ndr_pull_setup_relative_base_offset1(struct ndr_pull *ndr, const void *p, uint32_t offset)
869 ndr->relative_base_offset = offset;
870 return ndr_token_store(ndr, &ndr->relative_base_list, p, offset);
874 setup the current base for relative pointers for the pull
875 called in the NDR_BUFFERS stage
877 NTSTATUS ndr_pull_setup_relative_base_offset2(struct ndr_pull *ndr, const void *p)
879 return ndr_token_retrieve(&ndr->relative_base_list, p, &ndr->relative_base_offset);
883 pull a relative object - stage1
884 called during SCALARS processing
886 NTSTATUS ndr_pull_relative_ptr1(struct ndr_pull *ndr, const void *p, uint32_t rel_offset)
888 rel_offset += ndr->relative_base_offset;
889 if (rel_offset > ndr->data_size) {
890 return ndr_pull_error(ndr, NDR_ERR_BUFSIZE,
891 "ndr_pull_relative_ptr1 rel_offset(%u) > ndr->data_size(%u)",
892 rel_offset, ndr->data_size);
894 return ndr_token_store(ndr, &ndr->relative_list, p, rel_offset);
898 pull a relative object - stage2
899 called during BUFFERS processing
901 NTSTATUS ndr_pull_relative_ptr2(struct ndr_pull *ndr, const void *p)
904 NDR_CHECK(ndr_token_retrieve(&ndr->relative_list, p, &rel_offset));
905 return ndr_pull_set_offset(ndr, rel_offset);