|
|
|
/*
|
|
|
|
* Secure X Message Passing Library v2 examples.
|
|
|
|
*
|
|
|
|
* (c) Alexander Vdolainen 2013-2015 <avdolainen@gmail.com>
|
|
|
|
*
|
|
|
|
* 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 <http://www.gnu.org/licenses/>.";
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 <stdio.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#define __USE_GNU
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <sys/select.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/resource.h>
|
|
|
|
#include <netdb.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <uuid/uuid.h>
|
|
|
|
#include <execinfo.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
|
|
|
|
#include <tdata/usrtc.h>
|
|
|
|
#include <sexpr/sexp.h>
|
|
|
|
#include <sxmp/limits.h>
|
|
|
|
#include <sxmp/sxmp.h>
|
|
|
|
|
|
|
|
#include <pthread.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <getopt.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#include "filelist.h"
|
|
|
|
#include "../config.h"
|
|
|
|
|
|
|
|
#define FULL_PROGRAM_NAME "File lister client"
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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 ("./") : strlen(opts));
|
|
|
|
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 <path> -u | --user-x509 <path> -H | --hostname <ip or hostname>"
|
|
|
|
" -l | --login <login> -P | --password <password> -c | --command <command>"
|
|
|
|
" [-o | --command-options <options>] [-p | --port <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 <path>");
|
|
|
|
fprintf(fso, "\t%-33s Set pathname of the user X.509 public and private certificate.\n",
|
|
|
|
"-u | --user-x509 <path>");
|
|
|
|
fprintf(fso, "\t%-33s Hostname or IP address of the remote host.\n", "-H | --hostname <ip or hostname>");
|
|
|
|
fprintf(fso, "\t%-33s Set remote login name.\n", "-l | --login <login>");
|
|
|
|
fprintf(fso, "\t%-33s Set remote user password.\n", "-P | --password <password>");
|
|
|
|
fprintf(fso, "\t%-33s Command to run on the remote host.\n", "-c | --command <command>");
|
|
|
|
fprintf(fso, "\t%-33s Options for a command to run on the remote host [default: NULL].\n",
|
|
|
|
"-o | --command-options <options>");
|
|
|
|
fprintf(fso, "\t%-33s Remote host listening port number [default: %d].\n",
|
|
|
|
"-p | --port <port>", DEFAULT_PORT);
|
|
|
|
fprintf(fso, "\t%-33s Show help screen.\n", "-h | --help");
|
|
|
|
fprintf(fso, "\t%-33s Print version information.\n", "-v | --version");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __version_print(FILE *fso)
|
|
|
|
{
|
|
|
|
fprintf(fso, "\nFile list example client.\n");
|
|
|
|
fprintf(fso, "Examples bundle for %s %s.\n", PACKAGE_NAME, PACKAGE_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;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|