First pass at writing a writeable packets API
[openssl.git] / ssl / packet.c
1 /*
2  * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the OpenSSL license (the "License").  You may not use
5  * this file except in compliance with the License.  You can obtain a copy
6  * in the file LICENSE in the source distribution or at
7  * https://www.openssl.org/source/license.html
8  */
9
10 #include "packet_locl.h"
11
12 /*
13  * Allocate bytes in the PACKETW_BUF for the output. This reserves the bytes
14  * and count them as "written", but doesn't actually do the writing.
15  */
16 static unsigned char *PACKETW_BUF_allocate(PACKETW_BUF *wbuf, size_t len)
17 {
18     unsigned char *ret = wbuf->curr;
19
20     if (SIZE_MAX - wbuf->written < len)
21         return 0;
22
23     if (wbuf->maxsize > 0 && wbuf->written + len > wbuf->maxsize)
24         return 0;
25
26     if (wbuf->buf->length - wbuf->written < len) {
27         size_t newlen;
28
29         if (wbuf->buf->length > SIZE_MAX / 2)
30             newlen = SIZE_MAX;
31         else
32             newlen = wbuf->buf->length * 2;
33         if (BUF_MEM_grow(wbuf->buf, newlen) == 0)
34             return NULL;
35     }
36     wbuf->written += len;
37     wbuf->curr += len;
38
39     return ret;
40 }
41
42 /*
43  * Initialise a PACKETW with the buffer in |buf|. The buffer must exist
44  * for the whole time that the PACKETW is being used. Additionally |lenbytes| of
45  * data is preallocated at the start of the buffer to store the length of the
46  * PACKETW once we know it.
47  */
48 int PACKETW_init_len(PACKETW *pkt, BUF_MEM *buf, size_t lenbytes)
49 {
50     PACKETW_BUF *wbuf;
51     /* Sanity check */
52     if (buf == NULL)
53         return 0;
54
55     wbuf = OPENSSL_zalloc(sizeof(PACKETW_BUF));
56     if (wbuf == NULL) {
57         pkt->isclosed = 1;
58         return 0;
59     }
60
61     wbuf->buf = buf;
62     wbuf->curr = (unsigned char *)buf->data;
63     wbuf->written = 0;
64     wbuf->maxsize = 0;
65
66     pkt->parent = NULL;
67     pkt->wbuf = wbuf;
68     pkt->pwritten = lenbytes;
69     pkt->lenbytes = lenbytes;
70     pkt->haschild = 0;
71     pkt->isclosed = 0;
72
73     if (lenbytes == 0) {
74         pkt->packet_len = NULL;
75         return 1;
76     }
77
78     pkt->packet_len = PACKETW_BUF_allocate(wbuf, lenbytes);
79     if (pkt->packet_len == NULL) {
80         OPENSSL_free(wbuf);
81         pkt->wbuf = NULL;
82         pkt->isclosed = 1;
83         return 0;
84     }
85
86     return 1;
87 }
88
89 /*
90  * Same as PACKETW_init_len except there is no preallocation of the PACKETW
91  * length.
92  */
93 int PACKETW_init(PACKETW *pkt, BUF_MEM *buf)
94 {
95     return PACKETW_init_len(pkt, buf, 0);
96 }
97
98 /*
99  * Set the PACKETW length, and the location for where we should write that
100  * length. Normally this will be at the start of the PACKETW, and therefore
101  * the PACKETW would have been initialised via PACKETW_init_len(). However there
102  * is the possibility that the length needs to be written to some other location
103  * other than the start of the PACKETW. In that case init via PACKETW_init() and
104  * then set the location for the length using this function.
105  */
106 int PACKETW_set_packet_len(PACKETW *pkt, unsigned char *packet_len,
107                            size_t lenbytes)
108 {
109     /* We only allow this to be set once */
110     if (pkt->isclosed || pkt->packet_len != NULL)
111         return 0;
112
113     pkt->lenbytes = lenbytes;
114     pkt->packet_len = packet_len;
115
116     return 1;
117 }
118
119 int PACKETW_set_flags(PACKETW *pkt, unsigned int flags)
120 {
121     pkt->flags = flags;
122
123     return 1;
124 }
125
126 /*
127  * Closes the PACKETW and marks it as invalid for future writes. It also writes
128  * out the length of the packet to the required location (normally the start
129  * of the PACKETW) if appropriate. A PACKETW cannot be closed if it has an
130  * active sub-packet.
131  */
132 int PACKETW_close(PACKETW *pkt)
133 {
134     size_t packlen;
135
136     if (pkt->isclosed || pkt->haschild)
137         return 0;
138
139     packlen = pkt->wbuf->written - pkt->pwritten;
140     if (packlen == 0 && pkt->flags & OPENSSL_PACKETW_FLAGS_NON_ZERO_LENGTH)
141         return 0;
142
143     if (packlen == 0
144             && pkt->flags & OPENSSL_PACKETW_FLAGS_ABANDON_ON_ZERO_LENGTH) {
145         /* Deallocate any bytes allocated for the length of the PACKETW */
146         if ((pkt->wbuf->curr - pkt->lenbytes) == pkt->packet_len) {
147             pkt->wbuf->written -= pkt->lenbytes;
148             pkt->wbuf->curr -= pkt->lenbytes;
149         }
150
151         /* Don't write out the packet length */
152         pkt->packet_len = NULL;
153     }
154
155     /* Write out the PACKETW length if needed */
156     if (pkt->packet_len != NULL) {
157         size_t lenbytes;
158
159         lenbytes = pkt->lenbytes;
160
161         for (; lenbytes > 0; lenbytes--) {
162             pkt->packet_len[lenbytes - 1] = (unsigned char)(packlen & 0xff);
163             packlen >>= 8;
164         }
165         if (packlen > 0) {
166             /*
167              * We've extended beyond the max allowed for the number of len bytes
168              */
169             return 0;
170         }
171     }
172
173     if (pkt->parent != NULL) {
174         if (pkt->parent->haschild != 1) {
175             /* Should not happen! */
176             return 0;
177         }
178         pkt->parent->haschild = 0;
179         pkt->parent = NULL;
180     }
181
182     pkt->isclosed = 1;
183
184     return 1;
185 }
186
187 /*
188  * Initialise a new sub-packet (|subpkt|), based on a parent (|pkt|).
189  * Additionally |lenbytes| of data is preallocated at the start of the
190  * sub-packet to store its length once we know it.
191  */
192 int PACKETW_get_sub_packet_len(PACKETW *pkt, PACKETW *subpkt, size_t lenbytes)
193 {
194     if (pkt->isclosed || pkt->haschild || subpkt == NULL)
195         return 0;
196
197     subpkt->parent = pkt;
198     subpkt->wbuf = pkt->wbuf;
199     subpkt->pwritten = pkt->wbuf->written + lenbytes;
200     subpkt->lenbytes = lenbytes;
201     subpkt->haschild = 0;
202     subpkt->isclosed = 0;
203
204     if (lenbytes == 0) {
205         subpkt->packet_len = NULL;
206         pkt->haschild = 1;
207         return 1;
208     }
209
210     subpkt->packet_len = PACKETW_BUF_allocate(pkt->wbuf, lenbytes);
211     if (subpkt->packet_len == NULL) {
212         subpkt->isclosed = 1;
213         return 0;
214     }
215
216     pkt->haschild = 1;
217
218     return 1;
219 }
220
221 /*
222  * Same as PACKETW_get_sub_packet_len() except no bytes are pre-allocated for
223  * the sub-packet length.
224  */
225 int PACKETW_get_sub_packet(PACKETW *pkt, PACKETW *subpkt)
226 {
227     return PACKETW_get_sub_packet_len(pkt, subpkt, 0);
228 }
229
230 /*
231  * Allocate some bytes in the PACKETW for writing. That number of bytes is
232  * marked as having been written, and a pointer to their location is stored in
233  * |*allocbytes|.
234  */
235 int PACKETW_allocate_bytes(PACKETW *pkt, size_t bytes,
236                            unsigned char **allocbytes)
237 {
238     unsigned char *data;
239
240     if (pkt->isclosed || pkt->haschild || bytes == 0)
241         return 0;
242
243     data = PACKETW_BUF_allocate(pkt->wbuf, bytes);
244     if (data == NULL)
245         return 0;
246
247     *allocbytes = data;
248
249     return 1;
250 }
251
252 /*
253  * Write the value stored in |val| into the PACKETW. The value will consome
254  * |bytes| amount of storage. An error will occur if |val| cannot be accommdated
255  * in |bytes| storage, e.g. attempting to write the value 256 into 1 byte will
256  * fail.
257  */
258 int PACKETW_put_bytes(PACKETW *pkt, unsigned int val, size_t bytes)
259 {
260     unsigned char *data;
261
262     if (bytes > sizeof(unsigned int)
263             || !PACKETW_allocate_bytes(pkt, bytes, &data))
264         return 0;
265
266     data += bytes - 1;
267     for (; bytes > 0; bytes--) {
268         *data = (unsigned char)(val & 0xff);
269         data--;
270         val >>= 8;
271     }
272
273     /* Check whether we could fit the value in the assigned number of bytes */
274     if (val > 0)
275         return 0;
276
277     return 1;
278 }
279
280 /*
281  * Set a maximum size that we will not allow the PACKETW to grow beyond. If not
282  * set then there is no maximum.
283  */
284 int PACKETW_set_max_size(PACKETW *pkt, size_t maxsize)
285 {
286     pkt->wbuf->maxsize = maxsize;
287
288     return 1;
289 }
290
291 /*
292  * Copy |len| bytes of data from |*src| into the PACKETW.
293  */
294 int PACKETW_memcpy(PACKETW *pkt, const void *src, size_t len)
295 {
296     unsigned char *dest;
297
298     if (len == 0)
299         return 1;
300
301     if (!PACKETW_allocate_bytes(pkt, len, &dest))
302         return 0;
303
304     memcpy(dest, src, len);
305
306     return 1;
307 }
308
309 /*
310  * Return the total number of bytes written so far to the underlying buffer.
311  * This might includes bytes written by a parent PACKETW.
312  */
313 int PACKETW_get_total_written(PACKETW *pkt, size_t *written)
314 {
315     if (pkt->isclosed || written == NULL)
316         return 0;
317
318     *written = pkt->wbuf->curr - (unsigned char *)pkt->wbuf->buf->data;
319
320     return 1;
321 }
322
323 /*
324  * Returns the length of this PACKETW so far. This excludes any bytes allocated
325  * for the length itself.
326  */
327 int PACKETW_get_length(PACKETW *pkt, size_t *len)
328 {
329     if (pkt->isclosed || len == NULL)
330         return 0;
331
332     *len = pkt->wbuf->written - pkt->pwritten;
333
334     return 1;
335 }