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