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