/*1* Copyright (c) 2016 Thomas Pornin <[email protected]>2*3* Permission is hereby granted, free of charge, to any person obtaining4* a copy of this software and associated documentation files (the5* "Software"), to deal in the Software without restriction, including6* without limitation the rights to use, copy, modify, merge, publish,7* distribute, sublicense, and/or sell copies of the Software, and to8* permit persons to whom the Software is furnished to do so, subject to9* the following conditions:10*11* The above copyright notice and this permission notice shall be12* included in all copies or substantial portions of the Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,15* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF16* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND17* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS18* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN19* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN20* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE21* SOFTWARE.22*/2324#include "inner.h"2526/* see bearssl_ssl.h */27void28br_sslio_init(br_sslio_context *ctx,29br_ssl_engine_context *engine,30int (*low_read)(void *read_context,31unsigned char *data, size_t len),32void *read_context,33int (*low_write)(void *write_context,34const unsigned char *data, size_t len),35void *write_context)36{37ctx->engine = engine;38ctx->low_read = low_read;39ctx->read_context = read_context;40ctx->low_write = low_write;41ctx->write_context = write_context;42}4344/*45* Run the engine, until the specified target state is achieved, or46* an error occurs. The target state is SENDAPP, RECVAPP, or the47* combination of both (the combination matches either). When a match is48* achieved, this function returns 0. On error, it returns -1.49*/50static int51run_until(br_sslio_context *ctx, unsigned target)52{53for (;;) {54unsigned state;5556state = br_ssl_engine_current_state(ctx->engine);57if (state & BR_SSL_CLOSED) {58return -1;59}6061/*62* If there is some record data to send, do it. This takes63* precedence over everything else.64*/65if (state & BR_SSL_SENDREC) {66unsigned char *buf;67size_t len;68int wlen;6970buf = br_ssl_engine_sendrec_buf(ctx->engine, &len);71wlen = ctx->low_write(ctx->write_context, buf, len);72if (wlen < 0) {73/*74* If we received a close_notify and we75* still send something, then we have our76* own response close_notify to send, and77* the peer is allowed by RFC 5246 not to78* wait for it.79*/80if (!ctx->engine->shutdown_recv) {81br_ssl_engine_fail(82ctx->engine, BR_ERR_IO);83}84return -1;85}86if (wlen > 0) {87br_ssl_engine_sendrec_ack(ctx->engine, wlen);88}89continue;90}9192/*93* If we reached our target, then we are finished.94*/95if (state & target) {96return 0;97}9899/*100* If some application data must be read, and we did not101* exit, then this means that we are trying to write data,102* and that's not possible until the application data is103* read. This may happen if using a shared in/out buffer,104* and the underlying protocol is not strictly half-duplex.105* This is unrecoverable here, so we report an error.106*/107if (state & BR_SSL_RECVAPP) {108return -1;109}110111/*112* If we reached that point, then either we are trying113* to read data and there is some, or the engine is stuck114* until a new record is obtained.115*/116if (state & BR_SSL_RECVREC) {117unsigned char *buf;118size_t len;119int rlen;120121buf = br_ssl_engine_recvrec_buf(ctx->engine, &len);122rlen = ctx->low_read(ctx->read_context, buf, len);123if (rlen < 0) {124br_ssl_engine_fail(ctx->engine, BR_ERR_IO);125return -1;126}127if (rlen > 0) {128br_ssl_engine_recvrec_ack(ctx->engine, rlen);129}130continue;131}132133/*134* We can reach that point if the target RECVAPP, and135* the state contains SENDAPP only. This may happen with136* a shared in/out buffer. In that case, we must flush137* the buffered data to "make room" for a new incoming138* record.139*/140br_ssl_engine_flush(ctx->engine, 0);141}142}143144/* see bearssl_ssl.h */145int146br_sslio_read(br_sslio_context *ctx, void *dst, size_t len)147{148unsigned char *buf;149size_t alen;150151if (len == 0) {152return 0;153}154if (run_until(ctx, BR_SSL_RECVAPP) < 0) {155return -1;156}157buf = br_ssl_engine_recvapp_buf(ctx->engine, &alen);158if (alen > len) {159alen = len;160}161memcpy(dst, buf, alen);162br_ssl_engine_recvapp_ack(ctx->engine, alen);163return (int)alen;164}165166/* see bearssl_ssl.h */167int168br_sslio_read_all(br_sslio_context *ctx, void *dst, size_t len)169{170unsigned char *buf;171172buf = dst;173while (len > 0) {174int rlen;175176rlen = br_sslio_read(ctx, buf, len);177if (rlen < 0) {178return -1;179}180buf += rlen;181len -= (size_t)rlen;182}183return 0;184}185186/* see bearssl_ssl.h */187int188br_sslio_write(br_sslio_context *ctx, const void *src, size_t len)189{190unsigned char *buf;191size_t alen;192193if (len == 0) {194return 0;195}196if (run_until(ctx, BR_SSL_SENDAPP) < 0) {197return -1;198}199buf = br_ssl_engine_sendapp_buf(ctx->engine, &alen);200if (alen > len) {201alen = len;202}203memcpy(buf, src, alen);204br_ssl_engine_sendapp_ack(ctx->engine, alen);205return (int)alen;206}207208/* see bearssl_ssl.h */209int210br_sslio_write_all(br_sslio_context *ctx, const void *src, size_t len)211{212const unsigned char *buf;213214buf = src;215while (len > 0) {216int wlen;217218wlen = br_sslio_write(ctx, buf, len);219if (wlen < 0) {220return -1;221}222buf += wlen;223len -= (size_t)wlen;224}225return 0;226}227228/* see bearssl_ssl.h */229int230br_sslio_flush(br_sslio_context *ctx)231{232/*233* We trigger a flush. We know the data is gone when there is234* no longer any record data to send, and we can either read235* or write application data. The call to run_until() does the236* job because it ensures that any assembled record data is237* first sent down the wire before considering anything else.238*/239br_ssl_engine_flush(ctx->engine, 0);240return run_until(ctx, BR_SSL_SENDAPP | BR_SSL_RECVAPP);241}242243/* see bearssl_ssl.h */244int245br_sslio_close(br_sslio_context *ctx)246{247br_ssl_engine_close(ctx->engine);248while (br_ssl_engine_current_state(ctx->engine) != BR_SSL_CLOSED) {249/*250* Discard any incoming application data.251*/252size_t len;253254run_until(ctx, BR_SSL_RECVAPP);255if (br_ssl_engine_recvapp_buf(ctx->engine, &len) != NULL) {256br_ssl_engine_recvapp_ack(ctx->engine, len);257}258}259return br_ssl_engine_last_error(ctx->engine) == BR_ERR_OK;260}261262263