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