src/sock_gnutls.c
author Tero Marttila <terom@fixme.fi>
Sun, 22 Feb 2009 06:52:55 +0200
changeset 4 a3ca0f97a075
parent 3 cc94ae754e2a
child 5 a09a0797f6f0
permissions -rw-r--r--
change ERROR_* to use pointers again, and implement error_info for sock_init

#include "sock_gnutls.h"

#include <stdlib.h>
#include <err.h>

static void _sock_gnutls_error (struct sock_gnutls *sock, const char *func, int _err) {
    if (_err == GNUTLS_E_FATAL_ALERT_RECEIVED)
        errx(1, "%s: %s: %s", func, gnutls_strerror(_err), gnutls_alert_get_name(gnutls_alert_get(sock->session)));

    else
        errx(1, "%s: %s", func, gnutls_strerror(_err));
}

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);
    
    // just map to gnutls_record_recv
    return gnutls_record_recv(sock->session, buf, len);
}

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);
    
    // just map to gnutls_record_send
    return gnutls_record_send(sock->session, buf, len);
}

/*
 * Our sock_stream_Type
 */
struct sock_stream_type sock_gnutls_type = {
    .methods.read   = &sock_gnutls_read,
    .methods.write  = &sock_gnutls_write,
};

/*
 * XXX: global shared sock_gnutls_ctx
 */
struct sock_gnutls_client_ctx _sock_gnutls_client_ctx;

/*
 * Configure the given gnutls socket context to use simple anonymous client credentials
 */
static err_t sock_gnutls_client_ctx_anon (struct sock_gnutls_client_ctx *ctx, struct error_info *err)
{
    // init to use anonymous x509 cert
    if ((ERROR_EXTRA(err) = gnutls_certificate_allocate_credentials(&ctx->xcred)) < 0)
        return SET_ERROR(err, ERR_GNUTLS_CERT_ALLOC_CRED);

    // done
    return SUCCESS;
}

err_t sock_gnutls_init (struct error_info *err)
{
    // global init
    if ((ERROR_EXTRA(err) = gnutls_global_init()) < 0)
        return SET_ERROR(err, ERR_GNUTLS_GLOBAL_INIT);

    // init _sock_gnutls_ctx
    if (sock_gnutls_client_ctx_anon(&_sock_gnutls_client_ctx, err))
        return ERROR_CODE(err);

    // done
    return SUCCESS;
}


// XXX: errors
struct sock_stream *sock_ssl_connect (const char *host, const char *service)
{
    int _err;
    struct sock_gnutls *sock;
    struct sock_gnutls_client_ctx *ctx = &_sock_gnutls_client_ctx;

    // alloc
    if ((sock = calloc(1, sizeof(*sock))) == NULL)
        errx(1, "calloc");

    // initialize
    sock->base_tcp.base.type = &sock_gnutls_type;

    // initialize client session
    if ((_err = gnutls_init(&sock->session, GNUTLS_CLIENT)) < 0)
        errx(1, "gnutls_init: %s", gnutls_strerror(_err));

    // ...default priority stuff
    gnutls_set_default_priority(sock->session);

    // bind anon credentials
    gnutls_credentials_set(sock->session, GNUTLS_CRD_CERTIFICATE, ctx->xcred);

    // TCP connect
    sock_tcp_init_connect(SOCK_GNUTLS_TCP(sock), host, service);

    // bind default transport functions (recv/send) to use the TCP fd
    gnutls_transport_set_ptr(sock->session, (gnutls_transport_ptr_t) sock->base_tcp.fd);

    // perform the handshake
    if ((_err = gnutls_handshake(sock->session)) < 0)
        _sock_gnutls_error(sock, "gnutls_handshake", _err);

    // done
    return SOCK_GNUTLS_BASE(sock);
}