bf_lbuf.c revision 280304
1223328Sgavin/* crypto/bio/bf_buff.c */
2223328Sgavin/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
379971Sobrien * All rights reserved.
479971Sobrien *
5223328Sgavin * This package is an SSL implementation written
679971Sobrien * by Eric Young (eay@cryptsoft.com).
779971Sobrien * The implementation was written so as to conform with Netscapes SSL.
879971Sobrien *
979971Sobrien * This library is free for commercial and non-commercial use as long as
1079971Sobrien * the following conditions are aheared to.  The following conditions
1179971Sobrien * apply to all code found in this distribution, be it the RC4, RSA,
1279971Sobrien * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
1379971Sobrien * included with this distribution is covered by the same copyright terms
1479971Sobrien * except that the holder is Tim Hudson (tjh@cryptsoft.com).
1579971Sobrien *
1679971Sobrien * Copyright remains Eric Young's, and as such any Copyright notices in
1779971Sobrien * the code are not to be removed.
1879971Sobrien * If this package is used in a product, Eric Young should be given attribution
1979971Sobrien * as the author of the parts of the library used.
2079971Sobrien * This can be in the form of a textual message at program startup or
2179971Sobrien * in documentation (online or textual) provided with the package.
2279971Sobrien *
2379971Sobrien * Redistribution and use in source and binary forms, with or without
2479971Sobrien * modification, are permitted provided that the following conditions
2579971Sobrien * are met:
2679971Sobrien * 1. Redistributions of source code must retain the copyright
2779971Sobrien *    notice, this list of conditions and the following disclaimer.
2879971Sobrien * 2. Redistributions in binary form must reproduce the above copyright
2979971Sobrien *    notice, this list of conditions and the following disclaimer in the
3079971Sobrien *    documentation and/or other materials provided with the distribution.
3179971Sobrien * 3. All advertising materials mentioning features or use of this software
3279971Sobrien *    must display the following acknowledgement:
3379971Sobrien *    "This product includes cryptographic software written by
3479971Sobrien *     Eric Young (eay@cryptsoft.com)"
3579971Sobrien *    The word 'cryptographic' can be left out if the rouines from the library
3679971Sobrien *    being used are not cryptographic related :-).
3779971Sobrien * 4. If you include any Windows specific code (or a derivative thereof) from
3879971Sobrien *    the apps directory (application code) you must include an acknowledgement:
3979971Sobrien *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
4079971Sobrien *
4179971Sobrien * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
4279971Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4379971Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4479971Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
45121966Smikeh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
4679971Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
4779971Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
4879971Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
4979971Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5079971Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
5179971Sobrien * SUCH DAMAGE.
5279971Sobrien *
5379971Sobrien * The licence and distribution terms for any publically available version or
5479971Sobrien * derivative of this code cannot be changed.  i.e. this code cannot simply be
5579971Sobrien * copied and put under another distribution licence
5679971Sobrien * [including the GNU Public Licence.]
5779971Sobrien */
5879971Sobrien
5979971Sobrien#include <stdio.h>
6079971Sobrien#include <errno.h>
6179971Sobrien#include "cryptlib.h"
6279971Sobrien#include <openssl/bio.h>
6379971Sobrien#include <openssl/evp.h>
6479971Sobrien
65146309Smikehstatic int linebuffer_write(BIO *h, const char *buf, int num);
6679971Sobrienstatic int linebuffer_read(BIO *h, char *buf, int size);
6779971Sobrienstatic int linebuffer_puts(BIO *h, const char *str);
6879971Sobrienstatic int linebuffer_gets(BIO *h, char *str, int size);
6979971Sobrienstatic long linebuffer_ctrl(BIO *h, int cmd, long arg1, void *arg2);
7079971Sobrienstatic int linebuffer_new(BIO *h);
7179971Sobrienstatic int linebuffer_free(BIO *data);
7279971Sobrienstatic long linebuffer_callback_ctrl(BIO *h, int cmd, bio_info_cb *fp);
7379971Sobrien
7479971Sobrien/* A 10k maximum should be enough for most purposes */
7579971Sobrien#define DEFAULT_LINEBUFFER_SIZE 1024*10
7679971Sobrien
77146309Smikeh/* #define DEBUG */
7879971Sobrien
7979971Sobrienstatic BIO_METHOD methods_linebuffer = {
8079971Sobrien    BIO_TYPE_LINEBUFFER,
8179971Sobrien    "linebuffer",
8279971Sobrien    linebuffer_write,
8379971Sobrien    linebuffer_read,
8479971Sobrien    linebuffer_puts,
8579971Sobrien    linebuffer_gets,
8679971Sobrien    linebuffer_ctrl,
8779971Sobrien    linebuffer_new,
8879971Sobrien    linebuffer_free,
8979971Sobrien    linebuffer_callback_ctrl,
9079971Sobrien};
91223328Sgavin
92223328SgavinBIO_METHOD *BIO_f_linebuffer(void)
93223328Sgavin{
94223328Sgavin    return (&methods_linebuffer);
95116424Smikeh}
96116424Smikeh
97223328Sgavintypedef struct bio_linebuffer_ctx_struct {
98223328Sgavin    char *obuf;                 /* the output char array */
99223328Sgavin    int obuf_size;              /* how big is the output buffer */
100116424Smikeh    int obuf_len;               /* how many bytes are in it */
101116424Smikeh} BIO_LINEBUFFER_CTX;
102116424Smikeh
103116424Smikehstatic int linebuffer_new(BIO *bi)
104116424Smikeh{
105116424Smikeh    BIO_LINEBUFFER_CTX *ctx;
106223328Sgavin
107116424Smikeh    ctx = (BIO_LINEBUFFER_CTX *)OPENSSL_malloc(sizeof(BIO_LINEBUFFER_CTX));
108116424Smikeh    if (ctx == NULL)
109116424Smikeh        return (0);
11079971Sobrien    ctx->obuf = (char *)OPENSSL_malloc(DEFAULT_LINEBUFFER_SIZE);
11179971Sobrien    if (ctx->obuf == NULL) {
11279971Sobrien        OPENSSL_free(ctx);
113116424Smikeh        return (0);
114116424Smikeh    }
11579971Sobrien    ctx->obuf_size = DEFAULT_LINEBUFFER_SIZE;
116116424Smikeh    ctx->obuf_len = 0;
117116424Smikeh
118116424Smikeh    bi->init = 1;
119116424Smikeh    bi->ptr = (char *)ctx;
120116424Smikeh    bi->flags = 0;
121142129Smikeh    return (1);
122116424Smikeh}
123116424Smikeh
124116424Smikehstatic int linebuffer_free(BIO *a)
125223328Sgavin{
126116424Smikeh    BIO_LINEBUFFER_CTX *b;
127116424Smikeh
12879971Sobrien    if (a == NULL)
129223328Sgavin        return (0);
130223328Sgavin    b = (BIO_LINEBUFFER_CTX *)a->ptr;
13179971Sobrien    if (b->obuf != NULL)
13279971Sobrien        OPENSSL_free(b->obuf);
13379971Sobrien    OPENSSL_free(a->ptr);
13479971Sobrien    a->ptr = NULL;
13579971Sobrien    a->init = 0;
13679971Sobrien    a->flags = 0;
13779971Sobrien    return (1);
13879971Sobrien}
139223328Sgavin
14079971Sobrienstatic int linebuffer_read(BIO *b, char *out, int outl)
14179971Sobrien{
14279971Sobrien    int ret = 0;
143223328Sgavin
14479971Sobrien    if (out == NULL)
14579971Sobrien        return (0);
14698247Smikeh    if (b->next_bio == NULL)
147223328Sgavin        return (0);
148223328Sgavin    ret = BIO_read(b->next_bio, out, outl);
149232779Sgavin    BIO_clear_retry_flags(b);
150223328Sgavin    BIO_copy_next_retry(b);
15179971Sobrien    return (ret);
152223328Sgavin}
153223328Sgavin
15498247Smikehstatic int linebuffer_write(BIO *b, const char *in, int inl)
155223328Sgavin{
15698247Smikeh    int i, num = 0, foundnl;
15779971Sobrien    BIO_LINEBUFFER_CTX *ctx;
158142129Smikeh
159142129Smikeh    if ((in == NULL) || (inl <= 0))
16079971Sobrien        return (0);
16179971Sobrien    ctx = (BIO_LINEBUFFER_CTX *)b->ptr;
16279971Sobrien    if ((ctx == NULL) || (b->next_bio == NULL))
16379971Sobrien        return (0);
16479971Sobrien
16579971Sobrien    BIO_clear_retry_flags(b);
16679971Sobrien
16779971Sobrien    do {
16879971Sobrien        const char *p;
16979971Sobrien
17079971Sobrien        for (p = in; p < in + inl && *p != '\n'; p++) ;
17179971Sobrien        if (*p == '\n') {
17279971Sobrien            p++;
17379971Sobrien            foundnl = 1;
17479971Sobrien        } else
17579971Sobrien            foundnl = 0;
17679971Sobrien
17779971Sobrien        /*
17879971Sobrien         * If a NL was found and we already have text in the save buffer,
17979971Sobrien         * concatenate them and write
18079971Sobrien         */
18179971Sobrien        while ((foundnl || p - in > ctx->obuf_size - ctx->obuf_len)
18279971Sobrien               && ctx->obuf_len > 0) {
18379971Sobrien            int orig_olen = ctx->obuf_len;
18479971Sobrien
18579971Sobrien            i = ctx->obuf_size - ctx->obuf_len;
18679971Sobrien            if (p - in > 0) {
18779971Sobrien                if (i >= p - in) {
18879971Sobrien                    memcpy(&(ctx->obuf[ctx->obuf_len]), in, p - in);
18979971Sobrien                    ctx->obuf_len += p - in;
19079971Sobrien                    inl -= p - in;
19179971Sobrien                    num += p - in;
19279971Sobrien                    in = p;
193223328Sgavin                } else {
19479971Sobrien                    memcpy(&(ctx->obuf[ctx->obuf_len]), in, i);
19579971Sobrien                    ctx->obuf_len += i;
196223328Sgavin                    inl -= i;
19779971Sobrien                    in += i;
19879971Sobrien                    num += i;
199223328Sgavin                }
200223328Sgavin            }
20179971Sobrien#if 0
20279971Sobrien            BIO_write(b->next_bio, "<*<", 3);
20379971Sobrien#endif
204223328Sgavin            i = BIO_write(b->next_bio, ctx->obuf, ctx->obuf_len);
20595504Smikeh            if (i <= 0) {
206223328Sgavin                ctx->obuf_len = orig_olen;
207223328Sgavin                BIO_copy_next_retry(b);
208223328Sgavin
20979971Sobrien#if 0
21098247Smikeh                BIO_write(b->next_bio, ">*>", 3);
21198247Smikeh#endif
21298247Smikeh                if (i < 0)
21398247Smikeh                    return ((num > 0) ? num : i);
21498247Smikeh                if (i == 0)
215223328Sgavin                    return (num);
21679971Sobrien            }
21779971Sobrien#if 0
21879971Sobrien            BIO_write(b->next_bio, ">*>", 3);
21979971Sobrien#endif
22079971Sobrien            if (i < ctx->obuf_len)
22179971Sobrien                memmove(ctx->obuf, ctx->obuf + i, ctx->obuf_len - i);
22279971Sobrien            ctx->obuf_len -= i;
22379971Sobrien        }
22479971Sobrien
22579971Sobrien        /*
22679971Sobrien         * Now that the save buffer is emptied, let's write the input buffer
22779971Sobrien         * if a NL was found and there is anything to write.
22879971Sobrien         */
22979971Sobrien        if ((foundnl || p - in > ctx->obuf_size) && p - in > 0) {
23079971Sobrien#if 0
23179971Sobrien            BIO_write(b->next_bio, "<*<", 3);
23279971Sobrien#endif
233223328Sgavin            i = BIO_write(b->next_bio, in, p - in);
23479971Sobrien            if (i <= 0) {
23579971Sobrien                BIO_copy_next_retry(b);
23698247Smikeh#if 0
23779971Sobrien                BIO_write(b->next_bio, ">*>", 3);
23879971Sobrien#endif
23998247Smikeh                if (i < 0)
24079971Sobrien                    return ((num > 0) ? num : i);
24179971Sobrien                if (i == 0)
24279971Sobrien                    return (num);
24379971Sobrien            }
24479971Sobrien#if 0
24579971Sobrien            BIO_write(b->next_bio, ">*>", 3);
24679971Sobrien#endif
24779971Sobrien            num += i;
24879971Sobrien            in += i;
24979971Sobrien            inl -= i;
25079971Sobrien        }
25179971Sobrien    }
25279971Sobrien    while (foundnl && inl > 0);
25379971Sobrien    /*
25479971Sobrien     * We've written as much as we can.  The rest of the input buffer, if
25579971Sobrien     * any, is text that doesn't and with a NL and therefore needs to be
25679971Sobrien     * saved for the next trip.
25779971Sobrien     */
25879971Sobrien    if (inl > 0) {
25979971Sobrien        memcpy(&(ctx->obuf[ctx->obuf_len]), in, inl);
26079971Sobrien        ctx->obuf_len += inl;
26179971Sobrien        num += inl;
26279971Sobrien    }
26379971Sobrien    return num;
26479971Sobrien}
26579971Sobrien
26679971Sobrienstatic long linebuffer_ctrl(BIO *b, int cmd, long num, void *ptr)
26779971Sobrien{
26879971Sobrien    BIO *dbio;
26979971Sobrien    BIO_LINEBUFFER_CTX *ctx;
27079971Sobrien    long ret = 1;
27179971Sobrien    char *p;
27279971Sobrien    int r;
27379971Sobrien    int obs;
274223328Sgavin
27579971Sobrien    ctx = (BIO_LINEBUFFER_CTX *)b->ptr;
27695504Smikeh
27795504Smikeh    switch (cmd) {
27895504Smikeh    case BIO_CTRL_RESET:
27995504Smikeh        ctx->obuf_len = 0;
28095504Smikeh        if (b->next_bio == NULL)
28198247Smikeh            return (0);
28295504Smikeh        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
28398247Smikeh        break;
28498247Smikeh    case BIO_CTRL_INFO:
28598247Smikeh        ret = (long)ctx->obuf_len;
28695504Smikeh        break;
28795504Smikeh    case BIO_CTRL_WPENDING:
28879971Sobrien        ret = (long)ctx->obuf_len;
28979971Sobrien        if (ret == 0) {
29079971Sobrien            if (b->next_bio == NULL)
29179971Sobrien                return (0);
29279971Sobrien            ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
29379971Sobrien        }
29479971Sobrien        break;
29579971Sobrien    case BIO_C_SET_BUFF_SIZE:
29679971Sobrien        obs = (int)num;
29779971Sobrien        p = ctx->obuf;
29879971Sobrien        if ((obs > DEFAULT_LINEBUFFER_SIZE) && (obs != ctx->obuf_size)) {
299223328Sgavin            p = (char *)OPENSSL_malloc((int)num);
30079971Sobrien            if (p == NULL)
30179971Sobrien                goto malloc_error;
30279971Sobrien        }
30379971Sobrien        if (ctx->obuf != p) {
30479971Sobrien            if (ctx->obuf_len > obs) {
30579971Sobrien                ctx->obuf_len = obs;
30679971Sobrien            }
30779971Sobrien            memcpy(p, ctx->obuf, ctx->obuf_len);
30879971Sobrien            OPENSSL_free(ctx->obuf);
30979971Sobrien            ctx->obuf = p;
31079971Sobrien            ctx->obuf_size = obs;
31179971Sobrien        }
31279971Sobrien        break;
31379971Sobrien    case BIO_C_DO_STATE_MACHINE:
31479971Sobrien        if (b->next_bio == NULL)
31579971Sobrien            return (0);
31679971Sobrien        BIO_clear_retry_flags(b);
31779971Sobrien        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
31879971Sobrien        BIO_copy_next_retry(b);
31979971Sobrien        break;
32079971Sobrien
32179971Sobrien    case BIO_CTRL_FLUSH:
32279971Sobrien        if (b->next_bio == NULL)
32379971Sobrien            return (0);
32498247Smikeh        if (ctx->obuf_len <= 0) {
32598247Smikeh            ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
32698247Smikeh            break;
32798247Smikeh        }
32898247Smikeh
32998247Smikeh        for (;;) {
33098247Smikeh            BIO_clear_retry_flags(b);
33179971Sobrien            if (ctx->obuf_len > 0) {
33279971Sobrien                r = BIO_write(b->next_bio, ctx->obuf, ctx->obuf_len);
33379971Sobrien#if 0
33479971Sobrien                fprintf(stderr, "FLUSH %3d -> %3d\n", ctx->obuf_len, r);
33579971Sobrien#endif
33679971Sobrien                BIO_copy_next_retry(b);
33779971Sobrien                if (r <= 0)
33879971Sobrien                    return ((long)r);
33979971Sobrien                if (r < ctx->obuf_len)
34079971Sobrien                    memmove(ctx->obuf, ctx->obuf + r, ctx->obuf_len - r);
34179971Sobrien                ctx->obuf_len -= r;
34279971Sobrien            } else {
34379971Sobrien                ctx->obuf_len = 0;
34479971Sobrien                ret = 1;
34579971Sobrien                break;
346116424Smikeh            }
347116424Smikeh        }
348116424Smikeh        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
349223328Sgavin        break;
350116424Smikeh    case BIO_CTRL_DUP:
351116424Smikeh        dbio = (BIO *)ptr;
35279971Sobrien        if (!BIO_set_write_buffer_size(dbio, ctx->obuf_size))
35379971Sobrien            ret = 0;
35479971Sobrien        break;
355223328Sgavin    default:
35679971Sobrien        if (b->next_bio == NULL)
35779971Sobrien            return (0);
35879971Sobrien        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
35979971Sobrien        break;
36079971Sobrien    }
36179971Sobrien    return (ret);
362223328Sgavin malloc_error:
363223328Sgavin    BIOerr(BIO_F_LINEBUFFER_CTRL, ERR_R_MALLOC_FAILURE);
364223328Sgavin    return (0);
365223328Sgavin}
36679971Sobrien
36779971Sobrienstatic long linebuffer_callback_ctrl(BIO *b, int cmd, bio_info_cb *fp)
36879971Sobrien{
36979971Sobrien    long ret = 1;
37079971Sobrien
37179971Sobrien    if (b->next_bio == NULL)
37279971Sobrien        return (0);
37379971Sobrien    switch (cmd) {
374223328Sgavin    default:
37579971Sobrien        ret = BIO_callback_ctrl(b->next_bio, cmd, fp);
37679971Sobrien        break;
37779971Sobrien    }
378223328Sgavin    return (ret);
379223328Sgavin}
380223328Sgavin
38179971Sobrienstatic int linebuffer_gets(BIO *b, char *buf, int size)
38279971Sobrien{
38379971Sobrien    if (b->next_bio == NULL)
384223328Sgavin        return (0);
385223328Sgavin    return (BIO_gets(b->next_bio, buf, size));
38679971Sobrien}
38779971Sobrien
38879971Sobrienstatic int linebuffer_puts(BIO *b, const char *str)
38979971Sobrien{
39079971Sobrien    return (linebuffer_write(b, str, strlen(str)));
39179971Sobrien}
39279971Sobrien