/* * Secure X Message Passing Library v2 examples. * * (c) Originally written by somebody else ... * (c) Alexander Vdolainen 2013-2015,2016 * * 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 ."; * */ /* * 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 #include #define __USE_GNU #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; } /* * This function might be used to output log in any format you like. * In this case it is a prefix with date and time marks before a message body. */ static int __log(const sxlogtype_t type, const char *fmt, ...) { int r = 0; time_t timer; char buffer[26]; struct tm* tm_info; time(&timer); tm_info = localtime(&timer); strftime(buffer, 26, "%Y/%m/%d %H:%M:%S", tm_info); r += fprintf(stderr, "[%s] ", buffer); va_list arglist; va_start(arglist, fmt); r += vfprintf(stderr, fmt, arglist); va_end(arglist); return r; } /* 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; int is_daemon = 0; while((opt = getopt(argc, argv, "dp:r:u:")) != -1) { switch(opt) { case 'd': is_daemon = 1; break; case 'p': port = atoi(optarg); break; case 'r': rootca = strdup(optarg); break; case 'u': cert = strdup(optarg); break; default: fprintf(stderr, "usage: %s [-p ] -r -u [-d]\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; } /* become daemon if it's necessary */ if (is_daemon && (opt = daemonize())) { fprintf(stderr, "daemonize() failed (%d).\n Failure.\n", opt); 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 sxhub log function */ sxhub_set_logops(sxmphub, __log); /* 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; }