/*

    main

    This includes:

    * main()
    * transmit_thread() - transmits probe packets
    * receive_thread() - receives response packets

    You'll be wanting to study the transmit/receive threads, because that's
    where all the action is.

    This is the lynch-pin of the entire program, so it includes a heckuva lot
    of headers, and the functions have a lot of local variables. I'm trying
    to make this file relative "flat" this way so that everything is visible.
*/
#include "masscan.h"
#include "masscan-version.h"
#include "masscan-status.h"     /* open or closed */
#include "rand-blackrock.h"     /* the BlackRock shuffling func */
#include "rand-lcg.h"           /* the LCG randomization func */
#include "templ-pkt.h"          /* packet template, that we use to send */
#include "rawsock.h"            /* API on top of Linux, Windows, Mac OS X*/
#include "logger.h"             /* adjust with -v command-line opt */
#include "main-status.h"        /* printf() regular status updates */
#include "main-throttle.h"      /* rate limit */
#include "main-dedup.h"         /* ignore duplicate responses */
#include "main-ptrace.h"        /* for nmap --packet-trace feature */
#include "proto-arp.h"          /* for responding to ARP requests */
#include "stack-ndpv6.h"        /* IPv6 Neighbor Discovery Protocol */
#include "stack-arpv4.h"        /* Handle ARP resolution and requests */
#include "rawsock-adapter.h"
#include "proto-banner1.h"      /* for snatching banners from systems */
#include "proto-tcp.h"          /* for TCP/IP connection table */
#include "proto-preprocess.h"   /* quick parse of packets */
#include "proto-icmp.h"         /* handle ICMP responses */
#include "proto-udp.h"          /* handle UDP responses */
#include "syn-cookie.h"         /* for SYN-cookies on send */
#include "output.h"             /* for outputting results */
#include "rte-ring.h"           /* producer/consumer ring buffer */
#include "rawsock-pcapfile.h"   /* for saving pcap files w/ raw packets */
#include "stub-pcap.h"          /* dynamically load libpcap library */
#include "smack.h"              /* Aho-corasick state-machine pattern-matcher */
#include "pixie-timer.h"        /* portable time functions */
#include "pixie-threads.h"      /* portable threads */
#include "templ-payloads.h"     /* UDP packet payloads */
#include "proto-snmp.h"         /* parse SNMP responses */
#include "proto-ntp.h"          /* parse NTP responses */
#include "proto-coap.h"         /* CoAP selftest */
#include "in-binary.h"          /* convert binary output to XML/JSON */
#include "main-globals.h"       /* all the global variables in the program */
#include "proto-zeroaccess.h"
#include "siphash24.h"
#include "proto-x509.h"
#include "crypto-base64.h"      /* base64 encode/decode */
#include "pixie-backtrace.h"
#include "proto-sctp.h"
#include "proto-oproto.h"       /* Other protocols on top of IP */
#include "vulncheck.h"          /* checking vulns like monlist, poodle, heartblee */
#include "main-readrange.h"
#include "scripting.h"
#include "read-service-probes.h"
#include "misc-rstfilter.h"
#include "util-malloc.h"
#include "util-checksum.h"
#include "massip-parse.h"
#include "massip-port.h"

#include <assert.h>
#include <limits.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <signal.h>
#include <stdint.h>

#if defined(WIN32)
#include <WinSock.h>
#if defined(_MSC_VER)
#pragma comment(lib, "Ws2_32.lib")
#endif
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#endif

/*
 * yea I know globals suck
 */
unsigned volatile is_tx_done = 0;
unsigned volatile is_rx_done = 0;
time_t global_now;

uint64_t usec_start;


/***************************************************************************
 * We create a pair of transmit/receive threads for each network adapter.
 * This structure contains the parameters we send to each pair.
 ***************************************************************************/
struct ThreadPair {
    /** This points to the central configuration. Note that it's 'const',
     * meaning that the thread cannot change the contents. That'd be
     * unsafe */
    const struct Masscan *masscan;

    /** The adapter used by the thread-pair. Normally, thread-pairs have
     * their own network adapter, especially when doing PF_RING
     * clustering. */
    struct Adapter *adapter;

    struct stack_t *stack;

    /**
     * The index of the network adapter that we are using for this
     * thread-pair. This is an index into the "masscan->nic[]"
     * array.
     *
     * NOTE: this is also the "thread-id", because we create one
     * transmit/receive thread pair per NIC.
     */
    unsigned nic_index;

    /**
     * A copy of the master 'index' variable. This is just advisory for
     * other threads, to tell them how far we've gotten.
     */
    volatile uint64_t my_index;


    /* This is used both by the transmit and receive thread for
     * formatting packets */
    struct TemplateSet tmplset[1];

    /**
     * The current IP address we are using for transmit/receive.
     */
    struct stack_src_t _src_;

    macaddress_t source_mac;
    macaddress_t router_mac_ipv4;
    macaddress_t router_mac_ipv6;

    unsigned done_transmitting;
    unsigned done_receiving;

    double pt_start;

    struct Throttler throttler[1];

    uint64_t *total_synacks;
    uint64_t *total_tcbs;
    uint64_t *total_syns;

    size_t thread_handle_xmit;
    size_t thread_handle_recv;
};



/***************************************************************************
 * We support a range of source IP/port. This function converts that
 * range into useful variables we can use to pick things form that range.
 ***************************************************************************/
static void
adapter_get_source_addresses(const struct Masscan *masscan,
            unsigned nic_index,
            unsigned *src_ipv4,
            unsigned *src_ipv4_mask,
            unsigned *src_port,
            unsigned *src_port_mask,
            ipv6address *src_ipv6,
            ipv6address *src_ipv6_mask)
{
    const struct stack_src_t *src = &masscan->nic[nic_index].src;
    static ipv6address mask = {~0ULL, ~0ULL};

    *src_ipv4 = src->ipv4.first;
    *src_ipv4_mask = src->ipv4.last - src->ipv4.first;

    *src_port = src->port.first;
    *src_port_mask = src->port.last - src->port.first;

    *src_ipv6 = src->ipv6.first;

    /* TODO: currently supports only a single address. This needs to
     * be fixed to support a list of addresses */
    *src_ipv6_mask = mask;
}

/***************************************************************************
 * This thread spews packets as fast as it can
 *
 *      THIS IS WHERE ALL THE EXCITEMENT HAPPENS!!!!
 *      90% of CPU cycles are in the function.
 *
 ***************************************************************************/
