summaryrefslogtreecommitdiffstats
path: root/ncat/ncat_main.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--ncat/ncat_main.c1091
1 files changed, 1091 insertions, 0 deletions
diff --git a/ncat/ncat_main.c b/ncat/ncat_main.c
new file mode 100644
index 0000000..501c5a3
--- /dev/null
+++ b/ncat/ncat_main.c
@@ -0,0 +1,1091 @@
+/***************************************************************************
+ * ncat_main.c -- main function: option parsing and checking, dispatching *
+ * to mode-specific functions. *
+ ***********************IMPORTANT NMAP LICENSE TERMS************************
+ *
+ * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+ * Project"). Nmap is also a registered trademark of the Nmap Project.
+ *
+ * This program is distributed under the terms of the Nmap Public Source
+ * License (NPSL). The exact license text applying to a particular Nmap
+ * release or source code control revision is contained in the LICENSE
+ * file distributed with that version of Nmap or source code control
+ * revision. More Nmap copyright/legal information is available from
+ * https://nmap.org/book/man-legal.html, and further information on the
+ * NPSL license itself can be found at https://nmap.org/npsl/ . This
+ * header summarizes some key points from the Nmap license, but is no
+ * substitute for the actual license text.
+ *
+ * Nmap is generally free for end users to download and use themselves,
+ * including commercial use. It is available from https://nmap.org.
+ *
+ * The Nmap license generally prohibits companies from using and
+ * redistributing Nmap in commercial products, but we sell a special Nmap
+ * OEM Edition with a more permissive license and special features for
+ * this purpose. See https://nmap.org/oem/
+ *
+ * If you have received a written Nmap license agreement or contract
+ * stating terms other than these (such as an Nmap OEM license), you may
+ * choose to use and redistribute Nmap under those terms instead.
+ *
+ * The official Nmap Windows builds include the Npcap software
+ * (https://npcap.com) for packet capture and transmission. It is under
+ * separate license terms which forbid redistribution without special
+ * permission. So the official Nmap Windows builds may not be redistributed
+ * without special permission (such as an Nmap OEM license).
+ *
+ * Source is provided to this software because we believe users have a
+ * right to know exactly what a program is going to do before they run it.
+ * This also allows you to audit the software for security holes.
+ *
+ * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+ * new features. You are highly encouraged to submit your changes as a Github PR
+ * or by email to the dev@nmap.org mailing list for possible incorporation into
+ * the main distribution. Unless you specify otherwise, it is understood that
+ * you are offering us very broad rights to use your submissions as described in
+ * the Nmap Public Source License Contributor Agreement. This is important
+ * because we fund the project by selling licenses with various terms, and also
+ * because the inability to relicense code has caused devastating problems for
+ * other Free Software projects (such as KDE and NASM).
+ *
+ * The free version of Nmap 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. Warranties,
+ * indemnification and commercial support are all available through the
+ * Npcap OEM program--see https://nmap.org/oem/
+ *
+ ***************************************************************************/
+
+/* $Id$ */
+
+#include "nsock.h"
+#include "ncat.h"
+#include "util.h"
+#include "sys_wrap.h"
+
+#include <getopt.h>
+
+#ifndef WIN32
+#include <unistd.h>
+#endif
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#ifndef WIN32
+#include <netdb.h>
+#endif
+#include <fcntl.h>
+
+#ifdef HAVE_OPENSSL
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#endif
+
+#ifdef HAVE_LUA
+#include "ncat_lua.h"
+#endif
+
+static int ncat_connect_mode(void);
+static int ncat_listen_mode(void);
+
+/* Parses a port number */
+static unsigned int parseport(char *str, unsigned int maxport, char *msg)
+{
+ unsigned long port;
+ char *next;
+ errno = 0;
+ port = strtoul(str, &next, 10);
+ if (errno || *next || (maxport && port > maxport))
+ bye("Invalid %s number \"%s\".", msg, str);
+ return (unsigned int) port;
+}
+
+/* Parses proxy address/port combo */
+static size_t parseproxy(char *str, struct sockaddr_storage *ss,
+ size_t *sslen, unsigned short *portno)
+{
+ char *p = str;
+ int rc;
+
+ if (*p == '[') {
+ p = strchr(p, ']');
+ if (p == NULL)
+ bye("Invalid proxy IPv6 address \"%s\".", str);
+ ++str;
+ *p++ = '\0';
+ }
+
+ p = strchr(p, ':');
+ if (p != NULL && strchr(p + 1, ':') == NULL) {
+ *p++ = '\0';
+ *portno = (unsigned short) parseport(p, 0xFFFF, "proxy port");
+ }
+
+ rc = resolve(str, *portno, ss, sslen, o.af);
+ if (rc != 0) {
+ loguser("Could not resolve proxy \"%s\": %s.\n", str, gai_strerror(rc));
+ if (o.af == AF_INET6)
+ loguser("Did you specify the port number? It's required for IPv6.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return *sslen;
+}
+
+static int parse_timespec (const char *const tspec, const char *const optname)
+{
+ const long l = tval2msecs(tspec);
+ if (l <= 0 || l > INT_MAX)
+ bye("Invalid %s \"%s\" (must be greater than 0 and less than %ds).",
+ optname, tspec, INT_MAX / 1000);
+ if (l >= 100 * 1000 && tval_unit(tspec) == NULL)
+ bye("Since April 2010, the default unit for %s is seconds, so your "
+ "time of \"%s\" is %.1f minutes. Use \"%sms\" for %s milliseconds.",
+ optname, optarg, l / 1000.0 / 60, optarg, optarg);
+ return (int)l;
+}
+
+/* These functions implement a simple linked list to hold allow/deny
+ specifications until the end of option parsing. */
+struct host_list_node {
+ /* If false, then spec is the name of a file containing host patterns. */
+ int is_filename;
+ char *spec;
+ struct host_list_node *next;
+};
+
+static void host_list_add_spec(struct host_list_node **list, char *spec)
+{
+ struct host_list_node *node = (struct host_list_node *) safe_malloc(sizeof(*node));
+ node->is_filename = 0;
+ node->spec = spec;
+ node->next = *list;
+ *list = node;
+}
+
+static void host_list_add_filename(struct host_list_node **list, char *filename)
+{
+ struct host_list_node *node = (struct host_list_node *) safe_malloc(sizeof(*node));
+ node->is_filename = 1;
+ node->spec = filename;
+ node->next = *list;
+ *list = node;
+}
+
+static void host_list_free(struct host_list_node *list)
+{
+ struct host_list_node *next;
+ for ( ; list != NULL; list = next) {
+ next = list->next;
+ free(list);
+ }
+}
+
+static void host_list_to_set(struct addrset *set, struct host_list_node *list)
+{
+ struct host_list_node *node;
+
+ for (node = list; node != NULL; node = node->next) {
+ if (node->is_filename) {
+ FILE *fd;
+
+ fd = fopen(node->spec, "r");
+ if (fd == NULL)
+ bye("can't open %s: %s.", node->spec, strerror(errno));
+ if (!addrset_add_file(set, fd, o.af, !o.nodns))
+ bye("error in hosts file %s.", node->spec);
+ fclose(fd);
+ } else {
+ char *spec, *commalist;
+
+ commalist = node->spec;
+ while ((spec = strtok(commalist, ",")) != NULL) {
+ commalist = NULL;
+ if (!addrset_add_spec(set, spec, o.af, !o.nodns))
+ bye("error in host specification \"%s\".", node->spec);
+ }
+ }
+ }
+}
+
+static void print_banner(void)
+{
+ loguser("Version %s ( %s )\n", NCAT_VERSION, NCAT_URL);
+}
+
+int main(int argc, char *argv[])
+{
+ /* We have to buffer the lists of hosts to allow and deny until after option
+ parsing is done. Adding hosts to an addrset can require name resolution,
+ which may differ as a result of options like -n and -6. */
+ struct host_list_node *allow_host_list = NULL;
+ struct host_list_node *deny_host_list = NULL;
+
+ unsigned short proxyport;
+ /* vsock ports are 32 bits, so port variables must be at least that wide. */
+ unsigned int max_port = 65535;
+ long long int srcport = -1;
+ char *source = NULL;
+
+ struct option long_options[] = {
+ {"4", no_argument, NULL, '4'},
+ {"6", no_argument, NULL, '6'},
+#if HAVE_SYS_UN_H
+ {"unixsock", no_argument, NULL, 'U'},
+#endif
+#if HAVE_LINUX_VM_SOCKETS_H
+ {"vsock", no_argument, NULL, 0},
+#endif
+ {"crlf", no_argument, NULL, 'C'},
+ {"g", required_argument, NULL, 'g'},
+ {"G", required_argument, NULL, 'G'},
+ {"exec", required_argument, NULL, 'e'},
+ {"sh-exec", required_argument, NULL, 'c'},
+#ifdef HAVE_LUA
+ {"lua-exec", required_argument, NULL, 0},
+ {"lua-exec-internal",required_argument, NULL, 0},
+#endif
+ {"max-conns", required_argument, NULL, 'm'},
+ {"help", no_argument, NULL, 'h'},
+ {"delay", required_argument, NULL, 'd'},
+ {"listen", no_argument, NULL, 'l'},
+ {"output", required_argument, NULL, 'o'},
+ {"hex-dump", required_argument, NULL, 'x'},
+ {"append-output", no_argument, NULL, 0},
+ {"idle-timeout", required_argument, NULL, 'i'},
+ {"keep-open", no_argument, NULL, 'k'},
+ {"recv-only", no_argument, &o.recvonly, 1},
+ {"source-port", required_argument, NULL, 'p'},
+ {"source", required_argument, NULL, 's'},
+ {"send-only", no_argument, &o.sendonly, 1},
+ {"no-shutdown", no_argument, &o.noshutdown,1},
+ {"broker", no_argument, NULL, 0},
+ {"chat", no_argument, NULL, 0},
+ {"talk", no_argument, NULL, 0},
+ {"deny", required_argument, NULL, 0},
+ {"denyfile", required_argument, NULL, 0},
+ {"allow", required_argument, NULL, 0},
+ {"allowfile", required_argument, NULL, 0},
+ {"telnet", no_argument, NULL, 't'},
+ {"udp", no_argument, NULL, 'u'},
+ {"sctp", no_argument, NULL, 0},
+ {"version", no_argument, NULL, 0},
+ {"verbose", no_argument, NULL, 'v'},
+ {"wait", required_argument, NULL, 'w'},
+ {"nodns", no_argument, NULL, 'n'},
+ {"proxy", required_argument, NULL, 0},
+ {"proxy-type", required_argument, NULL, 0},
+ {"proxy-auth", required_argument, NULL, 0},
+ {"proxy-dns", required_argument, NULL, 0},
+ {"nsock-engine", required_argument, NULL, 0},
+ {"test", no_argument, NULL, 0},
+ {"ssl", no_argument, &o.ssl, 1},
+#ifdef HAVE_OPENSSL
+ {"ssl-cert", required_argument, NULL, 0},
+ {"ssl-key", required_argument, NULL, 0},
+ {"ssl-verify", no_argument, NULL, 0},
+ {"ssl-trustfile", required_argument, NULL, 0},
+ {"ssl-ciphers", required_argument, NULL, 0},
+ {"ssl-servername", required_argument, NULL, 0},
+ {"ssl-alpn", required_argument, NULL, 0},
+#else
+ {"ssl-cert", optional_argument, NULL, 0},
+ {"ssl-key", optional_argument, NULL, 0},
+ {"ssl-verify", no_argument, NULL, 0},
+ {"ssl-trustfile", optional_argument, NULL, 0},
+ {"ssl-ciphers", optional_argument, NULL, 0},
+ {"ssl-alpn", optional_argument, NULL, 0},
+#endif
+ {0, 0, 0, 0}
+ };
+
+ gettimeofday(&start_time, NULL);
+ /* Set default options. */
+ options_init();
+
+#ifdef WIN32
+ windows_init();
+#endif
+
+ while (1) {
+ /* handle command line arguments */
+ int option_index;
+ int c = getopt_long(argc, argv, "46UCc:e:g:G:i:km:hp:d:lo:x:ts:uvw:nz",
+ long_options, &option_index);
+
+ /* That's the end of the options. */
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case '4':
+ o.af = AF_INET;
+ break;
+ case '6':
+#ifdef HAVE_IPV6
+ o.af = AF_INET6;
+#else
+ bye("-6 chosen when IPv6 wasn't compiled in.");
+#endif
+ break;
+#if HAVE_SYS_UN_H
+ case 'U':
+ o.af = AF_UNIX;
+ break;
+#endif
+ case 'C':
+ o.crlf = 1;
+ break;
+ case 'c':
+ if (o.cmdexec != NULL)
+ bye("Only one of --exec, --sh-exec, and --lua-exec is allowed.");
+ o.cmdexec = optarg;
+ o.execmode = EXEC_SHELL;
+ break;
+ case 'e':
+ if (o.cmdexec != NULL)
+ bye("Only one of --exec, --sh-exec, and --lua-exec is allowed.");
+ o.cmdexec = optarg;
+ o.execmode = EXEC_PLAIN;
+ break;
+ case 'g': {
+ char *from = optarg;
+ char *a = NULL;
+ while (o.numsrcrtes < 8 && (a = strtok(from, ",")))
+ {
+ union sockaddr_u addr;
+ size_t sslen;
+ int rc;
+ from = NULL;
+
+ rc = resolve(a, 0, &addr.storage, &sslen, AF_INET);
+ if (rc != 0) {
+ bye("Sorry, could not resolve source route hop \"%s\": %s.",
+ a, gai_strerror(rc));
+ }
+ o.srcrtes[o.numsrcrtes++] = addr.in.sin_addr;
+ }
+ if (strtok(from, ","))
+ bye("Sorry, you gave too many source route hops.");
+ break;
+ }
+ case 'G':
+ o.srcrteptr = atoi(optarg);
+ if (o.srcrteptr < 4 || (o.srcrteptr % 4) || o.srcrteptr > 28)
+ bye("Invalid source-route hop pointer %d.", o.srcrteptr);
+ break;
+ case 'k':
+ o.keepopen = 1;
+ break;
+ case 'm':
+ o.conn_limit = atoi(optarg);
+ break;
+ case 'd':
+ o.linedelay = parse_timespec(optarg, "-d delay");
+ break;
+ case 'o':
+ o.normlog = optarg;
+ break;
+ case 'x':
+ o.hexlog = optarg;
+ break;
+ case 'p':
+ srcport = parseport(optarg, 0, "source port");
+ break;
+ case 'i':
+ o.idletimeout = parse_timespec(optarg, "-i timeout");
+ break;
+ case 's':
+ source = optarg;
+ break;
+ case 'l':
+ o.listen = 1;
+ break;
+ case 'u':
+ o.proto = IPPROTO_UDP;
+ break;
+ case 'v':
+ /* One -v activates verbose, after that it's debugging. */
+ if (o.verbose == 0)
+ o.verbose++;
+ else
+ o.debug++;
+ break;
+ case 'n':
+ o.nodns = 1;
+ break;
+ case 'w':
+ o.conntimeout = parse_timespec(optarg, "-w timeout");
+ break;
+ case 't':
+ o.telnet = 1;
+ break;
+ case 'z':
+ o.zerobyte = 1;
+ break;
+ case 0:
+ if (strcmp(long_options[option_index].name, "version") == 0) {
+ print_banner();
+ exit(EXIT_SUCCESS);
+ } else if (strcmp(long_options[option_index].name, "proxy") == 0) {
+ if (o.proxyaddr)
+ bye("You can't specify more than one --proxy.");
+ o.proxyaddr = optarg;
+ } else if (strcmp(long_options[option_index].name, "proxy-type") == 0) {
+ if (o.proxytype)
+ bye("You can't specify more than one --proxy-type.");
+ o.proxytype = optarg;
+ } else if (strcmp(long_options[option_index].name, "proxy-auth") == 0) {
+ if (o.proxy_auth)
+ bye("You can't specify more than one --proxy-auth.");
+ o.proxy_auth = optarg;
+ } else if (strcmp(long_options[option_index].name, "proxy-dns") == 0) {
+ if (strcmp(optarg, "none") == 0)
+ o.proxydns = 0;
+ else if (strcmp(optarg, "local") == 0)
+ o.proxydns = PROXYDNS_LOCAL;
+ else if (strcmp(optarg, "remote") == 0)
+ o.proxydns = PROXYDNS_REMOTE;
+ else if (strcmp(optarg, "both") == 0)
+ o.proxydns = PROXYDNS_LOCAL | PROXYDNS_REMOTE;
+ else
+ bye("Invalid proxy DNS type.");
+ } else if (strcmp(long_options[option_index].name, "nsock-engine") == 0) {
+ if (nsock_set_default_engine(optarg) < 0)
+ bye("Unknown or non-available engine: %s.", optarg);
+ o.nsock_engine = 1;
+ } else if (strcmp(long_options[option_index].name, "test") == 0) {
+ o.test = 1;
+ } else if (strcmp(long_options[option_index].name, "broker") == 0) {
+ o.broker = 1;
+ /* --broker implies --listen. */
+ o.listen = 1;
+ } else if (strcmp(long_options[option_index].name, "chat") == 0
+ || strcmp(long_options[option_index].name, "talk") == 0) {
+ /* --talk is an older name for --chat. */
+ o.chat = 1;
+ /* --chat implies --broker. */
+ o.broker = 1;
+ } else if (strcmp(long_options[option_index].name, "allow") == 0) {
+ o.allow = 1;
+ host_list_add_spec(&allow_host_list, optarg);
+ } else if (strcmp(long_options[option_index].name, "allowfile") == 0) {
+ o.allow = 1;
+ host_list_add_filename(&allow_host_list, optarg);
+ } else if (strcmp(long_options[option_index].name, "deny") == 0) {
+ host_list_add_spec(&deny_host_list, optarg);
+ } else if (strcmp(long_options[option_index].name, "denyfile") == 0) {
+ host_list_add_filename(&deny_host_list, optarg);
+ } else if (strcmp(long_options[option_index].name, "append-output") == 0) {
+ o.append = 1;
+ } else if (strcmp(long_options[option_index].name, "sctp") == 0) {
+ o.proto = IPPROTO_SCTP;
+ }
+#ifdef HAVE_OPENSSL
+ else if (strcmp(long_options[option_index].name, "ssl-cert") == 0) {
+ o.ssl = 1;
+ o.sslcert = Strdup(optarg);
+ } else if (strcmp(long_options[option_index].name, "ssl-key") == 0) {
+ o.ssl = 1;
+ o.sslkey = Strdup(optarg);
+ } else if (strcmp(long_options[option_index].name, "ssl-verify") == 0) {
+ o.sslverify = 1;
+ o.ssl = 1;
+ } else if (strcmp(long_options[option_index].name, "ssl-trustfile") == 0) {
+ o.ssl = 1;
+ if (o.ssltrustfile != NULL)
+ bye("The --ssl-trustfile option may be given only once.");
+ o.ssltrustfile = Strdup(optarg);
+ /* If they list a trustfile assume they want certificate
+ verification. */
+ o.sslverify = 1;
+ } else if (strcmp(long_options[option_index].name, "ssl-ciphers") == 0) {
+ o.ssl = 1;
+ o.sslciphers = Strdup(optarg);
+ } else if (strcmp(long_options[option_index].name, "ssl-servername") == 0) {
+ o.ssl = 1;
+ o.sslservername = Strdup(optarg);
+ }
+#ifdef HAVE_ALPN_SUPPORT
+ else if (strcmp(long_options[option_index].name, "ssl-alpn") == 0) {
+ o.ssl = 1;
+ o.sslalpn = Strdup(optarg);
+ }
+#else
+ else if (strcmp(long_options[option_index].name, "ssl-alpn") == 0) {
+ bye("OpenSSL does not have ALPN support compiled in. The --ssl-alpn option cannot be chosen.");
+ }
+#endif
+#else
+ else if (strcmp(long_options[option_index].name, "ssl-cert") == 0) {
+ bye("OpenSSL isn't compiled in. The --ssl-cert option cannot be chosen.");
+ } else if (strcmp(long_options[option_index].name, "ssl-key") == 0) {
+ bye("OpenSSL isn't compiled in. The --ssl-key option cannot be chosen.");
+ } else if (strcmp(long_options[option_index].name, "ssl-verify") == 0) {
+ bye("OpenSSL isn't compiled in. The --ssl-verify option cannot be chosen.");
+ } else if (strcmp(long_options[option_index].name, "ssl-trustfile") == 0) {
+ bye("OpenSSL isn't compiled in. The --ssl-trustfile option cannot be chosen.");
+ } else if (strcmp(long_options[option_index].name, "ssl-ciphers") == 0) {
+ bye("OpenSSL isn't compiled in. The --ssl-ciphers option cannot be chosen.");
+ } else if (strcmp(long_options[option_index].name, "ssl-servername") == 0) {
+ bye("OpenSSL isn't compiled in. The --ssl-servername option cannot be chosen.");
+ } else if (strcmp(long_options[option_index].name, "ssl-alpn") == 0) {
+ bye("OpenSSL isn't compiled in. The --ssl-alpn option cannot be chosen.");
+ }
+#endif
+#ifdef HAVE_LUA
+ else if (strcmp(long_options[option_index].name, "lua-exec") == 0) {
+ if (o.cmdexec != NULL)
+ bye("Only one of --exec, --sh-exec, and --lua-exec is allowed.");
+ o.cmdexec = optarg;
+ o.execmode = EXEC_LUA;
+ } else if (strcmp(long_options[option_index].name, "lua-exec-internal") == 0) {
+ /* This command-line switch is undocumented on purpose. Do NOT use it
+ explicitly as its behavior might differ between Ncat releases.
+
+ Its goal is to switch the Ncat process to the Lua interpreter state
+ so that its standard output and input can be redirected to
+ particular connection's streams. Although it is implemented by
+ forking in POSIX builds, Windows does not have the fork() system
+ call and thus requires this workaround. More info here:
+ http://seclists.org/nmap-dev/2013/q2/492 */
+#ifdef WIN32
+ if (o.debug)
+ logdebug("Enabling binary stdout for the Lua output.\n");
+ int result = _setmode(_fileno(stdout), _O_BINARY);
+ if (result == -1)
+ perror("Cannot set mode");
+#endif
+ ncat_assert(argc == 3);
+ o.cmdexec = argv[2];
+ lua_setup();
+ lua_run();
+ }
+#endif
+#if HAVE_LINUX_VM_SOCKETS_H
+ else if (strcmp(long_options[option_index].name, "vsock") == 0) {
+ o.af = AF_VSOCK;
+ }
+#endif
+ break;
+ case 'h':
+ printf("%s %s ( %s )\n", NCAT_NAME, NCAT_VERSION, NCAT_URL);
+ printf(
+"Usage: ncat [options] [hostname] [port]\n"
+"\n"
+"Options taking a time assume seconds. Append 'ms' for milliseconds,\n"
+"'s' for seconds, 'm' for minutes, or 'h' for hours (e.g. 500ms).\n"
+" -4 Use IPv4 only\n"
+" -6 Use IPv6 only\n"
+#if HAVE_SYS_UN_H
+" -U, --unixsock Use Unix domain sockets only\n"
+#endif
+#if HAVE_LINUX_VM_SOCKETS_H
+" --vsock Use vsock sockets only\n"
+#endif
+" -C, --crlf Use CRLF for EOL sequence\n"
+" -c, --sh-exec <command> Executes the given command via /bin/sh\n"
+" -e, --exec <command> Executes the given command\n"
+#ifdef HAVE_LUA
+" --lua-exec <filename> Executes the given Lua script\n"
+#endif
+" -g hop1[,hop2,...] Loose source routing hop points (8 max)\n"
+" -G <n> Loose source routing hop pointer (4, 8, 12, ...)\n"
+" -m, --max-conns <n> Maximum <n> simultaneous connections\n"
+" -h, --help Display this help screen\n"
+" -d, --delay <time> Wait between read/writes\n"
+" -o, --output <filename> Dump session data to a file\n"
+" -x, --hex-dump <filename> Dump session data as hex to a file\n"
+" -i, --idle-timeout <time> Idle read/write timeout\n"
+" -p, --source-port port Specify source port to use\n"
+" -s, --source addr Specify source address to use (doesn't affect -l)\n"
+" -l, --listen Bind and listen for incoming connections\n"
+" -k, --keep-open Accept multiple connections in listen mode\n"
+" -n, --nodns Do not resolve hostnames via DNS\n"
+" -t, --telnet Answer Telnet negotiations\n"
+" -u, --udp Use UDP instead of default TCP\n"
+" --sctp Use SCTP instead of default TCP\n"
+" -v, --verbose Set verbosity level (can be used several times)\n"
+" -w, --wait <time> Connect timeout\n"
+" -z Zero-I/O mode, report connection status only\n"
+" --append-output Append rather than clobber specified output files\n"
+" --send-only Only send data, ignoring received; quit on EOF\n"
+" --recv-only Only receive data, never send anything\n"
+" --no-shutdown Continue half-duplex when receiving EOF on stdin\n"
+" --allow Allow only given hosts to connect to Ncat\n"
+" --allowfile A file of hosts allowed to connect to Ncat\n"
+" --deny Deny given hosts from connecting to Ncat\n"
+" --denyfile A file of hosts denied from connecting to Ncat\n"
+" --broker Enable Ncat's connection brokering mode\n"
+" --chat Start a simple Ncat chat server\n"
+" --proxy <addr[:port]> Specify address of host to proxy through\n"
+" --proxy-type <type> Specify proxy type (\"http\", \"socks4\", \"socks5\")\n"
+" --proxy-auth <auth> Authenticate with HTTP or SOCKS proxy server\n"
+" --proxy-dns <type> Specify where to resolve proxy destination\n"
+
+#ifdef HAVE_OPENSSL
+" --ssl Connect or listen with SSL\n"
+" --ssl-cert Specify SSL certificate file (PEM) for listening\n"
+" --ssl-key Specify SSL private key (PEM) for listening\n"
+" --ssl-verify Verify trust and domain name of certificates\n"
+" --ssl-trustfile PEM file containing trusted SSL certificates\n"
+" --ssl-ciphers Cipherlist containing SSL ciphers to use\n"
+" --ssl-servername Request distinct server name (SNI)\n"
+" --ssl-alpn ALPN protocol list to use\n"
+#endif
+" --version Display Ncat's version information and exit\n"
+"\n"
+"See the ncat(1) manpage for full options, descriptions and usage examples\n"
+ );
+ exit(EXIT_SUCCESS);
+ case '?':
+ /* Consider unrecognised parameters/arguments as fatal. */
+ bye("Try `--help' or man(1) ncat for more information, usage options and help.");
+ default:
+ /* We consider an unrecognised option fatal. */
+ bye("Unrecognised option.");
+ }
+ }
+
+#if HAVE_LINUX_VM_SOCKETS_H
+ if (o.af == AF_VSOCK)
+ max_port = UINT32_MAX;
+#endif
+
+ if (srcport > max_port)
+ bye("Invalid source port %lld.", srcport);
+
+#ifndef HAVE_OPENSSL
+ if (o.ssl)
+ bye("OpenSSL isn't compiled in. The --ssl option cannot be chosen.");
+#endif
+
+ if (o.normlog)
+ o.normlogfd = ncat_openlog(o.normlog, o.append);
+ if (o.hexlog)
+ o.hexlogfd = ncat_openlog(o.hexlog, o.append);
+
+ if (o.verbose)
+ print_banner();
+
+ if (o.debug)
+ nbase_set_log(loguser, logdebug);
+ else
+ nbase_set_log(loguser, NULL);
+
+#if HAVE_SYS_UN_H
+ /* Using Unix domain sockets, so do the checks now */
+ if (o.af == AF_UNIX) {
+ if (o.proxyaddr || o.proxytype)
+ bye("Proxy option not supported when using Unix domain sockets.");
+#ifdef HAVE_OPENSSL
+ if (o.ssl)
+ bye("SSL option not supported when using Unix domain sockets.");
+#endif
+ if (o.broker)
+ bye("Connection brokering not supported when using Unix domain sockets.");
+ if (srcport != -1)
+ bye("Specifying source port when using Unix domain sockets doesn't make sense.");
+ if (o.numsrcrtes > 0)
+ bye("Loose source routing not allowed when using Unix domain sockets.");
+ }
+#endif /* HAVE_SYS_UN_H */
+
+#if HAVE_LINUX_VM_SOCKETS_H
+ if (o.af == AF_VSOCK) {
+ if (o.proxyaddr || o.proxytype)
+ bye("Proxy option not supported when using vsock sockets.");
+#ifdef HAVE_OPENSSL
+ if (o.ssl)
+ bye("SSL option not supported when using vsock sockets.");
+#endif
+ if (o.broker)
+ bye("Connection brokering not supported when using vsock sockets.");
+ if (o.numsrcrtes > 0)
+ bye("Loose source routing not allowed when using vsock sockets.");
+ }
+#endif /* HAVE_LINUX_VM_SOCKETS_H */
+
+ /* Create a static target address, because at least one target address must be always allocated */
+ targetaddrs = (struct sockaddr_list *)safe_zalloc(sizeof(struct sockaddr_list));
+
+ /* Will be AF_INET or AF_INET6 or AF_UNIX when valid */
+ memset(&srcaddr.storage, 0, sizeof(srcaddr.storage));
+ srcaddr.storage.ss_family = AF_UNSPEC;
+ targetaddrs->addr.storage = srcaddr.storage;
+
+ /* Clear the listenaddrs array */
+ int i;
+ for (i = 0; i < NUM_LISTEN_ADDRS; i++) {
+ listenaddrs[i].storage = srcaddr.storage;
+ }
+
+ if (o.proxyaddr) {
+ if (!o.proxytype)
+ o.proxytype = Strdup("http");
+
+ /* validate proxy type and configure its default port */
+ if (!strcmp(o.proxytype, "http"))
+ proxyport = DEFAULT_PROXY_PORT;
+ else if (!strcmp(o.proxytype, "socks4") || !strcmp(o.proxytype, "4"))
+ proxyport = DEFAULT_SOCKS4_PORT;
+ else if (!strcmp(o.proxytype, "socks5") || !strcmp(o.proxytype, "5"))
+ proxyport = DEFAULT_SOCKS5_PORT;
+ else
+ bye("Invalid proxy type \"%s\".", o.proxytype);
+
+ /* Parse HTTP/SOCKS proxy address and store it in targetss.
+ * If the proxy server is given as an IPv6 address (not hostname),
+ * the port number MUST be specified as well or parsing will break
+ * (due to the colons in the IPv6 address and host:port separator).
+ */
+
+ targetaddrs->addrlen = parseproxy(o.proxyaddr,
+ &targetaddrs->addr.storage, &targetaddrs->addrlen, &proxyport);
+ if (o.af == AF_INET) {
+ targetaddrs->addr.in.sin_port = htons(proxyport);
+ } else { // might modify to else if and test AF_{INET6|UNIX|UNSPEC}
+ targetaddrs->addr.in6.sin6_port = htons(proxyport);
+ }
+
+ if (o.listen)
+ bye("Invalid option combination: --proxy and -l.");
+ } else {
+ if (o.proxytype) {
+ if (!o.listen)
+ bye("Proxy type (--proxy-type) specified without proxy address (--proxy).");
+ if (strcmp(o.proxytype, "http"))
+ bye("Invalid proxy type \"%s\"; Ncat proxy server only supports \"http\".", o.proxytype);
+ }
+ }
+
+ if (!o.proxy_auth)
+ o.proxy_auth = getenv("NCAT_PROXY_AUTH");
+
+ if (o.zerobyte) {
+ if (o.listen)
+ bye("Services designed for LISTENING can't be used with -z");
+ if (o.telnet)
+ bye("Invalid option combination: -z and -t.");
+ if (o.execmode||o.cmdexec)
+ bye("Command execution can't be done along with option -z.");
+ if (!o.idletimeout && o.proto == IPPROTO_UDP)
+ o.idletimeout = 2 * 1000;
+ }
+ /* Default port */
+ if (o.listen && o.proxytype && !o.portno && srcport == -1)
+ o.portno = DEFAULT_PROXY_PORT;
+ else
+ o.portno = DEFAULT_NCAT_PORT;
+
+ /* Resolve the given source address */
+ if (source) {
+ int rc = 0;
+
+ if (o.listen)
+ bye("-l and -s are incompatible. Specify the address and port to bind to like you would a host to connect to.");
+
+#if HAVE_SYS_UN_H
+ /* if using UNIX sockets just copy the path.
+ * If it's not valid, it will fail later! */
+ if (o.af == AF_UNIX) {
+ if (o.proto == IPPROTO_UDP) {
+ NCAT_INIT_SUN(&srcaddr, source);
+ srcaddrlen = SUN_LEN(&srcaddr.un);
+ }
+ else
+ if (o.verbose)
+ loguser("Specifying source socket for other than DATAGRAM Unix domain sockets have no effect.\n");
+ } else
+#endif
+#if HAVE_LINUX_VM_SOCKETS_H
+ if (o.af == AF_VSOCK) {
+ long long_cid;
+
+ srcaddr.vm.svm_family = AF_VSOCK;
+
+ errno = 0;
+ long_cid = strtol(source, NULL, 10);
+ if (errno != 0 || long_cid <= 0 || long_cid > UINT32_MAX)
+ bye("Invalid source address CID \"%s\".", source);
+ srcaddr.vm.svm_cid = long_cid;
+
+ srcaddrlen = sizeof(srcaddr.vm);
+ } else
+#endif
+ rc = resolve(source, 0, &srcaddr.storage, &srcaddrlen, o.af);
+ if (rc != 0)
+ bye("Could not resolve source address \"%s\": %s.", source, gai_strerror(rc));
+ }
+
+ host_list_to_set(o.allowset, allow_host_list);
+ host_list_free(allow_host_list);
+ host_list_to_set(o.denyset, deny_host_list);
+ host_list_free(deny_host_list);
+
+ int rc;
+ int num_ports = 0;
+ if (srcport != -1 && o.listen) {
+ /* Treat "ncat -l -p <port>" the same as "ncat -l <port>" for nc
+ compatibility. */
+ o.portno = (unsigned int) srcport;
+ num_ports++;
+ }
+ /* How many arguments are left? */
+ ncat_assert(optind <= argc);
+ switch (argc - optind) {
+ case 2:
+#if HAVE_SYS_UN_H
+ /* We do not use ports with Unix domain sockets. */
+ if (o.af == AF_UNIX) {
+ bye("Using Unix domain sockets and specifying port doesn't make sense.");
+ }
+#endif
+ if (num_ports == 0)
+ o.portno = parseport(argv[optind + 1], max_port, "port");
+ num_ports++;
+ /* fall through: */
+ case 1:
+#if HAVE_SYS_UN_H
+ if (o.af == AF_UNIX) {
+ NCAT_INIT_SUN(&targetaddrs->addr, argv[optind]);
+ targetaddrs->addrlen = SUN_LEN(&targetaddrs->addr.un);
+ o.sslservername = o.target = argv[optind];
+ break;
+ }
+#endif
+#if HAVE_LINUX_VM_SOCKETS_H
+ if (o.af == AF_VSOCK) {
+ long long_cid;
+
+ memset(&targetaddrs->addr.storage, 0, sizeof(struct sockaddr_vm));
+ targetaddrs->addr.vm.svm_family = AF_VSOCK;
+
+ errno = 0;
+ long_cid = strtol(argv[optind], NULL, 10);
+ if (errno != 0 || long_cid <= 0 || long_cid > UINT32_MAX)
+ bye("Invalid CID \"%s\".", argv[optind]);
+ targetaddrs->addr.vm.svm_cid = long_cid;
+
+ targetaddrs->addrlen = sizeof(targetaddrs->addr.vm);
+ o.sslservername = o.target = argv[optind];
+ break;
+ }
+#endif
+ /* Support ncat -l <port>, but otherwise assume ncat <target> */
+ if (num_ports == 0 && o.listen) {
+ rc = strspn(argv[optind], "1234567890");
+ /* If the last arg is 5 or fewer digits, assume it's a port number */
+ if (argv[optind][rc] == '\0' && rc <= 5) {
+ o.portno = parseport(argv[optind], max_port, "port");
+ num_ports++;
+ break;
+ }
+ }
+ o.target = argv[optind];
+ /* resolve hostname only if o.proxytype == NULL
+ * targetss contains data already and you don't want remove them
+ */
+ if( !o.proxytype
+ && (rc = resolve_multi(o.target, 0, targetaddrs, o.af)) != 0)
+
+ bye("Could not resolve hostname \"%s\": %s.", o.target, gai_strerror(rc));
+ if (!o.sslservername)
+ o.sslservername = o.target;
+ break;
+ case 0:
+#if HAVE_SYS_UN_H
+ if (o.af == AF_UNIX) {
+ bye("You must specify a path to a socket to %s.",
+ o.listen ? "listen on" : "connect to");
+ }
+#endif
+ /* Listen defaults to any address and DEFAULT_NCAT_PORT */
+ if (!o.listen)
+ bye("You must specify a host to connect to.");
+ break;
+ default:
+ if (num_ports == 0)
+ o.portno = parseport(argv[optind + 1], max_port, "port");
+ num_ports += argc - optind - 1;
+ break;
+ }
+
+ if (num_ports > 1) {
+ loguser("Got more than one port specification: %u", o.portno);
+ for (rc = argc - num_ports + 1; rc < argc; rc++)
+ loguser_noprefix(" %s", argv[rc]);
+ loguser_noprefix(". QUITTING.\n");
+ exit(2);
+ }
+
+ if (o.proxytype && !o.listen)
+ ; /* Do nothing - port is already set to proxyport */
+ else {
+ struct sockaddr_list *targetaddrs_item = targetaddrs;
+ while (targetaddrs_item != NULL)
+ {
+ if (targetaddrs_item->addr.storage.ss_family == AF_INET)
+ targetaddrs_item->addr.in.sin_port = htons(o.portno);
+#ifdef HAVE_IPV6
+ else if (targetaddrs_item->addr.storage.ss_family == AF_INET6)
+ targetaddrs_item->addr.in6.sin6_port = htons(o.portno);
+#endif
+#if HAVE_SYS_UN_H
+ /* If we use Unix domain sockets, we have to count with them. */
+ else if (targetaddrs_item->addr.storage.ss_family == AF_UNIX)
+ ; /* Do nothing. */
+#endif
+#if HAVE_LINUX_VM_SOCKETS_H
+ else if (targetaddrs_item->addr.storage.ss_family == AF_VSOCK)
+ targetaddrs_item->addr.vm.svm_port = o.portno;
+#endif
+ else if (targetaddrs_item->addr.storage.ss_family == AF_UNSPEC)
+ ; /* Leave unspecified. */
+ else
+ bye("Unknown address family %d.", targetaddrs_item->addr.storage.ss_family);
+ targetaddrs_item = targetaddrs_item->next;
+ }
+ }
+
+ if (srcport != -1 && !o.listen) {
+ if (srcaddr.storage.ss_family == AF_UNSPEC) {
+ /* We have a source port but not an explicit source address;
+ fill in an unspecified address of the same family as the
+ target. */
+ srcaddr.storage.ss_family = targetaddrs->addr.storage.ss_family;
+ if (srcaddr.storage.ss_family == AF_INET)
+ srcaddr.in.sin_addr.s_addr = INADDR_ANY;
+ else if (srcaddr.storage.ss_family == AF_INET6)
+ srcaddr.in6.sin6_addr = in6addr_any;
+ }
+ if (srcaddr.storage.ss_family == AF_INET)
+ srcaddr.in.sin_port = htons((unsigned int) srcport);
+#ifdef HAVE_IPV6
+ else if (srcaddr.storage.ss_family == AF_INET6)
+ srcaddr.in6.sin6_port = htons((unsigned int) srcport);
+#endif
+#ifdef HAVE_LINUX_VM_SOCKETS_H
+ else if (srcaddr.storage.ss_family == AF_VSOCK)
+ srcaddr.vm.svm_port = (unsigned int) srcport;
+#endif
+ }
+
+ if (o.proto == IPPROTO_UDP) {
+
+#ifndef HAVE_DTLS_CLIENT_METHOD
+ if (o.ssl)
+ bye("OpenSSL does not have DTLS support compiled in.");
+#endif
+ }
+
+ /* Do whatever is necessary to receive \n for line endings on input from
+ the console. A no-op on Unix. */
+ set_lf_mode();
+
+#ifdef HAVE_LUA
+ if (o.execmode == EXEC_LUA)
+ lua_setup();
+#endif
+
+ if (o.listen)
+ return ncat_listen_mode();
+ else
+ return ncat_connect_mode();
+}
+
+/* connect error handling and operations. */
+static int ncat_connect_mode(void)
+{
+ /*
+ * allow/deny commands with connect make no sense. If you don't want to
+ * connect to a host, don't try to.
+ */
+ if (o.allow || o.deny)
+ bye("Invalid option combination: allow/deny with connect.");
+
+ /* o.conn_limit with 'connect' doesn't make any sense. */
+ if (o.conn_limit != -1)
+ bye("Invalid option combination: `--max-conns' with connect.");
+
+ if (o.chat)
+ bye("Invalid option combination: `--chat' with connect.");
+
+ if (o.keepopen)
+ bye("Invalid option combination: `--keep-open' with connect.");
+
+ return ncat_connect();
+}
+
+static int ncat_listen_mode(void)
+{
+ /* Can't 'listen' AND 'connect' to a proxy server at the same time. */
+ if (o.proxyaddr != NULL)
+ bye("Invalid option combination: --proxy and -l.");
+
+ if (o.broker && o.cmdexec != NULL)
+ bye("Invalid option combination: --broker and -e.");
+
+ if (o.proxytype != NULL && o.telnet)
+ bye("Invalid option combination: --telnet has no effect with --proxy-type.");
+
+ if (o.conn_limit != -1 && !(o.keepopen || o.broker))
+ loguser("Warning: Maximum connections ignored, since it does not take "
+ "effect without -k or --broker.\n");
+
+ /* Set the default maximum simultaneous TCP connection limit. */
+ if (o.conn_limit == -1)
+ o.conn_limit = DEFAULT_MAX_CONNS;
+
+#ifndef WIN32
+ /* See if the shell is executable before we get deep into this */
+ if (o.execmode == EXEC_SHELL && access("/bin/sh", X_OK) == -1)
+ bye("/bin/sh is not executable, so `-c' won't work.");
+#endif
+
+ if (targetaddrs->addr.storage.ss_family != AF_UNSPEC) {
+ listenaddrs[num_listenaddrs++] = targetaddrs->addr;
+ } else {
+ size_t ss_len;
+ int rc;
+
+ /* No command-line address. Listen on IPv4 or IPv6 or both. */
+ /* Try to bind to IPv6 first; on AIX a bound IPv4 socket blocks an IPv6
+ socket on the same port, despite IPV6_V6ONLY. */
+#ifdef HAVE_IPV6
+ if (o.af == AF_INET6 || o.af == AF_UNSPEC) {
+ ss_len = sizeof(listenaddrs[num_listenaddrs]);
+ rc = resolve("::", o.portno, &listenaddrs[num_listenaddrs].storage, &ss_len, AF_INET6);
+ if (rc == 0)
+ num_listenaddrs++;
+ else if (o.debug > 0)
+ logdebug("Failed to resolve default IPv6 address: %s\n", gai_strerror(rc));
+ }
+#endif
+ if (o.af == AF_INET || o.af == AF_UNSPEC) {
+ ss_len = sizeof(listenaddrs[num_listenaddrs]);
+ rc = resolve("0.0.0.0", o.portno, &listenaddrs[num_listenaddrs].storage, &ss_len, AF_INET);
+ if (rc != 0)
+ bye("Failed to resolve default IPv4 address: %s.", gai_strerror(rc));
+ num_listenaddrs++;
+ }
+#ifdef HAVE_LINUX_VM_SOCKETS_H
+ if (o.af == AF_VSOCK) {
+ listenaddrs[num_listenaddrs].vm.svm_family = AF_VSOCK;
+ listenaddrs[num_listenaddrs].vm.svm_cid = VMADDR_CID_ANY;
+ listenaddrs[num_listenaddrs].vm.svm_port = o.portno;
+ num_listenaddrs++;
+ }
+#endif
+ }
+
+ if (o.proxytype) {
+ if (strcmp(o.proxytype, "http") == 0)
+ o.httpserver = 1;
+ }
+
+ /* Fire the listen/select dispatcher for bog-standard listen operations. */
+ return ncat_listen();
+}