#ifndef MASSCAN_H
#define MASSCAN_H
#include "massip-addr.h"
#include "util-safefunc.h"
#include "stack-src.h"
#include "massip.h"
#include "util-bool.h"
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <time.h>

#include "massip.h"
#include "stack-queue.h"

struct Adapter;
struct TemplateSet;
struct Banner1;
struct TemplateOptions;

/**
 * This is the "operation" to be performed by masscan, which is almost always
 * to "scan" the network. However, there are some lesser operations to do
 * instead, like run a "regression self test", or "debug", or something else
 * instead of scanning. We parse the command-line in order to figure out the
 * proper operation
 */
enum Operation {
    Operation_Default = 0,          /* nothing specified, so print usage */
    Operation_List_Adapters = 1,    /* --listif */
    Operation_Selftest = 2,         /* --selftest or --regress */
    Operation_Scan = 3,         /* this is what you expect */
    Operation_DebugIF = 4,          /* --debug if */
    Operation_ListScan = 5,         /* -sL */
    Operation_ReadScan = 6,         /* --readscan <binary-output> */
    Operation_ReadRange = 7,        /* --readrange */
    Operation_Benchmark = 8,        /* --benchmark */
    Operation_Echo = 9,             /* --echo */
    Operation_EchoAll = 10,         /* --echo-all */
    Operation_EchoCidr = 11,        /* --echo-cidr */
};

/**
 * The format of the output. If nothing is specified, then the default will
 * be "--interactive", meaning that we'll print to the command-line live as
 * results come in. Only one output format can be specified, except that
 * "--interactive" can be specified alongside any of the other ones.
 */
enum OutputFormat {
    Output_Default      = 0x0000,
    Output_Interactive  = 0x0001,   /* --interactive, print to cmdline */
    Output_List         = 0x0002,
    Output_Binary       = 0x0004,   /* -oB, "binary", the primary format */
    Output_XML          = 0x0008,   /* -oX, "xml" */
    Output_JSON         = 0x0010,   /* -oJ, "json" */
    Output_NDJSON       = 0x0011,   /* -oD, "ndjson" */
    Output_Nmap         = 0x0020,
    Output_ScriptKiddie = 0x0040,
    Output_Grepable     = 0x0080,   /* -oG, "grepable" */
    Output_Redis        = 0x0100, 
    Output_Unicornscan  = 0x0200,   /* -oU, "unicornscan" */
    Output_None         = 0x0400,
    Output_Certs        = 0x0800,
    Output_Hostonly     = 0x1000,   /* -oH, "hostonly" */
    Output_All          = 0xFFBF,   /* not supported */
};


/**
 * Holds the list of TCP "hello" payloads, specified with the "--hello-file"
 * or "--hello-string" options
 */
struct TcpCfgPayloads
{
    /** The "hello" data in base64 format. This is either the base64 string
     * specified in the cmdline/cfgfile with "--hello-string", or the 
     * contents of a file specified with "--hello-file" that we've converted
     * into base64 */
    char *payload_base64;
    
    /** The TCP port that this hello belongs to */
    unsigned port;
    
    /** These configuration options are stored as a linked-list */
    struct TcpCfgPayloads *next;
};




/**
 * This is the master MASSCAN configuration structure. It is created on startup
 * by reading the command-line and parsing configuration files.
 *
 * Once read in at the start, this structure doesn't change. The transmit
 * and receive threads have only a "const" pointer to this structure.
 */
struct Masscan
{
    /**
     * What this program is doing, which is normally "Operation_Scan", but
     * which can be other things, like "Operation_SelfTest"
     */
    enum Operation op;
    
    struct {
        unsigned tcp:1;
        unsigned udp:1;     /* -sU */
        unsigned sctp:1;
        unsigned ping:1;    /* --ping, ICMP echo */
        unsigned arp:1;     /* --arp, local ARP scan */
        unsigned oproto:1;  /* -sO */
    } scan_type;
    
