]> source.dussan.org Git - rspamd.git/commitdiff
{Feature] Implement SSL IO
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Mon, 13 Jun 2016 16:18:26 +0000 (17:18 +0100)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Mon, 13 Jun 2016 16:31:29 +0000 (17:31 +0100)
src/libutil/ssl_util.c
src/libutil/ssl_util.h

index 17bd2880d42e6b04c05b6923a53c16e4be1b2dc1..a72f1de955bada79b5deb1dabca4b99f19176919 100644 (file)
@@ -29,7 +29,9 @@ struct rspamd_ssl_connection {
        enum {
                ssl_conn_reset = 0,
                ssl_conn_init,
-               ssl_conn_connected
+               ssl_conn_connected,
+               ssl_next_read,
+               ssl_next_write
        } state;
        SSL *ssl;
        gchar *hostname;
@@ -315,14 +317,6 @@ rspamd_ssl_peer_verify (struct rspamd_ssl_connection *c)
 
                return FALSE;
        }
-       else {
-               ver_err = ERR_get_error ();
-               g_set_error (&err, rspamd_ssl_quark (), ver_err,
-                               "ssl connect error: %s", ERR_error_string (ver_err, NULL));
-               c->err_handler (c->handler_data, err);
-               g_error_free (err);
-               return FALSE;
-       }
 
        /* Get server's certificate */
        server_cert =  SSL_get_peer_certificate (c->ssl);
@@ -355,20 +349,24 @@ rspamd_ssl_event_handler (gint fd, short what, gpointer ud)
        gint ret;
        GError *err = NULL;
 
-       if (c->state == ssl_conn_init) {
+       switch (c->state) {
+       case ssl_conn_init:
                /* Continue connection */
                ret = SSL_connect (c->ssl);
 
                if (ret == 1) {
+                       event_del (c->ev);
                        /* Verify certificate */
                        if (rspamd_ssl_peer_verify (c)) {
                                c->state = ssl_conn_connected;
                                c->handler (fd, EV_WRITE, c->handler_data);
                        }
                        else {
-                               /* Error handler has been called from peer verify */
-                               return;
+                               g_assert (0);
                        }
+               }
+               else {
+                       ret = SSL_get_error (c->ssl, ret);
 
                        if (ret == SSL_ERROR_WANT_READ) {
                                what = EV_READ;
@@ -384,10 +382,28 @@ rspamd_ssl_event_handler (gint fd, short what, gpointer ud)
                                return;
                        }
 
-                       event_set (c->ev, fd, EV_WRITE, rspamd_ssl_event_handler, c);
+                       event_set (c->ev, fd, what, rspamd_ssl_event_handler, c);
                        event_base_set (c->ev_base, c->ev);
                        event_add (c->ev, c->tv);
                }
+               break;
+       case ssl_next_read:
+               event_del (c->ev);
+               c->state = ssl_conn_connected;
+               c->handler (fd, EV_READ, c->handler_data);
+               break;
+       case ssl_next_write:
+       case ssl_conn_connected:
+               event_del (c->ev);
+               c->state = ssl_conn_connected;
+               c->handler (fd, EV_WRITE, c->handler_data);
+               break;
+       default:
+               g_set_error (&err, rspamd_ssl_quark (), EINVAL,
+                               "ssl bad state error: %d", c->state);
+               c->err_handler (c->handler_data, err);
+               g_error_free (err);
+               break;
        }
 }
 
