src/sock_gnutls.c
branchnew-transport
changeset 155 c59d3eaff0fb
parent 140 aa390e52eda8
child 156 6534a4ac957b
--- a/src/sock_gnutls.c	Tue Apr 28 17:52:48 2009 +0300
+++ b/src/sock_gnutls.c	Tue Apr 28 20:27:45 2009 +0300
@@ -13,23 +13,24 @@
 #include <assert.h>
 
 /**
- * Register for events based on the session's gnutls_record_get_direction().
+ * Enable the TCP events based on the session's gnutls_record_get_direction().
  */
-static err_t sock_gnutls_ev_enable (struct sock_gnutls *sock, struct error_info *err)
+static err_t sock_gnutls_ev_enable (struct sock_gnutls *sock, error_t *err)
 {
     int ret;
+    short mask;
 
     // gnutls_record_get_direction tells us what I/O operation gnutls would have required for the last
     // operation, so we can use that to determine what events to register
     switch ((ret = gnutls_record_get_direction(sock->session))) {
         case 0: 
             // read more data
-            sock_fd_enable_events(SOCK_GNUTLS_FD(sock), EV_READ); 
+            mask = EV_READ;
             break;
         
         case 1:
             // write buffer full
-            sock_fd_enable_events(SOCK_GNUTLS_FD(sock), EV_WRITE);
+            mask = EV_WRITE;
             break;
         
         default:
@@ -37,7 +38,11 @@
             RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_GET_DIRECTION, ret);
     }
     
-    // ok... wait
+    // do the enabling
+    if ((ERROR_CODE(err) = transport_fd_enable(SOCK_GNUTLS_FD(sock), EV_READ)))
+        return ERROR_CODE(err);
+    
+
     return SUCCESS;
 }
 
@@ -72,7 +77,7 @@
  *
  * Based on the GnuTLS examples/ex-rfc2818.c
  */
-static err_t sock_gnutls_verify (struct sock_gnutls *sock, struct error_info *err)
+static err_t sock_gnutls_verify (struct sock_gnutls *sock, error_t *err)
 {
     unsigned int status;
     const gnutls_datum_t *cert_list;
@@ -138,7 +143,7 @@
  *
  * @return >0 for finished handshake, 0 for handshake-in-progress, -err_t for errors.
  */
-static int sock_gnutls_handshake (struct sock_gnutls *sock, struct error_info *err)
+static int sock_gnutls_handshake (struct sock_gnutls *sock, error_t *err)
 {
     int ret;
 
@@ -177,60 +182,61 @@
 }
 
 /**
- * Our SOCK_STREAM event handler. Drive the handshake if that's current, otherwise, invoke user callbacks.
- *
- * XXX: this is ugly. This sock_stream-level separation doesn't really work that well.
+ * Our transport_fd event handler. Drive the handshake if that's current, otherwise, invoke user callbacks.
  */
-static void sock_gnutls_event_handler (int fd, short what, void *arg)
+static void sock_gnutls_on_event (struct transport_fd *fd, short what, void *arg)
 {
     struct sock_gnutls *sock = arg;
-    struct error_info err;
+    error_t err;
 
     (void) fd;
+
+    // XXX: timeouts
     (void) what;
 
     // are we in the handshake cycle?
     if (sock->handshake) {
         RESET_ERROR(&err);
 
+        // perform the next handshake step
         if (sock_gnutls_handshake(sock, &err) == 0) {
-            // wait for the next handshake step
+            // handshake continues
         
-        } else if (SOCK_GNUTLS_BASE(sock)->conn_cb_func) {
+        } else if (SOCK_GNUTLS_TRANSPORT(sock)->connected) {
             // the async connect process has now completed, either succesfully or with an error
             // invoke the user connect callback directly with appropriate error
-            sock_stream_invoke_conn_cb(SOCK_GNUTLS_BASE(sock), ERROR_CODE(&err) ? &err : NULL, true);
+            transport_connected(SOCK_GNUTLS_TRANSPORT(sock), ERROR_CODE(&err) ? &err : NULL, true);
 
         } else {
-            // re-handshake completed, so continue with the sock_stream_callbacks, so the user can call sock_gnutls_read/write
-
             if (ERROR_CODE(&err))
-                // XXX: bad, since we can't report this directly... we need to let the user call _read/write, and get
-                // the error from there
-                log_warn_err(&err, "sock_gnutls_handshake failed");
-            
-            // continue where we left off
-            sock_stream_invoke_callbacks(SOCK_GNUTLS_BASE(sock), sock->ev_mask);
+                // the re-handshake failed, so this transport is dead
+                transport_error(SOCK_GNUTLS_TRANSPORT(sock), &err);
+        
+            else
+                // re-handshake completed, so continue with the transport_callbacks
+                transport_fd_invoke(SOCK_GNUTLS_FD(sock), what);
         }
 
     } else {
         // normal sock_stream operation
-        // gnutls might be able to proceed now, so ask user to try what didn't work before now, using the mask given to
-        // event_enable().
-        sock_stream_invoke_callbacks(SOCK_GNUTLS_BASE(sock), sock->ev_mask);
+        // gnutls might be able to proceed now, so invoke user callbacks
+        transport_fd_invoke(SOCK_GNUTLS_FD(sock), what);
     }
 }
 
-static err_t sock_gnutls_read (struct sock_stream *base_sock, void *buf, size_t *len, struct error_info *err)
+static err_t sock_gnutls_read (transport_t *transport, void *buf, size_t *len, error_t *err)
 {
-    struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
+    struct sock_gnutls *sock = transport_check(transport, &sock_gnutls_type);
     int ret;
     
     // read gnutls record
-    ret = gnutls_record_recv(sock->session, buf, *len);
+    do {
+        ret = gnutls_record_recv(sock->session, buf, *len);
+
+    } while (ret == GNUTLS_E_INTERRUPTED);
     
     // errors
-    // XXX: E_INTERRUPTED, E_REHANDSHAKE?
+    // XXX: E_REHANDSHAKE?
     if (ret < 0 && ret != GNUTLS_E_AGAIN)
         RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_RECV, ret);
     
@@ -238,7 +244,7 @@
         return SET_ERROR(err, ERR_READ_EOF);
 
 
-    // eagain?
+    // EAGAIN?
     if (ret < 0) {
         *len = 0;
 
@@ -251,14 +257,17 @@
     return SUCCESS;
 }
 
-static err_t sock_gnutls_write (struct sock_stream *base_sock, const void *buf, size_t *len, struct error_info *err)
+static err_t sock_gnutls_write (transport_t *transport, const void *buf, size_t *len, error_t *err)
 {
-    struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
+    struct sock_gnutls *sock = transport_check(transport, &sock_gnutls_type);
     int ret;
  
     // read gnutls record
-    ret = gnutls_record_send(sock->session, buf, *len);
-    
+    do {
+        ret = gnutls_record_send(sock->session, buf, *len);
+   
+    } while (ret == GNUTLS_E_INTERRUPTED);
+
     // errors
     if (ret < 0 && ret != GNUTLS_E_AGAIN)
         RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_RECV, ret);
@@ -279,43 +288,21 @@
     return SUCCESS;
 }
 
