/* * Secure X Message Passing Library v2 examples. * * (c) Alexander Vdolainen 2013-2015 * * 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 client example, without builtin streams. * It's based on the daemon own stream implementation. * * 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 "../config.h" #define FULL_PROGRAM_NAME "File lister client" #include "helpers.h" struct _remote_host { char *root_x509; char *user_x509; char *hostname; char *login; char *password; int port; sxhub_t *hub; sxlink_t *link; }; struct _icmd { int id; int opt; char *cmdname; int (*__fu)(FILE *, struct _remote_host *, const char *); }; /* supported commands */ enum commands { __LISTDIR = 0, }; static int __remote_ls(FILE *, struct _remote_host *, const char *); struct _icmd _tool[] = { { .id = __LISTDIR, .opt = 0, .cmdname = "ls", .__fu = __remote_ls }, { .id = 0, .opt = 0, .cmdname = NULL, .__fu = NULL }, }; /* * This function might be used to check supported RPC calls * i.e. for each channel type id this callback is called. * Whereas the place to check it with ID and description. * Should return SXE_SUCCESS if supported. */ static int __rpcscheck_onclient(sxlink_t *l, int ch_tid, char *description) { return SXE_SUCCESS; } /* * This function might be used to output log in any format you like. * In this case it is a prefix with a message type before a message body. */ static int __log(const sxlogtype_t type, const char *fmt, ...) { int r = 0; switch(type) { case SXCRITICAL_LOG: r += fprintf(stderr, "[CRITICAL] "); break; case SXERROR_LOG: r += fprintf(stderr, "[ERROR] "); break; case SXWARNING_LOG: r += fprintf(stderr, "[WARNING] "); break; case SXINFO_LOG: r += fprintf(stderr, "[INFO] "); break; case SXDEBUG_LOG: r += fprintf(stderr, "[DEBUG] "); break; default: r += fprintf(stderr, "[UNKNOWN] "); break; } va_list arglist; va_start(arglist, fmt); r += vfprintf(stderr, fmt, arglist); va_end(arglist); return r; } /* supplemetary functions to make code more readable */ static int __close_dir_stream(sxchnl_t *c, int sid) { int r = SXE_SUCCESS; char *remote_rpc; size_t remote_rpc_len; sxmsg_t *msg = NULL; remote_rpc_len = strlen("dir-close") + sizeof(char)*16; if(!(remote_rpc = malloc(remote_rpc_len))) return ENOMEM; else remote_rpc_len = snprintf(remote_rpc, remote_rpc_len, "(dir-close %d)", sid); r = sxmsg_send(c, remote_rpc, remote_rpc_len, &msg); if(r == SXE_RAPIDMSG) sxmsg_clean(msg); else if(r == SXE_REPLYREQ) r = sxmsg_return(msg, SXE_SUCCESS); return r; } /* our commands or tools implementation */ static int __remote_ls(FILE *fso, struct _remote_host *rh, const char *opts) { char *remote_rpc; size_t remote_rpc_len; sxmsg_t *msg = NULL; sxchnl_t *rpc_channel = NULL; sexp_t *sx, *isx, *iisx; char *remote_reply = NULL, *dname, *dtype; int rrc = 0, stream_id = 0, i, ii; /* ok determine the lenght */ remote_rpc_len = strlen("dir-open") + 12*sizeof(char) + (opts ? strlen (opts) : strlen("./")); if(!(remote_rpc = malloc(remote_rpc_len))) return ENOMEM; else remote_rpc_len = snprintf(remote_rpc, remote_rpc_len, "(dir-open \"%s\")", opts ? opts : "./"); /* firstly open the required channel with required type */ if(!(rpc_channel = sxchannel_open(rh->link, READONLY_CHANNEL))) { rrc = errno; goto __fini; } rrc = sxmsg_send(rpc_channel, remote_rpc, remote_rpc_len, &msg); if(rrc == SXE_RAPIDMSG || rrc == SXE_REPLYREQ) { remote_reply = strdup(sxmsg_payload(msg)); if(rrc == SXE_RAPIDMSG) sxmsg_clean(msg); else rrc = sxmsg_return(msg, SXE_SUCCESS); } if(!remote_reply && (rrc != 0 || rrc != SXE_SUCCESS)) goto __fini; else sx = parse_sexp(remote_reply, strlen(remote_reply)); if(!sx) { rrc = EINVAL; goto __fini; } /* ok, now we will take a pass with our received S expression */ rrc = EINVAL; SEXP_ITERATE_LIST(sx, isx, i) { if(isx->ty == SEXP_LIST) stream_id = -1; if(!i && strcmp(isx->val, "dir-stream")) stream_id = -1; if(i == 1 && !stream_id) stream_id = atoi(isx->val); } /* free all the stuff */ destroy_sexp(sx); free(remote_reply); remote_reply = NULL; if(stream_id < 0) goto __fini; else rrc = SXE_SUCCESS; free(remote_rpc); /* now we're ready to read contents from the remote host */ remote_rpc_len = strlen("dir-read") + sizeof(char)*32; if(!(remote_rpc = malloc(remote_rpc_len))) { rrc = ENOMEM; goto __fini; } else remote_rpc_len = snprintf(remote_rpc, remote_rpc_len, "(dir-read %d)", stream_id); /* print header */ fprintf(fso, "%-24s | %-12s\n", "Name", "Type"); fprintf(fso, "---------------------------------------\n"); while(1) { rrc = sxmsg_send(rpc_channel, remote_rpc, remote_rpc_len, &msg); if(rrc == SXE_RAPIDMSG || rrc == SXE_REPLYREQ) { remote_reply = strdup(sxmsg_payload(msg)); if(rrc == SXE_RAPIDMSG) sxmsg_clean(msg); else rrc = sxmsg_return(msg, SXE_SUCCESS); } else goto __fini; if(!remote_reply) goto __fini; else if(!(sx = parse_sexp(remote_reply, strlen(remote_reply)))) { rrc = ENOMEM; goto __fini; } rrc = EINVAL; SEXP_ITERATE_LIST(sx, isx, i) { if(!i && isx->ty == SEXP_LIST) { rrc = EINVAL; goto __fini; } if(!i && !strcmp(isx->val, "dir-end")) { rrc = __close_dir_stream(rpc_channel, stream_id); goto __fini; } if(i && isx->ty != SEXP_LIST) { rrc = EINVAL; goto __fini; } else if(i) { SEXP_ITERATE_LIST(isx, iisx, ii) { if(!ii) dname = iisx->val; else dtype = iisx->val; } fprintf(fso, "%-24s | %-12s\n", dname, dtype); } } /* free it */ destroy_sexp(sx); free(remote_reply); remote_reply = NULL; } __fini: if(remote_reply) free(remote_reply); if(remote_rpc) free(remote_rpc); i = SXE_SUCCESS; if(rpc_channel) i = sxchannel_close(rpc_channel); if(rrc == SXE_SUCCESS && i == SXE_SUCCESS) rrc = 0; return rrc; } static int __remote_host_connect(struct _remote_host *rh) { int r = 0; sxhub_set_channelcall(rh->hub, __rpcscheck_onclient); /* here we go, connect to the hist i.e. create a link to it */ rh->link = sxlink_connect(rh->hub, rh->hostname, rh->port, rh->user_x509, rh->login, rh->password); if(!rh->link) r = errno; return r; } 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 -H | --hostname " " -l | --login -P | --password -c | --command " " [-o | --command-options ] [-p | --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 Hostname or IP address of the remote host.\n", "-H | --hostname "); fprintf(fso, "\t%-33s Set remote login name.\n", "-l | --login "); fprintf(fso, "\t%-33s Set remote user password.\n", "-P | --password "); fprintf(fso, "\t%-33s Command to run on the remote host.\n", "-c | --command "); fprintf(fso, "\t%-33s Options for a command to run on the remote host [default: NULL].\n", "-o | --command-options "); fprintf(fso, "\t%-33s Remote host listening port number [default: %d].\n", "-p | --port ", DEFAULT_PORT); 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, *cmd; char *login, *password, *options; int port = DEFAULT_PORT, opt, i = 0; int (*toolfunction)(FILE *, struct _remote_host *, const char *) = NULL; 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 = cmd = NULL; login = password = options = 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'}, {"hostname", required_argument, NULL, 'H'}, {"port", required_argument, NULL, 'p'}, {"login", required_argument, NULL, 'l'}, {"password", required_argument, NULL, 'P'}, {"command", required_argument, NULL, 'c'}, {"command-options", required_argument, NULL, 'o'}, { NULL, 0, NULL, 0 }, }; if((opt = getopt_long(argc, argv, "hvr:u:H:p:l:P:c:o:", 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; case 'H': if(!(host = strdup(optarg))) return ENOMEM; break; case 'l': if(!(login = strdup(optarg))) return ENOMEM; break; case 'P': if(!(password = strdup(optarg))) return ENOMEM; break; case 'c': if(!(cmd = strdup(optarg))) return ENOMEM; break; case 'o': if(!(options = strdup(optarg))) return ENOMEM; break; case 'p': port = atoi(optarg); break; } } /* 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 || !password) { fprintf(stderr, "Login or password 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; } /* ok, check up for the command */ if(!cmd) { fprintf(stderr, "No command given.\n"); __help_print(stderr, argv[0]); return EINVAL; } /* try to lookup it */ while(_tool[i].cmdname != NULL) { if(!strcmp(_tool[i].cmdname, cmd)) { if(_tool[i].opt && !options) { fprintf(stderr, "Command %s required options.\n", cmd); __help_print(stderr, argv[0]); return EINVAL; } toolfunction = _tool[i].__fu; } i++; } if(!toolfunction) { fprintf(stderr, "No such function `%s' exist.\n", cmd); __help_print(stderr, argv[0]); return EINVAL; } /* initialize sxmp first */ sxmp_init(); /* init library */ lhub = sxhub_create(); /* create sxhub for link creation */ if(!lhub) return ENOMEM; /* set sxhub log function */ sxhub_set_logops(lhub, __log); 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 { i = toolfunction(stdout, rh, options); if(i) fprintf(stderr, "Failed to run RPC on %s@%s:%d with %d.\n", rh->login, rh->hostname, rh->port, i); 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 i; }