/* * Secure X Message Passing Library v2 examples. * * (c) Alexander Vdolainen 2013-2015 * (c) Alexander Vdolainen 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. * Using the builtin stream functionality added since 0.4.2 version. * * 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 #include "filelist.h" #include "../config.h" #define FULL_PROGRAM_NAME "File lister master (builtin streams based)" #include "helpers.h" /* 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; } /* misc functions */ static int __ping(void *m, sexp_t *sx) { return sxmsg_return((sxmsg_t*)m, SXE_SUCCESS); } /* dir listing stream */ typedef struct __dirlist_type { DIR *dp; struct dirent dent; struct dirent *dres; } dirlist_t; static int dirlist_open(sxlink_t *link, struct sxstream_opened *stream, const char *dirname) { dirlist_t *pslist = malloc(sizeof(dirlist_t)); if(!pslist) return SXE_ENOMEM; else memset(pslist, 0, sizeof(dirlist_t)); if(!(pslist->dp = opendir(dirname ? dirname : "./"))) { free(pslist); return SXE_FAILED; } else sxstream_opened_setpriv(stream, pslist); return SXE_SUCCESS; } static int dirlist_close(struct sxstream_opened *stream) { dirlist_t *pslist = sxstream_opened_getpriv(stream); list_head_t *llist = sxstream_opened_getelist(stream); if(!pslist) return SXE_FAILED; if(llist) sxstream_generic_slist_free(llist); /* close dir and free strcuture */ if(pslist->dp) closedir(pslist->dp); free(pslist); return SXE_SUCCESS; } static inline const char *dump_type(struct dirent *d) { if(!d) return "nil"; switch(d->d_type) { case DT_REG: return "regular"; case DT_DIR: return "directory"; case DT_BLK: return "block"; case DT_CHR: return "char"; case DT_FIFO: return "fifo"; case DT_LNK: return "link"; case DT_UNKNOWN: return "unknown"; default: return "another"; } return "nil"; } static size_t dirlist_read(struct sxstream_opened *stream, size_t sz, uint64_t off, list_head_t **rlist) { dirlist_t *pslist = sxstream_opened_getpriv(stream); list_head_t *llist_prev = sxstream_opened_getelist(stream); list_head_t *llist_new = NULL; if(!pslist) return -1; /* read and fill new list */ if(readdir_r(pslist->dp, &(pslist->dent), &(pslist->dres))) return -1; /* if finished return 0 */ if(!pslist->dres) { if(llist_prev) sxstream_generic_slist_free(llist_prev); stream->ent_buf = NULL; return 0; } /* allocate a new */ if(!(llist_new = sxstream_generic_slist_alloc())) return -1; else { /* fill this list */ if(sxstream_generic_slist_additem(llist_new, pslist->dres->d_name)) { __fail: sxstream_generic_slist_free(llist_new); return -1; } if(sxstream_generic_slist_additem(llist_new, dump_type(pslist->dres))) goto __fail; } /* otherwise destroy last list */ if(llist_prev) sxstream_generic_slist_free(llist_prev); *rlist = llist_new; /* assign new list */ return 1; } static struct sxstream_ops dirlist_ops = { .s_open = dirlist_open, .s_close = dirlist_close, .s_eread = dirlist_read, .s_ewrite = NULL, }; static struct sxstream_description dirlist_stream = { .stid = DIRLIST_ID, .pcid = READONLY_CHANNEL, .type = 0, /* just a simple one */ .flags = SXE_O_READ | SXE_O_TRUNC, .ops = &dirlist_ops, }; /* command line options helpers */ static void __help_print(FILE *fso, const char *fmtname) { fprintf(fso, "\n%s\n\n", FULL_PROGRAM_NAME); /* usage options */ fprintf(fso, "Usage:\n"); fprintf(fso, "\t%s -r | --root-x509 -u | --master-x509 " " [-p | --listen-port ] [-d | --daemonize]\n", fmtname); /* defaults */ fprintf(fso, "\t%s -h | --help\n", fmtname); fprintf(fso, "\t%s -v | --version\n", fmtname); /* options description */ fprintf(fso, "\nOptions:\n"); fprintf(fso, "\t%-33s Set pathname of the remote root X.509 public certificate.\n", "-r | --root-x509 "); fprintf(fso, "\t%-33s Set pathname of the host X.509 public and private certificate.\n", "-m | --master-x509 "); fprintf(fso, "\t%-33s Remote host listening port number [default: %d].\n", "-p | --listen-port ", DEFAULT_PORT); fprintf(fso, "\t%-33s Work as a daemon.\n", "-d | --daemonize"); fprintf(fso, "\t%-33s Show help screen.\n", "-h | --help"); fprintf(fso, "\t%-33s Print version information.\n", "-v | --version"); return; } 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(1) { int option_index = 0; static struct option long_options[] = { /* These options a generic ones. */ {"help", no_argument, NULL, 'h'}, /* print out help and version info */ {"version", no_argument, NULL, 'v'}, /* just out a version info */ /* The following options about settings . */ {"root-x509", required_argument, NULL, 'r'}, {"master-x509", required_argument, NULL, 'm'}, {"listen-port", required_argument, NULL, 'p'}, {"daemonize", required_argument, NULL, 'd'}, { NULL, 0, NULL, 0 }, }; if((opt = getopt_long(argc, argv, "dhvr:m:p:", long_options, &option_index)) == -1) break; switch(opt) { case 'h': __help_print(stdout, argv[0]); return 0; break; case 'v': version_print(stdout); return 0; break; case 'r': if(!(rootca = strdup(optarg))) return ENOMEM; break; case 'm': if(!(cert = strdup(optarg))) return ENOMEM; break; case 'p': port = atoi(optarg); break; case 'd': is_daemon = 1; break; } } if(!rootca) { fprintf(stderr, "Root CA not pointed.\n Failure.\n"); __help_print(stderr, argv[0]); return EINVAL; } if(!cert) { fprintf(stderr, "User certificate not pointed.\n Failure.\n"); __help_print(stderr, argv[0]); 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 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; } /* ok, let's add stream functions */ opt = sxmp_rpclist_add_function(fulist, READONLY_CHANNEL, "ping", __ping); if(opt) { fprintf(stderr, "Failed to add functions to typed RPC channel\n Failure.\n"); return opt; } /* ok, setup it */ sxhub_set_rpcvalidator(sxmphub, __rettlist); /* setup our stream */ sxhub_stream_register(sxmphub, &dirlist_stream); 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; }