@@ -444,9 +460,11 @@ rspamd_ssl_connect_fd (struct rspamd_ssl_connection *conn, gint fd,
        if (ret == 1) {
                conn->state = ssl_conn_connected;
                event_set (ev, fd, EV_WRITE, rspamd_ssl_event_handler, conn);
+
                if (conn->ev_base) {
                        event_base_set (conn->ev_base, ev);
                }
+
                event_add (ev, tv);
        }
        else {
@@ -462,7 +480,7 @@ rspamd_ssl_connect_fd (struct rspamd_ssl_connection *conn, gint fd,
                        return FALSE;
                }
 
-               event_set (ev, fd, EV_WRITE, rspamd_ssl_event_handler, conn);
+               event_set (ev, fd, what, rspamd_ssl_event_handler, conn);
                event_base_set (conn->ev_base, ev);
                event_add (ev, tv);
        }
@@ -470,6 +488,176 @@ rspamd_ssl_connect_fd (struct rspamd_ssl_connection *conn, gint fd,
        return TRUE;
 }
 
+gssize
+rspamd_ssl_read (struct rspamd_ssl_connection *conn, gpointer buf,
+               gsize buflen)
+{
+       gint ret;
+       short what;
+       GError *err = NULL;
+
+       g_assert (conn != NULL);
+
+       if (conn->state != ssl_conn_connected && conn->state != ssl_next_read) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       ret = SSL_read (conn->ssl, buf, buflen);
+
+       if (ret > 0) {
+               conn->state = ssl_conn_connected;
+               return ret;
+       }
+       else if (ret == 0) {
+               ret = SSL_get_error (conn->ssl, ret);
+
+               if (ret == SSL_ERROR_ZERO_RETURN) {
+                       conn->state = ssl_conn_reset;
+                       return 0;
+               }
+               else {
+                       g_set_error (&err, rspamd_ssl_quark (), ret,
+                                       "ssl write error: %s", ERR_error_string (ret, NULL));
+                       conn->err_handler (conn->handler_data, err);
+                       g_error_free (err);
+                       errno = EINVAL;
+
+                       return -1;
+               }
+       }
+       else {
+               ret = SSL_get_error (conn->ssl, ret);
+               conn->state = ssl_next_read;
+
+               if (ret == SSL_ERROR_WANT_READ) {
+                       what = EV_READ;
+               }
+               else if (ret == SSL_ERROR_WANT_WRITE) {
+                       what = EV_WRITE;
+               }
+               else {
+                       g_set_error (&err, rspamd_ssl_quark (), ret,
+                                       "ssl read error: %s", ERR_error_string (ret, NULL));
+                       conn->err_handler (conn->handler_data, err);
+                       g_error_free (err);
+                       errno = EINVAL;
+
+                       return -1;
+               }
+
+               event_set (conn->ev, conn->fd, what, rspamd_ssl_event_handler, conn);
+               event_base_set (conn->ev_base, conn->ev);
+               event_add (conn->ev, conn->tv);
+
+               errno = EAGAIN;
+
+       }
+
+       return -1;
+}
+
+gssize
+rspamd_ssl_write (struct rspamd_ssl_connection *conn, gconstpointer buf,
+               gsize buflen)
+{
+       gint ret;
+       short what;
+       GError *err = NULL;
+
+       g_assert (conn != NULL);
+
+       if (conn->state != ssl_conn_connected && conn->state != ssl_next_write) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       ret = SSL_write (conn->ssl, buf, buflen);
+
+       if (ret > 0) {
+               conn->state = ssl_conn_connected;
+               return ret;
+       }
+       else if (ret == 0) {
+               ret = SSL_get_error (conn->ssl, ret);
+
+               if (ret == SSL_ERROR_ZERO_RETURN) {
+                       conn->state = ssl_conn_reset;
+                       return 0;
+               }
+               else {
+                       g_set_error (&err, rspamd_ssl_quark (), ret,
+                                       "ssl write error: %s", ERR_error_string (ret, NULL));
+                       conn->err_handler (conn->handler_data, err);
+                       g_error_free (err);
+                       errno = EINVAL;
+
+                       return -1;
+               }
+       }
+       else {
+               ret = SSL_get_error (conn->ssl, ret);
+               conn->state = ssl_next_read;
+
+               if (ret == SSL_ERROR_WANT_READ) {
+                       what = EV_READ;
+               }
+               else if (ret == SSL_ERROR_WANT_WRITE) {
+                       what = EV_WRITE;
+               }
+               else {
+                       g_set_error (&err, rspamd_ssl_quark (), ret,
+                                       "ssl fatal write error: %s", ERR_error_string (ret, NULL));
+                       conn->err_handler (conn->handler_data, err);
+                       g_error_free (err);
+                       errno = EINVAL;
+
+                       return -1;
+               }
+
+               event_set (conn->ev, conn->fd, what, rspamd_ssl_event_handler, conn);
+               event_base_set (conn->ev_base, conn->ev);
+               event_add (conn->ev, conn->tv);
+
+               errno = EAGAIN;
+       }
+
+       return -1;
+}
+
+gssize
+rspamd_ssl_writev (struct rspamd_ssl_connection *conn, struct iovec *iov,
+               gsize iovlen)
+{
+       static guchar ssl_buf[16000];
+       guchar *p;
+       struct iovec *cur;
+       guint i, remain;
+
+       remain = sizeof (ssl_buf);
+       p = ssl_buf;
+
+       for (i = 0; i < iovlen; i ++) {
+               cur = &iov[i];
+
+               if (cur->iov_len > 0) {
+                       if (remain >= cur->iov_len) {
+                               memcpy (p, cur->iov_base, cur->iov_len);
+                               p += cur->iov_len;
+                               remain -= cur->iov_len;
+                       }
+                       else {
+                               memcpy (p, cur->iov_base, remain);
+                               p += remain;
+                               remain = 0;
+                               break;
+                       }
+               }
+       }
+
+       return rspamd_ssl_write (conn, ssl_buf, p - ssl_buf);
+}
+
 /**
  * Removes connection data
  * @param conn
index 719c8ffd1502225550dc2b0cd7422b76e391e520..64e6a413e532d8c1e59043c83d358765c4b9ef5c 100644 (file)
@@ -48,6 +48,38 @@ gboolean rspamd_ssl_connect_fd (struct rspamd_ssl_connection *conn, gint fd,
                rspamd_ssl_handler_t handler, rspamd_ssl_error_handler_t err_handler,
                gpointer handler_data);
 
+/**
+ * Perform async read from SSL socket
+ * @param conn
+ * @param buf
+ * @param buflen
+ * @return
+ */
+gssize rspamd_ssl_read (struct rspamd_ssl_connection *conn, gpointer buf,
+               gsize buflen);
+
+/**
+ * Perform async write to ssl buffer
+ * @param conn
+ * @param buf
+ * @param buflen
+ * @param ev
+ * @param tv
+ * @return
+ */
+gssize rspamd_ssl_write (struct rspamd_ssl_connection *conn, gconstpointer buf,
+               gsize buflen);
+
+/**
+ * Emulate writev by copying iovec to a temporary buffer
+ * @param conn
+ * @param buf
+ * @param buflen
+ * @return
+ */
+gssize rspamd_ssl_writev (struct rspamd_ssl_connection *conn, struct iovec *iov,
+               gsize iovlen);
+
 /**
  * Removes connection data
  * @param conn