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