rename sock_ssl/sock_gnutls -> ssl/ssl_client
authorTero Marttila <terom@fixme.fi>
Thu, 07 May 2009 02:46:44 +0300
changeset 180 22967b165692
parent 179 0eaa5c1b926d
child 181 b12a6081fc85
rename sock_ssl/sock_gnutls -> ssl/ssl_client
src/CMakeLists.txt
src/error.c
src/irc_net.c
src/irc_net.h
src/irc_net_connect.c
src/lua_irc.c
src/module.c
src/nexus.c
src/sock.c
src/sock.h
src/sock_gnutls.c
src/sock_gnutls.h
src/sock_ssl.h
src/ssl.h
src/ssl_client.c
src/ssl_internal.h
--- a/src/CMakeLists.txt	Thu May 07 02:17:55 2009 +0300
+++ b/src/CMakeLists.txt	Thu May 07 02:46:44 2009 +0300
@@ -11,7 +11,7 @@
 
 # define our source code modules
 set (CORE_SOURCES error.c log.c str.c object.c)
-set (IO_SOURCES transport.c service.c transport_fd.c sock.c resolve.c tcp.c tcp_transport.c tcp_client.c tcp_server.c fifo.c line_proto.c)
+set (IO_SOURCES transport.c service.c transport_fd.c sock.c resolve.c tcp.c tcp_transport.c tcp_client.c tcp_server.c ssl.c ssl_client.c fifo.c line_proto.c)
 set (IRC_SOURCES irc_line.c irc_conn.c irc_net.c irc_chan.c chain.c irc_cmd.c irc_proto.c irc_client.c irc_user.c irc_queue.c irc_net_connect.c)
 set (LUA_SOURCES nexus_lua.c lua_objs.c lua_config.c lua_irc.c lua_func.c lua_type.c)
 set (CONSOLE_SOURCES console.c lua_console.c)
--- a/src/error.c	Thu May 07 02:17:55 2009 +0300
+++ b/src/error.c	Thu May 07 02:46:44 2009 +0300
@@ -3,7 +3,7 @@
 
 // for the error_desc tables
 #include "sock.h"
-#include "sock_gnutls.h"
+#include "ssl_internal.h"
 #include "module.h"
 
 #include <string.h>
--- a/src/irc_net.c	Thu May 07 02:17:55 2009 +0300
+++ b/src/irc_net.c	Thu May 07 02:46:44 2009 +0300
@@ -310,7 +310,7 @@
 
     // our info
     if (net->info.ssl_cred)
-        sock_ssl_client_cred_put(net->info.ssl_cred);
+        ssl_client_cred_put(net->info.ssl_cred);
     
     // misc
     irc_net_connect_destroy(net);
--- a/src/irc_net.h	Thu May 07 02:17:55 2009 +0300
+++ b/src/irc_net.h	Thu May 07 02:46:44 2009 +0300
@@ -13,7 +13,7 @@
 #include "error.h"
 #include "irc_conn.h"
 #include "irc_chan.h"
-#include "sock_ssl.h"
+#include "ssl.h"
 #include <sys/queue.h>
 
 /**
@@ -30,7 +30,7 @@
     const char *service;
 
     /** Use SSL if given as non-NULL, a reference will be held by the irc_net */
-    struct sock_ssl_client_cred *ssl_cred;
+    struct ssl_client_cred *ssl_cred;
 
     /** Protocol registration info (nickname etc) */
     struct irc_conn_register_info register_info;
--- a/src/irc_net_connect.c	Thu May 07 02:17:55 2009 +0300
+++ b/src/irc_net_connect.c	Thu May 07 02:46:44 2009 +0300
@@ -1,7 +1,7 @@
 
 #include "irc_net_internal.h"
 #include "tcp.h"
-#include "sock_ssl.h"
+#include "ssl.h"
 #include "log.h"
 
 #include <time.h>
@@ -134,12 +134,12 @@
     } else if (info->ssl_cred) {
         // aquire a ref
         // NOTE: before any error handling
-        sock_ssl_client_cred_get(info->ssl_cred);
+        ssl_client_cred_get(info->ssl_cred);
         
         log_debug("connecting to [%s]:%s using SSL", info->hostname, info->service);
 
         // connect
-        if (sock_ssl_connect(&transport_info, &transport, info->hostname, info->service, info->ssl_cred, err))
+        if (ssl_connect(&transport_info, &transport, info->hostname, info->service, info->ssl_cred, err))
             goto error;
 
         net->connecting = true;
--- a/src/lua_irc.c	Thu May 07 02:17:55 2009 +0300
+++ b/src/lua_irc.c	Thu May 07 02:46:44 2009 +0300
@@ -259,7 +259,7 @@
             return luaL_error(L, "must give both ssl_cert and ssl_pkey");
         
         // create
-        if (sock_ssl_client_cred_create(&net_info.ssl_cred, ssl_cafile, ssl_verify, ssl_cert, ssl_pkey, &err))
+        if (ssl_client_cred_create(&net_info.ssl_cred, ssl_cafile, ssl_verify, ssl_cert, ssl_pkey, &err))
             return luaL_error(L, "sock_ssl_client_cred_create(ssl_cafile=%s, ssl_verify=%b, ssl_cert=%s, ssl_pkey=%s): %s", 
                     ssl_cafile, ssl_verify, ssl_cert, ssl_pkey, error_msg(&err)
                 );
--- a/src/module.c	Thu May 07 02:17:55 2009 +0300
+++ b/src/module.c	Thu May 07 02:46:44 2009 +0300
@@ -382,7 +382,7 @@
 
     // unload the dl handle
     if (module->handle && dlclose(module->handle))
-        log_error("dlclose(%s): %s", module->info.name, dlerror());
+        log_warn("dlclose(%s): %s", module->info.name, dlerror());
     
     // release the path buf, if any
     free(module->path_buf);
--- a/src/nexus.c	Thu May 07 02:17:55 2009 +0300
+++ b/src/nexus.c	Thu May 07 02:46:44 2009 +0300
@@ -1,5 +1,6 @@
 #include "nexus.h"
 #include "lua_config.h"
+#include "sock.h"
 #include "log.h"
 
 #include <stdlib.h>
@@ -193,7 +194,7 @@
             JUMP_SET_ERROR_STR(err, ERR_CMD_OPT, "must give both :ssl_cert/:ssl_pkey");
         
         // create
-        if (sock_ssl_client_cred_create(&info.ssl_cred, ssl_cafile, ssl_verify, ssl_cert, ssl_pkey, err))
+        if (ssl_client_cred_create(&info.ssl_cred, ssl_cafile, ssl_verify, ssl_cert, ssl_pkey, err))
             goto error;
     }
 
