r9959: Fix bug where data offset was incorrect after parsing element with
[jelmer/samba4-debian.git] / source / lib / tdr / tdr.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    TDR (Trivial Data Representation) helper functions
5      Based loosely on ndr.c by Andrew Tridgell.
6
7    Copyright (C) Jelmer Vernooij 2005
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25 #include "system/network.h"
26
27 #define TDR_BASE_MARSHALL_SIZE 1024
28
29 #define TDR_PUSH_NEED_BYTES(tdr, n) TDR_CHECK(tdr_push_expand(tdr, tdr->offset+(n)))
30
31 #define TDR_PULL_NEED_BYTES(tdr, n) do { \
32         if ((n) > tdr->data.length || tdr->offset + (n) > tdr->data.length) { \
33                 return NT_STATUS_BUFFER_TOO_SMALL; \
34         } \
35 } while(0)
36
37 #define TDR_BE(tdr) ((tdr)->flags & TDR_BIG_ENDIAN)
38
39 #define TDR_SVAL(tdr, ofs) (TDR_BE(tdr)?RSVAL(tdr->data.data,ofs):SVAL(tdr->data.data,ofs))
40 #define TDR_IVAL(tdr, ofs) (TDR_BE(tdr)?RIVAL(tdr->data.data,ofs):IVAL(tdr->data.data,ofs))
41 #define TDR_IVALS(tdr, ofs) (TDR_BE(tdr)?RIVALS(tdr->data.data,ofs):IVALS(tdr->data.data,ofs))
42 #define TDR_SSVAL(tdr, ofs, v) do { if (TDR_BE(tdr))  { RSSVAL(tdr->data.data,ofs,v); } else SSVAL(tdr->data.data,ofs,v); } while (0)
43 #define TDR_SIVAL(tdr, ofs, v) do { if (TDR_BE(tdr))  { RSIVAL(tdr->data.data,ofs,v); } else SIVAL(tdr->data.data,ofs,v); } while (0)
44 #define TDR_SIVALS(tdr, ofs, v) do { if (TDR_BE(tdr))  { RSIVALS(tdr->data.data,ofs,v); } else SIVALS(tdr->data.data,ofs,v); } while (0)
45
46 /*
47   expand the available space in the buffer to 'size'
48 */
49 NTSTATUS tdr_push_expand(struct tdr_push *tdr, uint32_t size)
50 {
51         if (talloc_get_size(tdr->data.data) >= size) {
52                 return NT_STATUS_OK;
53         }
54
55         tdr->data.data = talloc_realloc(tdr, tdr->data.data, uint8_t, tdr->data.length + TDR_BASE_MARSHALL_SIZE);
56         return NT_STATUS_NO_MEMORY;
57 }
58
59
60 NTSTATUS tdr_pull_uint8(struct tdr_pull *tdr, uint8_t *v)
61 {
62         TDR_PULL_NEED_BYTES(tdr, 1);
63         SCVAL(tdr->data.data, tdr->offset, *v);
64         tdr->offset += 1;
65         return NT_STATUS_OK;
66 }
67
68 NTSTATUS tdr_push_uint8(struct tdr_push *tdr, const uint8_t *v)
69 {
70         TDR_PUSH_NEED_BYTES(tdr, 1);
71         SCVAL(tdr->data.data, tdr->offset, *v);
72         tdr->offset += 1;
73         return NT_STATUS_OK;
74 }
75
76 NTSTATUS tdr_print_uint8(struct tdr_print *tdr, const char *name, uint8_t *v)
77 {
78         tdr->print(tdr, "%-25s: 0x%02x (%u)", name, *v, *v);
79         return NT_STATUS_OK;
80 }
81
82 NTSTATUS tdr_pull_uint16(struct tdr_pull *tdr, uint16_t *v)
83 {
84         TDR_PULL_NEED_BYTES(tdr, 2);
85         *v = TDR_SVAL(tdr, tdr->offset);
86         tdr->offset += 2;
87         return NT_STATUS_OK;
88 }
89
90 NTSTATUS tdr_push_uint16(struct tdr_push *tdr, const uint16_t *v)
91 {
92         TDR_PUSH_NEED_BYTES(tdr, 2);
93         TDR_SSVAL(tdr, tdr->offset, *v);
94         tdr->offset += 2;
95         return NT_STATUS_OK;
96 }
97
98 NTSTATUS tdr_print_uint16(struct tdr_print *tdr, const char *name, uint16_t *v)
99 {
100         tdr->print(tdr, "%-25s: 0x%02x (%u)", name, *v, *v);
101         return NT_STATUS_OK;
102 }
103
104 NTSTATUS tdr_pull_uint32(struct tdr_pull *tdr, uint32_t *v)
105 {
106         TDR_PULL_NEED_BYTES(tdr, 4);
107         *v = TDR_IVAL(tdr, tdr->offset);
108         tdr->offset += 4;
109         return NT_STATUS_OK;
110 }
111
112 NTSTATUS tdr_push_uint32(struct tdr_push *tdr, const uint32_t *v)
113 {
114         TDR_PUSH_NEED_BYTES(tdr, 4);
115         TDR_SIVAL(tdr, tdr->offset, *v);
116         tdr->offset += 4;
117         return NT_STATUS_OK;
118 }
119
120 NTSTATUS tdr_print_uint32(struct tdr_print *tdr, const char *name, uint32_t *v)
121 {
122         tdr->print(tdr, "%-25s: 0x%02x (%u)", name, *v, *v);
123         return NT_STATUS_OK;
124 }
125
126 NTSTATUS tdr_pull_charset(struct tdr_pull *tdr, const char **v, uint32_t length, uint32_t el_size, int chset)
127 {
128         int ret;
129
130         if (length == -1) {
131                 switch (chset) {
132                         case CH_DOS:
133                                 length = ascii_len_n((const char*)tdr->data.data+tdr->offset, tdr->data.length-tdr->offset);
134                                 break;
135                         case CH_UTF16:
136                                 length = utf16_len_n(tdr->data.data+tdr->offset, tdr->data.length-tdr->offset);
137                                 break;
138
139                         default:
140                                 return NT_STATUS_INVALID_PARAMETER;
141                 }
142         }
143
144         TDR_PULL_NEED_BYTES(tdr, el_size*length);
145         
146         ret = convert_string_talloc(tdr, chset, CH_UNIX, tdr->data.data+tdr->offset, el_size*length, discard_const_p(void *, v));
147
148         if (ret == -1) {
149                 return NT_STATUS_INVALID_PARAMETER;
150         }
151
152         tdr->offset += length * el_size;
153
154         return NT_STATUS_OK;
155 }
156
157 NTSTATUS tdr_push_charset(struct tdr_push *tdr, const char **v, uint32_t length, uint32_t el_size, int chset)
158 {
159         ssize_t ret, required;
160
161         required = el_size * length;
162         TDR_PUSH_NEED_BYTES(tdr, required);
163
164         ret = convert_string(CH_UNIX, chset, *v, strlen(*v), tdr->data.data+tdr->offset, required);
165
166         if (ret == -1) {
167                 return NT_STATUS_INVALID_PARAMETER;
168         }
169
170         /* Make sure the remaining part of the string is filled with zeroes */
171         if (ret < required) {
172                 memset(tdr->data.data+tdr->offset+ret, 0, required-ret);
173         }
174         
175         tdr->offset += required;
176                                                  
177         return NT_STATUS_OK;
178 }
179
180 NTSTATUS tdr_print_charset(struct tdr_print *tdr, const char *name, const char **v, uint32_t length, uint32_t el_size, int chset)
181 {
182         tdr->print(tdr, "%-25s: %s", name, *v);
183         return NT_STATUS_OK;
184 }
185
186 /*
187   pull a ipv4address
188 */
189 NTSTATUS tdr_pull_ipv4address(struct tdr_pull *tdr, const char **address)
190 {
191         struct ipv4_addr in;
192         TDR_CHECK(tdr_pull_uint32(tdr, &in.addr));
193         in.addr = htonl(in.addr);
194         *address = talloc_strdup(tdr, sys_inet_ntoa(in));
195         NT_STATUS_HAVE_NO_MEMORY(*address);
196         return NT_STATUS_OK;
197 }
198
199 /*
200   push a ipv4address
201 */
202 NTSTATUS tdr_push_ipv4address(struct tdr_push *tdr, const char **address)
203 {
204         uint32_t addr = htonl(interpret_addr(*address));
205         TDR_CHECK(tdr_push_uint32(tdr, &addr));
206         return NT_STATUS_OK;
207 }
208
209 /*
210   print a ipv4address
211 */
212 NTSTATUS tdr_print_ipv4address(struct tdr_print *tdr, const char *name, 
213                            const char **address)
214 {
215         tdr->print(tdr, "%-25s: %s", name, *address);
216         return NT_STATUS_OK;
217 }
218
219 /*
220   parse a hyper
221 */
222 NTSTATUS tdr_pull_hyper(struct tdr_pull *tdr, uint64_t *v)
223 {
224         TDR_PULL_NEED_BYTES(tdr, 8);
225         *v = TDR_IVAL(tdr, tdr->offset);
226         *v |= (uint64_t)(TDR_IVAL(tdr, tdr->offset+4)) << 32;
227         tdr->offset += 8;
228         return NT_STATUS_OK;
229 }
230
231 /*
232   push a hyper
233 */
234 NTSTATUS tdr_push_hyper(struct tdr_push *tdr, uint64_t *v)
235 {
236         TDR_PUSH_NEED_BYTES(tdr, 8);
237         TDR_SIVAL(tdr, tdr->offset, ((*v) & 0xFFFFFFFF));
238         TDR_SIVAL(tdr, tdr->offset+4, ((*v)>>32));
239         tdr->offset += 8;
240         return NT_STATUS_OK;
241 }
242
243
244
245 /*
246   push a NTTIME
247 */
248 NTSTATUS tdr_push_NTTIME(struct tdr_push *tdr, NTTIME *t)
249 {
250         TDR_CHECK(tdr_push_hyper(tdr, t));
251         return NT_STATUS_OK;
252 }
253
254 /*
255   pull a NTTIME
256 */
257 NTSTATUS tdr_pull_NTTIME(struct tdr_pull *tdr, NTTIME *t)
258 {
259         TDR_CHECK(tdr_pull_hyper(tdr, t));
260         return NT_STATUS_OK;
261 }
262
263 /*
264   push a time_t
265 */
266 NTSTATUS tdr_push_time_t(struct tdr_push *tdr, time_t *t)
267 {
268         return tdr_push_uint32(tdr, (uint32_t *)t);
269 }
270
271 /*
272   pull a time_t
273 */
274 NTSTATUS tdr_pull_time_t(struct tdr_pull *tdr, time_t *t)
275 {
276         uint32_t tt;
277         TDR_CHECK(tdr_pull_uint32(tdr, &tt));
278         *t = tt;
279         return NT_STATUS_OK;
280 }
281
282 NTSTATUS tdr_print_time_t(struct tdr_print *tdr, const char *name, time_t *t)
283 {
284         if (*t == (time_t)-1 || *t == 0) {
285                 tdr->print(tdr, "%-25s: (time_t)%d", name, (int)*t);
286         } else {
287                 tdr->print(tdr, "%-25s: %s", name, timestring(tdr, *t));
288         }
289
290         return NT_STATUS_OK;
291 }
292
293 NTSTATUS tdr_print_NTTIME(struct tdr_print *tdr, const char *name, NTTIME *t)
294 {
295         tdr->print(tdr, "%-25s: %s", name, nt_time_string(tdr, *t));
296
297         return NT_STATUS_OK;
298 }
299
300 NTSTATUS tdr_print_DATA_BLOB(struct tdr_print *tdr, const char *name, DATA_BLOB *r)
301 {
302         tdr->print(tdr, "%-25s: DATA_BLOB length=%u", name, r->length);
303         if (r->length) {
304                 dump_data(10, r->data, r->length);
305         }
306
307         return NT_STATUS_OK;
308 }
309
310 #define TDR_ALIGN(tdr,n) (((tdr)->offset & ((n)-1)) == 0?0:((n)-((tdr)->offset&((n)-1))))
311
312 /*
313   push a DATA_BLOB onto the wire. 
314 */
315 NTSTATUS tdr_push_DATA_BLOB(struct tdr_push *tdr, DATA_BLOB *blob)
316 {
317         if (tdr->flags & TDR_ALIGN2) {
318                 blob->length = TDR_ALIGN(tdr, 2);
319         } else if (tdr->flags & TDR_ALIGN4) {
320                 blob->length = TDR_ALIGN(tdr, 4);
321         } else if (tdr->flags & TDR_ALIGN8) {
322                 blob->length = TDR_ALIGN(tdr, 8);
323         }
324
325         TDR_PUSH_NEED_BYTES(tdr, blob->length);
326         
327         memcpy(tdr->data.data+tdr->offset, blob->data, blob->length);
328         return NT_STATUS_OK;
329 }
330
331 /*
332   pull a DATA_BLOB from the wire. 
333 */
334 NTSTATUS tdr_pull_DATA_BLOB(struct tdr_pull *tdr, DATA_BLOB *blob)
335 {
336         uint32_t length;
337
338         if (tdr->flags & TDR_ALIGN2) {
339                 length = TDR_ALIGN(tdr, 2);
340         } else if (tdr->flags & TDR_ALIGN4) {
341                 length = TDR_ALIGN(tdr, 4);
342         } else if (tdr->flags & TDR_ALIGN8) {
343                 length = TDR_ALIGN(tdr, 8);
344         } else if (tdr->flags & TDR_REMAINING) {
345                 length = tdr->data.length - tdr->offset;
346         } else {
347                 return NT_STATUS_INVALID_PARAMETER;
348         }
349
350         if (tdr->data.length - tdr->offset < length) {
351                 length = tdr->data.length - tdr->offset;
352         }
353
354         TDR_PULL_NEED_BYTES(tdr, length);
355
356         *blob = data_blob_talloc(tdr, tdr->data.data+tdr->offset, length);
357         tdr->offset += length;
358         return NT_STATUS_OK;
359 }