You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libsxmp/lib/link.c

347 lines
8.6 KiB
C

/*
* Secure X Message Passing Library v2 implementation.
* (sxmplv2) it superseed all versions before due to the:
* - memory consumption
* - new features such as pulse emitting
* - performance optimization
*
* This is a proprietary software. See COPYING for further details.
*
* (c) Askele Group 2013-2015 <http://askele.com>
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <fcntl.h>
#ifdef WIN32
#include <Winsock2.h>
#define EBADE 1
#define NETDB_SUCCESS 0
#else
#include <sys/select.h>
#include <netdb.h>
#include <unistd.h>
#include <uuid/uuid.h>
#endif
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <tdata/usrtc.h>
#include <sexpr/sexp.h>
#include <sntl/sntllv2.h>
static int __insert_rpc_function(usrtc_t *tree, const char *name, int (*rpcf)(void *, sexp_t *))
{
cx_rpc_t *ent = malloc(sizeof(cx_rpc_t));
usrtc_node_t *node;
if(!ent) return ENOMEM;
else node = &ent->node;
if(!(ent->name = strdup(name))) {
free(ent);
return ENOMEM;
} else ent->rpcf = rpcf;
usrtc_node_init(node, ent);
usrtc_insert(tree, node, ent->name);
return 0;
}
static void __destroy_rpc_list_tree(usrtc_t *tree)
{
usrtc_node_t *node;
cx_rpc_t *ent;
for(node = usrtc_first(tree); node != NULL; node = usrtc_first(tree)) {
ent = (cx_rpc_t *)usrtc_node_getdata(node);
usrtc_delete(tree, node);
free(ent->name);
free(ent);
}
return;
}
/* negotiation functions */
/**
* Proto: (auth-set-credentials "<login>" "<password>")
* Return - message return; opcode as a return of this function.
* in batch mode message with 0 index used always
*/
static int __set_credentials(void *cctx, sexp_t *sx)
{
register int idx;
conn_t *co = (conn_t *)cctx;
conn_sys_t *ssys = co->ssys;
sexp_t *isx;
char *login = NULL;
char *passwd = NULL;
/* take a deal with S-exp */
SEXP_ITERATE_LIST(sx, isx, idx) {
if(isx->ty == SEXP_LIST) return SNE_BADPROTO;
if(idx > 0 && isx->aty != SEXP_DQUOTE) return SNE_BADPROTO;
if(idx == 1) login = isx->val;
else if(idx == 2) passwd = isx->val;
else if(idx > 2) return SNE_BADPROTO;
}
if(!login || !passwd) return SNE_BADPROTO;
co->pctx->login = strdup(login);
co->pctx->passwd = strdup(passwd);
if(!co->pctx->login || !co->pctx->passwd) {
if(co->pctx->login) free(co->pctx->login);
if(co->pctx->passwd) free(co->pctx->passwd);
return SNE_ENOMEM;
}
if(ssys->secure_check) return ssys->secure_check(co);
else return SNE_SUCCESS;
}
static int __get_channels_list(void *cctx, sexp_t *sx)
{
conn_t *co = (conn_t *)cctx;
conn_sys_t *ssys = co->ssys;
sxmsg_t *msg = co->messages[0];
char *buf = msg->payload;
usrtc_node_t *node;
rpc_typed_list_t *list_ent;
size_t maxlen = 65535 - sizeof(sntllv2_head_t);
size_t ulen = 0;
/* we will avoid S-exp scanning here */
/* call the function */
if(ssys->get_rpc_typed_list_tree)
co->rpc_list = ssys->get_rpc_typed_list_tree(co);
if(!co->rpc_list) return SNE_EPERM;
//buf += sizeof(sntllv2_head_t);
ulen += snprintf(buf + ulen, maxlen - ulen, "(set-channels-list ");
for(node = usrtc_first(co->rpc_list); node != NULL;
node = usrtc_next(co->rpc_list, node)) { /* fill the list */
list_ent = (rpc_typed_list_t *)usrtc_node_getdata(node);
ulen += snprintf(buf + ulen, maxlen - ulen, "(:%d \"%s\")",
list_ent->type_id, list_ent->description);
}
ulen += snprintf(buf + ulen, maxlen - ulen, ")");
msg->mhead.payload_length = ulen + 1;
/* we're ready for messaging mode */
co->flags |= SNSX_MESSAGINGMODE;
return SNE_SUCCESS;
}
static int __set_channels_list(void *cctx, sexp_t *sx)
{
register int idx;
conn_t *co = (conn_t *)cctx;
conn_sys_t *ssys = co->ssys;
sexp_t *isx, *iisx;
int id, r;
SEXP_ITERATE_LIST(sx, isx, idx) {
if(!idx) continue;
if(isx->ty != SEXP_LIST) return SNE_BADPROTO;
if(sexp_list_length(isx) != 2) return SNE_BADPROTO;
/* get id */
sexp_list_car(isx, &iisx);
if(iisx->ty == SEXP_LIST) return SNE_BADPROTO;
if(iisx->aty != SEXP_BASIC) return SNE_BADPROTO;
if(iisx->val[0] != ':') return SNE_BADPROTO;
id = atoi(iisx->val + sizeof(char));
/* get short description */
sexp_list_cdr(isx, &iisx);
if(iisx->ty == SEXP_LIST) return SNE_BADPROTO;
if(iisx->aty != SEXP_DQUOTE) return SNE_BADPROTO;
/* ok, here we go */
if(ssys->set_typed_list_callback) {
r = ssys->set_typed_list_callback(co, id, iisx->val);
if(r != SNE_SUCCESS) return r;
}
}
/* we're ready for messaging mode */
co->flags |= SNSX_MESSAGINGMODE;
co->flags &= ~SNSX_BATCHMODE;
return SNE_SUCCESS;
}
static int __init_systemrpc_tree(usrtc_t *rtree)
{
/* batch mode negotiation context functions */
if(__insert_rpc_function(rtree, "auth-set-credentials", __set_credentials)) goto __fail;
if(__insert_rpc_function(rtree, "get-channels-list", __get_channels_list)) goto __fail;
if(__insert_rpc_function(rtree, "set-channels-list", __set_channels_list)) goto __fail;
return 0;
__fail:
__destroy_rpc_list_tree(rtree);
return ENOMEM;
}
static long __cmp_cstr(const void *a, const void *b)
{
return (long)strcmp((const char *)a, (const char *)b);
}
int connections_init(conn_sys_t *ssys)
{
int r = 0;
if(!ssys) return EINVAL;
else memset(ssys, 0, sizeof(conn_sys_t));
if(!(ssys->connections = malloc(sizeof(usrtc_t)))) return ENOMEM;
/* init connections list */
usrtc_init(ssys->connections, USRTC_REDBLACK, MAX_CONNECTIONS,
__cmp_cstr);
if((r = pthread_rwlock_init(&(ssys->rwlock), NULL)))
goto __fini;
/* init RPC list related functions */
if(!(ssys->system_rpc = malloc(sizeof(cx_rpc_list_t)))) {
r = ENOMEM;
goto __lfini;
} else {
if(!(ssys->system_rpc->rpc_tree = malloc(sizeof(usrtc_t)))) {
r = ENOMEM;
goto __lfini;
}
usrtc_init(ssys->system_rpc->rpc_tree, USRTC_SPLAY, 256, __cmp_cstr);
r = __init_systemrpc_tree(ssys->system_rpc->rpc_tree);
if(r) {
free(ssys->system_rpc->rpc_tree);
goto __lfini;
}
}
return 0;
__lfini:
if(ssys->system_rpc) free(ssys->system_rpc);
pthread_rwlock_destroy(&(ssys->rwlock));
__fini:
if(ssys->connections) free(ssys->connections);
return r;
}
int connections_free(conn_sys_t *ssys)
{
__destroy_rpc_list_tree(ssys->system_rpc->rpc_tree);
free(ssys->system_rpc->rpc_tree);
free(ssys->system_rpc);
free(ssys->connections);
pthread_rwlock_destroy(&(ssys->rwlock));
SSL_CTX_free(ssys->ctx);
return 0;
}
int connections_destroy(conn_sys_t *ssys)
{
int r = connections_free(ssys);
free(ssys);
return r;
}
conn_sys_t *connections_create(void)
{
int r = 0;
conn_sys_t *nsys = malloc(sizeof(conn_sys_t));
if(!nsys) {
errno = ENOMEM;
return NULL;
}
r = connections_init(nsys);
if(r) {
errno = r;
free(nsys);
return NULL;
}
return nsys;
}
int connections_setsslserts(conn_sys_t *ssys, const char *rootca,
const char *certpem, const char *certkey)
{
int r = ENOMEM;
if(!ssys) return EINVAL;
/* simply copying */
if(!(ssys->rootca = strdup(rootca))) return ENOMEM;
if(!(ssys->certkey = strdup(certkey))) goto __fail;
if(!(ssys->certpem = strdup(certpem))) goto __fail;
r = 0;
return 0;
__fail:
if(ssys->rootca) free(ssys->rootca);
if(ssys->certkey) free(ssys->certkey);
if(ssys->certpem) free(ssys->certpem);
return r;
}
struct __scerrcode {
int code;
const char *desc;
};
static struct __scerrcode __lerr[] = {
{SNE_SUCCESS, "Success"},
{SNE_FAILED, "Failed, invalid parameters given"},
{SNE_ENOMEM, "Not enough memory"},
{SNE_BADPROTO, "Bad protocol"},
{SNE_ENORPC, "No such RPC exists"},
{SNE_EPERM, "Permission denied"},
{SNE_TOOLONG, "Message data payload too long to be sent with one message pass"},
{SNE_EBUSY, "Index or working threads are busy"},
{SNE_WOULDBLOCK, "Call will block operation"},
{SNE_LINKERROR, "Connection link error"},
{SNE_NOSUCHMSG, "No such message"},
{SNE_NOSUCHCHAN, "No such channel"},
{SNE_ETIMEDOUT, "Timeout exceed"},
{SNE_IGNORED, "Function call was ignored"},
{SNE_REPLYREQ, "Reply required to the message"},
{SNE_RAPIDMSG, "Message is a rapid reply and dialog closed"},
{SNE_ESSL, "SSL error occurs on connection link"},
{SNE_NOCHANNELS, "No channels available"},
{SNE_MCHANNELS, "Active channels limit exceed"},
{SNE_MMESSAGES, "Active messages limit exceed"},
{SNE_LINKBROKEN, "Connection link was broken"},
{SNE_INVALINDEX, "Invalid index given"},
};
const char *sntll_errno2cstr(int ec)
{
return __lerr[ec - __SNTL_EPREFIX].desc;
}