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