#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
*
* XXX: errors
*/
void sock_gnutls_client_ctx_anon (struct sock_gnutls_client_ctx *ctx)
{
gnutls_certificate_allocate_credentials(&ctx->xcred);
}
err_t sock_gnutls_init (void)
{
int _err;
// global init
if ((_err = gnutls_global_init()) < 0)
errx(1, "gnutls_global_init: %s", gnutls_strerror(_err));
// init _sock_gnutls_ctx
sock_gnutls_client_ctx_anon(&_sock_gnutls_client_ctx);
// 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);
}