-static err_t sock_gnutls_event_init (struct sock_stream *base_sock)
-{
-    struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
-
-    (void) sock;
-
-    // already setup, ok
-    return SUCCESS;
-}
-
-static err_t sock_gnutls_event_enable (struct sock_stream *base_sock, short mask)
+static void _sock_gnutls_destroy (transport_t *transport)
 {
-    struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
-    
-    // store the ev_mask. We don't care about it here, because we assume that event_enable is only called once read or
-    // write, respectively, return zero. This is really the only case we can handle with gnutls.
-    sock->ev_mask = mask;
+    struct sock_gnutls *sock = transport_check(transport, &sock_gnutls_type);
     
-    // then wait for the event
-    return sock_gnutls_ev_enable(sock, SOCK_GNUTLS_ERR(sock));
-}
-
-static void sock_gnutls_release (struct sock_stream *base_sock)
-{
-    struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
-    
-    // DIEEEE
+    // die
     sock_gnutls_destroy(sock);
 }
 
 /**
  * Our sock_tcp-invoked connect handler
  */
-static void sock_gnutls_on_connect (struct sock_stream *base_sock, struct error_info *tcp_err)
+static void sock_gnutls__connected (transport_t *transport, const error_t *tcp_err)
 {
-    struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
-    struct error_info err;
+    struct sock_gnutls *sock = transport_check(transport, &sock_gnutls_type);
+    error_t err;
 
     // trap errors to let the user handle them directly
     if (tcp_err)
@@ -325,7 +312,7 @@
     gnutls_transport_set_ptr(sock->session, (gnutls_transport_ptr_t) (long int) SOCK_GNUTLS_FD(sock)->fd);
 
     // add ourselves as the event handler
-    if ((ERROR_CODE(&err) = sock_fd_init_ev(SOCK_GNUTLS_FD(sock), &sock_gnutls_event_handler, sock)))
+    if ((ERROR_CODE(&err) = transport_fd_setup(SOCK_GNUTLS_FD(sock), sock_gnutls_on_event, sock)))
         goto error;
 
     // start handshake
@@ -338,20 +325,15 @@
 
 error:
     // tell the user
-    SOCK_GNUTLS_BASE(sock)->conn_cb_func(SOCK_GNUTLS_BASE(sock), &err, SOCK_GNUTLS_BASE(sock)->conn_cb_arg);
+    transport_connected(transport, &err, true);
 }
 
