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