static void
transmit_thread(void *v) /*aka. scanning_thread() */
{
    struct ThreadPair *parms = (struct ThreadPair *)v;
    uint64_t i;
    uint64_t start;
    uint64_t end;
    const struct Masscan *masscan = parms->masscan;
    uint64_t retries = masscan->retries;
    uint64_t rate = (uint64_t)masscan->max_rate;
    unsigned r = (unsigned)retries + 1;
    uint64_t range;
    uint64_t range_ipv6;
    struct BlackRock blackrock;
    uint64_t count_ipv4 = rangelist_count(&masscan->targets.ipv4);
    uint64_t count_ipv6 = range6list_count(&masscan->targets.ipv6).lo;
    struct Throttler *throttler = parms->throttler;
    struct TemplateSet pkt_template = templ_copy(parms->tmplset);
    struct Adapter *adapter = parms->adapter;
    uint64_t packets_sent = 0;
    unsigned increment = (masscan->shard.of-1) + masscan->nic_count;
    unsigned src_ipv4;
    unsigned src_ipv4_mask;
    unsigned src_port;
    unsigned src_port_mask;
    ipv6address src_ipv6;
    ipv6address src_ipv6_mask;
    uint64_t seed = masscan->seed;
    uint64_t repeats = 0; /* --infinite repeats */
    uint64_t *status_syn_count;
    uint64_t entropy = masscan->seed;

    /* Wait to make sure receive_thread is ready */
    pixie_usleep(1000000);
    LOG(1, "[+] starting transmit thread #%u\n", parms->nic_index);

    /* export a pointer to this variable outside this threads so
     * that the 'status' system can print the rate of syns we are
     * sending */
    status_syn_count = MALLOC(sizeof(uint64_t));
    *status_syn_count = 0;
    parms->total_syns = status_syn_count;


    /* Normally, we have just one source address. In special cases, though
     * we can have multiple. */
    adapter_get_source_addresses(masscan, parms->nic_index,
                &src_ipv4, &src_ipv4_mask,
                &src_port, &src_port_mask,
                &src_ipv6, &src_ipv6_mask);


    /* "THROTTLER" rate-limits how fast we transmit, set with the
     * --max-rate parameter */
    throttler_start(throttler, masscan->max_rate/masscan->nic_count);

infinite:
    
    /* Create the shuffler/randomizer. This creates the 'range' variable,
     * which is simply the number of IP addresses times the number of
     * ports.
     * IPv6: low index will pick addresses from the IPv6 ranges, and high
     * indexes will pick addresses from the IPv4 ranges. */
    range = count_ipv4 * rangelist_count(&masscan->targets.ports)
            + count_ipv6 * rangelist_count(&masscan->targets.ports);
    range_ipv6 = count_ipv6 * rangelist_count(&masscan->targets.ports);
    blackrock_init(&blackrock, range, seed, masscan->blackrock_rounds);

    /* Calculate the 'start' and 'end' of a scan. One reason to do this is
     * to support --shard, so that multiple machines can co-operate on
     * the same scan. Another reason to do this is so that we can bleed
     * a little bit past the end when we have --retries. Yet another
     * thing to do here is deal with multiple network adapters, which
     * is essentially the same logic as shards. */
    start = masscan->resume.index + (masscan->shard.one-1) + parms->nic_index;
    end = range;
    if (masscan->resume.count && end > start + masscan->resume.count)
        end = start + masscan->resume.count;
    end += retries * range;


    /* -----------------
     * the main loop
     * -----------------*/
    LOG(3, "THREAD: xmit: starting main loop: [%llu..%llu]\n", start, end);
    for (i=start; i<end; ) {
        uint64_t batch_size;

        /*
         * Do a batch of many packets at a time. That because per-packet
         * throttling is expensive at 10-million pps, so we reduce the
         * per-packet cost by doing batches. At slower rates, the batch
         * size will always be one. (--max-rate)
         */
        batch_size = throttler_next_batch(throttler, packets_sent);

        /*
         * Transmit packets from other thread, when doing --banners. This
         * takes priority over sending SYN packets. If there is so much
         * activity grabbing banners that we cannot transmit more SYN packets,
         * then "batch_size" will get decremented to zero, and we won't be
         * able to transmit SYN packets.
         */
        stack_flush_packets(parms->stack, adapter,
                        &packets_sent, &batch_size);


        /*
         * Transmit a bunch of packets. At any rate slower than 100,000
         * packets/second, the 'batch_size' is likely to be 1. At higher
         * rates, we can't afford to throttle on a per-packet basis and 
         * instead throttle on a per-batch basis. In other words, throttle
         * based on 2-at-a-time, 3-at-time, and so on, with the batch
         * size increasing as the packet rate increases. This gives us
         * very precise packet-timing for low rates below 100,000 pps,
         * while not incurring the overhead for high packet rates.
         */
        while (batch_size && i < end) {
            uint64_t xXx;
            uint64_t cookie;
            


            /*
             * RANDOMIZE THE TARGET:
             *  This is kinda a tricky bit that picks a random IP and port
             *  number in order to scan. We monotonically increment the
             *  index 'i' from [0..range]. We then shuffle (randomly transmog)
             *  that index into some other, but unique/1-to-1, number in the
             *  same range. That way we visit all targets, but in a random
             *  order. Then, once we've shuffled the index, we "pick" the
             *  IP address and port that the index refers to.
             */
            xXx = (i + (r--) * rate);
            if (rate > range)
                xXx %= range;
            else
                while (xXx >= range)
                    xXx -= range;
            xXx = blackrock_shuffle(&blackrock,  xXx);
            
            if (xXx < range_ipv6) {
                ipv6address ip_them;
                unsigned port_them;
                ipv6address ip_me;
                unsigned port_me;

                ip_them = range6list_pick(&masscan->targets.ipv6, xXx % count_ipv6);
                port_them = rangelist_pick(&masscan->targets.ports, xXx / count_ipv6);

                ip_me = src_ipv6;
                port_me = src_port;
                
                cookie = syn_cookie_ipv6(ip_them, port_them, ip_me, port_me, entropy);

                rawsock_send_probe_ipv6(
                        adapter,
                        ip_them, port_them,
                        ip_me, port_me,
                        (unsigned)cookie,
                        !batch_size, /* flush queue on last packet in batch */
                        &pkt_template
                        );

                /* Our index selects an IPv6 target */
            } else {
                /* Our index selects an IPv4 target. In other words, low numbers
                 * index into the IPv6 ranges, and high numbers index into the
                 * IPv4 ranges. */
                ipv4address ip_them;
                ipv4address port_them;
                unsigned ip_me;
                unsigned port_me;

                xXx -= range_ipv6;

                ip_them = rangelist_pick(&masscan->targets.ipv4, xXx % count_ipv4);
                port_them = rangelist_pick(&masscan->targets.ports, xXx / count_ipv4);

                /*
                 * SYN-COOKIE LOGIC
                 *  Figure out the source IP/port, and the SYN cookie
                 */
                if (src_ipv4_mask > 1 || src_port_mask > 1) {
                    uint64_t ck = syn_cookie_ipv4((unsigned)(i+repeats),
                                            (unsigned)((i+repeats)>>32),
                                            (unsigned)xXx, (unsigned)(xXx>>32),
                                            entropy);
                    port_me = src_port + (ck & src_port_mask);
                    ip_me = src_ipv4 + ((ck>>16) & src_ipv4_mask);
                } else {
                    ip_me = src_ipv4;
                    port_me = src_port;
                }
                cookie = syn_cookie_ipv4(ip_them, port_them, ip_me, port_me, entropy);

                /*
                 * SEND THE PROBE
                 *  This is sorta the entire point of the program, but little
                 *  exciting happens here. The thing to note that this may
                 *  be a "raw" transmit that bypasses the kernel, meaning
                 *  we can call this function millions of times a second.
                 */
                rawsock_send_probe_ipv4(
                        adapter,
                        ip_them, port_them,
                        ip_me, port_me,
                        (unsigned)cookie,
                        !batch_size, /* flush queue on last packet in batch */
                        &pkt_template
                        );
            }

            batch_size--;
            packets_sent++;
            (*status_syn_count)++;

            /*
             * SEQUENTIALLY INCREMENT THROUGH THE RANGE
             *  Yea, I know this is a puny 'i++' here, but it's a core feature
             *  of the system that is linearly increments through the range,
             *  but produces from that a shuffled sequence of targets (as
             *  described above). Because we are linearly incrementing this
             *  number, we can do lots of creative stuff, like doing clever
             *  retransmits and sharding.
             */
            if (r == 0) {
                i += increment; /* <------ increment by 1 normally, more with shards/nics */
                r = (unsigned)retries + 1;
            }

        } /* end of batch */


        /* save our current location for resuming, if the user pressed
         * <ctrl-c> to exit early */
        parms->my_index = i;

        /* If the user pressed <ctrl-c>, then we need to exit. In case
         * the user wants to --resume the scan later, we save the current
         * state in a file */
        if (is_tx_done) {
            break;
        }
    }

    /*
     * --infinite
     *  For load testing, go around and do this again
     */
    if (masscan->is_infinite && !is_tx_done) {
        seed++;
        repeats++;
        goto infinite;
    }

    /*
     * Flush any untransmitted packets. High-speed mechanisms like Windows
     * "sendq" and Linux's "PF_RING" queue packets and transmit many together,
     * so there may be some packets that we've queued but not yet transmitted.
     * This call makes sure they are transmitted.
     */
    rawsock_flush(adapter);

    /*
     * Wait until the receive thread realizes the scan is over
     */
    LOG(1, "[+] transmit thread #%u complete\n", parms->nic_index);

    /*
     * We are done transmitting. However, response packets will take several
     * seconds to arrive. Therefore, sit in short loop waiting for those
     * packets to arrive. Pressing <ctrl-c> a second time will exit this
     * prematurely.
     */
    while (!is_rx_done) {
        unsigned k;
        uint64_t batch_size;

        for (k=0; k<1000; k++) {
            
            /*
             * Only send a few packets at a time, throttled according to the max
             * --max-rate set by the user
             */
            batch_size = throttler_next_batch(throttler, packets_sent);


            /* Transmit packets from the receive thread */
            stack_flush_packets(  parms->stack, adapter,
                            &packets_sent,
                            &batch_size);

            /* Make sure they've actually been transmitted, not just queued up for
             * transmit */
            rawsock_flush(adapter);

            pixie_usleep(100);
        }
    }

    /* Thread is about to exit */
    parms->done_transmitting = 1;
    LOG(1, "[+] exiting transmit thread #%u                    \n", parms->nic_index);
}


