From: Marcel Poul Date: Fri, 25 Nov 2011 11:27:15 +0000 (+0000) Subject: communication over ssl support X-Git-Tag: merge_30_head_take2_dst~12 X-Git-Url: http://scientific.zcu.cz/git/?a=commitdiff_plain;h=8b998a90e62d5285b40d76cc300201ef0b6ce894;p=jra1mw.git communication over ssl support --- diff --git a/emi.canl.canl-c/src/canl_ssl.c b/emi.canl.canl-c/src/canl_ssl.c new file mode 100644 index 0000000..d479336 --- /dev/null +++ b/emi.canl.canl-c/src/canl_ssl.c @@ -0,0 +1,277 @@ +#include "canl_locl.h" + +static int do_ssl_connect( glb_ctx *cc, io_handler *io, struct timeval *timeout); + +int ssl_init(glb_ctx *cc, io_handler *io) +{ + int err = 0; + + if (!cc) { + return EINVAL; + } + if (!io) { + err = EINVAL; + goto end; + } + + SSL_load_error_strings(); + SSL_library_init(); + + io->s_ctx->ssl_meth = SSLv23_method(); + io->s_ctx->ssl_ctx = SSL_CTX_new(io->s_ctx->ssl_meth); + if (!io->s_ctx->ssl_ctx){ + err = 1; //TODO set appropriate + update_error(cc, err, "cannot create SSL context (ssl_init)"); + goto end; + } + +end: + if (err) + update_error(cc, err, ""); //TODO update error + return err; + +} + +int ssl_connect(glb_ctx *cc, io_handler *io, struct timeval *timeout) +{ + int err = 0, flags; + + if (!cc) { + return EINVAL; + } + if (!io) { + err = EINVAL; + goto end; + } + + flags = fcntl(io->sock, F_GETFL, 0); + (void)fcntl(io->sock, F_SETFL, flags | O_NONBLOCK); + + io->s_ctx->bio_conn = BIO_new_socket(io->sock, BIO_NOCLOSE); + (void)BIO_set_nbio(io->s_ctx->bio_conn,1); + + io->s_ctx->ssl_io = SSL_new(io->s_ctx->ssl_ctx); + //setup_SSL_proxy_handler(io->s_ctx->ssl_ctx, cacertdir); + SSL_set_bio(io->s_ctx->ssl_io, io->s_ctx->bio_conn, io->s_ctx->bio_conn); + + io->s_ctx->bio_conn = NULL; //TODO ???? + + if ((err = do_ssl_connect(cc, io, timeout))) { + update_error(cc, err, ""); //TODO update error + goto end; + } + + /* + if (post_connection_check(io->s_ctx->ssl_io)) { + opened = true; + (void)Send("0"); + return true; + } + */ + +end: + if (err) + update_error(cc, err, ""); //TODO update error + return err; +} + +/* + * Encapsulates select behaviour + * + * Returns: + * > 0 : Ready to read or write. + * = 0 : timeout reached. + * < 0 : error. + */ +int do_select(int fd, time_t starttime, int timeout, int wanted) +{ + fd_set rset; + fd_set wset; + + FD_ZERO(&rset); + FD_ZERO(&wset); + + if (wanted == 0 || wanted == SSL_ERROR_WANT_READ) + FD_SET(fd, &rset); + if (wanted == 0 || wanted == SSL_ERROR_WANT_WRITE) + FD_SET(fd, &wset); + + int ret = 0; + + if (timeout != -1) { + struct timeval endtime; + + time_t curtime = time(NULL); + + if (curtime - starttime >= timeout) + return 0; + + endtime.tv_sec = timeout - (curtime - starttime); + endtime.tv_usec = 0; + + ret = select(fd+1, &rset, &wset, NULL, &endtime); + } + else { + ret = select(fd+1, &rset, &wset, NULL, NULL); + } + + if (ret == 0) + return 0; + + if ((wanted == SSL_ERROR_WANT_READ && !FD_ISSET(fd, &rset)) || + (wanted == SSL_ERROR_WANT_WRITE && !FD_ISSET(fd, &wset))) + return -1; + + if (ret < 0 && (!FD_ISSET(fd, &rset) || !FD_ISSET(fd, &wset))) + return 1; + + return ret; +} + +#define TEST_SELECT(ret, ret2, timeout, curtime, starttime, errorcode) \ + ((ret) > 0 && ((ret2) <= 0 && (((timeout) == -1) || \ + (((timeout) != -1) && \ + ((curtime) - (starttime)) < (timeout))) && \ + ((errorcode) == SSL_ERROR_WANT_READ || \ + (errorcode) == SSL_ERROR_WANT_WRITE))) + +static int do_ssl_connect( glb_ctx *cc, io_handler *io, struct timeval *timeout) +{ + time_t starttime, curtime; + int ret = -1, ret2 = -1, err = 0; + long errorcode = 0; + int expected = 0; + int locl_timeout = -1; + + /* do not take tv_usec into account in this function*/ + if (timeout) + locl_timeout = timeout->tv_sec; + else + locl_timeout = -1; + curtime = starttime = time(NULL); + + do { + ret = do_select(io->sock, starttime, locl_timeout, expected); + if (ret > 0) { + ret2 = SSL_connect(io->s_ctx->ssl_io); + expected = errorcode = SSL_get_error(io->s_ctx->ssl_io, ret2); + } + curtime = time(NULL); + } while (TEST_SELECT(ret, ret2, locl_timeout, curtime, starttime, errorcode)); + + //TODO split ret2 and ret into 2 ifs to set approp. error message + if (ret2 <= 0 || ret <= 0) { + if (timeout && (curtime - starttime >= locl_timeout)){ + timeout->tv_sec=0; + timeout->tv_usec=0; + err = ETIMEDOUT; + update_error (cc, err, "Connection stuck during handshake: timeout reached (do_ssl_connect)"); + } + else{ + err = -1; //TODO set approp. error message + update_error (cc, err, "Error during SSL handshake (do_ssl_connect)"); + } + return err; + } + + return 0; +} + +/* this function has to return # bytes written or ret < 0 when sth went wrong*/ +int ssl_write(glb_ctx *cc, io_handler *io, void *buffer, size_t size, struct timeval *timeout) +{ + int err = 0; + int ret = 0, nwritten=0; + const char *str; + int fd; + time_t starttime, curtime; + int do_continue = 0; + int expected = 0; + int locl_timeout; + int tout = 0; + + if (!io->s_ctx->ssl_io) { + err = EINVAL; + goto end; + } + + if (!cc) { + return -1; + } + if (!io) { + err = EINVAL; + goto end; + } + + if (!buffer) { + err = EINVAL; //TODO really? + update_error(cc, err, "Nothing to write (ssl_write)"); + errno = err; + return -1; + } + + fd = BIO_get_fd(SSL_get_rbio(io->s_ctx->ssl_io), NULL); + str = buffer;//TODO !!!!!! text.c_str(); + + curtime = starttime = time(NULL); + if (timeout) { + locl_timeout = timeout->tv_sec; + } + else + locl_timeout = -1; + + do { + ret = do_select(fd, starttime, locl_timeout, expected); + + do_continue = 0; + if (ret > 0) { + int v; + errno = 0; + ret = SSL_write(io->s_ctx->ssl_io, str + nwritten, strlen(str) - nwritten); + v = SSL_get_error(io->s_ctx->ssl_io, ret); + + switch (v) { + case SSL_ERROR_NONE: + nwritten += ret; + if ((size_t)nwritten == strlen(str)) + do_continue = 0; + else + do_continue = 1; + break; + + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + expected = v; + ret = 1; + do_continue = 1; + break; + + default: + do_continue = 0; + } + } + curtime = time(NULL); + locl_timeout = locl_timeout - (curtime - starttime); + if (locl_timeout != -1 && locl_timeout <= 0){ + tout = 1; + goto end; + } + } while (ret <= 0 && do_continue); + +end: + if (err) { + errno = err; + update_error (cc, err, "Error during SSL write (ssl_write)"); + return -1; + } + if (tout){ + errno = err = ETIMEDOUT; + update_error(cc, err, "Connection stuck during write: timeout reached (ssl_write)"); + return -1; + } + if (ret <=0){ + err = -1;//TODO what to assign?????? + update_error (cc, err, "Error during SSL write (ssl_write)"); + } + return ret; +}