src/sock_gnutls.c
changeset 12 4147fae232d9
parent 10 9fe218576d13
child 14 3a70e5901f17
--- a/src/sock_gnutls.c	Sat Feb 28 17:39:37 2009 +0200
+++ b/src/sock_gnutls.c	Sat Feb 28 18:48:10 2009 +0200
@@ -8,14 +8,29 @@
 static err_t sock_gnutls_read (struct sock_stream *base_sock, void *buf, size_t *len)
 {
     struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
+    struct error_info *err = SOCK_GNUTLS_ERR(sock);
     int ret;
     
-    // just map to gnutls_record_recv
-    if ((ret = gnutls_record_recv(sock->session, buf, *len)) < 0)
-        RETURN_SET_ERROR_ERRNO(SOCK_GNUTLS_ERR(sock), ERR_GNUTLS_RECORD_RECV);
+    // read gnutls record
+    ret = gnutls_record_recv(sock->session, buf, *len);
     
-    // updated length
-    *len = ret;
+    // errors
+    if (ret < 0 && ret != GNUTLS_E_AGAIN)
+        RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_RECV, ret);
+    
+    else if (ret == 0)
+        return SET_ERROR(err, ERR_READ_EOF);
+
+
+    // eagain?
+    if (ret == 0) {
+        *len = 0;
+
+    } else {
+        // updated length
+        *len = ret;
+
+    }
 
     return SUCCESS;
 }
@@ -23,29 +38,87 @@
 static err_t sock_gnutls_write (struct sock_stream *base_sock, const void *buf, size_t *len)
 {
     struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
+    struct error_info *err = SOCK_GNUTLS_ERR(sock);
     int ret;
+ 
+    // read gnutls record
+    ret = gnutls_record_send(sock->session, buf, *len);
     
-    // just map to gnutls_record_send
-    if ((ret = gnutls_record_send(sock->session, buf, *len)) < 0)
-        RETURN_SET_ERROR_ERRNO(SOCK_GNUTLS_ERR(sock), ERR_GNUTLS_RECORD_SEND);
+    // errors
+    if (ret < 0 && ret != GNUTLS_E_AGAIN)
+        RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_RECV, ret);
     
-    // updated length
-    *len = ret;
+    else if (ret == 0)
+        return SET_ERROR(err, ERR_READ_EOF);
+
+
+    // eagain?
+    if (ret == 0) {
+        *len = 0;
+
+    } else {
+        // updated length
+        *len = ret;
+    }
 
     return SUCCESS;
 }
 
+static void sock_gnutls_event_handler (int fd, short what, void *arg)
+{
+    struct sock_gnutls *sock = arg;
+    
+    // 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);
+}
+
 static err_t sock_gnutls_event_init (struct sock_stream *base_sock)
 {
     struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
-    
+
+    err_t err;
+
+    // set nonblocking
+    if ((err = sock_tcp_set_nonblock(SOCK_GNUTLS_TCP(sock), 1)))
+        return err;
+
+    // add ourselves as the event handler
+    if ((err = sock_tcp_init_ev(SOCK_GNUTLS_TCP(sock), &sock_gnutls_event_handler, sock)))
+        return err;
+
+    // ok
     return SUCCESS;
 }
 
 static err_t sock_gnutls_event_enable (struct sock_stream *base_sock, short mask)
 {
     struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
+    int ret;
     
+    // 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;
+
+    // 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_tcp_add_event(SOCK_GNUTLS_TCP(sock), EV_READ); 
+            break;
+        
+        case 1:
+            // write buffer full
+            sock_tcp_add_event(SOCK_GNUTLS_TCP(sock), EV_WRITE);
+            break;
+        
+        default:
+            // random error
+            RETURN_SET_ERROR_EXTRA(SOCK_GNUTLS_ERR(sock), ERR_GNUTLS_RECORD_GET_DIRECTION, ret);
+    }
+    
+    // ok... wait
     return SUCCESS;
 }