Current section

Files

Jump to
libsodium c_src libsodium_request.c
Raw

c_src/libsodium_request.c

// -*- mode: c; tab-width: 8; indent-tabs-mode: 1; st-rulers: [70] -*-
// vim: ts=8 sw=8 ft=c noet
#include "libsodium_request.h"
#include "libsodium_api.h"
static void libsodium_request_badarg(libsodium_request_t *request);
static void libsodium_async_call(libsodium_request_t **request, char *buf, ErlDrvSizeT len);
static void libsodium_async_call_invoke(void *async_data);
static void libsodium_async_call_free(void *async_data);
libsodium_request_t *
libsodium_request_alloc(libsodium_port_t *port, ErlDrvTermData caller, unsigned int command)
{
ErlDrvSizeT x;
void *p;
libsodium_request_t *request;
x = (ErlDrvSizeT)((sizeof (libsodium_request_t)));
p = driver_alloc(x);
if (p == NULL) {
return NULL;
}
request = (libsodium_request_t *)(p);
request->port = port;
request->caller = caller;
request->command = NULL;
request->tag.buff = NULL;
request->tag.buffsz = 0;
request->tag.index = 0;
request->argc = -1;
request->argv = NULL;
request->execute = NULL;
request->error = 0;
request->reply.buff = NULL;
request->reply.buffsz = 0;
request->reply.index = 0;
switch (command) {
case LIBSODIUM_ASYNC_CALL:
request->command = libsodium_async_call;
break;
default:
(void) libsodium_request_badarg(request);
break;
}
return request;
}
void
libsodium_request_free(libsodium_request_t *request)
{
if (request == NULL) {
return;
}
if (request->tag.buff != NULL) {
(void) driver_free(request->tag.buff);
request->tag.buff = NULL;
request->tag.buffsz = 0;
request->tag.index = 0;
}
if (request->argv != NULL) {
(void) driver_free(request->argv);
request->argv = NULL;
}
if (request->reply.buff != NULL) {
(void) driver_free(request->reply.buff);
request->reply.buff = NULL;
request->reply.buffsz = 0;
request->reply.index = 0;
}
(void) driver_free(request);
request = NULL;
}
static void
libsodium_request_badarg(libsodium_request_t *request)
{
request->error = (int)(ERL_DRV_ERROR_BADARG);
}
static void
libsodium_async_call(libsodium_request_t **request, char *buf, ErlDrvSizeT len)
{
libsodium_request_t *req;
libsodium_request_t *sync_req;
char *buffer;
int version;
int version_beg;
int version_end;
int index;
int tag_beg;
int tag_end;
int type;
int type_length;
int arity;
libsodium_function_t *function;
void *async_data;
(void) len; // unused
req = *request;
buffer = buf;
index = 0;
version_beg = index;
if (ei_decode_version(buffer, &index, &version) < 0) {
(void) libsodium_request_badarg(req);
return;
}
version_end = index;
if (ei_decode_tuple_header(buffer, &index, &arity) < 0 || arity != 4) {
(void) libsodium_request_badarg(req);
return;
}
if (ei_get_type(buffer, &index, &type, &type_length) < 0
|| (type != ERL_REFERENCE_EXT
&& type != ERL_NEW_REFERENCE_EXT)) {
(void) libsodium_request_badarg(req);
return;
}
tag_beg = index;
if (ei_skip_term(buffer, &index) < 0) {
(void) libsodium_request_badarg(req);
return;
}
tag_end = index;
req->tag.buffsz = (version_end - version_beg) + (tag_end - tag_beg);
req->tag.index = req->tag.buffsz;
req->tag.buff = (char *)(driver_alloc(req->tag.buffsz));
if (req->tag.buff == NULL) {
(void) libsodium_request_badarg(req);
LS_FAIL_OOM(req->port->drv_port);
return;
}
(void) memcpy((req->tag.buff) + version_beg, buf + version_beg, (version_end - version_beg));
(void) memcpy((req->tag.buff) + (version_end - version_beg), buf + tag_beg, (tag_end - tag_beg));
if (ei_get_type(buffer, &index, &type, &type_length) < 0 || type != ERL_ATOM_EXT) {
(void) libsodium_request_badarg(req);
return;
}
if (ei_decode_ei_term(buffer, &index, &(req->namespace)) < 0) {
(void) libsodium_request_badarg(req);
return;
}
if (ei_get_type(buffer, &index, &type, &type_length) < 0 || type != ERL_ATOM_EXT) {
(void) libsodium_request_badarg(req);
return;
}
if (ei_decode_ei_term(buffer, &index, &(req->function)) < 0) {
(void) libsodium_request_badarg(req);
return;
}
if (ei_decode_tuple_header(buffer, &index, &(req->argc)) < 0) {
(void) libsodium_request_badarg(req);
return;
}
function = get_libsodium_api(req->namespace.value.atom_name, req->function.value.atom_name);
if (function == NULL || function->arity != req->argc) {
(void) libsodium_request_badarg(req);
return;
}
if (function->init != NULL && function->init(req, buffer, &index) < 0) {
(void) libsodium_request_badarg(req);
return;
}
req->execute = function->exec;
sync_req = libsodium_request_alloc(req->port, req->caller, LIBSODIUM_ASYNC_CALL);
if (sync_req == NULL) {
(void) libsodium_request_badarg(req);
LS_FAIL_OOM(req->port->drv_port);
return;
}
sync_req->tag.buffsz = req->tag.buffsz;
sync_req->tag.index = req->tag.index;
sync_req->tag.buff = (char *)(driver_alloc(sync_req->tag.buffsz));
if (sync_req->tag.buff == NULL) {
(void) libsodium_request_free(sync_req);
(void) libsodium_request_badarg(req);
LS_FAIL_OOM(req->port->drv_port);
return;
}
(void) memcpy(sync_req->tag.buff, req->tag.buff, sync_req->tag.buffsz);
sync_req->reply.buffsz = sync_req->tag.buffsz;
sync_req->reply.index = sync_req->tag.index;
sync_req->reply.buff = (char *)(driver_alloc(sync_req->reply.buffsz));
if (sync_req->reply.buff == NULL) {
(void) libsodium_request_free(sync_req);
(void) libsodium_request_badarg(req);
LS_FAIL_OOM(req->port->drv_port);
return;
}
(void) memcpy(sync_req->reply.buff, sync_req->tag.buff, sync_req->reply.buffsz);
sync_req->namespace = req->namespace;
sync_req->function = req->function;
sync_req->argc = req->argc;
sync_req->argv = NULL;
sync_req->execute = req->execute;
sync_req->error = req->error;
async_data = (void *)(req);
*request = sync_req;
if (driver_async(req->port->drv_port, NULL, libsodium_async_call_invoke, async_data, libsodium_async_call_free) < 0) {
(void) libsodium_request_badarg(sync_req);
return;
}
}
static void
libsodium_async_call_invoke(void *async_data)
{
libsodium_request_t *request;
request = (libsodium_request_t *)(async_data);
if (request == NULL) {
return;
}
// TRACE_F("(invoke) asynchronous call: %s\n", request->function.value.atom_name);
(void) (request->execute)(request);
}
static void
libsodium_async_call_free(void *async_data)
{
libsodium_request_t *request;
request = (libsodium_request_t *)(async_data);
if (request == NULL) {
return;
}
// TRACE_F("(free) asynchronous call: %s\n", request->function.value.atom_name);
(void) libsodium_request_free(request);
}