diff --git a/examples/helpers.c b/examples/helpers.c index 5ec229f..df60467 100644 --- a/examples/helpers.c +++ b/examples/helpers.c @@ -62,3 +62,56 @@ int openlistener_socket(int port) return sd; } +char *nodehostname(void) +{ + char *hm = malloc(HOST_NAME_MAX); + + if(!gethostname(hm, HOST_NAME_MAX)) + return hm; + else { + if(hm) free(hm); + return NULL; + } +} + +static inline int __removechar(char *b, size_t chrs) +{ + char *c = b + chrs; + size_t blen = strlen(c) + sizeof(char); + + memcpy(b, c, blen); + + return chrs; +} + +int normalize_path(char *path) +{ + char *obuf = path; + int cutlen; + + /* first remove useless / if found */ + while((obuf = strchr(obuf, '/'))) { + obuf++; + while(*obuf == '/') __removechar(obuf, 1); + } + + obuf = path; /* much cleaner now */ + while(*obuf != '\0') { + if(*obuf == '.' && *(obuf - 1) == '/') { + if(!strcmp(obuf, "./")) __removechar(obuf, 2); + else if(!strcmp(obuf, "../")) { + cutlen = strlen(path) - strlen(obuf); + if(cutlen < 4) return -1; + else cutlen = 4; + obuf -= 2; + while(*obuf != '/') { + obuf--; cutlen++; + } + __removechar(obuf, cutlen); + } + } + obuf++; + } + + return 0; +} diff --git a/examples/helpers.h b/examples/helpers.h index 3b75d9c..9a72c5e 100644 --- a/examples/helpers.h +++ b/examples/helpers.h @@ -24,4 +24,8 @@ int openlistener_socket(int port); +char *nodehostname(void); + +int normalize_path(char *path); + #endif /* __SXMP_EXAMPLES_HELPERS_H__ */ diff --git a/examples/smpfd.c b/examples/smpfd.c index ae16a07..b4a88bc 100644 --- a/examples/smpfd.c +++ b/examples/smpfd.c @@ -76,10 +76,6 @@ 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. */ @@ -89,11 +85,6 @@ static usrtc_t *__rettlist(sxlink_t *l) } /* - * 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) @@ -102,16 +93,32 @@ static int __validate_sslpem(sxlink_t *l) } /* - * To be more paranoic, sxmp also allow to - * check up username and password. - * We don't overview this tuff here, just - * always allow. + * don't check password and login, + * but assign session info for each link. */ static int __secure_check(sxlink_t *l) { + struct mp_session *session; + + if(!(session = malloc(sizeof(struct mp_session)))) + return SXE_ENOMEM; + + session->link = l; + strcpy(session->cwd, daemon_ctx.root_dir); + sxlink_setpriv(l, session); + return SXE_SUCCESS; } +void __session_free(sxlink_t *l) +{ + struct mp_session *session = (struct mp_session *)sxlink_getpriv(l); + + if(session) free(session); + + return; +} + /* remote functions implementation */ /* misc functions */ static int __ping(void *m, sexp_t *sx) @@ -155,6 +162,188 @@ static void __version_print(FILE *fso) return; } +/* control operations */ +static int __setcwd(void *m, sexp_t *sx) +{ + sxmsg_t *msg = (sxmsg_t*)m; + sxlink_t *link = sxmsg_link(msg); + char *dirname = NULL, *odir = NULL; + sexp_t *isx; + int r = SXE_SUCCESS, idx, olen = 0; + struct mp_session *session = (struct mp_session *)sxlink_getpriv(link); + + SEXP_ITERATE_LIST(sx, isx, idx) { + if(isx->ty == SEXP_LIST) r = SXE_BADPROTO; + if(idx > 1) r = SXE_BADPROTO; + + if(idx) dirname = isx->val; + } + if(r != SXE_SUCCESS || !dirname) goto __fini; + if(!strcmp(dirname, "./")) goto __fini; + if(!strlen(dirname) || !strcmp(dirname, ".")) goto __fini; + + + if(*dirname == '/') odir = strdup(dirname); + else { + olen = strlen(session->cwd) + strlen(dirname) + 2*sizeof(char); + odir = malloc(olen); + } + + if(!odir) { + r = SXE_ENOMEM; + goto __fini; + } + + if(olen) snprintf(odir, olen, "%s/%s", session->cwd, dirname); + + if(normalize_path(odir)) r = SXE_FAILED; + else strcpy(session->cwd, odir); + + __fini: + if(odir) free(odir); + return sxmsg_return(msg, r); +} + +/* readonly operations */ +static int __getcwd(void *m, sexp_t *sx) +{ + sxmsg_t *msg = (sxmsg_t*)m; + sxlink_t *link = sxmsg_link(msg); + char *rbuf = sxmsg_rapidbuf(msg); + size_t rbuf_len = 0; + struct mp_session *session = (struct mp_session *)sxlink_getpriv(link); + + rbuf_len = snprintf(rbuf, MAX_RBBUF_LEN, "(\"%s\")", session->cwd); + + return sxmsg_rreply(msg, rbuf_len); +} + +/* stream, simple entry nonamed streams */ +/* 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)); + char *odir = NULL; + struct mp_session *session = (struct mp_session *)sxlink_getpriv(link); + int olen = 0; + + if(!pslist) return SXE_ENOMEM; + else memset(pslist, 0, sizeof(dirlist_t)); + + if(!dirname) odir = session->cwd; + else if(*dirname == '/') odir = (char *)dirname; + else { + olen = strlen(dirname) + strlen(session->cwd) + 2*sizeof(char); + if(!(odir = malloc(olen))) { + free(pslist); + return SXE_ENOMEM; + } + + snprintf(odir, olen, "%s/%s", session->cwd, dirname); + } + + if(!(pslist->dp = opendir(odir))) { + if(dirname && *dirname != '/') free(odir); + free(pslist); + return SXE_FAILED; + } else sxstream_opened_setpriv(stream, pslist); + + if(dirname && *dirname != '/') free(odir); + + 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, +}; + int main(int argc, char **argv) { char *rootca = NULL, *cert = NULL; @@ -241,11 +430,17 @@ int main(int argc, char **argv) /* clean up */ free(rootca); free(cert); - free(root_dir); + + /* set context */ + daemon_ctx.root_dir = root_dir; + daemon_ctx.root = NULL; + daemon_ctx.hostname = nodehostname(); /* set important callbacks to do the security checking */ sxhub_set_authcheck(sxmphub, __secure_check); sxhub_set_sslvalidate(sxmphub, __validate_sslpem); + /* we using our internal session info */ + sxhub_set_ondestroy(sxmphub, __session_free); /* ok, now we need to construct RPC lists (channels) */ if(!(fulist = malloc(sizeof(usrtc_t)))) { @@ -258,26 +453,59 @@ int main(int argc, char **argv) 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); + /* + * Public channel + */ + opt = sxmp_rpclist_add(fulist, PUBLIC_CHANNEL, "Public channel", NULL); if(opt) { + __failaddchannel: 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); + opt = sxmp_rpclist_add_function(fulist, PUBLIC_CHANNEL, PING_CMD, __ping); if(opt) { + __failaddrpc: fprintf(stderr, "Failed to add functions to typed RPC channel\n Failure.\n"); return opt; } + /* + * Readonly channel + */ + opt = sxmp_rpclist_add(fulist, READONLY_CHANNEL, "Read only operations", NULL); + if(opt) goto __failaddchannel; + + /* ok, let's add read functions */ + opt = sxmp_rpclist_add_function(fulist, READONLY_CHANNEL, CWD_CMD, __setcwd); + if(opt) goto __failaddrpc; + opt = sxmp_rpclist_add_function(fulist, READONLY_CHANNEL, GETCWD_CMD, __getcwd); + if(opt) goto __failaddrpc; + opt = sxmp_rpclist_add_function(fulist, READONLY_CHANNEL, STAT_CMD, __ping); + if(opt) goto __failaddrpc; + + /* streams */ + sxhub_stream_register(sxmphub, &dirlist_stream); + + /* + * Write channel + */ + opt = sxmp_rpclist_add(fulist, WRITEONLY_CHANNEL, "Write only operations", NULL); + if(opt) goto __failaddchannel; + + /* functions */ + opt = sxmp_rpclist_add_function(fulist, WRITEONLY_CHANNEL, CREAT_CMD, __ping); + if(opt) goto __failaddrpc; + opt = sxmp_rpclist_add_function(fulist, WRITEONLY_CHANNEL, MKDIR_CMD, __ping); + if(opt) goto __failaddrpc; + opt = sxmp_rpclist_add_function(fulist, WRITEONLY_CHANNEL, RMDIR_CMD, __ping); + if(opt) goto __failaddrpc; + opt = sxmp_rpclist_add_function(fulist, WRITEONLY_CHANNEL, UNLINK_CMD, __ping); + if(opt) goto __failaddrpc; + /* 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 */