/***************************************************************************
 * 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-2012 Insecure.Com LLC. Nmap is    *
 * also a registered trademark of Insecure.Com LLC.  This program is free  *
 * software; you may redistribute and/or modify it under the terms of the  *
 * GNU General Public License as published by the Free Software            *
 * Foundation; Version 2 with the clarifications and exceptions described  *
 * below.  This guarantees your right to use, modify, and redistribute     *
 * this software under certain conditions.  If you wish to embed Nmap      *
 * technology into proprietary software, we sell alternative licenses      *
 * (contact sales@insecure.com).  Dozens of software vendors already       *
 * license Nmap technology such as host discovery, port scanning, OS       *
 * detection, version detection, and the Nmap Scripting Engine.            *
 *                                                                         *
 * Note that the GPL places important restrictions on "derived works", yet *
 * it does not provide a detailed definition of that term.  To avoid       *
 * misunderstandings, we interpret that term as broadly as copyright law   *
 * allows.  For example, we consider an application to constitute a        *
 * "derivative work" for the purpose of this license if it does any of the *
 * following:                                                              *
 * o Integrates source code from Nmap                                      *
 * o Reads or includes Nmap copyrighted data files, such as                *
 *   nmap-os-db or nmap-service-probes.                                    *
 * o Executes Nmap and parses the results (as opposed to typical shell or  *
 *   execution-menu apps, which simply display raw Nmap output and so are  *
 *   not derivative works.)                                                *
 * o Integrates/includes/aggregates Nmap into a proprietary executable     *
 *   installer, such as those produced by InstallShield.                   *
 * o Links to a library or executes a program that does any of the above   *
 *                                                                         *
 * The term "Nmap" should be taken to also include any portions or derived *
 * works of Nmap, as well as other software we distribute under this       *
 * license such as Zenmap, Ncat, and Nping.  This list is not exclusive,   *
 * but is meant to clarify our interpretation of derived works with some   *
 * common examples.  Our interpretation applies only to Nmap--we don't     *
 * speak for other people's GPL works.                                     *
 *                                                                         *
 * If you have any questions about the GPL licensing restrictions on using *
 * Nmap in non-GPL works, we would be happy to help.  As mentioned above,  *
 * we also offer alternative license to integrate Nmap into proprietary    *
 * applications and appliances.  These contracts have been sold to dozens  *
 * of software vendors, and generally include a perpetual license as well  *
 * as providing for priority support and updates.  They also fund the      *
 * continued development of Nmap.  Please email sales@insecure.com for     *
 * further information.                                                    *
 *                                                                         *
 * As a special exception to the GPL terms, Insecure.Com LLC grants        *
 * permission to link the code of this program with any version of the     *
 * OpenSSL library which is distributed under a license identical to that  *
 * listed in the included docs/licenses/OpenSSL.txt file, and distribute   *
 * linked combinations including the two. You must obey the GNU GPL in all *
 * respects for all of the code used other than OpenSSL.  If you modify    *
 * this file, you may extend this exception to your version of the file,   *
 * but you are not obligated to do so.                                     *
 *                                                                         *
 * If you received these files with a written license agreement or         *
 * contract stating terms other than the terms above, then that            *
 * alternative license agreement takes precedence over these comments.     *
 *                                                                         *
 * 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 (none     *
 * have been found so far).                                                *
 *                                                                         *
 * Source code also allows you to port Nmap to new platforms, fix bugs,    *
 * and add new features.  You are highly encouraged to send your changes   *
 * to nmap-dev@insecure.org for possible incorporation into the main       *
 * distribution.  By sending these changes to Fyodor or one of the         *
 * Insecure.Org development mailing lists, or checking them into the Nmap  *
 * source code repository, it is understood (unless you specify otherwise) *
 * that you are offering the Nmap Project (Insecure.Com LLC) the           *
 * unlimited, non-exclusive right to reuse, modify, and relicense the      *
 * code.  Nmap will always be available Open Source, but this is important *
 * because the inability to relicense code has caused devastating problems *
 * for other Free Software projects (such as KDE and NASM).  We also       *
 * occasionally relicense the code to third parties as discussed above.    *
 * If you wish to specify special license conditions of your               *
 * contributions, just say so when you send them.                          *
 *                                                                         *
 * This program 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       *
 * General Public License v2.0 for more details at                         *
 * http://www.gnu.org/licenses/gpl-2.0.html , or in the COPYING file       *
 * included with Nmap.                                                     *
 *                                                                         *
 ***************************************************************************/

/* $Id$ */

#include "nsock.h"
#include "ncat.h"
#include "util.h"
#include "sys_wrap.h"

#include <getopt.h>

#ifndef WIN32
#include <unistd.h>
#endif
#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

static int ncat_connect_mode(void);
static int ncat_listen_mode(void);

