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