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