head 1.14; access; symbols SIO_0_9_3:1.14 SIO_0_9_2:1.13 SIO_0_9_1:1.12 SIO_0_9_0:1.12; locks; strict; comment @ * @; 1.14 date 2005.10.03.09.06.11; author rse; state Exp; branches; next 1.13; 1.13 date 2003.06.30.10.36.52; author rse; state Exp; branches; next 1.12; 1.12 date 2003.02.06.12.45.18; author mlelstv; state Exp; branches; next 1.11; 1.11 date 2003.02.05.16.29.05; author mlelstv; state Exp; branches; next 1.10; 1.10 date 2003.01.20.15.34.13; author mlelstv; state Exp; branches; next 1.9; 1.9 date 2003.01.06.19.04.56; author rse; state Exp; branches; next 1.8; 1.8 date 2002.12.18.15.51.02; author mlelstv; state Exp; branches; next 1.7; 1.7 date 2002.11.29.14.26.31; author mlelstv; state Exp; branches; next 1.6; 1.6 date 2002.11.29.13.00.18; author mlelstv; state Exp; branches; next 1.5; 1.5 date 2002.11.27.15.50.29; author mlelstv; state Exp; branches; next 1.4; 1.4 date 2002.11.24.19.40.59; author mlelstv; state Exp; branches; next 1.3; 1.3 date 2002.11.24.19.39.17; author mlelstv; state Exp; branches; next 1.2; 1.2 date 2002.11.19.22.29.20; author mlelstv; state Exp; branches; next 1.1; 1.1 date 2002.11.19.15.54.51; author mlelstv; state Exp; branches; next ; desc @@ 1.14 log @adjust copyright messages @ text @/* ** OSSP sio - Stream I/O ** Copyright (c) 2002-2005 Cable & Wireless ** Copyright (c) 2002-2005 The OSSP Project ** Copyright (c) 2002-2005 Ralf S. Engelschall ** ** This file is part of OSSP sio, a layered stream I/O library ** which can be found at http://www.ossp.org/pkg/lib/sio/. ** ** Permission to use, copy, modify, and distribute this software for ** any purpose with or without fee is hereby granted, provided that ** the above copyright notice and this permission notice appear in all ** copies. ** ** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED ** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF ** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** ** sio_bio.c: OpenSSL BIO stage */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #if ENABLE_BIO #include #include #include #include #include "al.h" #include "sio.h" #include #include typedef enum { INIT, LOWER, UPPER } state_t; typedef struct { state_t state; int isoutput; al_t *in_buf; al_t *out_buf; BIO *bio; BIO *nbio; al_t *al_in, *al_out; /* upstream buffers */ int issink; int freebio; size_t inputsize; al_label_t data_label; al_label_t eof_label; al_label_t error_label; char eof; char error; size_t total; int needs_input; int eof_reached; int write_error; int flush_upper; int flush_lower; } private_t; /********************************************************************/ static int b_new(BIO *bi) { bi->init = 0; bi->num = -1; bi->flags = 0; bi->ptr = NULL; return 1; } static int b_free(BIO *a) { if (a == NULL) return 0; return 1; } static long b_pending(private_t *my) { return my ? al_bytes(my->al_in) : 0; } static long b_wpending(private_t *my) { return my ? al_bytes(my->al_out) : 0; } static long b_setflush(private_t *my) { if (my) my->flush_upper = 1; return 1; } static long b_geteof(private_t *my) { return my->eof_reached; } static long b_ctrl(BIO *b, int cmd, long num, void *ptr) { long ret = 1; switch (cmd) { case BIO_CTRL_PENDING: ret = b_pending((private_t *)b->ptr); break; case BIO_CTRL_WPENDING: ret = b_wpending((private_t *)b->ptr); break; case BIO_CTRL_FLUSH: ret = b_setflush((private_t *)b->ptr); break; case BIO_CTRL_EOF: ret = b_geteof((private_t *)b->ptr); break; case BIO_C_SET_FD: b->ptr = ptr; b->init = 1; ret = 1; break; case BIO_CTRL_RESET: case BIO_CTRL_SET: case BIO_CTRL_SET_CLOSE: case BIO_CTRL_DUP: ret = 1; break; case BIO_CTRL_GET_CLOSE: case BIO_CTRL_INFO: case BIO_CTRL_GET: default: ret = 0; break; } return ret; } static int b_read(BIO *b, char *out, int outl) { private_t *my = (private_t *)b->ptr; size_t n, m; al_label_t label; m = 0; if (al_bytes(my->al_in) > 0) { al_firstlabel(my->al_in, 0, 1, AL_FORWARD, NULL, &label); if (label == my->data_label) { al_flatten(my->al_in, 0, outl, AL_FORWARD_SPAN, label, out, &n); m = n; } else { al_flatten(my->al_in, 0, outl, AL_FORWARD_SPAN, label, NULL, &n); my->eof_reached = 1; m = 0; } al_splice(my->al_in, 0, n, NULL, NULL); } BIO_clear_retry_flags(b); if (m == 0) BIO_set_retry_read(b); return m; } static int b_write(BIO *b, const char *in, int inl) { private_t *my = (private_t *)b->ptr; al_append_bytes(my->al_out, in, inl, my->data_label); return inl; } static int b_puts(BIO *b, const char *s) { return b_write(b, s, strlen(s)); } static BIO_METHOD b_method = { BIO_TYPE_NONE, "SIO SSL", b_write, b_read, b_puts, NULL, b_ctrl, b_new, b_free, NULL }; /********************************************************************/ /* * create stage * * allocate private instance data */ static sio_rc_t siobio_init(sio_t *sio, void **up) { private_t *my; my = (private_t *)malloc(sizeof(private_t)); if (my == NULL) return SIO_ERR_MEM; my->bio = NULL; my->inputsize = 512; my->issink = 0; my->freebio = 0; my->nbio = NULL; my->eof = '\0'; my->error = '\0'; sio_label(sio, SIO_LN_DATA, &my->data_label); sio_label(sio, SIO_LN_EOF, &my->eof_label); sio_label(sio, SIO_LN_ERROR, &my->error_label); *up = my; return SIO_OK; } /* * configure stage * * pass two void pointers */ static sio_rc_t siobio_configure(sio_t *sio, void *u, void *obj, void *val) { private_t *my = (private_t *)u; const char *name = (const char *)obj; int v; if (!strcmp(name, "bio")) { my->bio = (BIO *)val; } else if (!strcmp(name, "inputsize")) { v = *(int *)val; if (v <= 0) return SIO_ERR_ARG; my->inputsize = v; } else if (!strcmp(name, "issink")) { my->issink = *(int *)val; } else if (!strcmp(name, "freebio")) { my->freebio = *(int *)val; } else { return SIO_ERR_ARG; } return SIO_OK; } /* * destroy stage */ static sio_rc_t siobio_cleanup(sio_t *sio, void *u) { private_t *my = (private_t *)u; free(my); return SIO_OK; } static sio_rc_t siobio_openr(sio_t *sio, al_t *al, void *u) { return SIO_OK; } static sio_rc_t siobio_closer(sio_t *sio, al_t *al, void *u) { return SIO_OK; } static sio_rc_t siobio_openw(sio_t *sio, al_t *al, void *u) { private_t *my = (private_t *)u; if (my->bio == NULL) return SIO_ERR_ARG; if (al_create(&my->al_in) != AL_OK) return SIO_ERR_INT; if (al_create(&my->al_out) != AL_OK) { al_destroy(my->al_in); my->al_in = NULL; return SIO_ERR_INT; } if (al_create(&my->in_buf) != AL_OK) { al_destroy(my->al_out); my->al_out = NULL; al_destroy(my->al_in); my->al_in = NULL; return SIO_ERR_INT; } if (al_create(&my->out_buf) != AL_OK) { al_destroy(my->in_buf); my->in_buf = NULL; al_destroy(my->al_out); my->al_out = NULL; al_destroy(my->al_in); my->al_in = NULL; return SIO_ERR_INT; } if (!my->issink) { my->nbio = BIO_new(&b_method); BIO_ctrl(my->nbio, BIO_C_SET_FD, 0, (void *)my); BIO_push(my->bio, my->nbio); } my->eof_reached = 0; my->needs_input = 0; my->write_error = 0; my->flush_upper = 0; my->flush_lower = 0; my->state = INIT; return SIO_OK; } static sio_rc_t siobio_closew(sio_t *sio, al_t *al, void *u) { private_t *my = (private_t *)u; if (my->nbio != NULL) { BIO_pop(my->bio); BIO_free(my->nbio); my->nbio = NULL; } al_destroy(my->out_buf); my->out_buf = NULL; al_destroy(my->in_buf); my->in_buf = NULL; al_destroy(my->al_out); my->al_out = NULL; al_destroy(my->al_in); my->al_in = NULL; return SIO_OK; } /********************************************************************/ /* * auto-destructor for allocated input buffer */ static void freebiobuf(char *p, size_t n, void *u) { free(p); } /* * chunk traversal of output buffer * * counts my->total * sets my->needs_input */ static al_rc_t siobio_write_chunk(al_chunk_t *alc, void *u) { private_t *my = (private_t *)u; int n = al_chunk_len(alc); al_rc_t arc = AL_OK; if (al_same_label(alc, my->data_label)) { char *p = al_chunk_ptr(alc, 0); int i, t; for (t=0; tbio ? BIO_write(my->bio, p+t, n-t) : (n-t); if (i <= 0) { if (!BIO_should_retry(my->bio)) { my->needs_input = 1; if (my->eof_reached) my->write_error = 1; } arc = AL_ERR_EOF; break; } my->total += i; } } else { my->total += n; my->flush_lower = 1; } return arc; } /********************************************************************/ /* UPSTREAM layer */ static sio_rc_t siobio_input_upper(private_t *my, al_t *al) { size_t n; /* copy global al into input buffer */ n = al_bytes(al); al_splice(al, 0, n, NULL, my->al_in); /* flush output */ if (my->flush_upper) return SIO_SCHED_CROSS; /* transport data to lower layers */ if (al_bytes(my->al_in) <= 0) return SIO_SCHED_UP; if (!my->isoutput) { my->state = LOWER; return SIO_SCHED_LOOP; } return SIO_SCHED_CROSS; } static sio_rc_t siobio_output_upper(private_t *my, al_t *al) { /* copy output buffer to global al */ my->flush_upper = 0; al_splice(al, al_bytes(al), 0, my->al_out, NULL); /* transport data to upper layers */ if (al_bytes(al) > 0) return SIO_SCHED_UP; /* lower layers signalled they need input */ if (my->needs_input) { my->needs_input = 0; /* place input buffer on global al so input routines * can pick it from there again */ al_splice(al, 0, al_bytes(al), my->al_in, NULL); return SIO_SCHED_CROSS; } if (my->isoutput) { my->state = LOWER; return SIO_SCHED_LOOP; } return SIO_SCHED_CROSS; } /* DOWNSTREAM layer */ static sio_rc_t siobio_input_lower(private_t *my, al_t *al) { al_splice(al, al_bytes(al), 0, my->in_buf, NULL); if (al_bytes(al) > 0 || my->eof_reached) { my->state = INIT; return SIO_SCHED_DOWN; } my->state = UPPER; return SIO_SCHED_LOOP; } static sio_rc_t siobio_output_lower(private_t *my, al_t *al) { al_splice(al, 0, al_bytes(al), NULL, my->out_buf); if (al_bytes(my->out_buf) <= 0) { my->state = INIT; return SIO_SCHED_DOWN; } my->state = UPPER; return SIO_SCHED_LOOP; } /* BIO layer */ static void siobio_bio_read(private_t *my) { char *p; int n, m; n = BIO_pending(my->bio); if (n <= 0 || (size_t)n > my->inputsize) n = my->inputsize; p = malloc(n); assert(p != NULL); if (my->bio) { do { m = BIO_read(my->bio, p, n); } while (m <= 0 && BIO_should_retry(my->bio)); if (m == -1 && !my->eof_reached) { my->flush_upper = 1; m = 0; } } else m = -2; if (m < 0) { free(p); if (m < -1) al_append_bytes(my->in_buf, &my->error, sizeof(my->error), my->error_label); else al_append_bytes(my->in_buf, &my->eof, sizeof(my->eof), my->eof_label); } else if (m > 0) al_attach_buffer(my->in_buf, p, m, my->data_label, freebiobuf, NULL); } static void siobio_bio_write(private_t *my) { my->needs_input = 0; my->total = 0; al_traverse_cb(my->out_buf, 0, al_bytes(my->out_buf), AL_FORWARD, NULL, siobio_write_chunk, (void *)my); al_splice(my->out_buf, 0, my->total, NULL, NULL); if (!my->write_error && my->flush_lower) { my->flush_upper = 1; if (BIO_flush(my->bio) > 0) my->flush_lower = 0; } } /********************************************************************/ static sio_rc_t siobio_input(sio_t *sio, al_t *al, void *u, sio_rc_t orc) { private_t *my = (private_t *)u; sio_rc_t rc = SIO_ERR_INT; switch (my->state) { case INIT: my->isoutput = 0; my->state = LOWER; rc = SIO_SCHED_LOOP; break; case LOWER: siobio_bio_read(my); rc = siobio_input_lower(my, al); if (my->eof_reached) { my->eof_reached = 0; my->state = INIT; al_append_bytes(al, &my->eof, sizeof(my->eof), my->eof_label); return SIO_SCHED_DOWN; } break; case UPPER: rc = siobio_input_upper(my, al); break; } return rc; } static sio_rc_t siobio_output(sio_t *sio, al_t *al, void *u, sio_rc_t orc) { private_t *my = (private_t *)u; sio_rc_t rc = SIO_ERR_INT; switch (my->state) { case INIT: my->isoutput = 1; my->state = LOWER; rc = SIO_SCHED_LOOP; break; case LOWER: rc = siobio_output_lower(my, al); siobio_bio_write(my); if (my->write_error) { my->write_error = 0; my->state = INIT; al_splice(al, 0, al_bytes(al), NULL, NULL); return SIO_SCHED_DOWN; } break; case UPPER: rc = siobio_output_upper(my, al); break; } return rc; } static sio_rc_t siobio_shutdown(sio_t *sio, void *u) { private_t *my = (private_t *)u; if (my->freebio && my->bio != NULL) { BIO_free(my->bio); my->bio = NULL; my->flush_upper = 1; my->state = UPPER; return SIO_OK; } return SIO_ERR_ARG; } sio_module_t sio_module_bio = { "bio", siobio_init, siobio_configure, siobio_cleanup, siobio_openr, siobio_closer, siobio_openw, siobio_closew, siobio_input, siobio_output, siobio_shutdown }; #else const char __sio_bio_c[] = ""; #endif /* ENABLE_BIO */ @ 1.13 log @*) Correctly check the "status" return value of waitpid(3) in the test suite. *) Change in the test suite "%08lx" format string to "%d" because the ts library only has a minimal formatting engine and the argument is "int" and not "long" anyway. *) Make sure that sio_{bio,sa,zlib}.c are not empty compilation units (not allowed in ISO C) even if BIO, SA or ZLIB support is not activated. *) Changed SIZE_T_MAX fallback definition to a more portable variant based on sizeof(size_t) instead of relying on the existance of (non portable) UINT_MAX. *) Added GNU autoconf checks for libnsl/libsocket under Solaris. *) Upgraded to GNU libtool 1.5 @ text @d3 3 a5 3 ** Copyright (c) 2002-2003 Cable & Wireless Deutschland ** Copyright (c) 2002-2003 The OSSP Project ** Copyright (c) 2002-2003 Ralf S. Engelschall @ 1.12 log @better working eof handling read deadlock solved default input buffer size comments @ text @d644 4 d649 1 @ 1.11 log @make default no-sink operation @ text @d234 1 a234 1 my->nbio = NULL; d237 1 d424 6 d434 2 a435 1 if (al_bytes(al) <= 0) a437 2 al_splice(al, 0, al_bytes(al), NULL, my->al_in); a441 1 d448 6 a453 4 /* flush output */ if (my->flush_upper) { my->flush_upper = 0; al_splice(al, al_bytes(al), 0, my->al_out, NULL); a454 1 } d456 1 d459 2 a468 1 d504 1 a504 1 int n; d514 6 a519 2 n = BIO_read(my->bio, p, n); } while (n <= 0 && BIO_should_retry(my->bio)); d521 1 a521 1 n = -2; d523 1 a523 1 if (n < 0) { d525 1 a525 1 if (n < -1) d531 2 a532 2 } else if (n > 0) al_attach_buffer(my->in_buf, p, n, my->data_label, freebiobuf, NULL); @ 1.10 log @make external references optional @ text @d235 1 a235 1 my->issink = 1; @ 1.9 log @- consistently use standard OSSP copyright message everywhere - strip trailing whitespaces @ text @d31 6 d632 1 @ 1.8 log @avoid compiler warning PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d1 30 d222 1 a222 1 @ 1.7 log @eof/write_error handling BIO_write retries now loops through scheduler to give upper layer a chance to run. PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d510 1 a510 1 sio_rc_t rc; d540 1 a540 1 sio_rc_t rc; @ 1.6 log @input/output now gets another parameter where scheduler tells them from where they methods called by passing the return value of the previously called stage. PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @a25 2 int reader_writes; int writer_reads; d35 3 a37 1 int should_retry; a39 1 int eof_reached; d138 2 a139 1 m = -1; a143 3 if (m < 0) my->eof_reached = 1; d202 1 a202 1 my->error = '\0'; d223 1 d228 4 a231 1 my->inputsize = *(int *)val; d300 6 a305 2 my->writer_reads = 0; my->reader_writes = 0; d346 1 a346 1 * sets my->should_retry d363 3 a365 3 my->should_retry = 1; arc = AL_ERR_EOF; break; d367 2 a368 1 i = 0; d370 1 a370 1 my->total += i; d414 2 a415 2 if (my->should_retry) { my->should_retry = 0; d433 1 a433 1 if (al_bytes(al) > 0) { d463 1 a463 1 if (n == 0 || n > my->inputsize) d490 2 a491 2 my->should_retry = 0; my->total = 0; d497 1 a497 1 if (my->flush_lower) { a506 7 sio_rc_t siobio_eof(private_t *my, al_t *al) { al_splice(al, 0, al_bytes(al), NULL, NULL); return SIO_SCHED_DOWN; } static d521 6 d551 6 @ 1.5 log @code cleanup PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d508 1 a508 1 sio_rc_t siobio_input(sio_t *sio, al_t *al, void *u) d532 1 a532 1 sio_rc_t siobio_output(sio_t *sio, al_t *al, void *u) @ 1.4 log @don't cause shutdown operation for BIOs that are not freed. PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d192 1 a192 1 sio_rc_t siobio_init(sio_t *sio, void **u) d211 1 a211 1 *u = my; @ 1.3 log @now supports full duplex BIOs. BIOs are shut down during a BIO_free() operation, so this has to be done while the pipe is active with the "freebio" parameter. PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d565 1 d568 1 a568 1 return SIO_OK; @ 1.2 log @support errors, do I/O loops PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d4 1 d10 7 d19 22 a40 8 BIO *bio; size_t inputsize; al_label_t data_label; al_label_t eof_label; al_label_t error_label; char eof; char error; size_t total; d43 143 d200 5 a204 2 my->bio = NULL; my->eof = '\0'; d231 4 d270 34 d310 13 d326 5 d336 7 d344 1 a344 1 sio_rc_t siobio_input(sio_t *sio, al_t *al, void *u) d347 106 d460 1 d462 1 a462 1 if (p != NULL) d466 2 d469 1 a469 1 if (p == NULL || n <= 0) { d472 2 a473 1 al_append_bytes(al, &my->error, sizeof(my->error), my->error_label); d475 24 a498 3 al_append_bytes(al, &my->eof, sizeof(my->eof), my->eof_label); } else al_attach_buffer(al, p, n, my->data_label, freebiobuf, NULL); d500 4 d508 1 a508 1 al_rc_t siobio_write_chunk(al_chunk_t *alc, void *u) d511 1 a511 1 al_rc_t arc = AL_OK; d513 13 a525 18 if (al_same_label(alc, my->data_label)) { char *p = al_chunk_ptr(alc, 0); int n = al_chunk_len(alc); int i, t; for (t=0; tbio, p+t, n-t); if (i <= 0) { if (!BIO_should_retry(my->bio)) { arc = AL_ERR_EOF; break; } i = 0; } my->total += i; } } else { my->total += al_chunk_len(alc); d528 1 a528 1 return arc; d530 1 d535 19 d555 4 a558 3 my->total = 0; al_traverse_cb(al, 0, al_bytes(al), AL_FORWARD, my->data_label, siobio_write_chunk, (void *)my); d560 6 a565 1 al_splice(al, 0, al_bytes(al), NULL, NULL); d567 1 a567 1 return SIO_SCHED_DOWN; d580 2 a581 1 siobio_output @ 1.1 log @wrap BIO objects from libcrypto as an endpoint adjust test program to use sio_bio instead of sio_sa PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d15 4 a18 1 char eof; d37 1 d39 3 a41 2 sio_label(sio, SIO_LN_DATA, &my->data_label); sio_label(sio, SIO_LN_EOF, &my->eof_label); a122 1 n = BIO_read(my->bio, p, n); d124 12 a135 3 if (n == 0 && !BIO_should_read(my->bio)) al_append_bytes(al, &my->eof, sizeof(my->eof), my->eof_label); else d145 1 d147 19 a165 2 if (al_same_label(alc, my->data_label)) BIO_write(my->bio, al_chunk_ptr(alc, 0), al_chunk_len(alc)); d167 1 a167 1 return AL_OK; d174 1 @