/***************************************************************************
 ***************************************************************************/
static unsigned
is_nic_port(const struct Masscan *masscan, unsigned ip)
{
    unsigned i;
    for (i=0; i<masscan->nic_count; i++)
        if (is_my_port(&masscan->nic[i].src, ip))
            return 1;
    return 0;
}

static unsigned
is_ipv6_multicast(ipaddress ip_me)
{
    /* If this is an IPv6 multicast packet, one sent to the IPv6
     * address with a prefix of FF02::/16 */
    return ip_me.version == 6 && (ip_me.ipv6.hi>>48ULL) == 0xFF02;
}


/***************************************************************************
 *
 * Asynchronous receive thread
 *
 * The transmit and receive threads run independently of each other. There
 * is no record what was transmitted. Instead, the transmit thread sets a
 * "SYN-cookie" in transmitted packets, which the receive thread will then
 * use to match up requests with responses.
 ***************************************************************************/
static void
receive_thread(void *v)
{
    struct ThreadPair *parms = (struct ThreadPair *)v;
    const struct Masscan *masscan = parms->masscan;
    struct Adapter *adapter = parms->adapter;
    int data_link = stack_if_datalink(adapter);
    struct Output *out;
    struct DedupTable *dedup;
    struct PcapFile *pcapfile = NULL;
    struct TCP_ConnectionTable *tcpcon = 0;
    uint64_t *status_synack_count;
    uint64_t *status_tcb_count;
    uint64_t entropy = masscan->seed;
    struct ResetFilter *rf;
    struct stack_t *stack = parms->stack;

    
    
    /* For reducing RST responses, see rstfilter_is_filter() below */
    rf = rstfilter_create(entropy, 16384);

    /* some status variables */
    status_synack_count = MALLOC(sizeof(uint64_t));
    *status_synack_count = 0;
    parms->total_synacks = status_synack_count;

    status_tcb_count = MALLOC(sizeof(uint64_t));
    *status_tcb_count = 0;
    parms->total_tcbs = status_tcb_count;

    LOG(1, "[+] starting receive thread #%u\n", parms->nic_index);
    
    /* Lock this thread to a CPU. Transmit threads are on even CPUs,
     * receive threads on odd CPUs */
    if (pixie_cpu_get_count() > 1) {
        unsigned cpu_count = pixie_cpu_get_count();
        unsigned cpu = parms->nic_index * 2 + 1;
        while (cpu >= cpu_count) {
            cpu -= cpu_count;
            cpu++;
        }
        //TODO:
        //pixie_cpu_set_affinity(cpu);
    }

    /*
     * If configured, open a --pcap file for saving raw packets. This is
     * so that we can debug scans, but also so that we can look at the
     * strange things people send us. Note that we don't record transmitted
     * packets, just the packets we've received.
     */
    if (masscan->pcap_filename[0]) {
        pcapfile = pcapfile_openwrite(masscan->pcap_filename, 1);
    }

    /*
     * Open output. This is where results are reported when saving
     * the --output-format to the --output-filename
     */
    out = output_create(masscan, parms->nic_index);

    /*
     * Create deduplication table. This is so when somebody sends us
     * multiple responses, we only record the first one.
     */
    dedup = dedup_create();

    /*
     * Create a TCP connection table (per thread pair) for interacting with live
     * connections when doing --banners
     */
    if (masscan->is_banners) {
        struct TcpCfgPayloads *pay;
        size_t i;

        /*
         * Create TCP connection table
         */
        tcpcon = tcpcon_create_table(
            (size_t)((masscan->max_rate/5) / masscan->nic_count),
            parms->stack,
            &parms->tmplset->pkts[Proto_TCP],
            output_report_banner,
            out,
            masscan->tcb.timeout,
            masscan->seed
            );
        
        /*
         * Initialize TCP scripting
         */
        scripting_init_tcp(tcpcon, masscan->scripting.L);
        
        
        /*
         * Set some flags [kludge]
         */
        tcpcon_set_banner_flags(tcpcon,
                masscan->is_capture_cert,
                masscan->is_capture_servername,
                masscan->is_capture_html,
                masscan->is_capture_heartbleed,
				masscan->is_capture_ticketbleed);
        if (masscan->is_hello_smbv1)
            tcpcon_set_parameter(tcpcon, "hello", 1, "smbv1");
        if (masscan->is_hello_http)
            tcpcon_set_parameter(tcpcon, "hello", 1, "http");
        if (masscan->is_hello_ssl)
            tcpcon_set_parameter(tcpcon, "hello", 1, "ssl");
        if (masscan->is_heartbleed)
            tcpcon_set_parameter(tcpcon, "heartbleed", 1, "1");
        if (masscan->is_ticketbleed)
            tcpcon_set_parameter(tcpcon, "ticketbleed", 1, "1");
        if (masscan->is_poodle_sslv3)
            tcpcon_set_parameter(tcpcon, "sslv3", 1, "1");

        if (masscan->http.payload)
            tcpcon_set_parameter(   tcpcon,
                                    "http-payload",
                                    masscan->http.payload_length,
                                    masscan->http.payload);
        if (masscan->http.user_agent)
            tcpcon_set_parameter(   tcpcon,
                                    "http-user-agent",
                                    masscan->http.user_agent_length,
                                    masscan->http.user_agent);
        if (masscan->http.host)
            tcpcon_set_parameter(   tcpcon,
                                    "http-host",
                                    masscan->http.host_length,
                                    masscan->http.host);
        if (masscan->http.method)
            tcpcon_set_parameter(   tcpcon,
                                    "http-method",
                                    masscan->http.method_length,
                                    masscan->http.method);
        if (masscan->http.url)
            tcpcon_set_parameter(   tcpcon,
                                    "http-url",
                                    masscan->http.url_length,
                                    masscan->http.url);
        if (masscan->http.version)
            tcpcon_set_parameter(   tcpcon,
                                    "http-version",
                                    masscan->http.version_length,
                                    masscan->http.version);


        if (masscan->tcp_connection_timeout) {
            char foo[64];
            sprintf_s(foo, sizeof(foo), "%u", masscan->tcp_connection_timeout);
            tcpcon_set_parameter(   tcpcon,
                                 "timeout",
                                 strlen(foo),
                                 foo);
        }
        if (masscan->tcp_hello_timeout) {
            char foo[64];
            sprintf_s(foo, sizeof(foo), "%u", masscan->tcp_hello_timeout);
            tcpcon_set_parameter(   tcpcon,
                                 "hello-timeout",
                                 strlen(foo),
                                 foo);
        }
        
        for (i=0; i<masscan->http.headers_count; i++) {
            tcpcon_set_http_header(tcpcon,
                        masscan->http.headers[i].name,
                        masscan->http.headers[i].value_length,
                        masscan->http.headers[i].value,
                        http_field_replace);
        }
        for (i=0; i<masscan->http.cookies_count; i++) {
            tcpcon_set_http_header(tcpcon,
                        "Cookie",
                        masscan->http.cookies[i].value_length,
                        masscan->http.cookies[i].value,
                        http_field_add);
        }
        for (i=0; i<masscan->http.remove_count; i++) {
            tcpcon_set_http_header(tcpcon,
                        masscan->http.headers[i].name,
                        0,
                        0,
                        http_field_remove);
        }

        for (pay = masscan->payloads.tcp; pay; pay = pay->next) {
            char name[64];
            sprintf_s(name, sizeof(name), "hello-string[%u]", pay->port);
            tcpcon_set_parameter(   tcpcon, 
                                    name, 
                                    strlen(pay->payload_base64), 
                                    pay->payload_base64);
        }

    }

    /*
     * In "offline" mode, we don't have any receive threads, so simply
     * wait until transmitter thread is done then go to the end
     */
    if (masscan->is_offline) {
        while (!is_rx_done)
            pixie_usleep(10000);
        parms->done_receiving = 1;
        goto end;
    }

    /*
     * Receive packets. This is where we catch any responses and print
     * them to the terminal.
     */
    LOG(2, "[+] THREAD: recv: starting main loop\n");
    while (!is_rx_done) {
        int status;
        unsigned length;
        unsigned secs;
        unsigned usecs;
        const unsigned char *px;
        int err;
        unsigned x;
        struct PreprocessedInfo parsed;
        ipaddress ip_me;
        unsigned port_me;
        ipaddress ip_them;
        unsigned port_them;
        unsigned seqno_me;
        unsigned seqno_them;
        unsigned cookie;
        unsigned Q = 0;

        /*
         * RECEIVE
         *
         * This is the boring part of actually receiving a packet
         */
        err = rawsock_recv_packet(
                    adapter,
                    &length,
                    &secs,
                    &usecs,
                    &px);
        if (err != 0) {
            if (tcpcon)
                tcpcon_timeouts(tcpcon, (unsigned)time(0), 0);
            continue;
        }
        

        /*
         * Do any TCP event timeouts based on the current timestamp from
         * the packet. For example, if the connection has been open for
         * around 10 seconds, we'll close the connection. (--banners)
         */
        if (tcpcon) {
            tcpcon_timeouts(tcpcon, secs, usecs);
        }

        if (length > 1514)
            continue;

        /*
         * "Preprocess" the response packet. This means to go through and
         * figure out where the TCP/IP headers are and the locations of
         * some fields, like IP address and port numbers.
         */
        x = preprocess_frame(px, length, data_link, &parsed);
        if (!x)
            continue; /* corrupt packet */
        ip_me = parsed.dst_ip;
        ip_them = parsed.src_ip;
        port_me = parsed.port_dst;
        port_them = parsed.port_src;
        seqno_them = TCP_SEQNO(px, parsed.transport_offset);
        seqno_me = TCP_ACKNO(px, parsed.transport_offset);
        
        assert(ip_me.version != 0);
        assert(ip_them.version != 0);

        switch (parsed.ip_protocol) {
        case 132: /* SCTP */
            cookie = syn_cookie(ip_them, port_them | (Proto_SCTP<<16), ip_me, port_me, entropy) & 0xFFFFFFFF;
            break;
        default:
            cookie = syn_cookie(ip_them, port_them, ip_me, port_me, entropy) & 0xFFFFFFFF;
        }

        /* verify: my IP address */
        if (!is_my_ip(stack->src, ip_me)) {
            /* NDP Neighbor Solicitations don't come to our IP address, but to
             * a multicast address */
            if (is_ipv6_multicast(ip_me)) {
                if (parsed.found == FOUND_NDPv6 && parsed.opcode == 135) {
                    stack_ndpv6_incoming_request(stack, &parsed, px, length);
                }
            }
            continue;
        }

        /*
         * Handle non-TCP protocols
         */
        switch (parsed.found) {
            case FOUND_NDPv6:
                switch (parsed.opcode) {
                case 133: /* Router Solicitation */
                    /* Ignore router solicitations, since we aren't a router */
                    continue;
                case 134: /* Router advertisement */
                    /* TODO: We need to process router advertisements while scanning
                     * so that we can print warning messages if router information
                     * changes while scanning. */
                    continue;
                case 135: /* Neighbor Solicitation */
                    /* When responses come back from our scans, the router will send us
                     * these packets. We need to respond to them, so that the router
                     * can then forward the packets to us. If we don't respond, we'll
                     * get no responses. */
                    stack_ndpv6_incoming_request(stack, &parsed, px, length);
                    continue;
                case 136: /* Neighbor Advertisement */
                    /* TODO: If doing an --ndpscan, the scanner subsystem needs to deal
                     * with these */
                    continue;
                case 137: /* Redirect */
                    /* We ignore these, since we really don't have the capability to send
                     * packets to one router for some destinations and to another router
                     * for other destinations */
                    continue;
                default:
                    break;
                }
                continue;
            case FOUND_ARP:
                LOGip(2, ip_them, 0, "-> ARP [%u] \n", px[parsed.found_offset]);

                switch (parsed.opcode) {
                case 1: /* request */
                    /* This function will transmit a "reply" to somebody's ARP request
                     * for our IP address (as part of our user-mode TCP/IP).
                     * Since we completely bypass the TCP/IP stack, we  have to handle ARPs
                     * ourself, or the router will lose track of us.*/
                     stack_arp_incoming_request(stack,
                                      ip_me.ipv4,
                                      parms->source_mac,
                                      px, length);
                    break;
                case 2: /* response */
                    /* This is for "arp scan" mode, where we are ARPing targets rather
                     * than port scanning them */

                    /* If we aren't doing an ARP scan, then ignore ARP responses */
                    if (!masscan->scan_type.arp)
                        break;

                    /* If this response isn't in our range, then ignore it */
                    if (!rangelist_is_contains(&masscan->targets.ipv4, ip_them.ipv4))
                        break;

                    /* Ignore duplicates */
                    if (dedup_is_duplicate(dedup, ip_them, 0, ip_me, 0))
                        continue;

                    /* ...everything good, so now report this response */
                    arp_recv_response(out, secs, px, length, &parsed);
                    break;
                }
                continue;
            case FOUND_UDP:
            case FOUND_DNS:
                if (!is_nic_port(masscan, port_me))
                    continue;
                if (parms->masscan->nmap.packet_trace)
                    packet_trace(stdout, parms->pt_start, px, length, 0);
                handle_udp(out, secs, px, length, &parsed, entropy);
                continue;
            case FOUND_ICMP:
                handle_icmp(out, secs, px, length, &parsed, entropy);
                continue;
            case FOUND_SCTP:
                handle_sctp(out, secs, px, length, cookie, &parsed, entropy);
                break;
            case FOUND_OPROTO: /* other IP proto */
                handle_oproto(out, secs, px, length, &parsed, entropy);
                break;
            case FOUND_TCP:
                /* fall down to below */
                break;
            default:
                continue;
        }


        /* verify: my port number */
        if (!is_my_port(stack->src, port_me))
            continue;
        if (parms->masscan->nmap.packet_trace)
            packet_trace(stdout, parms->pt_start, px, length, 0);

        Q = 0;

        /* Save raw packet in --pcap file */
        if (pcapfile) {
            pcapfile_writeframe(
                pcapfile,
                px,
                length,
                length,
                secs,
                usecs);
        }

        {
            char buf[64];
            LOGip(5, ip_them, port_them, "-> TCP ackno=0x%08x flags=0x%02x(%s)\n",
                seqno_me,
                TCP_FLAGS(px, parsed.transport_offset),
                reason_string(TCP_FLAGS(px, parsed.transport_offset), buf, sizeof(buf)));
        }

        /* If recording --banners, create a new "TCP Control Block (TCB)" */
        if (tcpcon) {
            struct TCP_Control_Block *tcb;

            /* does a TCB already exist for this connection? */
            tcb = tcb_lookup(tcpcon,
                            ip_me, ip_them,
                            port_me, port_them);

            if (TCP_IS_SYNACK(px, parsed.transport_offset)) {
                if (cookie != seqno_me - 1) {
                    ipaddress_formatted_t fmt = ipaddress_fmt(ip_them);
                    LOG(2, "%s - bad cookie: ackno=0x%08x expected=0x%08x\n",
                        fmt.string, seqno_me-1, cookie);
                    continue;
                }

                if (tcb == NULL) {
                    tcb = tcpcon_create_tcb(tcpcon,
                                    ip_me, ip_them,
                                    port_me, port_them,
                                    seqno_me, seqno_them+1,
                                    parsed.ip_ttl);
                    (*status_tcb_count)++;

                }

                Q += stack_incoming_tcp(tcpcon, tcb, TCP_WHAT_SYNACK,
                    0, 0, secs, usecs, seqno_them+1);

            } else if (tcb) {
                /* If this is an ACK, then handle that first */
                if (TCP_IS_ACK(px, parsed.transport_offset)) {
                    Q += stack_incoming_tcp(tcpcon, tcb, TCP_WHAT_ACK,
                        0, seqno_me, secs, usecs, seqno_them);
                }

                /* If this contains payload, handle that second */
                if (parsed.app_length) {
                    Q += stack_incoming_tcp(tcpcon, tcb, TCP_WHAT_DATA,
                        px + parsed.app_offset, parsed.app_length,
                        secs, usecs, seqno_them);
                }

                /* If this is a FIN, handle that. Note that ACK +
                 * payload + FIN can come together */
                if (TCP_IS_FIN(px, parsed.transport_offset)
                    && !TCP_IS_RST(px, parsed.transport_offset)) {
                    Q += stack_incoming_tcp(tcpcon, tcb, TCP_WHAT_FIN,
                        0, parsed.app_length, secs, usecs, seqno_them);
                }

                /* If this is a RST, then we'll be closing the connection */
                if (TCP_IS_RST(px, parsed.transport_offset)) {
                    Q += stack_incoming_tcp(tcpcon, tcb, TCP_WHAT_RST,
                        0, 0, secs, usecs, seqno_them);
                }
            } else if (TCP_IS_FIN(px, parsed.transport_offset)) {
                ipaddress_formatted_t fmt;
                /*
                 * NO TCB!
                 *  This happens when we've sent a FIN, deleted our connection,
                 *  but the other side didn't get the packet.
                 */
                fmt = ipaddress_fmt(ip_them);
                LOG(4, "%s: received FIN but no TCB\n", fmt.string);
                if (TCP_IS_RST(px, parsed.transport_offset))
                    ; /* ignore if it's own TCP flag is set */
                else {
                    int is_suppress;
                    
                    is_suppress = rstfilter_is_filter(rf, ip_me, port_me, ip_them, port_them);
                    if (!is_suppress)
                        tcpcon_send_RST(
                            tcpcon,
                            ip_me, ip_them,
                            port_me, port_them,
                            seqno_them, seqno_me);
                }
            }

        }

        if (Q == 0)
            ; //printf("\nerr\n");
   
        if (TCP_IS_SYNACK(px, parsed.transport_offset)
            || TCP_IS_RST(px, parsed.transport_offset)) {

            /* figure out the status */
            status = PortStatus_Unknown;
            if (TCP_IS_SYNACK(px, parsed.transport_offset))
                status = PortStatus_Open;
            if (TCP_IS_RST(px, parsed.transport_offset)) {
                status = PortStatus_Closed;
            }

            /* verify: syn-cookies */
            if (cookie != seqno_me - 1) {
                ipaddress_formatted_t fmt = ipaddress_fmt(ip_them);
                LOG(5, "%s - bad cookie: ackno=0x%08x expected=0x%08x\n",
                    fmt.string, seqno_me-1, cookie);
                continue;
            }

            /* verify: ignore duplicates */
            if (dedup_is_duplicate(dedup, ip_them, port_them, ip_me, port_me))
                continue;

            /* keep statistics on number received */
            if (TCP_IS_SYNACK(px, parsed.transport_offset))
                (*status_synack_count)++;

            /*
             * This is where we do the output
             */
            output_report_status(
                        out,
                        global_now,
                        status,
                        ip_them,
                        6, /* ip proto = tcp */
                        port_them,
                        px[parsed.transport_offset + 13], /* tcp flags */
                        parsed.ip_ttl,
                        parsed.mac_src
                        );
            

            /*
             * Send RST so other side isn't left hanging (only doing this in
             * complete stateless mode where we aren't tracking banners)
             */
            if (tcpcon == NULL && !masscan->is_noreset)
                tcp_send_RST(
                    &parms->tmplset->pkts[Proto_TCP],
                    parms->stack,
                    ip_them, ip_me,
                    port_them, port_me,
                    0, seqno_me);

        }
    }


    LOG(1, "[+] exiting receive thread #%u                    \n", parms->nic_index);
    
    /*
     * cleanup
     */
end:
    if (tcpcon)
        tcpcon_destroy_table(tcpcon);
    dedup_destroy(dedup);
    output_destroy(out);
    if (pcapfile)
        pcapfile_close(pcapfile);

    /*TODO: free stack packet buffers */

    /* Thread is about to exit */
    parms->done_receiving = 1;
}


