diff options
Diffstat (limited to 'src/options.c')
-rw-r--r-- | src/options.c | 405 |
1 files changed, 405 insertions, 0 deletions
diff --git a/src/options.c b/src/options.c new file mode 100644 index 0000000..a9b96f7 --- /dev/null +++ b/src/options.c @@ -0,0 +1,405 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#else +#define POTD_LOGFILE "/tmp/potd.log" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <assert.h> +#include <getopt.h> +#include <linux/limits.h> +#include <libgen.h> +#include <errno.h> + +#include "options.h" + +typedef enum opt_type { + OT_INVALID = 0, OT_NOARG, OT_L, OT_LL, OT_STR, + OT_PATH +} opt_type; + +struct opt_list; + +typedef union opt_ptr { + long int l; + long long int ll; + const char *str; + char *str_dup; + struct opt_list *list; +} opt_ptr; + +typedef struct opt_list { + opt_ptr value; + struct opt_list *next; +} opt_list; + +struct opt { + opt_type type; + opt_ptr value; + opt_ptr def_value; + int used; + int is_list; + + const char *arg_name; + const char *short_help; + const char *help; +}; + +#define OPT(type, def_value, arg, short_help, help) \ + { type, {0}, {def_value}, 0, 0, arg, short_help, help } +#define OPT_LIST(type, def_values, arg, short_help, help) \ + { type, {0}, {def_values}, 0, 1, arg, short_help, help } +#define OPT_NOARG(arg, short_help, help) \ + OPT(OT_NOARG, .ll = 0, arg, short_help, help) +static struct opt options[OPT_MAX+1] = { + OPT_NOARG("log-to-file", "log to the default logfile path\n", NULL), + OPT(OT_PATH, .str = POTD_LOGFILE, "log-file", "specify a logfile path\n", + NULL), + OPT(OT_STR, .str = "notice", "log-level", "set the loglevel\n", + "error - log only errors\n" + "warning - log errors,warnings\n" + "notice - log errors,warnings,notices\n" + "debug - log all messages\n"), + OPT_NOARG("daemon", "fork into background if possible\n", NULL), + OPT_LIST(OT_STR, .str = NULL, "redirect", "setup redirector service\n", + "format [listen]:[forward-to-protocol]\n" + "where [listen] contains [listen-addr]:[listen-port]\n" + "and [forward-to-protocol] contains [forward-addr]:[forward-port]\n" + "Example: 0.0.0.0:2222:127.0.0.1:22222\n"), + OPT_LIST(OT_STR, .str = NULL, "protocol", "setup (ssh) protocol service\n", + "format [listen]:[forward-to-jail]\n" + "where [listen] contains [listen-addr]:[listen-port]\n" + "and [forward-to-jail] contains [forward-addr]:[forward-port]\n" + "Example: 127.0.0.1:22222:127.0.0.1:33333\n"), + OPT_LIST(OT_STR, .str = NULL, "jail", "setup jail service\n", + "format [listen]\n" + "where [listen] contains [listen-addr]:[listen-port]\n" + "Example: 127.0.0.1:33333\n"), + OPT(OT_PATH, .str = POTD_DEFROOT, "rootfs", + "path to root directory/image\n", NULL), + OPT(OT_PATH, .str = POTD_NETNS_RUN_DIR, "netns-rundir", + "set the network namespace run directory\n", NULL), + OPT_NOARG("seccomp-minimal", "use a minimal seccomp ruleset\n", + "instead of setting an allowed syscall ruleset\n" + "use a minimal set of blocked syscalls e.g.\n" + "mount, umount, ptrace, kernel module syscalls\n" + "and some io syscalls\n" + "(use this if you acknowledge errors on some platforms)\n"), + + OPT_NOARG("help", "this\n", NULL), + OPT(OT_INVALID, .ll = 0, NULL, NULL, NULL) +}; + +static int opt_convert(opt_type t, opt_ptr *d); +static int setopt_list(struct opt *o, const char *optarg); +static int setopt(struct opt *o, const char *optarg); +static size_t snprint_multilined_ljust(const char *prefix, + const char *multiline, + char *buf, size_t siz); +static void usage(const char *arg0, int print_copyright); + + +static int parse_path(opt_ptr *d, char *some_path) +{ + int rc = 1; + char path[PATH_MAX]; + char *dir, *base; + + d->str_dup = realpath(some_path, NULL); + if (!d->str_dup && errno == ENOENT) { + snprintf(path, sizeof path, "%s", some_path); + dir = dirname(path); + if (!dir) + return 1; + dir = realpath(dir, NULL); + if (!dir) + return 1; + snprintf(path, sizeof path, "%s", some_path); + base = basename(path); + if (!base) + goto error; + snprintf(path, sizeof path, "%s/%s", dir, base); + d->str_dup = strndup(path, strnlen(path, sizeof path)); +error: + free(dir); + } + + if (d->str_dup) + rc = 0; + + return rc; +} + +static int opt_convert(opt_type t, opt_ptr *d) +{ + char *endptr = NULL; + + switch (t) { + case OT_L: + d->l = strtol(optarg, &endptr, 10); + break; + case OT_LL: + d->ll = strtoll(optarg, &endptr, 10); + break; + case OT_STR: + d->str_dup = strdup(optarg); + break; + case OT_PATH: + if (parse_path(d, optarg)) + return 1; + break; + case OT_NOARG: + case OT_INVALID: + return 1; + } + + if (endptr && *endptr != 0) + return 1; + + return 0; +} + +static int setopt_list(struct opt *o, const char *optarg) +{ + opt_list **l; + + assert(o && o->type != OT_INVALID); + + if (!optarg || o->type == OT_NOARG || !o->is_list) + return 1; + + l = &o->value.list; + while (*l) l = &(*l)->next; + *l = (opt_list *) calloc(1, sizeof **l); + + if (opt_convert(o->type, &(*l)->value)) + return 1; + + o->used = 1; + + return 0; +} + +static int setopt(struct opt *o, const char *optarg) +{ + assert(o && o->type != OT_INVALID); + if (o->used && !o->is_list) + return 1; + if (!optarg || o->type == OT_NOARG) + goto noarg; + + if (opt_convert(o->type, &o->value)) + return 1; + +noarg: + o->used = 1; + + return 0; +} + +static size_t snprint_multilined_ljust(const char *prefix, + const char *multiline, + char *buf, size_t siz) +{ + const char sep[] = "\n"; + const char *start, *end; + size_t off; + + off = 0; + start = multiline; + end = NULL; + do { + if (start) { + end = strstr(start, sep); + if (end) { + off += snprintf(buf + off, siz - off, "%s%.*s\n", prefix, + (int) (end-start), start); + start = end + strlen(sep); + } + } + } while (start && end); + + return off; +} + +static void usage(const char *arg0, int print_copyright) +{ + int i, has_default; + size_t off; + char spaces[6]; + char spaces_long[28]; + char buf_arg[64]; + char buf_shorthelp[BUFSIZ]; + char buf_help[BUFSIZ]; + char value[32]; + +#ifdef HAVE_CONFIG_H + if (print_copyright) + fprintf(stderr, "\n%s (C) 2018 Toni Uhlig <%s>\n\n", + PACKAGE_STRING, PACKAGE_BUGREPORT); + else +#endif + if (1) + fprintf(stderr, "%s", "\n"); + + memset(spaces, ' ', sizeof spaces); + spaces[sizeof spaces - 1] = 0; + memset(spaces_long, ' ', sizeof spaces_long); + spaces_long[sizeof spaces_long - 1] = 0; + + for (i = 0; i < OPT_MAX; ++i) { + snprintf(buf_arg, sizeof buf_arg, "--%s", options[i].arg_name); + + memset(buf_shorthelp, 0, sizeof buf_shorthelp); + if (options[i].short_help) + snprint_multilined_ljust(spaces, + options[i].short_help, + buf_shorthelp, + sizeof buf_shorthelp); + + memset(buf_help, 0, sizeof buf_help); + off = 0; + if (options[i].help) + off = snprint_multilined_ljust(spaces_long, options[i].help, + buf_help, sizeof buf_help); + + has_default = 0; + switch (options[i].type) { + case OT_L: + snprintf(value, sizeof value, "default: %lld\n", + options[i].def_value.ll); + has_default = 1; + break; + case OT_LL: + snprintf(value, sizeof value, "default: %ld\n", + options[i].def_value.l); + has_default = 1; + break; + case OT_STR: + case OT_PATH: + if (options[i].def_value.str) { + snprintf(value, sizeof value, "default: %s\n", + options[i].def_value.str); + has_default = 1; + } + break; + case OT_INVALID: + case OT_NOARG: + default: + break; + } + if (has_default) + snprint_multilined_ljust(&spaces_long[sizeof spaces_long / 2], + value, buf_help + off, sizeof buf_help - off); + + fprintf(stderr, "%16s %s" + "%s\n", buf_arg, + buf_shorthelp, buf_help); + } + fprintf(stderr, + "For example: %s \\\n" + " --redirect 0.0.0.0:2222:127.0.0.1:22222\n" + " --protocol 127.0.0.1:22222:127.0.0.1:33333\n" + " --jail 127.0.0.1:33333\n" + " will process/filter all incoming traffic\n" + " at 0.0.0.0:2222 and redirect it\n" + " to 127.0.0.1:22222 (libssh) which will redirect it finally\n" + " to 127.0.0.1:33333 (jail service).\n\n", + arg0); +} + +int parse_cmdline(int argc, char **argv) +{ + int rc, i, option, option_index; + struct option *o = (struct option *) calloc(OPT_MAX+1, sizeof *o); + + assert(o); + for (i = 0; i < OPT_MAX; ++i) { + o[i].name = options[i].arg_name; + if (options[i].def_value.ll) + o[i].has_arg = required_argument; + else + o[i].has_arg = + (options[i].type == OT_NOARG ? no_argument : required_argument); + } + + rc = 0; + while (1) { + option_index = -1; + option = getopt_long_only(argc, argv, "", o, &option_index); + + if (option_index == -1 && option != -1) { + rc = 1; + continue; + } + if (option == -1) + break; + + if (!option) { + if (!setopt_list(&options[option_index], optarg)) { + } else if (setopt(&options[option_index], optarg)) { + rc = 1; + goto error; + } + } else { + fprintf(stderr, "%s: unknown option '%c' [0x%X]\n", + argv[0], option, option); + } + } + +error: + free(o); + + if (rc) + usage(argv[0], 0); + else if (getopt_used(OPT_HELP)) { + usage(argv[0], 1); + exit(EXIT_SUCCESS); + } + + return rc; +} + +int getopt_used(opt_name on) +{ + return options[on].used; +} + +char * +getopt_str(opt_name on) +{ + char *str; + + assert(options[on].type == OT_STR || + options[on].type == OT_PATH); + assert(getopt_used(on) || options[on].def_value.str); + str = options[on].value.str_dup; + if (!str) + str = options[on].def_value.str_dup; + assert(str); + + return str; +} + +char * +getopt_strlist(opt_name on, opt_list **ol) +{ + opt_list *o; + + assert(options[on].is_list && ol); + assert(options[on].type == OT_STR || + options[on].type == OT_PATH); + assert(getopt_used(on) && !options[on].def_value.str); + + if (*ol) { + o = (*ol)->next; + } else { + o = options[on].value.list; + } + *ol = o; + + return (o ? o->value.str_dup : NULL); +} |