--- 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;
}