/***************************************************************************
 * We trap the <ctrl-c> so that instead of exiting immediately, we sit in
 * a loop for a few seconds waiting for any late response. But, the user
 * can press <ctrl-c> a second time to exit that waiting.
 ***************************************************************************/
static void control_c_handler(int x)
{
    static unsigned control_c_pressed = 0;
    static unsigned control_c_pressed_again = 0;
    if (control_c_pressed == 0) {
        fprintf(stderr,
                "waiting several seconds to exit..."
                "                                            \n"
                );
        fflush(stderr);
        control_c_pressed = 1+x;
        is_tx_done = control_c_pressed;
    } else {
        if (is_rx_done) {
            fprintf(stderr, "\nERROR: threads not exiting %d\n", is_rx_done);
            if (is_rx_done++ > 1)
                exit(1);
        } else {
            control_c_pressed_again = 1;
            is_rx_done = control_c_pressed_again;
        }
    }

}




/***************************************************************************
 * Called from main() to initiate the scan.
 * Launches the 'transmit_thread()' and 'receive_thread()' and waits for
 * them to exit.
 ***************************************************************************/
static int
main_scan(struct Masscan *masscan)
{
    struct ThreadPair parms_array[8];
    uint64_t count_ips;
    uint64_t count_ports;
    uint64_t range;
    unsigned index;
    time_t now = time(0);
    struct Status status;
    uint64_t min_index = UINT64_MAX;
    struct MassVulnCheck *vulncheck = NULL;
    struct stack_t *stack;

    memset(parms_array, 0, sizeof(parms_array));

    /*
     * Vuln check initialization
     */
    if (masscan->vuln_name) {
        unsigned i;
		unsigned is_error;
        vulncheck = vulncheck_lookup(masscan->vuln_name);
        
        /* If no ports specified on command-line, grab default ports */
        is_error = 0;
        if (rangelist_count(&masscan->targets.ports) == 0)
            rangelist_parse_ports(&masscan->targets.ports, vulncheck->ports, &is_error, 0);
        
        /* Kludge: change normal port range to vulncheck range */
        for (i=0; i<masscan->targets.ports.count; i++) {
            struct Range *r = &masscan->targets.ports.list[i];
            r->begin = (r->begin&0xFFFF) | Templ_VulnCheck;
            r->end = (r->end & 0xFFFF) | Templ_VulnCheck;
        }
    }
    
    /*
     * Initialize the task size
     */
    count_ips = rangelist_count(&masscan->targets.ipv4) + range6list_count(&masscan->targets.ipv6).lo;
    if (count_ips == 0) {
        LOG(0, "FAIL: target IP address list empty\n");
        LOG(0, " [hint] try something like \"--range 10.0.0.0/8\"\n");
        LOG(0, " [hint] try something like \"--range 192.168.0.100-192.168.0.200\"\n");
        return 1;
    }
    count_ports = rangelist_count(&masscan->targets.ports);
    if (count_ports == 0) {
        LOG(0, "FAIL: no ports were specified\n");
        LOG(0, " [hint] try something like \"-p80,8000-9000\"\n");
        LOG(0, " [hint] try something like \"--ports 0-65535\"\n");
        return 1;
    }
    range = count_ips * count_ports;
    range += (uint64_t)(masscan->retries * range);

    /*
     * If doing an ARP scan, then don't allow port scanning
     */
    if (rangelist_is_contains(&masscan->targets.ports, Templ_ARP)) {
        if (masscan->targets.ports.count != 1) {
            LOG(0, "FAIL: cannot arpscan and portscan at the same time\n");
            return 1;
        }
    }

    /*
     * If the IP address range is very big, then require that that the
     * user apply an exclude range
     */
    if (count_ips > 1000000000ULL && rangelist_count(&masscan->exclude.ipv4) == 0) {
        LOG(0, "FAIL: range too big, need confirmation\n");
        LOG(0, " [hint] to prevent accidents, at least one --exclude must be specified\n");
        LOG(0, " [hint] use \"--exclude 255.255.255.255\" as a simple confirmation\n");
        exit(1);
    }

    /*
     * trim the nmap UDP payloads down to only those ports we are using. This
     * makes lookups faster at high packet rates.
     */
    payloads_udp_trim(masscan->payloads.udp, &masscan->targets);
    payloads_oproto_trim(masscan->payloads.oproto, &masscan->targets);


#ifdef __AFL_HAVE_MANUAL_CONTROL
  __AFL_INIT();
#endif

    /*
     * Start scanning threats for each adapter
     */
    for (index=0; index<masscan->nic_count; index++) {
        struct ThreadPair *parms = &parms_array[index];
        int err;

        parms->masscan = masscan;
        parms->nic_index = index;
        parms->my_index = masscan->resume.index;
        parms->done_transmitting = 0;
        parms->done_receiving = 0;

        /* needed for --packet-trace option so that we know when we started
         * the scan */
        parms->pt_start = 1.0 * pixie_gettime() / 1000000.0;


        /*
         * Turn the adapter on, and get the running configuration
         */
        err = masscan_initialize_adapter(
                            masscan,
                            index,
                            &parms->source_mac,
                            &parms->router_mac_ipv4,
                            &parms->router_mac_ipv6
                            );
        if (err != 0)
            exit(1);
        parms->adapter = masscan->nic[index].adapter;
        if (!masscan->nic[index].is_usable) {
            LOG(0, "FAIL: failed to detect IP of interface\n");
            LOG(0, " [hint] did you spell the name correctly?\n");
            LOG(0, " [hint] if it has no IP address, "
                    "manually set with \"--adapter-ip 192.168.100.5\"\n");
            exit(1);
        }


        /*
         * Initialize the TCP packet template. The way this works is that
         * we parse an existing TCP packet, and use that as the template for
         * scanning. Then, we adjust the template with additional features,
         * such as the IP address and so on.
         */
        parms->tmplset->vulncheck = vulncheck;
        template_packet_init(
                    parms->tmplset,
                    parms->source_mac,
                    parms->router_mac_ipv4,
                    parms->router_mac_ipv6,
                    masscan->payloads.udp,
                    masscan->payloads.oproto,
                    stack_if_datalink(masscan->nic[index].adapter),
                    masscan->seed);

        /*
         * Set the "source port" of everything we transmit.
         */
        if (masscan->nic[index].src.port.range == 0) {
            unsigned port = 40000 + now % 20000;
            masscan->nic[index].src.port.first = port;
            masscan->nic[index].src.port.last = port;
            masscan->nic[index].src.port.range = 1;
        }

        stack = stack_create(parms->source_mac, &masscan->nic[index].src);
        parms->stack = stack;

        /*
         * Set the "TTL" (IP time-to-live) of everything we send.
         */
        if (masscan->nmap.ttl)
            template_set_ttl(parms->tmplset, masscan->nmap.ttl);

        if (masscan->nic[0].is_vlan)
            template_set_vlan(parms->tmplset, masscan->nic[0].vlan_id);


        /*
         * trap <ctrl-c> to pause
         */
        signal(SIGINT, control_c_handler);

    }

    /*
     * Print helpful text
     */
    {
        char buffer[80];
        struct tm x;

        now = time(0);
        gmtime_s(&x, &now);
        strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S GMT", &x);
        LOG(0, "Starting masscan " MASSCAN_VERSION " (http://bit.ly/14GZzcT) at %s\n",
            buffer);

        if (count_ports == 1 && \
            masscan->targets.ports.list->begin == Templ_ICMP_echo && \
            masscan->targets.ports.list->end == Templ_ICMP_echo)
            { /* ICMP only */
                //LOG(0, " -- forced options: -sn -n --randomize-hosts -v --send-eth\n");
                LOG(0, "Initiating ICMP Echo Scan\n");
                LOG(0, "Scanning %u hosts\n",(unsigned)count_ips);
             }
        else /* This could actually also be a UDP only or mixed UDP/TCP/ICMP scan */
            {
                //LOG(0, " -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth\n");
                LOG(0, "Initiating SYN Stealth Scan\n");
                LOG(0, "Scanning %u hosts [%u port%s/host]\n",
                    (unsigned)count_ips, (unsigned)count_ports, (count_ports==1)?"":"s");
            }
    }
    
    /*
     * Start all the threads
     */
    for (index=0; index<masscan->nic_count; index++) {
        struct ThreadPair *parms = &parms_array[index];
        
        /*
         * Start the scanning thread.
         * THIS IS WHERE THE PROGRAM STARTS SPEWING OUT PACKETS AT A HIGH
         * RATE OF SPEED.
         */
        parms->thread_handle_xmit = pixie_begin_thread(transmit_thread, 0, parms);

        /*
         * Start the MATCHING receive thread. Transmit and receive threads
         * come in matching pairs.
         */
        parms->thread_handle_recv = pixie_begin_thread(receive_thread, 0, parms);
    }

    /*
     * Now wait for <ctrl-c> to be pressed OR for threads to exit
     */
    pixie_usleep(1000 * 100);
    LOG(1, "[+] waiting for threads to finish\n");
    status_start(&status);
    status.is_infinite = masscan->is_infinite;
    while (!is_tx_done && masscan->output.is_status_updates) {
        unsigned i;
        double rate = 0;
        uint64_t total_tcbs = 0;
        uint64_t total_synacks = 0;
        uint64_t total_syns = 0;


        /* Find the minimum index of all the threads */
        min_index = UINT64_MAX;
        for (i=0; i<masscan->nic_count; i++) {
            struct ThreadPair *parms = &parms_array[i];

            if (min_index > parms->my_index)
                min_index = parms->my_index;

            rate += parms->throttler->current_rate;

            if (parms->total_tcbs)
                total_tcbs += *parms->total_tcbs;
            if (parms->total_synacks)
                total_synacks += *parms->total_synacks;
            if (parms->total_syns)
                total_syns += *parms->total_syns;
        }

        if (min_index >= range && !masscan->is_infinite) {
            /* Note: This is how we can tell the scan has ended */
            is_tx_done = 1;
        }

        /*
         * update screen about once per second with statistics,
         * namely packets/second.
         */
        if (masscan->output.is_status_updates)
            status_print(&status, min_index, range, rate,
                total_tcbs, total_synacks, total_syns,
                0, masscan->output.is_status_ndjson);

        /* Sleep for almost a second */
        pixie_mssleep(750);
    }

    /*
     * If we haven't completed the scan, then save the resume
     * information.
     */
    if (min_index < count_ips * count_ports) {
        masscan->resume.index = min_index;

        /* Write current settings to "paused.conf" so that the scan can be restarted */
        masscan_save_state(masscan);
    }



    /*
     * Now wait for all threads to exit
     */
    now = time(0);
    for (;;) {
        unsigned transmit_count = 0;
        unsigned receive_count = 0;
        unsigned i;
        double rate = 0;
        uint64_t total_tcbs = 0;
        uint64_t total_synacks = 0;
        uint64_t total_syns = 0;


        /* Find the minimum index of all the threads */
        min_index = UINT64_MAX;
        for (i=0; i<masscan->nic_count; i++) {
            struct ThreadPair *parms = &parms_array[i];

            if (min_index > parms->my_index)
                min_index = parms->my_index;

            rate += parms->throttler->current_rate;

            if (parms->total_tcbs)
                total_tcbs += *parms->total_tcbs;
            if (parms->total_synacks)
                total_synacks += *parms->total_synacks;
            if (parms->total_syns)
                total_syns += *parms->total_syns;
        }



        if (time(0) - now >= masscan->wait) {
            is_rx_done = 1;
        }

        if (time(0) - now - 10 > masscan->wait) {
            LOG(0, "[-] Passed the wait window but still running, forcing exit...\n");
            exit(0);
        }

        if (masscan->output.is_status_updates) {
            status_print(&status, min_index, range, rate,
                total_tcbs, total_synacks, total_syns,
                masscan->wait - (time(0) - now),
                masscan->output.is_status_ndjson);

            for (i=0; i<masscan->nic_count; i++) {
                struct ThreadPair *parms = &parms_array[i];

                transmit_count += parms->done_transmitting;
                receive_count += parms->done_receiving;

            }

            pixie_mssleep(250);

            if (transmit_count < masscan->nic_count)
                continue;
            is_tx_done = 1;
            is_rx_done = 1;
            if (receive_count < masscan->nic_count)
                continue;

        } else {
            /* [AFL-fuzz]
             * Join the threads, which doesn't allow us to print out 
             * status messages, but allows us to exit cleanly without
             * any waiting */
            for (i=0; i<masscan->nic_count; i++) {
                struct ThreadPair *parms = &parms_array[i];

                pixie_thread_join(parms->thread_handle_xmit);
                parms->thread_handle_xmit = 0;
                pixie_thread_join(parms->thread_handle_recv);
                parms->thread_handle_recv = 0;
            }
            is_tx_done = 1;
            is_rx_done = 1;
        }

        break;
    }


    /*
     * Now cleanup everything
     */
    status_finish(&status);

    if (!masscan->output.is_status_updates) {
        uint64_t usec_now = pixie_gettime();

        printf("%u milliseconds elapsed\n", (unsigned)((usec_now - usec_start)/1000));
    }
    
    LOG(1, "[+] all threads have exited                    \n");

    return 0;
}