@@ -209,7 +210,7 @@
 error:
     // cleanup
     if (info.ssl_cred)
-        sock_ssl_client_cred_put(info.ssl_cred);
+        ssl_client_cred_put(info.ssl_cred);
 
     return ERROR_CODE(err);
 }
--- a/src/sock.c	Thu May 07 02:17:55 2009 +0300
+++ b/src/sock.c	Thu May 07 02:46:44 2009 +0300
@@ -1,6 +1,6 @@
 
 #include "sock_internal.h"
-#include "sock_gnutls.h"
+#include "ssl_internal.h"
 
 #include <assert.h>
 
@@ -13,7 +13,7 @@
     _sock_stream_ctx.ev_base = ev_base;
 
     // XXX: just call these all directly for now
-    if (sock_gnutls_global_init(err))
+    if (ssl_global_init(err))
         return ERROR_CODE(err);
 
     // done
--- a/src/sock.h	Thu May 07 02:17:55 2009 +0300
+++ b/src/sock.h	Thu May 07 02:46:44 2009 +0300
@@ -4,7 +4,7 @@
 /**
  * @file
  *
- * General sock_* interface.
+ * Legacy sock_* interface for global state
  */
 #include "error.h"
 #include <sys/types.h>
--- a/src/sock_gnutls.c	Thu May 07 02:17:55 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,522 +0,0 @@
-
-#include "sock_gnutls.h"
-
-// XXX: remove
-#include "log.h"
-
-#include <gnutls/x509.h>
-
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-
-#include <assert.h>
-
-/**
- * Enable the TCP events based on the session's gnutls_record_get_direction().
- */
-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
-            mask = TRANSPORT_READ;
-            break;
-        
-        case 1:
-            // write buffer full
-            mask = TRANSPORT_WRITE;
-            break;
-        
-        default:
-            // random error
-            RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_GET_DIRECTION, ret);
-    }
-    
-    // do the enabling
-    if ((ERROR_CODE(err) = transport_fd_enable(SOCK_GNUTLS_FD(sock), mask)))
-        return ERROR_CODE(err);
-    
-
-    return SUCCESS;
-}
-
-/**
- * Translate a set of gnutls_certificate_status_t values to a constant error message
- */
-static const char* sock_gnutls_verify_error (unsigned int status)
-{
-    if (status & GNUTLS_CERT_REVOKED)
-        return "certificate was revoked";
-
-    else if (status & GNUTLS_CERT_INVALID) {
-        if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
-            return "certificate signer was not found";
-
-        else if (status & GNUTLS_CERT_SIGNER_NOT_CA)
-            return "certificate signer is not a Certificate Authority";
-
-        else if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
-            return "certificate signed using an insecure algorithm";
-
-        else
-            return "certificate could not be verified";
-
-    } else
-        return "unknown error";
-
-}
-
-/**
- * Perform the certificate validation procedure on the socket.
- *
- * Based on the GnuTLS examples/ex-rfc2818.c
- */
-static err_t sock_gnutls_verify (struct sock_gnutls *sock, error_t *err)
-{
-    unsigned int status;
-    const gnutls_datum_t *cert_list;
-    unsigned int cert_list_size;
-    gnutls_x509_crt_t cert = NULL;
-    time_t t, now;
-
-    // init
-    RESET_ERROR(err);
-    now = time(NULL);
-    
-    // inspect the peer's cert chain using the installed trusted CAs
-    if ((ERROR_EXTRA(err) = gnutls_certificate_verify_peers2(sock->session, &status)))
-        JUMP_SET_ERROR(err, ERR_GNUTLS_CERT_VERIFY_PEERS2);
-
-    // verify errors?
-    if (status)
-        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, sock_gnutls_verify_error(status));
-    
-    // import the main cert
-    assert(gnutls_certificate_type_get(sock->session) == GNUTLS_CRT_X509);
-
-    if ((ERROR_EXTRA(err) = gnutls_x509_crt_init(&cert)))
-        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_init");
-
-    if ((cert_list = gnutls_certificate_get_peers(sock->session, &cert_list_size)) == NULL)
-        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_certificate_get_peers");
-
-    if (!cert_list_size)
-        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "cert_list_size");
-
-    if ((ERROR_EXTRA(err) = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER)))
-        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_import");
-    
-    // check expire/activate... not sure if we need to do this
-    if ((t = gnutls_x509_crt_get_expiration_time(cert)) == ((time_t) -1) || t < now)
-        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_get_expiration_time");
-
-    if ((t = gnutls_x509_crt_get_activation_time(cert)) == ((time_t) -1) || t > now)
-        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_get_activation_time");
-    
-    // check hostname
-    if (!gnutls_x509_crt_check_hostname(cert, sock->hostname))
-        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_check_hostname");
-
-error:
-    // cleanup
-    if (cert)
-        gnutls_x509_crt_deinit(cert);
-    
-    // should be SUCCESS
-    return ERROR_CODE(err);    
-}
-
-
-/**
- * Our handshake driver. This will execute the next gnutls_handshake step, handling E_AGAIN.
- *
- * This updates the sock_gnutls::handshake state internally, as used by sock_gnutls_event_handler.
- *
- * If the sock is marked as verify, this will perform the verification, returning on any errors, and then unset the
- * verify flag - this ensures that the peer cert is only verified once per connection...
- *
- * @return >0 for finished handshake, 0 for handshake-in-progress, -err_t for errors.
- */
-static int sock_gnutls_handshake (struct sock_gnutls *sock, error_t *err)
-{
-    int ret;
-
-    // perform the handshake
-    if ((ret = gnutls_handshake(sock->session)) < 0 && ret != GNUTLS_E_AGAIN)
-        JUMP_SET_ERROR_EXTRA(err, ERR_GNUTLS_HANDSHAKE, ret);
-    
-    // complete?
-    if (ret == 0) {
-        // update state
-        sock->handshake = false;
-
-        // verify?
-        if (sock->verify) {
-            // perform the validation
-            if (sock_gnutls_verify(sock, err))
-                goto error;
-            
-            // unmark
-            sock->verify = false;
-        }
-
-        // handshake done
-        return 1;
-
-    } else {
-        // set state, isn't really needed every time, but easier this way
-        sock->handshake = true;
-
-        // re-enable the event for the next iteration
-        return sock_gnutls_ev_enable(sock, err);
-    }
-
-error:
-    return -ERROR_CODE(err);    
-}
-
-/**
- * Our transport_fd event handler. Drive the handshake if that's current, otherwise, invoke user callbacks.
- */
-static void sock_gnutls_on_event (struct transport_fd *fd, short what, void *arg)
-{
-    struct sock_gnutls *sock = arg;
-    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) {
-            // handshake continues
-        
-            // XXX: this state flag is completely wrong
-        } 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
-            transport_connected(SOCK_GNUTLS_TRANSPORT(sock), ERROR_CODE(&err) ? &err : NULL, true);
-
-        } else {
-            if (ERROR_CODE(&err))
-                // 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_invoke(SOCK_GNUTLS_TRANSPORT(sock), what);
-        }
-
-    } else {
-        // normal sock_stream operation
-        // gnutls might be able to proceed now, so invoke user callbacks
-        transport_invoke(SOCK_GNUTLS_TRANSPORT(sock), what);
-    }
-}
-
-static err_t sock_gnutls_read (transport_t *transport, void *buf, size_t *len, error_t *err)
-{
-    struct sock_gnutls *sock = transport_check(transport, &sock_gnutls_type);
-    int ret;
-    
-    // read gnutls record
-    do {
-        ret = gnutls_record_recv(sock->session, buf, *len);
-
-    } while (ret == GNUTLS_E_INTERRUPTED);
-    
-    // errors
-    // XXX: E_REHANDSHAKE?
-    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_EOF);
-
-
-    // EAGAIN?
-    if (ret < 0) {
-        *len = 0;
-
-    } else {
-        // updated length
-        *len = ret;
-
-    }
-
-    return SUCCESS;
-}
-
-static err_t sock_gnutls_write (transport_t *transport, const void *buf, size_t *len, error_t *err)
-{
-    struct sock_gnutls *sock = transport_check(transport, &sock_gnutls_type);
-    int ret;
- 
-    // read gnutls record
-    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_SEND, ret);
-    
-    else if (ret == 0)
-        return SET_ERROR(err, ERR_WRITE_EOF);
-
-
-    // eagain?
-    if (ret < 0) {
-        *len = 0;
-
-    } else {
-        // updated length
-        *len = ret;
-    }
-
-    return SUCCESS;
-}
-
-static void _sock_gnutls_destroy (transport_t *transport)
-{
-    struct sock_gnutls *sock = transport_check(transport, &sock_gnutls_type);
-    
-    // die
-    sock_gnutls_destroy(sock);
-}
-
-/**
- * Our sock_tcp-invoked connect handler
- */
-static void sock_gnutls__connected (transport_t *transport, const error_t *tcp_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)
-        JUMP_SET_ERROR_INFO(&err, tcp_err);
-    
-    // bind default transport functions (recv/send) to use the TCP fd
-    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) = transport_fd_setup(SOCK_GNUTLS_FD(sock), sock_gnutls_on_event, sock)))
-        goto error;
-
-    // start handshake
-    if (sock_gnutls_handshake(sock, &err))
-        // this should complete with SUCCESS if it returns >0
-        goto error;
-
-    // ok, so we wait...
-    return;
-
-error:
-    // tell the user
-    transport_connected(transport, &err, true);
-}
-
-struct transport_type sock_gnutls_type = {
-    .parent                 = &sock_tcp_type,
-    .methods                = {
-        .read               = sock_gnutls_read,
-        .write              = sock_gnutls_write,
-        .destroy            = _sock_gnutls_destroy,
-        ._connected         = sock_gnutls__connected,
-    },
-};
-
-/*
- * Global shared anonymous client credentials
- */
-static struct sock_ssl_client_cred sock_gnutls_client_cred_anon = { .x509 = NULL, .verify = false, .refcount = 0 };
-
-// XXX: GnuTLS log func
-void _log (int level, const char *msg)
-{
-    printf("gnutls: %d: %s", level, msg);
-}
-
-err_t sock_gnutls_global_init (error_t *err)
-{
-    // global init
-    if ((ERROR_EXTRA(err) = gnutls_global_init()) < 0)
-        return SET_ERROR(err, ERR_GNUTLS_GLOBAL_INIT);
-
-    // initialize the anon client credentials
-    if ((ERROR_EXTRA(err) = gnutls_certificate_allocate_credentials(&sock_gnutls_client_cred_anon.x509)) < 0)
-        return SET_ERROR(err, ERR_GNUTLS_CERT_ALLOC_CRED);
-
-    // XXX: debug
-//    gnutls_global_set_log_function(&_log);
-//    gnutls_global_set_log_level(11);
-
-    // done
-    return SUCCESS;
-}
-
-static void sock_ssl_client_cred_destroy (struct sock_ssl_client_cred *cred)
-{
-    // simple
-    gnutls_certificate_free_credentials(cred->x509);
-
-    free(cred);
-}
-
-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,
-        error_t *err
-) {
-    struct sock_ssl_client_cred *cred;
-
-    // alloc it
-    if ((cred = calloc(1, sizeof(*cred))) == NULL)
-        return SET_ERROR(err, ERR_CALLOC);
-
-    // create the cert
-    if ((ERROR_EXTRA(err) = gnutls_certificate_allocate_credentials(&cred->x509)) < 0)
-        JUMP_SET_ERROR(err, ERR_GNUTLS_CERT_ALLOC_CRED);
-    
-    // load the trusted ca certs?
-    if (cafile_path) {
-        // load them
-        if ((ERROR_EXTRA(err) = gnutls_certificate_set_x509_trust_file(cred->x509, cafile_path, GNUTLS_X509_FMT_PEM)) < 0)
-            JUMP_SET_ERROR(err, ERR_GNUTLS_CERT_SET_X509_TRUST_FILE);
-
-    }
-
-    // set the verify flags?
-    cred->verify = verify;
-    gnutls_certificate_set_verify_flags(cred->x509, 0);
-
-    // load the client cert?
-    if (cert_path || pkey_path) {
-        // need both...
-        assert(cert_path && pkey_path);
-
-        // load
-        if ((ERROR_EXTRA(err) = gnutls_certificate_set_x509_key_file(cred->x509, cert_path, pkey_path, GNUTLS_X509_FMT_PEM)))
-            JUMP_SET_ERROR(err, ERR_GNUTLS_CERT_SET_X509_KEY_FILE);
-    }
-
-    // ok
-    cred->refcount = 1;
-    *ctx_cred = cred;
-
-    return SUCCESS;
-
-error:
-    // release
-    sock_ssl_client_cred_destroy(cred);
-
-    return ERROR_CODE(err);
-}
-
-void sock_ssl_client_cred_get (struct sock_ssl_client_cred *cred)
-{
-    cred->refcount++;
-}
-
-void sock_ssl_client_cred_put (struct sock_ssl_client_cred *cred)
-{
-    if (--cred->refcount == 0)
-        sock_ssl_client_cred_destroy(cred);
-}
-
-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,
-        error_t *err
-    )
-{
-    struct sock_gnutls *sock = NULL;
-
-    // alloc
-    if ((sock = calloc(1, sizeof(*sock))) == NULL)
-        return SET_ERROR(err, ERR_CALLOC);
-
-    // initialize base
-    transport_init(SOCK_GNUTLS_TRANSPORT(sock), &sock_gnutls_type, info);
-
-    if (!cred) {
-        // default credentials
-        cred = &sock_gnutls_client_cred_anon;
-    
-    } else {
-        // take a ref
-        sock->cred = cred;
-        cred->refcount++;
-    };
-
-    // do verify?
-    if (cred->verify)
-        sock->verify = true;
-
-    // init
-    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);
-
-    // ...default priority stuff
-    if ((ERROR_EXTRA(err) = gnutls_set_default_priority(sock->session)))
-        JUMP_SET_ERROR(err, ERR_GNUTLS_SET_DEFAULT_PRIORITY);
-
-    // XXX: silly hack for OpenSSL interop
-    gnutls_dh_set_prime_bits(sock->session, 512);
-
-    // bind credentials
-    if ((ERROR_EXTRA(err) = gnutls_credentials_set(sock->session, GNUTLS_CRD_CERTIFICATE, cred->x509)))
-        JUMP_SET_ERROR(err, ERR_GNUTLS_CRED_SET);
-
-    // TCP connect
-    if (sock_tcp_connect_async(SOCK_GNUTLS_TCP(sock), hostname, service, err))
-        goto error;
-
-    // done, wait for the connect to complete
-    *transport_ptr = SOCK_GNUTLS_TRANSPORT(sock);
-
-    return SUCCESS;
-
-error:
-    // cleanup
-    sock_gnutls_destroy(sock);
-
-    return ERROR_CODE(err);    
-}
-
-void sock_gnutls_destroy (struct sock_gnutls *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);
-}
-
--- a/src/sock_gnutls.h	Thu May 07 02:17:55 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-#ifndef SOCK_GNUTLS_H
-#define SOCK_GNUTLS_H
-
-/**
- * @file
- *
- * A sock_stream implementation using GnuTLS for SSL
- */
-
-#include "sock_tcp_internal.h"
-
-#include <gnutls/gnutls.h>
-
-/**
- * GnuTLS library error codes
- */
-enum sock_gnutls_error_code {
-    _ERR_GNUTLS_BEGIN = _ERR_GNUTLS,
-    
-    ERR_GNUTLS_CERT_ALLOC_CRED,
-    ERR_GNUTLS_GLOBAL_INIT,
-    ERR_GNUTLS_INIT,
-    ERR_GNUTLS_SET_DEFAULT_PRIORITY,
-    ERR_GNUTLS_CRED_SET,
-    ERR_GNUTLS_HANDSHAKE,
-    ERR_GNUTLS_RECORD_SEND,
-    ERR_GNUTLS_RECORD_RECV,
-    ERR_GNUTLS_RECORD_GET_DIRECTION,   
-    ERR_GNUTLS_CERT_VERIFY_PEERS2,
-    ERR_GNUTLS_CERT_VERIFY,
-    ERR_GNUTLS_CERT_SET_X509_TRUST_FILE,
-    ERR_GNUTLS_CERT_SET_X509_KEY_FILE,
-};
-
-/*
- * Our transport_type
- */
-extern struct transport_type sock_gnutls_type;
-
-/**
- * GnuTLS credentials for client sockets.
- */
-struct sock_ssl_client_cred {
-    /** Our client certificate */
-    gnutls_certificate_credentials_t x509;
-
-    /** Should we verify? */
-    bool verify;
-
-    /** Refcount from sock_gnutls */
-    int refcount;
-};
-
-/**
- * An SSL-encrypted TCP connection, using libgnutls
- */
-struct sock_gnutls {
-    /** The underlying TCP connection */
-    struct sock_tcp base_tcp;
-
-    /** The hostname we connected to, for verification */
-    char *hostname;
-
-    /** The credentials we are using, unless anon */
-    struct sock_ssl_client_cred *cred;
-    
-    /** The GnuTLS session for this connection */
-    gnutls_session_t session;
-
-    /** The current event_enable mask */
-    int ev_mask;
-
-    /** Should we verify the peer cert? */
-    bool verify;
-
-    /** Are we running a handshake? */
-    bool handshake;
-};
-
-/**
- * Cast a sock_gnutls to a sock_tcp.
- */
-#define SOCK_GNUTLS_TCP(sock_ptr) (&(sock_ptr)->base_tcp)
-
-/**
- * Cast a sock_gnutls to a sock_fd.
- */
-#define SOCK_GNUTLS_FD(sock_ptr) SOCK_TCP_FD(SOCK_GNUTLS_TCP(sock_ptr))
-
-/**
- * Cast a sock_gnutls to a sock_stream.
- */
-#define SOCK_GNUTLS_TRANSPORT(sock_ptr) SOCK_TCP_TRANSPORT(SOCK_GNUTLS_TCP(sock_ptr))
-
-/**
- * Initialize the global gnutls state
- */
-err_t sock_gnutls_global_init (struct error_info *err);
-
-/**
- * Release the connection destructively, i.e. do not close it cleanly, just abort.
- */
-void sock_gnutls_destroy (struct sock_gnutls *sock);
-
-#endif /* SOCK_GNUTLS_H */
--- a/src/sock_ssl.h	Thu May 07 02:17:55 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,76 +0,0 @@
-#ifndef SOCK_SSL_H
-#define SOCK_SSL_H
-
-/**
- * @file
- *
- * SSL transport implementation.
- */
-#include "sock.h"
-
-/**
- * SSL client credentials for use with sock_ssl_client_credentials/sock_ssl_connect
- */
-struct sock_ssl_client_cred;
-
-/**
- * Set up SSL client credentials for use with sock_ssl_connect. This includes information both required to identify
- * ourselves to the server, as well as to verify the server.
- *
- * To verify the server's certificate, pass in a path to a file containing the CA certificate(s) that should be used to
- * verify the server's certificate, and then either give `verify` as true to force verification, or false to simply
- * warn. XXX: not entirely true
- *
- * To supply a client certificate to the server, pass in the paths to the cert/pkey files. If given as NULL, an
- * anonymous client certificate will be used. Both must be supplied if given.
- *
- * The newly created SSL client credential will initially have a refcount of one, and can then be used with sock_ssl_connect.
- *
- * @param ctx_cred the newly created client credentials are returned via this
- * @param cafile_path given as non-NULL to load trusted certs for verification from the given path
- * @param verify force verification of the peer cert
- * @param cert_path path to the client certificate file, or NULL
- * @param pkey_path path to the client private key, or NULL
- * @param err returned error info
- */
-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,
-        error_t *err
-);
-
-/**
- * Aquire a referenec for the given cred.
- */
-void sock_ssl_client_cred_get (struct sock_ssl_client_cred *cred);
-
-/**
- * Release a reference allocated for the given cred.
- */
-void sock_ssl_client_cred_put (struct sock_ssl_client_cred *cred);
-
-/**
- * Start a non-blocking SSL connect/handshake to the given host/service. The socket will not yet be connected when the
- * function returns, but rather, the eventual redyness/failure of the connect/handshake will be indicated later using
- * the given \a cb_func.
- *
- * The given sock_ssl_client_cred should either be NULL to use an anonymous client cert and not verify the server cert,
- * or a sock_ssl_client_cred allocated using sock_ssl_client_cred_create(). The contexts are reference-counted, so once
- * a cred context has been released, it will be destroyed once the last connection using it is destroyed.
- * 
- * @param info the transport setup info
- * @param transport_ptr returned transport
- * @param hostname the hostname to connect to
- * @param service the TCP service name (i.e. port) to connect to
- * @param cred the SSL client credentials to use, if not NULL
- * @param err returned error info
- */
-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,
-        error_t *err
-    );
-
-
-
-#endif /* SOCK_SSL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ssl.h	Thu May 07 02:46:44 2009 +0300
@@ -0,0 +1,77 @@
+#ifndef SSL_H
+#define SSL_H
+
+/**
+ * @file
+ *
+ * SSL transport implementation.
+ */
+#include "transport.h"
+#include <stdbool.h>
+
+/**
+ * SSL client credentials for use with ssl_client_credentials/sock_ssl_connect
+ */
+struct ssl_client_cred;
+
+/**
+ * Set up SSL client credentials for use with sock_ssl_connect. This includes information both required to identify
+ * ourselves to the server, as well as to verify the server.
+ *
+ * To verify the server's certificate, pass in a path to a file containing the CA certificate(s) that should be used to
+ * verify the server's certificate, and then either give `verify` as true to force verification, or false to simply
+ * warn. XXX: not entirely true
+ *
+ * To supply a client certificate to the server, pass in the paths to the cert/pkey files. If given as NULL, an
+ * anonymous client certificate will be used. Both must be supplied if given.
+ *
+ * The newly created SSL client credential will initially have a refcount of one, and can then be used with sock_ssl_connect.
+ *
+ * @param ctx_cred the newly created client credentials are returned via this
+ * @param cafile_path given as non-NULL to load trusted certs for verification from the given path
+ * @param verify force verification of the peer cert
+ * @param cert_path path to the client certificate file, or NULL
+ * @param pkey_path path to the client private key, or NULL
+ * @param err returned error info
+ */
+err_t ssl_client_cred_create (struct ssl_client_cred **ctx_cred,
+        const char *cafile_path, bool verify,
+        const char *cert_path, const char *pkey_path,
+        error_t *err
+);
+
+/**
+ * Aquire a referenec for the given cred.
+ */
+void ssl_client_cred_get (struct ssl_client_cred *cred);
+
+/**
+ * Release a reference allocated for the given cred.
+ */
+void ssl_client_cred_put (struct ssl_client_cred *cred);
+
+/**
+ * Start a non-blocking SSL connect/handshake to the given host/service. The socket will not yet be connected when the
+ * function returns, but rather, the eventual redyness/failure of the connect/handshake will be indicated later using
+ * the given \a cb_func.
+ *
+ * The given ssl_client_cred should either be NULL to use an anonymous client cert and not verify the server cert,
+ * or a ssl_client_cred allocated using ssl_client_cred_create(). The contexts are reference-counted, so once
+ * a cred context has been released, it will be destroyed once the last connection using it is destroyed.
+ * 
+ * @param info the transport setup info
+ * @param transport_ptr returned transport
+ * @param hostname the hostname to connect to
+ * @param service the TCP service name (i.e. port) to connect to
+ * @param cred the SSL client credentials to use, if not NULL
+ * @param err returned error info
+ */
+err_t ssl_connect (const struct transport_info *info, transport_t **transport_ptr, 
+        const char *hostname, const char *service,
+        struct ssl_client_cred *cred,
+        error_t *err
+    );
+
+
+
+#endif /* SSL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ssl_client.c	Thu May 07 02:46:44 2009 +0300
@@ -0,0 +1,453 @@
+#include "ssl_internal.h"
+
+#include <gnutls/x509.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+// XXX: remove
+#include "log.h"
+#include <assert.h>
+
+
+/**
+ * Cast a ssl_client to a sock_fd.
+ */
+#define SSL_CLIENT_FD(client_ptr) (&(client_ptr)->base_tcp.base_trans.base_fd)
+
+/**
+ * Cast a ssl_client to a sock_stream.
+ */
+#define SSL_CLIENT_TRANSPORT(client_ptr) (&(client_ptr)->base_tcp.base_trans.base_fd.base)
+
+
+
+/**
+ * Enable the TCP events based on the session's gnutls_record_get_direction().
+ */
+static err_t ssl_client_ev_enable (struct ssl_client *client, 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(client->session))) {
+        case 0: 
+            // read more data
+            mask = TRANSPORT_READ;
+            break;
+        
+        case 1:
+            // write buffer full
+            mask = TRANSPORT_WRITE;
+            break;
+        
+        default:
+            // random error
+            RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_GET_DIRECTION, ret);
+    }
+    
+    // do the enabling
+    if ((ERROR_CODE(err) = transport_fd_enable(SSL_CLIENT_FD(client), mask)))
+        return ERROR_CODE(err);
+    
+
+    return SUCCESS;
+}
+
+/**
+ * Translate a set of gnutls_certificate_status_t values to a constant error message
+ */
+static const char* ssl_client_verify_error (unsigned int status)
+{
+    if (status & GNUTLS_CERT_REVOKED)
+        return "certificate was revoked";
+
+    else if (status & GNUTLS_CERT_INVALID) {
+        if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
+            return "certificate signer was not found";
+
+        else if (status & GNUTLS_CERT_SIGNER_NOT_CA)
+            return "certificate signer is not a Certificate Authority";
+
+        else if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
+            return "certificate signed using an insecure algorithm";
+
+        else
+            return "certificate could not be verified";
+
+    } else
+        return "unknown error";
+
+}
+
+/**
+ * Perform the certificate validation procedure on the peer cert.
+ *
+ * Based on the GnuTLS examples/ex-rfc2818.c
+ */
+static err_t ssl_client_verify (struct ssl_client *client, error_t *err)
+{
+    unsigned int status;
+    const gnutls_datum_t *cert_list;
+    unsigned int cert_list_size;
+    gnutls_x509_crt_t cert = NULL;
+    time_t t, now;
+
+    // init
+    RESET_ERROR(err);
+    now = time(NULL);
+    
+    // inspect the peer's cert chain using the installed trusted CAs
+    if ((ERROR_EXTRA(err) = gnutls_certificate_verify_peers2(client->session, &status)))
+        JUMP_SET_ERROR(err, ERR_GNUTLS_CERT_VERIFY_PEERS2);
+
+    // verify errors?
+    if (status)
+        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, ssl_client_verify_error(status));
+    
+    // import the main cert
+    assert(gnutls_certificate_type_get(client->session) == GNUTLS_CRT_X509);
+
+    if ((ERROR_EXTRA(err) = gnutls_x509_crt_init(&cert)))
+        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_init");
+
+    if ((cert_list = gnutls_certificate_get_peers(client->session, &cert_list_size)) == NULL)
+        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_certificate_get_peers");
+
+    if (!cert_list_size)
+        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "cert_list_size");
+
+    if ((ERROR_EXTRA(err) = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER)))
+        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_import");
+    
+    // check expire/activate... not sure if we need to do this
+    if ((t = gnutls_x509_crt_get_expiration_time(cert)) == ((time_t) -1) || t < now)
+        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_get_expiration_time");
+
+    if ((t = gnutls_x509_crt_get_activation_time(cert)) == ((time_t) -1) || t > now)
+        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_get_activation_time");
+    
+    // check hostname
+    if (!gnutls_x509_crt_check_hostname(cert, client->hostname))
+        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_check_hostname");
+
+error:
+    // cleanup
+    if (cert)
+        gnutls_x509_crt_deinit(cert);
+    
+    // should be SUCCESS
+    return ERROR_CODE(err);    
+}
+
+
+/**
+ * Our handshake driver. This will execute the next gnutls_handshake step, handling E_AGAIN.
+ *
+ * This updates the ssl_client::handshake state internally, as used by ssl_client_event_handler.
+ *
+ * If the client is marked as verify, this will perform the verification, returning on any errors, and then unset the
+ * verify flag - this ensures that the peer cert is only verified once per connection...
+ *
+ * @return >0 for finished handshake, 0 for handshake-in-progress, -err_t for errors.
+ */
+static int ssl_client_handshake (struct ssl_client *client, error_t *err)
+{
+    int ret;
+
+    // perform the handshake
+    if ((ret = gnutls_handshake(client->session)) < 0 && ret != GNUTLS_E_AGAIN)
+        JUMP_SET_ERROR_EXTRA(err, ERR_GNUTLS_HANDSHAKE, ret);
+    
+    // complete?
+    if (ret == 0) {
+        // update state
+        client->handshake = false;
+
+        // verify?
+        if (client->verify) {
+            // perform the validation
+            if (ssl_client_verify(client, err))
+                goto error;
+            
+            // unmark
+            client->verify = false;
+        }
+
+        // handshake done
+        return 1;
+
+    } else {
+        // set state, isn't really needed every time, but easier this way
+        client->handshake = true;
+
+        // re-enable the event for the next iteration
+        return ssl_client_ev_enable(client, err);
+    }
+
+error:
+    return -ERROR_CODE(err);    
+}
+
+/**
+ * Our transport_fd event handler. Drive the handshake if that's current, otherwise, invoke user callbacks.
+ */
+static void ssl_client_on_event (struct transport_fd *fd, short what, void *arg)
+{
+    struct ssl_client *client = arg;
+    error_t err;
+
+    (void) fd;
+
+    // XXX: timeouts
+    (void) what;
+
+    // are we in the handshake cycle?
+    if (client->handshake) {
+        RESET_ERROR(&err);
+
+        // perform the next handshake step
+        if (ssl_client_handshake(client, &err) == 0) {
+            // handshake continues
+        
+            // XXX: this state flag is completely wrong
+        } else if (SSL_CLIENT_TRANSPORT(client)->connected) {
+            // the async connect process has now completed, either succesfully or with an error
+            // invoke the user connect callback directly with appropriate error
+            transport_connected(SSL_CLIENT_TRANSPORT(client), ERROR_CODE(&err) ? &err : NULL, true);
+
+        } else {
+            if (ERROR_CODE(&err))
+                // the re-handshake failed, so this transport is dead
+                transport_error(SSL_CLIENT_TRANSPORT(client), &err);
+        
+            else
+                // re-handshake completed, so continue with the transport_callbacks
+                transport_invoke(SSL_CLIENT_TRANSPORT(client), what);
+        }
+
+    } else {
+        // normal transport operation
+        // gnutls might be able to proceed now, so invoke user callbacks
+        transport_invoke(SSL_CLIENT_TRANSPORT(client), what);
+    }
+}
+
+static err_t ssl_client__read (transport_t *transport, void *buf, size_t *len, error_t *err)
+{
+    struct ssl_client *client = transport_check(transport, &ssl_client_type);
+    int ret;
+    
+    // read gnutls record
+    do {
+        ret = gnutls_record_recv(client->session, buf, *len);
+
+    } while (ret == GNUTLS_E_INTERRUPTED);
+    
+    // errors
+    // XXX: E_REHANDSHAKE?
+    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_EOF);
+
+
+    // EAGAIN?
+    if (ret < 0) {
+        *len = 0;
+
+    } else {
+        // updated length
+        *len = ret;
+
+    }
+
+    return SUCCESS;
+}
+
+static err_t ssl_client__write (transport_t *transport, const void *buf, size_t *len, error_t *err)
+{
+    struct ssl_client *client = transport_check(transport, &ssl_client_type);
+    int ret;
+ 
+    // read gnutls record
+    do {
+        ret = gnutls_record_send(client->session, buf, *len);
+   
+    } while (ret == GNUTLS_E_INTERRUPTED);
+
+    // errors
+    if (ret < 0 && ret != GNUTLS_E_AGAIN)
+        RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_SEND, ret);
+    
+    else if (ret == 0)
+        return SET_ERROR(err, ERR_WRITE_EOF);
+
+
+    // eagain?
+    if (ret < 0) {
+        *len = 0;
+
+    } else {
+        // updated length
+        *len = ret;
+    }
+
+    return SUCCESS;
+}
+
+void ssl_client_deinit (struct ssl_client *client)
+{
+    // close the session rudely
+    gnutls_deinit(client->session);
+    client->session = NULL;
+ 
+    // terminate the TCP transport
+    tcp_client_deinit(&client->base_tcp);
+   
+    if (client->cred) {
+        // drop the cred ref
+        ssl_client_cred_put(client->cred);
+
+        client->cred = NULL;
+    }
+
+    // free
+    free(client->hostname);
+    client->hostname = NULL;
+}
+
+
+static void ssl_client__deinit (transport_t *transport)
+{
+    struct ssl_client *client = transport_check(transport, &ssl_client_type);
+    
+    // die
+    ssl_client_deinit(client);
+}
+
+/**
+ * Our tcp_client-invoked connect handler
+ */
+static void ssl_client__connected (transport_t *transport, const error_t *tcp_err)
+{
+    struct ssl_client *client = transport_check(transport, &ssl_client_type);
+    error_t err;
+
+    // trap errors to let the user handle them directly
+    if (tcp_err)
+        JUMP_SET_ERROR_INFO(&err, tcp_err);
+    
+    // bind default transport functions (recv/send) to use the TCP fd
+    gnutls_transport_set_ptr(client->session, (gnutls_transport_ptr_t) (long int) SSL_CLIENT_FD(client)->fd);
+
+    // add ourselves as the event handler
+    if ((ERROR_CODE(&err) = transport_fd_setup(SSL_CLIENT_FD(client), ssl_client_on_event, client)))
+        goto error;
+
+    // start handshake
+    if (ssl_client_handshake(client, &err))
+        // this should complete with SUCCESS if it returns >0
+        goto error;
+
+    // ok, so we wait...
+    return;
+
+error:
+    // tell the user
+    transport_connected(transport, &err, true);
+}
+
+struct transport_type ssl_client_type = {
+    .base_type = {
+        .parent     = &tcp_client_type.base_type,
+    },
+    .methods = {
+        .read       = ssl_client__read,
+        .write      = ssl_client__write,
+        .deinit     = ssl_client__deinit,
+        ._connected = ssl_client__connected,
+    },
+};
+
+
+
+static void ssl_client_destroy (struct ssl_client *client)
+{
+    ssl_client_deinit(client);
+
+    free(client);
+}
+
+err_t ssl_connect (const struct transport_info *info, transport_t **transport_ptr, 
+        const char *hostname, const char *service,
+        struct ssl_client_cred *cred,
+        error_t *err
+    )
+{
+    struct ssl_client *client = NULL;
+
+    // alloc
+    if ((client = calloc(1, sizeof(*client))) == NULL)
+        return SET_ERROR(err, ERR_CALLOC);
+
+    // initialize base
+    transport_init(SSL_CLIENT_TRANSPORT(client), &ssl_client_type, info);
+
+    if (!cred) {
+        // default credentials
+        cred = &ssl_client_cred_anon;
+    
+    } else {
+        // take a ref
+        client->cred = cred;
+        cred->refcount++;
+    };
+
+    // do verify?
+    if (cred->verify)
+        client->verify = true;
+
+    // init
+    if ((client->hostname = strdup(hostname)) == NULL)
+        JUMP_SET_ERROR(err, ERR_STRDUP);
+
+    // initialize TCP
+    tcp_client_init(&client->base_tcp);
+
+    // initialize client session
+    if ((ERROR_EXTRA(err) = gnutls_init(&client->session, GNUTLS_CLIENT)) < 0)
+        JUMP_SET_ERROR(err, ERR_GNUTLS_INIT);
+
+    // ...default priority stuff
+    if ((ERROR_EXTRA(err) = gnutls_set_default_priority(client->session)))
+        JUMP_SET_ERROR(err, ERR_GNUTLS_SET_DEFAULT_PRIORITY);
+
+    // XXX: silly hack for OpenSSL interop
+    gnutls_dh_set_prime_bits(client->session, 512);
+
+    // bind credentials
+    if ((ERROR_EXTRA(err) = gnutls_credentials_set(client->session, GNUTLS_CRD_CERTIFICATE, cred->x509)))
+        JUMP_SET_ERROR(err, ERR_GNUTLS_CRED_SET);
+
+    // TCP connect
+    if (tcp_client_connect_async(&client->base_tcp, hostname, service, err))
+        goto error;
+
+    // done, wait for the connect to complete
+    *transport_ptr = SSL_CLIENT_TRANSPORT(client);
+
+    return SUCCESS;
+
+error:
+    // cleanup
+    ssl_client_destroy(client);
+
+    return ERROR_CODE(err);    
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ssl_internal.h	Thu May 07 02:46:44 2009 +0300
@@ -0,0 +1,87 @@
+#ifndef SSL_INTERNAL_H
+#define SSL_INTERNAL_H
+
+/**
+ * @file
+ *
+ * A sock_stream implementation using GnuTLS for SSL
+ */
+#include "ssl.h"
+#include "tcp_internal.h"
+
+#include <gnutls/gnutls.h>
+
+/**
+ * GnuTLS library error codes
+ */
+enum ssl_error_code {
+    _ERR_SSL_BEGIN = _ERR_GNUTLS,
+    
+    ERR_GNUTLS_CERT_ALLOC_CRED,
+    ERR_GNUTLS_GLOBAL_INIT,
+    ERR_GNUTLS_INIT,
+    ERR_GNUTLS_SET_DEFAULT_PRIORITY,
+    ERR_GNUTLS_CRED_SET,
+    ERR_GNUTLS_HANDSHAKE,
+    ERR_GNUTLS_RECORD_SEND,
+    ERR_GNUTLS_RECORD_RECV,
+    ERR_GNUTLS_RECORD_GET_DIRECTION,   
+    ERR_GNUTLS_CERT_VERIFY_PEERS2,
+    ERR_GNUTLS_CERT_VERIFY,
+    ERR_GNUTLS_CERT_SET_X509_TRUST_FILE,
+    ERR_GNUTLS_CERT_SET_X509_KEY_FILE,
+};
+
+/**
+ * GnuTLS credentials for client sockets.
+ */
+struct ssl_client_cred {
+    /** Our client certificate */
+    gnutls_certificate_credentials_t x509;
+
+    /** Should we verify? */
+    bool verify;
+
+    /** Refcount from ssl_client */
+    int refcount;
+};
+
+/**
+ * Global anonymous x509 credentials
+ */
+extern struct ssl_client_cred ssl_client_cred_anon;
+
+/*
+ * Our transport_type
+ */
+extern struct transport_type ssl_client_type;
+
+/**
+ * An SSL-encrypted TCP connection, using libgnutls
+ */
+struct ssl_client {
+    /** The underlying TCP connection */
+    struct tcp_client base_tcp;
+
+    /** The hostname we connected to, for verification */
+    char *hostname;
+
+    /** The credentials we are using, unless anon */
+    struct ssl_client_cred *cred;
+    
+    /** The GnuTLS session for this connection */
+    gnutls_session_t session;
+
+    /** Should we verify the peer cert? */
+    bool verify;
+
+    /** Are we running a handshake? */
+    bool handshake;
+};
+
+/**
+ * Initialize the global gnutls state
+ */
+err_t ssl_global_init (error_t *err);
+
+#endif /* SSL_INTERNAL_H */