You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libsxmp/examples/smpfc.c

508 lines
13 KiB
C

/*
* Secure X Message Passing Library v2 examples.
*
* (c) Alexander Vdolainen 2016 <avdolainen@zoho.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/>.";
*
*/
#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 <pthread.h>
#include <stdint.h>
#include <getopt.h>
#include <errno.h>
#include <tdata/usrtc.h>
#include <tdata/list.h>
#include <sexpr/sexp.h>
#include <sxmp/limits.h>
#include <sxmp/sxmp.h>
#include <readline/readline.h>
#include <readline/history.h>
#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 <path> -u | --user-x509 <path> <user>@<hostname>[: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 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;
}