r10013: Support zero-sized strings.
[bbaumbach/samba-autobuild/.git] / source4 / 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_CVAL(tdr, ofs) CVAL(tdr->data.data,ofs)
40 #define TDR_SVAL(tdr, ofs) (TDR_BE(tdr)?RSVAL(tdr->data.data,ofs):SVAL(tdr->data.data,ofs))
41 #define TDR_IVAL(tdr, ofs) (TDR_BE(tdr)?RIVAL(tdr->data.data,ofs):IVAL(tdr->data.data,ofs))
42 #define TDR_IVALS(tdr, ofs) (TDR_BE(tdr)?RIVALS(tdr->data.data,ofs):IVALS(tdr->data.data,ofs))
43 #define TDR_SCVAL(tdr, ofs, v) SCVAL(tdr->data.data,ofs,v)
44 #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)
45 #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)
46 #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)
47
48 /*
49   expand the available space in the buffer to 'size'
50 */
51 NTSTATUS tdr_push_expand(struct tdr_push *tdr, uint32_t size)
52 {
53         if (talloc_get_size(tdr->data.data) >= size) {
54                 return NT_STATUS_OK;
55         }
56
57         tdr->data.data = talloc_realloc(tdr, tdr->data.data, uint8_t, tdr->data.length + TDR_BASE_MARSHALL_SIZE);
58         return NT_STATUS_NO_MEMORY;
59 }
60
61
62 NTSTATUS tdr_pull_uint8(struct tdr_pull *tdr, uint8_t *v)
63 {
64         TDR_PULL_NEED_BYTES(tdr, 1);
65         *v = TDR_CVAL(tdr, tdr->offset);
66         tdr->offset += 1;
67         return NT_STATUS_OK;
68 }
69
70 NTSTATUS tdr_push_uint8(struct tdr_push *tdr, const uint8_t *v)
71 {
72         TDR_PUSH_NEED_BYTES(tdr, 1);
73         TDR_SCVAL(tdr, tdr->offset, *v);
74         tdr->offset += 1;
75         return NT_STATUS_OK;
76 }
77
78 NTSTATUS tdr_print_uint8(struct tdr_print *tdr, const char *name, uint8_t *v)
79 {
80         tdr->print(tdr, "%-25s: 0x%02x (%u)", name, *v, *v);
81         return NT_STATUS_OK;
82 }
83
84 NTSTATUS tdr_pull_uint16(struct tdr_pull *tdr, uint16_t *v)
85 {
86         TDR_PULL_NEED_BYTES(tdr, 2);
87         *v = TDR_SVAL(tdr, tdr->offset);
88         tdr->offset += 2;
89         return NT_STATUS_OK;
90 }
91
92 NTSTATUS tdr_push_uint16(struct tdr_push *tdr, const uint16_t *v)
93 {
94         TDR_PUSH_NEED_BYTES(tdr, 2);
95         TDR_SSVAL(tdr, tdr->offset, *v);
96         tdr->offset += 2;
97         return NT_STATUS_OK;
98 }
99
100 NTSTATUS tdr_print_uint16(struct tdr_print *tdr, const char *name, uint16_t *v)
101 {
102         tdr->print(tdr, "%-25s: 0x%02x (%u)", name, *v, *v);
103         return NT_STATUS_OK;
104 }
105
106 NTSTATUS tdr_pull_uint32(struct tdr_pull *tdr, uint32_t *v)
107 {
108         TDR_PULL_NEED_BYTES(tdr, 4);
109         *v = TDR_IVAL(tdr, tdr->offset);
110         tdr->offset += 4;
111         return NT_STATUS_OK;
112 }
113
114 NTSTATUS tdr_push_uint32(struct tdr_push *tdr, const uint32_t *v)
115 {
116         TDR_PUSH_NEED_BYTES(tdr, 4);
117         TDR_SIVAL(tdr, tdr->offset, *v);
118         tdr->offset += 4;
119         return NT_STATUS_OK;
120 }
121
122 NTSTATUS tdr_print_uint32(struct tdr_print *tdr, const char *name, uint32_t *v)
123 {
124         tdr->print(tdr, "%-25s: 0x%02x (%u)", name, *v, *v);
125         return NT_STATUS_OK;
126 }
127
128 NTSTATUS tdr_pull_charset(struct tdr_pull *tdr, const char **v, uint32_t length, uint32_t el_size, int chset)
129 {
130         int ret;
131
132         if (length == -1) {
133                 switch (chset) {
134                         case CH_DOS:
135                                 length = ascii_len_n((const char*)tdr->data.data+tdr->offset, tdr->data.length-tdr->offset);
136                                 break;
137                         case CH_UTF16:
138                                 length = utf16_len_n(tdr->data.data+tdr->offset, tdr->data.length-tdr->offset);
139                                 break;
140
141                         default:
142                                 return NT_STATUS_INVALID_PARAMETER;
143                 }
144         }
145
146         if (length == 0) {
147                 *v = talloc_strdup(tdr, "");
148                 return NT_STATUS_OK;
149         }
150
151         TDR_PULL_NEED_BYTES(tdr, el_size*length);
152         
153         ret = convert_string_talloc(tdr, chset, CH_UNIX, tdr->data.data+tdr->offset, el_size*length, discard_const_p(void *, v));
154
155         if (ret == -1) {
156                 return NT_STATUS_INVALID_PARAMETER;
157         }
158
159         tdr->offset += length * el_size;
160
161         return NT_STATUS_OK;
162 }
163
164 NTSTATUS tdr_push_charset(struct tdr_push *tdr, const char **v, uint32_t length, uint32_t el_size, int chset)
165 {
166         ssize_t ret, required;
167
168         required = el_size * length;
169         TDR_PUSH_NEED_BYTES(tdr, required);
170
171         ret = convert_string(CH_UNIX, chset, *v, strlen(*v), tdr->data.data+tdr->offset, required);
172
173         if (ret == -1) {
174                 return NT_STATUS_INVALID_PARAMETER;
175         }
176
177         /* Make sure the remaining part of the string is filled with zeroes */
178         if (ret < required) {
179                 memset(tdr->data.data+tdr->offset+ret, 0, required-ret);
180         }
181         
182         tdr->offset += required;
183                                                  
184         return NT_STATUS_OK;
185 }
186
187 NTSTATUS tdr_print_charset(struct tdr_print *tdr, const char *name, const char **v, uint32_t length, uint32_t el_size, int chset)
188 {
189         tdr->print(tdr, "%-25s: %s", name, *v);
190         return NT_STATUS_OK;
191 }
192
193 /*
194   pull a ipv4address
195 */
196 NTSTATUS tdr_pull_ipv4address(struct tdr_pull *tdr, const char **address)
197 {
198         struct ipv4_addr in;
199         TDR_CHECK(tdr_pull_uint32(tdr, &in.addr));
200         in.addr = htonl(in.addr);
201         *address = talloc_strdup(tdr, sys_inet_ntoa(in));
202         NT_STATUS_HAVE_NO_MEMORY(*address);
203         return NT_STATUS_OK;
204 }
205
206 /*
207   push a ipv4address
208 */
209 NTSTATUS tdr_push_ipv4address(struct tdr_push *tdr, const char **address)
210 {
211         uint32_t addr = htonl(interpret_addr(*address));
212         TDR_CHECK(tdr_push_uint32(tdr, &addr));
213         return NT_STATUS_OK;
214 }
215
216 /*
217   print a ipv4address
218 */
219 NTSTATUS tdr_print_ipv4address(struct tdr_print *tdr, const char *name, 
220                            const char **address)
221 {
222         tdr->print(tdr, "%-25s: %s", name, *address);
223         return NT_STATUS_OK;
224 }
225
226 /*
227   parse a hyper
228 */
229 NTSTATUS tdr_pull_hyper(struct tdr_pull *tdr, uint64_t *v)
230 {
231         TDR_PULL_NEED_BYTES(tdr, 8);
232         *v = TDR_IVAL(tdr, tdr->offset);
233         *v |= (uint64_t)(TDR_IVAL(tdr, tdr->offset+4)) << 32;
234         tdr->offset += 8;
235         return NT_STATUS_OK;
236 }
237
238 /*
239   push a hyper
240 */
241 NTSTATUS tdr_push_hyper(struct tdr_push *tdr, uint64_t *v)
242 {
243         TDR_PUSH_NEED_BYTES(tdr, 8);
244         TDR_SIVAL(tdr, tdr->offset, ((*v) & 0xFFFFFFFF));
245         TDR_SIVAL(tdr, tdr->offset+4, ((*v)>>32));
246         tdr->offset += 8;
247         return NT_STATUS_OK;
248 }
249
250
251
252 /*
253   push a NTTIME
254 */
255 NTSTATUS tdr_push_NTTIME(struct tdr_push *tdr, NTTIME *t)
256 {
257         TDR_CHECK(tdr_push_hyper(tdr, t));
258         return NT_STATUS_OK;
259 }
260
261 /*
262   pull a NTTIME
263 */
264 NTSTATUS tdr_pull_NTTIME(struct tdr_pull *tdr, NTTIME *t)
265 {
266         TDR_CHECK(tdr_pull_hyper(tdr, t));
267         return NT_STATUS_OK;
268 }
269
270 /*
271   push a time_t
272 */
273 NTSTATUS tdr_push_time_t(struct tdr_push *tdr, time_t *t)
274 {
275         return tdr_push_uint32(tdr, (uint32_t *)t);
276 }
277
278 /*
279   pull a time_t
280 */
281 NTSTATUS tdr_pull_time_t(struct tdr_pull *tdr, time_t *t)
282 {
283         uint32_t tt;
284         TDR_CHECK(tdr_pull_uint32(tdr, &tt));
285         *t = tt;
286         return NT_STATUS_OK;
287 }
288
289 NTSTATUS tdr_print_time_t(struct tdr_print *tdr, const char *name, time_t *t)
290 {
291         if (*t == (time_t)-1 || *t == 0) {
292                 tdr->print(tdr, "%-25s: (time_t)%d", name, (int)*t);
293         } else {
294                 tdr->print(tdr, "%-25s: %s", name, timestring(tdr, *t));
295         }
296
297         return NT_STATUS_OK;
298 }
299
300 NTSTATUS tdr_print_NTTIME(struct tdr_print *tdr, const char *name, NTTIME *t)
301 {
302         tdr->print(tdr, "%-25s: %s", name, nt_time_string(tdr, *t));
303
304         return NT_STATUS_OK;
305 }
306
307 NTSTATUS tdr_print_DATA_BLOB(struct tdr_print *tdr, const char *name, DATA_BLOB *r)
308 {
309         tdr->print(tdr, "%-25s: DATA_BLOB length=%u", name, r->length);
310         if (r->length) {
311                 dump_data(10, r->data, r->length);
312         }
313
314         return NT_STATUS_OK;
315 }
316
317 #define TDR_ALIGN(tdr,n) (((tdr)->offset & ((n)-1)) == 0?0:((n)-((tdr)->offset&((n)-1))))
318
319 /*
320   push a DATA_BLOB onto the wire. 
321 */
322 NTSTATUS tdr_push_DATA_BLOB(struct tdr_push *tdr, DATA_BLOB *blob)
323 {
324         if (tdr->flags & TDR_ALIGN2) {
325                 blob->length = TDR_ALIGN(tdr, 2);
326         } else if (tdr->flags & TDR_ALIGN4) {
327                 blob->length = TDR_ALIGN(tdr, 4);
328         } else if (tdr->flags & TDR_ALIGN8) {
329                 blob->length = TDR_ALIGN(tdr, 8);
330         }
331
332         TDR_PUSH_NEED_BYTES(tdr, blob->length);
333         
334         memcpy(tdr->data.data+tdr->offset, blob->data, blob->length);
335         return NT_STATUS_OK;
336 }
337
338 /*
339   pull a DATA_BLOB from the wire. 
340 */
341 NTSTATUS tdr_pull_DATA_BLOB(struct tdr_pull *tdr, DATA_BLOB *blob)
342 {
343         uint32_t length;
344
345         if (tdr->flags & TDR_ALIGN2) {
346                 length = TDR_ALIGN(tdr, 2);
347         } else if (tdr->flags & TDR_ALIGN4) {
348                 length = TDR_ALIGN(tdr, 4);
349         } else if (tdr->flags & TDR_ALIGN8) {
350                 length = TDR_ALIGN(tdr, 8);
351         } else if (tdr->flags & TDR_REMAINING) {
352                 length = tdr->data.length - tdr->offset;
353         } else {
354                 return NT_STATUS_INVALID_PARAMETER;
355         }
356
357         if (tdr->data.length - tdr->offset < length) {
358                 length = tdr->data.length - tdr->offset;
359         }
360
361         TDR_PULL_NEED_BYTES(tdr, length);
362
363         *blob = data_blob_talloc(tdr, tdr->data.data+tdr->offset, length);
364         tdr->offset += length;
365         return NT_STATUS_OK;
366 }