r6973: Merge new version of pidl into the main SAMBA_4_0 branch.
[samba.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         if (ndr->flags & LIBNDR_FLAG_RELATIVE_CURRENT) {
602                 return ndr_token_store(ndr, &ndr->relative_list, p, 
603                                        rel_offset + ndr->offset - 4);
604         } else {
605                 return ndr_token_store(ndr, &ndr->relative_list, p, rel_offset);
606         }
607 }
608
609 /*
610   pull a relative object - stage2
611   called during BUFFERS processing
612 */
613 NTSTATUS ndr_pull_relative_ptr2(struct ndr_pull *ndr, const void *p)
614 {
615         uint32_t rel_offset;
616         NDR_CHECK(ndr_token_retrieve(&ndr->relative_list, p, &rel_offset));
617         return ndr_pull_set_offset(ndr, rel_offset);
618 }
619
620 /*
621   push a relative object - stage1
622   this is called during SCALARS processing
623 */
624 NTSTATUS ndr_push_relative_ptr1(struct ndr_push *ndr, const void *p)
625 {
626         if (p == NULL) {
627                 NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, 0));
628                 return NT_STATUS_OK;
629         }
630         NDR_CHECK(ndr_push_align(ndr, 4));
631         NDR_CHECK(ndr_token_store(ndr, &ndr->relative_list, p, ndr->offset));
632         return ndr_push_uint32(ndr, NDR_SCALARS, 0xFFFFFFFF);
633 }
634
635 /*
636   push a relative object - stage2
637   this is called during buffers processing
638 */
639 NTSTATUS ndr_push_relative_ptr2(struct ndr_push *ndr, const void *p)
640 {
641         struct ndr_push_save save;
642         if (p == NULL) {
643                 return NT_STATUS_OK;
644         }
645         NDR_CHECK(ndr_push_align(ndr, 4));
646         ndr_push_save(ndr, &save);
647         NDR_CHECK(ndr_token_retrieve(&ndr->relative_list, p, &ndr->offset));
648         if (ndr->flags & LIBNDR_FLAG_RELATIVE_CURRENT) {
649                 NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, save.offset - ndr->offset));
650         } else {
651                 NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, save.offset));
652         }
653         ndr_push_restore(ndr, &save);
654         return NT_STATUS_OK;
655 }
656
657 /*
658   pull a struct from a blob using NDR
659 */
660 NTSTATUS ndr_pull_struct_blob(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p,
661                               ndr_pull_flags_fn_t fn)
662 {
663         struct ndr_pull *ndr;
664         ndr = ndr_pull_init_blob(blob, mem_ctx);
665         if (!ndr) {
666                 return NT_STATUS_NO_MEMORY;
667         }
668         return fn(ndr, NDR_SCALARS|NDR_BUFFERS, p);
669 }
670
671 /*
672   pull a struct from a blob using NDR - failing if all bytes are not consumed
673 */
674 NTSTATUS ndr_pull_struct_blob_all(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p,
675                                   ndr_pull_flags_fn_t fn)
676 {
677         struct ndr_pull *ndr;
678         NTSTATUS status;
679
680         ndr = ndr_pull_init_blob(blob, mem_ctx);
681         if (!ndr) {
682                 return NT_STATUS_NO_MEMORY;
683         }
684         status = fn(ndr, NDR_SCALARS|NDR_BUFFERS, p);
685         if (!NT_STATUS_IS_OK(status)) return status;
686         if (ndr->offset != ndr->data_size) {
687                 return NT_STATUS_BUFFER_TOO_SMALL;
688         }
689         return status;
690 }
691
692 /*
693   pull a union from a blob using NDR, given the union discriminator
694 */
695 NTSTATUS ndr_pull_union_blob(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p,
696                              uint32_t level, ndr_pull_flags_fn_t fn)
697 {
698         struct ndr_pull *ndr;
699         NTSTATUS status;
700
701         ndr = ndr_pull_init_blob(blob, mem_ctx);
702         if (!ndr) {
703                 return NT_STATUS_NO_MEMORY;
704         }
705         ndr_pull_set_switch_value(ndr, p, level);
706         status = fn(ndr, NDR_SCALARS|NDR_BUFFERS, p);
707         if (!NT_STATUS_IS_OK(status)) return status;
708         if (ndr->offset != ndr->data_size) {
709                 return NT_STATUS_BUFFER_TOO_SMALL;
710         }
711         return status;
712 }
713
714 /*
715   push a struct to a blob using NDR
716 */
717 NTSTATUS ndr_push_struct_blob(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p,
718                               ndr_push_flags_fn_t fn)
719 {
720         NTSTATUS status;
721         struct ndr_push *ndr;
722         ndr = ndr_push_init_ctx(mem_ctx);
723         if (!ndr) {
724                 return NT_STATUS_NO_MEMORY;
725         }
726         status = fn(ndr, NDR_SCALARS|NDR_BUFFERS, p);
727         if (!NT_STATUS_IS_OK(status)) {
728                 return status;
729         }
730
731         *blob = ndr_push_blob(ndr);
732
733         return NT_STATUS_OK;
734 }
735
736 /*
737   push a union to a blob using NDR
738 */
739 NTSTATUS ndr_push_union_blob(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p,
740                              uint32_t level, ndr_push_flags_fn_t fn)
741 {
742         NTSTATUS status;
743         struct ndr_push *ndr;
744         ndr = ndr_push_init_ctx(mem_ctx);
745         if (!ndr) {
746                 return NT_STATUS_NO_MEMORY;
747         }
748         ndr_push_set_switch_value(ndr, p, level);
749         status = fn(ndr, NDR_SCALARS|NDR_BUFFERS, p);
750         if (!NT_STATUS_IS_OK(status)) {
751                 return status;
752         }
753
754         *blob = ndr_push_blob(ndr);
755
756         return NT_STATUS_OK;
757 }
758
759 /*
760   generic ndr_size_*() handler for structures
761 */
762 size_t ndr_size_struct(const void *p, int flags, ndr_push_flags_fn_t push)
763 {
764         struct ndr_push *ndr;
765         NTSTATUS status;
766         size_t ret;
767
768         /* avoid recursion */
769         if (flags & LIBNDR_FLAG_NO_NDR_SIZE) return 0;
770
771         ndr = ndr_push_init_ctx(NULL);
772         if (!ndr) return 0;
773         ndr->flags |= flags | LIBNDR_FLAG_NO_NDR_SIZE;
774         status = push(ndr, NDR_SCALARS|NDR_BUFFERS, discard_const(p));
775         if (!NT_STATUS_IS_OK(status)) {
776                 return 0;
777         }
778         ret = ndr->offset;
779         talloc_free(ndr);
780         return ret;
781 }
782
783 /*
784   generic ndr_size_*() handler for unions
785 */
786 size_t ndr_size_union(const void *p, int flags, uint32_t level, ndr_push_flags_fn_t push)
787 {
788         struct ndr_push *ndr;
789         NTSTATUS status;
790         size_t ret;
791
792         /* avoid recursion */
793         if (flags & LIBNDR_FLAG_NO_NDR_SIZE) return 0;
794
795         ndr = ndr_push_init_ctx(NULL);
796         if (!ndr) return 0;
797         ndr->flags |= flags | LIBNDR_FLAG_NO_NDR_SIZE;
798         ndr_push_set_switch_value(ndr, p, level);
799         status = push(ndr, NDR_SCALARS|NDR_BUFFERS, discard_const(p));
800         if (!NT_STATUS_IS_OK(status)) {
801                 return 0;
802         }
803         ret = ndr->offset;
804         talloc_free(ndr);
805         return ret;
806 }