/* Determines if it's parsing HTTP or SOCKS by looking at defport */
static void parseproxy(char *str, struct sockaddr_storage *ss, unsigned short defport)
{
    char *c = strrchr(str, ':'), *ptr;
    int httpproxy = (defport == DEFAULT_PROXY_PORT);
    unsigned short portno;
    size_t sslen;

    ptr = str;

    if (c)
        *c = 0;

    if (c && strlen((c + 1)))
        portno = (unsigned short) atoi(c + 1);
    else
        portno = defport;

    if (!resolve(ptr, portno, ss, &sslen, o.af)) {
        loguser("Could not resolve proxy \"%s\".\n", ptr);
        if (o.af == AF_INET6 && httpproxy)
            loguser("Did you specify the port number? It's required for IPv6.\n");
        exit(EXIT_FAILURE);
    }
}

/* 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;

    int srcport = -1;
    char *source = NULL;
    char *proxyaddr = NULL;

    struct option long_options[] = {
        {"4",               no_argument,        NULL,         '4'},
        {"6",               no_argument,        NULL,         '6'},
        {"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'},
        {"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},
        {"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,        &o.sctp,      1},
        {"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},
        {"nsock-engine",    required_argument,  NULL,         0},
#ifdef HAVE_OPENSSL
        {"ssl",             no_argument,        &o.ssl,       1},
        {"ssl-cert",        required_argument,  NULL,         0},
        {"ssl-key",         required_argument,  NULL,         0},
        {"ssl-verify",      no_argument,        NULL,         0},
        {"ssl-trustfile",   required_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, "46Cc:e:g:G:i:km:hp:d:lo:x:ts:uvw:n",
                            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;
        case 'C':
            o.crlf = 1;
            break;
        case 'c':
            o.cmdexec = optarg;
            o.shellexec = 1;
            break;
        case 'e':
            o.cmdexec = optarg;
            break;
        case 'g': {
            char *a = strtok(optarg, ",");
            do {
                union sockaddr_u addr;
                size_t sslen;
                if (!resolve(a, 0, &addr.storage, &sslen, AF_INET))
                    bye("Sorry, could not resolve source route hop %s.", a);
                o.srcrtes[o.numsrcrtes] = addr.in.sin_addr;
            } while (o.numsrcrtes++ <= 8 && (a = strtok(NULL, ",")));
            if (o.numsrcrtes > 8)
                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 = tval2msecs(optarg);
            if (o.linedelay <= 0)
                bye("Invalid -d delay (must be greater than 0).", optarg);
            if (o.linedelay >= 100 * 1000 && tval_unit(optarg) == NULL)
                bye("Since April 2010, the default unit for -d is seconds, so your time of \"%s\" is %.1f minutes. Use \"%sms\" for %g milliseconds.", optarg, o.linedelay / 1000.0 / 60, optarg, o.linedelay / 1000.0);
            break;
        case 'o':
            o.normlog = optarg;
            break;
        case 'x':
            o.hexlog = optarg;
            break;
        case 'p':
            srcport = atoi(optarg);
            if (srcport < 0 || srcport > 0xffff)
                bye("Invalid source port %d.", srcport);
            break;
        case 'i':
            o.idletimeout = tval2msecs(optarg);
            if (o.idletimeout <= 0)
                bye("Invalid -i timeout (must be greater than 0).");
            if (o.linedelay >= 100 * 1000 && tval_unit(optarg) == NULL)
                bye("Since April 2010, the default unit for -i is seconds, so your time of \"%s\" is %.1f minutes. Use \"%sms\" for %g milliseconds.", optarg, o.linedelay / 1000.0 / 60, optarg, o.linedelay / 1000.0);
            break;
        case 's':
            source = optarg;
            break;
        case 'l':
            o.listen = 1;
            break;
        case 'u':
            o.udp = 1;
            break;
        case 'v':
            /* One -v activites 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 = tval2msecs(optarg);
            if (o.conntimeout <= 0)
                bye("Invalid -w timeout (must be greater than 0).");
            if (o.linedelay >= 100 * 1000 && tval_unit(optarg) == NULL)
                bye("Since April 2010, the default unit for -w is seconds, so your time of \"%s\" is %.1f minutes. Use \"%sms\" for %g milliseconds.", optarg, o.linedelay / 1000.0 / 60, optarg, o.linedelay / 1000.0);
            break;
        case 't':
            o.telnet = 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 (proxyaddr)
                    bye("You can't specify more than one --proxy.");
                proxyaddr = Strdup(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 = Strdup(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 = Strdup(optarg);
            } else if (strcmp(long_options[option_index].name, "nsock-engine") == 0) {
                nsock_set_default_engine(optarg);
            } 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;
            }
#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;
            }
#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"
"  -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"
"  -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 up to 3 times)\n"
"  -w, --wait <time>          Connect timeout\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"
"      --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\" or \"socks4\")\n"
"      --proxy-auth <auth>    Authenticate with HTTP or SOCKS proxy server\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"
#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 (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);

    /* Will be AF_INET or AF_INET6 when valid */
    memset(&targetss.storage, 0, sizeof(targetss.storage));
    targetss.storage.ss_family = AF_UNSPEC;
    httpconnect.storage = socksconnect.storage = srcaddr.storage = targetss.storage;

    /* Clear the listenaddrs array */
    int i;
    for (i = 0; i < NUM_LISTEN_ADDRS; i++) {
        listenaddrs[i].storage = targetss.storage;
    }

    if (proxyaddr) {
        if (!o.proxytype)
            o.proxytype = Strdup("http");

        if (!strcmp(o.proxytype, "http")) {
            /* Parse HTTP proxy address and temporarily store it in httpconnect.  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).
             */

            parseproxy(proxyaddr, &httpconnect.storage, DEFAULT_PROXY_PORT);
        } else if (!strcmp(o.proxytype, "socks4") || !strcmp(o.proxytype, "4")) {
            /* Parse SOCKS proxy address and temporarily store it in socksconnect */

            parseproxy(proxyaddr, &socksconnect.storage, DEFAULT_SOCKS4_PORT);
        } else {
            bye("Invalid proxy type \"%s\".", o.proxytype);
        }

        free(o.proxytype);
        free(proxyaddr);
    } 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\".", o.proxytype);
        }
    }

    /* Default port */
    o.portno = DEFAULT_NCAT_PORT;

    /* Resolve the given source address */
    if (source) {
        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 (!resolve(source, 0, &srcaddr.storage, &srcaddrlen, o.af))
            bye("Could not resolve source address %s.", source);
    }

    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);

    if (optind == argc) {
        /* Listen defaults to any address and DEFAULT_NCAT_PORT */
        if (!o.listen)
            bye("You must specify a host to connect to.");
    } else {
        /* Resolve hostname if we're given one */
        if (strspn(argv[optind], "0123456789") != strlen(argv[optind])) {
            o.target = argv[optind];
            /* resolve hostname */
            if (!resolve(o.target, 0, &targetss.storage, &targetsslen, o.af))
                bye("Could not resolve hostname %s.", o.target);
            optind++;
        } else {
            if (!o.listen)
                bye("You must specify a host to connect to.");
        }
    }

    /* Whatever's left is the port number; there should be at most one. */
    if (optind + 1 < argc || (o.listen && srcport != -1 && optind + 1 == argc)) {
        loguser("Got more than one port specification:");
        if (o.listen && srcport != -1)
            loguser_noprefix(" %d", srcport);
        for (; optind < argc; optind++)
            loguser_noprefix(" %s", argv[optind]);
        loguser_noprefix(". QUITTING.\n");
        exit(2);
    } else if (optind + 1 == argc) {
        long long_port;

        errno = 0;
        long_port = strtol(argv[optind], NULL, 10);
        if (errno != 0 || long_port <= 0 || long_port > 65535)
            bye("Invalid port number \"%s\".", argv[optind]);

        o.portno = (unsigned short) long_port;
    }

    if (o.af == AF_INET)
        targetss.in.sin_port = htons(o.portno);
