aboutsummaryrefslogtreecommitdiff
path: root/src/options.c
diff options
context:
space:
mode:
authorToni Uhlig <matzeton@googlemail.com>2017-12-18 23:31:09 +0100
committerToni Uhlig <matzeton@googlemail.com>2017-12-18 23:31:09 +0100
commit3c491a8bec71606b7af5f1d8c34de8e9710bbe13 (patch)
tree8d909a5a1fc72138887078c9c54b3b6514253234 /src/options.c
parent9c82c27e6326609150db837f37077774a8a5919c (diff)
ptunnel-ng:
* this is now an autotools project (added/renamed required files e.g. AUTHORS, COPYING) * removed user defined ip header (buggy; not useful anymore)
Diffstat (limited to 'src/options.c')
-rw-r--r--src/options.c504
1 files changed, 504 insertions, 0 deletions
diff --git a/src/options.c b/src/options.c
new file mode 100644
index 0000000..0c9bbe8
--- /dev/null
+++ b/src/options.c
@@ -0,0 +1,504 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "options.h"
+#include "utils.h"
+#include "ptunnel.h"
+#include "md5.h"
+
+
+struct options opts;
+
+enum option_type {
+ OPT_BOOL, OPT_DEC32, OPT_HEX32, OPT_STR
+};
+
+struct option_usage {
+ const char *short_help;
+ int required;
+ enum option_type otype;
+ union {
+ int32_t num;
+ uint32_t unum;
+ const char *str;
+ };
+ const char *long_help;
+};
+
+static const struct option_usage usage[] = {
+ /** --magic */
+ {"magic", 0, OPT_HEX32, {.unum = 0xdeadc0de},
+ "Set ptunnel magic hexadecimal number. (32-bit unsigned)\n"
+ "It is an identifier for all ICMP/UDP packets\n"
+ "and can be used to bypass Cisco IPS fingerprint scan.\n"
+ "This value has to be the same on the server and client!\n"
+ },
+ /** --proxy */
+ {"address", 1, OPT_STR, {.str = NULL},
+ "Set address of peer running packet forwarder. This causes\n"
+ "ptunnel to operate in forwarding mode - the absence of this\n"
+ "option causes ptunnel to operate in proxy mode.\n"
+ },
+ /** --listen */
+ {"port", 1, OPT_DEC32, {.unum = 2222},
+ "Set TCP listening port (only used when operating in forward mode)\n"
+ },
+ /** --remote-adr */
+ {"address", 1, OPT_STR, {.str = "127.0.0.1"},
+ "Set remote proxy destination address if client\n"
+ "Restrict to only this destination address if server\n"
+ },
+ /** --remote-port */
+ {"port", 1, OPT_DEC32, {.unum = 22},
+ "Set remote proxy destination port if client\n"
+ "Restrict to only this destination port if server\n"
+ },
+ /** --connections */
+ {"connections", 0, OPT_DEC32, {.unum = kMax_tunnels},
+ "Set maximum number of concurrent tunnels\n"
+ },
+ /** --verbosity */
+ {"level", 0, OPT_DEC32, {.num = kLog_event},
+ "Verbosity level (-1 to 4, where -1 is no output, and 4 is all output)\n"
+ "The special level 5 (or higher) includes xfer logging (lots of output)\n"
+ },
+ /** --libpcap */
+ {"interface", 0, OPT_STR, {.str = "eth0"},
+ "Enable libpcap on the given device.\n"
+ },
+ /** --logfile */
+ {"file", 0, OPT_STR, {.str = "/var/log/ptunnel.log"},
+ "Specify a file to log to, rather than printing to standard out.\n"
+ },
+ /** --statistics */
+ {NULL, 0, OPT_BOOL, {.num = 0},
+ "Client only. Enables continuous output of statistics (packet loss, etc.)\n"
+ },
+ /** --passwd */
+ {"password", 0, OPT_STR, {.str = NULL},
+ "Set password (must be same on client and proxy)\n"
+ "If no password is set, you will be asked during runtime.\n"
+ },
+ /** --udp */
+ {NULL, 0, OPT_BOOL, {.num = 0},
+ "Toggle use of UDP instead of ICMP. Proxy will listen on port 53 (must be root).\n"
+ },
+ /** --unprivileged */
+ {NULL, 0, OPT_BOOL, {.num = 0},
+ "Run proxy in unprivileged mode. This causes the proxy to forward\n"
+ "packets using standard echo requests, instead of crafting custom echo replies.\n"
+ "Unprivileged mode will only work on some systems, and is in general less reliable\n"
+ "than running in privileged mode.\n"
+ },
+ /** --base64 */
+ {NULL, 0, OPT_BOOL, {.num = 0},
+ "Base64 encode/decode all outoging/incoming packets."},
+#ifndef WIN32
+ /** --daemon */
+ {"pidfile", 0, OPT_STR, {.str = "/run/ptunnel.pid"},
+ "Run in background, the PID will be written in the file supplied as argument\n"
+ },
+ /** --syslog */
+ {NULL, 0, OPT_BOOL, {.num = 0},
+ "Output debug to syslog instead of standard out.\n"
+ },
+ /** --user */
+ {"user", 0, OPT_STR, {.str = "nobody"},
+ "When started in privileged mode, drop down to user's rights as soon as possible\n"
+ },
+ /** --group */
+ {"group", 0, OPT_STR, {.str = "nogroup"},
+ "When started in privileged mode, drop down to group's rights as soon as possible\n"
+ },
+ /** --chroot */
+ {"directory", 0, OPT_STR, {.str = "/var/lib/ptunnel"},
+ "When started in privileged mode, restrict file access to the specified directory\n"
+ },
+#endif
+ /** --setcon */
+ {NULL, 0, OPT_STR, {.num = 0},
+ "Set SELinux context when all there is left to do are network I/O operations\n"
+ "To combine with -chroot you will have to `mount --bind /proc /chrootdir/proc`\n"
+ },
+ /** --help */
+ {"help", 0, OPT_STR, {.str = NULL}, "this\n"},
+ {NULL,0,OPT_BOOL,{.unum=0},NULL}
+};
+
+static struct option long_options[] = {
+ {"magic", required_argument, 0, 'm'},
+ {"proxy", required_argument, 0, 'p'},
+ {"listen", required_argument, 0, 'l'},
+ {"remote-adr", optional_argument, 0, 'r'},
+ {"remote-port", optional_argument, 0, 'R'},
+ {"connections", required_argument, 0, 'c'},
+ {"verbosity", required_argument, 0, 'v'},
+ {"libpcap", required_argument, 0, 'a'},
+ {"logfile", optional_argument, 0, 'o'},
+ {"statistics", no_argument, 0, 's'},
+ {"passwd", required_argument, 0, 'x'},
+ {"udp", no_argument, &opts.udp, 1 },
+ {"unprivileged", no_argument, &opts.unprivileged, 1 },
+ {"base64", no_argument, &opts.base64, 1 },
+#ifndef WIN32
+ {"daemon", optional_argument, 0, 'd'},
+ {"syslog", no_argument, 0, 'S'},
+ {"user", optional_argument, 0, 'u'},
+ {"group", optional_argument, 0, 'g'},
+ {"chroot", optional_argument, 0, 'C'},
+#endif
+ {"setcon", no_argument, 0, 'e'},
+ {"help", no_argument, 0, 'h'},
+ {NULL,0,0,0}
+};
+
+
+static const void *get_default_optval(enum option_type opttype, const char *optname) {
+ for (unsigned i = 0; i < ARRAY_SIZE(long_options); ++i) {
+ if (strncmp(long_options[i].name, optname, strlen(long_options[i].name)) == 0) {
+ assert(usage[i].otype == opttype);
+ return &usage[i].str;
+ }
+ }
+ assert(NULL);
+ return NULL;
+}
+
+static void set_options_defaults(void) {
+#ifndef WIN32
+ char *tmp;
+ struct passwd *pwnam;
+ struct group *grnam;
+#endif
+
+ memset(&opts, 0, sizeof(opts));
+ opts.magic = *(uint32_t *) get_default_optval(OPT_HEX32, "magic");
+ opts.mode = kMode_proxy;
+ opts.tcp_listen_port = *(uint32_t *) get_default_optval(OPT_DEC32, "listen");
+ opts.given_dst_hostname = strdup(*(char **) get_default_optval(OPT_STR, "remote-adr"));
+ opts.given_dst_port = *(uint32_t *) get_default_optval(OPT_DEC32, "remote-port");
+ opts.max_tunnels = *(uint32_t *) get_default_optval(OPT_DEC32, "connections");
+ opts.log_level = *(int *) get_default_optval(OPT_DEC32, "verbosity");
+#ifdef HAVE_PCAP
+ opts.pcap_device = strdup(*(char **)get_default_optval(OPT_STR, "libpcap"));
+#endif
+ opts.log_path = strdup(*(char **)get_default_optval(OPT_STR, "logfile"));
+ opts.log_file = stdout;
+ opts.print_stats = *(int *) get_default_optval(OPT_BOOL, "statistics");
+ opts.udp = *(int *) get_default_optval(OPT_BOOL, "udp");
+ opts.unprivileged = *(int *) get_default_optval(OPT_BOOL, "unprivileged");
+#ifndef WIN32
+ opts.pid_path = strdup(*(char **)get_default_optval(OPT_STR, "daemon"));
+
+ errno = 0;
+ tmp = *(char **) get_default_optval(OPT_STR, "user");
+ if (NULL == (pwnam = getpwnam(tmp)))
+ pt_log(kLog_error, "%s: %s\n", tmp, errno ? strerror(errno) : "unknown user");
+ else {
+ opts.uid = pwnam->pw_uid;
+ if (!opts.gid)
+ opts.gid = pwnam->pw_gid;
+ }
+
+ errno = 0;
+ tmp = *(char **) get_default_optval(OPT_STR, "group");
+ if (NULL == (grnam = getgrnam(tmp)))
+ pt_log(kLog_error, "%s: %s\n", tmp, errno ? strerror(errno) : "unknown group");
+ else
+ opts.gid = grnam->gr_gid;
+
+ opts.root_dir = strdup(*(char **)get_default_optval(OPT_STR, "chroot"));
+#endif
+}
+
+static void print_multiline(const char *prefix, const char *multiline) {
+ const char sep[] = "\n";
+ const char *start, *end;
+
+ start = multiline;
+ do {
+ if (start) {
+ end = strstr(start, sep);
+ if (end) {
+ printf("%s%.*s\n", prefix, (int)(end-start), start);
+ start = end + strlen(sep);
+ }
+ }
+ } while (start && end);
+}
+
+static void print_long_help(unsigned index, int required_state) {
+ const char spaces[] = " ";
+
+ if (usage[index].required != required_state)
+ return;
+ if (!long_options[index].name)
+ return;
+
+ if (isalpha(long_options[index].val)) {
+ printf("%.*s-%c --%s\n", 4, spaces, long_options[index].val, long_options[index].name);
+ } else {
+ printf("%.*s--%s\n", 4, spaces, long_options[index].name);
+ }
+
+ if (usage[index].long_help) {
+ print_multiline(&spaces[4], usage[index].long_help);
+ }
+
+ switch (usage[index].otype) {
+ case OPT_BOOL:
+ break;
+ case OPT_DEC32:
+ printf("%s(default: %d)\n", spaces, usage[index].num);
+ break;
+ case OPT_HEX32:
+ printf("%s(default: 0x%X)\n", spaces, usage[index].unum);
+ break;
+ case OPT_STR:
+ if (usage[index].str)
+ printf("%s(default: %s)\n", spaces, usage[index].str);
+ break;
+ }
+}
+
+static void print_short_help(unsigned index, int required_state) {
+ const char *ob = (required_state == 0 ? "[" : "");
+ const char *cb = (required_state == 0 ? "]" : "");
+
+ if (usage[index].required != required_state)
+ return;
+ if (!long_options[index].name)
+ return;
+
+ if (!usage[index].short_help && isalpha(long_options[index].val)) {
+ printf(" %s-%c%s", ob, long_options[index].val, cb);
+ }
+ else if (!usage[index].short_help) {
+ printf(" %s--%s%s", ob, long_options[index].name, cb);
+ }
+ else if (isalpha(long_options[index].val)) {
+ printf(" %s-%c <%s>%s", ob, long_options[index].val, usage[index].short_help, cb);
+ }
+ else {
+ printf(" %s--%s <%s>%s", ob, long_options[index].name, usage[index].short_help, cb);
+ }
+}
+
+void print_usage(const char *arg0) {
+ unsigned i;
+
+ printf("ptunnel-ng v%d.%.2d\n\nUsage: %s", kMajor_version, kMinor_version, arg0);
+ /* print (short)help argument line */
+ for (i = 0; i < ARRAY_SIZE(usage); ++i) {
+ print_short_help(i, 1);
+ }
+ for (i = 0; i < ARRAY_SIZE(usage); ++i) {
+ print_short_help(i, 0);
+ }
+
+ printf("%s", "\n\n");
+ /* print (long)help lines */
+ for (i = 0; i < ARRAY_SIZE(usage); ++i) {
+ print_long_help(i, 1);
+ }
+ for (i = 0; i < ARRAY_SIZE(usage); ++i) {
+ print_long_help(i, 0);
+ }
+}
+
+int parse_options(int argc, char **argv) {
+ int c = 0, optind = -1, has_logfile = 0;
+ struct hostent *host_ent;
+ md5_state_t state;
+#ifndef WIN32
+ struct passwd *pwnam;
+ struct group *grnam;
+#endif
+ FILE *tmp_log;
+
+ assert( ARRAY_SIZE(long_options) == ARRAY_SIZE(usage) );
+
+ /* set defaults */
+ set_options_defaults();
+
+ /* parse command line arguments */
+ while (1) {
+ c = getopt_long(argc, argv, "m:p:l:r::R::c:v:a::o::sd::Sx:u::g::C::eh", &long_options[0], &optind);
+ if (c == -1) break;
+
+ switch (c) {
+ case 'm':
+ opts.magic = strtoul(optarg, NULL, 16);
+ break;
+ case 'p':
+ opts.mode = kMode_forward;
+ if (opts.given_proxy_hostname)
+ free(opts.given_proxy_hostname);
+ opts.given_proxy_hostname = strdup(optarg);
+ break;
+ case 'l':
+ if (optarg)
+ opts.tcp_listen_port = strtoul(optarg, NULL, 10);
+ break;
+ case 'r':
+ if (!optarg)
+ break;
+ if (opts.given_dst_hostname)
+ free(opts.given_dst_hostname);
+ opts.given_dst_hostname = strdup(optarg);
+ break;
+ case 'R':
+ if (optarg)
+ opts.given_dst_port = strtoul(optarg, NULL, 10);
+ break;
+ case 'c':
+ opts.max_tunnels = strtoul(optarg, NULL,10);
+ if (opts.max_tunnels > kMax_tunnels)
+ opts.max_tunnels = kMax_tunnels;
+ break;
+ case 'v':
+ opts.log_level = strtol(optarg, NULL, 10);
+ break;
+ case 'a':
+#ifdef HAVE_PCAP
+ opts.pcap = 1;
+ if (!optarg)
+ break;
+ if (opts.pcap_device)
+ free(opts.pcap_device);
+ opts.pcap_device = strdup(optarg);
+ break;
+#else
+ pt_log(kLog_error, "-%c: feature not supported\n", c);
+ exit(1);
+#endif
+ case 'o':
+ has_logfile = 1;
+ if (!optarg)
+ break;
+ if (opts.log_path)
+ free(opts.log_path);
+ opts.log_path = strdup(optarg);
+ break;
+ case 's':
+ opts.print_stats = !opts.print_stats;
+ break;
+ case 'x':
+ if (opts.password)
+ free(opts.password);
+ opts.password = strdup(optarg);
+ pt_log(kLog_debug, "Password set - unauthenicated connections will be refused.\n");
+ // Compute the password digest
+ md5_init(&state);
+ md5_append(&state, (md5_byte_t*)optarg, strlen(opts.password));
+ md5_finish(&state, &opts.password_digest[0]);
+ // Hide the password in process listing
+ memset(optarg, '*', strlen(optarg));
+ break;
+#ifndef WIN32
+ case 'd':
+ opts.daemonize = true;
+ if (!optarg)
+ break;
+ if (opts.pid_path)
+ free(opts.pid_path);
+ opts.pid_path = strdup(optarg);
+ break;
+ case 'S':
+ opts.use_syslog = 1;
+ break;
+ case 'u':
+ if (!optarg)
+ break;
+ errno = 0;
+ if (NULL == (pwnam = getpwnam(optarg))) {
+ pt_log(kLog_error, "%s: %s\n", optarg, errno ? strerror(errno) : "unknown user");
+ exit(1);
+ }
+ opts.uid = pwnam->pw_uid;
+ if (!opts.gid)
+ opts.gid = pwnam->pw_gid;
+ break;
+ case 'g':
+ if (!optarg)
+ break;
+ errno = 0;
+ if (NULL == (grnam = getgrnam(optarg))) {
+ pt_log(kLog_error, "%s: %s\n", optarg, errno ? strerror(errno) : "unknown group");
+ exit(1);
+ }
+ opts.gid = grnam->gr_gid;
+ break;
+ case 'C':
+ opts.chroot = 1;
+ if (!optarg)
+ break;
+ if (opts.root_dir)
+ free(opts.root_dir);
+ opts.root_dir = strdup(optarg);
+ break;
+#else
+ case 'd':
+ case 'S':
+ case 'u':
+ case 'g':
+ case 't':
+ pt_log(kLog_error, "-%c: feature not supported\n", c);
+ exit(1);
+#endif
+ case 'e':
+#ifdef HAVE_SELINUX
+ if (opts.selinux_context)
+ free(opts.selinux_context);
+ opts.selinux_context = strdup(optarg);
+ break;
+#else
+ pt_log(kLog_error, "-%c: feature not supported\n", c);
+ exit(1);
+#endif
+ case 'h':
+ print_usage(argv[0]);
+ _exit(EXIT_SUCCESS);
+ case 0: /* long opt only */
+ default:
+ break;
+ }
+ }
+
+ if (opts.given_proxy_hostname) {
+ if (NULL == (host_ent = gethostbyname(opts.given_proxy_hostname))) {
+ pt_log(kLog_error, "Failed to look up %s as proxy address\n", opts.given_proxy_hostname);
+ return 1;
+ }
+ opts.given_proxy_ip = *(uint32_t*)host_ent->h_addr_list[0];
+ }
+
+ if (NULL == (host_ent = gethostbyname(opts.given_dst_hostname))) {
+ pt_log(kLog_error, "Failed to look up %s as destination address\n", opts.given_dst_hostname);
+ return 1;
+ }
+ opts.given_dst_ip = *(uint32_t*)host_ent->h_addr_list[0];
+
+ if (NULL == (opts.pid_file = fopen(opts.pid_path, "w")))
+ pt_log(kLog_error, "Failed to open pidfile: \"%s\", Cause: %s\n", opts.pid_path, strerror(errno));
+
+ if (has_logfile && opts.log_path) {
+ pt_log(kLog_info, "Open Logfile: \"%s\"\n", opts.log_path);
+ tmp_log = fopen(opts.log_path, "a");
+ if (!tmp_log) {
+ pt_log(kLog_error, "Failed to open log file: \"%s\", Cause: %s\n", opts.log_path, strerror(errno));
+ pt_log(kLog_error, "Reverting log to standard out.\n");
+ } else opts.log_file = tmp_log;
+ }
+
+ if (opts.base64 != 0) {
+ pt_log(kLog_debug, "Base64 enabled.");
+ }
+
+ return 0;
+}