r10141: if some of the LIBNDR_ALIGN_* flags and LIBNDR_FLAG_REMAINING are set,
[ira/wip.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 #include "dlinklist.h"
32
33 #define NDR_BASE_MARSHALL_SIZE 1024
34
35 /*
36   work out the number of bytes needed to align on a n byte boundary
37 */
38 size_t ndr_align_size(uint32_t offset, size_t n)
39 {
40         if ((offset & (n-1)) == 0) return 0;
41         return n - (offset & (n-1));
42 }
43
44 /*
45   initialise a ndr parse structure from a data blob
46 */
47 struct ndr_pull *ndr_pull_init_blob(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
48 {
49         struct ndr_pull *ndr;
50
51         ndr = talloc_zero(mem_ctx, struct ndr_pull);
52         if (!ndr) return NULL;
53         ndr->current_mem_ctx = mem_ctx;
54
55         ndr->data = blob->data;
56         ndr->data_size = blob->length;
57
58         return ndr;
59 }
60
61 /*
62   advance by 'size' bytes
63 */
64 NTSTATUS ndr_pull_advance(struct ndr_pull *ndr, uint32_t size)
65 {
66         ndr->offset += size;
67         if (ndr->offset > ndr->data_size) {
68                 return ndr_pull_error(ndr, NDR_ERR_BUFSIZE, 
69                                       "ndr_pull_advance by %u failed",
70                                       size);
71         }
72         return NT_STATUS_OK;
73 }
74
75 /*
76   set the parse offset to 'ofs'
77 */
78 static NTSTATUS ndr_pull_set_offset(struct ndr_pull *ndr, uint32_t ofs)
79 {
80         ndr->offset = ofs;
81         if (ndr->offset > ndr->data_size) {
82                 return ndr_pull_error(ndr, NDR_ERR_BUFSIZE, 
83                                       "ndr_pull_set_offset %u failed",
84                                       ofs);
85         }
86         return NT_STATUS_OK;
87 }
88
89 /* save the offset/size of the current ndr state */
90 void ndr_pull_save(struct ndr_pull *ndr, struct ndr_pull_save *save)
91 {
92         save->offset = ndr->offset;
93         save->data_size = ndr->data_size;
94 }
95
96 /* restore the size/offset of a ndr structure */
97 void ndr_pull_restore(struct ndr_pull *ndr, struct ndr_pull_save *save)
98 {
99         ndr->offset = save->offset;
100         ndr->data_size = save->data_size;
101 }
102
103
104 /* create a ndr_push structure, ready for some marshalling */
105 struct ndr_push *ndr_push_init_ctx(TALLOC_CTX *mem_ctx)
106 {
107         struct ndr_push *ndr;
108
109         ndr = talloc_zero(mem_ctx, struct ndr_push);
110         if (!ndr) {
111                 return NULL;
112         }
113
114         ndr->flags = 0;
115         ndr->alloc_size = NDR_BASE_MARSHALL_SIZE;
116         ndr->data = talloc_array(ndr, uint8_t, ndr->alloc_size);
117         if (!ndr->data) {
118                 return NULL;
119         }
120
121         return ndr;
122 }
123
124
125 /* create a ndr_push structure, ready for some marshalling */
126 struct ndr_push *ndr_push_init(void)
127 {
128         return ndr_push_init_ctx(NULL);
129 }
130
131 /* free a ndr_push structure */
132 void ndr_push_free(struct ndr_push *ndr)
133 {
134         talloc_free(ndr);
135 }
136
137
138 /* return a DATA_BLOB structure for the current ndr_push marshalled data */
139 DATA_BLOB ndr_push_blob(struct ndr_push *ndr)
140 {
141         DATA_BLOB blob;
142         blob.data = ndr->data;
143         blob.length = ndr->offset;
144         if (ndr->alloc_size > ndr->offset) {
145                 ndr->data[ndr->offset] = 0;
146         }
147         return blob;
148 }
149
150
151 /*
152   expand the available space in the buffer to 'size'
153 */
154 NTSTATUS ndr_push_expand(struct ndr_push *ndr, uint32_t size)
155 {
156         if (ndr->alloc_size > size) {
157                 return NT_STATUS_OK;
158         }
159
160         ndr->alloc_size += NDR_BASE_MARSHALL_SIZE;
161         if (size+1 > ndr->alloc_size) {
162                 ndr->alloc_size = size+1;
163         }
164         ndr->data = talloc_realloc(ndr, ndr->data, uint8_t, ndr->alloc_size);
165         if (!ndr->data) {
166                 return ndr_push_error(ndr, NDR_ERR_ALLOC, "Failed to push_expand to %u",
167                                       ndr->alloc_size);
168         }
169
170         return NT_STATUS_OK;
171 }
172
173 void ndr_print_debug_helper(struct ndr_print *ndr, const char *format, ...) _PRINTF_ATTRIBUTE(2,3)
174 {
175         va_list ap;
176         char *s = NULL;
177         int i;
178
179         va_start(ap, format);
180         vasprintf(&s, format, ap);
181         va_end(ap);
182
183         for (i=0;i<ndr->depth;i++) {
184                 DEBUG(0,("    "));
185         }
186
187         DEBUG(0,("%s\n", s));
188         free(s);
189 }
190
191 /*
192   a useful helper function for printing idl structures via DEBUG()
193 */
194 void ndr_print_debug(ndr_print_fn_t fn, const char *name, void *ptr)
195 {
196         struct ndr_print *ndr;
197
198         ndr = talloc_zero(NULL, struct ndr_print);
199         if (!ndr) return;
200         ndr->print = ndr_print_debug_helper;
201         ndr->depth = 1;
202         ndr->flags = 0;
203         fn(ndr, name, ptr);
204         talloc_free(ndr);
205 }
206
207 /*
208   a useful helper function for printing idl unions via DEBUG()
209 */
210 void ndr_print_union_debug(ndr_print_fn_t fn, const char *name, uint32_t level, void *ptr)
211 {
212         struct ndr_print *ndr;
213
214         ndr = talloc_zero(NULL, struct ndr_print);
215         if (!ndr) return;
216         ndr->print = ndr_print_debug_helper;
217         ndr->depth = 1;
218         ndr->flags = 0;
219         ndr_print_set_switch_value(ndr, ptr, level);
220         fn(ndr, name, ptr);
221         talloc_free(ndr);
222 }
223
224 /*
225   a useful helper function for printing idl function calls via DEBUG()
226 */
227 void ndr_print_function_debug(ndr_print_function_t fn, const char *name, int flags, void *ptr)
228 {
229         struct ndr_print *ndr;
230
231         ndr = talloc_zero(NULL, struct ndr_print);
232         if (!ndr) return;
233         ndr->print = ndr_print_debug_helper;
234         ndr->depth = 1;
235         ndr->flags = 0;
236         fn(ndr, name, flags, ptr);
237         talloc_free(ndr);
238 }
239
240 void ndr_set_flags(uint32_t *pflags, uint32_t new_flags)
241 {
242         /* the big/little endian flags are inter-dependent */
243         if (new_flags & LIBNDR_FLAG_LITTLE_ENDIAN) {
244                 (*pflags) &= ~LIBNDR_FLAG_BIGENDIAN;
245         }
246         if (new_flags & LIBNDR_FLAG_BIGENDIAN) {
247                 (*pflags) &= ~LIBNDR_FLAG_LITTLE_ENDIAN;
248         }
249         if (new_flags & LIBNDR_FLAG_REMAINING) {
250                 (*pflags) &= ~LIBNDR_ALIGN_FLAGS;
251         }
252         if (new_flags & LIBNDR_ALIGN_FLAGS) {
253                 (*pflags) &= ~LIBNDR_FLAG_REMAINING;
254         }
255         (*pflags) |= new_flags;
256 }
257
258 static NTSTATUS ndr_map_error(enum ndr_err_code err)
259 {
260         switch (err) {
261         case NDR_ERR_BUFSIZE:
262                 return NT_STATUS_BUFFER_TOO_SMALL;
263         case NDR_ERR_TOKEN:
264                 return NT_STATUS_INTERNAL_ERROR;
265         case NDR_ERR_ALLOC:
266                 return NT_STATUS_NO_MEMORY;
267         case NDR_ERR_ARRAY_SIZE:
268                 return NT_STATUS_ARRAY_BOUNDS_EXCEEDED;
269         default:
270                 break;
271         }
272
273         /* we should map all error codes to different status codes */
274         return NT_STATUS_INVALID_PARAMETER;
275 }
276
277 /*
278   return and possibly log an NDR error
279 */
280 NTSTATUS ndr_pull_error(struct ndr_pull *ndr, 
281                         enum ndr_err_code err, const char *format, ...) _PRINTF_ATTRIBUTE(3,4)
282 {
283         char *s=NULL;
284         va_list ap;
285
286         va_start(ap, format);
287         vasprintf(&s, format, ap);
288         va_end(ap);
289
290         DEBUG(3,("ndr_pull_error(%u): %s\n", err, s));
291
292         free(s);
293
294         return ndr_map_error(err);
295 }
296
297 /*
298   return and possibly log an NDR error
299 */
300 NTSTATUS ndr_push_error(struct ndr_push *ndr, enum ndr_err_code err, const char *format, ...)  _PRINTF_ATTRIBUTE(3,4)
301 {
302         char *s=NULL;
303         va_list ap;
304
305         va_start(ap, format);
306         vasprintf(&s, format, ap);
307         va_end(ap);
308
309         DEBUG(3,("ndr_push_error(%u): %s\n", err, s));
310
311         free(s);
312
313         return ndr_map_error(err);
314 }
315
316 /*
317   handle subcontext buffers, which in midl land are user-marshalled, but
318   we use magic in pidl to make them easier to cope with
319 */
320 NTSTATUS ndr_pull_subcontext_start(struct ndr_pull *ndr, 
321                                    struct ndr_pull **_subndr,
322                                    size_t header_size,
323                                    ssize_t size_is)
324 {
325         struct ndr_pull *subndr;
326         uint32_t r_content_size;
327
328         switch (header_size) {
329         case 0: {
330                 uint32_t content_size = ndr->data_size - ndr->offset;
331                 if (size_is >= 0) {
332                         content_size = size_is;
333                 }
334                 r_content_size = content_size;
335                 break;
336         }
337
338         case 2: {
339                 uint16_t content_size;
340                 NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &content_size));
341                 if (size_is >= 0 && size_is != content_size) {
342                         return ndr_pull_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PULL) size_is(%d) mismatch content_size %d", 
343                                                 (int)size_is, (int)content_size);
344                 }
345                 r_content_size = content_size;
346                 break;
347         }
348
349         case 4: {
350                 uint32_t content_size;
351                 NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &content_size));
352                 if (size_is >= 0 && size_is != content_size) {
353                         return ndr_pull_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PULL) size_is(%d) mismatch content_size %d", 
354                                                 (int)size_is, (int)content_size);
355                 }
356                 r_content_size = content_size;
357                 break;
358         }
359         default:
360                 return ndr_pull_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PULL) header_size %d", 
361                                       (int)header_size);
362         }
363
364         NDR_PULL_NEED_BYTES(ndr, r_content_size);
365
366         subndr = talloc_zero(ndr, struct ndr_pull);
367         NT_STATUS_HAVE_NO_MEMORY(subndr);
368         subndr->flags           = ndr->flags;
369         subndr->current_mem_ctx = ndr->current_mem_ctx;
370
371         subndr->data = ndr->data + ndr->offset;
372         subndr->offset = 0;
373         subndr->data_size = r_content_size;
374
375         *_subndr = subndr;
376         return NT_STATUS_OK;
377 }
378
379 NTSTATUS ndr_pull_subcontext_end(struct ndr_pull *ndr, 
380                                  struct ndr_pull *subndr,
381                                  size_t header_size,
382                                  ssize_t size_is)
383 {
384         uint32_t advance;
385         if (size_is >= 0) {
386                 advance = size_is;
387         } else if (header_size > 0) {
388                 advance = subndr->data_size;
389         } else {
390                 advance = subndr->offset;
391         }
392         NDR_CHECK(ndr_pull_advance(ndr, advance));
393         return NT_STATUS_OK;
394 }
395
396 NTSTATUS ndr_push_subcontext_start(struct ndr_push *ndr,
397                                    struct ndr_push **_subndr,
398                                    size_t header_size,
399                                    ssize_t size_is)
400 {
401         struct ndr_push *subndr;
402
403         subndr = ndr_push_init_ctx(ndr);
404         NT_STATUS_HAVE_NO_MEMORY(subndr);
405         subndr->flags   = ndr->flags;
406
407         *_subndr = subndr;
408         return NT_STATUS_OK;
409 }
410
411 /*
412   push a subcontext header 
413 */
414 NTSTATUS ndr_push_subcontext_end(struct ndr_push *ndr,
415                                  struct ndr_push *subndr,
416                                  size_t header_size,
417                                  ssize_t size_is)
418 {
419         if (size_is >= 0) {
420                 ssize_t padding_len = size_is - subndr->offset;
421                 if (padding_len > 0) {
422                         NDR_CHECK(ndr_push_zero(subndr, padding_len));
423                 } else if (padding_len < 0) {
424                         return ndr_push_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PUSH) content_size %d is larger than size_is(%d)",
425                                               (int)subndr->offset, (int)size_is);
426                 }
427         }
428
429         switch (header_size) {
430         case 0: 
431                 break;
432
433         case 2: 
434                 NDR_CHECK(ndr_push_uint16(ndr, NDR_SCALARS, subndr->offset));
435                 break;
436
437         case 4: 
438                 NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, subndr->offset));
439                 break;
440
441         default:
442                 return ndr_push_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext header size %d", 
443                                       (int)header_size);
444         }
445
446         NDR_CHECK(ndr_push_bytes(ndr, subndr->data, subndr->offset));
447         return NT_STATUS_OK;
448 }
449
450 /*
451   store a token in the ndr context, for later retrieval
452 */
453 NTSTATUS ndr_token_store(TALLOC_CTX *mem_ctx, 
454                          struct ndr_token_list **list, 
455                          const void *key, 
456                          uint32_t value)
457 {
458         struct ndr_token_list *tok;
459         tok = talloc(mem_ctx, struct ndr_token_list);
460         if (tok == NULL) {
461                 return NT_STATUS_NO_MEMORY;
462         }
463         tok->key = key;
464         tok->value = value;
465         DLIST_ADD((*list), tok);
466         return NT_STATUS_OK;
467 }
468
469 /*
470   retrieve a token from a ndr context, using cmp_fn to match the tokens
471 */
472 NTSTATUS ndr_token_retrieve_cmp_fn(struct ndr_token_list **list, const void *key, uint32_t *v,
473                                    comparison_fn_t _cmp_fn, BOOL _remove_tok)
474 {
475         struct ndr_token_list *tok;
476         for (tok=*list;tok;tok=tok->next) {
477                 if (_cmp_fn && _cmp_fn(tok->key,key)==0) goto found;
478                 else if (!_cmp_fn && tok->key == key) goto found;
479         }
480         return ndr_map_error(NDR_ERR_TOKEN);
481 found:
482         *v = tok->value;
483         if (_remove_tok) {
484                 DLIST_REMOVE((*list), tok);
485                 talloc_free(tok);
486         }
487         return NT_STATUS_OK;            
488 }
489
490 /*
491   retrieve a token from a ndr context
492 */
493 NTSTATUS ndr_token_retrieve(struct ndr_token_list **list, const void *key, uint32_t *v)
494 {
495         return ndr_token_retrieve_cmp_fn(list, key, v, NULL, True);
496 }
497
498 /*
499   peek at but don't removed a token from a ndr context
500 */
501 uint32_t ndr_token_peek(struct ndr_token_list **list, const void *key)
502 {
503         NTSTATUS status;
504         uint32_t v;
505         status = ndr_token_retrieve_cmp_fn(list, key, &v, NULL, False);
506         if (NT_STATUS_IS_OK(status)) return v;
507         return 0;
508 }
509
510 /*
511   pull an array size field and add it to the array_size_list token list
512 */
513 NTSTATUS ndr_pull_array_size(struct ndr_pull *ndr, const void *p)
514 {
515         uint32_t size;
516         NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &size));
517         return ndr_token_store(ndr, &ndr->array_size_list, p, size);
518 }
519
520 /*
521   get the stored array size field
522 */
523 uint32_t ndr_get_array_size(struct ndr_pull *ndr, const void *p)
524 {
525         return ndr_token_peek(&ndr->array_size_list, p);
526 }
527
528 /*
529   check the stored array size field
530 */
531 NTSTATUS ndr_check_array_size(struct ndr_pull *ndr, void *p, uint32_t size)
532 {
533         uint32_t stored;
534         stored = ndr_token_peek(&ndr->array_size_list, p);
535         if (stored != size) {
536                 return ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE, 
537                                       "Bad array size - got %u expected %u\n",
538                                       stored, size);
539         }
540         return NT_STATUS_OK;
541 }
542
543 /*
544   pull an array length field and add it to the array_length_list token list
545 */
546 NTSTATUS ndr_pull_array_length(struct ndr_pull *ndr, const void *p)
547 {
548         uint32_t length, offset;
549         NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &offset));
550         if (offset != 0) {
551                 return ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE, 
552                                       "non-zero array offset %u\n", offset);
553         }
554         NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &length));
555         return ndr_token_store(ndr, &ndr->array_length_list, p, length);
556 }
557
558 /*
559   get the stored array length field
560 */
561 uint32_t ndr_get_array_length(struct ndr_pull *ndr, const void *p)
562 {
563         return ndr_token_peek(&ndr->array_length_list, p);
564 }
565
566 /*
567   check the stored array length field
568 */
569 NTSTATUS ndr_check_array_length(struct ndr_pull *ndr, void *p, uint32_t length)
570 {
571         uint32_t stored;
572         stored = ndr_token_peek(&ndr->array_length_list, p);
573         if (stored != length) {
574                 return ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE, 
575                                       "Bad array length - got %u expected %u\n",
576                                       stored, length);
577         }
578         return NT_STATUS_OK;
579 }
580
581 /*
582   store a switch value
583  */
584 NTSTATUS ndr_push_set_switch_value(struct ndr_push *ndr, const void *p, uint32_t val)
585 {
586         return ndr_token_store(ndr, &ndr->switch_list, p, val);
587 }
588
589 NTSTATUS ndr_pull_set_switch_value(struct ndr_pull *ndr, const void *p, uint32_t val)
590 {
591         return ndr_token_store(ndr, &ndr->switch_list, p, val);
592 }
593
594 NTSTATUS ndr_print_set_switch_value(struct ndr_print *ndr, const void *p, uint32_t val)
595 {
596         return ndr_token_store(ndr, &ndr->switch_list, p, val);
597 }
598
599 /*
600   retrieve a switch value
601  */
602 uint32_t ndr_push_get_switch_value(struct ndr_push *ndr, const void *p)
603 {
604         return ndr_token_peek(&ndr->switch_list, p);
605 }
606
607 uint32_t ndr_pull_get_switch_value(struct ndr_pull *ndr, const void *p)
608 {
609         return ndr_token_peek(&ndr->switch_list, p);
610 }
611
612 uint32_t ndr_print_get_switch_value(struct ndr_print *ndr, const void *p)
613 {
614         return ndr_token_peek(&ndr->switch_list, p);
615 }
616
617 /*
618   pull a struct from a blob using NDR
619 */
620 NTSTATUS ndr_pull_struct_blob(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p,
621                               ndr_pull_flags_fn_t fn)
622 {
623         struct ndr_pull *ndr;
624         ndr = ndr_pull_init_blob(blob, mem_ctx);
625         if (!ndr) {
626                 return NT_STATUS_NO_MEMORY;
627         }
628         return fn(ndr, NDR_SCALARS|NDR_BUFFERS, p);
629 }
630
631 /*
632   pull a struct from a blob using NDR - failing if all bytes are not consumed
633 */
634 NTSTATUS ndr_pull_struct_blob_all(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p,
635                                   ndr_pull_flags_fn_t fn)
636 {
637         struct ndr_pull *ndr;
638         NTSTATUS status;
639
640         ndr = ndr_pull_init_blob(blob, mem_ctx);
641         if (!ndr) {
642                 return NT_STATUS_NO_MEMORY;
643         }
644         status = fn(ndr, NDR_SCALARS|NDR_BUFFERS, p);
645         if (!NT_STATUS_IS_OK(status)) return status;
646         if (ndr->offset != ndr->data_size) {
647                 return NT_STATUS_BUFFER_TOO_SMALL;
648         }
649         return status;
650 }
651
652 /*
653   pull a union from a blob using NDR, given the union discriminator
654 */
655 NTSTATUS ndr_pull_union_blob(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p,
656                              uint32_t level, ndr_pull_flags_fn_t fn)
657 {
658         struct ndr_pull *ndr;
659         NTSTATUS status;
660
661         ndr = ndr_pull_init_blob(blob, mem_ctx);
662         if (!ndr) {
663                 return NT_STATUS_NO_MEMORY;
664         }
665         ndr_pull_set_switch_value(ndr, p, level);
666         status = fn(ndr, NDR_SCALARS|NDR_BUFFERS, p);
667         if (!NT_STATUS_IS_OK(status)) return status;
668         if (ndr->offset != ndr->data_size) {
669                 return NT_STATUS_BUFFER_TOO_SMALL;
670         }
671         return status;
672 }
673
674 /*
675   push a struct to a blob using NDR
676 */
677 NTSTATUS ndr_push_struct_blob(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, const void *p,
678                               ndr_push_flags_fn_t fn)
679 {
680         NTSTATUS status;
681         struct ndr_push *ndr;
682         ndr = ndr_push_init_ctx(mem_ctx);
683         if (!ndr) {
684                 return NT_STATUS_NO_MEMORY;
685         }
686         status = fn(ndr, NDR_SCALARS|NDR_BUFFERS, p);
687         if (!NT_STATUS_IS_OK(status)) {
688                 return status;
689         }
690
691         *blob = ndr_push_blob(ndr);
692
693         return NT_STATUS_OK;
694 }
695
696 /*
697   push a union to a blob using NDR
698 */
699 NTSTATUS ndr_push_union_blob(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p,
700                              uint32_t level, ndr_push_flags_fn_t fn)
701 {
702         NTSTATUS status;
703         struct ndr_push *ndr;
704         ndr = ndr_push_init_ctx(mem_ctx);
705         if (!ndr) {
706                 return NT_STATUS_NO_MEMORY;
707         }
708         ndr_push_set_switch_value(ndr, p, level);
709         status = fn(ndr, NDR_SCALARS|NDR_BUFFERS, p);
710         if (!NT_STATUS_IS_OK(status)) {
711                 return status;
712         }
713
714         *blob = ndr_push_blob(ndr);
715
716         return NT_STATUS_OK;
717 }
718
719 /*
720   generic ndr_size_*() handler for structures
721 */
722 size_t ndr_size_struct(const void *p, int flags, ndr_push_flags_fn_t push)
723 {
724         struct ndr_push *ndr;
725         NTSTATUS status;
726         size_t ret;
727
728         /* avoid recursion */
729         if (flags & LIBNDR_FLAG_NO_NDR_SIZE) return 0;
730
731         ndr = ndr_push_init_ctx(NULL);
732         if (!ndr) return 0;
733         ndr->flags |= flags | LIBNDR_FLAG_NO_NDR_SIZE;
734         status = push(ndr, NDR_SCALARS|NDR_BUFFERS, discard_const(p));
735         if (!NT_STATUS_IS_OK(status)) {
736                 return 0;
737         }
738         ret = ndr->offset;
739         talloc_free(ndr);
740         return ret;
741 }
742
743 /*
744   generic ndr_size_*() handler for unions
745 */
746 size_t ndr_size_union(const void *p, int flags, uint32_t level, ndr_push_flags_fn_t push)
747 {
748         struct ndr_push *ndr;
749         NTSTATUS status;
750         size_t ret;
751
752         /* avoid recursion */
753         if (flags & LIBNDR_FLAG_NO_NDR_SIZE) return 0;
754
755         ndr = ndr_push_init_ctx(NULL);
756         if (!ndr) return 0;
757         ndr->flags |= flags | LIBNDR_FLAG_NO_NDR_SIZE;
758         ndr_push_set_switch_value(ndr, p, level);
759         status = push(ndr, NDR_SCALARS|NDR_BUFFERS, p);
760         if (!NT_STATUS_IS_OK(status)) {
761                 return 0;
762         }
763         ret = ndr->offset;
764         talloc_free(ndr);
765         return ret;
766 }
767
768 /*
769   get the current base for relative pointers for the push
770 */
771 uint32_t ndr_push_get_relative_base_offset(struct ndr_push *ndr)
772 {
773         return ndr->relative_base_offset;
774 }
775
776 /*
777   restore the old base for relative pointers for the push
778 */
779 void ndr_push_restore_relative_base_offset(struct ndr_push *ndr, uint32_t offset)
780 {
781         ndr->relative_base_offset = offset;
782 }
783
784 /*
785   setup the current base for relative pointers for the push
786   called in the NDR_SCALAR stage
787 */
788 NTSTATUS ndr_push_setup_relative_base_offset1(struct ndr_push *ndr, const void *p, uint32_t offset)
789 {
790         ndr->relative_base_offset = offset;
791         return ndr_token_store(ndr, &ndr->relative_base_list, p, offset);
792 }
793
794 /*
795   setup the current base for relative pointers for the push
796   called in the NDR_BUFFERS stage
797 */
798 NTSTATUS ndr_push_setup_relative_base_offset2(struct ndr_push *ndr, const void *p)
799 {
800         return ndr_token_retrieve(&ndr->relative_base_list, p, &ndr->relative_base_offset);
801 }
802
803 /*
804   push a relative object - stage1
805   this is called during SCALARS processing
806 */
807 NTSTATUS ndr_push_relative_ptr1(struct ndr_push *ndr, const void *p)
808 {
809         if (p == NULL) {
810                 NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, 0));
811                 return NT_STATUS_OK;
812         }
813         NDR_CHECK(ndr_push_align(ndr, 4));
814         NDR_CHECK(ndr_token_store(ndr, &ndr->relative_list, p, ndr->offset));
815         return ndr_push_uint32(ndr, NDR_SCALARS, 0xFFFFFFFF);
816 }
817
818 /*
819   push a relative object - stage2
820   this is called during buffers processing
821 */
822 NTSTATUS ndr_push_relative_ptr2(struct ndr_push *ndr, const void *p)
823 {
824         struct ndr_push_save save;
825         uint32_t ptr_offset = 0xFFFFFFFF;
826         if (p == NULL) {
827                 return NT_STATUS_OK;
828         }
829         ndr_push_save(ndr, &save);
830         NDR_CHECK(ndr_token_retrieve(&ndr->relative_list, p, &ptr_offset));
831         if (ptr_offset > ndr->offset) {
832                 return ndr_push_error(ndr, NDR_ERR_BUFSIZE, 
833                                       "ndr_push_relative_ptr2 ptr_offset(%u) > ndr->offset(%u)",
834                                       ptr_offset, ndr->offset);
835         }
836         ndr->offset = ptr_offset;
837         if (save.offset < ndr->relative_base_offset) {
838                 return ndr_push_error(ndr, NDR_ERR_BUFSIZE, 
839                                       "ndr_push_relative_ptr2 save.offset(%u) < ndr->relative_base_offset(%u)",
840                                       save.offset, ndr->relative_base_offset);
841         }       
842         NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, save.offset - ndr->relative_base_offset));
843         ndr_push_restore(ndr, &save);
844         return NT_STATUS_OK;
845 }
846
847 /*
848   get the current base for relative pointers for the pull
849 */
850 uint32_t ndr_pull_get_relative_base_offset(struct ndr_pull *ndr)
851 {
852         return ndr->relative_base_offset;
853 }
854
855 /*
856   restore the old base for relative pointers for the pull
857 */
858 void ndr_pull_restore_relative_base_offset(struct ndr_pull *ndr, uint32_t offset)
859 {
860         ndr->relative_base_offset = offset;
861 }
862
863 /*
864   setup the current base for relative pointers for the pull
865   called in the NDR_SCALAR stage
866 */
867 NTSTATUS ndr_pull_setup_relative_base_offset1(struct ndr_pull *ndr, const void *p, uint32_t offset)
868 {
869         ndr->relative_base_offset = offset;
870         return ndr_token_store(ndr, &ndr->relative_base_list, p, offset);
871 }
872
873 /*
874   setup the current base for relative pointers for the pull
875   called in the NDR_BUFFERS stage
876 */
877 NTSTATUS ndr_pull_setup_relative_base_offset2(struct ndr_pull *ndr, const void *p)
878 {
879         return ndr_token_retrieve(&ndr->relative_base_list, p, &ndr->relative_base_offset);
880 }
881
882 /*
883   pull a relative object - stage1
884   called during SCALARS processing
885 */
886 NTSTATUS ndr_pull_relative_ptr1(struct ndr_pull *ndr, const void *p, uint32_t rel_offset)
887 {
888         rel_offset += ndr->relative_base_offset;
889         if (rel_offset > ndr->data_size) {
890                 return ndr_pull_error(ndr, NDR_ERR_BUFSIZE, 
891                                       "ndr_pull_relative_ptr1 rel_offset(%u) > ndr->data_size(%u)",
892                                       rel_offset, ndr->data_size);
893         }
894         return ndr_token_store(ndr, &ndr->relative_list, p, rel_offset);
895 }
896
897 /*
898   pull a relative object - stage2
899   called during BUFFERS processing
900 */
901 NTSTATUS ndr_pull_relative_ptr2(struct ndr_pull *ndr, const void *p)
902 {
903         uint32_t rel_offset;
904         NDR_CHECK(ndr_token_retrieve(&ndr->relative_list, p, &rel_offset));
905         return ndr_pull_set_offset(ndr, rel_offset);
906 }