xref: /freebsd/crypto/openssl/crypto/bio/bf_lbuf.c (revision f25b8c9fb4f58cf61adb47d7570abe7caa6d385d)
1 /*
2  * Copyright 1995-2026 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 #include <openssl/evp.h>
15 
16 static int linebuffer_write(BIO *h, const char *buf, int num);
17 static int linebuffer_read(BIO *h, char *buf, int size);
18 static int linebuffer_puts(BIO *h, const char *str);
19 static int linebuffer_gets(BIO *h, char *str, int size);
20 static long linebuffer_ctrl(BIO *h, int cmd, long arg1, void *arg2);
21 static int linebuffer_new(BIO *h);
22 static int linebuffer_free(BIO *data);
23 static long linebuffer_callback_ctrl(BIO *h, int cmd, BIO_info_cb *fp);
24 
25 /* A 10k maximum should be enough for most purposes */
26 #define DEFAULT_LINEBUFFER_SIZE 1024 * 10
27 
28 /* #define DEBUG */
29 
30 static const BIO_METHOD methods_linebuffer = {
31     BIO_TYPE_LINEBUFFER,
32     "linebuffer",
33     bwrite_conv,
34     linebuffer_write,
35     bread_conv,
36     linebuffer_read,
37     linebuffer_puts,
38     linebuffer_gets,
39     linebuffer_ctrl,
40     linebuffer_new,
41     linebuffer_free,
42     linebuffer_callback_ctrl,
43 };
44 
BIO_f_linebuffer(void)45 const BIO_METHOD *BIO_f_linebuffer(void)
46 {
47     return &methods_linebuffer;
48 }
49 
50 typedef struct bio_linebuffer_ctx_struct {
51     char *obuf; /* the output char array */
52     int obuf_size; /* how big is the output buffer */
53     int obuf_len; /* how many bytes are in it */
54 } BIO_LINEBUFFER_CTX;
55 
linebuffer_new(BIO * bi)56 static int linebuffer_new(BIO *bi)
57 {
58     BIO_LINEBUFFER_CTX *ctx;
59 
60     if ((ctx = OPENSSL_malloc(sizeof(*ctx))) == NULL)
61         return 0;
62     ctx->obuf = OPENSSL_malloc(DEFAULT_LINEBUFFER_SIZE);
63     if (ctx->obuf == NULL) {
64         OPENSSL_free(ctx);
65         return 0;
66     }
67     ctx->obuf_size = DEFAULT_LINEBUFFER_SIZE;
68     ctx->obuf_len = 0;
69 
70     bi->init = 1;
71     bi->ptr = (char *)ctx;
72     bi->flags = 0;
73     return 1;
74 }
75 
linebuffer_free(BIO * a)76 static int linebuffer_free(BIO *a)
77 {
78     BIO_LINEBUFFER_CTX *b;
79 
80     if (a == NULL)
81         return 0;
82     b = (BIO_LINEBUFFER_CTX *)a->ptr;
83     OPENSSL_free(b->obuf);
84     OPENSSL_free(a->ptr);
85     a->ptr = NULL;
86     a->init = 0;
87     a->flags = 0;
88     return 1;
89 }
90 
linebuffer_read(BIO * b,char * out,int outl)91 static int linebuffer_read(BIO *b, char *out, int outl)
92 {
93     int ret = 0;
94 
95     if (out == NULL)
96         return 0;
97     if (b->next_bio == NULL)
98         return 0;
99     ret = BIO_read(b->next_bio, out, outl);
100     BIO_clear_retry_flags(b);
101     BIO_copy_next_retry(b);
102     return ret;
103 }
104 
linebuffer_write(BIO * b,const char * in,int inl)105 static int linebuffer_write(BIO *b, const char *in, int inl)
106 {
107     int i, num = 0, foundnl;
108     BIO_LINEBUFFER_CTX *ctx;
109 
110     if ((in == NULL) || (inl <= 0))
111         return 0;
112     ctx = (BIO_LINEBUFFER_CTX *)b->ptr;
113     if ((ctx == NULL) || (b->next_bio == NULL))
114         return 0;
115 
116     BIO_clear_retry_flags(b);
117 
118     do {
119         const char *p;
120         char c;
121 
122         for (p = in, c = '\0'; p < in + inl && (c = *p) != '\n'; p++)
123             ;
124         if (c == '\n') {
125             p++;
126             foundnl = 1;
127         } else
128             foundnl = 0;
129 
130         /*
131          * If a NL was found and we already have text in the save buffer,
132          * concatenate them and write
133          */
134         while ((foundnl || p - in > ctx->obuf_size - ctx->obuf_len)
135             && ctx->obuf_len > 0) {
136             int orig_olen = ctx->obuf_len;
137 
138             i = ctx->obuf_size - ctx->obuf_len;
139             if (p - in > 0) {
140                 if (i >= p - in) {
141                     memcpy(&(ctx->obuf[ctx->obuf_len]), in, p - in);
142                     ctx->obuf_len += p - in;
143                     inl -= p - in;
144                     num += p - in;
145                     in = p;
146                 } else {
147                     memcpy(&(ctx->obuf[ctx->obuf_len]), in, i);
148                     ctx->obuf_len += i;
149                     inl -= i;
150                     in += i;
151                     num += i;
152                 }
153             }
154             i = BIO_write(b->next_bio, ctx->obuf, ctx->obuf_len);
155             if (i <= 0) {
156                 ctx->obuf_len = orig_olen;
157                 BIO_copy_next_retry(b);
158 
159                 if (i < 0)
160                     return ((num > 0) ? num : i);
161                 if (i == 0)
162                     return num;
163             }
164             if (i < ctx->obuf_len)
165                 memmove(ctx->obuf, ctx->obuf + i, ctx->obuf_len - i);
166             ctx->obuf_len -= i;
167         }
168 
169         /*
170          * Now that the save buffer is emptied, let's write the input buffer
171          * if a NL was found and there is anything to write.
172          */
173         if ((foundnl || p - in > ctx->obuf_size) && p - in > 0) {
174             i = BIO_write(b->next_bio, in, p - in);
175             if (i <= 0) {
176                 BIO_copy_next_retry(b);
177                 if (i < 0)
178                     return ((num > 0) ? num : i);
179                 if (i == 0)
180                     return num;
181             }
182             num += i;
183             in += i;
184             inl -= i;
185         }
186     } while (foundnl && inl > 0);
187     /*
188      * We've written as much as we can.  The rest of the input buffer, if
189      * any, is text that doesn't end with a NL and therefore we need to try
190      * free up some space in our obuf so we can make forward progress.
191      */
192     while (inl > 0) {
193         size_t avail = (size_t)ctx->obuf_size - (size_t)ctx->obuf_len;
194         size_t to_copy;
195 
196         if (avail == 0) {
197             /* Flush buffered data to make room */
198             i = BIO_write(b->next_bio, ctx->obuf, ctx->obuf_len);
199             if (i <= 0) {
200                 BIO_copy_next_retry(b);
201                 return num > 0 ? num : i;
202             }
203             if (i < ctx->obuf_len)
204                 memmove(ctx->obuf, ctx->obuf + i, ctx->obuf_len - i);
205             ctx->obuf_len -= i;
206             continue;
207         }
208 
209         to_copy = inl > (int)avail ? avail : (size_t)inl;
210         memcpy(&(ctx->obuf[ctx->obuf_len]), in, to_copy);
211         ctx->obuf_len += (int)to_copy;
212         in += to_copy;
213         inl -= (int)to_copy;
214         num += (int)to_copy;
215     }
216 
217     return num;
218 }
219 
linebuffer_ctrl(BIO * b,int cmd,long num,void * ptr)220 static long linebuffer_ctrl(BIO *b, int cmd, long num, void *ptr)
221 {
222     BIO *dbio;
223     BIO_LINEBUFFER_CTX *ctx;
224     long ret = 1;
225     char *p;
226     int r;
227     int obs;
228 
229     ctx = (BIO_LINEBUFFER_CTX *)b->ptr;
230 
231     switch (cmd) {
232     case BIO_CTRL_RESET:
233         ctx->obuf_len = 0;
234         if (b->next_bio == NULL)
235             return 0;
236         ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
237         break;
238     case BIO_CTRL_INFO:
239         ret = (long)ctx->obuf_len;
240         break;
241     case BIO_CTRL_WPENDING:
242         ret = (long)ctx->obuf_len;
243         if (ret == 0) {
244             if (b->next_bio == NULL)
245                 return 0;
246             ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
247         }
248         break;
249     case BIO_C_SET_BUFF_SIZE:
250         if (num > INT_MAX)
251             return 0;
252         obs = (int)num;
253         p = ctx->obuf;
254         if ((obs > DEFAULT_LINEBUFFER_SIZE) && (obs != ctx->obuf_size)) {
255             p = OPENSSL_malloc((size_t)obs);
256             if (p == NULL)
257                 return 0;
258         }
259         if (ctx->obuf != p) {
260             if (ctx->obuf_len > obs) {
261                 ctx->obuf_len = obs;
262             }
263             memcpy(p, ctx->obuf, ctx->obuf_len);
264             OPENSSL_free(ctx->obuf);
265             ctx->obuf = p;
266             ctx->obuf_size = obs;
267         }
268         break;
269     case BIO_C_DO_STATE_MACHINE:
270         if (b->next_bio == NULL)
271             return 0;
272         BIO_clear_retry_flags(b);
273         ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
274         BIO_copy_next_retry(b);
275         break;
276 
277     case BIO_CTRL_FLUSH:
278         if (b->next_bio == NULL)
279             return 0;
280         if (ctx->obuf_len <= 0) {
281             ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
282             BIO_copy_next_retry(b);
283             break;
284         }
285 
286         for (;;) {
287             BIO_clear_retry_flags(b);
288             if (ctx->obuf_len > 0) {
289                 r = BIO_write(b->next_bio, ctx->obuf, ctx->obuf_len);
290                 BIO_copy_next_retry(b);
291                 if (r <= 0)
292                     return (long)r;
293                 if (r < ctx->obuf_len)
294                     memmove(ctx->obuf, ctx->obuf + r, ctx->obuf_len - r);
295                 ctx->obuf_len -= r;
296             } else {
297                 ctx->obuf_len = 0;
298                 break;
299             }
300         }
301         ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
302         BIO_copy_next_retry(b);
303         break;
304     case BIO_CTRL_DUP:
305         dbio = (BIO *)ptr;
306         if (BIO_set_write_buffer_size(dbio, ctx->obuf_size) <= 0)
307             ret = 0;
308         break;
309     default:
310         if (b->next_bio == NULL)
311             return 0;
312         ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
313         break;
314     }
315     return ret;
316 }
317 
linebuffer_callback_ctrl(BIO * b,int cmd,BIO_info_cb * fp)318 static long linebuffer_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp)
319 {
320     if (b->next_bio == NULL)
321         return 0;
322     return BIO_callback_ctrl(b->next_bio, cmd, fp);
323 }
324 
linebuffer_gets(BIO * b,char * buf,int size)325 static int linebuffer_gets(BIO *b, char *buf, int size)
326 {
327     if (b->next_bio == NULL)
328         return 0;
329     return BIO_gets(b->next_bio, buf, size);
330 }
331 
linebuffer_puts(BIO * b,const char * str)332 static int linebuffer_puts(BIO *b, const char *str)
333 {
334     return linebuffer_write(b, str, strlen(str));
335 }
336