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