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