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