* fixed conformant arrays in structures
[ira/wip.git] / source / librpc / ndr / ndr.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    libndr interface
5
6    Copyright (C) Andrew Tridgell 2003
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 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
32 #define NDR_BASE_MARSHALL_SIZE 1024
33
34 /*
35   initialise a ndr parse structure from a data blob
36 */
37 struct ndr_pull *ndr_pull_init_blob(DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
38 {
39         struct ndr_pull *ndr;
40
41         ndr = talloc(mem_ctx, sizeof(*ndr));
42         if (!ndr) return NULL;
43
44         ndr->flags = 0;
45         ndr->data = blob->data;
46         ndr->data_size = blob->length;
47         ndr->offset = 0;
48         ndr->mem_ctx = mem_ctx;
49
50         return ndr;
51 }
52
53
54 /* limit the remaining size of the current ndr parse structure to the
55    given size, starting at the given offset 
56
57    this is used when a ndr packet has an explicit size on the wire, and we
58    need to make sure that we don't use more data than is indicated
59
60    the 'ofs' parameter indicates how many bytes back from the current
61    offset in the buffer the 'size' number of bytes starts
62 */
63 NTSTATUS ndr_pull_limit_size(struct ndr_pull *ndr, uint32 size, uint32 ofs)
64 {
65         uint32 new_size;
66         new_size = ndr->offset + size - ofs;
67
68         if (new_size > ndr->data_size) {
69                 return NT_STATUS_BUFFER_TOO_SMALL;
70         }
71         ndr->data_size = new_size;
72
73         return NT_STATUS_OK;
74 }
75
76
77 /*
78   advance by 'size' bytes
79 */
80 NTSTATUS ndr_pull_advance(struct ndr_pull *ndr, uint32 size)
81 {
82         ndr->offset += size;
83         if (ndr->offset > ndr->data_size) {
84                 return NT_STATUS_BUFFER_TOO_SMALL;
85         }
86         return NT_STATUS_OK;
87 }
88
89 /*
90   set the parse offset to 'ofs'
91 */
92 NTSTATUS ndr_pull_set_offset(struct ndr_pull *ndr, uint32 ofs)
93 {
94         ndr->offset = ofs;
95         if (ndr->offset > ndr->data_size) {
96                 return NT_STATUS_BUFFER_TOO_SMALL;
97         }
98         return NT_STATUS_OK;
99 }
100
101 /* save the offset/size of the current ndr state */
102 void ndr_pull_save(struct ndr_pull *ndr, struct ndr_pull_save *save)
103 {
104         save->offset = ndr->offset;
105         save->data_size = ndr->data_size;
106 }
107
108 /* restore the size/offset of a ndr structure */
109 void ndr_pull_restore(struct ndr_pull *ndr, struct ndr_pull_save *save)
110 {
111         ndr->offset = save->offset;
112         ndr->data_size = save->data_size;
113 }
114
115
116
117
118 /* create a ndr_push structure, ready for some marshalling */
119 struct ndr_push *ndr_push_init(void)
120 {
121         struct ndr_push *ndr;
122         TALLOC_CTX *mem_ctx = talloc_init("ndr_push_init");
123         if (!mem_ctx) return NULL;
124
125         ndr = talloc(mem_ctx, sizeof(*ndr));
126         if (!ndr) {
127                 talloc_destroy(mem_ctx);
128                 return NULL;
129         }
130
131         ndr->mem_ctx = mem_ctx;
132         ndr->flags = 0;
133         ndr->alloc_size = NDR_BASE_MARSHALL_SIZE;
134         ndr->data = talloc(ndr->mem_ctx, ndr->alloc_size);
135         if (!ndr->data) {
136                 ndr_push_free(ndr);
137                 return NULL;
138         }
139         ndr->offset = 0;
140         
141         return ndr;
142 }
143
144 /* free a ndr_push structure */
145 void ndr_push_free(struct ndr_push *ndr)
146 {
147         talloc_destroy(ndr->mem_ctx);
148 }
149
150
151 /* return a DATA_BLOB structure for the current ndr_push marshalled data */
152 DATA_BLOB ndr_push_blob(struct ndr_push *ndr)
153 {
154         DATA_BLOB blob;
155         blob.data = ndr->data;
156         blob.length = ndr->offset;
157         return blob;
158 }
159
160
161 /*
162   expand the available space in the buffer to 'size'
163 */
164 NTSTATUS ndr_push_expand(struct ndr_push *ndr, uint32 size)
165 {
166         if (ndr->alloc_size >= size) {
167                 return NT_STATUS_OK;
168         }
169
170         ndr->alloc_size += NDR_BASE_MARSHALL_SIZE;
171         if (size > ndr->alloc_size) {
172                 ndr->alloc_size = size;
173         }
174         ndr->data = talloc_realloc(ndr->mem_ctx, ndr->data, ndr->alloc_size);
175         if (!ndr->data) {
176                 return NT_STATUS_NO_MEMORY;
177         }
178
179         return NT_STATUS_OK;
180 }
181
182 /*
183   set the push offset to 'ofs'
184 */
185 NTSTATUS ndr_push_set_offset(struct ndr_push *ndr, uint32 ofs)
186 {
187         NDR_CHECK(ndr_push_expand(ndr, ofs));
188         ndr->offset = ofs;
189         return NT_STATUS_OK;
190 }
191
192 /*
193   push a generic array
194 */
195 NTSTATUS ndr_push_array(struct ndr_push *ndr, int ndr_flags, void *base, 
196                               size_t elsize, uint32 count, 
197                               NTSTATUS (*push_fn)(struct ndr_push *, int, void *))
198 {
199         int i;
200         char *p = base;
201         if (!(ndr_flags & NDR_SCALARS)) goto buffers;
202         for (i=0;i<count;i++) {
203                 NDR_CHECK(push_fn(ndr, NDR_SCALARS, p));
204                 p += elsize;
205         }
206         if (!(ndr_flags & NDR_BUFFERS)) goto done;
207 buffers:
208         p = base;
209         for (i=0;i<count;i++) {
210                 NDR_CHECK(push_fn(ndr, NDR_BUFFERS, p));
211                 p += elsize;
212         }
213 done:
214         return NT_STATUS_OK;
215 }
216
217 /*
218   pull a constant sized array
219 */
220 NTSTATUS ndr_pull_array(struct ndr_pull *ndr, int ndr_flags, void *base, 
221                         size_t elsize, uint32 count, 
222                         NTSTATUS (*pull_fn)(struct ndr_pull *, int, void *))
223 {
224         int i;
225         char *p;
226         p = base;
227         if (!(ndr_flags & NDR_SCALARS)) goto buffers;
228         for (i=0;i<count;i++) {
229                 NDR_CHECK(pull_fn(ndr, NDR_SCALARS, p));
230                 p += elsize;
231         }
232         if (!(ndr_flags & NDR_BUFFERS)) goto done;
233 buffers:
234         p = base;
235         for (i=0;i<count;i++) {
236                 NDR_CHECK(pull_fn(ndr, NDR_BUFFERS, p));
237                 p += elsize;
238         }
239 done:
240         return NT_STATUS_OK;
241 }
242
243
244 /*
245   print a generic array
246 */
247 void ndr_print_array(struct ndr_print *ndr, const char *name, void *base, 
248                      size_t elsize, uint32 count, 
249                      void (*print_fn)(struct ndr_print *, const char *, void *))
250 {
251         int i;
252         char *p = base;
253         ndr->print(ndr, "%s: ARRAY(%d)", name, count);
254         ndr->depth++;
255         for (i=0;i<count;i++) {
256                 char *idx=NULL;
257                 asprintf(&idx, "[%d]", i);
258                 if (idx) {
259                         print_fn(ndr, idx, p);
260                         free(idx);
261                 }
262                 p += elsize;
263         }
264         ndr->depth--;
265 }
266
267
268
269 static void ndr_print_debug_helper(struct ndr_print *ndr, const char *format, ...)
270 {
271         va_list ap;
272         char *s = NULL;
273         int i;
274
275         va_start(ap, format);
276         vasprintf(&s, format, ap);
277         va_end(ap);
278
279         for (i=0;i<ndr->depth;i++) {
280                 DEBUG(0,("    "));
281         }
282
283         DEBUG(0,("%s\n", s));
284         free(s);
285 }
286
287 /*
288   a useful helper function for printing idl structures via DEBUG()
289 */
290 void ndr_print_debug(void (*fn)(struct ndr_print *, const char *, void *),
291                      const char *name,
292                      void *ptr)
293 {
294         struct ndr_print ndr;
295
296         ndr.mem_ctx = talloc_init("ndr_print_debug");
297         if (!ndr.mem_ctx) return;
298         ndr.print = ndr_print_debug_helper;
299         ndr.depth = 1;
300         fn(&ndr, name, ptr);
301         talloc_destroy(ndr.mem_ctx);
302 }
303
304 /*
305   a useful helper function for printing idl unions via DEBUG()
306 */
307 void ndr_print_union_debug(void (*fn)(struct ndr_print *, const char *, uint16, void *),
308                            const char *name,
309                            uint16 level,
310                            void *ptr)
311 {
312         struct ndr_print ndr;
313
314         ndr.mem_ctx = talloc_init("ndr_print_debug");
315         if (!ndr.mem_ctx) return;
316         ndr.print = ndr_print_debug_helper;
317         ndr.depth = 1;
318         fn(&ndr, name, level, ptr);
319         talloc_destroy(ndr.mem_ctx);
320 }
321
322 /*
323   return and possibly log an NDR error
324 */
325 NTSTATUS ndr_pull_error(struct ndr_pull *ndr, enum ndr_err_code err, const char *format, ...)
326 {
327         char *s=NULL;
328         va_list ap;
329
330         va_start(ap, format);
331         vasprintf(&s, format, ap);
332         va_end(ap);
333
334         DEBUG(3,("ndr_pull_error(%u): %s\n", err, s));
335
336         free(s);
337         /* we should map to different status codes */
338         return NT_STATUS_INVALID_PARAMETER;
339 }