    /**
     * After scan type has been configured, add these ports. In other words,
     * the user may specify `-sU` or `-sT` after the `--top-ports` parameter,
     * so we have to wait until after parsing arguments to fill in the ports.
     */
    unsigned top_ports;
    
    /**
     * Temporary file to echo parameters to, used for saving configuration
     * to a file
     */
    FILE *echo;
    unsigned echo_all;

    /**
     * One or more network adapters that we'll use for scanning. Each adapter
     * should have a separate set of IP source addresses, except in the case
     * of PF_RING dnaX:Y adapters.
     */
    struct {
        char ifname[256];
        struct Adapter *adapter;
        struct stack_src_t src;
        macaddress_t source_mac;
        macaddress_t router_mac_ipv4;
        macaddress_t router_mac_ipv6;
        ipv4address_t router_ip;
        int link_type; /* libpcap definitions */
        unsigned char my_mac_count; /*is there a MAC address? */
        unsigned vlan_id;
        unsigned is_vlan:1;
        unsigned is_usable:1;
    } nic[8];
    unsigned nic_count;

    /**
     * The target ranges of IPv4 addresses that are included in the scan.
     * The user can specify anything here, and we'll resolve all overlaps
     * and such, and sort the target ranges.
     */
    struct MassIP targets;
    
    /**
     * IPv4 addresses/ranges that are to be excluded from the scan. This takes
     * precedence over any 'include' statement. What happens is this: after
     * all the configuration has been read, we then apply the exclude/blacklist
     * on top of the target/whitelist, leaving only a target/whitelist left.
     * Thus, during the scan, we only choose from the target/whitelist and
     * don't consult the exclude/blacklist.
     */
    struct MassIP exclude;

    /**
     * Only output these types of banners
     */
    struct RangeList banner_types;


    /**
     * Maximum rate, in packets-per-second (--rate parameter). This can be
     * a fraction of a packet-per-second, or be as high as 30000000.0 (or
     * more actually, but I've only tested to 30megapps).
     */
    double max_rate;

    /**
     * Number of retries (--retries or --max-retries parameter). Retries
     * happen a few seconds apart.
     */
    unsigned retries;

    
    unsigned is_pfring:1;       /* --pfring */
    unsigned is_sendq:1;        /* --sendq */
    unsigned is_banners:1;      /* --banners */
    unsigned is_banners_rawudp:1; /* --rawudp */
    unsigned is_offline:1;      /* --offline */
    unsigned is_noreset:1;      /* --noreset, don't transmit RST */
    unsigned is_gmt:1;          /* --gmt, all times in GMT */
    unsigned is_capture_cert:1; /* --capture cert */
    unsigned is_capture_html:1; /* --capture html */
    unsigned is_capture_heartbleed:1; /* --capture heartbleed */
    unsigned is_capture_ticketbleed:1; /* --capture ticket */
    unsigned is_test_csv:1;     /* (temporary testing feature) */
    unsigned is_infinite:1;     /* -infinite */
    unsigned is_readscan:1;     /* --readscan, Operation_Readscan */
    unsigned is_heartbleed:1;   /* --heartbleed, scan for this vuln */
    unsigned is_ticketbleed:1;  /* --ticketbleed, scan for this vuln */
    unsigned is_poodle_sslv3:1; /* --vuln poodle, scan for this vuln */
    unsigned is_hello_ssl:1;    /* --ssl, use SSL HELLO on all ports */
    unsigned is_hello_smbv1:1;  /* --smbv1, use SMBv1 hello, instead of v1/v2 hello */
    unsigned is_hello_http:1;    /* --hello=http, use HTTP on all ports */
    unsigned is_scripting:1;    /* whether scripting is needed */
    unsigned is_capture_servername:1; /* --capture servername */

    /** Packet template options, such as whether we should add a TCP MSS
     * value, or remove it from the packet */
    struct TemplateOptions *templ_opts; /* e.g. --tcpmss */

