r6182: Jelmer, I think we need to initialize the switch_list, else we are
[metze/samba/wip.git] / source4 / librpc / ndr / ndr.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    libndr interface
5
6    Copyright (C) Andrew Tridgell 2003
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 /*
24   this provides the core routines for NDR parsing functions
25
26   see http://www.opengroup.org/onlinepubs/9629399/chap14.htm for details
27   of NDR encoding rules
28 */
29
30 #include "includes.h"
31 #include "dlinklist.h"
32
33 #define NDR_BASE_MARSHALL_SIZE 1024
34
35 /*
36   work out the number of bytes needed to align on a n byte boundary
37 */
38 size_t ndr_align_size(uint32_t offset, size_t n)
39 {
40         if ((offset & (n-1)) == 0) return 0;
41         return n - (offset & (n-1));
42 }
43
44 /*
45   initialise a ndr parse structure from a data blob
46 */
47 struct ndr_pull *ndr_pull_init_blob(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
48 {
49         struct ndr_pull *ndr;
50
51         ndr = talloc_zero(mem_ctx, struct ndr_pull);
52         if (!ndr) return NULL;
53
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(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_array(ndr, uint8_t, 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, uint8_t, 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(NULL, struct ndr_print);
319         if (!ndr) return;
320         ndr->print = ndr_print_debug_helper;
321         ndr->depth = 1;
322         ndr->flags = 0;
323         ndr->switch_list = talloc(ndr, struct ndr_token_list);
324         if (!ndr->switch_list)
325                 goto fail;
326         fn(ndr, name, ptr);
327 fail:
328         talloc_free(ndr);
329 }
330
331 /*
332   a useful helper function for printing idl function calls via DEBUG()
333 */
334 void ndr_print_function_debug(ndr_print_function_t fn, const char *name, int flags, void *ptr)
335 {
336         struct ndr_print *ndr;
337
338         ndr = talloc(NULL, struct ndr_print);
339         if (!ndr) return;
340         ndr->print = ndr_print_debug_helper;
341         ndr->depth = 1;
342         ndr->flags = 0;
343         fn(ndr, name, flags, ptr);
344         talloc_free(ndr);
345 }
346
347 void ndr_set_flags(uint32_t *pflags, uint32_t new_flags)
348 {
349         /* the big/little endian flags are inter-dependent */
350         if (new_flags & LIBNDR_FLAG_LITTLE_ENDIAN) {
351                 (*pflags) &= ~LIBNDR_FLAG_BIGENDIAN;
352         }
353         if (new_flags & LIBNDR_FLAG_BIGENDIAN) {
354                 (*pflags) &= ~LIBNDR_FLAG_LITTLE_ENDIAN;
355         }
356         (*pflags) |= new_flags;
357 }
358
359 static NTSTATUS ndr_map_error(enum ndr_err_code err)
360 {
361         switch (err) {
362         case NDR_ERR_BUFSIZE:
363                 return NT_STATUS_BUFFER_TOO_SMALL;
364         case NDR_ERR_TOKEN:
365                 return NT_STATUS_INTERNAL_ERROR;
366         case NDR_ERR_ALLOC:
367                 return NT_STATUS_NO_MEMORY;
368         case NDR_ERR_ARRAY_SIZE:
369                 return NT_STATUS_ARRAY_BOUNDS_EXCEEDED;
370         default:
371                 break;
372         }
373
374         /* we should all error codes to different status codes */
375         return NT_STATUS_INVALID_PARAMETER;
376 }
377
378 /*
379   return and possibly log an NDR error
380 */
381 NTSTATUS ndr_pull_error(struct ndr_pull *ndr, 
382                         enum ndr_err_code err, const char *format, ...) _PRINTF_ATTRIBUTE(3,4)
383 {
384         char *s=NULL;
385         va_list ap;
386
387         va_start(ap, format);
388         vasprintf(&s, format, ap);
389         va_end(ap);
390
391         DEBUG(3,("ndr_pull_error(%u): %s\n", err, s));
392
393         free(s);
394
395         return ndr_map_error(err);
396 }
397
398 /*
399   return and possibly log an NDR error
400 */
401 NTSTATUS ndr_push_error(struct ndr_push *ndr, enum ndr_err_code err, const char *format, ...)  _PRINTF_ATTRIBUTE(3,4)
402 {
403         char *s=NULL;
404         va_list ap;
405
406         va_start(ap, format);
407         vasprintf(&s, format, ap);
408         va_end(ap);
409
410         DEBUG(3,("ndr_push_error(%u): %s\n", err, s));
411
412         free(s);
413
414         return ndr_map_error(err);
415 }
416
417
418 /*
419   handle subcontext buffers, which in midl land are user-marshalled, but
420   we use magic in pidl to make them easier to cope with
421 */
422 NTSTATUS ndr_pull_subcontext_header(struct ndr_pull *ndr, 
423                                            size_t header_size,
424                                            ssize_t size_is,
425                                            struct ndr_pull *ndr2)
426 {
427         ndr2->flags = ndr->flags;
428
429         switch (header_size) {
430         case 0: {
431                 uint32_t content_size = ndr->data_size - ndr->offset;
432                 if (size_is >= 0) {
433                         content_size = size_is;
434                 }
435                 NDR_CHECK(ndr_pull_subcontext(ndr, ndr2, content_size));
436                 break;
437         }
438
439         case 2: {
440                 uint16_t content_size;
441                 NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &content_size));
442                 if (size_is >= 0 && size_is != content_size) {
443                         return ndr_pull_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PULL) size_is(%d) mismatch content_size %d", 
444                                                 size_is, content_size);
445                 }
446                 NDR_CHECK(ndr_pull_subcontext(ndr, ndr2, content_size));
447                 break;
448         }
449
450         case 4: {
451                 uint32_t content_size;
452                 NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &content_size));
453                 if (size_is >= 0 && size_is != content_size) {
454                         return ndr_pull_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PULL) size_is(%d) mismatch content_size %d", 
455                                                 size_is, content_size);
456                 }
457                 NDR_CHECK(ndr_pull_subcontext(ndr, ndr2, content_size));
458                 break;
459         }
460         default:
461                 return ndr_pull_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PULL) header_size %d", 
462                                       header_size);
463         }
464         return NT_STATUS_OK;
465 }
466
467 /*
468   push a subcontext header 
469 */
470 NTSTATUS ndr_push_subcontext_header(struct ndr_push *ndr, 
471                                            size_t header_size,
472                                            ssize_t size_is,
473                                            struct ndr_push *ndr2)
474 {
475         if (size_is >= 0) {
476                 ssize_t padding_len = size_is - ndr2->offset;
477                 if (padding_len > 0) {
478                         NDR_CHECK(ndr_push_zero(ndr2, padding_len));
479                 } else if (padding_len < 0) {
480                         return ndr_push_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PUSH) content_size %d is larger than size_is(%d)",
481                                               ndr2->offset, size_is);
482                 }
483         }
484
485         switch (header_size) {
486         case 0: 
487                 break;
488
489         case 2: 
490                 NDR_CHECK(ndr_push_uint16(ndr, NDR_SCALARS, ndr2->offset));
491                 break;
492
493         case 4: 
494                 NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, ndr2->offset));
495                 break;
496
497         default:
498                 return ndr_push_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext header size %d", 
499                                       header_size);
500         }
501         return NT_STATUS_OK;
502 }
503
504 /*
505   mark the start of a structure
506 */
507 NTSTATUS ndr_pull_struct_start(struct ndr_pull *ndr)
508 {
509         return NT_STATUS_OK;
510 }
511
512 /*
513   mark the end of a structure
514 */
515 void ndr_pull_struct_end(struct ndr_pull *ndr)
516 {
517 }
518
519 /*
520   mark the start of a structure
521 */
522 NTSTATUS ndr_push_struct_start(struct ndr_push *ndr)
523 {
524         return NT_STATUS_OK;
525 }
526
527 /*
528   mark the end of a structure
529 */
530 void ndr_push_struct_end(struct ndr_push *ndr)
531 {
532 }
533
534 /*
535   store a token in the ndr context, for later retrieval
536 */
537 static NTSTATUS ndr_token_store(TALLOC_CTX *mem_ctx, 
538                                 struct ndr_token_list **list, 
539                                 const void *key, 
540                                 uint32_t value)
541 {
542         struct ndr_token_list *tok;
543         tok = talloc(mem_ctx, struct ndr_token_list);
544         if (tok == NULL) {
545                 return NT_STATUS_NO_MEMORY;
546         }
547         tok->key = key;
548         tok->value = value;
549         DLIST_ADD((*list), tok);
550         return NT_STATUS_OK;
551 }
552
553 /*
554   retrieve a token from a ndr context
555 */
556 static NTSTATUS ndr_token_retrieve(struct ndr_token_list **list, const void *key, uint32_t *v)
557 {
558         struct ndr_token_list *tok;
559         for (tok=*list;tok;tok=tok->next) {
560                 if (tok->key == key) {
561                         DLIST_REMOVE((*list), tok);
562                         *v = tok->value;
563                         return NT_STATUS_OK;
564                 }
565         }
566         return ndr_map_error(NDR_ERR_TOKEN);
567 }
568
569 /*
570   peek at but don't removed a token from a ndr context
571 */
572 static uint32_t ndr_token_peek(struct ndr_token_list **list, const void *key)
573 {
574         struct ndr_token_list *tok;
575         for (tok=*list;tok;tok=tok->next) {
576                 if (tok->key == key) {
577                         return tok->value;
578                 }
579         }
580         return 0;
581 }
582
583 /*
584   pull an array size field and add it to the array_size_list token list
585 */
586 NTSTATUS ndr_pull_array_size(struct ndr_pull *ndr, const void *p)
587 {
588         uint32_t size;
589         NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &size));
590         return ndr_token_store(ndr, &ndr->array_size_list, p, size);
591 }
592
593 /*
594   get the stored array size field
595 */
596 uint32_t ndr_get_array_size(struct ndr_pull *ndr, const void *p)
597 {
598         return ndr_token_peek(&ndr->array_size_list, p);
599 }
600
601 /*
602   check the stored array size field
603 */
604 NTSTATUS ndr_check_array_size(struct ndr_pull *ndr, void *p, uint32_t size)
605 {
606         uint32_t stored;
607         NDR_CHECK(ndr_token_retrieve(&ndr->array_size_list, p, &stored));
608         if (stored != size) {
609                 return ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE, 
610                                       "Bad array size - got %u expected %u\n",
611                                       stored, size);
612         }
613         return NT_STATUS_OK;
614 }
615
616 /*
617   pull an array length field and add it to the array_length_list token list
618 */
619 NTSTATUS ndr_pull_array_length(struct ndr_pull *ndr, const void *p)
620 {
621         uint32_t length, offset;
622         NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &offset));
623         if (offset != 0) {
624                 return ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE, 
625                                       "non-zero array offset %u\n", offset);
626         }
627         NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &length));
628         return ndr_token_store(ndr, &ndr->array_length_list, p, length);
629 }
630
631 /*
632   get the stored array length field
633 */
634 uint32_t ndr_get_array_length(struct ndr_pull *ndr, const void *p)
635 {
636         return ndr_token_peek(&ndr->array_length_list, p);
637 }
638
639 /*
640   check the stored array length field
641 */
642 NTSTATUS ndr_check_array_length(struct ndr_pull *ndr, void *p, uint32_t length)
643 {
644         uint32_t stored;
645         NDR_CHECK(ndr_token_retrieve(&ndr->array_length_list, p, &stored));
646         if (stored != length) {
647                 return ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE, 
648                                       "Bad array length - got %u expected %u\n",
649                                       stored, length);
650         }
651         return NT_STATUS_OK;
652 }
653
654 /*
655   store a switch value
656  */
657 NTSTATUS ndr_push_set_switch_value(struct ndr_push *ndr, const void *p, uint32_t val)
658 {
659         return ndr_token_store(ndr, &ndr->switch_list, p, val);
660 }
661
662 NTSTATUS ndr_pull_set_switch_value(struct ndr_pull *ndr, const void *p, uint32_t val)
663 {
664         return ndr_token_store(ndr, &ndr->switch_list, p, val);
665 }
666
667 /*
668   retrieve a switch value
669  */
670 uint32_t ndr_push_get_switch_value(struct ndr_push *ndr, const void *p)
671 {
672         return ndr_token_peek(&ndr->switch_list, p);
673 }
674
675 uint32_t ndr_pull_get_switch_value(struct ndr_pull *ndr, const void *p)
676 {
677         return ndr_token_peek(&ndr->switch_list, p);
678 }
679
680 NTSTATUS ndr_print_set_switch_value(struct ndr_print *ndr, const void *p, uint32_t val)
681 {
682         return ndr_token_store(ndr, &ndr->switch_list, p, val);
683 }
684
685 /*
686   retrieve a switch value
687  */
688 uint32_t ndr_print_get_switch_value(struct ndr_print *ndr, const void *p)
689 {
690         return ndr_token_peek(&ndr->switch_list, p);
691 }
692
693 /*
694   pull a relative object - stage1
695   called during SCALARS processing
696 */
697 NTSTATUS ndr_pull_relative_ptr1(struct ndr_pull *ndr, const void *p, uint32_t rel_offset)
698 {
699         if (ndr->flags & LIBNDR_FLAG_RELATIVE_CURRENT) {
700                 return ndr_token_store(ndr, &ndr->relative_list, p, 
701                                        rel_offset + ndr->offset - 4);
702         } else {
703                 return ndr_token_store(ndr, &ndr->relative_list, p, rel_offset);
704         }
705 }
706
707 /*
708   pull a relative object - stage2
709   called during BUFFERS processing
710 */
711 NTSTATUS ndr_pull_relative_ptr2(struct ndr_pull *ndr, const void *p)
712 {
713         uint32_t rel_offset;
714         NDR_CHECK(ndr_token_retrieve(&ndr->relative_list, p, &rel_offset));
715         return ndr_pull_set_offset(ndr, rel_offset);
716 }
717
718 /*
719   push a relative object - stage1
720   this is called during SCALARS processing
721 */
722 NTSTATUS ndr_push_relative_ptr1(struct ndr_push *ndr, const void *p)
723 {
724         if (p == NULL) {
725                 NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, 0));
726                 return NT_STATUS_OK;
727         }
728         NDR_CHECK(ndr_push_align(ndr, 4));
729         NDR_CHECK(ndr_token_store(ndr, &ndr->relative_list, p, ndr->offset));
730         return ndr_push_uint32(ndr, NDR_SCALARS, 0xFFFFFFFF);
731 }
732
733 /*
734   push a relative object - stage2
735   this is called during buffers processing
736 */
737 NTSTATUS ndr_push_relative_ptr2(struct ndr_push *ndr, const void *p)
738 {
739         struct ndr_push_save save;
740         if (p == NULL) {
741                 return NT_STATUS_OK;
742         }
743         NDR_CHECK(ndr_push_align(ndr, 4));
744         ndr_push_save(ndr, &save);
745         NDR_CHECK(ndr_token_retrieve(&ndr->relative_list, p, &ndr->offset));
746         if (ndr->flags & LIBNDR_FLAG_RELATIVE_CURRENT) {
747                 NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, save.offset - ndr->offset));
748         } else {
749                 NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, save.offset));
750         }
751         ndr_push_restore(ndr, &save);
752         return NT_STATUS_OK;
753 }
754
755 /*
756   pull a struct from a blob using NDR
757 */
758 NTSTATUS ndr_pull_struct_blob(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p,
759                               ndr_pull_flags_fn_t fn)
760 {
761         struct ndr_pull *ndr;
762         ndr = ndr_pull_init_blob(blob, mem_ctx);
763         if (!ndr) {
764                 return NT_STATUS_NO_MEMORY;
765         }
766         return fn(ndr, NDR_SCALARS|NDR_BUFFERS, p);
767 }
768
769 /*
770   push a struct to a blob using NDR
771 */
772 NTSTATUS ndr_push_struct_blob(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p,
773                               ndr_push_flags_fn_t fn)
774 {
775         NTSTATUS status;
776         struct ndr_push *ndr;
777         ndr = ndr_push_init_ctx(mem_ctx);
778         if (!ndr) {
779                 return NT_STATUS_NO_MEMORY;
780         }
781         status = fn(ndr, NDR_SCALARS|NDR_BUFFERS, p);
782         if (!NT_STATUS_IS_OK(status)) {
783                 return status;
784         }
785
786         *blob = ndr_push_blob(ndr);
787
788         return NT_STATUS_OK;
789 }
790
791 /*
792   generic ndr_size_*() handler for structures
793 */
794 size_t ndr_size_struct(const void *p, int flags, ndr_push_flags_fn_t push)
795 {
796         struct ndr_push *ndr;
797         NTSTATUS status;
798         size_t ret;
799
800         /* avoid recursion */
801         if (flags & LIBNDR_FLAG_NO_NDR_SIZE) return 0;
802
803         ndr = ndr_push_init_ctx(NULL);
804         if (!ndr) return 0;
805         ndr->flags |= flags | LIBNDR_FLAG_NO_NDR_SIZE;
806         status = push(ndr, NDR_SCALARS|NDR_BUFFERS, discard_const(p));
807         if (!NT_STATUS_IS_OK(status)) {
808                 return 0;
809         }
810         ret = ndr->offset;
811         talloc_free(ndr);
812         return ret;
813 }
814
815 /*
816   generic ndr_size_*() handler for unions
817 */
818 size_t ndr_size_union(const void *p, int flags, uint32_t level, ndr_push_flags_fn_t push)
819 {
820         struct ndr_push *ndr;
821         NTSTATUS status;
822         size_t ret;
823
824         /* avoid recursion */
825         if (flags & LIBNDR_FLAG_NO_NDR_SIZE) return 0;
826
827         ndr = ndr_push_init_ctx(NULL);
828         if (!ndr) return 0;
829         ndr->flags |= flags | LIBNDR_FLAG_NO_NDR_SIZE;
830         ndr_push_set_switch_value(ndr, p, level);
831         status = push(ndr, NDR_SCALARS|NDR_BUFFERS, discard_const(p));
832         if (!NT_STATUS_IS_OK(status)) {
833                 return 0;
834         }
835         ret = ndr->offset;
836         talloc_free(ndr);
837         return ret;
838 }