r7182: remove current support for RELATIVE_CURRENT, this will be replaced with a...
[jelmer/samba4-debian.git] / source / librpc / ndr / ndr.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    libndr interface
5
6    Copyright (C) Andrew Tridgell 2003
7    
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.
12    
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.
17    
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.
21 */
22
23 /*
24   this provides the core routines for NDR parsing functions
25
26   see http://www.opengroup.org/onlinepubs/9629399/chap14.htm for details
27   of NDR encoding rules
28 */
29
30 #include "includes.h"
31 #include "dlinklist.h"
32
33 #define NDR_BASE_MARSHALL_SIZE 1024
34
35 /*
36   work out the number of bytes needed to align on a n byte boundary
37 */
38 size_t ndr_align_size(uint32_t offset, size_t n)
39 {
40         if ((offset & (n-1)) == 0) return 0;
41         return n - (offset & (n-1));
42 }
43
44 /*
45   initialise a ndr parse structure from a data blob
46 */
47 struct ndr_pull *ndr_pull_init_blob(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
48 {
49         struct ndr_pull *ndr;
50
51         ndr = talloc_zero(mem_ctx, struct ndr_pull);
52         if (!ndr) return NULL;
53
54         ndr->data = blob->data;
55         ndr->data_size = blob->length;
56
57         return ndr;
58 }
59
60 /*
61   create an ndr sub-context based on an existing context. The new context starts
62   at the current offset, with the given size limit
63 */
64 NTSTATUS ndr_pull_subcontext(struct ndr_pull *ndr, struct ndr_pull *ndr2, uint32_t size)
65 {
66         NDR_PULL_NEED_BYTES(ndr, size);
67         *ndr2 = *ndr;
68         ndr2->data += ndr2->offset;
69         ndr2->offset = 0;
70         ndr2->data_size = size;
71         ndr2->flags = ndr->flags;
72         return NT_STATUS_OK;
73 }
74
75
76 /*
77   advance by 'size' bytes
78 */
79 NTSTATUS ndr_pull_advance(struct ndr_pull *ndr, uint32_t size)
80 {
81         ndr->offset += size;
82         if (ndr->offset > ndr->data_size) {
83                 return ndr_pull_error(ndr, NDR_ERR_BUFSIZE, 
84                                       "ndr_pull_advance by %u failed",
85                                       size);
86         }
87         return NT_STATUS_OK;
88 }
89
90 /*
91   set the parse offset to 'ofs'
92 */
93 static NTSTATUS ndr_pull_set_offset(struct ndr_pull *ndr, uint32_t ofs)
94 {
95         ndr->offset = ofs;
96         if (ndr->offset > ndr->data_size) {
97                 return ndr_pull_error(ndr, NDR_ERR_BUFSIZE, 
98                                       "ndr_pull_set_offset %u failed",
99                                       ofs);
100         }
101         return NT_STATUS_OK;
102 }
103
104 /* save the offset/size of the current ndr state */
105 void ndr_pull_save(struct ndr_pull *ndr, struct ndr_pull_save *save)
106 {
107         save->offset = ndr->offset;
108         save->data_size = ndr->data_size;
109 }
110
111 /* restore the size/offset of a ndr structure */
112 void ndr_pull_restore(struct ndr_pull *ndr, struct ndr_pull_save *save)
113 {
114         ndr->offset = save->offset;
115         ndr->data_size = save->data_size;
116 }
117
118
119 /* create a ndr_push structure, ready for some marshalling */
120 struct ndr_push *ndr_push_init_ctx(TALLOC_CTX *mem_ctx)
121 {
122         struct ndr_push *ndr;
123
124         ndr = talloc_zero(mem_ctx, struct ndr_push);
125         if (!ndr) {
126                 return NULL;
127         }
128
129         ndr->flags = 0;
130         ndr->alloc_size = NDR_BASE_MARSHALL_SIZE;
131         ndr->data = talloc_array(ndr, uint8_t, ndr->alloc_size);
132         if (!ndr->data) {
133                 return NULL;
134         }
135
136         return ndr;
137 }
138
139
140 /* create a ndr_push structure, ready for some marshalling */
141 struct ndr_push *ndr_push_init(void)
142 {
143         return ndr_push_init_ctx(NULL);
144 }
145
146 /* free a ndr_push structure */
147 void ndr_push_free(struct ndr_push *ndr)
148 {
149         talloc_free(ndr);
150 }
151
152
153 /* return a DATA_BLOB structure for the current ndr_push marshalled data */
154 DATA_BLOB ndr_push_blob(struct ndr_push *ndr)
155 {
156         DATA_BLOB blob;
157         blob.data = ndr->data;
158         blob.length = ndr->offset;
159         return blob;
160 }
161
162
163 /*
164   expand the available space in the buffer to 'size'
165 */
166 NTSTATUS ndr_push_expand(struct ndr_push *ndr, uint32_t size)
167 {
168         if (ndr->alloc_size >= size) {
169                 return NT_STATUS_OK;
170         }
171
172         ndr->alloc_size += NDR_BASE_MARSHALL_SIZE;
173         if (size > ndr->alloc_size) {
174                 ndr->alloc_size = size;
175         }
176         ndr->data = talloc_realloc(ndr, ndr->data, uint8_t, ndr->alloc_size);
177         if (!ndr->data) {
178                 return ndr_push_error(ndr, NDR_ERR_ALLOC, "Failed to push_expand to %u",
179                                       ndr->alloc_size);
180         }
181
182         return NT_STATUS_OK;
183 }
184
185 void ndr_print_debug_helper(struct ndr_print *ndr, const char *format, ...) _PRINTF_ATTRIBUTE(2,3)
186 {
187         va_list ap;
188         char *s = NULL;
189         int i;
190
191         va_start(ap, format);
192         vasprintf(&s, format, ap);
193         va_end(ap);
194
195         for (i=0;i<ndr->depth;i++) {
196                 DEBUG(0,("    "));
197         }
198
199         DEBUG(0,("%s\n", s));
200         free(s);
201 }
202
203 /*
204   a useful helper function for printing idl structures via DEBUG()
205 */
206 void ndr_print_debug(ndr_print_fn_t fn, const char *name, void *ptr)
207 {
208         struct ndr_print *ndr;
209
210         ndr = talloc_zero(NULL, struct ndr_print);
211         if (!ndr) return;
212         ndr->print = ndr_print_debug_helper;
213         ndr->depth = 1;
214         ndr->flags = 0;
215         fn(ndr, name, ptr);
216         talloc_free(ndr);
217 }
218
219 /*
220   a useful helper function for printing idl unions via DEBUG()
221 */
222 void ndr_print_union_debug(ndr_print_fn_t fn, const char *name, uint32_t level, void *ptr)
223 {
224         struct ndr_print *ndr;
225
226         ndr = talloc_zero(NULL, struct ndr_print);
227         if (!ndr) return;
228         ndr->print = ndr_print_debug_helper;
229         ndr->depth = 1;
230         ndr->flags = 0;
231         ndr_print_set_switch_value(ndr, ptr, level);
232         fn(ndr, name, ptr);
233         talloc_free(ndr);
234 }
235
236 /*
237   a useful helper function for printing idl function calls via DEBUG()
238 */
239 void ndr_print_function_debug(ndr_print_function_t fn, const char *name, int flags, void *ptr)
240 {
241         struct ndr_print *ndr;
242
243         ndr = talloc_zero(NULL, struct ndr_print);
244         if (!ndr) return;
245         ndr->print = ndr_print_debug_helper;
246         ndr->depth = 1;
247         ndr->flags = 0;
248         fn(ndr, name, flags, ptr);
249         talloc_free(ndr);
250 }
251
252 void ndr_set_flags(uint32_t *pflags, uint32_t new_flags)
253 {
254         /* the big/little endian flags are inter-dependent */
255         if (new_flags & LIBNDR_FLAG_LITTLE_ENDIAN) {
256                 (*pflags) &= ~LIBNDR_FLAG_BIGENDIAN;
257         }
258         if (new_flags & LIBNDR_FLAG_BIGENDIAN) {
259                 (*pflags) &= ~LIBNDR_FLAG_LITTLE_ENDIAN;
260         }
261         (*pflags) |= new_flags;
262 }
263
264 static NTSTATUS ndr_map_error(enum ndr_err_code err)
265 {
266         switch (err) {
267         case NDR_ERR_BUFSIZE:
268                 return NT_STATUS_BUFFER_TOO_SMALL;
269         case NDR_ERR_TOKEN:
270                 return NT_STATUS_INTERNAL_ERROR;
271         case NDR_ERR_ALLOC:
272                 return NT_STATUS_NO_MEMORY;
273         case NDR_ERR_ARRAY_SIZE:
274                 return NT_STATUS_ARRAY_BOUNDS_EXCEEDED;
275         default:
276                 break;
277         }
278
279         /* we should map all error codes to different status codes */
280         return NT_STATUS_INVALID_PARAMETER;
281 }
282
283 /*
284   return and possibly log an NDR error
285 */
286 NTSTATUS ndr_pull_error(struct ndr_pull *ndr, 
287                         enum ndr_err_code err, const char *format, ...) _PRINTF_ATTRIBUTE(3,4)
288 {
289         char *s=NULL;
290         va_list ap;
291
292         va_start(ap, format);
293         vasprintf(&s, format, ap);
294         va_end(ap);
295
296         DEBUG(3,("ndr_pull_error(%u): %s\n", err, s));
297
298         free(s);
299
300         return ndr_map_error(err);
301 }
302
303 /*
304   return and possibly log an NDR error
305 */
306 NTSTATUS ndr_push_error(struct ndr_push *ndr, enum ndr_err_code err, const char *format, ...)  _PRINTF_ATTRIBUTE(3,4)
307 {
308         char *s=NULL;
309         va_list ap;
310
311         va_start(ap, format);
312         vasprintf(&s, format, ap);
313         va_end(ap);
314
315         DEBUG(3,("ndr_push_error(%u): %s\n", err, s));
316
317         free(s);
318
319         return ndr_map_error(err);
320 }
321
322
323 /*
324   handle subcontext buffers, which in midl land are user-marshalled, but
325   we use magic in pidl to make them easier to cope with
326 */
327 NTSTATUS ndr_pull_subcontext_header(struct ndr_pull *ndr, 
328                                            size_t header_size,
329                                            ssize_t size_is,
330                                            struct ndr_pull *ndr2)
331 {
332         ndr2->flags = ndr->flags;
333
334         switch (header_size) {
335         case 0: {
336                 uint32_t content_size = ndr->data_size - ndr->offset;
337                 if (size_is >= 0) {
338                         content_size = size_is;
339                 }
340                 NDR_CHECK(ndr_pull_subcontext(ndr, ndr2, content_size));
341                 break;
342         }
343
344         case 2: {
345                 uint16_t content_size;
346                 NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &content_size));
347                 if (size_is >= 0 && size_is != content_size) {
348                         return ndr_pull_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PULL) size_is(%d) mismatch content_size %d", 
349                                                 size_is, content_size);
350                 }
351                 NDR_CHECK(ndr_pull_subcontext(ndr, ndr2, content_size));
352                 break;
353         }
354
355         case 4: {
356                 uint32_t content_size;
357                 NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &content_size));
358                 if (size_is >= 0 && size_is != content_size) {
359                         return ndr_pull_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PULL) size_is(%d) mismatch content_size %d", 
360                                                 size_is, content_size);
361                 }
362                 NDR_CHECK(ndr_pull_subcontext(ndr, ndr2, content_size));
363                 break;
364         }
365         default:
366                 return ndr_pull_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PULL) header_size %d", 
367                                       header_size);
368         }
369         return NT_STATUS_OK;
370 }
371
372 /*
373   push a subcontext header 
374 */
375 NTSTATUS ndr_push_subcontext_header(struct ndr_push *ndr, 
376                                            size_t header_size,
377                                            ssize_t size_is,
378                                            struct ndr_push *ndr2)
379 {
380         if (size_is >= 0) {
381                 ssize_t padding_len = size_is - ndr2->offset;
382                 if (padding_len > 0) {
383                         NDR_CHECK(ndr_push_zero(ndr2, padding_len));
384                 } else if (padding_len < 0) {
385                         return ndr_push_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PUSH) content_size %d is larger than size_is(%d)",
386                                               ndr2->offset, size_is);
387                 }
388         }
389
390         switch (header_size) {
391         case 0: 
392                 break;
393
394         case 2: 
395                 NDR_CHECK(ndr_push_uint16(ndr, NDR_SCALARS, ndr2->offset));
396                 break;
397
398         case 4: 
399                 NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, ndr2->offset));
400                 break;
401
402         default:
403                 return ndr_push_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext header size %d", 
404                                       header_size);
405         }
406         return NT_STATUS_OK;
407 }
408
409 /*
410   mark the start of a structure
411 */
412 NTSTATUS ndr_pull_struct_start(struct ndr_pull *ndr)
413 {
414         return NT_STATUS_OK;
415 }
416
417 /*
418   mark the end of a structure
419 */
420 void ndr_pull_struct_end(struct ndr_pull *ndr)
421 {
422 }
423
424 /*
425   mark the start of a structure
426 */
427 NTSTATUS ndr_push_struct_start(struct ndr_push *ndr)
428 {
429         return NT_STATUS_OK;
430 }
431
432 /*
433   mark the end of a structure
434 */
435 void ndr_push_struct_end(struct ndr_push *ndr)
436 {
437 }
438
439 /*
440   store a token in the ndr context, for later retrieval
441 */
442 static NTSTATUS ndr_token_store(TALLOC_CTX *mem_ctx, 
443                                 struct ndr_token_list **list, 
444                                 const void *key, 
445                                 uint32_t value)
446 {
447         struct ndr_token_list *tok;
448         tok = talloc(mem_ctx, struct ndr_token_list);
449         if (tok == NULL) {
450                 return NT_STATUS_NO_MEMORY;
451         }
452         tok->key = key;
453         tok->value = value;
454         DLIST_ADD((*list), tok);
455         return NT_STATUS_OK;
456 }
457
458 /*
459   retrieve a token from a ndr context
460 */
461 static NTSTATUS ndr_token_retrieve(struct ndr_token_list **list, const void *key, uint32_t *v)
462 {
463         struct ndr_token_list *tok;
464         for (tok=*list;tok;tok=tok->next) {
465                 if (tok->key == key) {
466                         DLIST_REMOVE((*list), tok);
467                         *v = tok->value;
468                         return NT_STATUS_OK;
469                 }
470         }
471         return ndr_map_error(NDR_ERR_TOKEN);
472 }
473
474 /*
475   peek at but don't removed a token from a ndr context
476 */
477 static uint32_t ndr_token_peek(struct ndr_token_list **list, const void *key)
478 {
479         struct ndr_token_list *tok;
480         for (tok=*list;tok;tok=tok->next) {
481                 if (tok->key == key) {
482                         return tok->value;
483                 }
484         }
485         return 0;
486 }
487
488 /*
489   pull an array size field and add it to the array_size_list token list
490 */
491 NTSTATUS ndr_pull_array_size(struct ndr_pull *ndr, const void *p)
492 {
493         uint32_t size;
494         NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &size));
495         return ndr_token_store(ndr, &ndr->array_size_list, p, size);
496 }
497
498 /*
499   get the stored array size field
500 */
501 uint32_t ndr_get_array_size(struct ndr_pull *ndr, const void *p)
502 {
503         return ndr_token_peek(&ndr->array_size_list, p);
504 }
505
506 /*
507   check the stored array size field
508 */
509 NTSTATUS ndr_check_array_size(struct ndr_pull *ndr, void *p, uint32_t size)
510 {
511         uint32_t stored;
512         stored = ndr_token_peek(&ndr->array_size_list, p);
513         if (stored != size) {
514                 return ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE, 
515                                       "Bad array size - got %u expected %u\n",
516                                       stored, size);
517         }
518         return NT_STATUS_OK;
519 }
520
521 /*
522   pull an array length field and add it to the array_length_list token list
523 */
524 NTSTATUS ndr_pull_array_length(struct ndr_pull *ndr, const void *p)
525 {
526         uint32_t length, offset;
527         NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &offset));
528         if (offset != 0) {
529                 return ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE, 
530                                       "non-zero array offset %u\n", offset);
531         }
532         NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &length));
533         return ndr_token_store(ndr, &ndr->array_length_list, p, length);
534 }
535
536 /*
537   get the stored array length field
538 */
539 uint32_t ndr_get_array_length(struct ndr_pull *ndr, const void *p)
540 {
541         return ndr_token_peek(&ndr->array_length_list, p);
542 }
543
544 /*
545   check the stored array length field
546 */
547 NTSTATUS ndr_check_array_length(struct ndr_pull *ndr, void *p, uint32_t length)
548 {
549         uint32_t stored;
550         stored = ndr_token_peek(&ndr->array_length_list, p);
551         if (stored != length) {
552                 return ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE, 
553                                       "Bad array length - got %u expected %u\n",
554                                       stored, length);
555         }
556         return NT_STATUS_OK;
557 }
558
559 /*
560   store a switch value
561  */
562 NTSTATUS ndr_push_set_switch_value(struct ndr_push *ndr, const void *p, uint32_t val)
563 {
564         return ndr_token_store(ndr, &ndr->switch_list, p, val);
565 }
566
567 NTSTATUS ndr_pull_set_switch_value(struct ndr_pull *ndr, const void *p, uint32_t val)
568 {
569         return ndr_token_store(ndr, &ndr->switch_list, p, val);
570 }
571
572 NTSTATUS ndr_print_set_switch_value(struct ndr_print *ndr, const void *p, uint32_t val)
573 {
574         return ndr_token_store(ndr, &ndr->switch_list, p, val);
575 }
576
577 /*
578   retrieve a switch value
579  */
580 uint32_t ndr_push_get_switch_value(struct ndr_push *ndr, const void *p)
581 {
582         return ndr_token_peek(&ndr->switch_list, p);
583 }
584
585 uint32_t ndr_pull_get_switch_value(struct ndr_pull *ndr, const void *p)
586 {
587         return ndr_token_peek(&ndr->switch_list, p);
588 }
589
590 uint32_t ndr_print_get_switch_value(struct ndr_print *ndr, const void *p)
591 {
592         return ndr_token_peek(&ndr->switch_list, p);
593 }
594
595 /*
596   pull a relative object - stage1
597   called during SCALARS processing
598 */
599 NTSTATUS ndr_pull_relative_ptr1(struct ndr_pull *ndr, const void *p, uint32_t rel_offset)
600 {
601         return ndr_token_store(ndr, &ndr->relative_list, p, rel_offset);
602 }
603
604 /*
605   pull a relative object - stage2
606   called during BUFFERS processing
607 */
608 NTSTATUS ndr_pull_relative_ptr2(struct ndr_pull *ndr, const void *p)
609 {
610         uint32_t rel_offset;
611         NDR_CHECK(ndr_token_retrieve(&ndr->relative_list, p, &rel_offset));
612         return ndr_pull_set_offset(ndr, rel_offset);
613 }
614
615 /*
616   push a relative object - stage1
617   this is called during SCALARS processing
618 */
619 NTSTATUS ndr_push_relative_ptr1(struct ndr_push *ndr, const void *p)
620 {
621         if (p == NULL) {
622                 NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, 0));
623                 return NT_STATUS_OK;
624         }
625         NDR_CHECK(ndr_push_align(ndr, 4));
626         NDR_CHECK(ndr_token_store(ndr, &ndr->relative_list, p, ndr->offset));
627         return ndr_push_uint32(ndr, NDR_SCALARS, 0xFFFFFFFF);
628 }
629
630 /*
631   push a relative object - stage2
632   this is called during buffers processing
633 */
634 NTSTATUS ndr_push_relative_ptr2(struct ndr_push *ndr, const void *p)
635 {
636         struct ndr_push_save save;
637         if (p == NULL) {
638                 return NT_STATUS_OK;
639         }
640         NDR_CHECK(ndr_push_align(ndr, 4));
641         ndr_push_save(ndr, &save);
642         NDR_CHECK(ndr_token_retrieve(&ndr->relative_list, p, &ndr->offset));
643         NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, save.offset));
644         ndr_push_restore(ndr, &save);
645         return NT_STATUS_OK;
646 }
647
648 /*
649   pull a struct from a blob using NDR
650 */
651 NTSTATUS ndr_pull_struct_blob(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p,
652                               ndr_pull_flags_fn_t fn)
653 {
654         struct ndr_pull *ndr;
655         ndr = ndr_pull_init_blob(blob, mem_ctx);
656         if (!ndr) {
657                 return NT_STATUS_NO_MEMORY;
658         }
659         return fn(ndr, NDR_SCALARS|NDR_BUFFERS, p);
660 }
661
662 /*
663   pull a struct from a blob using NDR - failing if all bytes are not consumed
664 */
665 NTSTATUS ndr_pull_struct_blob_all(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p,
666                                   ndr_pull_flags_fn_t fn)
667 {
668         struct ndr_pull *ndr;
669         NTSTATUS status;
670
671         ndr = ndr_pull_init_blob(blob, mem_ctx);
672         if (!ndr) {
673                 return NT_STATUS_NO_MEMORY;
674         }
675         status = fn(ndr, NDR_SCALARS|NDR_BUFFERS, p);
676         if (!NT_STATUS_IS_OK(status)) return status;
677         if (ndr->offset != ndr->data_size) {
678                 return NT_STATUS_BUFFER_TOO_SMALL;
679         }
680         return status;
681 }
682
683 /*
684   pull a union from a blob using NDR, given the union discriminator
685 */
686 NTSTATUS ndr_pull_union_blob(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p,
687                              uint32_t level, ndr_pull_flags_fn_t fn)
688 {
689         struct ndr_pull *ndr;
690         NTSTATUS status;
691
692         ndr = ndr_pull_init_blob(blob, mem_ctx);
693         if (!ndr) {
694                 return NT_STATUS_NO_MEMORY;
695         }
696         ndr_pull_set_switch_value(ndr, p, level);
697         status = fn(ndr, NDR_SCALARS|NDR_BUFFERS, p);
698         if (!NT_STATUS_IS_OK(status)) return status;
699         if (ndr->offset != ndr->data_size) {
700                 return NT_STATUS_BUFFER_TOO_SMALL;
701         }
702         return status;
703 }
704
705 /*
706   push a struct to a blob using NDR
707 */
708 NTSTATUS ndr_push_struct_blob(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p,
709                               ndr_push_flags_fn_t fn)
710 {
711         NTSTATUS status;
712         struct ndr_push *ndr;
713         ndr = ndr_push_init_ctx(mem_ctx);
714         if (!ndr) {
715                 return NT_STATUS_NO_MEMORY;
716         }
717         status = fn(ndr, NDR_SCALARS|NDR_BUFFERS, p);
718         if (!NT_STATUS_IS_OK(status)) {
719                 return status;
720         }
721
722         *blob = ndr_push_blob(ndr);
723
724         return NT_STATUS_OK;
725 }
726
727 /*
728   push a union to a blob using NDR
729 */
730 NTSTATUS ndr_push_union_blob(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p,
731                              uint32_t level, ndr_push_flags_fn_t fn)
732 {
733         NTSTATUS status;
734         struct ndr_push *ndr;
735         ndr = ndr_push_init_ctx(mem_ctx);
736         if (!ndr) {
737                 return NT_STATUS_NO_MEMORY;
738         }
739         ndr_push_set_switch_value(ndr, p, level);
740         status = fn(ndr, NDR_SCALARS|NDR_BUFFERS, p);
741         if (!NT_STATUS_IS_OK(status)) {
742                 return status;
743         }
744
745         *blob = ndr_push_blob(ndr);
746
747         return NT_STATUS_OK;
748 }
749
750 /*
751   generic ndr_size_*() handler for structures
752 */
753 size_t ndr_size_struct(const void *p, int flags, ndr_push_flags_fn_t push)
754 {
755         struct ndr_push *ndr;
756         NTSTATUS status;
757         size_t ret;
758
759         /* avoid recursion */
760         if (flags & LIBNDR_FLAG_NO_NDR_SIZE) return 0;
761
762         ndr = ndr_push_init_ctx(NULL);
763         if (!ndr) return 0;
764         ndr->flags |= flags | LIBNDR_FLAG_NO_NDR_SIZE;
765         status = push(ndr, NDR_SCALARS|NDR_BUFFERS, discard_const(p));
766         if (!NT_STATUS_IS_OK(status)) {
767                 return 0;
768         }
769         ret = ndr->offset;
770         talloc_free(ndr);
771         return ret;
772 }
773
774 /*
775   generic ndr_size_*() handler for unions
776 */
777 size_t ndr_size_union(const void *p, int flags, uint32_t level, ndr_push_flags_fn_t push)
778 {
779         struct ndr_push *ndr;
780         NTSTATUS status;
781         size_t ret;
782
783         /* avoid recursion */
784         if (flags & LIBNDR_FLAG_NO_NDR_SIZE) return 0;
785
786         ndr = ndr_push_init_ctx(NULL);
787         if (!ndr) return 0;
788         ndr->flags |= flags | LIBNDR_FLAG_NO_NDR_SIZE;
789         ndr_push_set_switch_value(ndr, p, level);
790         status = push(ndr, NDR_SCALARS|NDR_BUFFERS, discard_const(p));
791         if (!NT_STATUS_IS_OK(status)) {
792                 return 0;
793         }
794         ret = ndr->offset;
795         talloc_free(ndr);
796         return ret;
797 }