    /**
     * Wait forever for responses, instead of the default 10 seconds
     */
    unsigned wait;

    /**
     * --resume
     * This structure contains options for pausing the scan (by exiting the
     * program) and restarting it later.
     */
    struct {
        /** --resume-index */
        uint64_t index;
        
        /** --resume-count */
        uint64_t count;
        
        /** Derives the --resume-index from the target ip:port */
        struct {
            unsigned ip;
            unsigned port;
        } target;
    } resume;

    /**
     * --shard n/m
     * This is used for distributing a scan across multiple "shards". Every
     * shard in the scan must know the total number of shards, and must also
     * know which of those shards is it's identity. Thus, shard 1/5 scans
     * a different range than 2/5. These numbers start at 1, so it's
     * 1/3 (#1 out of three), 2/3, and 3/3 (but not 0/3).
     */
    struct {
        unsigned one;
        unsigned of;
    } shard;

    /**
     * The packet template set we are current using. We store a binary template
     * for TCP, UDP, SCTP, ICMP, and so on. All the scans using that protocol
     * are then scanned using that basic template. IP and TCP options can be
     * added to the basic template without affecting any other component
     * of the system.
     */
    struct TemplateSet *pkt_template;

    /**
     * A random seed for randomization if zero, otherwise we'll use
     * the configured seed for repeatable tests.
     */
    uint64_t seed;
    
    /**
     * This block configures what we do for the output files
     */
    struct OutputStuff {
        
        /**
         * --output-format
         * Examples are "xml", "binary", "json", "ndjson", "grepable", and so on.
         */
        enum OutputFormat format;
        
        /**
         * --output-filename
         * The name of the file where we are storing scan results.
         * Note: the filename "-" means that we should send the file to
         * <stdout> rather than to a file.
         */
        char filename[256];

        /**
         * A feature of the XML output where we can insert an optional 
         * stylesheet into the file for better rendering on web browsers
         */
        char stylesheet[256];

        /**
         * --append
         * We should append to the output file rather than overwriting it.
         */
        unsigned is_append:1;
        
        /**
         * --json-status
         * Print each status update line to stderr as JSON ending with a newline
         *
         * This only applies to the three types of status lines that are printed
         * in status_print(); it does *not* apply to things like startup messages,
         * error messages or discovery of individual ports
         *
         */
        bool is_status_ndjson;

        /**
         * --open
         * --open-only
         * --show open
         * Whether to show open ports
         */
        unsigned is_show_open:1;
        
        /**
         * --show closed
         * Whether to show closed ports (i.e. RSTs)
         */
        unsigned is_show_closed:1;
        
        /**
         * --show host
         * Whether to show host messages other than closed ports
         */
        unsigned is_show_host:1;
        
        /**
         * print reason port is open, which is redundant for us 
         */
        unsigned is_reason:1;
    
        /**
         * --interactive
         * Print to command-line while also writing to output file. This isn't
         * needed if the output format is already 'interactive' (the default),
         * but only if the default output format is anything else, and the
         * user also wants interactivity.
         */
        unsigned is_interactive:1;
        
        /**
        * Print state updates
        */
        unsigned is_status_updates:1;

        /**
         * --output-flush
         * Flush the file with every host/banner found
         */
        unsigned is_output_flush:1;

        struct {
            /**
             * When we should rotate output into the target directory
             */
            unsigned timeout;
            
            /**
             * When doing "--rotate daily", the rotation is done at GMT. In 
             * order to fix this, add an offset.
             */
            unsigned offset;
            
            /**
             * Instead of rotating by timeout, we can rotate by filesize 
             */
            uint64_t filesize;
            
            /**
             * The directory to which we store rotated files
             */
            char directory[256];
        } rotate;
    } output;

    struct {
        unsigned data_length; /* number of bytes to randomly append */
        unsigned ttl; /* starting IP TTL field */
        unsigned badsum; /* bad TCP/UDP/SCTP checksum */

        unsigned packet_trace:1; /* print transmit messages */
        
