3960 lines
139 KiB
C
3960 lines
139 KiB
C
/*
|
|
Read in the configuration for MASSCAN.
|
|
|
|
Configuration parameters can be read either from the command-line
|
|
or a configuration file. Long parameters of the --xxxx variety have
|
|
the same name in both.
|
|
|
|
Most of the code in this module is for 'nmap' options we don't support.
|
|
That's because we support some 'nmap' options, and I wanted to give
|
|
more feedback for some of them why they don't work as expected, such
|
|
as reminding people that this is an asynchronous scanner.
|
|
|
|
*/
|
|
#include "masscan.h"
|
|
#include "massip-addr.h"
|
|
#include "masscan-version.h"
|
|
#include "util-safefunc.h"
|
|
#include "util-logger.h"
|
|
#include "proto-banner1.h"
|
|
#include "templ-payloads.h"
|
|
#include "crypto-base64.h"
|
|
#include "vulncheck.h"
|
|
#include "masscan-app.h"
|
|
#include "unusedparm.h"
|
|
#include "read-service-probes.h"
|
|
#include "util-malloc.h"
|
|
#include "massip.h"
|
|
#include "massip-parse.h"
|
|
#include "massip-port.h"
|
|
#include "templ-opts.h"
|
|
#include <ctype.h>
|
|
#include <limits.h>
|
|
|
|
#ifdef WIN32
|
|
#include <direct.h>
|
|
#define getcwd _getcwd
|
|
#else
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#ifndef min
|
|
#define min(a,b) ((a)<(b)?(a):(b))
|
|
#endif
|
|
|
|
#if defined(_MSC_VER)
|
|
#define strdup _strdup
|
|
#endif
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
***************************************************************************/
|
|
/*static struct Range top_ports_tcp[] = {
|
|
{80, 80},{23, 23}, {443,443},{21,22},{25,25},{3389,3389},{110,110},
|
|
{445,445},
|
|
};
|
|
static struct Range top_ports_udp[] = {
|
|
{161, 161}, {631, 631}, {137,138},{123,123},{1434},{445,445},{135,135},
|
|
{67,67},
|
|
};
|
|
static struct Range top_ports_sctp[] = {
|
|
{7, 7},{9, 9},{20,22},{80,80},{179,179},{443,443},{1167,1167},
|
|
};*/
|
|
|
|
/***************************************************************************
|
|
***************************************************************************/
|
|
void
|
|
masscan_usage(void)
|
|
{
|
|
printf("usage: masscan [options] [<IP|RANGE>... -pPORT[,PORT...]]\n");
|
|
printf("\n");
|
|
printf("examples:\n");
|
|
printf(" masscan -p80,8000-8100 10.0.0.0/8 --rate=10000\n");
|
|
printf(" scan some web ports on 10.x.x.x at 10kpps\n");
|
|
printf("\n");
|
|
printf(" masscan --nmap\n");
|
|
printf(" list those options that are compatible with nmap\n");
|
|
printf("\n");
|
|
printf(" masscan -p80 10.0.0.0/8 --banners -oB <filename>\n");
|
|
printf(" save results of scan in binary format to <filename>\n");
|
|
printf("\n");
|
|
printf(" masscan --open --banners --readscan <filename> -oX <savefile>\n");
|
|
printf(" read binary scan results in <filename> and save them as xml in <savefile>\n");
|
|
exit(1);
|
|
}
|
|
|
|
/***************************************************************************
|
|
***************************************************************************/
|
|
static void
|
|
print_version()
|
|
{
|
|
const char *cpu = "unknown";
|
|
const char *compiler = "unknown";
|
|
const char *compiler_version = "unknown";
|
|
const char *os = "unknown";
|
|
printf("\n");
|
|
printf("Masscan version %s ( %s )\n",
|
|
MASSCAN_VERSION,
|
|
"https://github.com/robertdavidgraham/masscan"
|
|
);
|
|
printf("Compiled on: %s %s\n", __DATE__, __TIME__);
|
|
|
|
#if defined(__x86_64) || defined(__x86_64__)
|
|
cpu = "x86";
|
|
#endif
|
|
|
|
#if defined(_MSC_VER)
|
|
#if defined(_M_AMD64) || defined(_M_X64)
|
|
cpu = "x86";
|
|
#elif defined(_M_IX86)
|
|
cpu = "x86";
|
|
#elif defined (_M_ARM_FP)
|
|
cpu = "arm";
|
|
#endif
|
|
|
|
{
|
|
int msc_ver = _MSC_VER;
|
|
|
|
compiler = "VisualStudio";
|
|
|
|
if (msc_ver < 1500)
|
|
compiler_version = "pre2008";
|
|
else if (msc_ver == 1500)
|
|
compiler_version = "2008";
|
|
else if (msc_ver == 1600)
|
|
compiler_version = "2010";
|
|
else if (msc_ver == 1700)
|
|
compiler_version = "2012";
|
|
else if (msc_ver == 1800)
|
|
compiler_version = "2013";
|
|
else
|
|
compiler_version = "post-2013";
|
|
}
|
|
|
|
|
|
#elif defined(__GNUC__)
|
|
# if defined(__clang__)
|
|
compiler = "clang";
|
|
# else
|
|
compiler = "gcc";
|
|
# endif
|
|
compiler_version = __VERSION__;
|
|
|
|
#if defined(i386) || defined(__i386) || defined(__i386__)
|
|
cpu = "x86";
|
|
#endif
|
|
|
|
#if defined(__corei7) || defined(__corei7__)
|
|
cpu = "x86-Corei7";
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#if defined(WIN32)
|
|
os = "Windows";
|
|
#elif defined(__linux__)
|
|
os = "Linux";
|
|
#elif defined(__APPLE__)
|
|
os = "Apple";
|
|
#elif defined(__MACH__)
|
|
os = "MACH";
|
|
#elif defined(__FreeBSD__)
|
|
os = "FreeBSD";
|
|
#elif defined(__NetBSD__)
|
|
os = "NetBSD";
|
|
#elif defined(unix) || defined(__unix) || defined(__unix__)
|
|
os = "Unix";
|
|
#endif
|
|
|
|
printf("Compiler: %s %s\n", compiler, compiler_version);
|
|
printf("OS: %s\n", os);
|
|
printf("CPU: %s (%u bits)\n", cpu, (unsigned)(sizeof(void*))*8);
|
|
|
|
#if defined(GIT)
|
|
printf("GIT version: %s\n", GIT);
|
|
#endif
|
|
}
|
|
|
|
/***************************************************************************
|
|
***************************************************************************/
|
|
static void
|
|
print_nmap_help(void)
|
|
{
|
|
printf("Masscan (https://github.com/robertdavidgraham/masscan)\n"
|
|
"Usage: masscan [Options] -p{Target-Ports} {Target-IP-Ranges}\n"
|
|
"TARGET SPECIFICATION:\n"
|
|
" Can pass only IPv4/IPv6 address, CIDR networks, or ranges (non-nmap style)\n"
|
|
" Ex: 10.0.0.0/8, 192.168.0.1, 10.0.0.1-10.0.0.254\n"
|
|
" -iL <inputfilename>: Input from list of hosts/networks\n"
|
|
" --exclude <host1[,host2][,host3],...>: Exclude hosts/networks\n"
|
|
" --excludefile <exclude_file>: Exclude list from file\n"
|
|
" --randomize-hosts: Randomize order of hosts (default)\n"
|
|
"HOST DISCOVERY:\n"
|
|
" -Pn: Treat all hosts as online (default)\n"
|
|
" -n: Never do DNS resolution (default)\n"
|
|
"SCAN TECHNIQUES:\n"
|
|
" -sS: TCP SYN (always on, default)\n"
|
|
"SERVICE/VERSION DETECTION:\n"
|
|
" --banners: get the banners of the listening service if available. The\n"
|
|
" default timeout for waiting to receive data is 30 seconds.\n"
|
|
"PORT SPECIFICATION AND SCAN ORDER:\n"
|
|
" -p <port ranges>: Only scan specified ports\n"
|
|
" Ex: -p22; -p1-65535; -p 111,137,80,139,8080\n"
|
|
"TIMING AND PERFORMANCE:\n"
|
|
" --max-rate <number>: Send packets no faster than <number> per second\n"
|
|
" --connection-timeout <number>: time in seconds a TCP connection will\n"
|
|
" timeout while waiting for banner data from a port.\n"
|
|
"FIREWALL/IDS EVASION AND SPOOFING:\n"
|
|
" -S/--source-ip <IP_Address>: Spoof source address\n"
|
|
" -e <iface>: Use specified interface\n"
|
|
" -g/--source-port <portnum>: Use given port number\n"
|
|
" --ttl <val>: Set IP time-to-live field\n"
|
|
" --spoof-mac <mac address/prefix/vendor name>: Spoof your MAC address\n"
|
|
"OUTPUT:\n"
|
|
" --output-format <format>: Sets output to binary/list/unicornscan/json/ndjson/grepable/xml\n"
|
|
" --output-file <file>: Write scan results to file. If --output-format is\n"
|
|
" not given default is xml\n"
|
|
" -oL/-oJ/-oD/-oG/-oB/-oX/-oU <file>: Output scan in List/JSON/nDjson/Grepable/Binary/XML/Unicornscan format,\n"
|
|
" respectively, to the given filename. Shortcut for\n"
|
|
" --output-format <format> --output-file <file>\n"
|
|
" -v: Increase verbosity level (use -vv or more for greater effect)\n"
|
|
" -d: Increase debugging level (use -dd or more for greater effect)\n"
|
|
" --open: Only show open (or possibly open) ports\n"
|
|
" --packet-trace: Show all packets sent and received\n"
|
|
" --iflist: Print host interfaces and routes (for debugging)\n"
|
|
" --append-output: Append to rather than clobber specified output files\n"
|
|
" --resume <filename>: Resume an aborted scan\n"
|
|
"MISC:\n"
|
|
" --send-eth: Send using raw ethernet frames (default)\n"
|
|
" -V: Print version number\n"
|
|
" -h: Print this help summary page.\n"
|
|
"EXAMPLES:\n"
|
|
" masscan -v -sS 192.168.0.0/16 10.0.0.0/8 -p 80\n"
|
|
" masscan 23.0.0.0/0 -p80 --banners -output-format binary --output-filename internet.scan\n"
|
|
" masscan --open --banners --readscan internet.scan -oG internet_scan.grepable\n"
|
|
"SEE (https://github.com/robertdavidgraham/masscan) FOR MORE HELP\n"
|
|
"\n");
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
***************************************************************************/
|
|
static unsigned
|
|
count_cidr6_bits(struct Range6 *range, bool *exact)
|
|
{
|
|
uint64_t i;
|
|
|
|
/* for the comments of this function, see count_cidr_bits */
|
|
*exact = false;
|
|
|
|
for (i=0; i<128; i++) {
|
|
uint64_t mask_hi;
|
|
uint64_t mask_lo;
|
|
if (i < 64) {
|
|
mask_hi = 0xFFFFFFFFffffffffull >> i;
|
|
mask_lo = 0xFFFFFFFFffffffffull;
|
|
} else {
|
|
mask_hi = 0;
|
|
mask_lo = 0xFFFFFFFFffffffffull >> (i - 64);
|
|
}
|
|
if ((range->begin.hi & mask_hi) != 0 || (range->begin.lo & mask_lo) != 0) {
|
|
continue;
|
|
}
|
|
if ((range->begin.hi & ~mask_hi) == (range->end.hi & ~mask_hi) &&
|
|
(range->begin.lo & ~mask_lo) == (range->end.lo & ~mask_lo)) {
|
|
if (((range->end.hi & mask_hi) == mask_hi) && ((range->end.lo & mask_lo) == mask_lo)) {
|
|
*exact = true;
|
|
return (unsigned) i;
|
|
}
|
|
} else {
|
|
*exact = false;
|
|
range->begin.hi = range->begin.hi + mask_hi;
|
|
if (range->begin.lo >= 0xffffffffffffffff - 1 - mask_lo) {
|
|
range->begin.hi += 1;
|
|
}
|
|
range->begin.lo = range->begin.lo + mask_lo + 1;
|
|
return (unsigned) i;
|
|
}
|
|
}
|
|
range->begin.lo = range->begin.lo + 1;
|
|
if (range->begin.lo == 0) {
|
|
range->begin.hi = range->begin.hi + 1;
|
|
}
|
|
return 128;
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
* Echoes the configuration for one NIC
|
|
***************************************************************************/
|
|
static void
|
|
masscan_echo_nic(struct Masscan *masscan, FILE *fp, unsigned i)
|
|
{
|
|
char idx_str[64];
|
|
|
|
/* If we have only one adapter, then don't print the array indexes.
|
|
* Otherwise, we need to print the array indexes to distinguish
|
|
* the NICs from each other */
|
|
if (masscan->nic_count <= 1)
|
|
idx_str[0] = '\0';
|
|
else
|
|
snprintf(idx_str, sizeof(idx_str), "[%u]", i);
|
|
|
|
if (masscan->nic[i].ifname[0])
|
|
fprintf(fp, "adapter%s = %s\n", idx_str, masscan->nic[i].ifname);
|
|
|
|
if (masscan->nic[i].src.ipv4.first != 0 || masscan->nic[i].src.ipv4.last != 0) {
|
|
|
|
/**
|
|
* FIX 495.1 for issue #495: Single adapter-ip is not saved at all
|
|
*
|
|
* The else case handles a simple invocation of one adapter-ip:
|
|
*
|
|
* 1. masscan ... --adapter-ip 1.2.3.1 ... [BROKEN]
|
|
*
|
|
* This looks like it was just copy pasta/typo. If the first ip is the same
|
|
* as the last ip, it is a single adapter-ip
|
|
*
|
|
* This never worked as it was before so paused.conf would never save the
|
|
* adapter-ip as it fell through this if/else if into nowhere. It probably
|
|
* went undetected because in simple environments and/or in simple scans,
|
|
* masscan is able to intelligently determine the adapter-ip and only
|
|
* advanced usage requires overriding the chosen value. In addition to
|
|
* that, it is probably relatively uncommon to interrupt a scan as not many
|
|
* users are doing multi-hour / multi-day scans, having them paused and
|
|
* then resuming them (apparently)
|
|
*/
|
|
if (masscan->nic[i].src.ipv4.first == masscan->nic[i].src.ipv4.last) {
|
|
ipaddress_formatted_t fmt = ipv4address_fmt(masscan->nic[i].src.ipv4.first);
|
|
fprintf(fp, "adapter-ip%s = %s\n", idx_str, fmt.string);
|
|
}
|
|
|
|
/**
|
|
* FIX 495.2 for issue #495: Ranges of size two don't print. When 495.1 is
|
|
* added, ranges of size two print as only the first value in the range
|
|
* Before 495.1, they didn't print at all, so this is not a bug that is
|
|
* introduced by 495.1, just noticed while applying that fix
|
|
*
|
|
* The first if case here is for handling when adapter-ip is a range
|
|
*
|
|
* Examples of the multiple/range case:
|
|
*
|
|
* 1. masscan ... --adapter-ip 1.2.3.1-1.2.3.2 ... [BROKEN]
|
|
* 2. masscan ... --adapter-ip 1.2.3.1-1.2.3.4 ... [OK]
|
|
*
|
|
* If the range spans exactly two adapter-ips, it will not hit the range
|
|
* printing logic case here because of an off-by-one
|
|
*
|
|
* Changing it from < to <= fixes that issue and both of the above cases
|
|
* now print the correct range as expected
|
|
*/
|
|
else if (masscan->nic[i].src.ipv4.first < masscan->nic[i].src.ipv4.last) {
|
|
ipaddress_formatted_t fmt1 = ipv4address_fmt(masscan->nic[i].src.ipv4.first);
|
|
ipaddress_formatted_t fmt2 = ipv4address_fmt(masscan->nic[i].src.ipv4.last);
|
|
fprintf(fp, "adapter-ip%s = %s-%s\n", idx_str, fmt1.string, fmt2.string);
|
|
}
|
|
}
|
|
|
|
if (masscan->nic[i].src.ipv6.range) {
|
|
if (ipv6address_is_lessthan(masscan->nic[i].src.ipv6.first, masscan->nic[i].src.ipv6.last)) {
|
|
ipaddress_formatted_t fmt1 = ipv6address_fmt(masscan->nic[i].src.ipv6.first);
|
|
ipaddress_formatted_t fmt2 = ipv6address_fmt(masscan->nic[i].src.ipv6.last);
|
|
fprintf(fp, "adapter-ip%s = %s-%s\n", idx_str, fmt1.string, fmt2.string);
|
|
} else {
|
|
ipaddress_formatted_t fmt = ipv6address_fmt(masscan->nic[i].src.ipv6.first);
|
|
fprintf(fp, "adapter-ip%s = %s\n", idx_str, fmt.string);
|
|
}
|
|
}
|
|
|
|
if (masscan->nic[i].my_mac_count) {
|
|
ipaddress_formatted_t fmt = macaddress_fmt(masscan->nic[i].source_mac);
|
|
fprintf(fp, "adapter-mac%s = %s\n", idx_str, fmt.string);
|
|
}
|
|
if (masscan->nic[i].router_ip) {
|
|
ipaddress_formatted_t fmt = ipv4address_fmt(masscan->nic[i].router_ip);
|
|
fprintf(fp, "router-ip%s = %s\n", idx_str, fmt.string);
|
|
}
|
|
if (!macaddress_is_zero(masscan->nic[i].router_mac_ipv4)) {
|
|
ipaddress_formatted_t fmt = macaddress_fmt(masscan->nic[i].router_mac_ipv4);
|
|
fprintf(fp, "router-mac-ipv4%s = %s\n", idx_str, fmt.string);
|
|
}
|
|
if (!macaddress_is_zero(masscan->nic[i].router_mac_ipv6)) {
|
|
ipaddress_formatted_t fmt = macaddress_fmt(masscan->nic[i].router_mac_ipv6);
|
|
fprintf(fp, "router-mac-ipv6%s = %s\n", idx_str, fmt.string);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
***************************************************************************/
|
|
void
|
|
masscan_save_state(struct Masscan *masscan)
|
|
{
|
|
char filename[512];
|
|
FILE *fp;
|
|
|
|
safe_strcpy(filename, sizeof(filename), "paused.conf");
|
|
fprintf(stderr, " "
|
|
" \r");
|
|
fprintf(stderr, "saving resume file to: %s\n", filename);
|
|
|
|
fp = fopen(filename, "wt");
|
|
if (fp == NULL) {
|
|
fprintf(stderr, "[-] FAIL: saving resume file\n");
|
|
fprintf(stderr, "[-] %s: %s\n", filename, strerror(errno));
|
|
return;
|
|
}
|
|
|
|
|
|
masscan_echo(masscan, fp, 0);
|
|
|
|
fclose(fp);
|
|
}
|
|
|
|
|
|
#if 0
|
|
/*****************************************************************************
|
|
* Read in ranges from a file
|
|
*
|
|
* There can be multiple ranges on a line, delimited by spaces. In fact,
|
|
* millions of ranges can be on a line: there is limit to the line length.
|
|
* That makes reading the file a little bit squirrelly. From one perspective
|
|
* this parser doesn't treat the new-line '\n' any different than other
|
|
* space. But, from another perspective, it has to, because things like
|
|
* comments are terminated by a newline. Also, it has to count the number
|
|
* of lines correctly to print error messages.
|
|
*****************************************************************************/
|
|
static void
|
|
ranges_from_file(struct RangeList *ranges, const char *filename)
|
|
{
|
|
FILE *fp;
|
|
unsigned line_number = 0;
|
|
|
|
fp = fopen(filename, "rt");
|
|
if (fp) {
|
|
perror(filename);
|
|
exit(1); /* HARD EXIT: because if it's an exclusion file, we don't
|
|
* want to continue. We don't want ANY chance of
|
|
* accidentally scanning somebody */
|
|
}
|
|
|
|
while (!feof(fp)) {
|
|
int c = '\n';
|
|
|
|
/* remove leading whitespace */
|
|
while (!feof(fp)) {
|
|
c = getc(fp);
|
|
line_number += (c == '\n');
|
|
if (!isspace(c&0xFF))
|
|
break;
|
|
}
|
|
|
|
/* If this is a punctuation, like '#', then it's a comment */
|
|
if (ispunct(c&0xFF)) {
|
|
while (!feof(fp)) {
|
|
c = getc(fp);
|
|
line_number += (c == '\n');
|
|
if (c == '\n') {
|
|
break;
|
|
}
|
|
}
|
|
/* Loop back to the begining state at the start of a line */
|
|
continue;
|
|
}
|
|
|
|
if (c == '\n') {
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Read in a single entry
|
|
*/
|
|
if (!feof(fp)) {
|
|
char address[64];
|
|
size_t i;
|
|
struct Range range;
|
|
unsigned offset = 0;
|
|
|
|
|
|
/* Grab all bytes until the next space or comma */
|
|
address[0] = (char)c;
|
|
i = 1;
|
|
while (!feof(fp)) {
|
|
c = getc(fp);
|
|
if (c == EOF)
|
|
break;
|
|
line_number += (c == '\n');
|
|
if (isspace(c&0xFF) || c == ',') {
|
|
break;
|
|
}
|
|
if (i+1 >= sizeof(address)) {
|
|
LOG(0, "%s:%u:%u: bad address spec: \"%.*s\"\n",
|
|
filename, line_number, offset, (int)i, address);
|
|
exit(1);
|
|
} else
|
|
address[i] = (char)c;
|
|
i++;
|
|
}
|
|
address[i] = '\0';
|
|
|
|
/* parse the address range */
|
|
range = range_parse_ipv4(address, &offset, (unsigned)i);
|
|
if (range.begin == 0xFFFFFFFF && range.end == 0) {
|
|
LOG(0, "%s:%u:%u: bad range spec: \"%.*s\"\n",
|
|
filename, line_number, offset, (int)i, address);
|
|
exit(1);
|
|
} else {
|
|
rangelist_add_range(ranges, range.begin, range.end);
|
|
}
|
|
}
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
/* Target list must be sorted every time it's been changed,
|
|
* before it can be used */
|
|
rangelist_sort(ranges);
|
|
}
|
|
#endif
|
|
|
|
/***************************************************************************
|
|
***************************************************************************/
|
|
static unsigned
|
|
hexval(char c)
|
|
{
|
|
if ('0' <= c && c <= '9')
|
|
return (unsigned)(c - '0');
|
|
if ('a' <= c && c <= 'f')
|
|
return (unsigned)(c - 'a' + 10);
|
|
if ('A' <= c && c <= 'F')
|
|
return (unsigned)(c - 'A' + 10);
|
|
return 0xFF;
|
|
}
|
|
|
|
/***************************************************************************
|
|
***************************************************************************/
|
|
static int
|
|
parse_mac_address(const char *text, macaddress_t *mac)
|
|
{
|
|
unsigned i;
|
|
|
|
for (i=0; i<6; i++) {
|
|
unsigned x;
|
|
char c;
|
|
|
|
while (isspace(*text & 0xFF) && ispunct(*text & 0xFF))
|
|
text++;
|
|
|
|
c = *text;
|
|
if (!isxdigit(c&0xFF))
|
|
return -1;
|
|
x = hexval(c)<<4;
|
|
text++;
|
|
|
|
c = *text;
|
|
if (!isxdigit(c&0xFF))
|
|
return -1;
|
|
x |= hexval(c);
|
|
text++;
|
|
|
|
mac->addr[i] = (unsigned char)x;
|
|
|
|
if (ispunct(*text & 0xFF))
|
|
text++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***************************************************************************
|
|
***************************************************************************/
|
|
static uint64_t
|
|
parseInt(const char *str)
|
|
{
|
|
uint64_t result = 0;
|
|
|
|
while (*str && isdigit(*str & 0xFF)) {
|
|
result = result * 10 + (*str - '0');
|
|
str++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* a stricter function for determining if something is boolean.
|
|
*/
|
|
static bool
|
|
isBoolean(const char *str) {
|
|
size_t length = str?strlen(str):0;
|
|
|
|
if (length == 0)
|
|
return false;
|
|
|
|
/* "0" or "1" is boolean */
|
|
if (isdigit(str[0])) {
|
|
if (strtoul(str,0,0) == 0)
|
|
return true;
|
|
else if (strtoul(str,0,0) == 1)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
switch (str[0]) {
|
|
case 'e':
|
|
case 'E':
|
|
if (memcasecmp("enable", str, length)==0)
|
|
return true;
|
|
if (memcasecmp("enabled", str, length)==0)
|
|
return true;
|
|
return false;
|
|
case 'd':
|
|
case 'D':
|
|
if (memcasecmp("disable", str, length)==0)
|
|
return true;
|
|
if (memcasecmp("disabled", str, length)==0)
|
|
return true;
|
|
return false;
|
|
|
|
case 't':
|
|
case 'T':
|
|
if (memcasecmp("true", str, length)==0)
|
|
return true;
|
|
return false;
|
|
case 'f':
|
|
case 'F':
|
|
if (memcasecmp("false", str, length)==0)
|
|
return true;
|
|
return false;
|
|
|
|
case 'o':
|
|
case 'O':
|
|
if (memcasecmp("on", str, length)==0)
|
|
return true;
|
|
if (memcasecmp("off", str, length)==0)
|
|
return true;
|
|
return false;
|
|
case 'Y':
|
|
case 'y':
|
|
if (memcasecmp("yes", str, length)==0)
|
|
return true;
|
|
return false;
|
|
case 'n':
|
|
case 'N':
|
|
if (memcasecmp("no", str, length)==0)
|
|
return true;
|
|
return false;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static unsigned
|
|
parseBoolean(const char *str)
|
|
{
|
|
if (str == NULL || str[0] == 0)
|
|
return 1;
|
|
if (isdigit(str[0])) {
|
|
if (strtoul(str,0,0) == 0)
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|
|
switch (str[0]) {
|
|
case 'e': /* enable */
|
|
case 'E':
|
|
return 1;
|
|
case 'd': /* disable */
|
|
case 'D':
|
|
return 0;
|
|
|
|
case 't': /* true */
|
|
case 'T':
|
|
return 1;
|
|
case 'f': /* false */
|
|
case 'F':
|
|
return 0;
|
|
|
|
case 'o': /* on or off */
|
|
case 'O':
|
|
if (str[1] == 'f' || str[1] == 'F')
|
|
return 0;
|
|
else
|
|
return 1;
|
|
break;
|
|
|
|
case 'Y': /* yes */
|
|
case 'y':
|
|
return 1;
|
|
case 'n': /* no */
|
|
case 'N':
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/***************************************************************************
|
|
* Parses the number of seconds (for rotating files mostly). We do a little
|
|
* more than just parse an integer. We support strings like:
|
|
*
|
|
* hourly
|
|
* daily
|
|
* Week
|
|
* 5days
|
|
* 10-months
|
|
* 3600
|
|
***************************************************************************/
|
|
static uint64_t
|
|
parseTime(const char *value)
|
|
{
|
|
uint64_t num = 0;
|
|
unsigned is_negative = 0;
|
|
|
|
while (*value == '-') {
|
|
is_negative = 1;
|
|
value++;
|
|
}
|
|
|
|
while (isdigit(value[0]&0xFF)) {
|
|
num = num*10 + (value[0] - '0');
|
|
value++;
|
|
}
|
|
while (ispunct(value[0]) || isspace(value[0]))
|
|
value++;
|
|
|
|
if (isalpha(value[0]) && num == 0)
|
|
num = 1;
|
|
|
|
if (value[0] == '\0')
|
|
return num;
|
|
|
|
switch (tolower(value[0])) {
|
|
case 's':
|
|
num *= 1;
|
|
break;
|
|
case 'm':
|
|
num *= 60;
|
|
break;
|
|
case 'h':
|
|
num *= 60*60;
|
|
break;
|
|
case 'd':
|
|
num *= 24*60*60;
|
|
break;
|
|
case 'w':
|
|
num *= 24*60*60*7;
|
|
break;
|
|
default:
|
|
fprintf(stderr, "--rotate-offset: unknown character\n");
|
|
exit(1);
|
|
}
|
|
if (num >= 24*60*60) {
|
|
fprintf(stderr, "--rotate-offset: value is greater than 1 day\n");
|
|
exit(1);
|
|
}
|
|
if (is_negative)
|
|
num = 24*60*60 - num;
|
|
|
|
return num;
|
|
}
|
|
|
|
/***************************************************************************
|
|
* Parses a size integer, which can be suffixed with "tera", "giga",
|
|
* "mega", and "kilo". These numbers are in units of 1024 so suck it.
|
|
***************************************************************************/
|
|
static uint64_t
|
|
parseSize(const char *value)
|
|
{
|
|
uint64_t num = 0;
|
|
|
|
while (isdigit(value[0]&0xFF)) {
|
|
num = num*10 + (value[0] - '0');
|
|
value++;
|
|
}
|
|
while (ispunct(value[0]) || isspace(value[0]))
|
|
value++;
|
|
|
|
if (isalpha(value[0]) && num == 0)
|
|
num = 1;
|
|
|
|
if (value[0] == '\0')
|
|
return num;
|
|
|
|
switch (tolower(value[0])) {
|
|
case 'k': /* kilobyte */
|
|
num *= 1024ULL;
|
|
break;
|
|
case 'm': /* megabyte */
|
|
num *= 1024ULL * 1024ULL;
|
|
break;
|
|
case 'g': /* gigabyte */
|
|
num *= 1024ULL * 1024ULL * 1024ULL;
|
|
break;
|
|
case 't': /* terabyte, 'cause we roll that way */
|
|
num *= 1024ULL * 1024ULL * 1024ULL * 1024ULL;
|
|
break;
|
|
case 'p': /* petabyte, 'cause we are awesome */
|
|
num *= 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL;
|
|
break;
|
|
case 'e': /* exabyte, now that's just silly */
|
|
num *= 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL;
|
|
break;
|
|
default:
|
|
fprintf(stderr, "--rotate-size: unknown character\n");
|
|
exit(1);
|
|
}
|
|
return num;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
***************************************************************************/
|
|
static int
|
|
is_power_of_two(uint64_t x)
|
|
{
|
|
while ((x&1) == 0)
|
|
x >>= 1;
|
|
return x == 1;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
* Tests if the named parameter on the command-line. We do a little
|
|
* more than a straight string compare, because I get confused
|
|
* whether parameter have punctuation. Is it "--excludefile" or
|
|
* "--exclude-file"? I don't know if it's got that dash. Screw it,
|
|
* I'll just make the code so it don't care.
|
|
***************************************************************************/
|
|
static int
|
|
EQUALS(const char *lhs, const char *rhs)
|
|
{
|
|
for (;;) {
|
|
while (*lhs == '-' || *lhs == '.' || *lhs == '_')
|
|
lhs++;
|
|
while (*rhs == '-' || *rhs == '.' || *rhs == '_')
|
|
rhs++;
|
|
if (*lhs == '\0' && *rhs == '[')
|
|
return 1; /*arrays*/
|
|
if (tolower(*lhs & 0xFF) != tolower(*rhs & 0xFF))
|
|
return 0;
|
|
if (*lhs == '\0')
|
|
return 1;
|
|
lhs++;
|
|
rhs++;
|
|
}
|
|
}
|
|
|
|
static int
|
|
EQUALSx(const char *lhs, const char *rhs, size_t rhs_length)
|
|
{
|
|
for (;;) {
|
|
while (*lhs == '-' || *lhs == '.' || *lhs == '_')
|
|
lhs++;
|
|
while (*rhs == '-' || *rhs == '.' || *rhs == '_')
|
|
rhs++;
|
|
if (*lhs == '\0' && *rhs == '[')
|
|
return 1; /*arrays*/
|
|
if (tolower(*lhs & 0xFF) != tolower(*rhs & 0xFF))
|
|
return 0;
|
|
if (*lhs == '\0')
|
|
return 1;
|
|
lhs++;
|
|
rhs++;
|
|
if (--rhs_length == 0)
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static unsigned
|
|
INDEX_OF(const char *str, char c)
|
|
{
|
|
unsigned i;
|
|
for (i=0; str[i] && str[i] != c; i++)
|
|
;
|
|
return i;
|
|
}
|
|
|
|
static unsigned
|
|
ARRAY(const char *rhs)
|
|
{
|
|
const char *p = strchr(rhs, '[');
|
|
if (p == NULL)
|
|
return 0;
|
|
else
|
|
p++;
|
|
return (unsigned)parseInt(p);
|
|
}
|
|
|
|
/**
|
|
* Called if user specified `--top-ports` on the command-line.
|
|
*/
|
|
static void
|
|
config_top_ports(struct Masscan *masscan, unsigned maxports)
|
|
{
|
|
unsigned i;
|
|
static const unsigned short top_udp_ports[] = {
|
|
161, /* SNMP - should be found on all network equipment */
|
|
135, /* MS-RPC - should be found on all modern Windows */
|
|
500, /* ISAKMP - for establishing IPsec tunnels */
|
|
137, /* NetBIOS-NameService - should be found on old Windows */
|
|
138, /* NetBIOS-Datagram - should be found on old Windows */
|
|
445, /* SMB datagram service */
|
|
67, /* DHCP */
|
|
53, /* DNS */
|
|
1900, /* UPnP - Microsoft-focused local discovery */
|
|
5353, /* mDNS - Apple-focused local discovery */
|
|
4500, /* nat-t-ike - IPsec NAT traversal */
|
|
514, /* syslog - all Unix machiens */
|
|
69, /* TFTP */
|
|
49152, /* first of modern ephemeral ports */
|
|
631, /* IPP - printing protocol for Linux */
|
|
123, /* NTP network time protocol */
|
|
1434, /* MS-SQL server*/
|
|
520, /* RIP - routers use this protocol sometimes */
|
|
7, /* Echo */
|
|
111, /* SunRPC portmapper */
|
|
2049, /* SunRPC NFS */
|
|
5683, /* COAP */
|
|
11211, /* memcached */
|
|
1701, /* L2TP */
|
|
27960, /* quaked amplifier */
|
|
1645, /* RADIUS */
|
|
1812, /* RADIUS */
|
|
1646, /* RADIUS */
|
|
1813, /* RADIUS */
|
|
3343, /* Microsoft Cluster Services */
|
|
2535, /* MADCAP rfc2730 TODO FIXME */
|
|
|
|
};
|
|
|
|
static const unsigned short top_tcp_ports[] = {
|
|
80, 443, 8080, /* also web */
|
|
21, 990, /* FTP, oldie but goodie */
|
|
22, /* SSH, so much infrastructure */
|
|
23, 992, /* Telnet, oldie but still around*/
|
|
24, /* people put things here instead of TelnetSSH*/
|
|
25, 465, 587, 2525, /* SMTP email*/
|
|
5800, 5900, 5901, /* VNC */
|
|
111, /* SunRPC */
|
|
139, 445, /* Microsoft Windows networking */
|
|
135, /* DCEPRC, more Microsoft Windows */
|
|
3389, /* Microsoft Windows RDP */
|
|
88, /* Kerberos, also Microsoft windows */
|
|
389, 636, /* LDAP and MS Win */
|
|
1433, /* MS SQL */
|
|
53, /* DNS */
|
|
2083, 2096, /* cPanel */
|
|
9050, /* ToR */
|
|
8140, /* Puppet */
|
|
11211, /* memcached */
|
|
1098, 1099, /* Java RMI */
|
|
6000, 6001, /* XWindows */
|
|
5060, 5061, /* SIP - session initiation protocool */
|
|
554, /* RTSP */
|
|
548, /* AFP */
|
|
|
|
|
|
1,3,4,6,7,9,13,17,19,20,26,30,32,33,37,42,43,49,70,
|
|
79,81,82,83,84,85,89,90,99,100,106,109,110,113,119,125,
|
|
143,144,146,161,163,179,199,211,212,222,254,255,256,259,264,280,
|
|
301,306,311,340,366,406,407,416,417,425,427,444,458,464,
|
|
465,481,497,500,512,513,514,515,524,541,543,544,545,554,555,563,
|
|
593,616,617,625,631,646,648,666,667,668,683,687,691,700,705,
|
|
711,714,720,722,726,749,765,777,783,787,800,801,808,843,873,880,888,
|
|
898,900,901,902,903,911,912,981,987,993,995,999,1000,1001,
|
|
1002,1007,1009,1010,1011,1021,1022,1023,1024,1025,1026,1027,1028,
|
|
1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,
|
|
1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,
|
|
1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,
|
|
1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,
|
|
1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,
|
|
1094,1095,1096,1097,1100,1102,1104,1105,1106,1107,1108,
|
|
1110,1111,1112,1113,1114,1117,1119,1121,1122,1123,1124,1126,1130,
|
|
1131,1132,1137,1138,1141,1145,1147,1148,1149,1151,1152,1154,1163,
|
|
1164,1165,1166,1169,1174,1175,1183,1185,1186,1187,1192,1198,1199,
|
|
1201,1213,1216,1217,1218,1233,1234,1236,1244,1247,1248,1259,1271,
|
|
1272,1277,1287,1296,1300,1301,1309,1310,1311,1322,1328,1334,1352,
|
|
1417,1434,1443,1455,1461,1494,1500,1501,1503,1521,1524,1533,
|
|
1556,1580,1583,1594,1600,1641,1658,1666,1687,1688,1700,1717,1718,
|
|
1719,1720,1721,1723,1755,1761,1782,1783,1801,1805,1812,1839,1840,
|
|
1862,1863,1864,1875,1900,1914,1935,1947,1971,1972,1974,1984,1998,
|
|
1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2013,
|
|
2020,2021,2022,2030,2033,2034,2035,2038,2040,2041,2042,2043,2045,
|
|
2046,2047,2048,2049,2065,2068,2099,2100,2103,2105,2106,2107,2111,
|
|
2119,2121,2126,2135,2144,2160,2161,2170,2179,2190,2191,2196,2200,
|
|
2222,2251,2260,2288,2301,2323,2366,2381,2382,2383,2393,2394,2399,
|
|
2401,2492,2500,2522,2557,2601,2602,2604,2605,2607,2608,2638,
|
|
2701,2702,2710,2717,2718,2725,2800,2809,2811,2869,2875,2909,2910,
|
|
2920,2967,2968,2998,3000,3001,3003,3005,3006,3007,3011,3013,3017,
|
|
3030,3031,3052,3071,3077,3128,3168,3211,3221,3260,3261,3268,3269,
|
|
3283,3300,3301,3306,3322,3323,3324,3325,3333,3351,3367,3369,3370,
|
|
3371,3372,3389,3390,3404,3476,3493,3517,3527,3546,3551,3580,3659,
|
|
3689,3690,3703,3737,3766,3784,3800,3801,3809,3814,3826,3827,3828,
|
|
3851,3869,3871,3878,3880,3889,3905,3914,3918,3920,3945,3971,3986,
|
|
3995,3998,4000,4001,4002,4003,4004,4005,4006,4045,4111,4125,4126,
|
|
4129,4224,4242,4279,4321,4343,4443,4444,4445,4446,4449,4550,4567,
|
|
4662,4848,4899,4900,4998,5000,5001,5002,5003,5004,5009,5030,5033,
|
|
5050,5051,5054,5080,5087,5100,5101,5102,5120,5190,5200,
|
|
5214,5221,5222,5225,5226,5269,5280,5298,5357,5405,5414,5431,5432,
|
|
5440,5500,5510,5544,5550,5555,5560,5566,5631,5633,5666,5678,5679,
|
|
5718,5730,5801,5802,5810,5811,5815,5822,5825,5850,5859,5862,
|
|
5877,5902,5903,5904,5906,5907,5910,5911,5915,5922,5925,
|
|
5950,5952,5959,5960,5961,5962,5963,5987,5988,5989,5998,5999,
|
|
6002,6003,6004,6005,6006,6007,6009,6025,6059,6100,6101,6106,
|
|
6112,6123,6129,6156,6346,6389,6502,6510,6543,6547,6565,6566,6567,
|
|
6580,6646,6666,6667,6668,6669,6689,6692,6699,6779,6788,6789,6792,
|
|
6839,6881,6901,6969,7000,7001,7002,7004,7007,7019,7025,7070,7100,
|
|
7103,7106,7200,7201,7402,7435,7443,7496,7512,7625,7627,7676,7741,
|
|
7777,7778,7800,7911,7920,7921,7937,7938,7999,8000,8001,8002,8007,
|
|
8008,8009,8010,8011,8021,8022,8031,8042,8045,8080,8081,8082,8083,
|
|
8084,8085,8086,8087,8088,8089,8090,8093,8099,8100,8180,8181,8192,
|
|
8193,8194,8200,8222,8254,8290,8291,8292,8300,8333,8383,8400,8402,
|
|
8443,8500,8600,8649,8651,8652,8654,8701,8800,8873,8888,8899,8994,
|
|
9000,9001,9002,9003,9009,9010,9011,9040,9071,9080,9081,9090,
|
|
9091,9099,9100,9101,9102,9103,9110,9111,9200,9207,9220,9290,9415,
|
|
9418,9485,9500,9502,9503,9535,9575,9593,9594,9595,9618,9666,9876,
|
|
9877,9878,9898,9900,9917,9929,9943,9944,9968,9998,9999,10000,10001,
|
|
10002,10003,10004,10009,10010,10012,10024,10025,10082,10180,10215,
|
|
10243,10566,10616,10617,10621,10626,10628,10629,10778,11110,11111,
|
|
11967,12000,12174,12265,12345,13456,13722,13782,13783,14000,14238,
|
|
14441,14442,15000,15002,15003,15004,15660,15742,16000,16001,16012,
|
|
16016,16018,16080,16113,16992,16993,17877,17988,18040,18101,18988,
|
|
19101,19283,19315,19350,19780,19801,19842,20000,20005,20031,20221,
|
|
20222,20828,21571,22939,23502,24444,24800,25734,25735,26214,27000,
|
|
27352,27353,27355,27356,27715,28201,30000,30718,30951,31038,31337,
|
|
32768,32769,32770,32771,32772,32773,32774,32775,32776,32777,32778,
|
|
32779,32780,32781,32782,32783,32784,32785,33354,33899,34571,34572,
|
|
34573,35500,38292,40193,40911,41511,42510,44176,44442,44443,44501,
|
|
45100,48080,49152,49153,49154,49155,49156,49157,49158,49159,49160,
|
|
49161,49163,49165,49167,49175,49176,49400,49999,50000,50001,50002,
|
|
50003,50006,50300,50389,50500,50636,50800,51103,51493,52673,52822,
|
|
52848,52869,54045,54328,55055,55056,55555,55600,56737,56738,57294,
|
|
57797,58080,60020,60443,61532,61900,62078,63331,64623,64680,65000,
|
|
65129,65389};
|
|
struct RangeList *ports = &masscan->targets.ports;
|
|
static const unsigned max_tcp_ports = sizeof(top_tcp_ports)/sizeof(top_tcp_ports[0]);
|
|
static const unsigned max_udp_ports = sizeof(top_udp_ports)/sizeof(top_udp_ports[0]);
|
|
|
|
|
|
if (masscan->scan_type.tcp) {
|
|
LOG(2, "[+] adding TCP top-ports = %u\n", maxports);
|
|
for (i=0; i<maxports && i<max_tcp_ports; i++)
|
|
rangelist_add_range_tcp(ports,
|
|
top_tcp_ports[i],
|
|
top_tcp_ports[i]);
|
|
}
|
|
|
|
if (masscan->scan_type.udp) {
|
|
LOG(2, "[+] adding UDP top-ports = %u\n", maxports);
|
|
for (i=0; i<maxports && i<max_udp_ports; i++)
|
|
rangelist_add_range_udp(ports,
|
|
top_udp_ports[i],
|
|
top_udp_ports[i]);
|
|
}
|
|
|
|
/* Targets must be sorted after every change, before being used */
|
|
rangelist_sort(ports);
|
|
}
|
|
|
|
/***************************************************************************
|
|
***************************************************************************/
|
|
static int
|
|
isInteger(const char *value)
|
|
{
|
|
size_t i;
|
|
|
|
if (value == NULL)
|
|
return 0;
|
|
|
|
for (i=0; value[i]; i++)
|
|
if (!isdigit(value[i]&0xFF))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
/***************************************************************************
|
|
***************************************************************************/
|
|
typedef int (*SET_PARAMETER)(struct Masscan *masscan, const char *name, const char *value);
|
|
enum {CONF_OK, CONF_WARN, CONF_ERR};
|
|
|
|
static int SET_arpscan(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
struct Range range;
|
|
|
|
UNUSEDPARM(name);
|
|
UNUSEDPARM(value);
|
|
|
|
if (masscan->echo) {
|
|
if (masscan->scan_type.arp || masscan->echo_all)
|
|
fprintf(masscan->echo, "arpscan = %s\n", masscan->scan_type.arp?"true":"false");
|
|
return 0;
|
|
}
|
|
range.begin = Templ_ARP;
|
|
range.end = Templ_ARP;
|
|
rangelist_add_range(&masscan->targets.ports, range.begin, range.end);
|
|
rangelist_sort(&masscan->targets.ports);
|
|
masscan_set_parameter(masscan, "router-mac", "ff-ff-ff-ff-ff-ff");
|
|
masscan->scan_type.arp = 1;
|
|
LOG(5, "--arpscan\n");
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_banners(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if (masscan->is_banners || masscan->echo_all)
|
|
fprintf(masscan->echo, "banners = %s\n", masscan->is_banners?"true":"false");
|
|
return 0;
|
|
}
|
|
masscan->is_banners = parseBoolean(value);
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_banners_rawudp(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if (masscan->is_banners_rawudp || masscan->echo_all)
|
|
fprintf(masscan->echo, "rawudp = %s\n", masscan->is_banners_rawudp?"true":"false");
|
|
return 0;
|
|
}
|
|
masscan->is_banners_rawudp = parseBoolean(value);
|
|
if (masscan->is_banners_rawudp)
|
|
masscan->is_banners = true;
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_capture(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
if (masscan->echo) {
|
|
if (!masscan->is_capture_cert || masscan->echo_all)
|
|
fprintf(masscan->echo, "%scapture = cert\n", masscan->is_capture_cert?"":"no");
|
|
if (!masscan->is_capture_servername || masscan->echo_all)
|
|
fprintf(masscan->echo, "%scapture = servername\n", masscan->is_capture_servername?"":"no");
|
|
if (masscan->is_capture_html || masscan->echo_all)
|
|
fprintf(masscan->echo, "%scapture = html\n", masscan->is_capture_html?"":"no");
|
|
if (masscan->is_capture_heartbleed || masscan->echo_all)
|
|
fprintf(masscan->echo, "%scapture = heartbleed\n", masscan->is_capture_heartbleed?"":"no");
|
|
if (masscan->is_capture_ticketbleed || masscan->echo_all)
|
|
fprintf(masscan->echo, "%scapture = ticketbleed\n", masscan->is_capture_ticketbleed?"":"no");
|
|
return 0;
|
|
}
|
|
if (EQUALS("capture", name)) {
|
|
if (EQUALS("cert", value))
|
|
masscan->is_capture_cert = 1;
|
|
else if (EQUALS("servername", value))
|
|
masscan->is_capture_servername = 1;
|
|
else if (EQUALS("html", value))
|
|
masscan->is_capture_html = 1;
|
|
else if (EQUALS("heartbleed", value))
|
|
masscan->is_capture_heartbleed = 1;
|
|
else if (EQUALS("ticketbleed", value))
|
|
masscan->is_capture_ticketbleed = 1;
|
|
else {
|
|
fprintf(stderr, "FAIL: %s: unknown capture type\n", value);
|
|
return CONF_ERR;
|
|
}
|
|
} else if (EQUALS("nocapture", name)) {
|
|
if (EQUALS("cert", value))
|
|
masscan->is_capture_cert = 0;
|
|
else if (EQUALS("servername", value))
|
|
masscan->is_capture_servername = 0;
|
|
else if (EQUALS("html", value))
|
|
masscan->is_capture_html = 0;
|
|
else if (EQUALS("heartbleed", value))
|
|
masscan->is_capture_heartbleed = 0;
|
|
else if (EQUALS("ticketbleed", value))
|
|
masscan->is_capture_ticketbleed = 0;
|
|
else {
|
|
fprintf(stderr, "FAIL: %s: unknown nocapture type\n", value);
|
|
return CONF_ERR;
|
|
}
|
|
}
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_hello(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if (masscan->is_hello_ssl) {
|
|
fprintf(masscan->echo, "hello = ssl\n");
|
|
} else if (masscan->is_hello_smbv1) {
|
|
fprintf(masscan->echo, "hello = smbv1\n");
|
|
} else if (masscan->is_hello_http) {
|
|
fprintf(masscan->echo, "hello = http\n");
|
|
}
|
|
return 0;
|
|
}
|
|
if (EQUALS("ssl", value))
|
|
masscan->is_hello_ssl = 1;
|
|
else if (EQUALS("smbv1", value))
|
|
masscan->is_hello_smbv1 = 1;
|
|
else if (EQUALS("http", value))
|
|
masscan->is_hello_http = 1;
|
|
else {
|
|
fprintf(stderr, "FAIL: %s: unknown hello type\n", value);
|
|
return CONF_ERR;
|
|
}
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_hello_file(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
unsigned index;
|
|
FILE *fp;
|
|
char buf[16384];
|
|
char buf2[16384];
|
|
size_t bytes_read;
|
|
size_t bytes_encoded;
|
|
char foo[64];
|
|
|
|
if (masscan->echo) {
|
|
//Echoed as a string "hello-string" that was originally read
|
|
//from a file, not the "hello-filename"
|
|
return 0;
|
|
}
|
|
|
|
index = ARRAY(name);
|
|
if (index >= 65536) {
|
|
fprintf(stderr, "%s: bad index\n", name);
|
|
return CONF_ERR;
|
|
}
|
|
|
|
/* When connecting via TCP, send this file */
|
|
fp = fopen(value, "rb");
|
|
if (fp == NULL) {
|
|
LOG(0, "[-] [FAILED] --hello-file\n");
|
|
LOG(0, "[-] %s: %s\n", value, strerror(errno));
|
|
return CONF_ERR;
|
|
}
|
|
|
|
bytes_read = fread(buf, 1, sizeof(buf), fp);
|
|
if (bytes_read == 0) {
|
|
LOG(0, "[FAILED] could not read hello file\n");
|
|
perror(value);
|
|
fclose(fp);
|
|
return CONF_ERR;
|
|
}
|
|
fclose(fp);
|
|
|
|
bytes_encoded = base64_encode(buf2, sizeof(buf2)-1, buf, bytes_read);
|
|
buf2[bytes_encoded] = '\0';
|
|
|
|
snprintf(foo, sizeof(foo), "hello-string[%u]", (unsigned)index);
|
|
|
|
masscan_set_parameter(masscan, foo, buf2);
|
|
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_hello_string(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
unsigned index;
|
|
char *value2;
|
|
struct TcpCfgPayloads *pay;
|
|
|
|
if (masscan->echo) {
|
|
for (pay = masscan->payloads.tcp; pay; pay = pay->next) {
|
|
fprintf(masscan->echo, "hello-string[%u] = %s\n",
|
|
pay->port, pay->payload_base64);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
index = ARRAY(name);
|
|
if (index >= 65536) {
|
|
fprintf(stderr, "%s: bad index\n", name);
|
|
exit(1);
|
|
}
|
|
|
|
|
|
value2 = STRDUP(value);
|
|
|
|
pay = MALLOC(sizeof(*pay));
|
|
|
|
pay->payload_base64 = value2;
|
|
pay->port = index;
|
|
pay->next = masscan->payloads.tcp;
|
|
masscan->payloads.tcp = pay;
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_hello_timeout(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if (masscan->tcp_hello_timeout || masscan->echo_all)
|
|
fprintf(masscan->echo, "hello-timeout = %u\n", masscan->tcp_hello_timeout);
|
|
return 0;
|
|
}
|
|
masscan->tcp_hello_timeout = (unsigned)parseInt(value);
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_http_cookie(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
unsigned char *newvalue;
|
|
size_t value_length;
|
|
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if (masscan->http.cookies_count || masscan->echo_all) {
|
|
size_t i;
|
|
for (i=0; i<masscan->http.cookies_count; i++) {
|
|
fprintf(masscan->echo,
|
|
"http-cookie = %.*s\n",
|
|
(unsigned)masscan->http.cookies[i].value_length,
|
|
masscan->http.cookies[i].value);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* allocate new value */
|
|
value_length = strlen(value);
|
|
newvalue = MALLOC(value_length+1);
|
|
memcpy(newvalue, value, value_length+1);
|
|
newvalue[value_length] = '\0';
|
|
|
|
/* Add to our list of headers */
|
|
if (masscan->http.cookies_count < sizeof(masscan->http.cookies)/sizeof(masscan->http.cookies[0])) {
|
|
size_t x = masscan->http.cookies_count;
|
|
masscan->http.cookies[x].value = newvalue;
|
|
masscan->http.cookies[x].value_length = value_length;
|
|
masscan->http.cookies_count++;
|
|
}
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_http_header(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
unsigned name_length;
|
|
char *newname;
|
|
unsigned char *newvalue;
|
|
size_t value_length;
|
|
|
|
if (masscan->echo) {
|
|
if (masscan->http.headers_count || masscan->echo_all) {
|
|
size_t i;
|
|
for (i=0; i<masscan->http.headers_count; i++) {
|
|
if (masscan->http.headers[i].name == 0)
|
|
continue;
|
|
fprintf(masscan->echo,
|
|
"http-header = %s:%.*s\n",
|
|
masscan->http.headers[i].name,
|
|
(unsigned)masscan->http.headers[i].value_length,
|
|
masscan->http.headers[i].value);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* allocate a new name
|
|
*/
|
|
name += 11;
|
|
if (*name == '[') {
|
|
/* Specified as: "--http-header[name] value" */
|
|
while (ispunct(*name))
|
|
name++;
|
|
name_length = (unsigned)strlen(name);
|
|
while (name_length && ispunct(name[name_length-1]))
|
|
name_length--;
|
|
newname = MALLOC(name_length+1);
|
|
memcpy(newname, name, name_length+1);
|
|
newname[name_length] = '\0';
|
|
} else if (strchr(value, ':')) {
|
|
/* Specified as: "--http-header Name:value" */
|
|
name_length = INDEX_OF(value, ':');
|
|
newname = MALLOC(name_length + 1);
|
|
memcpy(newname, value, name_length + 1);
|
|
|
|
/* Trim the value */
|
|
value = value + name_length + 1;
|
|
while (*value && isspace(*value & 0xFF))
|
|
value++;
|
|
|
|
/* Trim the name */
|
|
while (name_length && isspace(newname[name_length-1]&0xFF))
|
|
name_length--;
|
|
newname[name_length] = '\0';
|
|
} else {
|
|
fprintf(stderr, "[-] --http-header needs both a name and value\n");
|
|
fprintf(stderr, " hint: \"--http-header Name:value\"\n");
|
|
exit(1);
|
|
}
|
|
|
|
/* allocate new value */
|
|
value_length = strlen(value);
|
|
newvalue = MALLOC(value_length+1);
|
|
memcpy(newvalue, value, value_length+1);
|
|
newvalue[value_length] = '\0';
|
|
|
|
/* Add to our list of headers */
|
|
if (masscan->http.headers_count < sizeof(masscan->http.headers)/sizeof(masscan->http.headers[0])) {
|
|
size_t x = masscan->http.headers_count;
|
|
masscan->http.headers[x].name = newname;
|
|
masscan->http.headers[x].value = newvalue;
|
|
masscan->http.headers[x].value_length = value_length;
|
|
masscan->http.headers_count++;
|
|
}
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_http_method(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if (masscan->http.method || masscan->echo_all)
|
|
fprintf(masscan->echo, "http-method = %.*s\n", (unsigned)masscan->http.method_length, masscan->http.method);
|
|
return 0;
|
|
}
|
|
if (masscan->http.method)
|
|
free(masscan->http.method);
|
|
masscan->http.method_length = strlen(value);
|
|
masscan->http.method = MALLOC(masscan->http.method_length+1);
|
|
memcpy(masscan->http.method, value, masscan->http.method_length+1);
|
|
return CONF_OK;
|
|
}
|
|
static int SET_http_url(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if (masscan->http.url || masscan->echo_all)
|
|
fprintf(masscan->echo, "http-url = %.*s\n", (unsigned)masscan->http.url_length, masscan->http.url);
|
|
return 0;
|
|
}
|
|
if (masscan->http.url)
|
|
free(masscan->http.url);
|
|
masscan->http.url_length = strlen(value);
|
|
masscan->http.url = MALLOC(masscan->http.url_length+1);
|
|
memcpy(masscan->http.url, value, masscan->http.url_length+1);
|
|
return CONF_OK;
|
|
}
|
|
static int SET_http_version(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if (masscan->http.version || masscan->echo_all)
|
|
fprintf(masscan->echo, "http-version = %.*s\n", (unsigned)masscan->http.version_length, masscan->http.version);
|
|
return 0;
|
|
}
|
|
if (masscan->http.version)
|
|
free(masscan->http.version);
|
|
masscan->http.version_length = strlen(value);
|
|
masscan->http.version = MALLOC(masscan->http.version_length+1);
|
|
memcpy(masscan->http.version, value, masscan->http.version_length+1);
|
|
return CONF_OK;
|
|
}
|
|
static int SET_http_host(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if (masscan->http.host || masscan->echo_all)
|
|
fprintf(masscan->echo, "http-host = %.*s\n", (unsigned)masscan->http.host_length, masscan->http.host);
|
|
return 0;
|
|
}
|
|
if (masscan->http.host)
|
|
free(masscan->http.host);
|
|
masscan->http.host_length = strlen(value);
|
|
masscan->http.host = MALLOC(masscan->http.host_length+1);
|
|
memcpy(masscan->http.host, value, masscan->http.host_length+1);
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_http_user_agent(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if (masscan->http.user_agent || masscan->echo_all)
|
|
fprintf(masscan->echo, "http-user-agent = %.*s\n", (unsigned)masscan->http.user_agent_length, masscan->http.user_agent);
|
|
return 0;
|
|
}
|
|
if (masscan->http.user_agent)
|
|
free(masscan->http.user_agent);
|
|
masscan->http.user_agent_length = strlen(value);
|
|
masscan->http.user_agent = MALLOC(masscan->http.user_agent_length+1);
|
|
memcpy( masscan->http.user_agent,
|
|
value,
|
|
masscan->http.user_agent_length+1
|
|
);
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_http_payload(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if (masscan->http.payload || masscan->echo_all)
|
|
fprintf(masscan->echo, "http-payload = %.*s\n", (unsigned)masscan->http.payload_length, masscan->http.payload);
|
|
return 0;
|
|
}
|
|
masscan->http.payload_length = strlen(value);
|
|
masscan->http.payload = REALLOC(masscan->http.payload, masscan->http.payload_length+1);
|
|
memcpy( masscan->http.payload,
|
|
value,
|
|
masscan->http.payload_length+1
|
|
);
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_status_ndjson(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
|
|
if (masscan->echo) {
|
|
if (masscan->output.is_status_ndjson || masscan->echo_all)
|
|
fprintf(masscan->echo, "ndjson-status = %s\n", masscan->output.is_status_ndjson?"true":"false");
|
|
return 0;
|
|
}
|
|
masscan->output.is_status_ndjson = parseBoolean(value);
|
|
return CONF_OK;
|
|
}
|
|
static int SET_status_json(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
/* NOTE: this is here just to warn people they mistyped it */
|
|
UNUSEDPARM(name);
|
|
UNUSEDPARM(value);
|
|
|
|
if (masscan->echo) {
|
|
return 0;
|
|
}
|
|
fprintf(stderr, "[-] FAIL: %s not supported, use --status-ndjson\n", name);
|
|
fprintf(stderr, " hint: new-line delimited JSON status is what we use\n");
|
|
return CONF_ERR;
|
|
}
|
|
|
|
static int SET_min_packet(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if (masscan->min_packet_size != 60 || masscan->echo_all)
|
|
fprintf(masscan->echo, "min-packet = %u\n", masscan->min_packet_size);
|
|
return 0;
|
|
}
|
|
masscan->min_packet_size = (unsigned)parseInt(value);
|
|
return CONF_OK;
|
|
}
|
|
|
|
|
|
static int SET_nobanners(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
return 0;
|
|
}
|
|
masscan->is_banners = !parseBoolean(value);
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_noreset(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if (masscan->is_noreset || masscan->echo_all)
|
|
fprintf(masscan->echo, "noreset = %s\n", masscan->is_noreset?"true":"false");
|
|
return 0;
|
|
}
|
|
masscan->is_noreset = parseBoolean(value);
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_nmap_payloads(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
|
|
if (masscan->echo) {
|
|
if ((masscan->payloads.nmap_payloads_filename && masscan->payloads.nmap_payloads_filename[0]) || masscan->echo_all)
|
|
fprintf(masscan->echo, "nmap-payloads = %s\n", masscan->payloads.nmap_payloads_filename);
|
|
return 0;
|
|
}
|
|
|
|
if (masscan->payloads.nmap_payloads_filename)
|
|
free(masscan->payloads.nmap_payloads_filename);
|
|
masscan->payloads.nmap_payloads_filename = strdup(value);
|
|
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_nmap_service_probes(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
|
|
if (masscan->echo) {
|
|
if ((masscan->payloads.nmap_service_probes_filename && masscan->payloads.nmap_service_probes_filename[0]) || masscan->echo_all)
|
|
fprintf(masscan->echo, "nmap-service-probes = %s\n", masscan->payloads.nmap_service_probes_filename);
|
|
return 0;
|
|
}
|
|
|
|
if (masscan->payloads.nmap_service_probes_filename)
|
|
free(masscan->payloads.nmap_service_probes_filename);
|
|
masscan->payloads.nmap_service_probes_filename = strdup(value);
|
|
|
|
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_offline(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
|
|
if (masscan->echo) {
|
|
if (masscan->is_offline || masscan->echo_all)
|
|
fprintf(masscan->echo, "offline = %s\n", masscan->is_offline?"true":"false");
|
|
return 0;
|
|
}
|
|
masscan->is_offline = parseBoolean(value);
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_output_append(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
if (masscan->echo) {
|
|
if (masscan->output.is_append || masscan->echo_all)
|
|
fprintf(masscan->echo, "output-append = %s\n",
|
|
masscan->output.is_append?"true":"false");
|
|
return 0;
|
|
}
|
|
if (EQUALS("overwrite", name) || !parseBoolean(value))
|
|
masscan->output.is_append = 0;
|
|
else
|
|
masscan->output.is_append = 1;
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_output_filename(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if (masscan->output.filename[0] || masscan->echo_all)
|
|
fprintf(masscan->echo, "output-filename = %s\n", masscan->output.filename);
|
|
return 0;
|
|
}
|
|
if (masscan->output.format == 0)
|
|
masscan->output.format = Output_XML; /*TODO: Why is the default XML?*/
|
|
safe_strcpy(masscan->output.filename,
|
|
sizeof(masscan->output.filename),
|
|
value);
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_output_format(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
enum OutputFormat x = 0;
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
FILE *fp = masscan->echo;
|
|
ipaddress_formatted_t fmt;
|
|
switch (masscan->output.format) {
|
|
case Output_Default: if (masscan->echo_all) fprintf(fp, "output-format = interactive\n"); break;
|
|
case Output_Interactive:fprintf(fp, "output-format = interactive\n"); break;
|
|
case Output_List: fprintf(fp, "output-format = list\n"); break;
|
|
case Output_Unicornscan:fprintf(fp, "output-format = unicornscan\n"); break;
|
|
case Output_XML: fprintf(fp, "output-format = xml\n"); break;
|
|
case Output_Binary: fprintf(fp, "output-format = binary\n"); break;
|
|
case Output_Grepable: fprintf(fp, "output-format = grepable\n"); break;
|
|
case Output_JSON: fprintf(fp, "output-format = json\n"); break;
|
|
case Output_NDJSON: fprintf(fp, "output-format = ndjson\n"); break;
|
|
case Output_Certs: fprintf(fp, "output-format = certs\n"); break;
|
|
case Output_None: fprintf(fp, "output-format = none\n"); break;
|
|
case Output_Hostonly: fprintf(fp, "output-format = hostonly\n"); break;
|
|
case Output_Redis:
|
|
fmt = ipaddress_fmt(masscan->redis.ip);
|
|
fprintf(fp, "output-format = redis\n");
|
|
fprintf(fp, "redis = %s %u\n", fmt.string, masscan->redis.port);
|
|
break;
|
|
|
|
default:
|
|
fprintf(fp, "output-format = unknown(%u)\n", masscan->output.format);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
if (EQUALS("unknown(0)", value)) x = Output_Interactive;
|
|
else if (EQUALS("interactive", value)) x = Output_Interactive;
|
|
else if (EQUALS("list", value)) x = Output_List;
|
|
else if (EQUALS("unicornscan", value)) x = Output_Unicornscan;
|
|
else if (EQUALS("xml", value)) x = Output_XML;
|
|
else if (EQUALS("binary", value)) x = Output_Binary;
|
|
else if (EQUALS("greppable", value)) x = Output_Grepable;
|
|
else if (EQUALS("grepable", value)) x = Output_Grepable;
|
|
else if (EQUALS("json", value)) x = Output_JSON;
|
|
else if (EQUALS("ndjson", value)) x = Output_NDJSON;
|
|
else if (EQUALS("certs", value)) x = Output_Certs;
|
|
else if (EQUALS("none", value)) x = Output_None;
|
|
else if (EQUALS("redis", value)) x = Output_Redis;
|
|
else if (EQUALS("hostonly", value)) x = Output_Hostonly;
|
|
else {
|
|
LOG(0, "FAIL: unknown output-format: %s\n", value);
|
|
LOG(0, " hint: 'binary', 'xml', 'grepable', ...\n");
|
|
return CONF_ERR;
|
|
}
|
|
masscan->output.format = x;
|
|
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_output_noshow(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if (masscan->echo_all) {
|
|
fprintf(masscan->echo, "output-noshow = %s%s%s\n",
|
|
(!masscan->output.is_show_open)?"open,":"",
|
|
(!masscan->output.is_show_closed)?"closed,":"",
|
|
(!masscan->output.is_show_host)?"host,":""
|
|
);
|
|
}
|
|
return 0;
|
|
}
|
|
for (;;) {
|
|
const char *val2 = value;
|
|
unsigned val2_len = INDEX_OF(val2, ',');
|
|
if (val2_len == 0)
|
|
break;
|
|
if (EQUALSx("open", val2, val2_len))
|
|
masscan->output.is_show_open = 0;
|
|
else if (EQUALSx("closed", val2, val2_len) || EQUALSx("close", val2, val2_len))
|
|
masscan->output.is_show_closed = 0;
|
|
else if (EQUALSx("open", val2, val2_len))
|
|
masscan->output.is_show_host = 0;
|
|
else if (EQUALSx("all",val2,val2_len)) {
|
|
masscan->output.is_show_open = 0;
|
|
masscan->output.is_show_host = 0;
|
|
masscan->output.is_show_closed = 0;
|
|
}
|
|
else {
|
|
LOG(0, "FAIL: unknown 'noshow' spec: %.*s\n", val2_len, val2);
|
|
exit(1);
|
|
}
|
|
value += val2_len;
|
|
while (*value == ',')
|
|
value++;
|
|
}
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_output_show(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if (masscan->echo_all) {
|
|
fprintf(masscan->echo, "output-show = %s%s%s\n",
|
|
masscan->output.is_show_open?"open,":"",
|
|
masscan->output.is_show_closed?"closed,":"",
|
|
masscan->output.is_show_host?"host,":""
|
|
);
|
|
}
|
|
return 0;
|
|
}
|
|
for (;;) {
|
|
const char *val2 = value;
|
|
unsigned val2_len = INDEX_OF(val2, ',');
|
|
if (val2_len == 0)
|
|
break;
|
|
if (EQUALSx("open", val2, val2_len))
|
|
masscan->output.is_show_open = 1;
|
|
else if (EQUALSx("closed", val2, val2_len) || EQUALSx("close", val2, val2_len))
|
|
masscan->output.is_show_closed = 1;
|
|
else if (EQUALSx("open", val2, val2_len))
|
|
masscan->output.is_show_host = 1;
|
|
else if (EQUALSx("all",val2,val2_len)) {
|
|
masscan->output.is_show_open = 1;
|
|
masscan->output.is_show_host = 1;
|
|
masscan->output.is_show_closed = 1;
|
|
}
|
|
else {
|
|
LOG(0, "FAIL: unknown 'show' spec: %.*s\n", val2_len, val2);
|
|
exit(1);
|
|
}
|
|
value += val2_len;
|
|
while (*value == ',')
|
|
value++;
|
|
}
|
|
return CONF_OK;
|
|
}
|
|
static int SET_output_show_open(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
UNUSEDPARM(value);
|
|
if (masscan->echo) {
|
|
return 0;
|
|
}
|
|
/* "open" "open-only" */
|
|
masscan->output.is_show_open = 1;
|
|
masscan->output.is_show_closed = 0;
|
|
masscan->output.is_show_host = 0;
|
|
return CONF_OK;
|
|
}
|
|
|
|
/* Specifies a 'libpcap' file where the received packets will be written.
|
|
* This is useful while debugging so that we can see what exactly is
|
|
* going on. It's also an alternate mode for getting output from this
|
|
* program. Instead of relying upon this program's determination of what
|
|
* ports are open or closed, you can instead simply parse this capture
|
|
* file yourself and make your own determination */
|
|
static int SET_pcap_filename(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if (masscan->pcap_filename[0])
|
|
fprintf(masscan->echo, "pcap-filename = %s\n", masscan->pcap_filename);
|
|
return 0;
|
|
}
|
|
if (value)
|
|
safe_strcpy(masscan->pcap_filename, sizeof(masscan->pcap_filename), value);
|
|
return CONF_OK;
|
|
}
|
|
|
|
/* Specifies a 'libpcap' file from which to read packet-payloads. The payloads found
|
|
* in this file will serve as the template for spewing out custom packets. There are
|
|
* other options that can set payloads as well, like "--nmap-payloads" for reading
|
|
* their custom payload file, as well as the various "hello" options for specifying
|
|
* the string sent to the server once a TCP connection has been established. */
|
|
static int SET_pcap_payloads(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if ((masscan->payloads.pcap_payloads_filename && masscan->payloads.pcap_payloads_filename[0]) || masscan->echo_all)
|
|
fprintf(masscan->echo, "pcap-payloads = %s\n", masscan->payloads.pcap_payloads_filename);
|
|
return 0;
|
|
}
|
|
|
|
if (masscan->payloads.pcap_payloads_filename)
|
|
free(masscan->payloads.pcap_payloads_filename);
|
|
masscan->payloads.pcap_payloads_filename = strdup(value);
|
|
|
|
/* file will be loaded in "masscan_load_database_files()" */
|
|
|
|
return CONF_OK;
|
|
}
|
|
|
|
|
|
static int SET_randomize_hosts(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
UNUSEDPARM(value);
|
|
if (masscan->echo) {
|
|
//fprintf(masscan->echo, "randomize-hosts = true\n");
|
|
return 0;
|
|
}
|
|
return CONF_OK;
|
|
}
|
|
|
|
|
|
static int SET_rate(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
double rate = 0.0;
|
|
double point = 10.0;
|
|
unsigned i;
|
|
|
|
if (masscan->echo) {
|
|
if ((unsigned)(masscan->max_rate * 100000) % 100000) {
|
|
/* print as floating point number, which is rare */
|
|
fprintf(masscan->echo, "rate = %f\n", masscan->max_rate);
|
|
} else {
|
|
/* pretty print as just an integer, which is what most people
|
|
* expect */
|
|
fprintf(masscan->echo, "rate = %-10.0f\n", masscan->max_rate);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
for (i=0; value[i] && value[i] != '.'; i++) {
|
|
char c = value[i];
|
|
if (c < '0' || '9' < c) {
|
|
fprintf(stderr, "CONF: non-digit in rate spec: %s=%s\n", name, value);
|
|
return CONF_ERR;
|
|
}
|
|
rate = rate * 10.0 + (c - '0');
|
|
}
|
|
|
|
if (value[i] == '.') {
|
|
i++;
|
|
while (value[i]) {
|
|
char c = value[i];
|
|
if (c < '0' || '9' < c) {
|
|
fprintf(stderr, "CONF: non-digit in rate spec: %s=%s\n",
|
|
name, value);
|
|
return CONF_ERR;
|
|
}
|
|
rate += (c - '0')/point;
|
|
point *= 10.0;
|
|
value++;
|
|
}
|
|
}
|
|
|
|
masscan->max_rate = rate;
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_resume_count(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if (masscan->resume.count || masscan->echo_all) {
|
|
fprintf(masscan->echo, "resume-count = %" PRIu64 "\n", masscan->resume.count);
|
|
}
|
|
return 0;
|
|
}
|
|
masscan->resume.count = parseInt(value);
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_resume_index(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if (masscan->resume.index || masscan->echo_all) {
|
|
fprintf(masscan->echo, "\n# resume information\n");
|
|
fprintf(masscan->echo, "resume-index = %" PRIu64 "\n", masscan->resume.index);
|
|
}
|
|
return 0;
|
|
}
|
|
masscan->resume.index = parseInt(value);
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_retries(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
uint64_t x;
|
|
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if (masscan->retries || masscan->echo_all)
|
|
fprintf(masscan->echo, "retries = %u\n", masscan->retries);
|
|
return 0;
|
|
}
|
|
x = strtoul(value, 0, 0);
|
|
if (x >= 1000) {
|
|
fprintf(stderr, "FAIL: retries=<n>: expected number less than 1000\n");
|
|
return CONF_ERR;
|
|
}
|
|
masscan->retries = (unsigned)x;
|
|
return CONF_OK;
|
|
|
|
}
|
|
|
|
static int SET_rotate_time(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if (masscan->output.rotate.timeout || masscan->echo_all)
|
|
fprintf(masscan->echo, "rotate = %u\n", masscan->output.rotate.timeout);
|
|
return 0;
|
|
}
|
|
masscan->output.rotate.timeout = (unsigned)parseTime(value);
|
|
return CONF_OK;
|
|
}
|
|
static int SET_rotate_directory(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
char *p;
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if (memcmp(masscan->output.rotate.directory, ".",2) != 0 || masscan->echo_all) {
|
|
fprintf(masscan->echo, "rotate-dir = %s\n", masscan->output.rotate.directory);
|
|
}
|
|
return 0;
|
|
}
|
|
safe_strcpy( masscan->output.rotate.directory,
|
|
sizeof(masscan->output.rotate.directory),
|
|
value);
|
|
/* strip trailing slashes */
|
|
p = masscan->output.rotate.directory;
|
|
while (*p && (p[strlen(p)-1] == '/' || p[strlen(p)-1] == '\\')) /* Fix for #561 */
|
|
p[strlen(p)-1] = '\0';
|
|
return CONF_OK;
|
|
}
|
|
static int SET_rotate_offset(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
/* Time offset, otherwise output files are aligned to nearest time
|
|
* interval, e.g. at the start of the hour for "hourly" */
|
|
if (masscan->echo) {
|
|
if (masscan->output.rotate.offset || masscan->echo_all)
|
|
fprintf(masscan->echo, "rotate-offset = %u\n", masscan->output.rotate.offset);
|
|
return 0;
|
|
}
|
|
masscan->output.rotate.offset = (unsigned)parseTime(value);
|
|
return CONF_OK;
|
|
}
|
|
static int SET_rotate_filesize(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if (masscan->output.rotate.filesize || masscan->echo_all)
|
|
fprintf(masscan->echo, "rotate-size = %" PRIu64 "\n", masscan->output.rotate.filesize);
|
|
return 0;
|
|
}
|
|
masscan->output.rotate.filesize = parseSize(value);
|
|
return CONF_OK;
|
|
|
|
}
|
|
|
|
static int SET_script(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if ((masscan->scripting.name && masscan->scripting.name[0]) || masscan->echo_all)
|
|
fprintf(masscan->echo, "script = %s\n", masscan->scripting.name);
|
|
return 0;
|
|
}
|
|
if (value && value[0])
|
|
masscan->is_scripting = 1;
|
|
else
|
|
masscan->is_scripting = 0;
|
|
|
|
if (masscan->scripting.name)
|
|
free(masscan->scripting.name);
|
|
|
|
masscan->scripting.name = strdup(value);
|
|
|
|
return CONF_OK;
|
|
}
|
|
|
|
|
|
static int SET_seed(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
fprintf(masscan->echo, "seed = %" PRIu64 "\n", masscan->seed);
|
|
return 0;
|
|
}
|
|
if (EQUALS("time", value))
|
|
masscan->seed = time(0);
|
|
else
|
|
masscan->seed = parseInt(value);
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_space(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
UNUSEDPARM(value);
|
|
if (masscan->echo) {
|
|
fprintf(masscan->echo, "\n");
|
|
return 0;
|
|
}
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_shard(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
unsigned one = 0;
|
|
unsigned of = 0;
|
|
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if (masscan->shard.of > 0 || masscan->echo_all)
|
|
fprintf(masscan->echo, "shard = %u/%u\n", masscan->shard.one, masscan->shard.of);
|
|
return 0;
|
|
}
|
|
while (isdigit(*value))
|
|
one = one*10 + (*(value++)) - '0';
|
|
while (ispunct(*value))
|
|
value++;
|
|
while (isdigit(*value))
|
|
of = of*10 + (*(value++)) - '0';
|
|
|
|
if (one < 1) {
|
|
LOG(0, "FAIL: shard index can't be zero\n");
|
|
LOG(0, "hint it goes like 1/4 2/4 3/4 4/4\n");
|
|
return CONF_ERR;
|
|
}
|
|
if (one > of) {
|
|
LOG(0, "FAIL: shard spec is wrong\n");
|
|
LOG(0, "hint it goes like 1/4 2/4 3/4 4/4\n");
|
|
return CONF_ERR;
|
|
}
|
|
masscan->shard.one = one;
|
|
masscan->shard.of = of;
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_output_stylesheet(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
UNUSEDPARM(name);
|
|
if (masscan->echo) {
|
|
if (masscan->output.stylesheet[0] || masscan->echo_all)
|
|
fprintf(masscan->echo, "stylesheet = %s\n", masscan->output.stylesheet);
|
|
return 0;
|
|
}
|
|
|
|
if (masscan->output.format == 0)
|
|
masscan->output.format = Output_XML;
|
|
|
|
safe_strcpy(masscan->output.stylesheet, sizeof(masscan->output.stylesheet), value);
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_topports(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
unsigned default_value = 20;
|
|
|
|
if (masscan->echo) {
|
|
/* don't echo: this instead triggers filling the `--port`
|
|
* list, so the ports themselves will be echoed, not this
|
|
* parameter */
|
|
return 0;
|
|
}
|
|
|
|
if (value == 0 || value[0] == '\0') {
|
|
/* can be specified by itself on the command-line, alone
|
|
* without a following parameter */
|
|
/* ex: `--top-ports` */
|
|
masscan->top_ports = default_value;
|
|
} else if (isBoolean(value)) {
|
|
/* ex: `--top-ports enable` */
|
|
if (parseBoolean(value))
|
|
masscan->top_ports = default_value;
|
|
else
|
|
masscan->top_ports = 0;
|
|
} else if (isInteger(value)) {
|
|
/* ex: `--top-ports 5` */
|
|
uint64_t num = parseInt(value);
|
|
masscan->top_ports = (unsigned)num;
|
|
} else {
|
|
fprintf(stderr, "[-] %s: bad value: %s\n", name, value);
|
|
return CONF_ERR;
|
|
}
|
|
return CONF_OK;
|
|
}
|
|
|
|
static int SET_tcp_mss(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
/* h/t @IvreRocks */
|
|
static const unsigned default_mss = 1460;
|
|
|
|
if (masscan->echo) {
|
|
if (masscan->templ_opts) {
|
|
switch (masscan->templ_opts->tcp.is_mss) {
|
|
case Default:
|
|
break;
|
|
case Add:
|
|
if (masscan->templ_opts->tcp.mss == default_mss)
|
|
fprintf(masscan->echo, "tcp-mss = %s\n", "enable");
|
|
else
|
|
fprintf(masscan->echo, "tcp-mss = %u\n",
|
|
masscan->templ_opts->tcp.mss);
|
|
break;
|
|
case Remove:
|
|
fprintf(masscan->echo, "tcp-mss = %s\n", "disable");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (masscan->templ_opts == NULL)
|
|
masscan->templ_opts = calloc(1, sizeof(*masscan->templ_opts));
|
|
|
|
if (value == 0 || value[0] == '\0') {
|
|
/* no following parameter, so interpret this to mean "enable" */
|
|
masscan->templ_opts->tcp.is_mss = Add;
|
|
masscan->templ_opts->tcp.mss = default_mss; /* 1460 */
|
|
} else if (isBoolean(value)) {
|
|
/* looking for "enable" or "disable", but any boolean works,
|
|
* like "true/false" or "off/on" */
|
|
if (parseBoolean(value)) {
|
|
masscan->templ_opts->tcp.is_mss = Add;
|
|
masscan->templ_opts->tcp.mss = default_mss; /* 1460 */
|
|
} else
|
|
masscan->templ_opts->tcp.is_mss = Remove;
|
|
} else if (isInteger(value)) {
|
|
/* A specific number was specified */
|
|
uint64_t num = parseInt(value);
|
|
if (num >= 0x10000)
|
|
goto fail;
|
|
masscan->templ_opts->tcp.is_mss = Add;
|
|
masscan->templ_opts->tcp.mss = (unsigned)num;
|
|
} else
|
|
goto fail;
|
|
|
|
return CONF_OK;
|
|
fail:
|
|
fprintf(stderr, "[-] %s: bad value: %s\n", name, value);
|
|
return CONF_ERR;
|
|
}
|
|
|
|
static int SET_tcp_wscale(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
static const unsigned default_value = 3;
|
|
|
|
if (masscan->echo) {
|
|
if (masscan->templ_opts) {
|
|
switch (masscan->templ_opts->tcp.is_wscale) {
|
|
case Default:
|
|
break;
|
|
case Add:
|
|
if (masscan->templ_opts->tcp.wscale == default_value)
|
|
fprintf(masscan->echo, "tcp-wscale = %s\n", "enable");
|
|
else
|
|
fprintf(masscan->echo, "tcp-wscale = %u\n",
|
|
masscan->templ_opts->tcp.wscale);
|
|
break;
|
|
case Remove:
|
|
fprintf(masscan->echo, "tcp-wscale = %s\n", "disable");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (masscan->templ_opts == NULL)
|
|
masscan->templ_opts = calloc(1, sizeof(*masscan->templ_opts));
|
|
|
|
if (value == 0 || value[0] == '\0') {
|
|
masscan->templ_opts->tcp.is_wscale = Add;
|
|
masscan->templ_opts->tcp.wscale = default_value;
|
|
} else if (isBoolean(value)) {
|
|
if (parseBoolean(value)) {
|
|
masscan->templ_opts->tcp.is_wscale = Add;
|
|
masscan->templ_opts->tcp.wscale = default_value;
|
|
} else
|
|
masscan->templ_opts->tcp.is_wscale = Remove;
|
|
} else if (isInteger(value)) {
|
|
uint64_t num = parseInt(value);
|
|
if (num >= 255)
|
|
goto fail;
|
|
masscan->templ_opts->tcp.is_wscale = Add;
|
|
masscan->templ_opts->tcp.wscale = (unsigned)num;
|
|
} else
|
|
goto fail;
|
|
|
|
return CONF_OK;
|
|
fail:
|
|
fprintf(stderr, "[-] %s: bad value: %s\n", name, value);
|
|
return CONF_ERR;
|
|
}
|
|
|
|
static int SET_tcp_tsecho(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
static const unsigned default_value = 0x12345678;
|
|
|
|
if (masscan->echo) {
|
|
if (masscan->templ_opts) {
|
|
switch (masscan->templ_opts->tcp.is_tsecho) {
|
|
case Default:
|
|
break;
|
|
case Add:
|
|
if (masscan->templ_opts->tcp.tsecho == default_value)
|
|
fprintf(masscan->echo, "tcp-tsecho = %s\n", "enable");
|
|
else
|
|
fprintf(masscan->echo, "tcp-tsecho = %u\n",
|
|
masscan->templ_opts->tcp.tsecho);
|
|
break;
|
|
case Remove:
|
|
fprintf(masscan->echo, "tcp-tsecho = %s\n", "disable");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (masscan->templ_opts == NULL)
|
|
masscan->templ_opts = calloc(1, sizeof(*masscan->templ_opts));
|
|
|
|
if (value == 0 || value[0] == '\0') {
|
|
masscan->templ_opts->tcp.is_tsecho = Add;
|
|
masscan->templ_opts->tcp.tsecho = default_value;
|
|
} else if (isBoolean(value)) {
|
|
if (parseBoolean(value)) {
|
|
masscan->templ_opts->tcp.is_tsecho = Add;
|
|
masscan->templ_opts->tcp.tsecho = default_value;
|
|
} else
|
|
masscan->templ_opts->tcp.is_tsecho = Remove;
|
|
} else if (isInteger(value)) {
|
|
uint64_t num = parseInt(value);
|
|
if (num >= 255)
|
|
goto fail;
|
|
masscan->templ_opts->tcp.is_tsecho = Add;
|
|
masscan->templ_opts->tcp.tsecho = (unsigned)num;
|
|
} else
|
|
goto fail;
|
|
|
|
return CONF_OK;
|
|
fail:
|
|
fprintf(stderr, "[-] %s: bad value: %s\n", name, value);
|
|
return CONF_ERR;
|
|
}
|
|
|
|
static int SET_tcp_sackok(struct Masscan *masscan, const char *name, const char *value)
|
|
{
|
|
if (masscan->echo) {
|
|
if (masscan->templ_opts) {
|
|
switch (masscan->templ_opts->tcp.is_sackok) {
|
|
case Default:
|
|
break;
|
|
case Add:
|
|
fprintf(masscan->echo, "tcp-sackok = %s\n", "enable");
|
|
break;
|
|
case Remove:
|
|
fprintf(masscan->echo, "tcp-sackok = %s\n", "disable");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (masscan->templ_opts == NULL)
|
|
masscan->templ_opts = calloc(1, sizeof(*masscan->templ_opts));
|
|
|
|
if (value == 0 || value[0] == '\0') {
|
|
masscan->templ_opts->tcp.is_sackok = Add;
|
|
} else if (isBoolean(value)) {
|
|
if (parseBoolean(value)) {
|
|
masscan->templ_opts->tcp.is_sackok = Add;
|
|
} else
|
|
masscan->templ_opts->tcp.is_sackok = Remove;
|
|
} else if (isInteger(value)) {
|
|
if (parseInt(value) != 0)
|
|
masscan->templ_opts->tcp.is_sackok = Add;
|
|
} else
|
|
goto fail;
|
|
|
|
return CONF_OK;
|
|
fail:
|
|
fprintf(stderr, "[-] %s: bad value: %s\n", name, value);
|
|
return CONF_ERR;
|
|
}
|
|
|
|
|
|
static int SET_debug_tcp(struct Masscan *masscan, const char *name, const char *value) {
|
|
extern int is_tcp_debug; /* global */
|
|
UNUSEDPARM(name);
|
|
UNUSEDPARM(masscan);
|
|
|
|
if (value == 0 || value[0] == '\0')
|
|
is_tcp_debug = 1;
|
|
else
|
|
is_tcp_debug = parseBoolean(value);
|
|
return CONF_OK;
|
|
}
|
|
|
|
|
|
|
|
struct ConfigParameter {
|
|
const char *name;
|
|
SET_PARAMETER set;
|
|
unsigned flags;
|
|
const char *alts[6];
|
|
};
|
|
enum {F_NONE, F_BOOL=1, F_NUMABLE=2};
|
|
struct ConfigParameter config_parameters[] = {
|
|
{"resume-index", SET_resume_index, 0, {0}},
|
|
{"resume-count", SET_resume_count, 0, {0}},
|
|
{"seed", SET_seed, 0, {0}},
|
|
{"arpscan", SET_arpscan, F_BOOL, {"arp",0}},
|
|
{"randomize-hosts", SET_randomize_hosts, F_BOOL, {0}},
|
|
{"rate", SET_rate, 0, {"max-rate",0}},
|
|
{"shard", SET_shard, 0, {"shards",0}},
|
|
{"banners", SET_banners, F_BOOL, {"banner",0}}, /* --banners */
|
|
{"rawudp", SET_banners_rawudp, F_BOOL, {"rawudp",0}}, /* --rawudp */
|
|
{"nobanners", SET_nobanners, F_BOOL, {"nobanner",0}},
|
|
{"retries", SET_retries, 0, {"retry", "max-retries", "max-retry", 0}},
|
|
{"noreset", SET_noreset, F_BOOL, {0}},
|
|
{"nmap-payloads", SET_nmap_payloads, 0, {"nmap-payload",0}},
|
|
{"nmap-service-probes",SET_nmap_service_probes, 0, {"nmap-service-probe",0}},
|
|
{"offline", SET_offline, F_BOOL, {"notransmit", "nosend", "dry-run", 0}},
|
|
{"pcap-filename", SET_pcap_filename, 0, {"pcap",0}},
|
|
{"pcap-payloads", SET_pcap_payloads, 0, {"pcap-payload",0}},
|
|
{"hello", SET_hello, 0, {0}},
|
|
{"hello-file", SET_hello_file, 0, {"hello-filename",0}},
|
|
{"hello-string", SET_hello_string, 0, {0}},
|
|
{"hello-timeout", SET_hello_timeout, 0, {0}},
|
|
{"http-cookie", SET_http_cookie, 0, {0}},
|
|
{"http-header", SET_http_header, 0, {"http-field", 0}},
|
|
{"http-method", SET_http_method, 0, {0}},
|
|
{"http-version", SET_http_version, 0, {0}},
|
|
{"http-url", SET_http_url, 0, {"http-uri",0}},
|
|
{"http-user-agent", SET_http_user_agent, 0, {"http-useragent",0}},
|
|
{"http-host", SET_http_host, 0, {0}},
|
|
{"http-payload", SET_http_payload, 0, {0}},
|
|
{"ndjson-status", SET_status_ndjson, F_BOOL, {"status-ndjson", 0}},
|
|
{"json-status", SET_status_json, F_BOOL, {"status-json", 0}},
|
|
{"min-packet", SET_min_packet, 0, {"min-pkt",0}},
|
|
{"capture", SET_capture, 0, {0}},
|
|
{"nocapture", SET_capture, 0, {"no-capture", 0}},
|
|
{"SPACE", SET_space, 0, {0}},
|
|
{"output-filename", SET_output_filename, 0, {"output-file",0}},
|
|
{"output-format", SET_output_format, 0, {0}},
|
|
{"output-show", SET_output_show, 0, {"output-status", "show",0}},
|
|
{"output-noshow", SET_output_noshow, 0, {"noshow",0}},
|
|
{"output-show-open",SET_output_show_open, F_BOOL, {"open", "open-only", 0}},
|
|
{"output-append", SET_output_append, 0, {"append-output",0}},
|
|
{"rotate", SET_rotate_time, 0, {"output-rotate", "rotate-output", "rotate-time", 0}},
|
|
{"rotate-dir", SET_rotate_directory, 0, {"output-rotate-dir", "rotate-directory", 0}},
|
|
{"rotate-offset", SET_rotate_offset, 0, {"output-rotate-offset", 0}},
|
|
{"rotate-size", SET_rotate_filesize, 0, {"output-rotate-filesize", "rotate-filesize", 0}},
|
|
{"stylesheet", SET_output_stylesheet, 0, {0}},
|
|
{"script", SET_script, 0, {0}},
|
|
{"SPACE", SET_space, 0, {0}},
|
|
{"tcp-mss", SET_tcp_mss, F_NUMABLE, {"tcpmss",0}},
|
|
{"tcp-wscale", SET_tcp_wscale, F_NUMABLE, {0}},
|
|
{"tcp-tsecho", SET_tcp_tsecho, F_NUMABLE, {0}},
|
|
{"tcp-sackok", SET_tcp_sackok, F_BOOL, {0}},
|
|
{"top-ports", SET_topports, F_NUMABLE, {"top-port",0}},
|
|
|
|
{"debug-tcp", SET_debug_tcp, F_BOOL, {"tcp-debug", 0}},
|
|
{0}
|
|
};
|
|
|
|
/***************************************************************************
|
|
* Called either from the "command-line" parser when it sees a --param,
|
|
* or from the "config-file" parser for normal options.
|
|
***************************************************************************/
|
|
void
|
|
masscan_set_parameter(struct Masscan *masscan,
|
|
const char *name, const char *value)
|
|
{
|
|
unsigned index = ARRAY(name);
|
|
if (index >= 65536) {
|
|
fprintf(stderr, "%s: bad index\n", name);
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* NEW:
|
|
* Go through configured list of parameters
|
|
*/
|
|
{
|
|
size_t i;
|
|
|
|
for (i=0; config_parameters[i].name; i++) {
|
|
if (EQUALS(config_parameters[i].name, name)) {
|
|
config_parameters[i].set(masscan, name, value);
|
|
return;
|
|
} else {
|
|
size_t j;
|
|
for (j=0; config_parameters[i].alts[j]; j++) {
|
|
if (EQUALS(config_parameters[i].alts[j], name)) {
|
|
config_parameters[i].set(masscan, name, value);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* OLD:
|
|
* Configure the old parameters, the ones we don't have in the new config
|
|
* system yet (see the NEW part above).
|
|
* TODO: transition all these old params to the new system
|
|
*/
|
|
if (EQUALS("conf", name) || EQUALS("config", name)) {
|
|
masscan_read_config_file(masscan, value);
|
|
} else if (EQUALS("adapter", name) || EQUALS("if", name) || EQUALS("interface", name)) {
|
|
if (masscan->nic[index].ifname[0]) {
|
|
fprintf(stderr, "CONF: overwriting \"adapter=%s\"\n", masscan->nic[index].ifname);
|
|
}
|
|
if (masscan->nic_count < index + 1)
|
|
masscan->nic_count = index + 1;
|
|
snprintf( masscan->nic[index].ifname,
|
|
sizeof(masscan->nic[index].ifname),
|
|
"%s",
|
|
value);
|
|
|
|
}
|
|
else if (EQUALS("adapter-ip", name) || EQUALS("source-ip", name)
|
|
|| EQUALS("source-address", name) || EQUALS("spoof-ip", name)
|
|
|| EQUALS("spoof-address", name) || EQUALS("src-ip", name)) {
|
|
/* Send packets FROM this IP address */
|
|
struct Range range;
|
|
struct Range6 range6;
|
|
int err;
|
|
|
|
/* Grab the next IPv4 or IPv6 range */
|
|
err = massip_parse_range(value, 0, 0, &range, &range6);
|
|
switch (err) {
|
|
case Ipv4_Address:
|
|
/* If more than one IP address given, make the range is
|
|
* an even power of two (1, 2, 4, 8, 16, ...) */
|
|
if (!is_power_of_two((uint64_t)range.end - range.begin + 1)) {
|
|
LOG(0, "FAIL: range must be even power of two: %s=%s\n",
|
|
name, value);
|
|
exit(1);
|
|
}
|
|
masscan->nic[index].src.ipv4.first = range.begin;
|
|
masscan->nic[index].src.ipv4.last = range.end;
|
|
masscan->nic[index].src.ipv4.range = range.end - range.begin + 1;
|
|
break;
|
|
case Ipv6_Address:
|
|
masscan->nic[index].src.ipv6.first = range6.begin;
|
|
masscan->nic[index].src.ipv6.last = range6.end;
|
|
masscan->nic[index].src.ipv6.range = 1; /* TODO: add support for more than one source */
|
|
break;
|
|
default:
|
|
LOG(0, "FAIL: bad source IP address: %s=%s\n",
|
|
name, value);
|
|
LOG(0, "hint addresses look like \"192.168.1.23\" or \"2001:db8:1::1ce9\".\n");
|
|
exit(1);
|
|
}
|
|
|
|
|
|
|
|
} else if (EQUALS("adapter-port", name) || EQUALS("source-port", name)
|
|
|| EQUALS("src-port", name)) {
|
|
/* Send packets FROM this port number */
|
|
unsigned is_error = 0;
|
|
struct RangeList ports = {0};
|
|
memset(&ports, 0, sizeof(ports));
|
|
|
|
rangelist_parse_ports(&ports, value, &is_error, 0);
|
|
|
|
/* Check if there was an error in parsing */
|
|
if (is_error) {
|
|
LOG(0, "FAIL: bad source port specification: %s\n",
|
|
name);
|
|
exit(1);
|
|
}
|
|
|
|
/* Only allow one range of ports */
|
|
if (ports.count != 1) {
|
|
LOG(0, "FAIL: only one '%s' range may be specified, found %u ranges\n",
|
|
name, ports.count);
|
|
exit(1);
|
|
}
|
|
|
|
/* verify range is even power of 2 (1, 2, 4, 8, 16, ...) */
|
|
if (!is_power_of_two(ports.list[0].end - ports.list[0].begin + 1)) {
|
|
LOG(0, "FAIL: source port range must be even power of two: %s=%s\n",
|
|
name, value);
|
|
exit(1);
|
|
}
|
|
|
|
masscan->nic[index].src.port.first = ports.list[0].begin;
|
|
masscan->nic[index].src.port.last = ports.list[0].end;
|
|
masscan->nic[index].src.port.range = ports.list[0].end - ports.list[0].begin + 1;
|
|
} else if (EQUALS("adapter-mac", name) || EQUALS("spoof-mac", name)
|
|
|| EQUALS("source-mac", name) || EQUALS("src-mac", name)) {
|
|
/* Send packets FROM this MAC address */
|
|
macaddress_t source_mac;
|
|
int err;
|
|
|
|
err = parse_mac_address(value, &source_mac);
|
|
if (err) {
|
|
LOG(0, "[-] CONF: bad MAC address: %s = %s\n", name, value);
|
|
return;
|
|
}
|
|
|
|
/* Check for duplicates */
|
|
if (macaddress_is_equal(masscan->nic[index].source_mac, source_mac)) {
|
|
/* suppresses warning message about duplicate MAC addresses if
|
|
* they are in fact the same */
|
|
return;
|
|
}
|
|
|
|
/* Warn if we are overwriting a Mac address */
|
|
if (masscan->nic[index].my_mac_count != 0) {
|
|
ipaddress_formatted_t fmt1 = macaddress_fmt(masscan->nic[index].source_mac);
|
|
ipaddress_formatted_t fmt2 = macaddress_fmt(source_mac);
|
|
LOG(0, "[-] WARNING: overwriting MAC address, was %s, now %s\n",
|
|
fmt1.string,
|
|
fmt2.string);
|
|
}
|
|
|
|
masscan->nic[index].source_mac = source_mac;
|
|
masscan->nic[index].my_mac_count = 1;
|
|
}
|
|
else if (EQUALS("router-mac", name) || EQUALS("router", name)
|
|
|| EQUALS("dest-mac", name) || EQUALS("destination-mac", name)
|
|
|| EQUALS("dst-mac", name) || EQUALS("target-mac", name)) {
|
|
macaddress_t router_mac;
|
|
int err;
|
|
|
|
err = parse_mac_address(value, &router_mac);
|
|
if (err) {
|
|
fprintf(stderr, "[-] CONF: bad MAC address: %s = %s\n", name, value);
|
|
return;
|
|
}
|
|
|
|
masscan->nic[index].router_mac_ipv4 = router_mac;
|
|
masscan->nic[index].router_mac_ipv6 = router_mac;
|
|
}
|
|
else if (EQUALS("router-mac-ipv4", name) || EQUALS("router-ipv4", name)) {
|
|
macaddress_t router_mac;
|
|
int err;
|
|
|
|
err = parse_mac_address(value, &router_mac);
|
|
if (err) {
|
|
fprintf(stderr, "[-] CONF: bad MAC address: %s = %s\n", name, value);
|
|
return;
|
|
}
|
|
|
|
masscan->nic[index].router_mac_ipv4 = router_mac;
|
|
}
|
|
else if (EQUALS("router-mac-ipv6", name) || EQUALS("router-ipv6", name)) {
|
|
macaddress_t router_mac;
|
|
int err;
|
|
|
|
err = parse_mac_address(value, &router_mac);
|
|
if (err) {
|
|
fprintf(stderr, "[-] CONF: bad MAC address: %s = %s\n", name, value);
|
|
return;
|
|
}
|
|
|
|
masscan->nic[index].router_mac_ipv6 = router_mac;
|
|
}
|
|
else if (EQUALS("router-ip", name)) {
|
|
/* Send packets FROM this IP address */
|
|
struct Range range;
|
|
|
|
range = range_parse_ipv4(value, 0, 0);
|
|
|
|
/* Check for bad format */
|
|
if (range.begin != range.end) {
|
|
LOG(0, "FAIL: bad source IPv4 address: %s=%s\n",
|
|
name, value);
|
|
LOG(0, "hint addresses look like \"19.168.1.23\"\n");
|
|
exit(1);
|
|
}
|
|
|
|
masscan->nic[index].router_ip = range.begin;
|
|
}
|
|
else if (EQUALS("udp-ports", name) || EQUALS("udp-port", name)) {
|
|
unsigned is_error = 0;
|
|
masscan->scan_type.udp = 1;
|
|
rangelist_parse_ports(&masscan->targets.ports, value, &is_error, Templ_UDP);
|
|
if (masscan->op == 0)
|
|
masscan->op = Operation_Scan;
|
|
}
|
|
else if (EQUALS("oprotos", name) || EQUALS("oproto", name)) {
|
|
unsigned is_error = 0;
|
|
masscan->scan_type.oproto = 1;
|
|
rangelist_parse_ports(&masscan->targets.ports, value, &is_error, Templ_Oproto_first);
|
|
if (masscan->op == 0)
|
|
masscan->op = Operation_Scan;
|
|
}
|
|
else if (EQUALS("tcp-ports", name) || EQUALS("tcp-port", name)) {
|
|
unsigned is_error = 0;
|
|
masscan->scan_type.tcp = 1;
|
|
rangelist_parse_ports(&masscan->targets.ports, value, &is_error, Templ_TCP);
|
|
if (masscan->op == 0)
|
|
masscan->op = Operation_Scan;
|
|
}
|
|
else if (EQUALS("ports", name) || EQUALS("port", name)
|
|
|| EQUALS("dst-port", name) || EQUALS("dest-port", name)
|
|
|| EQUALS("destination-port", name)
|
|
|| EQUALS("target-port", name)) {
|
|
unsigned defaultrange = 0;
|
|
int err;
|
|
|
|
if (masscan->scan_type.udp)
|
|
defaultrange = Templ_UDP;
|
|
else if (masscan->scan_type.sctp)
|
|
defaultrange = Templ_SCTP;
|
|
|
|
err = massip_add_port_string(&masscan->targets, value, defaultrange);
|
|
if (err) {
|
|
fprintf(stderr, "[-] FAIL: bad target port: %s\n", value);
|
|
fprintf(stderr, " Hint: a port is a number [0..65535]\n");
|
|
exit(1);
|
|
}
|
|
if (masscan->op == 0)
|
|
masscan->op = Operation_Scan; }
|
|
else if (EQUALS("banner-types", name) || EQUALS("banner-type", name)
|
|
|| EQUALS("banner-apps", name) || EQUALS("banner-app", name)
|
|
) {
|
|
enum ApplicationProtocol app;
|
|
|
|
app = masscan_string_to_app(value);
|
|
|
|
if (app) {
|
|
rangelist_add_range(&masscan->banner_types, app, app);
|
|
rangelist_sort(&masscan->banner_types);
|
|
} else {
|
|
LOG(0, "FAIL: bad banner app: %s\n", value);
|
|
fprintf(stderr, "err\n");
|
|
exit(1);
|
|
}
|
|
} else if (EQUALS("exclude-ports", name) || EQUALS("exclude-port", name)) {
|
|
unsigned defaultrange = 0;
|
|
int err;
|
|
|
|
if (masscan->scan_type.udp)
|
|
defaultrange = Templ_UDP;
|
|
else if (masscan->scan_type.sctp)
|
|
defaultrange = Templ_SCTP;
|
|
|
|
err = massip_add_port_string(&masscan->exclude, value, defaultrange);
|
|
if (err) {
|
|
fprintf(stderr, "[-] FAIL: bad exclude port: %s\n", value);
|
|
fprintf(stderr, " Hint: a port is a number [0..65535]\n");
|
|
exit(1);
|
|
}
|
|
if (masscan->op == 0)
|
|
masscan->op = Operation_Scan;
|
|
} else if (EQUALS("bpf", name)) {
|
|
size_t len = strlen(value) + 1;
|
|
if (masscan->bpf_filter)
|
|
free(masscan->bpf_filter);
|
|
masscan->bpf_filter = MALLOC(len);
|
|
memcpy(masscan->bpf_filter, value, len);
|
|
} else if (EQUALS("ping", name) || EQUALS("ping-sweep", name)) {
|
|
/* Add ICMP ping request */
|
|
struct Range range;
|
|
range.begin = Templ_ICMP_echo;
|
|
range.end = Templ_ICMP_echo;
|
|
rangelist_add_range(&masscan->targets.ports, range.begin, range.end);
|
|
rangelist_sort(&masscan->targets.ports);
|
|
masscan->scan_type.ping = 1;
|
|
LOG(5, "--ping\n");
|
|
} else if (EQUALS("range", name) || EQUALS("ranges", name)
|
|
|| EQUALS("ip", name) || EQUALS("ipv4", name)
|
|
|| EQUALS("dst-ip", name) || EQUALS("dest-ip", name)
|
|
|| EQUALS("destination-ip", name)
|
|
|| EQUALS("target-ip", name)) {
|
|
int err;
|
|
err = massip_add_target_string(&masscan->targets, value);
|
|
if (err) {
|
|
fprintf(stderr, "ERROR: bad IP address/range: %s\n", value);
|
|
}
|
|
|
|
if (masscan->op == 0)
|
|
masscan->op = Operation_Scan;
|
|
}
|
|
else if (
|
|
EQUALS("exclude", name) ||
|
|
EQUALS("exclude-range", name) ||
|
|
EQUALS("exclude-ranges", name) ||
|
|
EQUALS("exclude-ip", name) ||
|
|
EQUALS("exclude-ipv4", name)
|
|
) {
|
|
int err;
|
|
err = massip_add_target_string(&masscan->exclude, value);
|
|
if (err) {
|
|
fprintf(stderr, "ERROR: bad exclude address/range: %s\n", value);
|
|
}
|
|
if (masscan->op == 0)
|
|
masscan->op = Operation_Scan;
|
|
} else if (EQUALS("badsum", name)) {
|
|
masscan->nmap.badsum = 1;
|
|
} else if (EQUALS("banner1", name)) {
|
|
banner1_test(value);
|
|
exit(1);
|
|
} else if (EQUALS("blackrock-rounds", name)) {
|
|
masscan->blackrock_rounds = (unsigned)parseInt(value);
|
|
} else if (EQUALS("connection-timeout", name) || EQUALS("tcp-timeout", name)) {
|
|
/* The timeout for banners TCP connections */
|
|
masscan->tcp_connection_timeout = (unsigned)parseInt(value);
|
|
} else if (EQUALS("datadir", name)) {
|
|
safe_strcpy(masscan->nmap.datadir, sizeof(masscan->nmap.datadir), value);
|
|
} else if (EQUALS("data-length", name)) {
|
|
unsigned x = (unsigned)strtoul(value, 0, 0);
|
|
if (x >= 1514 - 14 - 40) {
|
|
fprintf(stderr, "error: %s=<n>: expected number less than 1500\n", name);
|
|
} else {
|
|
masscan->nmap.data_length = x;
|
|
}
|
|
} else if (EQUALS("debug", name)) {
|
|
if (EQUALS("if", value)) {
|
|
masscan->op = Operation_DebugIF;
|
|
}
|
|
} else if (EQUALS("dns-servers", name)) {
|
|
fprintf(stderr, "nmap(%s): unsupported: DNS lookups too synchronous\n",
|
|
name);
|
|
exit(1);
|
|
} else if (EQUALS("echo", name)) {
|
|
masscan->op = Operation_Echo;
|
|
} else if (EQUALS("echo-all", name)) {
|
|
masscan->op = Operation_EchoAll;
|
|
} else if (EQUALS("echo-cidr", name)) {
|
|
masscan->op = Operation_EchoCidr;
|
|
} else if (EQUALS("excludefile", name)) {
|
|
unsigned count1 = masscan->exclude.ipv4.count;
|
|
unsigned count2;
|
|
int err;
|
|
const char *filename = value;
|
|
|
|
LOG(1, "EXCLUDING: %s\n", value);
|
|
err = massip_parse_file(&masscan->exclude, filename);
|
|
if (err) {
|
|
LOG(0, "[-] FAIL: error reading from exclude file\n");
|
|
exit(1);
|
|
}
|
|
|
|
/* Detect if this file has made any change, otherwise don't print
|
|
* a message */
|
|
count2 = masscan->exclude.ipv4.count;
|
|
if (count2 - count1)
|
|
fprintf(stderr, "%s: excluding %u ranges from file\n",
|
|
value, count2 - count1);
|
|
} else if (EQUALS("heartbleed", name)) {
|
|
masscan->is_heartbleed = 1;
|
|
masscan_set_parameter(masscan, "no-capture", "cert");
|
|
masscan_set_parameter(masscan, "no-capture", "heartbleed");
|
|
masscan_set_parameter(masscan, "banners", "true");
|
|
} else if (EQUALS("ticketbleed", name)) {
|
|
masscan->is_ticketbleed = 1;
|
|
masscan_set_parameter(masscan, "no-capture", "cert");
|
|
masscan_set_parameter(masscan, "no-capture", "ticketbleed");
|
|
masscan_set_parameter(masscan, "banners", "true");
|
|
} else if (EQUALS("host-timeout", name)) {
|
|
fprintf(stderr, "nmap(%s): unsupported: this is an asynchronous tool, so no timeouts\n", name);
|
|
exit(1);
|
|
} else if (EQUALS("iflist", name)) {
|
|
masscan->op = Operation_List_Adapters;
|
|
} else if (EQUALS("includefile", name)) {
|
|
int err;
|
|
const char *filename = value;
|
|
|
|
err = massip_parse_file(&masscan->targets, filename);
|
|
if (err) {
|
|
LOG(0, "[-] FAIL: error reading from include file\n");
|
|
exit(1);
|
|
}
|
|
if (masscan->op == 0)
|
|
masscan->op = Operation_Scan;
|
|
} else if (EQUALS("infinite", name)) {
|
|
masscan->is_infinite = 1;
|
|
} else if (EQUALS("interactive", name)) {
|
|
masscan->output.is_interactive = 1;
|
|
} else if (EQUALS("nointeractive", name)) {
|
|
masscan->output.is_interactive = 0;
|
|
} else if (EQUALS("status", name)) {
|
|
masscan->output.is_status_updates = 1;
|
|
} else if (EQUALS("nostatus", name)) {
|
|
masscan->output.is_status_updates = 0;
|
|
} else if (EQUALS("ip-options", name)) {
|
|
fprintf(stderr, "nmap(%s): unsupported: maybe soon\n", name);
|
|
exit(1);
|
|
} else if (EQUALS("log-errors", name)) {
|
|
fprintf(stderr, "nmap(%s): unsupported: maybe soon\n", name);
|
|
exit(1);
|
|
} else if (EQUALS("min-hostgroup", name) || EQUALS("max-hostgroup", name)) {
|
|
fprintf(stderr, "nmap(%s): unsupported: we randomize all the groups!\n", name);
|
|
exit(1);
|
|
} else if (EQUALS("min-parallelism", name) || EQUALS("max-parallelism", name)) {
|
|
fprintf(stderr, "nmap(%s): unsupported: we all the parallel!\n", name);
|
|
exit(1);
|
|
} else if (EQUALS("min-rtt-timeout", name) || EQUALS("max-rtt-timeout", name) || EQUALS("initial-rtt-timeout", name)) {
|
|
fprintf(stderr, "nmap(%s): unsupported: we are asynchronous, so no timeouts, no RTT tracking!\n", name);
|
|
exit(1);
|
|
} else if (EQUALS("min-rate", name)) {
|
|
fprintf(stderr, "nmap(%s): unsupported, we go as fast as --max-rate allows\n", name);
|
|
/* no exit here, since it's just info */
|
|
} else if (EQUALS("mtu", name)) {
|
|
fprintf(stderr, "nmap(%s): fragmentation not yet supported\n", name);
|
|
exit(1);
|
|
} else if (EQUALS("nmap", name)) {
|
|
print_nmap_help();
|
|
exit(1);
|
|
} else if (EQUALS("osscan-limit", name)) {
|
|
fprintf(stderr, "nmap(%s): OS scanning unsupported\n", name);
|
|
exit(1);
|
|
} else if (EQUALS("osscan-guess", name)) {
|
|
fprintf(stderr, "nmap(%s): OS scanning unsupported\n", name);
|
|
exit(1);
|
|
} else if (EQUALS("packet-trace", name) || EQUALS("trace-packet", name)) {
|
|
masscan->nmap.packet_trace = 1;
|
|
} else if (EQUALS("privileged", name) || EQUALS("unprivileged", name)) {
|
|
fprintf(stderr, "nmap(%s): unsupported\n", name);
|
|
exit(1);
|
|
} else if (EQUALS("pfring", name)) {
|
|
masscan->is_pfring = 1;
|
|
} else if (EQUALS("port-ratio", name)) {
|
|
fprintf(stderr, "nmap(%s): unsupported\n", name);
|
|
exit(1);
|
|
} else if (EQUALS("readrange", name) || EQUALS("readranges", name)) {
|
|
masscan->op = Operation_ReadRange;
|
|
} else if (EQUALS("reason", name)) {
|
|
masscan->output.is_reason = 1;
|
|
} else if (EQUALS("redis", name)) {
|
|
struct Range range;
|
|
unsigned offset = 0;
|
|
unsigned max_offset = (unsigned)strlen(value);
|
|
unsigned port = 6379;
|
|
|
|
range = range_parse_ipv4(value, &offset, max_offset);
|
|
if ((range.begin == 0 && range.end == 0) || range.begin != range.end) {
|
|
LOG(0, "FAIL: bad redis IP address: %s\n", value);
|
|
exit(1);
|
|
}
|
|
if (offset < max_offset) {
|
|
while (offset < max_offset && isspace(value[offset]))
|
|
offset++;
|
|
if (offset+1 < max_offset && value[offset] == ':' && isdigit(value[offset+1]&0xFF)) {
|
|
port = (unsigned)strtoul(value+offset+1, 0, 0);
|
|
if (port > 65535 || port == 0) {
|
|
LOG(0, "FAIL: bad redis port: %s\n", value+offset+1);
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* TODO: add support for connecting to IPv6 addresses here */
|
|
masscan->redis.ip.ipv4 = range.begin;
|
|
masscan->redis.ip.version = 4;
|
|
|
|
masscan->redis.port = port;
|
|
masscan->output.format = Output_Redis;
|
|
safe_strcpy(masscan->output.filename,
|
|
sizeof(masscan->output.filename),
|
|
"<redis>");
|
|
} else if(EQUALS("redis-pwd", name)) {
|
|
masscan->redis.password = strdup(value);
|
|
} else if (EQUALS("release-memory", name)) {
|
|
fprintf(stderr, "nmap(%s): this is our default option\n", name);
|
|
} else if (EQUALS("resume", name)) {
|
|
masscan_read_config_file(masscan, value);
|
|
masscan_set_parameter(masscan, "output-append", "true");
|
|
} else if (EQUALS("vuln", name)) {
|
|
if (EQUALS("heartbleed", value)) {
|
|
masscan_set_parameter(masscan, "heartbleed", "true");
|
|
return;
|
|
} else if (EQUALS("ticketbleed", value)) {
|
|
masscan_set_parameter(masscan, "ticketbleed", "true");
|
|
return;
|
|
} else if (EQUALS("poodle", value) || EQUALS("sslv3", value)) {
|
|
masscan->is_poodle_sslv3 = 1;
|
|
masscan_set_parameter(masscan, "no-capture", "cert");
|
|
masscan_set_parameter(masscan, "banners", "true");
|
|
return;
|
|
}
|
|
|
|
if (!vulncheck_lookup(value)) {
|
|
fprintf(stderr, "FAIL: vuln check '%s' does not exist\n", value);
|
|
fprintf(stderr, " hint: use '--vuln list' to list available scripts\n");
|
|
exit(1);
|
|
}
|
|
if (masscan->vuln_name != NULL) {
|
|
if (strcmp(masscan->vuln_name, value) == 0)
|
|
return; /* ok */
|
|
else {
|
|
fprintf(stderr, "FAIL: only one vuln check supported at a time\n");
|
|
fprintf(stderr, " hint: '%s' is existing vuln check, '%s' is new vuln check\n",
|
|
masscan->vuln_name, value);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
masscan->vuln_name = vulncheck_lookup(value)->name;
|
|
} else if (EQUALS("scan-delay", name) || EQUALS("max-scan-delay", name)) {
|
|
fprintf(stderr, "nmap(%s): unsupported: we do timing VASTLY differently!\n", name);
|
|
exit(1);
|
|
} else if (EQUALS("scanflags", name)) {
|
|
fprintf(stderr, "nmap(%s): TCP scan flags not yet supported\n", name);
|
|
exit(1);
|
|
} else if (EQUALS("sendq", name) || EQUALS("sendqueue", name)) {
|
|
masscan->is_sendq = 1;
|
|
} else if (EQUALS("send-eth", name)) {
|
|
fprintf(stderr, "nmap(%s): unnecessary, we always do --send-eth\n", name);
|
|
} else if (EQUALS("send-ip", name)) {
|
|
fprintf(stderr, "nmap(%s): unsupported, we only do --send-eth\n", name);
|
|
exit(1);
|
|
} else if (EQUALS("selftest", name) || EQUALS("self-test", name) || EQUALS("regress", name)) {
|
|
masscan->op = Operation_Selftest;
|
|
return;
|
|
} else if (EQUALS("benchmark", name)) {
|
|
masscan->op = Operation_Benchmark;
|
|
return;
|
|
} else if (EQUALS("source-port", name) || EQUALS("sourceport", name)) {
|
|
masscan_set_parameter(masscan, "adapter-port", value);
|
|
} else if (EQUALS("nobacktrace", name) || EQUALS("backtrace", name)) {
|
|
;
|
|
} else if (EQUALS("no-stylesheet", name)) {
|
|
masscan->output.stylesheet[0] = '\0';
|
|
} else if (EQUALS("system-dns", name)) {
|
|
fprintf(stderr, "nmap(%s): DNS lookups will never be supported by this code\n", name);
|
|
exit(1);
|
|
} else if (EQUALS("top-ports", name)) {
|
|
unsigned n = (unsigned)parseInt(value);
|
|
if (!isInteger(value))
|
|
n = 100;
|
|
LOG(2, "top-ports = %u\n", n);
|
|
masscan->top_ports = n;
|
|
} else if (EQUALS("traceroute", name)) {
|
|
fprintf(stderr, "nmap(%s): unsupported\n", name);
|
|
exit(1);
|
|
} else if (EQUALS("test", name)) {
|
|
if (EQUALS("csv", value))
|
|
masscan->is_test_csv = 1;
|
|
} else if (EQUALS("notest", name)) {
|
|
if (EQUALS("csv", value))
|
|
masscan->is_test_csv = 0;
|
|
} else if (EQUALS("ttl", name)) {
|
|
unsigned x = (unsigned)strtoul(value, 0, 0);
|
|
if (x >= 256) {
|
|
fprintf(stderr, "error: %s=<n>: expected number less than 256\n", name);
|
|
} else {
|
|
masscan->nmap.ttl = x;
|
|
}
|
|
} else if (EQUALS("version", name)) {
|
|
print_version();
|
|
exit(1);
|
|
} else if (EQUALS("version-intensity", name)) {
|
|
fprintf(stderr, "nmap(%s): unsupported\n", name);
|
|
exit(1);
|
|
} else if (EQUALS("version-light", name)) {
|
|
fprintf(stderr, "nmap(%s): unsupported\n", name);
|
|
exit(1);
|
|
} else if (EQUALS("version-all", name)) {
|
|
fprintf(stderr, "nmap(%s): unsupported\n", name);
|
|
exit(1);
|
|
} else if (EQUALS("version-trace", name)) {
|
|
fprintf(stderr, "nmap(%s): unsupported\n", name);
|
|
exit(1);
|
|
} else if (EQUALS("vlan", name) || EQUALS("adapter-vlan", name)) {
|
|
masscan->nic[index].is_vlan = 1;
|
|
masscan->nic[index].vlan_id = (unsigned)parseInt(value);
|
|
} else if (EQUALS("wait", name)) {
|
|
if (EQUALS("forever", value))
|
|
masscan->wait = INT_MAX;
|
|
else
|
|
masscan->wait = (unsigned)parseInt(value);
|
|
} else if (EQUALS("webxml", name)) {
|
|
masscan_set_parameter(masscan, "stylesheet", "http://nmap.org/svn/docs/nmap.xsl");
|
|
} else {
|
|
fprintf(stderr, "CONF: unknown config option: %s=%s\n", name, value);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
static bool
|
|
is_numable(const char *name) {
|
|
size_t i;
|
|
|
|
for (i=0; config_parameters[i].name; i++) {
|
|
if (EQUALS(config_parameters[i].name, name)) {
|
|
return (config_parameters[i].flags & F_NUMABLE) == F_NUMABLE;
|
|
} else {
|
|
size_t j;
|
|
for (j=0; config_parameters[i].alts[j]; j++) {
|
|
if (EQUALS(config_parameters[i].alts[j], name)) {
|
|
return (config_parameters[i].flags & F_NUMABLE) == F_NUMABLE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/***************************************************************************
|
|
* Command-line parsing code assumes every --parm is followed by a value.
|
|
* This is a list of the parameters that don't follow the default.
|
|
***************************************************************************/
|
|
static int
|
|
is_singleton(const char *name)
|
|
{
|
|
static const char *singletons[] = {
|
|
"echo", "echo-all", "echo-cidr", "selftest", "self-test", "regress",
|
|
"benchmark",
|
|
"system-dns", "traceroute", "version",
|
|
"version-light",
|
|
"version-all", "version-trace",
|
|
"osscan-limit", "osscan-guess",
|
|
"badsum", "reason", "open", "open-only",
|
|
"packet-trace", "release-memory",
|
|
"log-errors", "append-output", "webxml",
|
|
"no-stylesheet", "heartbleed", "ticketbleed",
|
|
"send-eth", "send-ip", "iflist",
|
|
"nmap", "trace-packet", "pfring", "sendq",
|
|
"ping", "ping-sweep", "nobacktrace", "backtrace",
|
|
"infinite", "nointeractive", "interactive", "status", "nostatus",
|
|
"read-range", "read-ranges", "readrange", "read-ranges",
|
|
0};
|
|
size_t i;
|
|
|
|
for (i=0; singletons[i]; i++) {
|
|
if (EQUALS(singletons[i], name))
|
|
return 1;
|
|
}
|
|
|
|
for (i=0; config_parameters[i].name; i++) {
|
|
if (EQUALS(config_parameters[i].name, name)) {
|
|
return (config_parameters[i].flags & F_BOOL) == F_BOOL;
|
|
} else {
|
|
size_t j;
|
|
for (j=0; config_parameters[i].alts[j]; j++) {
|
|
if (EQUALS(config_parameters[i].alts[j], name)) {
|
|
return (config_parameters[i].flags & F_BOOL) == F_BOOL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*****************************************************************************/
|
|
static void
|
|
masscan_help()
|
|
{
|
|
printf(
|
|
"usage: masscan [options] [<IP|RANGE>... -pPORT[,PORT...]]\n"
|
|
"MASSCAN is a fast port scanner. The primary input parameters are the\n"
|
|
"IP addresses/ranges you want to scan, and the port numbers. An example\n"
|
|
"is the following, which scans the 10.x.x.x network for web servers:\n"
|
|
"\n"
|
|
" masscan 10.0.0.0/8 -p80\n"
|
|
"\n"
|
|
"The program auto-detects network interface/adapter settings. If this\n"
|
|
"fails, you'll have to set these manually. The following is an\n"
|
|
"example of all the parameters that are needed:\n"
|
|
"\n"
|
|
" --adapter-ip 192.168.10.123\n"
|
|
" --adapter-mac 00-11-22-33-44-55\n"
|
|
" --router-mac 66-55-44-33-22-11\n"
|
|
"\n"
|
|
"Parameters can be set either via the command-line or config-file. The\n"
|
|
"names are the same for both. Thus, the above adapter settings would\n"
|
|
"appear as follows in a configuration file:\n"
|
|
"\n"
|
|
" adapter-ip = 192.168.10.123\n"
|
|
" adapter-mac = 00-11-22-33-44-55\n"
|
|
" router-mac = 66-55-44-33-22-11\n"
|
|
"\n"
|
|
"All single-dash parameters have a spelled out double-dash equivalent,\n"
|
|
"so '-p80' is the same as '--ports 80' (or 'ports = 80' in config file).\n"
|
|
"To use the config file, type:\n"
|
|
"\n"
|
|
" masscan -c <filename>\n"
|
|
"\n"
|
|
"To generate a config-file from the current settings, use the --echo\n"
|
|
"option. This stops the program from actually running, and just echoes\n"
|
|
"the current configuration instead. This is a useful way to generate\n"
|
|
"your first config file, or see a list of parameters you didn't know\n"
|
|
"about. I suggest you try it now:\n"
|
|
"\n"
|
|
" masscan -p1234 --echo\n"
|
|
"\n");
|
|
exit(1);
|
|
}
|
|
|
|
/***************************************************************************
|
|
***************************************************************************/
|
|
void
|
|
masscan_load_database_files(struct Masscan *masscan)
|
|
{
|
|
const char *filename;
|
|
|
|
/*
|
|
* "pcap-payloads"
|
|
*/
|
|
filename = masscan->payloads.pcap_payloads_filename;
|
|
if (filename) {
|
|
if (masscan->payloads.udp == NULL)
|
|
masscan->payloads.udp = payloads_udp_create();
|
|
if (masscan->payloads.oproto == NULL)
|
|
masscan->payloads.oproto = payloads_udp_create();
|
|
|
|
payloads_read_pcap(filename, masscan->payloads.udp, masscan->payloads.oproto);
|
|
}
|
|
|
|
/*
|
|
* `--nmap-payloads`
|
|
*/
|
|
filename = masscan->payloads.nmap_payloads_filename;
|
|
if (filename) {
|
|
FILE *fp;
|
|
|
|
fp = fopen(filename, "rt");
|
|
if (fp == NULL) {
|
|
fprintf(stderr, "[-] FAIL: --nmap-payloads\n");
|
|
fprintf(stderr, "[-] %s:%s\n", filename, strerror(errno));
|
|
} else {
|
|
if (masscan->payloads.udp == NULL)
|
|
masscan->payloads.udp = payloads_udp_create();
|
|
|
|
payloads_udp_readfile(fp, filename, masscan->payloads.udp);
|
|
|
|
fclose(fp);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* "nmap-service-probes"
|
|
*/
|
|
filename = masscan->payloads.nmap_service_probes_filename;
|
|
if (filename) {
|
|
if (masscan->payloads.probes)
|
|
nmapserviceprobes_free(masscan->payloads.probes);
|
|
|
|
masscan->payloads.probes = nmapserviceprobes_read_file(filename);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
* Read the configuration from the command-line.
|
|
* Called by 'main()' when starting up.
|
|
***************************************************************************/
|
|
void
|
|
masscan_command_line(struct Masscan *masscan, int argc, char *argv[])
|
|
{
|
|
int i;
|
|
|
|
for (i=1; i<argc; i++) {
|
|
|
|
/*
|
|
* --name=value
|
|
* --name:value
|
|
* --name value
|
|
*/
|
|
if (argv[i][0] == '-' && argv[i][1] == '-') {
|
|
const char *argname = argv[i] + 2;
|
|
|
|
if (EQUALS("help", argname)) {
|
|
masscan_help();
|
|
exit(1);
|
|
} else if (is_numable(argname)) {
|
|
/* May exist by itself like a bool or take an additional
|
|
* numeric argument */
|
|
char name2[64];
|
|
const char *name = argname;
|
|
unsigned name_length;
|
|
const char *value;
|
|
|
|
/* Look for:
|
|
* --name=value
|
|
* --name:value */
|
|
value = strchr(argname, '=');
|
|
if (value == NULL)
|
|
value = strchr(&argv[i][2], ':');
|
|
if (value) {
|
|
name_length = (unsigned)(value - name);
|
|
} else {
|
|
/* The next parameter contains the name */
|
|
if (i+1 < argc) {
|
|
value = argv[i+1];
|
|
if (isInteger(value) || isBoolean(value))
|
|
i++;
|
|
else
|
|
value = "";
|
|
} else
|
|
value = "";
|
|
name_length = (unsigned)strlen(argname);
|
|
}
|
|
|
|
/* create a copy of the name */
|
|
if (name_length > sizeof(name2) - 1) {
|
|
fprintf(stderr, "%.*s: name too long\n", name_length, name);
|
|
name_length = sizeof(name2) - 1;
|
|
}
|
|
memcpy(name2, name, name_length);
|
|
name2[name_length] = '\0';
|
|
|
|
masscan_set_parameter(masscan, name2, value);
|
|
} else if (EQUALS("readscan", argv[i]+2)) {
|
|
/* Read in a binary file instead of scanning the network*/
|
|
masscan->op = Operation_ReadScan;
|
|
|
|
/* Default to reading banners */
|
|
masscan->is_banners = true;
|
|
masscan->is_banners_rawudp = true;
|
|
|
|
/* This option may be followed by many filenames, therefore,
|
|
* skip forward in the argument list until the next
|
|
* argument */
|
|
while (i+1 < argc && argv[i+1][0] != '-')
|
|
i++;
|
|
continue;
|
|
} else {
|
|
char name2[64];
|
|
char *name = argv[i] + 2;
|
|
unsigned name_length;
|
|
const char *value;
|
|
|
|
value = strchr(&argv[i][2], '=');
|
|
if (value == NULL)
|
|
value = strchr(&argv[i][2], ':');
|
|
if (value == NULL) {
|
|
if (is_singleton(name))
|
|
value = "";
|
|
else
|
|
value = argv[++i];
|
|
name_length = (unsigned)strlen(name);
|
|
} else {
|
|
name_length = (unsigned)(value - name);
|
|
value++;
|
|
}
|
|
|
|
if (i >= argc) {
|
|
fprintf(stderr, "%.*s: empty parameter\n", name_length, name);
|
|
break;
|
|
}
|
|
|
|
if (name_length > sizeof(name2) - 1) {
|
|
fprintf(stderr, "%.*s: name too long\n", name_length, name);
|
|
name_length = sizeof(name2) - 1;
|
|
}
|
|
|
|
memcpy(name2, name, name_length);
|
|
name2[name_length] = '\0';
|
|
|
|
masscan_set_parameter(masscan, name2, value);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* For for a single-dash parameter */
|
|
if (argv[i][0] == '-') {
|
|
const char *arg;
|
|
|
|
switch (argv[i][1]) {
|
|
case '6':
|
|
/* Silently ignore this: IPv6 features enabled all the time */
|
|
break;
|
|
case 'A':
|
|
fprintf(stderr, "nmap(%s): unsupported: this tool only does SYN scan\n", argv[i]);
|
|
exit(1);
|
|
case 'b':
|
|
fprintf(stderr, "nmap(%s): FTP bounce scans will never be supported\n", argv[i]);
|
|
exit(1);
|
|
case 'c':
|
|
if (argv[i][2])
|
|
arg = argv[i]+2;
|
|
else
|
|
arg = argv[++i];
|
|
masscan_read_config_file(masscan, arg);
|
|
break;
|
|
case 'd': /* just do same as verbosity level */
|
|
{
|
|
int v;
|
|
for (v=1; argv[i][v] == 'd'; v++) {
|
|
LOG_add_level(1);
|
|
}
|
|
}
|
|
break;
|
|
case 'e':
|
|
if (argv[i][2])
|
|
arg = argv[i]+2;
|
|
else
|
|
arg = argv[++i];
|
|
masscan_set_parameter(masscan, "adapter", arg);
|
|
break;
|
|
case 'f':
|
|
fprintf(stderr, "nmap(%s): fragmentation not yet supported\n", argv[i]);
|
|
exit(1);
|
|
case 'F':
|
|
fprintf(stderr, "nmap(%s): unsupported, no slow/fast mode\n", argv[i]);
|
|
exit(1);
|
|
case 'g':
|
|
if (argv[i][2])
|
|
arg = argv[i]+2;
|
|
else
|
|
arg = argv[++i];
|
|
masscan_set_parameter(masscan, "adapter-port", arg);
|
|
break;
|
|
case 'h':
|
|
case '?':
|
|
masscan_usage();
|
|
break;
|
|
case 'i':
|
|
if (argv[i][3] == '\0' && !isdigit(argv[i][2]&0xFF)) {
|
|
/* This looks like an nmap option*/
|
|
switch (argv[i][2]) {
|
|
case 'L':
|
|
masscan_set_parameter(masscan, "includefile", argv[++i]);
|
|
break;
|
|
case 'R':
|
|
/* -iR in nmap makes it randomize addresses completely. Thus,
|
|
* it's nearest equivalent is scanning the entire Internet range */
|
|
masscan_set_parameter(masscan, "include", "0.0.0.0/0");
|
|
break;
|
|
default:
|
|
fprintf(stderr, "nmap(%s): unsupported option\n", argv[i]);
|
|
exit(1);
|
|
}
|
|
|
|
} else {
|
|
if (argv[i][2])
|
|
arg = argv[i]+2;
|
|
else
|
|
arg = argv[++i];
|
|
|
|
masscan_set_parameter(masscan, "adapter", arg);
|
|
}
|
|
break;
|
|
case 'n':
|
|
/* This looks like an nmap option*/
|
|
/* Do nothing: this code never does DNS lookups anyway */
|
|
break;
|
|
case 'o': /* nmap output format */
|
|
switch (argv[i][2]) {
|
|
case 'A':
|
|
masscan->output.format = Output_All;
|
|
fprintf(stderr, "nmap(%s): unsupported output format\n", argv[i]);
|
|
exit(1);
|
|
break;
|
|
case 'B':
|
|
masscan->output.format = Output_Binary;
|
|
break;
|
|
case 'D':
|
|
masscan->output.format = Output_NDJSON;
|
|
break;
|
|
case 'J':
|
|
masscan->output.format = Output_JSON;
|
|
break;
|
|
case 'N':
|
|
masscan->output.format = Output_Nmap;
|
|
fprintf(stderr, "nmap(%s): unsupported output format\n", argv[i]);
|
|
exit(1);
|
|
break;
|
|
case 'X':
|
|
masscan->output.format = Output_XML;
|
|
break;
|
|
case 'R':
|
|
masscan->output.format = Output_Redis;
|
|
if (i+1 < argc && argv[i+1][0] != '-')
|
|
masscan_set_parameter(masscan, "redis", argv[i+1]);
|
|
break;
|
|
case 'S':
|
|
masscan->output.format = Output_ScriptKiddie;
|
|
fprintf(stderr, "nmap(%s): unsupported output format\n", argv[i]);
|
|
exit(1);
|
|
break;
|
|
case 'G':
|
|
masscan->output.format = Output_Grepable;
|
|
break;
|
|
case 'L':
|
|
masscan_set_parameter(masscan, "output-format", "list");
|
|
break;
|
|
case 'U':
|
|
masscan_set_parameter(masscan, "output-format", "unicornscan");
|
|
break;
|
|
case 'H':
|
|
masscan_set_parameter(masscan, "output-format", "hostonly");
|
|
break;
|
|
default:
|
|
fprintf(stderr, "nmap(%s): unknown output format\n", argv[i]);
|
|
exit(1);
|
|
}
|
|
|
|
++i;
|
|
if (i >= argc || (argv[i][0] == '-' && argv[i][1] != '\0')) {
|
|
fprintf(stderr, "missing output filename\n");
|
|
exit(1);
|
|
}
|
|
|
|
masscan_set_parameter(masscan, "output-filename", argv[i]);
|
|
break;
|
|
case 'O':
|
|
fprintf(stderr, "nmap(%s): unsupported, OS detection is too complex\n", argv[i]);
|
|
exit(1);
|
|
break;
|
|
case 'p':
|
|
if (argv[i][2])
|
|
arg = argv[i]+2;
|
|
else
|
|
arg = argv[++i];
|
|
if (i >= argc || arg[0] == 0) { // if string is empty
|
|
fprintf(stderr, "%s: empty parameter\n", argv[i]);
|
|
} else
|
|
masscan_set_parameter(masscan, "ports", arg);
|
|
break;
|
|
case 'P':
|
|
switch (argv[i][2]) {
|
|
case 'n':
|
|
/* we already do this */
|
|
break;
|
|
default:
|
|
fprintf(stderr, "nmap(%s): unsupported option, maybe in future\n", argv[i]);
|
|
exit(1);
|
|
}
|
|
break;
|
|
case 'r':
|
|
/* This looks like an nmap option*/
|
|
fprintf(stderr, "nmap(%s): wat? randomization is our raison d'etre!! rethink prease\n", argv[i]);
|
|
exit(1);
|
|
break;
|
|
case 'R':
|
|
/* This looks like an nmap option*/
|
|
fprintf(stderr, "nmap(%s): unsupported. This code will never do DNS lookups.\n", argv[i]);
|
|
exit(1);
|
|
break;
|
|
case 's': /* NMAP: scan type */
|
|
if (argv[i][3] == '\0' && !isdigit(argv[i][2]&0xFF)) {
|
|
unsigned j;
|
|
|
|
for (j=2; argv[i][j]; j++)
|
|
switch (argv[i][j]) {
|
|
case 'A':
|
|
fprintf(stderr, "nmap(%s): ACK scan not yet supported\n", argv[i]);
|
|
exit(1);
|
|
case 'C':
|
|
fprintf(stderr, "nmap(%s): unsupported\n", argv[i]);
|
|
exit(1);
|
|
case 'F':
|
|
fprintf(stderr, "nmap(%s): FIN scan not yet supported\n", argv[i]);
|
|
exit(1);
|
|
case 'I':
|
|
fprintf(stderr, "nmap(%s): Zombie scans will never be supported\n", argv[i]);
|
|
exit(1);
|
|
case 'L': /* List Scan - simply list targets to scan */
|
|
masscan->op = Operation_ListScan;
|
|
break;
|
|
case 'M':
|
|
fprintf(stderr, "nmap(%s): Maimon scan not yet supported\n", argv[i]);
|
|
exit(1);
|
|
case 'n': /* Ping Scan - disable port scan */
|
|
fprintf(stderr, "nmap(%s): ping-sweeps not yet supported\n", argv[i]);
|
|
exit(1);
|
|
case 'N':
|
|
fprintf(stderr, "nmap(%s): NULL scan not yet supported\n", argv[i]);
|
|
exit(1);
|
|
case 'O': /* Other IP protocols (not ICMP, UDP, TCP, or SCTP) */
|
|
masscan->scan_type.oproto = 1;
|
|
break;
|
|
case 'S': /* TCP SYN scan - THIS IS WHAT WE DO! */
|
|
masscan->scan_type.tcp = 1;
|
|
break;
|
|
case 'T': /* TCP connect scan */
|
|
fprintf(stderr, "nmap(%s): connect() is too synchronous for cool kids\n", argv[i]);
|
|
fprintf(stderr, "WARNING: doing SYN scan (-sS) anyway, ignoring (-sT)\n");
|
|
break;
|
|
case 'U': /* UDP scan */
|
|
masscan->scan_type.udp = 1;
|
|
break;
|
|
case 'V':
|
|
fprintf(stderr, "nmap(%s): unlikely this will be supported\n", argv[i]);
|
|
exit(1);
|
|
case 'W':
|
|
fprintf(stderr, "nmap(%s): Windows scan not yet supported\n", argv[i]);
|
|
exit(1);
|
|
case 'X':
|
|
fprintf(stderr, "nmap(%s): Xmas scan not yet supported\n", argv[i]);
|
|
exit(1);
|
|
case 'Y':
|
|
break;
|
|
case 'Z':
|
|
masscan->scan_type.sctp = 1;
|
|
break;
|
|
default:
|
|
fprintf(stderr, "nmap(%s): unsupported option\n", argv[i]);
|
|
exit(1);
|
|
}
|
|
|
|
} else {
|
|
fprintf(stderr, "%s: unknown parameter\n", argv[i]);
|
|
exit(1);
|
|
}
|
|
break;
|
|
case 'S':
|
|
if (argv[i][2])
|
|
arg = argv[i]+2;
|
|
else
|
|
arg = argv[++i];
|
|
masscan_set_parameter(masscan, "adapter-ip", arg);
|
|
break;
|
|
case 'v':
|
|
{
|
|
int v;
|
|
for (v=1; argv[i][v] == 'v'; v++)
|
|
LOG_add_level(1);
|
|
}
|
|
break;
|
|
case 'V': /* print version and exit */
|
|
masscan_set_parameter(masscan, "version", "");
|
|
break;
|
|
case 'W':
|
|
masscan->op = Operation_List_Adapters;
|
|
return;
|
|
case 'T':
|
|
fprintf(stderr, "nmap(%s): unsupported, we do timing WAY different than nmap\n", argv[i]);
|
|
exit(1);
|
|
return;
|
|
default:
|
|
LOG(0, "FAIL: unknown option: -%s\n", argv[i]);
|
|
LOG(0, " [hint] try \"--help\"\n");
|
|
LOG(0, " [hint] ...or, to list nmap-compatible options, try \"--nmap\"\n");
|
|
exit(1);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (!isdigit(argv[i][0]) && argv[i][0] != ':' && argv[i][0] != '[') {
|
|
fprintf(stderr, "FAIL: unknown command-line parameter \"%s\"\n", argv[i]);
|
|
fprintf(stderr, " [hint] did you want \"--%s\"?\n", argv[i]);
|
|
exit(1);
|
|
}
|
|
|
|
/* If parameter doesn't start with '-', assume it's an
|
|
* IPv4 range
|
|
*/
|
|
masscan_set_parameter(masscan, "range", argv[i]);
|
|
}
|
|
|
|
/*
|
|
* If no other "scan type" found, then default to TCP
|
|
*/
|
|
if (masscan->scan_type.udp == 0 && masscan->scan_type.sctp == 0
|
|
&& masscan->scan_type.ping == 0 && masscan->scan_type.arp == 0
|
|
&& masscan->scan_type.oproto == 0)
|
|
masscan->scan_type.tcp = 1;
|
|
|
|
/*
|
|
* If "top-ports" specified, then add all those ports. This may be in
|
|
* addition to any other ports
|
|
*/
|
|
if (masscan->top_ports) {
|
|
config_top_ports(masscan, masscan->top_ports);
|
|
}
|
|
if (masscan->shard.of < masscan->shard.one) {
|
|
fprintf(stderr, "[-] WARNING: the shard number must be less than the total shard count: %u/%u\n",
|
|
masscan->shard.one, masscan->shard.of);
|
|
}
|
|
if (masscan->shard.of > 1 && masscan->seed == 0) {
|
|
fprintf(stderr, "[-] WARNING: --seed <num> is not specified\n HINT: all shards must share the same seed\n");
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
* Prints the current configuration to the command-line then exits.
|
|
* Use#1: create a template file of all settable parameters.
|
|
* Use#2: make sure your configuration was interpreted correctly.
|
|
***************************************************************************/
|
|
void
|
|
masscan_echo(struct Masscan *masscan, FILE *fp, unsigned is_echo_all)
|
|
{
|
|
unsigned i;
|
|
unsigned l = 0;
|
|
|
|
/*
|
|
* NEW:
|
|
* Print all configuration parameters
|
|
*/
|
|
masscan->echo = fp;
|
|
masscan->echo_all = is_echo_all;
|
|
for (i=0; config_parameters[i].name; i++) {
|
|
config_parameters[i].set(masscan, 0, 0);
|
|
}
|
|
masscan->echo = 0;
|
|
masscan->echo_all = 0;
|
|
|
|
/*
|
|
* OLD:
|
|
* Things here below are the old way of echoing parameters.
|
|
* TODO: cleanup this code, replacing with the new way.
|
|
*/
|
|
if (masscan->nic_count == 0)
|
|
masscan_echo_nic(masscan, fp, 0);
|
|
else {
|
|
for (i=0; i<masscan->nic_count; i++)
|
|
masscan_echo_nic(masscan, fp, i);
|
|
}
|
|
|
|
/**
|
|
* Fix for #737, save adapter-port/source-port value or range
|
|
*/
|
|
if (masscan->nic[0].src.port.first != 0) {
|
|
fprintf(fp, "adapter-port = %d", masscan->nic[0].src.port.first);
|
|
if (masscan->nic[0].src.port.first != masscan->nic[0].src.port.last) {
|
|
/* --adapter-port <first>-<last> */
|
|
fprintf(fp, "-%d", masscan->nic[0].src.port.last);
|
|
}
|
|
fprintf(fp, "\n");
|
|
}
|
|
|
|
/*
|
|
* Targets
|
|
*/
|
|
fprintf(fp, "# TARGET SELECTION (IP, PORTS, EXCLUDES)\n");
|
|
fprintf(fp, "ports = ");
|
|
/* Disable comma generation for the first element */
|
|
l = 0;
|
|
for (i=0; i<masscan->targets.ports.count; i++) {
|
|
struct Range range = masscan->targets.ports.list[i];
|
|
do {
|
|
struct Range rrange = range;
|
|
unsigned done = 0;
|
|
if (l)
|
|
fprintf(fp, ",");
|
|
l = 1;
|
|
if (rrange.begin >= Templ_ICMP_echo) {
|
|
rrange.begin -= Templ_ICMP_echo;
|
|
rrange.end -= Templ_ICMP_echo;
|
|
fprintf(fp,"I:");
|
|
done = 1;
|
|
} else if (rrange.begin >= Templ_SCTP) {
|
|
rrange.begin -= Templ_SCTP;
|
|
rrange.end -= Templ_SCTP;
|
|
fprintf(fp,"S:");
|
|
range.begin = Templ_ICMP_echo;
|
|
} else if (rrange.begin >= Templ_UDP) {
|
|
rrange.begin -= Templ_UDP;
|
|
rrange.end -= Templ_UDP;
|
|
fprintf(fp,"U:");
|
|
range.begin = Templ_SCTP;
|
|
} else if (Templ_Oproto_first <= rrange.begin && rrange.begin <= Templ_Oproto_last) {
|
|
rrange.begin -= Templ_Oproto_first;
|
|
rrange.end -= Templ_Oproto_first;
|
|
fprintf(fp, "O:");
|
|
range.begin = Templ_Oproto_first;
|
|
} else
|
|
range.begin = Templ_UDP;
|
|
rrange.end = min(rrange.end, 65535);
|
|
if (rrange.begin == rrange.end)
|
|
fprintf(fp, "%u", rrange.begin);
|
|
else
|
|
fprintf(fp, "%u-%u", rrange.begin, rrange.end);
|
|
if (done)
|
|
break;
|
|
} while (range.begin <= range.end);
|
|
}
|
|
fprintf(fp, "\n");
|
|
|
|
/*
|
|
* IPv4 address targets
|
|
*/
|
|
for (i=0; i<masscan->targets.ipv4.count; i++) {
|
|
unsigned prefix_bits;
|
|
struct Range range = masscan->targets.ipv4.list[i];
|
|
|
|
if (range.begin == range.end) {
|
|
fprintf(fp, "range = %u.%u.%u.%u",
|
|
(range.begin>>24)&0xFF,
|
|
(range.begin>>16)&0xFF,
|
|
(range.begin>> 8)&0xFF,
|
|
(range.begin>> 0)&0xFF
|
|
);
|
|
} else if (range_is_cidr(range, &prefix_bits)) {
|
|
fprintf(fp, "range = %u.%u.%u.%u/%u",
|
|
(range.begin>>24)&0xFF,
|
|
(range.begin>>16)&0xFF,
|
|
(range.begin>> 8)&0xFF,
|
|
(range.begin>> 0)&0xFF,
|
|
prefix_bits
|
|
);
|
|
|
|
} else {
|
|
fprintf(fp, "range = %u.%u.%u.%u-%u.%u.%u.%u",
|
|
(range.begin>>24)&0xFF,
|
|
(range.begin>>16)&0xFF,
|
|
(range.begin>> 8)&0xFF,
|
|
(range.begin>> 0)&0xFF,
|
|
(range.end>>24)&0xFF,
|
|
(range.end>>16)&0xFF,
|
|
(range.end>> 8)&0xFF,
|
|
(range.end>> 0)&0xFF
|
|
);
|
|
}
|
|
fprintf(fp, "\n");
|
|
}
|
|
for (i=0; i<masscan->targets.ipv6.count; i++) {
|
|
bool exact = false;
|
|
struct Range6 range = masscan->targets.ipv6.list[i];
|
|
ipaddress_formatted_t fmt = ipv6address_fmt(range.begin);
|
|
|
|
fprintf(fp, "range = %s", fmt.string);
|
|
if (!ipv6address_is_equal(range.begin, range.end)) {
|
|
unsigned cidr_bits = count_cidr6_bits(&range, &exact);
|
|
|
|
if (exact && cidr_bits) {
|
|
fprintf(fp, "/%u", cidr_bits);
|
|
} else {
|
|
fmt = ipv6address_fmt(range.end);
|
|
fprintf(fp, "-%s", fmt.string);
|
|
}
|
|
}
|
|
fprintf(fp, "\n");
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
* Prints the list of CIDR to scan to the command-line then exits.
|
|
* Use: provide this list to other tools. Unlike masscan -sL, it keeps
|
|
* the CIDR aggretated format, and does not randomize the order of output.
|
|
* For example, given the starting range of [10.0.0.1-10.0.0.255], this will
|
|
* print all the CIDR ranges that make this up:
|
|
* 10.0.0.1/32
|
|
* 10.0.0.2/31
|
|
* 10.0.0.4/30
|
|
* 10.0.0.8/29
|
|
* 10.0.0.16/28
|
|
* 10.0.0.32/27
|
|
* 10.0.0.64/26
|
|
* 10.0.0.128/25
|
|
***************************************************************************/
|
|
void
|
|
masscan_echo_cidr(struct Masscan *masscan, FILE *fp, unsigned is_echo_all)
|
|
{
|
|
unsigned i;
|
|
UNUSEDPARM(is_echo_all);
|
|
|
|
masscan->echo = fp;
|
|
|
|
/*
|
|
* For all IPv4 ranges ...
|
|
*/
|
|
for (i=0; i<masscan->targets.ipv4.count; i++) {
|
|
|
|
/* Get the next range in the list */
|
|
struct Range range = masscan->targets.ipv4.list[i];
|
|
|
|
/* If not a single CIDR range, print all the CIDR ranges
|
|
* needed to completely represent this addres */
|
|
for (;;) {
|
|
unsigned prefix_length;
|
|
struct Range cidr;
|
|
|
|
/* Find the largest CIDR range (one that can be specified
|
|
* with a /prefix) at the start of this range. */
|
|
cidr = range_first_cidr(range, &prefix_length);
|
|
fprintf(fp, "%u.%u.%u.%u/%u\n",
|
|
(cidr.begin>>24)&0xFF,
|
|
(cidr.begin>>16)&0xFF,
|
|
(cidr.begin>> 8)&0xFF,
|
|
(cidr.begin>> 0)&0xFF,
|
|
prefix_length
|
|
);
|
|
|
|
/* If this is the last range, then stop. There are multiple
|
|
* ways to gets to see if we get to the end, but I think
|
|
* this is the best. */
|
|
if (cidr.end >= range.end)
|
|
break;
|
|
|
|
/* If the CIDR range didn't cover the entire range,
|
|
* then remove it from the beginning of the range
|
|
* and process the remainder */
|
|
range.begin = cidr.end+1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* For all IPv6 ranges...
|
|
*/
|
|
for (i=0; i<masscan->targets.ipv6.count; i++) {
|
|
struct Range6 range = masscan->targets.ipv6.list[i];
|
|
bool exact = false;
|
|
while (!exact) {
|
|
ipaddress_formatted_t fmt = ipv6address_fmt(range.begin);
|
|
fprintf(fp, "%s", fmt.string);
|
|
if (range.begin.hi == range.end.hi && range.begin.lo == range.end.lo) {
|
|
fprintf(fp, "/128");
|
|
exact = true;
|
|
} else {
|
|
unsigned cidr_bits = count_cidr6_bits(&range, &exact);
|
|
fprintf(fp, "/%u", cidr_bits);
|
|
}
|
|
fprintf(fp, "\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
* remove leading/trailing whitespace
|
|
***************************************************************************/
|
|
static void
|
|
trim(char *line, size_t sizeof_line)
|
|
{
|
|
if (sizeof_line > strlen(line))
|
|
sizeof_line = strlen(line);
|
|
|
|
while (isspace(*line & 0xFF))
|
|
memmove(line, line+1, sizeof_line--);
|
|
while (*line && isspace(line[sizeof_line-1] & 0xFF))
|
|
line[--sizeof_line] = '\0';
|
|
}
|
|
|
|
/***************************************************************************
|
|
***************************************************************************/
|
|
void
|
|
masscan_read_config_file(struct Masscan *masscan, const char *filename)
|
|
{
|
|
FILE *fp;
|
|
char line[65536];
|
|
|
|
fp = fopen(filename, "rt");
|
|
if (fp == NULL) {
|
|
char dir[512];
|
|
char *x;
|
|
|
|
fprintf(stderr, "[-] FAIL: reading configuration file\n");
|
|
fprintf(stderr, "[-] %s: %s\n", filename, strerror(errno));
|
|
|
|
x = getcwd(dir, sizeof(dir));
|
|
if (x)
|
|
fprintf(stderr, "[-] cwd = %s\n", dir);
|
|
return;
|
|
}
|
|
|
|
while (fgets(line, sizeof(line), fp)) {
|
|
char *name;
|
|
char *value;
|
|
|
|
trim(line, sizeof(line));
|
|
|
|
if (ispunct(line[0] & 0xFF) || line[0] == '\0')
|
|
continue;
|
|
|
|
name = line;
|
|
value = strchr(line, '=');
|
|
if (value == NULL)
|
|
continue;
|
|
*value = '\0';
|
|
value++;
|
|
trim(name, sizeof(line));
|
|
trim(value, sizeof(line));
|
|
|
|
masscan_set_parameter(masscan, name, value);
|
|
}
|
|
|
|
fclose(fp);
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
***************************************************************************/
|
|
int masscan_conf_contains(const char *x, int argc, char **argv)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<argc; i++) {
|
|
if (strcmp(argv[i], x) == 0)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
***************************************************************************/
|
|
int
|
|
mainconf_selftest()
|
|
{
|
|
char test[] = " test 1 ";
|
|
|
|
trim(test, sizeof(test));
|
|
if (strcmp(test, "test 1") != 0) {
|
|
goto failure;
|
|
}
|
|
|
|
|
|
/* */
|
|
{
|
|
int argc = 6;
|
|
char *argv[] = { "foo", "bar", "-ddd", "--readscan", "xxx", "--something" };
|
|
|
|
if (masscan_conf_contains("--nothing", argc, argv))
|
|
goto failure;
|
|
|
|
if (!masscan_conf_contains("--readscan", argc, argv))
|
|
goto failure;
|
|
}
|
|
|
|
return 0;
|
|
failure:
|
|
fprintf(stderr, "[+] selftest failure: config subsystem\n");
|
|
return 1;
|
|
}
|
|
|