Author: Ross Kinsey <RossIKinsey@gmail.com>
[openssl.git] / crypto / bio / bf_buff.c
1 /*
2  * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the Apache License 2.0 (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 <stdio.h>
11 #include <errno.h>
12 #include "bio_local.h"
13 #include "internal/cryptlib.h"
14
15 static int buffer_write(BIO *h, const char *buf, int num);
16 static int buffer_read(BIO *h, char *buf, int size);
17 static int buffer_puts(BIO *h, const char *str);
18 static int buffer_gets(BIO *h, char *str, int size);
19 static long buffer_ctrl(BIO *h, int cmd, long arg1, void *arg2);
20 static int buffer_new(BIO *h);
21 static int buffer_free(BIO *data);
22 static long buffer_callback_ctrl(BIO *h, int cmd, BIO_info_cb *fp);
23 #define DEFAULT_BUFFER_SIZE     4096
24
25 static const BIO_METHOD methods_buffer = {
26     BIO_TYPE_BUFFER,
27     "buffer",
28     /* TODO: Convert to new style write function */
29     bwrite_conv,
30     buffer_write,
31     /* TODO: Convert to new style read function */
32     bread_conv,
33     buffer_read,
34     buffer_puts,
35     buffer_gets,
36     buffer_ctrl,
37     buffer_new,
38     buffer_free,
39     buffer_callback_ctrl,
40 };
41
42 const BIO_METHOD *BIO_f_buffer(void)
43 {
44     return &methods_buffer;
45 }
46
47 static int buffer_new(BIO *bi)
48 {
49     BIO_F_BUFFER_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx));
50
51     if (ctx == NULL)
52         return 0;
53     ctx->ibuf_size = DEFAULT_BUFFER_SIZE;
54     ctx->ibuf = OPENSSL_malloc(DEFAULT_BUFFER_SIZE);
55     if (ctx->ibuf == NULL) {
56         OPENSSL_free(ctx);
57         return 0;
58     }
59     ctx->obuf_size = DEFAULT_BUFFER_SIZE;
60     ctx->obuf = OPENSSL_malloc(DEFAULT_BUFFER_SIZE);
61     if (ctx->obuf == NULL) {
62         OPENSSL_free(ctx->ibuf);
63         OPENSSL_free(ctx);
64         return 0;
65     }
66
67     bi->init = 1;
68     bi->ptr = (char *)ctx;
69     bi->flags = 0;
70     return 1;
71 }
72
73 static int buffer_free(BIO *a)
74 {
75     BIO_F_BUFFER_CTX *b;
76
77     if (a == NULL)
78         return 0;
79     b = (BIO_F_BUFFER_CTX *)a->ptr;
80     OPENSSL_free(b->ibuf);
81     OPENSSL_free(b->obuf);
82     OPENSSL_free(a->ptr);
83     a->ptr = NULL;
84     a->init = 0;
85     a->flags = 0;
86     return 1;
87 }
88
89 static int buffer_read(BIO *b, char *out, int outl)
90 {
91     int i, num = 0;
92     BIO_F_BUFFER_CTX *ctx;
93
94     if (out == NULL)
95         return 0;
96     ctx = (BIO_F_BUFFER_CTX *)b->ptr;
97
98     if ((ctx == NULL) || (b->next_bio == NULL))
99         return 0;
100     num = 0;
101     BIO_clear_retry_flags(b);
102
103  start:
104     i = ctx->ibuf_len;
105     /* If there is stuff left over, grab it */
106     if (i != 0) {
107         if (i > outl)
108             i = outl;
109         memcpy(out, &(ctx->ibuf[ctx->ibuf_off]), i);
110         ctx->ibuf_off += i;
111         ctx->ibuf_len -= i;
112         num += i;
113         if (outl == i)
114             return num;
115         outl -= i;
116         out += i;
117     }
118
119     /*
120      * We may have done a partial read. try to do more. We have nothing in
121      * the buffer. If we get an error and have read some data, just return it
122      * and let them retry to get the error again. copy direct to parent
123      * address space
124      */
125     if (outl > ctx->ibuf_size) {
126         for (;;) {
127             i = BIO_read(b->next_bio, out, outl);
128             if (i <= 0) {
129                 BIO_copy_next_retry(b);
130                 if (i < 0)
131                     return ((num > 0) ? num : i);
132                 if (i == 0)
133                     return num;
134             }
135             num += i;
136             if (outl == i)
137                 return num;
138             out += i;
139             outl -= i;
140         }
141     }
142     /* else */
143
144     /* we are going to be doing some buffering */
145     i = BIO_read(b->next_bio, ctx->ibuf, ctx->ibuf_size);
146     if (i <= 0) {
147         BIO_copy_next_retry(b);
148         if (i < 0)
149             return ((num > 0) ? num : i);
150         if (i == 0)
151             return num;
152     }
153     ctx->ibuf_off = 0;
154     ctx->ibuf_len = i;
155
156     /* Lets re-read using ourselves :-) */
157     goto start;
158 }
159
160 static int buffer_write(BIO *b, const char *in, int inl)
161 {
162     int i, num = 0;
163     BIO_F_BUFFER_CTX *ctx;
164
165     if ((in == NULL) || (inl <= 0))
166         return 0;
167     ctx = (BIO_F_BUFFER_CTX *)b->ptr;
168     if ((ctx == NULL) || (b->next_bio == NULL))
169         return 0;
170
171     BIO_clear_retry_flags(b);
172  start:
173     i = ctx->obuf_size - (ctx->obuf_len + ctx->obuf_off);
174     /* add to buffer and return */
175     if (i >= inl) {
176         memcpy(&(ctx->obuf[ctx->obuf_off + ctx->obuf_len]), in, inl);
177         ctx->obuf_len += inl;
178         return (num + inl);
179     }
180     /* else */
181     /* stuff already in buffer, so add to it first, then flush */
182     if (ctx->obuf_len != 0) {
183         if (i > 0) {            /* lets fill it up if we can */
184             memcpy(&(ctx->obuf[ctx->obuf_off + ctx->obuf_len]), in, i);
185             in += i;
186             inl -= i;
187             num += i;
188             ctx->obuf_len += i;
189         }
190         /* we now have a full buffer needing flushing */
191         for (;;) {
192             i = BIO_write(b->next_bio, &(ctx->obuf[ctx->obuf_off]),
193                           ctx->obuf_len);
194             if (i <= 0) {
195                 BIO_copy_next_retry(b);
196
197                 if (i < 0)
198                     return ((num > 0) ? num : i);
199                 if (i == 0)
200                     return num;
201             }
202             ctx->obuf_off += i;
203             ctx->obuf_len -= i;
204             if (ctx->obuf_len == 0)
205                 break;
206         }
207     }
208     /*
209      * we only get here if the buffer has been flushed and we still have
210      * stuff to write
211      */
212     ctx->obuf_off = 0;
213
214     /* we now have inl bytes to write */
215     while (inl >= ctx->obuf_size) {
216         i = BIO_write(b->next_bio, in, inl);
217         if (i <= 0) {
218             BIO_copy_next_retry(b);
219             if (i < 0)
220                 return ((num > 0) ? num : i);
221             if (i == 0)
222                 return num;
223         }
224         num += i;
225         in += i;
226         inl -= i;
227         if (inl == 0)
228             return num;
229     }
230
231     /*
232      * copy the rest into the buffer since we have only a small amount left
233      */
234     goto start;
235 }
236
237 static long buffer_ctrl(BIO *b, int cmd, long num, void *ptr)
238 {
239     BIO *dbio;
240     BIO_F_BUFFER_CTX *ctx;
241     long ret = 1;
242     char *p1, *p2;
243     int r, i, *ip;
244     int ibs, obs;
245
246     ctx = (BIO_F_BUFFER_CTX *)b->ptr;
247
248     switch (cmd) {
249     case BIO_CTRL_RESET:
250         ctx->ibuf_off = 0;
251         ctx->ibuf_len = 0;
252         ctx->obuf_off = 0;
253         ctx->obuf_len = 0;
254         if (b->next_bio == NULL)
255             return 0;
256         ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
257         break;
258     case BIO_CTRL_EOF:
259         if (ctx->ibuf_len > 0)
260             return 0;
261         ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
262         break;
263     case BIO_CTRL_INFO:
264         ret = (long)ctx->obuf_len;
265         break;
266     case BIO_C_GET_BUFF_NUM_LINES:
267         ret = 0;
268         p1 = ctx->ibuf;
269         for (i = 0; i < ctx->ibuf_len; i++) {
270             if (p1[ctx->ibuf_off + i] == '\n')
271                 ret++;
272         }
273         break;
274     case BIO_CTRL_WPENDING:
275         ret = (long)ctx->obuf_len;
276         if (ret == 0) {
277             if (b->next_bio == NULL)
278                 return 0;
279             ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
280         }
281         break;
282     case BIO_CTRL_PENDING:
283         ret = (long)ctx->ibuf_len;
284         if (ret == 0) {
285             if (b->next_bio == NULL)
286                 return 0;
287             ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
288         }
289         break;
290     case BIO_C_SET_BUFF_READ_DATA:
291         if (num > ctx->ibuf_size) {
292             p1 = OPENSSL_malloc((int)num);
293             if (p1 == NULL)
294                 goto malloc_error;
295             OPENSSL_free(ctx->ibuf);
296             ctx->ibuf = p1;
297         }
298         ctx->ibuf_off = 0;
299         ctx->ibuf_len = (int)num;
300         memcpy(ctx->ibuf, ptr, (int)num);
301         ret = 1;
302         break;
303     case BIO_C_SET_BUFF_SIZE:
304         if (ptr != NULL) {
305             ip = (int *)ptr;
306             if (*ip == 0) {
307                 ibs = (int)num;
308                 obs = ctx->obuf_size;
309             } else {            /* if (*ip == 1) */
310
311                 ibs = ctx->ibuf_size;
312                 obs = (int)num;
313             }
314         } else {
315             ibs = (int)num;
316             obs = (int)num;
317         }
318         p1 = ctx->ibuf;
319         p2 = ctx->obuf;
320         if ((ibs > DEFAULT_BUFFER_SIZE) && (ibs != ctx->ibuf_size)) {
321             p1 = OPENSSL_malloc((int)num);
322             if (p1 == NULL)
323                 goto malloc_error;
324         }
325         if ((obs > DEFAULT_BUFFER_SIZE) && (obs != ctx->obuf_size)) {
326             p2 = OPENSSL_malloc((int)num);
327             if (p2 == NULL) {
328                 if (p1 != ctx->ibuf)
329                     OPENSSL_free(p1);
330                 goto malloc_error;
331             }
332         }
333         if (ctx->ibuf != p1) {
334             OPENSSL_free(ctx->ibuf);
335             ctx->ibuf = p1;
336             ctx->ibuf_off = 0;
337             ctx->ibuf_len = 0;
338             ctx->ibuf_size = ibs;
339         }
340         if (ctx->obuf != p2) {
341             OPENSSL_free(ctx->obuf);
342             ctx->obuf = p2;
343             ctx->obuf_off = 0;
344             ctx->obuf_len = 0;
345             ctx->obuf_size = obs;
346         }
347         break;
348     case BIO_C_DO_STATE_MACHINE:
349         if (b->next_bio == NULL)
350             return 0;
351         BIO_clear_retry_flags(b);
352         ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
353         BIO_copy_next_retry(b);
354         break;
355
356     case BIO_CTRL_FLUSH:
357         if (b->next_bio == NULL)
358             return 0;
359         if (ctx->obuf_len <= 0) {
360             ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
361             break;
362         }
363
364         for (;;) {
365             BIO_clear_retry_flags(b);
366             if (ctx->obuf_len > 0) {
367                 r = BIO_write(b->next_bio,
368                               &(ctx->obuf[ctx->obuf_off]), ctx->obuf_len);
369                 BIO_copy_next_retry(b);
370                 if (r <= 0)
371                     return (long)r;
372                 ctx->obuf_off += r;
373                 ctx->obuf_len -= r;
374             } else {
375                 ctx->obuf_len = 0;
376                 ctx->obuf_off = 0;
377                 break;
378             }
379         }
380         ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
381         break;
382     case BIO_CTRL_DUP:
383         dbio = (BIO *)ptr;
384         if (!BIO_set_read_buffer_size(dbio, ctx->ibuf_size) ||
385             !BIO_set_write_buffer_size(dbio, ctx->obuf_size))
386             ret = 0;
387         break;
388     case BIO_CTRL_PEEK:
389         /* Ensure there's stuff in the input buffer */
390         {
391             char fake_buf[1];
392             (void)buffer_read(b, fake_buf, 0);
393         }
394         if (num > ctx->ibuf_len)
395             num = ctx->ibuf_len;
396         memcpy(ptr, &(ctx->ibuf[ctx->ibuf_off]), num);
397         ret = num;
398         break;
399     default:
400         if (b->next_bio == NULL)
401             return 0;
402         ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
403         break;
404     }
405     return ret;
406  malloc_error:
407     BIOerr(BIO_F_BUFFER_CTRL, ERR_R_MALLOC_FAILURE);
408     return 0;
409 }
410
411 static long buffer_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp)
412 {
413     if (b->next_bio == NULL)
414         return 0;
415     return BIO_callback_ctrl(b->next_bio, cmd, fp);
416 }
417
418 static int buffer_gets(BIO *b, char *buf, int size)
419 {
420     BIO_F_BUFFER_CTX *ctx;
421     int num = 0, i, flag;
422     char *p;
423
424     ctx = (BIO_F_BUFFER_CTX *)b->ptr;
425     size--;                     /* reserve space for a '\0' */
426     BIO_clear_retry_flags(b);
427
428     for (;;) {
429         if (ctx->ibuf_len > 0) {
430             p = &(ctx->ibuf[ctx->ibuf_off]);
431             flag = 0;
432             for (i = 0; (i < ctx->ibuf_len) && (i < size); i++) {
433                 *(buf++) = p[i];
434                 if (p[i] == '\n') {
435                     flag = 1;
436                     i++;
437                     break;
438                 }
439             }
440             num += i;
441             size -= i;
442             ctx->ibuf_len -= i;
443             ctx->ibuf_off += i;
444             if (flag || size == 0) {
445                 *buf = '\0';
446                 return num;
447             }
448         } else {                /* read another chunk */
449
450             i = BIO_read(b->next_bio, ctx->ibuf, ctx->ibuf_size);
451             if (i <= 0) {
452                 BIO_copy_next_retry(b);
453                 *buf = '\0';
454                 if (i < 0)
455                     return ((num > 0) ? num : i);
456                 if (i == 0)
457                     return num;
458             }
459             ctx->ibuf_len = i;
460             ctx->ibuf_off = 0;
461         }
462     }
463 }
464
465 static int buffer_puts(BIO *b, const char *str)
466 {
467     return buffer_write(b, str, strlen(str));
468 }