        char datadir[256];
    } nmap;

    char pcap_filename[256];

    struct {
        unsigned timeout;
    } tcb;

    struct {
        char *pcap_payloads_filename;
        char *nmap_payloads_filename;
        char *nmap_service_probes_filename;
    
        struct PayloadsUDP *udp;
        struct PayloadsUDP *oproto;
        struct TcpCfgPayloads *tcp;
        struct NmapServiceProbeList *probes;
    } payloads;
    
    /** Reconfigure the HTTP header */
    struct {
        /* Method */
        unsigned char *method;
        size_t method_length;

        /* URL */
        unsigned char *url;
        size_t url_length;

        /* Version */
        unsigned char *version;
        size_t version_length;

        /* Host */
        unsigned char *host;
        size_t host_length;

        /* User-Agent */
        unsigned char *user_agent;
        size_t user_agent_length;

        /* Payload after the header*/
        unsigned char *payload;
        size_t payload_length;

        /* Headers */
        struct {
            const char *name;
            unsigned char *value;
            size_t value_length;
        } headers[16];
        size_t headers_count;

        /* Cookies */
        struct {
            unsigned char *value;
            size_t value_length;
        } cookies[16];
        size_t cookies_count;

        /* Remove */
        struct {
            unsigned char *name;
        } remove[16];
        size_t remove_count;
    } http;

    unsigned tcp_connection_timeout;
    
    /** Number of seconds to wait for a 'hello' from the server before
     * giving up and sending a 'hello' from the client. Should be a small
     * value when doing scans that expect client-side hellos, like HTTP or
     * SSL, but should be a longer value when doing scans that expect server
     * hellos, such as FTP or VNC */
    unsigned tcp_hello_timeout;


    char *bpf_filter;

    struct {
        ipaddress ip;
        char    *password;
        unsigned port;
    } redis;



    /**
     * --min-packet
     */
    unsigned min_packet_size;

    /**
     * Number of rounds for randomization
     * --blackrock-rounds
     */
    unsigned blackrock_rounds;
    
    /**
     * --script <name>
     */
    struct {
        /* The name (filename) of the script to run */
        char *name;
        
        /* The script VM */
        struct lua_State *L;
    } scripting;

    
    /**
     * --vuln <name>
     * The name of a vuln to check, like "poodle"
     */
    const char *vuln_name;

};


int mainconf_selftest(void);
void masscan_read_config_file(struct Masscan *masscan, const char *filename);
void masscan_command_line(struct Masscan *masscan, int argc, char *argv[]);
void masscan_usage(void);
void masscan_save_state(struct Masscan *masscan);
void main_listscan(struct Masscan *masscan);

/**
 * Load databases, such as:
 *  - nmap-payloads
 *  - nmap-service-probes
 *  - pcap-payloads
 */
void masscan_load_database_files(struct Masscan *masscan);

/**
 * Pre-scan the command-line looking for options that may affect how
 * previous options are handled. This is a bit of a kludge, really.
 */
int masscan_conf_contains(const char *x, int argc, char **argv);

/**
 * Called to set a <name=value> pair.
 */
void
masscan_set_parameter(struct Masscan *masscan,
                      const char *name, const char *value);



/**
 * Discover the local network adapter parameters, such as which
 * MAC address we are using and the MAC addresses of the
 * local routers.
 */
int
masscan_initialize_adapter(
    struct Masscan *masscan,
    unsigned index,
    macaddress_t *source_mac,
    macaddress_t *router_mac_ipv4,
    macaddress_t *router_mac_ipv6);

/**
 * Echoes the settings to the command-line. By default, echoes only
 * non-default values. With "echo-all", everything is echoed.
 */
void
masscan_echo(struct Masscan *masscan, FILE *fp, unsigned is_echo_all);

/**
 * Echoes the list of CIDR ranges to scan.
 */
void
masscan_echo_cidr(struct Masscan *masscan, FILE *fp, unsigned is_echo_all);

#endif
