205607e0cd6817c10f030bda1760668a99011b1d
[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         ndr.flags = 0;
355         fn(&ndr, name, flags, ptr);
356         talloc_destroy(ndr.mem_ctx);
357 }
358
359
360 static NTSTATUS ndr_map_error(enum ndr_err_code err)
361 {
362         switch (err) {
363         case NDR_ERR_BUFSIZE:
364                 return NT_STATUS_BUFFER_TOO_SMALL;
365         case NDR_ERR_ALLOC:
366                 return NT_STATUS_NO_MEMORY;
367         }
368
369         /* we should all error codes to different status codes */
370         return NT_STATUS_INVALID_PARAMETER;
371 }
372
373 /*
374   return and possibly log an NDR error
375 */
376 NTSTATUS ndr_pull_error(struct ndr_pull *ndr, enum ndr_err_code err, const char *format, ...)
377 {
378         char *s=NULL;
379         va_list ap;
380
381         va_start(ap, format);
382         vasprintf(&s, format, ap);
383         va_end(ap);
384
385         DEBUG(3,("ndr_pull_error(%u): %s\n", err, s));
386
387         free(s);
388
389         return ndr_map_error(err);
390 }
391
392 /*
393   return and possibly log an NDR error
394 */
395 NTSTATUS ndr_push_error(struct ndr_push *ndr, enum ndr_err_code err, const char *format, ...)
396 {
397         char *s=NULL;
398         va_list ap;
399
400         va_start(ap, format);
401         vasprintf(&s, format, ap);
402         va_end(ap);
403
404         DEBUG(3,("ndr_push_error(%u): %s\n", err, s));
405
406         free(s);
407
408         return ndr_map_error(err);
409 }
410
411
412 /*
413   handle subcontext buffers, which in midl land are user-marshalled, but
414   we use magic in pidl to make them easier to cope with
415 */
416 static NTSTATUS ndr_pull_subcontext_header(struct ndr_pull *ndr, 
417                                            size_t sub_size,
418                                            struct ndr_pull *ndr2)
419 {
420         switch (sub_size) {
421         case 0: {
422                 uint32 size = ndr->data_size - ndr->offset;
423                 if (size == 0) return NT_STATUS_OK;
424                 NDR_CHECK(ndr_pull_subcontext(ndr, ndr2, size));
425                 break;
426         }
427
428         case 2: {
429                 uint16 size;
430                 NDR_CHECK(ndr_pull_uint16(ndr, &size));
431                 if (size == 0) return NT_STATUS_OK;
432                 NDR_CHECK(ndr_pull_subcontext(ndr, ndr2, size));
433                 break;
434         }
435
436         case 4: {
437                 uint32 size;
438                 NDR_CHECK(ndr_pull_uint32(ndr, &size));
439                 if (size == 0) return NT_STATUS_OK;
440                 NDR_CHECK(ndr_pull_subcontext(ndr, ndr2, size));
441                 break;
442         }
443         default:
444                 return ndr_pull_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext size %d", 
445                                       sub_size);
446         }
447         return NT_STATUS_OK;
448 }
449
450 /*
451   handle subcontext buffers, which in midl land are user-marshalled, but
452   we use magic in pidl to make them easier to cope with
453 */
454 NTSTATUS ndr_pull_subcontext_fn(struct ndr_pull *ndr, 
455                                 size_t sub_size,
456                                 void *base,
457                                 NTSTATUS (*fn)(struct ndr_pull *, void *))
458 {
459         struct ndr_pull ndr2;
460
461         NDR_CHECK(ndr_pull_subcontext_header(ndr, sub_size, &ndr2));
462         NDR_CHECK(fn(&ndr2, base));
463         if (sub_size) {
464                 NDR_CHECK(ndr_pull_advance(ndr, ndr2.data_size));
465         } else {
466                 NDR_CHECK(ndr_pull_advance(ndr, ndr2.offset));
467         }
468         return NT_STATUS_OK;
469 }
470
471
472 NTSTATUS ndr_pull_subcontext_flags_fn(struct ndr_pull *ndr, 
473                                       size_t sub_size,
474                                       void *base,
475                                       NTSTATUS (*fn)(struct ndr_pull *, int , void *))
476 {
477         struct ndr_pull ndr2;
478
479         NDR_CHECK(ndr_pull_subcontext_header(ndr, sub_size, &ndr2));
480         NDR_CHECK(fn(&ndr2, NDR_SCALARS|NDR_BUFFERS, base));
481         if (sub_size) {
482                 NDR_CHECK(ndr_pull_advance(ndr, ndr2.data_size));
483         } else {
484                 NDR_CHECK(ndr_pull_advance(ndr, ndr2.offset));
485         }
486         return NT_STATUS_OK;
487 }
488
489 NTSTATUS ndr_pull_subcontext_union_fn(struct ndr_pull *ndr, 
490                                       size_t sub_size,
491                                       uint32 level,
492                                       void *base,
493                                       NTSTATUS (*fn)(struct ndr_pull *, int , uint32 , void *))
494 {
495         struct ndr_pull ndr2;
496
497         NDR_CHECK(ndr_pull_subcontext_header(ndr, sub_size, &ndr2));
498         NDR_CHECK(fn(&ndr2, NDR_SCALARS|NDR_BUFFERS, level, base));
499         if (sub_size) {
500                 NDR_CHECK(ndr_pull_advance(ndr, ndr2.data_size));
501         } else {
502                 NDR_CHECK(ndr_pull_advance(ndr, ndr2.offset));
503         }
504         return NT_STATUS_OK;
505 }
506
507
508 /*
509   push a subcontext header 
510 */
511 static NTSTATUS ndr_push_subcontext_header(struct ndr_push *ndr, 
512                                            size_t sub_size,
513                                            struct ndr_push *ndr2)
514 {
515         switch (sub_size) {
516         case 0: 
517                 break;
518
519         case 2: 
520                 NDR_CHECK(ndr_push_uint16(ndr, ndr2->offset));
521                 break;
522
523         case 4: 
524                 NDR_CHECK(ndr_push_uint32(ndr, ndr2->offset));
525                 break;
526
527         default:
528                 return ndr_push_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext size %d", 
529                                       sub_size);
530         }
531         return NT_STATUS_OK;
532 }
533
534 /*
535   handle subcontext buffers, which in midl land are user-marshalled, but
536   we use magic in pidl to make them easier to cope with
537 */
538 NTSTATUS ndr_push_subcontext_fn(struct ndr_push *ndr, 
539                                 size_t sub_size,
540                                 void *base,
541                                 NTSTATUS (*fn)(struct ndr_push *, void *))
542 {
543         struct ndr_push *ndr2;
544
545         ndr2 = ndr_push_init_ctx(ndr->mem_ctx);
546         if (!ndr2) return NT_STATUS_NO_MEMORY;
547
548         ndr2->flags = ndr->flags;
549         NDR_CHECK(fn(ndr2, base));
550         NDR_CHECK(ndr_push_subcontext_header(ndr, sub_size, ndr2));
551         NDR_CHECK(ndr_push_bytes(ndr, ndr2->data, ndr2->offset));
552         return NT_STATUS_OK;
553 }
554
555 /*
556   handle subcontext buffers for function that take a flags arg
557 */
558 NTSTATUS ndr_push_subcontext_flags_fn(struct ndr_push *ndr, 
559                                       size_t sub_size,
560                                       void *base,
561                                       NTSTATUS (*fn)(struct ndr_push *, int, void *))
562 {
563         struct ndr_push *ndr2;
564
565         ndr2 = ndr_push_init_ctx(ndr->mem_ctx);
566         if (!ndr2) return NT_STATUS_NO_MEMORY;
567
568         ndr2->flags = ndr->flags;
569         NDR_CHECK(fn(ndr2, NDR_SCALARS|NDR_BUFFERS, base));
570         NDR_CHECK(ndr_push_subcontext_header(ndr, sub_size, ndr2));
571         NDR_CHECK(ndr_push_bytes(ndr, ndr2->data, ndr2->offset));
572         return NT_STATUS_OK;
573 }
574
575 /*
576   handle subcontext buffers for function that take a union
577 */
578 NTSTATUS ndr_push_subcontext_union_fn(struct ndr_push *ndr, 
579                                       size_t sub_size,
580                                       uint32 level,
581                                       void *base,
582                                       NTSTATUS (*fn)(struct ndr_push *, int, uint32, void *))
583 {
584         struct ndr_push *ndr2;
585
586         ndr2 = ndr_push_init_ctx(ndr->mem_ctx);
587         if (!ndr2) return NT_STATUS_NO_MEMORY;
588
589         ndr2->flags = ndr->flags;
590         NDR_CHECK(fn(ndr2, NDR_SCALARS|NDR_BUFFERS, level, base));
591         NDR_CHECK(ndr_push_subcontext_header(ndr, sub_size, ndr2));
592         NDR_CHECK(ndr_push_bytes(ndr, ndr2->data, ndr2->offset));
593         return NT_STATUS_OK;
594 }
595
596
597 /*
598   mark the start of a structure
599 */
600 NTSTATUS ndr_pull_struct_start(struct ndr_pull *ndr)
601 {
602         struct ndr_ofs_list *ofs;
603         NDR_ALLOC(ndr, ofs);
604         ofs->offset = ndr->offset;
605         ofs->next = ndr->ofs_list;
606         ndr->ofs_list = ofs;
607         return NT_STATUS_OK;
608 }
609
610 /*
611   mark the end of a structure
612 */
613 void ndr_pull_struct_end(struct ndr_pull *ndr)
614 {
615         ndr->ofs_list = ndr->ofs_list->next;
616 }
617
618 /*
619   mark the start of a structure
620 */
621 NTSTATUS ndr_push_struct_start(struct ndr_push *ndr)
622 {
623         struct ndr_ofs_list *ofs;
624         NDR_PUSH_ALLOC(ndr, ofs);
625         ofs->offset = ndr->offset;
626         ofs->next = ndr->ofs_list;
627         ndr->ofs_list = ofs;
628         return NT_STATUS_OK;
629 }
630
631 /*
632   mark the end of a structure
633 */
634 void ndr_push_struct_end(struct ndr_push *ndr)
635 {
636         ndr->ofs_list = ndr->ofs_list->next;
637 }
638
639
640 /*
641   pull a relative structure
642 */
643 NTSTATUS ndr_pull_relative(struct ndr_pull *ndr, const void **buf, size_t size, 
644                            NTSTATUS (*fn)(struct ndr_pull *, int ndr_flags, void *))
645 {
646         struct ndr_pull ndr2;
647         uint32 ofs;
648         struct ndr_pull_save save;
649         void *p;
650
651         NDR_CHECK(ndr_pull_uint32(ndr, &ofs));
652         if (ofs == 0) {
653                 (*buf) = NULL;
654                 return NT_STATUS_OK;
655         }
656         ndr_pull_save(ndr, &save);
657         NDR_CHECK(ndr_pull_set_offset(ndr, ofs + ndr->ofs_list->offset));
658         NDR_CHECK(ndr_pull_subcontext(ndr, &ndr2, ndr->data_size - ndr->offset));
659         /* strings must be allocated by the backend functions */
660         if (ndr->flags & LIBNDR_STRING_FLAGS) {
661                 NDR_CHECK(fn(&ndr2, NDR_SCALARS|NDR_BUFFERS, &p));
662         } else {
663                 NDR_ALLOC_SIZE(ndr, p, size);
664                 NDR_CHECK(fn(&ndr2, NDR_SCALARS|NDR_BUFFERS, p));
665         }
666         (*buf) = p;
667         ndr_pull_restore(ndr, &save);
668         return NT_STATUS_OK;
669 }
670
671 /*
672   push a relative structure
673 */
674 NTSTATUS ndr_push_relative(struct ndr_push *ndr, int ndr_flags, const void *p, 
675                            NTSTATUS (*fn)(struct ndr_push *, int , const void *))
676 {
677         struct ndr_ofs_list *ofs;
678         if (ndr_flags & NDR_SCALARS) {
679                 if (!p) {
680                         NDR_CHECK(ndr_push_uint32(ndr, 0));
681                         return NT_STATUS_OK;
682                 }
683                 NDR_PUSH_ALLOC(ndr, ofs);
684                 NDR_CHECK(ndr_push_align(ndr, 4));
685                 ofs->offset = ndr->offset;
686                 NDR_CHECK(ndr_push_uint32(ndr, 0xFFFFFFFF));
687                 ofs->next = NULL;
688                 if (ndr->relative_list_end) {
689                         ndr->relative_list_end->next = ofs;
690                 } else {
691                         ndr->relative_list = ofs;
692                 }
693                 ndr->relative_list_end = ofs;
694         }
695         if (ndr_flags & NDR_BUFFERS) {
696                 struct ndr_push_save save;
697                 if (!p) {
698                         return NT_STATUS_OK;
699                 }
700                 ofs = ndr->relative_list;
701                 if (!ofs) {
702                         return ndr_push_error(ndr, NDR_ERR_RELATIVE, "Empty relative stack");
703                 }
704                 ndr->relative_list = ndr->relative_list->next;
705                 if (ndr->relative_list == NULL) {
706                         ndr->relative_list_end = NULL;
707                 }
708                 NDR_CHECK(ndr_push_align(ndr, 4));
709                 ndr_push_save(ndr, &save);
710                 ndr->offset = ofs->offset;
711                 NDR_CHECK(ndr_push_uint32(ndr, save.offset - ndr->ofs_list->offset));
712                 ndr_push_restore(ndr, &save);
713                 NDR_CHECK(fn(ndr, NDR_SCALARS|NDR_BUFFERS, p));
714         }
715         return NT_STATUS_OK;
716 }
717
718
719 /*
720   pull a union from a blob using NDR
721 */
722 NTSTATUS ndr_pull_union_blob(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, uint32 level, void *p,
723                              NTSTATUS (*fn)(struct ndr_pull *, int ndr_flags, uint32, void *))
724 {
725         struct ndr_pull *ndr;
726         ndr = ndr_pull_init_blob(blob, mem_ctx);
727         if (!ndr) {
728                 return NT_STATUS_NO_MEMORY;
729         }
730         return fn(ndr, NDR_SCALARS|NDR_BUFFERS, level, p);
731 }
732
733 /*
734   pull a struct from a blob using NDR
735 */
736 NTSTATUS ndr_pull_struct_blob(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p,
737                               NTSTATUS (*fn)(struct ndr_pull *, int , void *))
738 {
739         struct ndr_pull *ndr;
740         ndr = ndr_pull_init_blob(blob, mem_ctx);
741         if (!ndr) {
742                 return NT_STATUS_NO_MEMORY;
743         }
744         return fn(ndr, NDR_SCALARS|NDR_BUFFERS, p);
745 }
746
747 /*
748   push a struct to a blob using NDR
749 */
750 NTSTATUS ndr_push_struct_blob(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p,
751                               NTSTATUS (*fn)(struct ndr_push *, int , void *))
752 {
753         NTSTATUS status;
754         struct ndr_push *ndr;
755         ndr = ndr_push_init_ctx(mem_ctx);
756         if (!ndr) {
757                 return NT_STATUS_NO_MEMORY;
758         }
759         status = fn(ndr, NDR_SCALARS|NDR_BUFFERS, p);
760         if (!NT_STATUS_IS_OK(status)) {
761                 return status;
762         }
763
764         *blob = ndr_push_blob(ndr);
765
766         return NT_STATUS_OK;
767 }