#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_global_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;
}
err_t sock_gnutls_connect (struct sock_stream **sock_ptr, const char *host, const char *service, struct error_info *err)
{
struct sock_gnutls *sock = NULL;
struct sock_gnutls_client_ctx *ctx = &_sock_gnutls_client_ctx;
// alloc
if ((sock = calloc(1, sizeof(*sock))) == NULL)
return SET_ERROR(err, ERR_CALLOC);
// initialize base
sock_stream_init(SOCK_GNUTLS_BASE(sock), &sock_gnutls_type);
// 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);
// bind anon credentials
if ((ERROR_EXTRA(err) = gnutls_credentials_set(sock->session, GNUTLS_CRD_CERTIFICATE, ctx->xcred)))
JUMP_SET_ERROR(err, ERR_GNUTLS_CRED_SET);
// TCP connect
if (sock_tcp_init_connect(SOCK_GNUTLS_TCP(sock), host, service))
JUMP_SET_ERROR_INFO(err, SOCK_GNUTLS_ERR(sock));
// 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 ((ERROR_EXTRA(err) = gnutls_handshake(sock->session)) < 0)
JUMP_SET_ERROR(err, ERR_GNUTLS_HANDSHAKE);
// done
*sock_ptr = SOCK_GNUTLS_BASE(sock);
return SUCCESS;
error:
// XXX: cleanup
return ERROR_CODE(err);
}