-/*
- * Our sock_stream_Type
- */
-struct sock_stream_type sock_gnutls_type = {
+struct transport_type sock_gnutls_type = {
     .methods                = {
-        .read               = &sock_gnutls_read,
-        .write              = &sock_gnutls_write,
-        .event_init         = &sock_gnutls_event_init,
-        .event_enable       = &sock_gnutls_event_enable,
-        .release            = &sock_gnutls_release,
-        ._conn_cb           = &sock_gnutls_on_connect,
+        .read               = sock_gnutls_read,
+        .write              = sock_gnutls_write,
+        .destroy            = _sock_gnutls_destroy,
+        ._connected         = sock_gnutls__connected,
     },
 };
 
@@ -366,7 +348,7 @@
     printf("gnutls: %d: %s", level, msg);
 }
 
-err_t sock_gnutls_global_init (struct error_info *err)
+err_t sock_gnutls_global_init (error_t *err)
 {
     // global init
     if ((ERROR_EXTRA(err) = gnutls_global_init()) < 0)
@@ -395,7 +377,7 @@
 err_t sock_ssl_client_cred_create (struct sock_ssl_client_cred **ctx_cred,
         const char *cafile_path, bool verify,
         const char *cert_path, const char *pkey_path,
-        struct error_info *err
+        error_t *err
 ) {
     struct sock_ssl_client_cred *cred;
 
@@ -453,11 +435,10 @@
         sock_ssl_client_cred_destroy(cred);
 }
 
-err_t sock_ssl_connect_async (struct sock_stream **sock_ptr, 
+err_t sock_ssl_connect (const struct transport_info *info, transport_t **transport_ptr, 
         const char *hostname, const char *service,
         struct sock_ssl_client_cred *cred,
-        sock_stream_connect_cb cb_func, void *cb_arg, 
-        struct error_info *err
+        error_t *err
     )
 {
     struct sock_gnutls *sock = NULL;
@@ -467,7 +448,7 @@
         return SET_ERROR(err, ERR_CALLOC);
 
     // initialize base
-    sock_stream_init(SOCK_GNUTLS_BASE(sock), &sock_gnutls_type, cb_func, cb_arg);
+    transport_init(SOCK_GNUTLS_TRANSPORT(sock), &sock_gnutls_type, info);
 
     if (!cred) {
         // default credentials
@@ -487,6 +468,9 @@
     if ((sock->hostname = strdup(hostname)) == NULL)
         JUMP_SET_ERROR(err, ERR_STRDUP);
 
+    // initialize TCP
+    sock_tcp_init(SOCK_GNUTLS_TCP(sock));
+
     // initialize client session
     if ((ERROR_EXTRA(err) = gnutls_init(&sock->session, GNUTLS_CLIENT)) < 0)
         JUMP_SET_ERROR(err, ERR_GNUTLS_INIT);
@@ -503,11 +487,11 @@
         JUMP_SET_ERROR(err, ERR_GNUTLS_CRED_SET);
 
     // TCP connect
-    if (sock_tcp_connect_async_begin(SOCK_GNUTLS_TCP(sock), hostname, service, err))
+    if (sock_tcp_connect_async(SOCK_GNUTLS_TCP(sock), hostname, service, err))
         goto error;
 
     // done, wait for the connect to complete
-    *sock_ptr = SOCK_GNUTLS_BASE(sock);
+    *transport_ptr = SOCK_GNUTLS_TRANSPORT(sock);
 
     return SUCCESS;
 
@@ -520,18 +504,17 @@
 
 void sock_gnutls_destroy (struct sock_gnutls *sock)
 {
-    // terminate the TCP transport
-    sock_fd_close(SOCK_GNUTLS_FD(sock));
-
     // close the session rudely
     gnutls_deinit(sock->session);
-    
+ 
+    // terminate the TCP transport
+    sock_tcp_destroy(SOCK_GNUTLS_TCP(sock));
+   
     if (sock->cred)
         // drop the cred ref
         sock_ssl_client_cred_put(sock->cred);
 
     // free
     free(sock->hostname);
-    free(sock);
 }