communication over ssl support
authorMarcel Poul <marcel.poul@cern.ch>
Fri, 25 Nov 2011 11:27:15 +0000 (11:27 +0000)
committerMarcel Poul <marcel.poul@cern.ch>
Fri, 25 Nov 2011 11:27:15 +0000 (11:27 +0000)
emi.canl.canl-c/src/canl_ssl.c [new file with mode: 0644]

diff --git a/emi.canl.canl-c/src/canl_ssl.c b/emi.canl.canl-c/src/canl_ssl.c
new file mode 100644 (file)
index 0000000..d479336
--- /dev/null
@@ -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;
+}