#ifdef HAVE_IPV6
    else
        targetss.in6.sin6_port = htons(o.portno);
#endif

    if (srcport != -1) {
        if (o.listen) {
            /* Treat "ncat -l -p <port>" the same as "ncat -l <port>" for nc
               compatibility. */
            o.portno = srcport;
        } else {
            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 = targetss.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(srcport);
#ifdef HAVE_IPV6
            else
                srcaddr.in6.sin6_port = htons(srcport);
#endif
        }
    }

    /* Since the host we're actually *connecting* to is the proxy server, we
     * need to reverse these address structures to avoid any further confusion
     */
    if (httpconnect.storage.ss_family != AF_UNSPEC) {
        union sockaddr_u tmp = targetss;
        targetss = httpconnect;
        httpconnect = tmp;
    } else if (socksconnect.storage.ss_family != AF_UNSPEC) {
        union sockaddr_u tmp = targetss;
        targetss = socksconnect;
        socksconnect = tmp;
    }

    if (o.udp) {
        /* Don't allow a false sense of security if someone tries SSL over UDP. */
        if (o.ssl)
            bye("UDP mode does not support SSL.");
        if (o.keepopen && o.cmdexec == NULL)
            bye("UDP mode does not support the -k or --keep-open options, except with --exec or --sh-exec.");
        if (o.broker)
            bye("UDP mode does not support connection brokering.\n\
If this feature is important to you, write nmap-dev@insecure.org with a\n\
description of how you intend to use it, as an aid to deciding how UDP\n\
connection brokering should work.");
    }

    /* Do whatever is necessary to receive \n for line endings on input from
       the console. A no-op on Unix. */
    set_lf_mode();

    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 (httpconnect.storage.ss_family != AF_UNSPEC || socksconnect.storage.ss_family != AF_UNSPEC)
        bye("Invalid option combination: --proxy and -l.");

    if (o.idletimeout != 0)
        bye("An idle timeout only works in connect mode.");

    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.shellexec && access("/bin/sh", X_OK) == -1)
        bye("/bin/sh is not executable, so `-c' won't work.");
#endif

    if (targetss.storage.ss_family != AF_UNSPEC) {
        listenaddrs[num_listenaddrs++] = targetss;
    } 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)
                bye("Failed to resolve default IPv6 address.");
            num_listenaddrs++;
        }
#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)
                bye("Failed to resolve default IPv4 address.");
            num_listenaddrs++;
        }
    }

    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();
}