/***************************************************************************
 ***************************************************************************/
int main(int argc, char *argv[])
{
    struct Masscan masscan[1];
    unsigned i;
    int has_target_addresses = 0;
    int has_target_ports = 0;
    
    usec_start = pixie_gettime();
#if defined(WIN32)
    {WSADATA x; WSAStartup(0x101, &x);}
#endif

    global_now = time(0);

    /* Set system to report debug information on crash */
    {
        int is_backtrace = 1;
        for (i=1; i<(unsigned)argc; i++) {
            if (strcmp(argv[i], "--nobacktrace") == 0)
                is_backtrace = 0;
        }
        if (is_backtrace)
            pixie_backtrace_init(argv[0]);
    }
    
    /*
     * Initialize those defaults that aren't zero
     */
    memset(masscan, 0, sizeof(*masscan));
    /* 14 rounds seem to give way better statistical distribution than 4 with a 
    very low impact on scan rate */
    masscan->blackrock_rounds = 14;
    masscan->output.is_show_open = 1; /* default: show syn-ack, not rst */
    masscan->output.is_status_updates = 1; /* default: show status updates */
    masscan->wait = 10; /* how long to wait for responses when done */
    masscan->max_rate = 100.0; /* max rate = hundred packets-per-second */
    masscan->nic_count = 1;
    masscan->shard.one = 1;
    masscan->shard.of = 1;
    masscan->min_packet_size = 60;
    masscan->payloads.udp = payloads_udp_create();
    masscan->payloads.oproto = payloads_oproto_create();
    strcpy_s(   masscan->output.rotate.directory,
                sizeof(masscan->output.rotate.directory),
                ".");
    masscan->is_capture_cert = 1;

    /*
     * Pre-parse the command-line
     */
    if (masscan_conf_contains("--readscan", argc, argv)) {
        masscan->is_readscan = 1;
    }

    /*
     * On non-Windows systems, read the defaults from the file in
     * the /etc directory. These defaults will contain things
     * like the output directory, max packet rates, and so on. Most
     * importantly, the master "--excludefile" might be placed here,
     * so that blacklisted ranges won't be scanned, even if the user
     * makes a mistake
     */
#if !defined(WIN32)
    if (!masscan->is_readscan) {
        if (access("/etc/masscan/masscan.conf", 0) == 0) {
            masscan_read_config_file(masscan, "/etc/masscan/masscan.conf");
        }
    }
#endif

    /*
     * Read in the configuration from the command-line. We are looking for
     * either options or a list of IPv4 address ranges.
     */
    masscan_command_line(masscan, argc, argv);
    if (masscan->seed == 0)
        masscan->seed = get_entropy(); /* entropy for randomness */

    /*
     * Load database files like "nmap-payloads" and "nmap-service-probes"
     */
    masscan_load_database_files(masscan);

    /*
     * Load the scripting engine if needed and run those that were
     * specified.
     */
    if (masscan->is_scripting)
        scripting_init(masscan);

    /* We need to do a separate "raw socket" initialization step. This is
     * for Windows and PF_RING. */
    if (pcap_init() != 0)
        LOG(2, "libpcap: failed to load\n");
    rawsock_init();

    /* Init some protocol parser data structures */
    snmp_init();
    x509_init();


    /*
     * Apply excludes. People ask us not to scan them, so we maintain a list
     * of their ranges, and when doing wide scans, add the exclude list to
     * prevent them from being scanned.
     */
    has_target_addresses = massip_has_ipv4_targets(&masscan->targets) || massip_has_ipv6_targets(&masscan->targets);
    has_target_ports = massip_has_target_ports(&masscan->targets);
    massip_apply_excludes(&masscan->targets, &masscan->exclude);
    if (!has_target_ports && masscan->op == Operation_ListScan)
        massip_add_port_string(&masscan->targets, "80", 0);




    /* Optimize target selection so it's a quick binary search instead
     * of walking large memory tables. When we scan the entire Internet
     * our --excludefile will chop up our pristine 0.0.0.0/0 range into
     * hundreds of subranges. This allows us to grab addresses faster. */
    massip_optimize(&masscan->targets);
    
    /* FIXME: we only support 63-bit scans at the current time.
     * This is big enough for the IPv4 Internet, where scanning
     * for all TCP ports on all IPv4 addresses results in a 48-bit
     * scan, but this isn't big enough even for a single port on
     * an IPv6 subnet (which are 64-bits in size, usually). However,
     * even at millions of packets per second scanning rate, you still
     * can't complete a 64-bit scan in a reasonable amount of time.
     * Nor would you want to attempt the feat, as it would overload
     * the target IPv6 subnet. Since implementing this would be
     * difficult for 32-bit processors, for now, I'm going to stick
     * to a simple 63-bit scan.
     */
    if (massint128_bitcount(massip_range(&masscan->targets)) > 63) {
        fprintf(stderr, "[-] FAIL: scan range too large, max is 63-bits, requested is %u bits\n",
                massint128_bitcount(massip_range(&masscan->targets)));
        fprintf(stderr, "    Hint: scan range is number of IP addresses times number of ports\n");
        fprintf(stderr, "    Hint: IPv6 subnet must be at least /66 \n");
        exit(1);
    }

    /*
     * Once we've read in the configuration, do the operation that was
     * specified
     */
    switch (masscan->op) {
    case Operation_Default:
        /* Print usage info and exit */
        masscan_usage();
        break;

    case Operation_Scan:
        /*
         * THIS IS THE NORMAL THING
         */
        if (rangelist_count(&masscan->targets.ipv4) == 0 && massint128_is_zero(range6list_count(&masscan->targets.ipv6))) {
            /* We check for an empty target list here first, before the excludes,
             * so that we can differentiate error messages after excludes, in case
             * the user specified addresses, but they were removed by excludes. */
            LOG(0, "FAIL: target IP address list empty\n");
            if (has_target_addresses) {
                LOG(0, " [hint] all addresses were removed by exclusion ranges\n");
            } else {
                LOG(0, " [hint] try something like \"--range 10.0.0.0/8\"\n");
                LOG(0, " [hint] try something like \"--range 192.168.0.100-192.168.0.200\"\n");
            }
            exit(1);
        }
        if (rangelist_count(&masscan->targets.ports) == 0) {
            if (has_target_ports) {
                LOG(0, " [hint] all ports were removed by exclusion ranges\n");
            } else {
                LOG(0, "FAIL: no ports were specified\n");
                LOG(0, " [hint] try something like \"-p80,8000-9000\"\n");
                LOG(0, " [hint] try something like \"--ports 0-65535\"\n");
            }
            return 1;
        }
        return main_scan(masscan);

    case Operation_ListScan:
        /* Create a randomized list of IP addresses */
        main_listscan(masscan);
        return 0;

    case Operation_List_Adapters:
        /* List the network adapters we might want to use for scanning */
        rawsock_list_adapters();
        break;

    case Operation_DebugIF:
        for (i=0; i<masscan->nic_count; i++)
            rawsock_selftest_if(masscan->nic[i].ifname);
        return 0;

    case Operation_ReadRange:
        main_readrange(masscan);
        return 0;

    case Operation_ReadScan:
        {
            unsigned start;
            unsigned stop;

            /* find first file */
            for (start=1; start<(unsigned)argc; start++) {
                if (memcmp(argv[start], "--readscan", 10) == 0) {
                    start++;
                    break;
                }
            }

            /* find last file */
            for (stop=start+1; stop<(unsigned)argc && argv[stop][0] != '-'; stop++)
                ;

            /*
             * read the binary files, and output them again depending upon
             * the output parameters
             */
            read_binary_scanfile(masscan, start, stop, argv);

        }
        break;

    case Operation_Benchmark:
        printf("=== benchmarking (%u-bits) ===\n\n", (unsigned)sizeof(void*)*8);
        blackrock_benchmark(masscan->blackrock_rounds);
        blackrock2_benchmark(masscan->blackrock_rounds);
        smack_benchmark();
        exit(1);
        break;

    case Operation_Echo:
        masscan_echo(masscan, stdout, 0);
        exit(0);
        break;

    case Operation_EchoAll:
        masscan_echo(masscan, stdout, 0);
        exit(0);
        break;

    case Operation_Selftest:
        /*
         * Do a regression test of all the significant units
         */
        {
            int x = 0;
            x += massip_selftest();
            x += ranges6_selftest();
            x += dedup_selftest();
            x += checksum_selftest();
            x += ipv6address_selftest();
            x += proto_coap_selftest();
            x += smack_selftest();
            x += sctp_selftest();
            x += base64_selftest();
            x += banner1_selftest();
            x += output_selftest();
            x += siphash24_selftest();
            x += ntp_selftest();
            x += snmp_selftest();
            x += payloads_udp_selftest();
            x += blackrock_selftest();
            x += rawsock_selftest();
            x += lcg_selftest();
            x += template_selftest();
            x += ranges_selftest();
            x += massip_parse_selftest();
            x += pixie_time_selftest();
            x += rte_ring_selftest();
            x += mainconf_selftest();
            x += zeroaccess_selftest();
            x += nmapserviceprobes_selftest();
            x += rstfilter_selftest();


            if (x != 0) {
                /* one of the selftests failed, so return error */
                fprintf(stderr, "regression test: failed :( \n");
                return 1;
            } else {
                fprintf(stderr, "regression test: success!\n");
                return 0;
            }
        }
        break;
    }


    return 0;
}


