|
|
|
/*
|
|
|
|
* Secure X Message Passing Library v2 examples.
|
|
|
|
*
|
|
|
|
* (c) Alexander Vdolainen 2016 <avdolainen@zoho.com>
|
|
|
|
*
|
|
|
|
* libsxmp is free software: you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU Lesser General Public License as published
|
|
|
|
* by the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* libsxmp is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
* See the GNU Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.";
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is an example of sxmp usage.
|
|
|
|
* NOTE: It was developed quite fast within one day,
|
|
|
|
* btw - this the reason of some ugly code here.
|
|
|
|
*
|
|
|
|
* This is a master or simply - daemon part, it will
|
|
|
|
* listen a requests and create a stream with directory entries,
|
|
|
|
* which ridden by the client.
|
|
|
|
* This implements a simple client-server topology, to see
|
|
|
|
* more advanced technics check out other examples.
|
|
|
|
*
|
|
|
|
* NOTE(win32): don't have a time to test it or fix it to
|
|
|
|
* make it works on windows, if you can - u're welcome.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#define __USE_GNU
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <sys/select.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/resource.h>
|
|
|
|
#include <netdb.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <uuid/uuid.h>
|
|
|
|
#include <execinfo.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
|
|
|
|
#include <tdata/usrtc.h>
|
|
|
|
#include <sexpr/sexp.h>
|
|
|
|
#include <sxmp/limits.h>
|
|
|
|
#include <sxmp/sxmp.h>
|
|
|
|
|
|
|
|
#include <pthread.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <getopt.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#include "filelist.h"
|
|
|
|
#include "helpers.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* type used to stream a directory contents
|
|
|
|
* in case of lot of files we cannot send it
|
|
|
|
* within one message.
|
|
|
|
*/
|
|
|
|
typedef struct __datastream_type {
|
|
|
|
int dsid;
|
|
|
|
usrtc_node_t node;
|
|
|
|
DIR *dp;
|
|
|
|
struct dirent dent;
|
|
|
|
struct dirent *dres;
|
|
|
|
} datastream_t;
|
|
|
|
|
|
|
|
/* ugly, but no so important here */
|
|
|
|
static usrtc_t *_rd_streams;
|
|
|
|
static int _rd_last_id = 0;
|
|
|
|
static pthread_rwlock_t _lock;
|
|
|
|
|
|
|
|
/* some helper functions, not any serios stuff */
|
|
|
|
static long __cmp_int(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
return *(int *)a - *(int *)b;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void dump_dirent(struct dirent *d, char *buf)
|
|
|
|
{
|
|
|
|
if (d == NULL) return;
|
|
|
|
strcat(buf, "\"");
|
|
|
|
strcat(buf, d->d_name);
|
|
|
|
strcat(buf, "\" ");
|
|
|
|
|
|
|
|
switch(d->d_type) {
|
|
|
|
case DT_REG: strcat(buf, "\"regular\""); break;
|
|
|
|
case DT_DIR: strcat(buf, "\"directory\""); break;
|
|
|
|
case DT_BLK: strcat(buf, "\"block\""); break;
|
|
|
|
case DT_CHR: strcat(buf, "\"char\""); break;
|
|
|
|
case DT_FIFO: strcat(buf, "\"fifo\""); break;
|
|
|
|
case DT_LNK: strcat(buf, "\"link\""); break;
|
|
|
|
case DT_UNKNOWN: strcat(buf, "\"unknown\""); break;
|
|
|
|
default: strcat(buf, "\"another\""); break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* firstly we need to take an rpc calls data,
|
|
|
|
* sxmp uses usrtc from libtdata to store it
|
|
|
|
*/
|
|
|
|
static usrtc_t *fulist;
|
|
|
|
|
|
|
|
/* ok, here we will define out sxmp specific functions */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* RPC list contain groups of RPC calls called channel types
|
|
|
|
* for each sxmp link depends on access rules you can change
|
|
|
|
* available channel types.
|
|
|
|
* To do so you must provide a special callback returning this list.
|
|
|
|
* Since we haven't any access restrictions in our example, just
|
|
|
|
* create a fake.
|
|
|
|
*/
|
|
|
|
static usrtc_t *__rettlist(sxlink_t *l)
|
|
|
|
{
|
|
|
|
return fulist;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Each sxmp link validated by certificate ID,
|
|
|
|
* i.e. when basic checking is done, you can also
|
|
|
|
* review the certificate ID, for example you need
|
|
|
|
* to disable certificate for a time being.
|
|
|
|
*
|
|
|
|
* Here we don't have those checks, just always accept
|
|
|
|
*/
|
|
|
|
static int __validate_sslpem(sxlink_t *l)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* To be more paranoic, sxmp also allow to
|
|
|
|
* check up username and password.
|
|
|
|
* We don't overview this tuff here, just
|
|
|
|
* always allow.
|
|
|
|
*/
|
|
|
|
static int __secure_check(sxlink_t *l)
|
|
|
|
{
|
|
|
|
return SXE_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Our RPC functions it used to get a message and reply */
|
|
|
|
static int __dir_open(void *m, sexp_t *sx)
|
|
|
|
{
|
|
|
|
sexp_t *lsx = NULL;
|
|
|
|
sxmsg_t *msg = (sxmsg_t *)m;
|
|
|
|
char *buf;
|
|
|
|
size_t ln = 0;
|
|
|
|
|
|
|
|
if(sexp_list_cdr(sx, &lsx) || !sx->list->next || !sx->list->next->val) {
|
|
|
|
fprintf(stderr, "Invalid protocol\n");
|
|
|
|
return sxmsg_return(msg, SXE_BADPROTO);
|
|
|
|
}
|
|
|
|
|
|
|
|
char *dir_name = strdup(sx->list->next->val);
|
|
|
|
|
|
|
|
if ( usrtc_isfull(_rd_streams) ) {
|
|
|
|
return sxmsg_return(msg, SXE_TOOLONG);
|
|
|
|
}
|
|
|
|
|
|
|
|
datastream_t *item;
|
|
|
|
if ( !(item = malloc(sizeof(datastream_t))) ) {
|
|
|
|
return sxmsg_return(msg, SXE_ENOMEM);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* find free id */
|
|
|
|
pthread_rwlock_wrlock(&_lock);
|
|
|
|
do {
|
|
|
|
if (_rd_last_id == INT_MAX)
|
|
|
|
_rd_last_id = 1;
|
|
|
|
else
|
|
|
|
++_rd_last_id;
|
|
|
|
} while (usrtc_lookup(_rd_streams, &_rd_last_id));
|
|
|
|
|
|
|
|
/* create rd stream */
|
|
|
|
DIR *dp = NULL;
|
|
|
|
if ( !(dp = opendir(dir_name)) ) {
|
|
|
|
pthread_rwlock_unlock(&_lock);
|
|
|
|
FREE(item);
|
|
|
|
FREE(dir_name);
|
|
|
|
return sxmsg_return(msg, SXE_FAILED);
|
|
|
|
}
|
|
|
|
|
|
|
|
FREE(dir_name);
|
|
|
|
|
|
|
|
/* init stream */
|
|
|
|
item->dsid = _rd_last_id;
|
|
|
|
item->dp = dp;
|
|
|
|
item->dres = NULL;
|
|
|
|
usrtc_node_init(&(item->node), item);
|
|
|
|
usrtc_insert(_rd_streams, &(item->node), &item->dsid);
|
|
|
|
|
|
|
|
buf = sxmsg_rapidbuf(msg);
|
|
|
|
ln = snprintf(buf, MAX_RBBUF_LEN, "(dir-stream %d)", item->dsid);
|
|
|
|
|
|
|
|
pthread_rwlock_unlock(&_lock);
|
|
|
|
|
|
|
|
return sxmsg_rreply(msg, ln + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __dir_read(void *m, sexp_t *sx)
|
|
|
|
{
|
|
|
|
sexp_t *lsx = NULL;
|
|
|
|
sxmsg_t *msg = (sxmsg_t *)m;
|
|
|
|
char *buf;
|
|
|
|
size_t ln = 0;
|
|
|
|
int stid = -1;
|
|
|
|
datastream_t *item;
|
|
|
|
usrtc_node_t *node;
|
|
|
|
char dump[2048];
|
|
|
|
|
|
|
|
if (sexp_list_cdr(sx, &lsx) || !sx->list->next || !sx->list->next->val
|
|
|
|
|| (0 >= (stid = atoi(sx->list->next->val) )) )
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Invalid protocol\n");
|
|
|
|
return sxmsg_return(msg, SXE_BADPROTO);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get stream item */
|
|
|
|
pthread_rwlock_rdlock(&_lock);
|
|
|
|
if ( !(node = usrtc_lookup(_rd_streams, &stid)) ) {
|
|
|
|
pthread_rwlock_unlock(&_lock);
|
|
|
|
return sxmsg_return(msg, SXE_INVALINDEX);
|
|
|
|
}
|
|
|
|
pthread_rwlock_unlock(&_lock);
|
|
|
|
|
|
|
|
pthread_rwlock_wrlock(&_lock);
|
|
|
|
item = usrtc_node_getdata(node);
|
|
|
|
if (readdir_r(item->dp, &(item->dent), &(item->dres))) {
|
|
|
|
pthread_rwlock_unlock(&_lock);
|
|
|
|
return sxmsg_return(msg, SXE_FAILED);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!item->dres) {
|
|
|
|
buf = sxmsg_rapidbuf(msg);
|
|
|
|
ln = snprintf(buf, MAX_RBBUF_LEN, "(dir-end %d)", item->dsid);
|
|
|
|
goto __finish;
|
|
|
|
}
|
|
|
|
|
|
|
|
sprintf(dump, "(dir-entry (");
|
|
|
|
dump_dirent(item->dres, dump);
|
|
|
|
strcat(dump, "))");
|
|
|
|
|
|
|
|
buf = sxmsg_rapidbuf(msg);
|
|
|
|
ln = snprintf(buf, MAX_RBBUF_LEN, "%s", dump);
|
|
|
|
|
|
|
|
__finish:
|
|
|
|
pthread_rwlock_unlock(&_lock);
|
|
|
|
return sxmsg_rreply(msg, ln + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __dir_close(void *m, sexp_t *sx)
|
|
|
|
{
|
|
|
|
sexp_t *lsx = NULL;
|
|
|
|
sxmsg_t *msg = (sxmsg_t *)m;
|
|
|
|
int stid = -1;
|
|
|
|
datastream_t *item;
|
|
|
|
usrtc_node_t *node;
|
|
|
|
|
|
|
|
if (sexp_list_cdr(sx, &lsx) || !sx->list->next || !sx->list->next->val
|
|
|
|
|| (0 >= (stid = atoi(sx->list->next->val) )) )
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Invalid protocol\n");
|
|
|
|
return sxmsg_return(msg, SXE_BADPROTO);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get stream item */
|
|
|
|
pthread_rwlock_rdlock(&_lock);
|
|
|
|
if ( !(node = usrtc_lookup(_rd_streams, &stid)) ) {
|
|
|
|
pthread_rwlock_unlock(&_lock);
|
|
|
|
return sxmsg_return(msg, ENOENT);
|
|
|
|
}
|
|
|
|
pthread_rwlock_unlock(&_lock);
|
|
|
|
|
|
|
|
pthread_rwlock_wrlock(&_lock);
|
|
|
|
item = usrtc_node_getdata(node);
|
|
|
|
closedir(item->dp);
|
|
|
|
usrtc_delete(_rd_streams, node);
|
|
|
|
FREE(item);
|
|
|
|
pthread_rwlock_unlock(&_lock);
|
|
|
|
|
|
|
|
return sxmsg_return(msg, SXE_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
char *rootca = NULL, *cert = NULL;
|
|
|
|
sxhub_t *sxmphub = sxhub_create();
|
|
|
|
int port = DEFAULT_PORT;
|
|
|
|
int opt;
|
|
|
|
|
|
|
|
while((opt = getopt(argc, argv, "p:r:u:")) != -1) {
|
|
|
|
switch(opt) {
|
|
|
|
case 'p':
|
|
|
|
port = atoi(optarg);
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
rootca = strdup(optarg);
|
|
|
|
break;
|
|
|
|
case 'u':
|
|
|
|
cert = strdup(optarg);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "usage: %s [-p <PORTNUM>] -r <PATH to Root CA> -u <PATH"
|
|
|
|
" to SSL certificate>\n", argv[0]);
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!rootca) {
|
|
|
|
fprintf(stderr, "Root CA not pointed.\n Failure.\n");
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!cert) {
|
|
|
|
fprintf(stderr, "User certificate not pointed.\n Failure.\n");
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
sxmp_init();
|
|
|
|
|
|
|
|
/* all is fine let's init connection subsystem */
|
|
|
|
if(!sxmphub) {
|
|
|
|
fprintf(stderr, "Subsystem init failed: %d\n", opt);
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
/* set working certificates */
|
|
|
|
opt = sxhub_setsslserts(sxmphub, rootca, cert, cert);
|
|
|
|
if(opt) {
|
|
|
|
fprintf(stderr, "Subsystem init failed (set SSL x.509 pems): %d\n", opt);
|
|
|
|
return opt;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* clean up */
|
|
|
|
free(rootca);
|
|
|
|
free(cert);
|
|
|
|
|
|
|
|
/* set important callbacks to do the security checking */
|
|
|
|
sxhub_set_authcheck(sxmphub, __secure_check);
|
|
|
|
sxhub_set_sslvalidate(sxmphub, __validate_sslpem);
|
|
|
|
|
|
|
|
/* ok, now we need to construct RPC lists (channels) */
|
|
|
|
if(!(fulist = malloc(sizeof(usrtc_t)))) {
|
|
|
|
fprintf(stderr, "Cannot allocate memory for RPC lists\n Failure.\n");
|
|
|
|
return ENOMEM;
|
|
|
|
}
|
|
|
|
opt = sxmp_rpclist_init(fulist);
|
|
|
|
if(opt) {
|
|
|
|
fprintf(stderr, "Failed to init rpc list\n Failure.\n");
|
|
|
|
return opt;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we will add one channel with type id READONLY_CHANNEL for read only operations */
|
|
|
|
opt = sxmp_rpclist_add(fulist, READONLY_CHANNEL, "Read only operations", NULL);
|
|
|
|
if(opt) {
|
|
|
|
fprintf(stderr, "Failed to add typed RPC channel\n Failure.\n");
|
|
|
|
return opt;
|
|
|
|
}
|
|
|
|
opt = sxmp_rpclist_add(fulist, 1, "Public", NULL);
|
|
|
|
if(opt) {
|
|
|
|
fprintf(stderr, "Failed to add typed RPC channel\n Failure.\n");
|
|
|
|
return opt;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ok, let's add stream functions */
|
|
|
|
opt = sxmp_rpclist_add_function(fulist, READONLY_CHANNEL, "dir-open", __dir_open);
|
|
|
|
if(opt) {
|
|
|
|
__fail:
|
|
|
|
fprintf(stderr, "Failed to add functions to typed RPC channel\n Failure.\n");
|
|
|
|
return opt;
|
|
|
|
}
|
|
|
|
opt = sxmp_rpclist_add_function(fulist, READONLY_CHANNEL, "dir-read", __dir_read);
|
|
|
|
if(opt) goto __fail;
|
|
|
|
opt = sxmp_rpclist_add_function(fulist, READONLY_CHANNEL, "dir-close", __dir_close);
|
|
|
|
if(opt) goto __fail;
|
|
|
|
|
|
|
|
/* ok, setup it */
|
|
|
|
sxhub_set_rpcvalidator(sxmphub, __rettlist);
|
|
|
|
|
|
|
|
/* create stream tree */
|
|
|
|
if(!(_rd_streams = malloc(sizeof(usrtc_t)) ))
|
|
|
|
return ENOMEM;
|
|
|
|
usrtc_init(_rd_streams, USRTC_REDBLACK, MAX_STREAMS, __cmp_int);
|
|
|
|
|
|
|
|
signal(SIGPIPE, SIG_IGN);
|
|
|
|
|
|
|
|
/* now we're ready to run the listen process */
|
|
|
|
int srv = openlistener_socket(port);
|
|
|
|
|
|
|
|
while(1) {
|
|
|
|
struct sockaddr_in addr;
|
|
|
|
socklen_t len = sizeof(addr);
|
|
|
|
sxlink_t *link;
|
|
|
|
|
|
|
|
int client = accept(srv, (struct sockaddr*)&addr, &len); /* accept connection as usual */
|
|
|
|
|
|
|
|
link = sxlink_master_accept(sxmphub, client, NULL);
|
|
|
|
|
|
|
|
if(!link) fprintf(stderr, "Cannot create sxmp link (%d)\n", errno);
|
|
|
|
}
|
|
|
|
|
|
|
|
sxhub_destroy(sxmphub);
|
|
|
|
|
|
|
|
sxmp_finalize();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|