/* * Secure X Message Passing Library v2 examples. * * (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 ."; * */ #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 #include #include "smpf.h" #include "misc.h" #include "../config.h" #define FULL_PROGRAM_NAME "SMPF (secure message passing file) client" #include "helpers.h" struct _session { list_head_t channels; char cwd[MAX_CWDLEN]; }; struct _remote_host { char *root_x509; char *user_x509; char *hostname; char *login; char *password; int port; sxhub_t *hub; sxlink_t *link; }; struct _shll_cmds { char cmd[64]; int (*execute)(sxlink_t *, char **, int); }; static int __cwd(sxlink_t *link, char **argv, int argc) { sxchnl_t *channel = sxchannel_open(link, READONLY_CHANNEL); sxmsg_t *msg; char buf[1024]; int r = SXE_SUCCESS; if(!channel) return SXE_FAILED; if(argc < 2) return SXE_SUCCESS; /* nothing to do if we don't have any parameters */ snprintf(buf, 1024, "(%s %s)", CWD_CMD, argv[1]); r = sxmsg_send(channel, buf, strlen(buf), &msg); if(channel) sxchannel_close(channel); return r; } static int __stat(sxlink_t *link, char **argv, int argc) { sxchnl_t *channel = sxchannel_open(link, READONLY_CHANNEL); sxmsg_t *msg; sexp_t *sx = NULL, *isx, *rsx; char buf[1024]; char *cmsg = NULL, *entry = NULL; int r = SXE_SUCCESS, i; if(!channel) return SXE_FAILED; if(argc < 2) return SXE_SUCCESS; /* nothing to do if we don't have any parameters */ snprintf(buf, 1024, "(%s %s)", STAT_CMD, argv[1]); r = sxmsg_send(channel, buf, strlen(buf), &msg); if(r == SXE_RAPIDMSG) { cmsg = strdup(strdup(sxmsg_payload(msg))); sxmsg_clean(msg); } if(channel) sxchannel_close(channel); if(cmsg) { sx = parse_sexp(cmsg, strlen(cmsg)); if(!sx) { r = SXE_ENOMEM; goto __fini; } SEXP_ITERATE_LIST(sx, isx, i) { if(isx->ty != SEXP_LIST) { __badproto: r = SXE_BADPROTO; goto __fini; } sexp_list_car(isx, &rsx); if(rsx && rsx->ty != SEXP_LIST) { entry = rsx->val; sexp_list_cdr(isx, &rsx); if(!rsx || rsx->ty == SEXP_LIST) goto __badproto; if(entry == rsx->val) goto __badproto; if(!strcmp(entry, ":user")) fprintf(stdout, "User: %s\n", rsx->val); else if(!strcmp(entry, ":group")) fprintf(stdout, "Group: %s\n", rsx->val); else if(!strcmp(entry, ":size")) fprintf(stdout, "Size: %s\n", rsx->val); else if(!strcmp(entry, ":mode")) fprintf(stdout, "Mode: %s\n", rsx->val); else goto __badproto; } else goto __badproto; } r = SXE_SUCCESS; } else r = SXE_FAILED; __fini: if(cmsg) free(cmsg); if(sx) destroy_sexp(sx); return r; } static void __dirlist_out(FILE *f, list_head_t *list) { char *dname = NULL, *dtype = NULL; list_node_t *iter, *siter; struct _nn_stream_entry_node *entry; int i = 0; list_for_each_safe(list, iter, siter) { entry = list_entry(iter, struct _nn_stream_entry_node, node); if(!i) dname = entry->value; else if(i == 1) dtype = entry->value; i++; } fprintf(f, "%-24s | %-12s\n", dname, dtype); return; } static int __list(sxlink_t *link, char **argv, int argc) { char *opt = NULL; int r = SXE_SUCCESS; sxstream_t *stream; list_head_t *list; if(argc > 2) opt = argv[1]; fprintf(stdout, "%-24s | %-12s\n", "Name", "Type"); fprintf(stdout, "---------------------------------------\n"); stream = sxstream_open(link, opt, DIRLIST_ID, SXE_O_READ); if(!stream) { fprintf(stderr, "Failed to open stream with %d ID (%d).\n", DIRLIST_ID, errno); return SXE_FAILED; } while((list = sxstream_read(stream)) != NULL) __dirlist_out(stdout, list); if(errno != SXE_EOS) fprintf(stderr, "Reading failed (%d)\n", errno); sxstream_close(stream); return r; } struct _shll_cmds shcmds[] = { { "cd", __cwd }, { "ls", __list }, { "stat", __stat }, { "", NULL}, }; /* misc remote commands */ static int __remote_getcwd(sxlink_t *link, char *buf, size_t buflen) { sxchnl_t *channel = sxchannel_open(link, READONLY_CHANNEL); sxmsg_t *msg; sexp_t *sx = NULL, *isx; int r = SXE_SUCCESS, idx; if(!channel || !buf) return SXE_FAILED; snprintf(buf, buflen, "(%s)", GETCWD_CMD); r = sxmsg_send(channel, buf, strlen(buf), &msg); if(r == SXE_RAPIDMSG || r == SXE_REPLYREQ) { sx = parse_sexp(sxmsg_payload(msg), strlen(sxmsg_payload(msg))); if(r == SXE_RAPIDMSG) sxmsg_clean(msg); else sxmsg_return(msg, SXE_SUCCESS); } else goto __fini; if(!sx) { r = SXE_ENOMEM; goto __fini; } SEXP_ITERATE_LIST(sx, isx, idx) { if(isx->ty == SEXP_LIST) { r = SXE_BADPROTO; goto __fini; } if(!idx) strcpy(buf, isx->val); else { r = SXE_BADPROTO; goto __fini; } } r = SXE_SUCCESS; __fini: if(sx) destroy_sexp(sx); if(channel) sxchannel_close(channel); return r; } static int __exec_line(sxlink_t *link, const char *line) { char **argv, *tline = (char *)line, *oline; int argc, r = SXE_FAILED, i; int (*exe)(sxlink_t *, char **, int) = NULL; if(!strlen(line)) return SXE_NILREPLY; /* get arguments count */ argc = 1; oline = tline; for(tline = strchr(tline, ' '); tline != NULL; tline += sizeof(char), tline = strchr(tline, ' ')) { if((strlen(oline) - strlen(tline)) > sizeof(char)) argc++; oline = tline; } if(!(argv = malloc(sizeof(uintptr_t)*argc))) return SXE_ENOMEM; /* fill arguments */ for(oline = tline = (char *)line, i = 0; i < argc; i++) { if(!(tline = strchr(tline, ' '))) argv[i] = strdup(oline); else if((strlen(oline) - strlen(tline)) > sizeof(char)) /* that's our bottle of beer */ argv[i] = strndup(oline, strlen(oline) - strlen(tline)); tline += sizeof(char); oline = tline; } for(i = 0; i < argc; i++) { /* check up */ if(argv[i] == NULL) { r = SXE_ENOMEM; goto __fini; } } /* now let's search our cmd */ for(i = 0; strlen(shcmds[i].cmd); i++) { if(!strcmp(shcmds[i].cmd, argv[0])) exe = shcmds[i].execute; } if(!exe) r = SXE_IGNORED; /* ignoring stuff like that */ else r = exe(link, argv, argc); __fini: /* free all arguments */ if(argv && argc) { for(i = 0; i < argc; i++) { if(argv[i]) free(argv[i]); } free(argv); } return r; } /* session processing itself */ static void __shell(sxlink_t *link) { struct _session *s = (struct _session *)sxlink_getpriv(link); char *line, cwd[1024], promt[1024]; int r, rrc; r = __remote_getcwd(link, cwd, 1024); if(r == SXE_SUCCESS) snprintf(promt, 1024, "%s$>", cwd); else snprintf(promt, 1024, "@e:%d$>", r); for(;;) { line = readline(promt); if(line && *line) add_history(line); else break; if(!strcmp(line, "exit")) break; else { rrc = __exec_line(link, line); /* getcwd, in some cases it might be changed */ r = __remote_getcwd(link, cwd, 1024); if(r == SXE_SUCCESS) { if(rrc == SXE_SUCCESS) snprintf(promt, 1024, "%s$>", cwd); else snprintf(promt, 1024, "[%d]%s$>", rrc, cwd); } else snprintf(promt, 1024, "@e:%d$>", r); } } free(line); return; } /* sxmp stuff below */ static void __session_free(sxlink_t *l) { struct _session *s = (struct _session *)sxlink_getpriv(l); /* TODO: free all list entries if exists */ free(s); return; } static int __rpcscheck_onclient(sxlink_t *l, int ch_tid, char *description) { struct _session *s = (struct _session *)sxlink_getpriv(l); /* TODO: add all stuff to the list */ return SXE_SUCCESS; } static int __remote_host_connect(struct _remote_host *rh) { int r = 0; struct _session *s = malloc(sizeof(struct _session)); sxhub_set_channelcall(rh->hub, __rpcscheck_onclient); sxhub_set_ondestroy(rh->hub, __session_free); if(!s) return SXE_ENOMEM; /* here we go, connect to the hist i.e. create a link to it */ rh->link = sxlink_connect_at(rh->hub, rh->hostname, rh->port, rh->user_x509, rh->login, rh->password, s); if(!rh->link) r = errno; return r; } /* standard output 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 | --user-x509 @[:port]\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 user X.509 public and private certificate.\n", "-u | --user-x509 "); 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, *cert, *host, *cline; char *login, *password; int port = DEFAULT_PORT, opt, i = 0; struct _remote_host *rh = malloc(sizeof(struct _remote_host)); sxhub_t *lhub; if(!rh) return ENOMEM; else memset(rh, 0, sizeof(struct _remote_host)); rootca = cert = host = login = password = NULL; 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'}, {"user-x509", required_argument, NULL, 'u'}, { NULL, 0, NULL, 0 }, }; if((opt = getopt_long(argc, argv, "hvr:u:", 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 'u': if(!(cert = strdup(optarg))) return ENOMEM; break; } } if(optind < argc) cline = argv[optind]; else { fprintf(stderr, "No remote address given connect to.\n\n"); __help_print(stderr, argv[0]); return EINVAL; } /* TODO: better check */ exmps_getaddrs(cline, &login, &host, &port); if(!host) { fprintf(stderr, "Host not pointed.\n"); __help_print(stderr, argv[0]); return EINVAL; } /* ok validate all before doing any stuff */ if(!rootca || !cert) { fprintf(stderr, "One or more x.509 certificates files not pointed.\n"); __help_print(stderr, argv[0]); return EINVAL; } if(!login) { fprintf(stderr, "Login not pointed.\n"); __help_print(stderr, argv[0]); return EINVAL; } if(port <= 0) { fprintf(stderr, "Port number has invalid value.\n"); __help_print(stderr, argv[0]); return EINVAL; } /* read the password */ password = exmps_getpass("Password: "); fprintf(stdout, "\n"); /* initialize sxmp first */ sxmp_init(); /* init library */ lhub = sxhub_create(); /* create sxhub for link creation */ if(!lhub) return ENOMEM; i = sxhub_setsslserts(lhub, rootca, cert, cert); if(i) return i; /* ok setup our structure */ rh->port = port; rh->root_x509 = rootca; rh->user_x509 = cert; rh->hostname = host; rh->login = login; rh->password = password; rh->hub = lhub; i = __remote_host_connect(rh); if(i) { fprintf(stderr, "Failed to create link to %s@%s:%d with %d.\n", rh->login, rh->hostname, rh->port, i); } else { /* hooray now we ready to do something */ __shell(rh->link); sxlink_close(rh->link); /* don't forget to do this */ } /* free all stuff */ if(rh->root_x509) free(rh->root_x509); if(rh->user_x509) free(rh->user_x509); if(rh->hostname) free(rh->hostname); if(rh->password) free(rh->password); if(rh->login) free(rh->login); free(rh); if(lhub) sxhub_destroy(lhub); return 0; }