diff --git a/.gitignore b/.gitignore index 16cfc29..d1656df 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,14 @@ *~ \#* src/zmap +CMakeFiles +*.cmake +Makefile +CMakeCache.txt +src/zopt.h +src/zopt.c +lexer.c +lexer.h +parser.c +parser.h +install_manifest.txt diff --git a/AUTHORS b/AUTHORS index e5a0436..766fec7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,3 +1,7 @@ Zakir Durumeric J. Alex Halderman -Eric Wustrow \ No newline at end of file +Eric Wustrow + +David Adrian +HD Moore + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..bea62ab --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,70 @@ +cmake_minimum_required (VERSION 2.6) +project (ZMAP C) + +option(WITH_REDIS "Build with support for Redis DB" OFF) +option(WITH_JSON "Build with support for JSON" OFF) +option(ENABLE_DEVELOPMENT "Enable development specific compiler and linker flags" OFF) +option(ENABLE_HARDENING "Add hardening specific compiler and linker flags" ON) + +if(ENABLE_DEVELOPMENT) + # Hardening and warnings for building with gcc + # Maybe add -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations + set(GCCWARNINGS + "-Wall -Wformat=2 -Wno-format-nonliteral" + "-pedantic -fno-strict-aliasing" + "-Wextra" + "-Wfloat-equal -Wundef -Wwrite-strings -Wredundant-decls" + "-Wnested-externs -Wbad-function-cast -Winit-self" + "-Wmissing-noreturn -Wnormalized=id" + "-Wstack-protector" + "-Werror" + ) + + # Fix line breaks + string(REPLACE ";" " " GCCWARNINGS "${GCCWARNINGS}") + + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GCCWARNINGS} -g") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -g") +endif() + +if(ENABLE_HARDENING) + set(GCCHARDENING "-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fstack-protector-all -fwrapv -fPIC --param ssp-buffer-size=1") + set(LDHARDENING "-z relro -z now") + + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GCCHARDENING}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LDHARDENING}") +endif() + +if(WITH_REDIS) + set(REDIS_LIBS hiredis) + add_definitions("-DREDIS") +endif() + +if(WITH_JSON) + include(FindPkgConfig) + pkg_check_modules(JSON json) + if(JSON_FOUND) + include_directories(JSON_INCLUDE_DIRS) + else() + message(FATAL_ERROR "Did not find libjson") + endif() + + add_definitions("-DJSON") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${JSON_CFLAGS}") +endif() + +# Standard FLAGS +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pthread") + +# Extra target FLAGS +set(CMAKE_C_FLAGS_DEBUG "-O2 -g") +set(CMAKE_C_FLAGS_RELEASE "-O2") + +add_subdirectory(src) + +# Install conf files +FILE(GLOB CONF_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/conf" "conf/*") +message(STATUS "${CONF_FILES}") +configure_file(zmap_conf_install.cmake.in zmap_conf_install.cmake) +install(SCRIPT zmap_conf_install.cmake) diff --git a/INSTALL b/INSTALL index 442f292..9ca68df 100644 --- a/INSTALL +++ b/INSTALL @@ -1,38 +1,38 @@ SYSTEM REQUIREMENTS ZMap is designed to run on GNU/Linux systems and can be built with -most recent versions of gcc. Currently, ZMap only supports 64-bit -systems. Running it requires at least 600 MB of free memory. +most recent versions of gcc. Currently, ZMap only supports 64-bit +systems. Running it requires at least 600 MB of free memory. BUILDING AND INSTALLING ZMAP ZMap requires GMP, a free library for arbitrary precision arithmetic, -gengetopt, and libpcap. These packages can be installed on +gengetopt, and libpcap. These packages can be installed on Debian-based systems by running: - sudo apt-get install libgmp3-dev gengetopt libpcap-dev + sudo apt-get install libgmp3-dev gengetopt libpcap-dev flex byacc or on RHEL- and Fedora-based systems by running: - sudo yum install gmp gmp-devel gengetopt libpcap-devel + sudo yum install gmp gmp-devel gengetopt libpcap-devel flex byacc Once these prerequisites have been installed, ZMap can be installed by running: - cd src + cmake [-DWITH_REDIS=ON] [-DWITH_JSON=ON] [-DENABLE_DEVELOPMENT=ON] [-DENABLE_HARDENING=ON] ./ make followed by: sudo make install -Redis support is not enabled by default. If you are want to use ZMap -with Redis, you will first need to install Hiredis. Then, rebuild -ZMap with the command "make REDIS=true". - -JSON support is not enabled by default. If you are want to use ZMap -with JSON output, you will first need to install json-c. Then, rebuild -ZMap with the command "make JSON=true". +Redis support is not enabled by default. If you are want to use ZMap +with Redis, you will first need to install Hiredis. Then run cmake with +"-DWITH_REDIS=ON". +JSON support is not enabled by default. If you are want to use ZMap +with JSON output, you will first need to install json-c. Then, run cmake with +"-DWITH_JSON=ON" + Installing json-c requires git and autotools to be available. For more information on how to install json-c, please see http://github.com/json-c/json-c diff --git a/lib/blacklist.c b/lib/blacklist.c index 49eaa32..9319d9f 100644 --- a/lib/blacklist.c +++ b/lib/blacklist.c @@ -25,6 +25,10 @@ static constraint_t *constraint = NULL; +uint32_t blacklist_lookup_index(uint64_t index) { + return ntohl(constraint_lookup_index(constraint, index, ADDR_ALLOWED)); +} + // check whether a single IP address is allowed to be scanned. // 1 => is allowed // 0 => is not allowed @@ -47,7 +51,39 @@ void whitelist_prefix(char *ip, int prefix_len) constraint_set(constraint, ntohl(inet_addr(ip)), prefix_len, ADDR_ALLOWED); } -static int init(char *file, const char *name, int value) +static int init_from_string(char *ip, int value) +{ + int prefix_len = 32; + char *slash = strchr(ip, '/'); + if (slash) { // split apart network and prefix length + *slash = '\0'; + char *end; + char *len = slash+1; + errno = 0; + prefix_len = strtol(len, &end, 10); + if (end == len || errno != 0 || prefix_len < 0 || prefix_len > 32) { + log_fatal("constraint", "'%s' is not a valid prefix length", len); + return -1; + } + } + struct in_addr addr; + if (inet_aton(ip, &addr) == 0) { + log_error("constraint", "'%s' is not a valid IP address", ip); + return -1; + } + constraint_set(constraint, ntohl(addr.s_addr), prefix_len, value); + const char *name; + if (value == ADDR_DISALLOWED) + name = "blacklisting"; + else + name = "whitelisting"; + log_trace(name, "%s %s/%i", + name, ip, prefix_len); + return 0; +} + + +static int init_from_file(char *file, const char *name, int value) { FILE *fp; char line[1000]; @@ -66,31 +102,22 @@ static int init(char *file, const char *name, int value) if ((sscanf(line, "%32s", ip)) == EOF) { continue; } - int prefix_len = 32; - char *slash = strchr(ip, '/'); - if (slash) { // split apart network and prefix length - *slash = '\0'; - char *end; - char *len = slash+1; - errno = 0; - prefix_len = strtol(len, &end, 10); - if (end == len || errno != 0 || prefix_len < 0 || prefix_len > 32) { - log_fatal(name, "Unable to parse %s file: %s ('%s' is not a valid prefix length)", name, file, len); - } + if (init_from_string(ip, value)) { + log_fatal(name, "unable to parse %s file: %s", name, file); } - struct in_addr addr; - if (inet_aton(ip, &addr) == 0) { - log_fatal(name, "Unable to parse %s file: %s ('%s' is not a valid IP address)", name, file, ip); - } - constraint_set(constraint, ntohl(addr.s_addr), prefix_len, value); - log_trace(name, "%sing %s/%i", - name, ip, prefix_len); } fclose(fp); return 0; } +static void init_from_array(char **cidrs, size_t len, int value) +{ + for (int i=0; i < (int) len; i++) { + init_from_string(cidrs[i], value); + } +} + uint64_t blacklist_count_allowed() { assert(constraint); @@ -103,27 +130,43 @@ uint64_t blacklist_count_not_allowed() return constraint_count_ips(constraint, ADDR_DISALLOWED); } - // Initialize address constraints from whitelist and blacklist files. // Either can be set to NULL to omit. -int blacklist_init_from_files(char *whitelist_filename, char *blacklist_filename) +int blacklist_init(char *whitelist_filename, char *blacklist_filename, + char **whitelist_entries, size_t whitelist_entries_len, + char **blacklist_entries, size_t blacklist_entries_len) { assert(!constraint); - if (whitelist_filename) { + if (whitelist_filename && whitelist_entries) { + log_warn("whitelist", "both a whitelist file and destination addresses " + "were specified. The union of these two sources " + "will be utilized."); + } + if (whitelist_filename || whitelist_entries) { // using a whitelist, so default to allowing nothing constraint = constraint_init(ADDR_DISALLOWED); log_trace("whitelist", "blacklisting 0.0.0.0/0"); - init(whitelist_filename, "whitelist", ADDR_ALLOWED); + if (whitelist_filename) { + init_from_file(whitelist_filename, "whitelist", ADDR_ALLOWED); + } + if (whitelist_entries) { + init_from_array(whitelist_entries, + whitelist_entries_len, ADDR_ALLOWED); + } } else { // no whitelist, so default to allowing everything constraint = constraint_init(ADDR_ALLOWED); } if (blacklist_filename) { - init(blacklist_filename, "blacklist", ADDR_DISALLOWED); + init_from_file(blacklist_filename, "blacklist", ADDR_DISALLOWED); } - constraint_optimize(constraint); + if (blacklist_entries) { + init_from_array(blacklist_entries, blacklist_entries_len, ADDR_DISALLOWED); + } + constraint_paint_value(constraint, ADDR_ALLOWED); uint64_t allowed = blacklist_count_allowed(); log_debug("blacklist", "%lu addresses allowed to be scanned (%0.0f%% of address space)", allowed, allowed*100./((long long int)1 << 32)); - return 0; + return EXIT_SUCCESS; } + diff --git a/lib/blacklist.h b/lib/blacklist.h index 80a44fc..ce5f136 100644 --- a/lib/blacklist.h +++ b/lib/blacklist.h @@ -1,12 +1,18 @@ +#include #include #ifndef BLACKLIST_H #define BLACKLIST_H +uint32_t blacklist_lookup_index(uint64_t index); int blacklist_is_allowed(uint32_t s_addr); void blacklist_prefix(char *ip, int prefix_len); void whitelist_prefix(char *ip, int prefix_len); -int blacklist_init_from_files(char *whitelist, char*blacklist); +int blacklist_init(char *whitelist, char *blacklist, + char **whitelist_entries, + size_t whitelist_entries_len, + char **blacklist_entries, + size_t blacklist_entries_len); uint64_t blacklist_count_allowed(); uint64_t blacklist_count_not_allowed(); diff --git a/lib/constraint.c b/lib/constraint.c index aa8d0b9..2f70dd2 100644 --- a/lib/constraint.c +++ b/lib/constraint.c @@ -50,16 +50,19 @@ typedef struct node { struct node *l; struct node *r; value_t value; + uint64_t count; } node_t; // As an optimization, we precompute lookups for every prefix of this // length: -#define RADIX_LENGTH 16 +#define RADIX_LENGTH 20 struct _constraint { - node_t *root; // root node of the tree - node_t **radix; // array of nodes for every RADIX_LENGTH prefix - int optimized; // is radix populated and up-to-date? + node_t *root; // root node of the tree + uint32_t *radix; // array of prefixes (/RADIX_LENGTH) that are painted paint_value + size_t radix_len; // number of prefixes in radix array + int painted; // have we precomputed counts for each node? + value_t paint_value; // value for which we precomputed counts }; // Tree operations respect the invariant that every node that isn't a @@ -150,7 +153,7 @@ void constraint_set(constraint_t *con, uint32_t prefix, int len, value_t value) { assert(con); _set_recurse(con->root, prefix, len, value); - con->optimized = 0; + con->painted = 0; } // Return the value pertaining to an address, according to the tree @@ -176,67 +179,88 @@ static int _lookup_ip(node_t *root, uint32_t address) // Return the value pertaining to an address. // (Note: address must be in host byte order.) -int constraint_lookup_ip(constraint_t *con, uint32_t address) +value_t constraint_lookup_ip(constraint_t *con, uint32_t address) { assert(con); - if (con->optimized) { - // Use radix optimization - node_t *node = con->radix[address >> (32 - RADIX_LENGTH)]; + return _lookup_ip(con->root, address); +} + +// Return the nth painted IP address. +static int _lookup_index(node_t *root, uint64_t n) +{ + assert(root); + node_t *node = root; + uint32_t ip = 0; + uint32_t mask = 0x80000000; + for (;;) { if (IS_LEAF(node)) { - return node->value; + return ip | n; } - return _lookup_ip(node, address << RADIX_LENGTH); - } else { - // Do a full lookup using the tree - log_trace("constraint", "Unoptimized lookup"); - return _lookup_ip(con->root, address); + if (n < node->l->count) { + node = node->l; + } else { + n -= node->l->count; + node = node->r; + ip |= mask; + } + mask >>= 1; } } +// For a given value, return the IP address with zero-based index n. +// (i.e., if there are three addresses with value 0xFF, looking up index 1 +// will return the second one). +// Note that the tree must have been previously painted with this value. +uint32_t constraint_lookup_index(constraint_t *con, uint64_t index, value_t value) +{ + assert(con); + if (!con->painted || con->paint_value != value) { + constraint_paint_value(con, value); + } + + uint64_t radix_idx = index / (1 << (32 - RADIX_LENGTH)); + if (radix_idx < con->radix_len) { + // Radix lookup + uint32_t radix_offset = index % (1 << (32 - RADIX_LENGTH)); // TODO: bitwise maths + return con->radix[radix_idx] | radix_offset; + } + + // Otherwise, do the "slow" lookup in tree. + // Note that tree counts do NOT include things in the radix, + // so we subtract these off here. + index -= con->radix_len * (1 << (32 - RADIX_LENGTH)); + assert(index < con->root->count); + return _lookup_index(con->root, index); +} + // Implement count_ips by recursing on halves of the tree. Size represents // the number of addresses in a prefix at the current level of the tree. -static uint64_t _count_ips_recurse(node_t *node, value_t value, uint64_t size) +// If paint is specified, each node will have its count set to the number of +// leaves under it set to value. +// If exclude_radix is specified, the number of addresses will exlcude prefixes +// that are a /RADIX_LENGTH or larger +static uint64_t _count_ips_recurse(node_t *node, value_t value, uint64_t size, int paint, int exclude_radix) { assert(node); + uint64_t n; if (IS_LEAF(node)) { if (node->value == value) { - return size; + n = size; + // Exclude prefixes already included in the radix + if (exclude_radix && size >= (1 << (32 -RADIX_LENGTH))) { + n = 0; + } } else { - return 0; + n = 0; } + } else { + n = _count_ips_recurse(node->l, value, size >> 1, paint, exclude_radix) + + _count_ips_recurse(node->r, value, size >> 1, paint, exclude_radix); } - return _count_ips_recurse(node->l, value, size >> 1) + - _count_ips_recurse(node->r, value, size >> 1); -} - -// Return the number of addresses that have a given value. -uint64_t constraint_count_ips(constraint_t *con, value_t value) -{ - assert(con); - return _count_ips_recurse(con->root, value, (uint64_t)1 << 32); -} - -// Initialize the tree. -// All addresses will initally have the given value. -constraint_t* constraint_init(value_t value) -{ - log_trace("constraint", "Initializing"); - constraint_t* con = malloc(sizeof(constraint_t)); - con->root = _create_leaf(value); - con->radix = calloc(sizeof(node_t *), 1 << RADIX_LENGTH); - assert(con->radix); - con->optimized = 0; - return con; -} - -// Deinitialize and free the tree. -void constraint_free(constraint_t *con) -{ - assert(con); - log_trace("constraint", "Cleaning up"); - _destroy_subtree(con->root); - free(con->radix); - free(con); + if (paint) { + node->count = n; + } + return n; } // Return a node that determines the values for the addresses with @@ -250,8 +274,9 @@ static node_t* _lookup_node(node_t *root, uint32_t prefix, int len) node_t *node = root; uint32_t mask = 0x80000000; + int i; - for (int i=0; i < len; i++) { + for (i=0; i < len; i++) { if (IS_LEAF(node)) { return node; } @@ -265,21 +290,66 @@ static node_t* _lookup_node(node_t *root, uint32_t prefix, int len) return node; } -// After values have been set, precompute prefix lookups. -void constraint_optimize(constraint_t *con) +// For each node, precompute the count of leaves beneath it set to value. +// Note that the tree can be painted for only one value at a time. +void constraint_paint_value(constraint_t *con, value_t value) { assert(con); - if (con->optimized) { - return; - } - log_trace("constraint", "Optimizing constraints"); - for (uint32_t i=0; i < (1 << RADIX_LENGTH); i++) { + log_trace("constraint", "Painting value %lu", value); + + // Paint everything except what we will put in radix + _count_ips_recurse(con->root, value, (uint64_t)1 << 32, 1, 1); + + // Fill in the radix array with a list of addresses + uint32_t i; + con->radix_len = 0; + for (i=0; i < (1 << RADIX_LENGTH); i++) { uint32_t prefix = i << (32 - RADIX_LENGTH); - con->radix[i] = _lookup_node(con->root, prefix, RADIX_LENGTH); + node_t *node = _lookup_node(con->root, prefix, RADIX_LENGTH); + if (IS_LEAF(node) && node->value == value) { + // Add this prefix to the radix + con->radix[con->radix_len++] = prefix; + } } - con->optimized = 1; + log_debug("constraint", "%lu IPs in radix array, %lu IPs in tree", + con->radix_len * (1 << (32 - RADIX_LENGTH)), con->root->count); + con->painted = 1; + con->paint_value = value; } +// Return the number of addresses that have a given value. +uint64_t constraint_count_ips(constraint_t *con, value_t value) +{ + assert(con); + if (con->painted && con->paint_value == value) { + return con->root->count + con->radix_len * (1 << (32 - RADIX_LENGTH)); + } else { + return _count_ips_recurse(con->root, value, (uint64_t)1 << 32, 0, 0); + } +} + +// Initialize the tree. +// All addresses will initally have the given value. +constraint_t* constraint_init(value_t value) +{ + log_trace("constraint", "Initializing"); + constraint_t* con = malloc(sizeof(constraint_t)); + con->root = _create_leaf(value); + con->radix = calloc(sizeof(uint32_t), 1 << RADIX_LENGTH); + assert(con->radix); + con->painted = 0; + return con; +} + +// Deinitialize and free the tree. +void constraint_free(constraint_t *con) +{ + assert(con); + log_trace("constraint", "Cleaning up"); + _destroy_subtree(con->root); + free(con->radix); + free(con); +} /* int main(void) @@ -317,68 +387,3 @@ int main(void) } */ -/* -static int init(constraint_t *con, char *file, const char *name, value_t value) -{ - FILE *fp; - char line[1000]; - int blocked = 0; - - fp = fopen(file, "r"); - if (fp == NULL) { - log_fatal(name, "Unable to open %s file: %s: %s", - name, file, strerror(errno)); - } - - while (fgets(line, sizeof(line), fp) != NULL) { - char *comment = strchr(line, '#'); - if (comment) { - *comment = '\0'; - } - char ip[33]; - if ((sscanf(line, "%32s", ip)) == EOF) { - continue; - } - int prefix_len; - char *slash = strchr(ip, '/'); - if (slash == NULL) { - log_fatal(name, - "Unable to parse %s file: %s", - name, file); - } - // split apart network and prefix length - *slash = '\0'; - prefix_len = atoi(&slash[1]); - constraint_set(con, ntohl(inet_addr(ip)), prefix_len, value); - - blocked++; - } - fclose(fp); - return 0; -} - - - -void main() -{ - log_init(stderr, LOG_TRACE); - - constraint_t *con = constraint_init(1); - init(con, "blacklist.prefixes", "blacklist", 0); - //constraint_optimize(con); - - printf("count(0)=%lu\n", constraint_count_ips(con, 0)); - printf("count(1)=%lu\n", constraint_count_ips(con, 1)); - - uint32_t i=0, count=0; - do { - if (constraint_lookup_ip(con, i)) - count++; - } while (++i != 0); - printf("derived count(1)=%u\n", count); - - constraint_free(con); - -} - - */ diff --git a/lib/constraint.h b/lib/constraint.h index 2170f8f..228d755 100644 --- a/lib/constraint.h +++ b/lib/constraint.h @@ -1,14 +1,17 @@ #ifndef CONSTRAINT_H #define CONSTRAINT_H +#include + typedef struct _constraint constraint_t; -typedef int value_t; +typedef unsigned int value_t; constraint_t* constraint_init(value_t value); void constraint_free(constraint_t *con); void constraint_set(constraint_t *con, uint32_t prefix, int len, value_t value); -void constraint_optimize(constraint_t *con); -int constraint_lookup_ip(constraint_t *con, uint32_t address); +value_t constraint_lookup_ip(constraint_t *con, uint32_t address); uint64_t constraint_count_ips(constraint_t *con, value_t value); +uint32_t constraint_lookup_index(constraint_t *con, uint64_t index, value_t value); +void constraint_paint_value(constraint_t *con, value_t value); #endif //_CONSTRAINT_H diff --git a/lib/pbm.c b/lib/pbm.c new file mode 100644 index 0000000..154a535 --- /dev/null +++ b/lib/pbm.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include + +#include "logger.h" + +#define NUM_VALUES 0xFFFFFFFF +#define PAGE_SIZE 0xFFFF +#define NUM_PAGES (NUM_VALUES / PAGE_SIZE) +#define SIZE (8*sizeof(uint64_t)) + +uint64_t** pbm_init(void) +{ + uint64_t** retv = calloc(NUM_PAGES, sizeof(void*)); + if (!retv) { + log_fatal("pbm", "unable to allocate memory for base page table"); + } + return retv; +} + +static int bm_check(uint64_t *bm, uint32_t v) +{ + return bm[v/SIZE] & (1<< (v % SIZE)); +} + +static void bm_set(uint64_t *bm, uint32_t v) +{ + bm[v/SIZE] |= (1 << (v % SIZE)); +} + +int pbm_check(uint64_t **b, uint32_t v) +{ + uint32_t top = v >> 16; + uint32_t bottom = v & PAGE_SIZE; + return b[top] && bm_check(b[top], bottom); +} + +void pbm_set(uint64_t **b, uint32_t v) +{ + uint32_t top = v >> 16; + uint32_t bottom = v & PAGE_SIZE; + if (!b[top]) { + uint64_t *bm = malloc(PAGE_SIZE); + if (!bm) { + log_fatal("bpm", "unable to allocate memory for new bitmap page"); + } + memset(bm, 0, PAGE_SIZE); + b[top] = bm; + } + bm_set(b[top], bottom); +} + diff --git a/lib/pbm.h b/lib/pbm.h new file mode 100644 index 0000000..df3b6fd --- /dev/null +++ b/lib/pbm.h @@ -0,0 +1,5 @@ +#include + +uint64_t** pbm_init(void); +int pbm_check(uint64_t **b, uint32_t v); +void pbm_set(uint64_t **b, uint32_t v); diff --git a/lib/redis.c b/lib/redis.c index 20bf4e7..4e11743 100644 --- a/lib/redis.c +++ b/lib/redis.c @@ -6,16 +6,18 @@ * of the License at http://www.apache.org/licenses/LICENSE-2.0 */ -#include #include "redis.h" + +#include #include #include -#include "assert.h" -#include "logger.h" #include +#include + #include -#define REDIS_UNIX_PATH "/tmp/redis.sock" +#include "logger.h" + #define REDIS_TIMEOUT 2 #undef MIN @@ -23,13 +25,74 @@ static redisContext *rctx; -static redisContext* redis_connect(void) +redisconf_t *redis_parse_connstr(char *connstr) { + redisconf_t *retv = malloc(sizeof(redisconf_t)); + if (!strncmp("tcp://", connstr, 6)) { + char *servername = malloc(strlen(connstr)); + assert(servername); + char *list_name = malloc(strlen(connstr)); + assert(list_name); + uint32_t port; + if (sscanf(connstr, "tcp://%[^:]:%u/%s", servername, + &port, list_name) != 3) { + log_fatal("redis", "unable to parse redis connection string. This " + "should be of the form tcp://server:port/list-name " + "for TCP connections. All fields are required."); + } + retv->type = T_TCP; + retv->server = servername; + retv->port = port; + retv->list_name = list_name; + retv->path = NULL; + } else if (!strncmp("local://", connstr, 8)) { + // looking for something along the lines of + // local:///tmp/redis.sock/list-name + char *path = malloc(strlen(connstr)); + assert(path); + char *list_name = malloc(strlen(connstr)); + assert(list_name); + connstr = connstr + (size_t) 8; + char *listname = strrchr(connstr, '/') + (size_t) 1; + connstr[strrchr(connstr, '/') - connstr] = '\0'; + strcpy(path, connstr); + strcpy(list_name, listname); + retv->type = T_LOCAL; + retv->list_name = list_name; + retv->path = path; + retv->server = NULL; + retv->port = 0; + } else { + log_fatal("redis", "unable to parse connection string. does not begin with " + "local:// or tcp:// as expected"); + } + return retv; +} + +static redisContext* redis_connect(char *connstr) +{ + redisconf_t *c; + // handle old behavior where we only connected to a specific + // socket that we #defined. + if (!connstr) { + c = malloc(sizeof(redisconf_t)); + assert(c); + c->type = T_LOCAL; + c->path = "/tmp/redis.sock"; + } else { + c = redis_parse_connstr(connstr); + assert(c); + } struct timeval timeout; timeout.tv_sec = REDIS_TIMEOUT; timeout.tv_usec = 0; - return (redisContext*) redisConnectUnixWithTimeout(REDIS_UNIX_PATH, + if (c->type == T_LOCAL) { + return (redisContext*) redisConnectUnixWithTimeout(c->path, timeout); + } else { + return (redisContext*) redisConnectWithTimeout(c->server, + c->port, timeout); + } } static int chkerr(redisReply *reply) @@ -47,9 +110,9 @@ static int chkerr(redisReply *reply) return 0; } -int redis_init(void) +int redis_init(char *connstr) { - rctx = redis_connect(); + rctx = redis_connect(connstr); if (!rctx) { return -1; } diff --git a/lib/redis.h b/lib/redis.h index 229136c..ca636bf 100644 --- a/lib/redis.h +++ b/lib/redis.h @@ -5,7 +5,20 @@ #ifndef REDIS_ZHELPERS_H #define REDIS_ZHELPERS_H -int redis_init(void); +#define T_TCP 0 +#define T_LOCAL 1 + +typedef struct redisconf { + int type; + char *path; + char *server; + uint16_t port; + char *list_name; +} redisconf_t; + +redisconf_t *redis_parse_connstr(char *connstr); + +int redis_init(char*); int redis_close(void); diff --git a/lib/stack.c b/lib/stack.c new file mode 100644 index 0000000..8dfe0db --- /dev/null +++ b/lib/stack.c @@ -0,0 +1,40 @@ +#include "stack.h" +#include "../lib/xalloc.h" + +#include + +struct stack { + size_t max_size; + size_t cur_size; + void** arr; +}; + +stack_t* alloc_stack(size_t size) +{ + stack_t* stack = xmalloc(sizeof(stack_t)); + stack->arr = xcalloc(size, sizeof(void*)); + stack->max_size = size; + stack->cur_size = 0; + return stack; +} + +void free_stack(stack_t* stack) +{ + xfree(stack->arr); + xfree(stack); +} + +void push(stack_t* stack, void* elt) +{ + if (stack->cur_size == stack->max_size) { + stack->max_size *= 2; + xrealloc(stack->arr, stack->max_size); + } + stack->arr[stack->cur_size++] = elt; +} + +void* pop(stack_t* stack) +{ + void* res = stack->arr[--stack->cur_size]; + return res; +} diff --git a/lib/stack.h b/lib/stack.h new file mode 100644 index 0000000..9417238 --- /dev/null +++ b/lib/stack.h @@ -0,0 +1,15 @@ +#ifndef ZMAP_STACK_H +#define ZMAP_STACK_H + +#include + +struct stack; +typedef struct stack stack_t; + +stack_t* alloc_stack(size_t size); +void free_stack(stack_t* stack); + +void push(stack_t* stack, void* elt); +void* pop(stack_t* stack); + +#endif /* ZMAP_STACK_H */ \ No newline at end of file diff --git a/lib/xalloc.c b/lib/xalloc.c new file mode 100644 index 0000000..60e73e7 --- /dev/null +++ b/lib/xalloc.c @@ -0,0 +1,44 @@ +#include "xalloc.h" +#include "../lib/logger.h" + +#include + +void die() __attribute__((noreturn)); + +void* xcalloc(size_t count, size_t size) +{ + void* res = calloc(count, size); + if (res == NULL) { + die(); + } + return res; +} + +void xfree(void *ptr) +{ + free(ptr); +} + +void* xmalloc(size_t size) +{ + void* res = malloc(size); + if (res == NULL) { + die(); + } + return res; +} + +void* xrealloc(void *ptr, size_t size) +{ + void* res = realloc(ptr, size); + if (res == NULL) { + die(); + } + return res; +} + +void die() +{ + log_fatal("zmap", "Out of memory"); +} + diff --git a/lib/xalloc.h b/lib/xalloc.h new file mode 100644 index 0000000..78cd278 --- /dev/null +++ b/lib/xalloc.h @@ -0,0 +1,14 @@ +#ifndef ZMAP_ALLOC_H +#define ZMAP_ALLOC_H + +#include + +void* xcalloc(size_t count, size_t size); + +void xfree(void *ptr); + +void* xmalloc(size_t size); + +void* xrealloc(void *ptr, size_t size); + +#endif \ No newline at end of file diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..87de78c --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,4 @@ +lexer.c +lexer.h +parser.c +parser.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..95bd9ab --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,107 @@ +include_directories( + "${CMAKE_CURRENT_BINARY_DIR}" + ${PROJECT_SOURCE_DIR}/lib + ${PROJECT_SOURCE_DIR}/src + ${PROJECT_SOURCE_DIR}/src/output_modules + ) + +SET(LIB_SOURCES + ${PROJECT_SOURCE_DIR}/lib/blacklist.c + ${PROJECT_SOURCE_DIR}/lib/constraint.c + ${PROJECT_SOURCE_DIR}/lib/logger.c + ${PROJECT_SOURCE_DIR}/lib/pbm.c + ${PROJECT_SOURCE_DIR}/lib/random.c + ${PROJECT_SOURCE_DIR}/lib/rijndael-alg-fst.c + ${PROJECT_SOURCE_DIR}/lib/stack.c + ${PROJECT_SOURCE_DIR}/lib/xalloc.c +) + +# ADD YOUR PROBE MODULE HERE +SET(EXTRA_PROBE_MODULES + + ) + +# ADD YOUR OUTPUT MODULE HERE +SET(EXTRA_OUTPUT_MODULES + + ) + +SET(OUTPUT_MODULE_SOURCES + output_modules/module_csv.c + output_modules/output_modules.c + ) + +SET(PROBE_MODULE_SOURCES + probe_modules/module_icmp_echo.c + probe_modules/module_tcp_synscan.c + probe_modules/module_udp.c + probe_modules/packet.c + probe_modules/probe_modules.c + ) + +SET(SOURCES + aesrand.c + cyclic.c + expression.c + fieldset.c + filter.c + get_gateway.c + monitor.c + recv.c + send.c + state.c + validate.c + zmap.c + zopt_compat.c + "${CMAKE_CURRENT_BINARY_DIR}/zopt.h" + "${CMAKE_CURRENT_BINARY_DIR}/lexer.c" + "${CMAKE_CURRENT_BINARY_DIR}/parser.c" + ${EXTRA_PROBE_MODULES} + ${EXTRA_OUTPUT_MODULES} + ${PROBE_MODULE_SOURCES} + ${OUTPUT_MODULE_SOURCES} + ${LIB_SOURCES} + ) + +if (WITH_JSON) + SET(SOURCES ${SOURCES} output_modules/module_json.c) +endif() + +if (WITH_REDIS) + SET(SOURCES ${SOURCES} ${PROJECT_SOURCE_DIR}/lib/redis.c output_modules/module_redis.c) +endif() + +add_custom_command(OUTPUT zopt.h + COMMAND gengetopt -C --no-help --no-version --unamed-opts=SUBNETS -i "${CMAKE_CURRENT_SOURCE_DIR}/zopt.ggo" -F "${CMAKE_CURRENT_BINARY_DIR}/zopt" + ) + +add_custom_command(OUTPUT lexer.c + COMMAND flex -o "${CMAKE_CURRENT_BINARY_DIR}/lexer.c" --header-file="${CMAKE_CURRENT_BINARY_DIR}/lexer.h" lexer.l + ) + +add_custom_command(OUTPUT parser.c + COMMAND byacc -d -o parser.c parser.y + ) + +add_executable(zmap ${SOURCES}) + +target_link_libraries( + zmap + pcap gmp m + ${REDIS_LIBS} + ${JSON_LIBRARIES} + ) + +# Install binary +install( + TARGETS + zmap + RUNTIME DESTINATION sbin +) + +# Install Manpages +install( + FILES + zmap.1 + DESTINATION share/man/man1 + ) diff --git a/src/Makefile b/src/Makefile deleted file mode 100644 index 7a85fd5..0000000 --- a/src/Makefile +++ /dev/null @@ -1,116 +0,0 @@ -#<<<<<<< HEAD -CC=gcc -#CC=clang - -#CFLAGS=-Wall -pedantic -Wextra -std=gnu99 -I../lib -I./ -Ioutput_modules -O2 -g -#wbk makefile hack to get rolling with C port. Fix later lol -CFLAGS=-Wall -pedantic -Wextra -std=gnu99 -I../lib -I./ -Ioutput_modules \ --O2 -g -D__FREEBSD__ -D__FREEBSD_INCLUDES__ -D__FREEBSD_PTHREADS__ -DZMAP_PCAP_INJECT -LDFLAGS=-g -pthread -LDLIBS= -lpcap -lgmp -lm -#======= -#INCLUDE+=-I../lib -I./ -Ioutput_modules -#LDFLAGS+=-pthread -#LDLIBS+=-lpcap -lgmp -lm -#>>>>>>> 5cd6f3294cb4f6ddf711b4c42d72893989a9dc3d -TARGETS=zmap -VPATH=../lib:output_modules:probe_modules -PREFIX=/usr/local - -#wbk -LIBDIR=/usr/local/lib - -INSTALL=install -INSTALLDATA=install -m 644 -mandir=$(PREFIX)/man/man1/ -oldmanfile=/usr/share/man/man1/zmap.1 -bindir=$(PREFIX)/sbin - -# Hardening and warnings for building with gcc -#<<<<<<< HEAD -#M aybe add -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -#-Wold-style-definition -Wswitch-enum -GCCWARNINGS = -Wall -fno-strict-aliasing -W -Wfloat-equal -Wundef \ --Wpointer-arith \ --Wwrite-strings -Wredundant-decls -Wchar-subscripts -Wcomment \ --Wformat=2 -Wwrite-strings -Wredundant-decls \ --Wnested-externs -Wbad-function-cast -Winit-self \ --Wmissing-field-initializers \ --Waddress -Wmissing-noreturn -Wnormalized=id \ --Woverride-init -Wstrict-overflow=1 -Wextra \ --Wstack-protector -Wformat -Wformat-security -Wpointer-sign -Wno-format-nonliteral -Wno-format-y2k -GCCHARDENING=-D_FORTIFY_SOURCE=2 -fstack-protector-all -fwrapv -fPIC --param ssp-buffer-size=1 -# clang doesn't like this -#LDHARDENING=-z relro -z now - -EXTRACFLAGS=-g -O2 $(EXTRA_CFLAGS) $(GCCHARDENING) $(GCCWARNINGS) -#======= -# Maybe add -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -#GCCWARNINGS = -Wall -Wformat=2 -Wno-format-nonliteral\ -#-pedantic -fno-strict-aliasing \ -#-Wextra \ -#-Wfloat-equal -Wundef -Wwrite-strings -Wredundant-decls \ -#-Wnested-externs -Wbad-function-cast -Winit-self \ -#-Wmissing-noreturn -Wnormalized=id \ -#-Wstack-protector - -#GCCHARDENING=-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fstack-protector-all -fwrapv -fPIC --param ssp-buffer-size=1 -#LDHARDENING=-z relro -z now - -#EXTRACFLAGS=-std=gnu99 -g -O2 $(GCCHARDENING) $(GCCWARNINGS) $(EXTRA_CFLAGS) -Werror -#>>>>>>> 5cd6f3294cb4f6ddf711b4c42d72893989a9dc3d -EXTRALDFLAGS= $(LDHARDENING) - -CFLAGS+=$(INCLUDE) $(EXTRACFLAGS) -LDFLAGS+=$(EXTRALDFLAGS) -LDFLAGS+=-L$(LIBDIR) - -probemodules=module_tcp_synscan.o module_icmp_echo.o module_udp.o #ADD YOUR PROBE MODULE HERE -outputmodules= module_csv.o #ADD YOUR OUTPUT MODULE HERE - -objects=constraint.o blacklist.o cyclic.o logger.o send.o recv.o state.o monitor.o zopt_compat.o zmap.o random.o output_modules.o packet.o probe_modules.o ${probemodules} ${outputmodules} validate.o rijndael-alg-fst.o get_gateway.o aesrand.o fieldset.o -redis_objects=module_redis.o redis.o - -ifeq ($(REDIS), true) - LDLIBS+=-lhiredis - objects+=$(redis_objects) - CFLAGS+=-DREDIS -endif - -ifeq ($(JSON), true) - LDLIBS+=$(shell pkg-config --libs json-c) - CFLAGS+=-DJSON $(shell pkg-config --cflags json-c) - objects+=module_json.o -endif - -all: $(TARGETS) - -$(TARGETS): - $(CC) $(CFLAGS) $(DFLAGS) $(LDFLAGS) $^ -o $@ $(LDLIBS) - -zmap: $(objects) - -zopt_compat.o: zopt.c - -zopt.c zopt.h: zopt.ggo - gengetopt -C --no-help --no-version -i $^ -F $* - -install: zmap - $(INSTALL) zmap $(bindir)/zmap - test -d /etc/zmap || (mkdir /etc/zmap && $(INSTALLDATA) ../conf/* /etc/zmap/) - test -f $(oldmanfile) && rm -f $(oldmanfile) && mandb -f $(oldmanfile) || /bin/true # remove old man page if it's there - test -d $(mandir) || mkdir -p $(mandir) - $(INSTALLDATA) ./zmap.1 $(mandir) - @echo "\n**************\nSuccess! ZMap is installed. Try running (as root):\nzmap -p 80 -N 10 -B 1M -o -\n**************" - -uninstall: - test -f $(oldmanfile) && rm -f $(oldmanfile) && mandb -f $(oldmanfile) || /bin/true # remove old man page if it's there - test -f $(mandir)/zmap.1 && rm -f $(mandir)/zmap.1 && mandb -f $(mandir)/zmap.1 || /bin/true # remove current man page if it's there - rm -f $(bindir)/zmap - - -clean: - -rm -f $(objects) $(redis_objects) $(TARGETS) - -.PHONY: install clean - diff --git a/src/cyclic.c b/src/cyclic.c index 2134d7e..3fa18a3 100644 --- a/src/cyclic.c +++ b/src/cyclic.c @@ -61,28 +61,65 @@ #include "aesrand.h" #define LSRC "cyclic" -#define PRIME 4294967311 // 2^32 + 15 -#define KNOWN_PRIMROOT 3 -// distinct prime factors of 2^32 + 15 -static const uint64_t psub1_f[] = { 2, 3, 5, 131, 364289 }; +typedef struct cyclic_group { + uint64_t prime; + uint64_t known_primroot; + size_t num_prime_factors; // number of unique prime factors of (prime-1) + uint64_t prime_factors[10]; // unique prime factors of (prime-1) +} cyclic_group_t; -// selected primitive root that we'll use as the generator +// We will pick the first cyclic group from this list that is +// larger than the number of IPs in our whitelist. E.g. for an +// entire Internet scan, this would be cyclic32 +// Note: this list should remain ordered by size (primes) ascending. +static cyclic_group_t groups[] = { +{ // 2^16 + 1 + .prime = 65537, + .known_primroot = 3, + .prime_factors = {2}, + .num_prime_factors = 1 +}, +{ // 2^24 + 43 + .prime = 16777259, + .known_primroot = 2, + .prime_factors = {2, 23, 103, 3541}, + .num_prime_factors = 4 +}, +{ // 2^28 + 3 + .prime = 268435459, + .known_primroot = 2, + .prime_factors = {2, 3, 19, 87211}, + .num_prime_factors = 4 +}, +{ // 2^32 + 15 + .prime = 4294967311, + .known_primroot = 3, + .prime_factors = {2, 3, 5, 131, 364289}, + .num_prime_factors = 5 +} +}; + + +// selected prime/primitive root that we'll use as the generator +static uint64_t prime = 0; static uint64_t primroot = 0; static uint64_t current = 0; +static uint64_t num_addrs = 0; + #define COPRIME 1 -#define NOT_COPRIME 0 +#define NOT_COPRIME 0 // check whether two integers are coprime -static int check_coprime(uint64_t check) +static int check_coprime(uint64_t check, const cyclic_group_t *group) { - for (unsigned i=0; i < sizeof(psub1_f)/sizeof(psub1_f[0]); i++) { - if (psub1_f[i] > check && !(psub1_f[i] % check)) { + for (unsigned i=0; i < group->num_prime_factors; i++) { + if (group->prime_factors[i] > check && !(group->prime_factors[i] % check)) { return NOT_COPRIME; - } else if (psub1_f[i] < check && !(check % psub1_f[i])) { + } else if (group->prime_factors[i] < check && !(check % group->prime_factors[i])) { return NOT_COPRIME; - } else if (psub1_f[i] == check) { + } else if (group->prime_factors[i] == check) { return NOT_COPRIME; } } @@ -90,18 +127,18 @@ static int check_coprime(uint64_t check) } // find gen of cyclic group Z modulo PRIME -static uint64_t find_primroot(void) +static uint64_t find_primroot(const cyclic_group_t *group) { // what luck, rand() returns a uint32_t! - uint32_t candidate = (uint32_t) aesrand_getword() & 0xFFFF; - while(check_coprime(candidate) != COPRIME) { + uint32_t candidate = (uint32_t) aesrand_getword() & 0xFFFFFFFF; + while(check_coprime(candidate, group) != COPRIME) { ++candidate; } // pre-modded result is gigantic so use GMP mpz_t base, power, prime, primroot; - mpz_init_set_d(base, (double) KNOWN_PRIMROOT); + mpz_init_set_d(base, (double) group->known_primroot); mpz_init_set_d(power, (double) candidate); - mpz_init_set_d(prime, (double) PRIME); + mpz_init_set_d(prime, (double) group->prime); mpz_init(primroot); mpz_powm(primroot, base, power, prime); uint64_t retv = (uint64_t) mpz_get_ui(primroot); @@ -115,6 +152,33 @@ static uint64_t find_primroot(void) int cyclic_init(uint32_t primroot_, uint32_t current_) { assert(!(!primroot_ && current_)); + // Initialize blacklist + if (blacklist_init(zconf.whitelist_filename, zconf.blacklist_filename, + zconf.destination_cidrs, zconf.destination_cidrs_len, + NULL, 0)) { + return -1; + } + num_addrs = blacklist_count_allowed(); + if (!num_addrs) { + log_error("blacklist", "no addresses are eligible to be scanned in the " + "current configuration. This may be because the " + "blacklist being used by ZMap (%s) prevents " + "any addresses from receiving probe packets.", + zconf.blacklist_filename + ); + exit(EXIT_FAILURE); + } + + const cyclic_group_t *cur_group = NULL; + for (uint32_t i=0; i num_addrs) { + cur_group = &groups[i]; + log_debug("cyclic", "using prime %lu, known_primroot %lu", + cur_group->prime, cur_group->known_primroot); + prime = groups[i].prime; + break; + } + } if (zconf.use_seed) { aesrand_init(zconf.seed+1); @@ -123,17 +187,17 @@ int cyclic_init(uint32_t primroot_, uint32_t current_) } if (!primroot_) { do { - primroot = find_primroot(); + primroot = find_primroot(cur_group); } while (primroot >= (1LL << 32)); log_debug(LSRC, "primitive root: %lld", primroot); - current = (uint32_t) aesrand_getword() & 0xFFFF; + current = (uint32_t) aesrand_getword() & 0xFFFFFFFF; log_debug(LSRC, "starting point: %lld", current); } else { primroot = primroot_; log_debug(LSRC, "primitive root %lld specified by caller", primroot); if (!current_) { - current = (uint32_t) aesrand_getword() & 0xFFFF; + current = (uint32_t) aesrand_getword() & 0xFFFFFFFF; log_debug(LSRC, "no cyclic starting point, " "selected random startpoint: %lld", current); @@ -144,11 +208,6 @@ int cyclic_init(uint32_t primroot_, uint32_t current_) } } zconf.generator = primroot; - if (blacklist_init_from_files(zconf.whitelist_filename, - zconf.blacklist_filename)) { - return -1; - } - // make sure current is an allowed ip cyclic_get_next_ip(); @@ -157,7 +216,7 @@ int cyclic_init(uint32_t primroot_, uint32_t current_) uint32_t cyclic_get_curr_ip(void) { - return (uint32_t) current; + return (uint32_t) blacklist_lookup_index(current-1); } uint32_t cyclic_get_primroot(void) @@ -169,7 +228,7 @@ static inline uint32_t cyclic_get_next_elem(void) { do { current *= primroot; - current %= PRIME; + current %= prime; } while (current >= (1LL << 32)); return (uint32_t) current; } @@ -178,11 +237,10 @@ uint32_t cyclic_get_next_ip(void) { while (1) { uint32_t candidate = cyclic_get_next_elem(); - if (!blacklist_is_allowed(candidate)) { - zsend.blacklisted++; - } else { - return candidate; + if (candidate-1 < num_addrs) { + return blacklist_lookup_index(candidate-1); } + zsend.blacklisted++; } } diff --git a/src/expression.c b/src/expression.c new file mode 100644 index 0000000..537be78 --- /dev/null +++ b/src/expression.c @@ -0,0 +1,162 @@ +#include "expression.h" +#include "fieldset.h" + +#include "../lib/xalloc.h" + +/* Static helper functions */ + +static node_t* alloc_node(); +static int eval_gt_node(node_t *node, fieldset_t *fields); +static int eval_lt_node(node_t *node, fieldset_t *fields); +static int eval_eq_node(node_t *node, fieldset_t *fields); +static int eval_lt_eq_node(node_t *node, fieldset_t *fields); +static int eval_gt_eq_node(node_t *node, fieldset_t *fields); + + +static node_t* alloc_node() +{ + node_t *node = xmalloc(sizeof(node_t)); + memset(node, 0, sizeof(node_t)); + return node; +} + +static int eval_gt_node(node_t *node, fieldset_t *fields) +{ + int index = node->left_child->value.field.index; + uint64_t expected = node->right_child->value.int_literal; + uint64_t actual = fs_get_uint64_by_index(fields, index); + return (actual > expected); +} + +static int eval_lt_node(node_t *node, fieldset_t *fields) +{ + int index = node->left_child->value.field.index; + uint64_t expected = node->right_child->value.int_literal; + uint64_t actual = fs_get_uint64_by_index(fields, index); + return (actual < expected); +} + +static int eval_eq_node(node_t *node, fieldset_t *fields) +{ + node_t *literal = node->right_child; + int index = node->left_child->value.field.index; + char *expected, *actual; + switch (literal->type) { + case STRING: + expected = literal->value.string_literal; + actual = fs_get_string_by_index(fields, index); + return (strcmp(expected, actual) == 0); + break; + case INT: + return (fs_get_uint64_by_index(fields, index) == literal->value.int_literal); + break; + default: + printf("wat\n"); + break; + } + return 0; +} + +static int eval_lt_eq_node(node_t *node, fieldset_t *fields) +{ + return !(eval_gt_node(node, fields)); +} + +static int eval_gt_eq_node(node_t *node, fieldset_t *fields) +{ + return !(eval_lt_node(node, fields)); +} + + +/* Exposed functions */ + +node_t* make_op_node(enum operation op) +{ + node_t* node = alloc_node(); + node->type = OP; + node->value.op = op; + return node; +} + +node_t* make_field_node(char *fieldname) +{ + node_t *node = alloc_node(); + node->type = FIELD; + node->value.field.fieldname = fieldname; + return node; +} + +node_t* make_string_node(char *literal) +{ + node_t *node = alloc_node(); + node->type = STRING; + node->value.string_literal = literal; + return node; +} + +node_t* make_int_node(int literal) +{ + node_t *node = alloc_node(); + node->type = INT; + node->value.int_literal = literal; + return node; +} + +int evaluate_expression(node_t *root, fieldset_t *fields) +{ + if (!root) return 1; + switch (root->type) { /* XXX Not sure if runs */ + case FIELD: + case STRING: + case INT: + return 1; + case OP: + break; + } + switch (root->value.op) { + case GT: + return eval_gt_node(root, fields); + case LT: + return eval_lt_node(root, fields); + case EQ: + return eval_eq_node(root, fields); + case NEQ: + return (!eval_eq_node(root, fields)); + case LT_EQ: + return eval_lt_eq_node(root, fields); + case GT_EQ: + return eval_gt_eq_node(root, fields); + case AND: + return (evaluate_expression(root->left_child, fields) + && evaluate_expression(root->right_child, fields)); + case OR: + return (evaluate_expression(root->left_child, fields) + || evaluate_expression(root->right_child, fields)); + } + return 0; +} + +void print_expression(node_t *root) +{ + if (!root) return; + printf("%s", "( "); + print_expression(root->left_child); + switch (root->type) { + case OP: + printf(" %i ", root->value.op); + break; + case FIELD: + printf(" (%s", root->value.field.fieldname); + break; + case STRING: + printf("%s) ", root->value.string_literal); + break; + case INT: + printf(" %llu) ", (long long unsigned) root->value.int_literal); + break; + default: + break; + } + print_expression(root->right_child); + printf("%s", " )"); +} diff --git a/src/expression.h b/src/expression.h new file mode 100644 index 0000000..d65668d --- /dev/null +++ b/src/expression.h @@ -0,0 +1,50 @@ +#ifndef ZMAP_TREE_H +#define ZMAP_TREE_H + +#include "fieldset.h" + +#include +#include +#include +#include + +enum operation { + GT, LT, EQ, NEQ, AND, OR, LT_EQ, GT_EQ +}; + +enum node_type { + OP, FIELD, STRING, INT +}; + +struct field_id { + int index; + char *fieldname; +}; + +union node_value { + struct field_id field; + char *string_literal; + uint64_t int_literal; + enum operation op; +}; + +typedef struct node { + struct node *left_child; + struct node *right_child; + enum node_type type; + union node_value value; +} node_t; + +node_t* make_op_node(enum operation op); + +node_t* make_field_node(char *fieldname); + +node_t* make_string_node(char *literal); + +node_t* make_int_node(int literal); + +int evaluate_expression(node_t *root, fieldset_t *fields); + +void print_expression(node_t *root); + +#endif /* ZMAP_TREE_H */ \ No newline at end of file diff --git a/src/fieldset.c b/src/fieldset.c index 5a3534d..fa92652 100644 --- a/src/fieldset.c +++ b/src/fieldset.c @@ -32,13 +32,13 @@ fieldset_t *fs_new_fieldset(void) log_fatal("fieldset", "unable to allocate new fieldset"); } memset(f, 0, sizeof(fieldset_t)); - return f; + return f; } /* wbk TODO: Might have some subtle issues here on 32 bit architectures. Getting compiler warnings about casting void* to uint64_t */ static inline void fs_add_word(fieldset_t *fs, const char *name, int type, - int free_, size_t len, void *value) + int free_, size_t len, field_val_t value) { if (fs->len + 1 >= MAX_FIELDS) { log_fatal("fieldset", "out of room in fieldset"); @@ -48,24 +48,24 @@ static inline void fs_add_word(fieldset_t *fs, const char *name, int type, f->type = type; f->name = name; f->len = len; - f->value = (uint64_t) value; + f->value = value; f->free_ = free_; } static void fs_modify_word(fieldset_t *fs, const char *name, int type, - int free_, size_t len, void *value) + int free_, size_t len, field_val_t value) { int i; for (i=0; ilen; i++) { if (!strcmp(fs->fields[i].name, name)) { if (fs->fields[i].free_) { - free((void*)fs->fields[i].value); - fs->fields[i].value = 0; + free(fs->fields[i].value.ptr); + fs->fields[i].value.ptr = NULL; } fs->fields[i].type = type; fs->fields[i].free_ = free_; fs->fields[i].len = len; - fs->fields[i].value = (uint64_t)value; + fs->fields[i].value = value; return; } } @@ -74,55 +74,63 @@ static void fs_modify_word(fieldset_t *fs, const char *name, int type, void fs_add_null(fieldset_t *fs, const char *name) { - fs_add_word(fs, name, FS_NULL, 0, 0, NULL); + field_val_t val = { .ptr = NULL }; + fs_add_word(fs, name, FS_NULL, 0, 0, val); } void fs_add_string(fieldset_t *fs, const char *name, char *value, int free_) { - fs_add_word(fs, name, FS_STRING, free_, strlen(value), (void*) value); + field_val_t val = { .ptr = value }; + fs_add_word(fs, name, FS_STRING, free_, strlen(value), val); } void fs_add_uint64(fieldset_t *fs, const char *name, uint64_t value) { - fs_add_word(fs, name, FS_UINT64, 0, sizeof(uint64_t), (void*) value); + field_val_t val = { .num = value }; + fs_add_word(fs, name, FS_UINT64, 0, sizeof(uint64_t), val); } void fs_add_binary(fieldset_t *fs, const char *name, size_t len, void *value, int free_) { - fs_add_word(fs, name, FS_BINARY, free_, len, value); + field_val_t val = { .ptr = value }; + fs_add_word(fs, name, FS_BINARY, free_, len, val); } // Modify void fs_modify_null(fieldset_t *fs, const char *name) { - fs_modify_word(fs, name, FS_NULL, 0, 0, NULL); + field_val_t val = { .ptr = NULL }; + fs_modify_word(fs, name, FS_NULL, 0, 0, val); } void fs_modify_string(fieldset_t *fs, const char *name, char *value, int free_) { - fs_modify_word(fs, name, FS_STRING, free_, strlen(value), (void*) value); + field_val_t val = { .ptr = value }; + fs_modify_word(fs, name, FS_STRING, free_, strlen(value), val); } void fs_modify_uint64(fieldset_t *fs, const char *name, uint64_t value) { - fs_modify_word(fs, name, FS_UINT64, 0, sizeof(uint64_t), (void*) value); + field_val_t val = { .num = value }; + fs_modify_word(fs, name, FS_UINT64, 0, sizeof(uint64_t), val); } void fs_modify_binary(fieldset_t *fs, const char *name, size_t len, void *value, int free_) { - fs_modify_word(fs, name, FS_BINARY, free_, len, value); + field_val_t val = { .ptr = value }; + fs_modify_word(fs, name, FS_BINARY, free_, len, val); } uint64_t fs_get_uint64_by_index(fieldset_t *fs, int index) { - return (uint64_t) fs->fields[index].value; + return (uint64_t) fs->fields[index].value.num; } char* fs_get_string_by_index(fieldset_t *fs, int index) { - return (char*) fs->fields[index].value; + return (char*) fs->fields[index].value.ptr; } int fds_get_index_by_name(fielddefset_t *fds, char *name) @@ -143,7 +151,7 @@ void fs_free(fieldset_t *fs) for (int i=0; i < fs->len; i++) { field_t *f = &(fs->fields[i]); if (f->free_) { - free((void*) f->value); + free(f->value.ptr); } } free(fs); @@ -162,7 +170,7 @@ void fs_generate_fieldset_translation(translation_t *t, log_fatal("fieldset", "specified field (%s) not " "available in selected " "probe module.", req[i]); - } + } t->translation[t->len++] = l; } } diff --git a/src/fieldset.h b/src/fieldset.h index 66de892..4f288ce 100644 --- a/src/fieldset.h +++ b/src/fieldset.h @@ -36,13 +36,18 @@ typedef struct fielddef_set { int len; } fielddefset_t; +typedef union field_val { + void *ptr; + uint64_t num; +} field_val_t; + // the internal field type used by fieldset typedef struct field { const char *name; int type; int free_; size_t len; - uint64_t value; + union field_val value; } field_t; // data structure that is populated by the probe module diff --git a/src/filter.c b/src/filter.c new file mode 100644 index 0000000..83bee43 --- /dev/null +++ b/src/filter.c @@ -0,0 +1,99 @@ +#include "filter.h" +#include "state.h" +#include "lexer.h" +#include "parser.h" +#include "expression.h" +#include "../lib/logger.h" + +#include + +extern int yyparse(); + +node_t *zfilter; + +static int validate_node(node_t *node, fielddefset_t *fields) +{ + int index, found = 0; + if (node->type == OP) { + // These end up getting validated later + if (node->value.op == AND || node->value.op == OR) { + return 1; + } + // Comparison node (=, >, <, etc.) + // Validate that the field (left child) exists in the fieldset + for (index = 0; index < fields->len; index++) { + if (fields->fielddefs[index].name) { + if (strcmp(fields->fielddefs[index].name, + node->left_child->value.field.fieldname) == 0) { + node->left_child->value.field.index = index; + found = 1; + break; + } + } + } + if (!found) { + fprintf(stderr, "Field '%s' does not exist\n", + node->left_child->value.field.fieldname); + return 0; + } + // Fieldname is fine, match the type. + switch (node->right_child->type) { + case STRING: + if (strcmp(fields->fielddefs[index].type, "string") == 0) { + return 1; + } else { + fprintf(stderr, "Field '%s' is not of type 'string'\n", + fields->fielddefs[index].name); + return 0; + } + case INT: + if (strcmp(fields->fielddefs[index].type, "int") == 0) { + return 1; + } else { + fprintf(stderr, "Field '%s' is not of type 'int'\n", + fields->fielddefs[index].name); + return 0; + } + default: + return 0; + } + } else { + // All non-op nodes are valid + return 1; + } + // Didn't validate + return 0; + +} + +int parse_filter_string(char *filter) +{ + YY_BUFFER_STATE buffer_state = yy_scan_string(filter); + int status = yyparse(); + yy_delete_buffer(buffer_state); + if (status) { + // Error + log_error("zmap", "Unable to parse filter string: '%s'", filter); + return 0; + } + zconf.filter.expression = zfilter; + return 1; +} + +/* + * 0 Valid + * -1 Invalid Field Name + * -2 Type Mismatch + */ +int validate_filter(node_t *root, fielddefset_t *fields) +{ + int valid; + if (!root) { + return 1; + } + valid = validate_node(root, fields); + if (!valid) { + return 0; + } + return (validate_filter(root->left_child, fields) && validate_filter(root->right_child, fields)); +} diff --git a/src/filter.h b/src/filter.h new file mode 100644 index 0000000..f42af6d --- /dev/null +++ b/src/filter.h @@ -0,0 +1,15 @@ +#ifndef ZMAP_FILTER_H +#define ZMAP_FILTER_H + +#include "expression.h" +#include "fieldset.h" + +struct output_filter { + node_t *expression; +}; + +int parse_filter_string(char *filter); + +int validate_filter(node_t *root, fielddefset_t *fields); + +#endif /* ZMAP_FILTER_H */ \ No newline at end of file diff --git a/src/lexer.l b/src/lexer.l new file mode 100644 index 0000000..cd884fa --- /dev/null +++ b/src/lexer.l @@ -0,0 +1,26 @@ +%{ +#pragma GCC diagnostic ignored "-Wredundant-decls" +#include +#include "parser.h" + +%} + +%option noinput +%option nounput +%% +[0-9]+ yylval.int_literal = (uint64_t) atoll(yytext); return T_NUMBER; +\n /* Ignore end of line */ +[ \t]+ /* Ignore whitespace */ +!= return T_NOT_EQ; +>= return T_GT_EQ; +"<=" return T_LT_EQ; +&& return T_AND; +"||" return T_OR; += return '='; +">" return '>'; +"<" return '<'; +"(" return '('; +")" return ')'; +[a-zA-Z][a-zA-Z0-9]+ yylval.string_literal = strdup(yytext); return T_FIELD; + +%% \ No newline at end of file diff --git a/src/output_modules/module_csv.c b/src/output_modules/module_csv.c index 985be91..51c9278 100644 --- a/src/output_modules/module_csv.c +++ b/src/output_modules/module_csv.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "../../lib/logger.h" #include "../fieldset.h" @@ -79,11 +80,11 @@ int csv_process(fieldset_t *fs) fprintf(file, ", "); } if (f->type == FS_STRING) { - fprintf(file, "%s", (char*) f->value); + fprintf(file, "%s", (char*) f->value.ptr); } else if (f->type == FS_UINT64) { - fprintf(file, "%lu", (uint64_t) f->value); + fprintf(file, "%" PRIu64, (uint64_t) f->value.num); } else if (f->type == FS_BINARY) { - hex_encode(file, (unsigned char*) f->value, f->len); + hex_encode(file, (unsigned char*) f->value.ptr, f->len); } else if (f->type == FS_NULL) { // do nothing } else { @@ -102,5 +103,6 @@ output_module_t module_csv_file = { .update_interval = 0, .close = &csv_close, .process_ip = &csv_process, + .helptext = NULL }; diff --git a/src/output_modules/module_json.c b/src/output_modules/module_json.c index 096540c..bb0ed9e 100644 --- a/src/output_modules/module_json.c +++ b/src/output_modules/module_json.c @@ -149,13 +149,13 @@ int json_output_file_ip(fieldset_t *fs) field_t *f = &(fs->fields[i]); if (f->type == FS_STRING) { json_object_object_add(obj, f->name, - json_object_new_string((char*) f->value)); + json_object_new_string((char *) f->value.ptr)); } else if (f->type == FS_UINT64) { json_object_object_add(obj, f->name, - json_object_new_int((int) f->value)); + json_object_new_int((int) f->value.num)); } else if (f->type == FS_BINARY) { json_output_file_store_data(obj, - (const u_char*) f->value, f->len); + (const u_char*) f->value.ptr, f->len); } else if (f->type == FS_NULL) { // do nothing } else { @@ -188,5 +188,6 @@ output_module_t module_json_file = { .update_interval = 0, .close = &json_output_file_close, .process_ip = &json_output_file_ip, + .helptext = NULL }; diff --git a/src/output_modules/module_redis.c b/src/output_modules/module_redis.c index a6dc8f8..f766693 100644 --- a/src/output_modules/module_redis.c +++ b/src/output_modules/module_redis.c @@ -22,55 +22,60 @@ #define UNUSED __attribute__((unused)) -typedef struct scannable_t { - in_addr_t ip_address; - uint8_t source; -} scannable_t; - -#define QUEUE_NAME "zmap_results" #define BUFFER_SIZE 500 -#define SOURCE_ZMAP 0 -static scannable_t* buffer; +static uint32_t *buffer; static int buffer_fill = 0; +static char *queue_name = NULL; -int redismodule_init(UNUSED struct state_conf *conf) +static int redismodule_init(struct state_conf *conf, char **fields, int fieldlens) { - buffer = calloc(BUFFER_SIZE, sizeof(scannable_t)); + assert(fieldlens == 1); + buffer = calloc(BUFFER_SIZE, sizeof(uint32_t)); assert(buffer); buffer_fill = 0; - return redis_init(); + + if (conf->output_args) { + redisconf_t *rconf = redis_parse_connstr(conf->output_args); + if (rconf->type == T_TCP) { + log_info("redis-module", "{type: TCP, server: %s, " + "port: %u, list: %s}", rconf->server, + rconf->port, rconf->list_name); + } else { + log_info("redis-module", "{type: LOCAL, path: %s, " + "list: %s}", rconf->path, rconf->list_name); + } + queue_name = rconf->list_name; + } else { + queue_name = "zmap_output"; + } + return redis_init(conf->output_args); } -int redismodule_flush(void) +static int redismodule_flush(void) { - if (redis_lpush((char *)QUEUE_NAME, buffer, - buffer_fill, sizeof(scannable_t))) { + if (redis_lpush((char *)queue_name, buffer, + buffer_fill, sizeof(uint32_t))) { return EXIT_FAILURE; } buffer_fill = 0; return EXIT_SUCCESS; } -int redismodule_newip(ipaddr_n_t saddr, UNUSED ipaddr_n_t daddr, - UNUSED const char *response_type, int is_repeat, - UNUSED int in_cooldown, UNUSED const u_char *packet, UNUSED size_t len) +static int redismodule_process(fieldset_t *fs) { - if (!is_repeat) { - buffer[buffer_fill].ip_address = saddr; - buffer[buffer_fill].source = SOURCE_ZMAP; - - if (++buffer_fill == BUFFER_SIZE) { - if (redismodule_flush()) { - return EXIT_FAILURE; - } + field_t *f = &(fs->fields[0]); + buffer[buffer_fill] = (uint32_t) f->value.num; + if (++buffer_fill == BUFFER_SIZE) { + if (redismodule_flush()) { + return EXIT_FAILURE; } } return EXIT_SUCCESS; } -int redismodule_close(UNUSED struct state_conf* c, - UNUSED struct state_send* s, +static int redismodule_close(UNUSED struct state_conf* c, + UNUSED struct state_send* s, UNUSED struct state_recv* r) { if (redismodule_flush()) { @@ -83,13 +88,13 @@ int redismodule_close(UNUSED struct state_conf* c, } output_module_t module_redis = { - .name = "redis", + .name = "redis-packed", .init = &redismodule_init, .start = NULL, .update = NULL, .update_interval = 0, .close = &redismodule_close, - .success_ip = &redismodule_newip, - .other_ip = NULL + .process_ip = &redismodule_process, + .helptext = NULL }; diff --git a/src/output_modules/output_modules.c b/src/output_modules/output_modules.c index c0c5aed..60fd9f7 100644 --- a/src/output_modules/output_modules.c +++ b/src/output_modules/output_modules.c @@ -6,7 +6,6 @@ * of the License at http://www.apache.org/licenses/LICENSE-2.0 */ - #include #include @@ -25,7 +24,7 @@ extern output_module_t module_json_file; output_module_t* output_modules[] = { &module_csv_file, #ifdef REDIS - //&module_redis, + &module_redis, #endif #ifdef JSON &module_json_file @@ -33,8 +32,6 @@ output_module_t* output_modules[] = { // ADD YOUR MODULE HERE }; - - output_module_t* get_output_module_by_name(const char* name) { int num_modules = (int) (sizeof(output_modules)/sizeof(output_modules[0])); diff --git a/src/output_modules/output_modules.h b/src/output_modules/output_modules.h index 8da6399..8b236a4 100644 --- a/src/output_modules/output_modules.h +++ b/src/output_modules/output_modules.h @@ -30,9 +30,9 @@ typedef struct output_module { output_update_cb update; output_update_cb close; output_packet_cb process_ip; + const char *helptext; } output_module_t; - output_module_t* get_output_module_by_name(const char*); void print_output_modules(void); diff --git a/src/parser.y b/src/parser.y new file mode 100644 index 0000000..6c44e17 --- /dev/null +++ b/src/parser.y @@ -0,0 +1,144 @@ +%{ +#include +#include +#include "expression.h" +#include "lexer.h" +#include "filter.h" + +void yyerror(const char *str) +{ + fprintf(stderr,"Parse error: %s\n",str); +} + +int yywrap() +{ + return 1; +} + +extern node_t *zfilter; + +%} + +%union { + int int_literal; + char *string_literal; + struct node *expr; +} + +%token '(' ')' T_AND T_OR +%token T_NUMBER +%token T_FIELD +%token T_NOT_EQ T_GT_EQ '>' '<' '=' T_LT_EQ + +%left T_OR +%left T_AND + +%type filter +%type number_filter +%type string_filter +%type filter_expr + + +%% + +expression: filter_expr + { + zfilter = $1; + } + + +filter_expr: + filter_expr T_OR filter_expr + { + $$ = make_op_node(OR); + $$->left_child = $1; + $$->right_child = $3; + } + | filter_expr T_AND filter_expr + { + $$ = make_op_node(AND); + $$->left_child = $1; + $$->right_child = $3; + } + | '(' filter_expr ')' + { + $$ = $2; + } + | filter + { + $$ = $1; + } + ; + +filter: number_filter + { + $$ = $1; + } + | string_filter + { + $$ = $1; + } + ; + +number_filter: T_FIELD '=' T_NUMBER + { + $$ = make_op_node(EQ); + $$->left_child = make_field_node($1); + $$->right_child = make_int_node($3); + } + | + T_FIELD '>' T_NUMBER + { + $$ = make_op_node(GT); + $$->left_child = make_field_node($1); + $$->right_child = make_int_node($3); + } + | + T_FIELD '<' T_NUMBER + { + $$ = make_op_node(LT); + $$->left_child = make_field_node($1); + $$->right_child = make_int_node($3); + } + | + T_FIELD T_NOT_EQ T_NUMBER + { + $$ = make_op_node(NEQ); + $$->left_child = make_field_node($1); + $$->right_child = make_int_node($3); + } + | + T_FIELD T_GT_EQ T_NUMBER + { + $$ = make_op_node(GT_EQ); + $$->left_child = make_field_node($1); + $$->right_child = make_int_node($3); + } + | + T_FIELD T_LT_EQ T_NUMBER + { + $$ = make_op_node(LT_EQ); + $$->left_child = make_field_node($1); + $$->right_child = make_int_node($3); + } + ; + +string_filter: + T_FIELD '=' T_FIELD + { + $$ = make_op_node(EQ); + $$->left_child = make_field_node($1); + $$->right_child = make_string_node($3); + } + | + T_FIELD T_NOT_EQ T_FIELD + { + $$ = make_op_node(NEQ); + $$->left_child = make_field_node($1); + $$->right_child = make_string_node($3); + } + ; + +%% + + diff --git a/src/recv.c b/src/recv.c index b55b582..f8c24dd 100644 --- a/src/recv.c +++ b/src/recv.c @@ -35,10 +35,12 @@ #include #include "../lib/logger.h" +#include "../lib/pbm.h" #include "state.h" #include "validate.h" #include "fieldset.h" +#include "expression.h" #include "probe_modules/probe_modules.h" #include "output_modules/output_modules.h" @@ -49,20 +51,7 @@ static uint32_t num_src_ports; static pcap_t *pc = NULL; // bitmap of observed IP addresses -static uint64_t *ip_seen = NULL; -static const int IP_SEEN_SIZE = 0x4000000; // == 2^32/64 - -// check if we've received a response from this address previously -static inline int check_ip(uint32_t ip) -{ - return (ip_seen[ip >> 6] >> (ip & 0x3F)) & 1; -} - -// set that we've received a response from the address -static inline void set_ip(uint32_t ip) -{ - ip_seen[ip >> 6] |= (uint64_t)1 << (ip & 0x3F); -} +static uint64_t **seen = NULL; static u_char fake_eth_hdr[65535]; @@ -120,7 +109,7 @@ void packet_cb(u_char __attribute__((__unused__)) *user, return; } - int is_repeat = check_ip(src_ip); + int is_repeat = pbm_check(seen, ntohl(src_ip)); fieldset_t *fs = fs_new_fieldset(); fs_add_ip_fields(fs, ip_hdr); @@ -150,7 +139,7 @@ void packet_cb(u_char __attribute__((__unused__)) *user, zrecv.success_total++; if (!is_repeat) { zrecv.success_unique++; - set_ip(src_ip); + pbm_set(seen, ntohl(src_ip)); } if (zsend.complete) { zrecv.cooldown_total++; @@ -170,6 +159,9 @@ void packet_cb(u_char __attribute__((__unused__)) *user, if (is_repeat && zconf.filter_duplicates) { goto cleanup; } + if (!evaluate_expression(zconf.filter.expression, fs)) { + goto cleanup; + } o = translate_fieldset(fs, &zconf.fsconf.translation); if (zconf.output_module && zconf.output_module->process_ip) { zconf.output_module->process_ip(o); @@ -205,10 +197,6 @@ int recv_run(pthread_mutex_t *recv_ready_mutex) { log_debug("recv", "thread started"); num_src_ports = zconf.source_port_last - zconf.source_port_first + 1; - ip_seen = calloc(IP_SEEN_SIZE, sizeof(uint64_t)); - if (!ip_seen) { - log_fatal("recv", "could not allocate address bitmap"); - } log_debug("recv", "using dev %s", zconf.iface); if (!zconf.dryrun) { char errbuf[PCAP_ERRBUF_SIZE]; @@ -235,6 +223,8 @@ int recv_run(pthread_mutex_t *recv_ready_mutex) memset(fake_eth_hdr, 0, sizeof(fake_eth_hdr)); eth->h_proto = htons(ETH_P_IP); } + // initialize paged bitmap + seen = pbm_init(); log_debug("recv", "receiver ready"); if (zconf.filter_duplicates) { log_debug("recv", "duplicate responses will be excluded from output"); diff --git a/src/state.h b/src/state.h index ef8fe45..1fd7c1e 100644 --- a/src/state.h +++ b/src/state.h @@ -25,6 +25,7 @@ #include "types.h" #include "fieldset.h" +#include "filter.h" #ifndef STATE_H #define STATE_H @@ -87,8 +88,11 @@ struct state_conf { char *output_filename; char *blacklist_filename; char *whitelist_filename; + char **destination_cidrs; + int destination_cidrs_len; char *raw_output_fields; char **output_fields; + struct output_filter filter; struct fieldset_conf fsconf; int output_fields_len; int dryrun; diff --git a/src/zmap.c b/src/zmap.c index 8c10d6f..d6865f5 100644 --- a/src/zmap.c +++ b/src/zmap.c @@ -40,6 +40,7 @@ #include "state.h" #include "monitor.h" #include "get_gateway.h" +#include "filter.h" #include "output_modules/output_modules.h" #include "probe_modules/probe_modules.h" @@ -199,8 +200,6 @@ static void summary(void) static void start_zmap(void) { log_info("zmap", "started"); - - // finish setting up configuration if (zconf.iface == NULL) { char errbuf[PCAP_ERRBUF_SIZE]; char *iface = pcap_lookupdev(errbuf); @@ -242,7 +241,6 @@ static void start_zmap(void) zconf.gw_mac[0], zconf.gw_mac[1], zconf.gw_mac[2], zconf.gw_mac[3], zconf.gw_mac[4], zconf.gw_mac[5]); } - // initialization if (zconf.output_module && zconf.output_module->init) { zconf.output_module->init(&zconf, zconf.output_fields, @@ -397,9 +395,69 @@ int main(int argc, char *argv[]) zconf.log_level = args.verbosity_arg; log_init(stderr, zconf.log_level); log_trace("zmap", "zmap main thread started"); - + // parse the provided probe and output module s.t. that we can support + // other command-line helpers (e.g. probe help) + if (!args.output_module_given) { + zconf.output_module = get_output_module_by_name("csv"); + zconf.raw_output_fields = (char*) "saddr"; + zconf.filter_duplicates = 1; + zconf.filter_unsuccessful = 1; + } else if (!strcmp(args.output_module_arg, "simple_file")) { + log_warn("zmap", "the simple_file output interface has been deprecated and " + "will be removed in the future. Users should use the csv " + "output module. Newer scan options such as output-fields " + "are not supported with this output module."); + zconf.output_module = get_output_module_by_name("csv"); + zconf.raw_output_fields = (char*) "saddr"; + zconf.filter_duplicates = 1; + zconf.filter_unsuccessful = 1; + } else if (!strcmp(args.output_module_arg, "extended_file")) { + log_warn("zmap", "the extended_file output interface has been deprecated and " + "will be removed in the future. Users should use the csv " + "output module. Newer scan options such as output-fields " + "are not supported with this output module."); + zconf.output_module = get_output_module_by_name("csv"); + zconf.raw_output_fields = (char*) "classification, saddr, " + "daddr, sport, dport, " + "seqnum, acknum, cooldown, " + "repeat, timestamp-str"; + zconf.filter_duplicates = 0; + } else if (!strcmp(args.output_module_arg, "redis")) { + log_warn("zmap", "the redis output interface has been deprecated and " + "will be removed in the future. Users should " + "either use redis-packed or redis-json in the " + "future."); + zconf.output_module = get_output_module_by_name("redis-packed"); + zconf.raw_output_fields = (char*) "saddr"; + zconf.filter_duplicates = 1; + } else { + zconf.output_module = get_output_module_by_name(args.output_module_arg); + if (!zconf.output_module) { + fprintf(stderr, "%s: specified output module (%s) does not exist\n", + CMDLINE_PARSER_PACKAGE, args.output_module_arg); + exit(EXIT_FAILURE); + } + } + zconf.probe_module = get_probe_module_by_name(args.probe_module_arg); + if (!zconf.probe_module) { + fprintf(stderr, "%s: specified probe module (%s) does not exist\n", + CMDLINE_PARSER_PACKAGE, args.probe_module_arg); + exit(EXIT_FAILURE); + } if (args.help_given) { cmdline_parser_print_help(); + printf("\nselected probe-module (%s) help\n", zconf.probe_module->name); + if (zconf.probe_module->helptext) { + printf("%s\n", zconf.probe_module->helptext); + } else { + printf("no help text available\n"); + } + printf("\nselected output-module (%s) help\n", zconf.output_module->name); + if (zconf.output_module->helptext) { + printf("%s\n", zconf.output_module->helptext); + } else { + printf("no help text available\n"); + } exit(EXIT_SUCCESS); } if (args.version_given) { @@ -430,48 +488,7 @@ int main(int argc, char *argv[]) if (cmdline_parser_required(&args, CMDLINE_PARSER_PACKAGE) != 0) { exit(EXIT_FAILURE); } - // parse the provided probe and output module s.t. that we can support - // other command-line helpers (e.g. probe help) - if (!args.output_module_given) { - zconf.output_module = get_output_module_by_name("csv"); - zconf.raw_output_fields = (char*) "saddr"; - zconf.filter_duplicates = 1; - zconf.filter_unsuccessful = 1; - } else if (!strcmp(args.output_module_arg, "simple_file")) { - log_warn("zmap", "the simple_file output interface has been deprecated and " - "will be removed in the future. Users should use the csv " - "output module. Newer scan options such as output-fields " - "are not supported with this output module."); - zconf.output_module = get_output_module_by_name("csv"); - zconf.raw_output_fields = (char*) "saddr"; - zconf.filter_duplicates = 1; - zconf.filter_unsuccessful = 1; - } else if (!strcmp(args.output_module_arg, "extended_file")) { - log_warn("zmap", "the extended_file output interface has been deprecated and " - "will be removed in the future. Users should use the csv " - "output module. Newer scan options such as output-fields " - "are not supported with this output module."); - zconf.output_module = get_output_module_by_name("csv"); - zconf.raw_output_fields = (char*) "classification, saddr, " - "daddr, sport, dport, " - "seqnum, acknum, cooldown, " - "repeat, timestamp-str"; - zconf.filter_duplicates = 0; - } else { - zconf.output_module = get_output_module_by_name(args.output_module_arg); - if (!zconf.output_module) { - fprintf(stderr, "%s: specified output module (%s) does not exist\n", - CMDLINE_PARSER_PACKAGE, args.output_module_arg); - exit(EXIT_FAILURE); - } - } - zconf.probe_module = get_probe_module_by_name(args.probe_module_arg); - if (!zconf.probe_module) { - fprintf(stderr, "%s: specified probe module (%s) does not exist\n", - CMDLINE_PARSER_PACKAGE, args.probe_module_arg); - exit(EXIT_FAILURE); - } - + // now that we know the probe module, let's find what it supports memset(&zconf.fsconf, 0, sizeof(struct fieldset_conf)); // the set of fields made available to a user is constructed @@ -523,6 +540,19 @@ int main(int argc, char *argv[]) &zconf.fsconf.defs, zconf.output_fields, zconf.output_fields_len); + // Parse and validate the output filter, if any + if (args.output_filter_arg) { + // Run it through yyparse to build the expression tree + if (!parse_filter_string(args.output_filter_arg)) { + log_fatal("zmap", "Unable to parse filter expression"); + } + + // Check the fields used against the fieldset in use + if (!validate_filter(zconf.filter.expression, &zconf.fsconf.defs)) { + log_fatal("zmap", "Invalid filter"); + } + } + SET_BOOL(zconf.dryrun, dryrun); SET_BOOL(zconf.quiet, quiet); SET_BOOL(zconf.summary, summary); @@ -530,7 +560,6 @@ int main(int argc, char *argv[]) zconf.senders = args.sender_threads_arg; SET_IF_GIVEN(zconf.output_filename, output_file); SET_IF_GIVEN(zconf.blacklist_filename, blacklist_file); - SET_IF_GIVEN(zconf.whitelist_filename, whitelist_file); SET_IF_GIVEN(zconf.probe_args, probe_args); SET_IF_GIVEN(zconf.output_args, output_args); SET_IF_GIVEN(zconf.iface, interface); @@ -538,8 +567,21 @@ int main(int argc, char *argv[]) SET_IF_GIVEN(zconf.max_results, max_results); SET_IF_GIVEN(zconf.rate, rate); SET_IF_GIVEN(zconf.packet_streams, probes); - + // find if zmap wants any specific cidrs scanned instead + // of the entire Internet + zconf.destination_cidrs = args.inputs; + zconf.destination_cidrs_len = args.inputs_num; + if (zconf.destination_cidrs && zconf.blacklist_filename + && !strcmp(zconf.blacklist_filename, "/etc/zmap/blacklist.conf")) { + log_warn("blacklist", "ZMap is currently using the default blacklist located " + "at /etc/zmap/blacklist.conf. By default, this blacklist excludes locally " + "scoped networks (e.g. 10.0.0.0/8, 127.0.0.1/8, and 192.168.0.0/16). If you are" + " trying to scan local networks, you can change the default blacklist by " + "editing the default ZMap configuration at /etc/zmap/zmap.conf."); + } + SET_IF_GIVEN(zconf.whitelist_filename, whitelist_file); + if (zconf.probe_module->port_args) { if (args.source_port_given) { char *dash = strchr(args.source_port_arg, '-'); diff --git a/src/zopt.c b/src/zopt.c deleted file mode 100644 index 3210e44..0000000 --- a/src/zopt.c +++ /dev/null @@ -1,1399 +0,0 @@ -/* - File autogenerated by gengetopt version 2.22.5 - generated with the following command: - gengetopt -C --no-help --no-version -i zopt.ggo -F zopt - - The developers of gengetopt consider the fixed text that goes in all - gengetopt output files to be in the public domain: - we make no copyright claims on it. -*/ - -/* If we use autoconf. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include - -#ifndef FIX_UNUSED -#define FIX_UNUSED(X) (void) (X) /* avoid warnings for unused params */ -#endif - -#include - -#include "zopt.h" - -const char *gengetopt_args_info_purpose = "A fast Internet-wide scanner."; - -const char *gengetopt_args_info_usage = "Usage: zmap [OPTIONS]..."; - -const char *gengetopt_args_info_description = ""; - -const char *gengetopt_args_info_help[] = { - "Basic arguments:", - " -p, --target-port=port TCP port number to scan (for SYN scans)", - " -o, --output-file=name Output file", - " -b, --blacklist-file=path File of subnets to exclude, in CIDR notation, \n e.g. 192.168.0.0/16", - " -w, --whitelist-file=path File of subnets to constrain scan to, in CIDR \n notation, e.g. 192.168.0.0/16", - " -f, --output-fields=fields Fields that should be output in result set", - "\nScan options:", - " -n, --max-targets=n Cap number of targets to probe (as a number or \n a percentage of the address space)", - " -N, --max-results=n Cap number of results to return", - " -t, --max-runtime=ses Cap length of time for sending packets", - " -r, --rate=pps Set send rate in packets/sec", - " -B, --bandwidth=bps Set send rate in bits/second (supports suffixes \n G, M and K)", - " -c, --cooldown-time=secs How long to continue receiving after sending \n last probe (default=`8')", - " -e, --seed=n Seed used to select address permutation", - " -T, --sender-threads=n Threads used to send packets (default=`1')", - " -P, --probes=n Number of probes to send to each IP \n (default=`1')", - " -d, --dryrun Don't actually send packets", - "\nNetwork options:", - " -s, --source-port=port|range Source port(s) for scan packets", - " -S, --source-ip=ip|range Source address(es) for scan packets", - " -G, --gateway-mac=addr Specify gateway MAC address", - " -i, --interface=name Specify network interface to use", - " -X, --vpn Sends IP packets instead of Ethernet (for VPNs)", - "\nAdvanced options:", - " -M, --probe-module=name Select probe module (default=`tcp_synscan')", - " -O, --output-module=name Select output module (default=`simple_file')", - " --probe-args=args Arguments to pass to probe module", - " --output-args=args Arguments to pass to output module", - " --list-output-modules List available output modules", - " --list-probe-modules List available probe modules", - " --list-output-fields List all fields that can be output by selected \n probe module", - "\nAdditional options:", - " -C, --config=filename Read a configuration file, which can specify \n any of these options \n (default=`/etc/zmap/zmap.conf')", - " -q, --quiet Do not print status updates", - " -g, --summary Print configuration and summary at end of scan", - " -v, --verbosity=n Level of log detail (0-5) (default=`3')", - " -h, --help Print help and exit", - " -V, --version Print version and exit", - "\nExamples:\n zmap -p 443 (scans the whole Internet for hosts with port 443 open)\n zmap -N 5 -B 10M -p 80 -o - (find 5 HTTP servers, scanning at 10 Mb/s)", - 0 -}; - -typedef enum {ARG_NO - , ARG_STRING - , ARG_INT -} cmdline_parser_arg_type; - -static -void clear_given (struct gengetopt_args_info *args_info); -static -void clear_args (struct gengetopt_args_info *args_info); - -static int -cmdline_parser_internal (int argc, char **argv, struct gengetopt_args_info *args_info, - struct cmdline_parser_params *params, const char *additional_error); - -struct line_list -{ - char * string_arg; - struct line_list * next; -}; - -static struct line_list *cmd_line_list = 0; -static struct line_list *cmd_line_list_tmp = 0; - -static void -free_cmd_list(void) -{ - /* free the list of a previous call */ - if (cmd_line_list) - { - while (cmd_line_list) { - cmd_line_list_tmp = cmd_line_list; - cmd_line_list = cmd_line_list->next; - free (cmd_line_list_tmp->string_arg); - free (cmd_line_list_tmp); - } - } -} - - -static char * -gengetopt_strdup (const char *s); - -static -void clear_given (struct gengetopt_args_info *args_info) -{ - args_info->target_port_given = 0 ; - args_info->output_file_given = 0 ; - args_info->blacklist_file_given = 0 ; - args_info->whitelist_file_given = 0 ; - args_info->output_fields_given = 0 ; - args_info->max_targets_given = 0 ; - args_info->max_results_given = 0 ; - args_info->max_runtime_given = 0 ; - args_info->rate_given = 0 ; - args_info->bandwidth_given = 0 ; - args_info->cooldown_time_given = 0 ; - args_info->seed_given = 0 ; - args_info->sender_threads_given = 0 ; - args_info->probes_given = 0 ; - args_info->dryrun_given = 0 ; - args_info->source_port_given = 0 ; - args_info->source_ip_given = 0 ; - args_info->gateway_mac_given = 0 ; - args_info->interface_given = 0 ; - args_info->vpn_given = 0 ; - args_info->probe_module_given = 0 ; - args_info->output_module_given = 0 ; - args_info->probe_args_given = 0 ; - args_info->output_args_given = 0 ; - args_info->list_output_modules_given = 0 ; - args_info->list_probe_modules_given = 0 ; - args_info->list_output_fields_given = 0 ; - args_info->config_given = 0 ; - args_info->quiet_given = 0 ; - args_info->summary_given = 0 ; - args_info->verbosity_given = 0 ; - args_info->help_given = 0 ; - args_info->version_given = 0 ; -} - -static -void clear_args (struct gengetopt_args_info *args_info) -{ - FIX_UNUSED (args_info); - args_info->target_port_orig = NULL; - args_info->output_file_arg = NULL; - args_info->output_file_orig = NULL; - args_info->blacklist_file_arg = NULL; - args_info->blacklist_file_orig = NULL; - args_info->whitelist_file_arg = NULL; - args_info->whitelist_file_orig = NULL; - args_info->output_fields_arg = NULL; - args_info->output_fields_orig = NULL; - args_info->max_targets_arg = NULL; - args_info->max_targets_orig = NULL; - args_info->max_results_orig = NULL; - args_info->max_runtime_orig = NULL; - args_info->rate_orig = NULL; - args_info->bandwidth_arg = NULL; - args_info->bandwidth_orig = NULL; - args_info->cooldown_time_arg = 8; - args_info->cooldown_time_orig = NULL; - args_info->seed_orig = NULL; - args_info->sender_threads_arg = 1; - args_info->sender_threads_orig = NULL; - args_info->probes_arg = 1; - args_info->probes_orig = NULL; - args_info->source_port_arg = NULL; - args_info->source_port_orig = NULL; - args_info->source_ip_arg = NULL; - args_info->source_ip_orig = NULL; - args_info->gateway_mac_arg = NULL; - args_info->gateway_mac_orig = NULL; - args_info->interface_arg = NULL; - args_info->interface_orig = NULL; - args_info->probe_module_arg = gengetopt_strdup ("tcp_synscan"); - args_info->probe_module_orig = NULL; - args_info->output_module_arg = gengetopt_strdup ("simple_file"); - args_info->output_module_orig = NULL; - args_info->probe_args_arg = NULL; - args_info->probe_args_orig = NULL; - args_info->output_args_arg = NULL; - args_info->output_args_orig = NULL; - args_info->config_arg = gengetopt_strdup ("/etc/zmap/zmap.conf"); - args_info->config_orig = NULL; - args_info->verbosity_arg = 3; - args_info->verbosity_orig = NULL; - -} - -static -void init_args_info(struct gengetopt_args_info *args_info) -{ - - - args_info->target_port_help = gengetopt_args_info_help[1] ; - args_info->output_file_help = gengetopt_args_info_help[2] ; - args_info->blacklist_file_help = gengetopt_args_info_help[3] ; - args_info->whitelist_file_help = gengetopt_args_info_help[4] ; - args_info->output_fields_help = gengetopt_args_info_help[5] ; - args_info->max_targets_help = gengetopt_args_info_help[7] ; - args_info->max_results_help = gengetopt_args_info_help[8] ; - args_info->max_runtime_help = gengetopt_args_info_help[9] ; - args_info->rate_help = gengetopt_args_info_help[10] ; - args_info->bandwidth_help = gengetopt_args_info_help[11] ; - args_info->cooldown_time_help = gengetopt_args_info_help[12] ; - args_info->seed_help = gengetopt_args_info_help[13] ; - args_info->sender_threads_help = gengetopt_args_info_help[14] ; - args_info->probes_help = gengetopt_args_info_help[15] ; - args_info->dryrun_help = gengetopt_args_info_help[16] ; - args_info->source_port_help = gengetopt_args_info_help[18] ; - args_info->source_ip_help = gengetopt_args_info_help[19] ; - args_info->gateway_mac_help = gengetopt_args_info_help[20] ; - args_info->interface_help = gengetopt_args_info_help[21] ; - args_info->vpn_help = gengetopt_args_info_help[22] ; - args_info->probe_module_help = gengetopt_args_info_help[24] ; - args_info->output_module_help = gengetopt_args_info_help[25] ; - args_info->probe_args_help = gengetopt_args_info_help[26] ; - args_info->output_args_help = gengetopt_args_info_help[27] ; - args_info->list_output_modules_help = gengetopt_args_info_help[28] ; - args_info->list_probe_modules_help = gengetopt_args_info_help[29] ; - args_info->list_output_fields_help = gengetopt_args_info_help[30] ; - args_info->config_help = gengetopt_args_info_help[32] ; - args_info->quiet_help = gengetopt_args_info_help[33] ; - args_info->summary_help = gengetopt_args_info_help[34] ; - args_info->verbosity_help = gengetopt_args_info_help[35] ; - args_info->help_help = gengetopt_args_info_help[36] ; - args_info->version_help = gengetopt_args_info_help[37] ; - -} - -void -cmdline_parser_print_version (void) -{ - printf ("%s %s\n", - (strlen(CMDLINE_PARSER_PACKAGE_NAME) ? CMDLINE_PARSER_PACKAGE_NAME : CMDLINE_PARSER_PACKAGE), - CMDLINE_PARSER_VERSION); -} - -static void print_help_common(void) { - cmdline_parser_print_version (); - - if (strlen(gengetopt_args_info_purpose) > 0) - printf("\n%s\n", gengetopt_args_info_purpose); - - if (strlen(gengetopt_args_info_usage) > 0) - printf("\n%s\n", gengetopt_args_info_usage); - - printf("\n"); - - if (strlen(gengetopt_args_info_description) > 0) - printf("%s\n\n", gengetopt_args_info_description); -} - -void -cmdline_parser_print_help (void) -{ - int i = 0; - print_help_common(); - while (gengetopt_args_info_help[i]) - printf("%s\n", gengetopt_args_info_help[i++]); -} - -void -cmdline_parser_init (struct gengetopt_args_info *args_info) -{ - clear_given (args_info); - clear_args (args_info); - init_args_info (args_info); -} - -void -cmdline_parser_params_init(struct cmdline_parser_params *params) -{ - if (params) - { - params->override = 0; - params->initialize = 1; - params->check_required = 1; - params->check_ambiguity = 0; - params->print_errors = 1; - } -} - -struct cmdline_parser_params * -cmdline_parser_params_create(void) -{ - struct cmdline_parser_params *params = - (struct cmdline_parser_params *)malloc(sizeof(struct cmdline_parser_params)); - cmdline_parser_params_init(params); - return params; -} - -static void -free_string_field (char **s) -{ - if (*s) - { - free (*s); - *s = 0; - } -} - - -static void -cmdline_parser_release (struct gengetopt_args_info *args_info) -{ - - free_string_field (&(args_info->target_port_orig)); - free_string_field (&(args_info->output_file_arg)); - free_string_field (&(args_info->output_file_orig)); - free_string_field (&(args_info->blacklist_file_arg)); - free_string_field (&(args_info->blacklist_file_orig)); - free_string_field (&(args_info->whitelist_file_arg)); - free_string_field (&(args_info->whitelist_file_orig)); - free_string_field (&(args_info->output_fields_arg)); - free_string_field (&(args_info->output_fields_orig)); - free_string_field (&(args_info->max_targets_arg)); - free_string_field (&(args_info->max_targets_orig)); - free_string_field (&(args_info->max_results_orig)); - free_string_field (&(args_info->max_runtime_orig)); - free_string_field (&(args_info->rate_orig)); - free_string_field (&(args_info->bandwidth_arg)); - free_string_field (&(args_info->bandwidth_orig)); - free_string_field (&(args_info->cooldown_time_orig)); - free_string_field (&(args_info->seed_orig)); - free_string_field (&(args_info->sender_threads_orig)); - free_string_field (&(args_info->probes_orig)); - free_string_field (&(args_info->source_port_arg)); - free_string_field (&(args_info->source_port_orig)); - free_string_field (&(args_info->source_ip_arg)); - free_string_field (&(args_info->source_ip_orig)); - free_string_field (&(args_info->gateway_mac_arg)); - free_string_field (&(args_info->gateway_mac_orig)); - free_string_field (&(args_info->interface_arg)); - free_string_field (&(args_info->interface_orig)); - free_string_field (&(args_info->probe_module_arg)); - free_string_field (&(args_info->probe_module_orig)); - free_string_field (&(args_info->output_module_arg)); - free_string_field (&(args_info->output_module_orig)); - free_string_field (&(args_info->probe_args_arg)); - free_string_field (&(args_info->probe_args_orig)); - free_string_field (&(args_info->output_args_arg)); - free_string_field (&(args_info->output_args_orig)); - free_string_field (&(args_info->config_arg)); - free_string_field (&(args_info->config_orig)); - free_string_field (&(args_info->verbosity_orig)); - - - - clear_given (args_info); -} - - -static void -write_into_file(FILE *outfile, const char *opt, const char *arg, const char *values[]) -{ - FIX_UNUSED (values); - if (arg) { - fprintf(outfile, "%s=\"%s\"\n", opt, arg); - } else { - fprintf(outfile, "%s\n", opt); - } -} - - -int -cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info) -{ - int i = 0; - - if (!outfile) - { - fprintf (stderr, "%s: cannot dump options to stream\n", CMDLINE_PARSER_PACKAGE); - return EXIT_FAILURE; - } - - if (args_info->target_port_given) - write_into_file(outfile, "target-port", args_info->target_port_orig, 0); - if (args_info->output_file_given) - write_into_file(outfile, "output-file", args_info->output_file_orig, 0); - if (args_info->blacklist_file_given) - write_into_file(outfile, "blacklist-file", args_info->blacklist_file_orig, 0); - if (args_info->whitelist_file_given) - write_into_file(outfile, "whitelist-file", args_info->whitelist_file_orig, 0); - if (args_info->output_fields_given) - write_into_file(outfile, "output-fields", args_info->output_fields_orig, 0); - if (args_info->max_targets_given) - write_into_file(outfile, "max-targets", args_info->max_targets_orig, 0); - if (args_info->max_results_given) - write_into_file(outfile, "max-results", args_info->max_results_orig, 0); - if (args_info->max_runtime_given) - write_into_file(outfile, "max-runtime", args_info->max_runtime_orig, 0); - if (args_info->rate_given) - write_into_file(outfile, "rate", args_info->rate_orig, 0); - if (args_info->bandwidth_given) - write_into_file(outfile, "bandwidth", args_info->bandwidth_orig, 0); - if (args_info->cooldown_time_given) - write_into_file(outfile, "cooldown-time", args_info->cooldown_time_orig, 0); - if (args_info->seed_given) - write_into_file(outfile, "seed", args_info->seed_orig, 0); - if (args_info->sender_threads_given) - write_into_file(outfile, "sender-threads", args_info->sender_threads_orig, 0); - if (args_info->probes_given) - write_into_file(outfile, "probes", args_info->probes_orig, 0); - if (args_info->dryrun_given) - write_into_file(outfile, "dryrun", 0, 0 ); - if (args_info->source_port_given) - write_into_file(outfile, "source-port", args_info->source_port_orig, 0); - if (args_info->source_ip_given) - write_into_file(outfile, "source-ip", args_info->source_ip_orig, 0); - if (args_info->gateway_mac_given) - write_into_file(outfile, "gateway-mac", args_info->gateway_mac_orig, 0); - if (args_info->interface_given) - write_into_file(outfile, "interface", args_info->interface_orig, 0); - if (args_info->vpn_given) - write_into_file(outfile, "vpn", 0, 0 ); - if (args_info->probe_module_given) - write_into_file(outfile, "probe-module", args_info->probe_module_orig, 0); - if (args_info->output_module_given) - write_into_file(outfile, "output-module", args_info->output_module_orig, 0); - if (args_info->probe_args_given) - write_into_file(outfile, "probe-args", args_info->probe_args_orig, 0); - if (args_info->output_args_given) - write_into_file(outfile, "output-args", args_info->output_args_orig, 0); - if (args_info->list_output_modules_given) - write_into_file(outfile, "list-output-modules", 0, 0 ); - if (args_info->list_probe_modules_given) - write_into_file(outfile, "list-probe-modules", 0, 0 ); - if (args_info->list_output_fields_given) - write_into_file(outfile, "list-output-fields", 0, 0 ); - if (args_info->config_given) - write_into_file(outfile, "config", args_info->config_orig, 0); - if (args_info->quiet_given) - write_into_file(outfile, "quiet", 0, 0 ); - if (args_info->summary_given) - write_into_file(outfile, "summary", 0, 0 ); - if (args_info->verbosity_given) - write_into_file(outfile, "verbosity", args_info->verbosity_orig, 0); - if (args_info->help_given) - write_into_file(outfile, "help", 0, 0 ); - if (args_info->version_given) - write_into_file(outfile, "version", 0, 0 ); - - - i = EXIT_SUCCESS; - return i; -} - -int -cmdline_parser_file_save(const char *filename, struct gengetopt_args_info *args_info) -{ - FILE *outfile; - int i = 0; - - outfile = fopen(filename, "w"); - - if (!outfile) - { - fprintf (stderr, "%s: cannot open file for writing: %s\n", CMDLINE_PARSER_PACKAGE, filename); - return EXIT_FAILURE; - } - - i = cmdline_parser_dump(outfile, args_info); - fclose (outfile); - - return i; -} - -void -cmdline_parser_free (struct gengetopt_args_info *args_info) -{ - cmdline_parser_release (args_info); -} - -/** @brief replacement of strdup, which is not standard */ -char * -gengetopt_strdup (const char *s) -{ - char *result = 0; - if (!s) - return result; - - result = (char*)malloc(strlen(s) + 1); - if (result == (char*)0) - return (char*)0; - strcpy(result, s); - return result; -} - -int -cmdline_parser (int argc, char **argv, struct gengetopt_args_info *args_info) -{ - return cmdline_parser2 (argc, argv, args_info, 0, 1, 1); -} - -int -cmdline_parser_ext (int argc, char **argv, struct gengetopt_args_info *args_info, - struct cmdline_parser_params *params) -{ - int result; - result = cmdline_parser_internal (argc, argv, args_info, params, 0); - - if (result == EXIT_FAILURE) - { - cmdline_parser_free (args_info); - exit (EXIT_FAILURE); - } - - return result; -} - -int -cmdline_parser2 (int argc, char **argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required) -{ - int result; - struct cmdline_parser_params params; - - params.override = override; - params.initialize = initialize; - params.check_required = check_required; - params.check_ambiguity = 0; - params.print_errors = 1; - - result = cmdline_parser_internal (argc, argv, args_info, ¶ms, 0); - - if (result == EXIT_FAILURE) - { - cmdline_parser_free (args_info); - exit (EXIT_FAILURE); - } - - return result; -} - -int -cmdline_parser_required (struct gengetopt_args_info *args_info, const char *prog_name) -{ - FIX_UNUSED (args_info); - FIX_UNUSED (prog_name); - return EXIT_SUCCESS; -} - - -static char *package_name = 0; - -/** - * @brief updates an option - * @param field the generic pointer to the field to update - * @param orig_field the pointer to the orig field - * @param field_given the pointer to the number of occurrence of this option - * @param prev_given the pointer to the number of occurrence already seen - * @param value the argument for this option (if null no arg was specified) - * @param possible_values the possible values for this option (if specified) - * @param default_value the default value (in case the option only accepts fixed values) - * @param arg_type the type of this option - * @param check_ambiguity @see cmdline_parser_params.check_ambiguity - * @param override @see cmdline_parser_params.override - * @param no_free whether to free a possible previous value - * @param multiple_option whether this is a multiple option - * @param long_opt the corresponding long option - * @param short_opt the corresponding short option (or '-' if none) - * @param additional_error possible further error specification - */ -static -int update_arg(void *field, char **orig_field, - unsigned int *field_given, unsigned int *prev_given, - char *value, const char *possible_values[], - const char *default_value, - cmdline_parser_arg_type arg_type, - int check_ambiguity, int override, - int no_free, int multiple_option, - const char *long_opt, char short_opt, - const char *additional_error) -{ - char *stop_char = 0; - const char *val = value; - int found; - char **string_field; - FIX_UNUSED (field); - - stop_char = 0; - found = 0; - - if (!multiple_option && prev_given && (*prev_given || (check_ambiguity && *field_given))) - { - if (short_opt != '-') - fprintf (stderr, "%s: `--%s' (`-%c') option given more than once%s\n", - package_name, long_opt, short_opt, - (additional_error ? additional_error : "")); - else - fprintf (stderr, "%s: `--%s' option given more than once%s\n", - package_name, long_opt, - (additional_error ? additional_error : "")); - return 1; /* failure */ - } - - FIX_UNUSED (default_value); - - if (field_given && *field_given && ! override) - return 0; - if (prev_given) - (*prev_given)++; - if (field_given) - (*field_given)++; - if (possible_values) - val = possible_values[found]; - - switch(arg_type) { - case ARG_INT: - if (val) *((int *)field) = strtol (val, &stop_char, 0); - break; - case ARG_STRING: - if (val) { - string_field = (char **)field; - if (!no_free && *string_field) - free (*string_field); /* free previous string */ - *string_field = gengetopt_strdup (val); - } - break; - default: - break; - }; - - /* check numeric conversion */ - switch(arg_type) { - case ARG_INT: - if (val && !(stop_char && *stop_char == '\0')) { - fprintf(stderr, "%s: invalid numeric value: %s\n", package_name, val); - return 1; /* failure */ - } - break; - default: - ; - }; - - /* store the original value */ - switch(arg_type) { - case ARG_NO: - break; - default: - if (value && orig_field) { - if (no_free) { - *orig_field = value; - } else { - if (*orig_field) - free (*orig_field); /* free previous string */ - *orig_field = gengetopt_strdup (value); - } - } - }; - - return 0; /* OK */ -} - - -int -cmdline_parser_internal ( - int argc, char **argv, struct gengetopt_args_info *args_info, - struct cmdline_parser_params *params, const char *additional_error) -{ - int c; /* Character of the parsed option. */ - - int error = 0; - struct gengetopt_args_info local_args_info; - - int override; - int initialize; - int check_required; - int check_ambiguity; - - package_name = argv[0]; - - override = params->override; - initialize = params->initialize; - check_required = params->check_required; - check_ambiguity = params->check_ambiguity; - - if (initialize) - cmdline_parser_init (args_info); - - cmdline_parser_init (&local_args_info); - - optarg = 0; - optind = 0; - opterr = params->print_errors; - optopt = '?'; - - while (1) - { - int option_index = 0; - - static struct option long_options[] = { - { "target-port", 1, NULL, 'p' }, - { "output-file", 1, NULL, 'o' }, - { "blacklist-file", 1, NULL, 'b' }, - { "whitelist-file", 1, NULL, 'w' }, - { "output-fields", 1, NULL, 'f' }, - { "max-targets", 1, NULL, 'n' }, - { "max-results", 1, NULL, 'N' }, - { "max-runtime", 1, NULL, 't' }, - { "rate", 1, NULL, 'r' }, - { "bandwidth", 1, NULL, 'B' }, - { "cooldown-time", 1, NULL, 'c' }, - { "seed", 1, NULL, 'e' }, - { "sender-threads", 1, NULL, 'T' }, - { "probes", 1, NULL, 'P' }, - { "dryrun", 0, NULL, 'd' }, - { "source-port", 1, NULL, 's' }, - { "source-ip", 1, NULL, 'S' }, - { "gateway-mac", 1, NULL, 'G' }, - { "interface", 1, NULL, 'i' }, - { "vpn", 0, NULL, 'X' }, - { "probe-module", 1, NULL, 'M' }, - { "output-module", 1, NULL, 'O' }, - { "probe-args", 1, NULL, 0 }, - { "output-args", 1, NULL, 0 }, - { "list-output-modules", 0, NULL, 0 }, - { "list-probe-modules", 0, NULL, 0 }, - { "list-output-fields", 0, NULL, 0 }, - { "config", 1, NULL, 'C' }, - { "quiet", 0, NULL, 'q' }, - { "summary", 0, NULL, 'g' }, - { "verbosity", 1, NULL, 'v' }, - { "help", 0, NULL, 'h' }, - { "version", 0, NULL, 'V' }, - { 0, 0, 0, 0 } - }; - - c = getopt_long (argc, argv, "p:o:b:w:f:n:N:t:r:B:c:e:T:P:ds:S:G:i:XM:O:C:qgv:hV", long_options, &option_index); - - if (c == -1) break; /* Exit from `while (1)' loop. */ - - switch (c) - { - case 'p': /* TCP port number to scan (for SYN scans). */ - - - if (update_arg( (void *)&(args_info->target_port_arg), - &(args_info->target_port_orig), &(args_info->target_port_given), - &(local_args_info.target_port_given), optarg, 0, 0, ARG_INT, - check_ambiguity, override, 0, 0, - "target-port", 'p', - additional_error)) - goto failure; - - break; - case 'o': /* Output file. */ - - - if (update_arg( (void *)&(args_info->output_file_arg), - &(args_info->output_file_orig), &(args_info->output_file_given), - &(local_args_info.output_file_given), optarg, 0, 0, ARG_STRING, - check_ambiguity, override, 0, 0, - "output-file", 'o', - additional_error)) - goto failure; - - break; - case 'b': /* File of subnets to exclude, in CIDR notation, e.g. 192.168.0.0/16. */ - - - if (update_arg( (void *)&(args_info->blacklist_file_arg), - &(args_info->blacklist_file_orig), &(args_info->blacklist_file_given), - &(local_args_info.blacklist_file_given), optarg, 0, 0, ARG_STRING, - check_ambiguity, override, 0, 0, - "blacklist-file", 'b', - additional_error)) - goto failure; - - break; - case 'w': /* File of subnets to constrain scan to, in CIDR notation, e.g. 192.168.0.0/16. */ - - - if (update_arg( (void *)&(args_info->whitelist_file_arg), - &(args_info->whitelist_file_orig), &(args_info->whitelist_file_given), - &(local_args_info.whitelist_file_given), optarg, 0, 0, ARG_STRING, - check_ambiguity, override, 0, 0, - "whitelist-file", 'w', - additional_error)) - goto failure; - - break; - case 'f': /* Fields that should be output in result set. */ - - - if (update_arg( (void *)&(args_info->output_fields_arg), - &(args_info->output_fields_orig), &(args_info->output_fields_given), - &(local_args_info.output_fields_given), optarg, 0, 0, ARG_STRING, - check_ambiguity, override, 0, 0, - "output-fields", 'f', - additional_error)) - goto failure; - - break; - case 'n': /* Cap number of targets to probe (as a number or a percentage of the address space). */ - - - if (update_arg( (void *)&(args_info->max_targets_arg), - &(args_info->max_targets_orig), &(args_info->max_targets_given), - &(local_args_info.max_targets_given), optarg, 0, 0, ARG_STRING, - check_ambiguity, override, 0, 0, - "max-targets", 'n', - additional_error)) - goto failure; - - break; - case 'N': /* Cap number of results to return. */ - - - if (update_arg( (void *)&(args_info->max_results_arg), - &(args_info->max_results_orig), &(args_info->max_results_given), - &(local_args_info.max_results_given), optarg, 0, 0, ARG_INT, - check_ambiguity, override, 0, 0, - "max-results", 'N', - additional_error)) - goto failure; - - break; - case 't': /* Cap length of time for sending packets. */ - - - if (update_arg( (void *)&(args_info->max_runtime_arg), - &(args_info->max_runtime_orig), &(args_info->max_runtime_given), - &(local_args_info.max_runtime_given), optarg, 0, 0, ARG_INT, - check_ambiguity, override, 0, 0, - "max-runtime", 't', - additional_error)) - goto failure; - - break; - case 'r': /* Set send rate in packets/sec. */ - - - if (update_arg( (void *)&(args_info->rate_arg), - &(args_info->rate_orig), &(args_info->rate_given), - &(local_args_info.rate_given), optarg, 0, 0, ARG_INT, - check_ambiguity, override, 0, 0, - "rate", 'r', - additional_error)) - goto failure; - - break; - case 'B': /* Set send rate in bits/second (supports suffixes G, M and K). */ - - - if (update_arg( (void *)&(args_info->bandwidth_arg), - &(args_info->bandwidth_orig), &(args_info->bandwidth_given), - &(local_args_info.bandwidth_given), optarg, 0, 0, ARG_STRING, - check_ambiguity, override, 0, 0, - "bandwidth", 'B', - additional_error)) - goto failure; - - break; - case 'c': /* How long to continue receiving after sending last probe. */ - - - if (update_arg( (void *)&(args_info->cooldown_time_arg), - &(args_info->cooldown_time_orig), &(args_info->cooldown_time_given), - &(local_args_info.cooldown_time_given), optarg, 0, "8", ARG_INT, - check_ambiguity, override, 0, 0, - "cooldown-time", 'c', - additional_error)) - goto failure; - - break; - case 'e': /* Seed used to select address permutation. */ - - - if (update_arg( (void *)&(args_info->seed_arg), - &(args_info->seed_orig), &(args_info->seed_given), - &(local_args_info.seed_given), optarg, 0, 0, ARG_INT, - check_ambiguity, override, 0, 0, - "seed", 'e', - additional_error)) - goto failure; - - break; - case 'T': /* Threads used to send packets. */ - - - if (update_arg( (void *)&(args_info->sender_threads_arg), - &(args_info->sender_threads_orig), &(args_info->sender_threads_given), - &(local_args_info.sender_threads_given), optarg, 0, "1", ARG_INT, - check_ambiguity, override, 0, 0, - "sender-threads", 'T', - additional_error)) - goto failure; - - break; - case 'P': /* Number of probes to send to each IP. */ - - - if (update_arg( (void *)&(args_info->probes_arg), - &(args_info->probes_orig), &(args_info->probes_given), - &(local_args_info.probes_given), optarg, 0, "1", ARG_INT, - check_ambiguity, override, 0, 0, - "probes", 'P', - additional_error)) - goto failure; - - break; - case 'd': /* Don't actually send packets. */ - - - if (update_arg( 0 , - 0 , &(args_info->dryrun_given), - &(local_args_info.dryrun_given), optarg, 0, 0, ARG_NO, - check_ambiguity, override, 0, 0, - "dryrun", 'd', - additional_error)) - goto failure; - - break; - case 's': /* Source port(s) for scan packets. */ - - - if (update_arg( (void *)&(args_info->source_port_arg), - &(args_info->source_port_orig), &(args_info->source_port_given), - &(local_args_info.source_port_given), optarg, 0, 0, ARG_STRING, - check_ambiguity, override, 0, 0, - "source-port", 's', - additional_error)) - goto failure; - - break; - case 'S': /* Source address(es) for scan packets. */ - - - if (update_arg( (void *)&(args_info->source_ip_arg), - &(args_info->source_ip_orig), &(args_info->source_ip_given), - &(local_args_info.source_ip_given), optarg, 0, 0, ARG_STRING, - check_ambiguity, override, 0, 0, - "source-ip", 'S', - additional_error)) - goto failure; - - break; - case 'G': /* Specify gateway MAC address. */ - - - if (update_arg( (void *)&(args_info->gateway_mac_arg), - &(args_info->gateway_mac_orig), &(args_info->gateway_mac_given), - &(local_args_info.gateway_mac_given), optarg, 0, 0, ARG_STRING, - check_ambiguity, override, 0, 0, - "gateway-mac", 'G', - additional_error)) - goto failure; - - break; - case 'i': /* Specify network interface to use. */ - - - if (update_arg( (void *)&(args_info->interface_arg), - &(args_info->interface_orig), &(args_info->interface_given), - &(local_args_info.interface_given), optarg, 0, 0, ARG_STRING, - check_ambiguity, override, 0, 0, - "interface", 'i', - additional_error)) - goto failure; - - break; - case 'X': /* Sends IP packets instead of Ethernet (for VPNs). */ - - - if (update_arg( 0 , - 0 , &(args_info->vpn_given), - &(local_args_info.vpn_given), optarg, 0, 0, ARG_NO, - check_ambiguity, override, 0, 0, - "vpn", 'X', - additional_error)) - goto failure; - - break; - case 'M': /* Select probe module. */ - - - if (update_arg( (void *)&(args_info->probe_module_arg), - &(args_info->probe_module_orig), &(args_info->probe_module_given), - &(local_args_info.probe_module_given), optarg, 0, "tcp_synscan", ARG_STRING, - check_ambiguity, override, 0, 0, - "probe-module", 'M', - additional_error)) - goto failure; - - break; - case 'O': /* Select output module. */ - - - if (update_arg( (void *)&(args_info->output_module_arg), - &(args_info->output_module_orig), &(args_info->output_module_given), - &(local_args_info.output_module_given), optarg, 0, "simple_file", ARG_STRING, - check_ambiguity, override, 0, 0, - "output-module", 'O', - additional_error)) - goto failure; - - break; - case 'C': /* Read a configuration file, which can specify any of these options. */ - - - if (update_arg( (void *)&(args_info->config_arg), - &(args_info->config_orig), &(args_info->config_given), - &(local_args_info.config_given), optarg, 0, "/etc/zmap/zmap.conf", ARG_STRING, - check_ambiguity, override, 0, 0, - "config", 'C', - additional_error)) - goto failure; - - break; - case 'q': /* Do not print status updates. */ - - - if (update_arg( 0 , - 0 , &(args_info->quiet_given), - &(local_args_info.quiet_given), optarg, 0, 0, ARG_NO, - check_ambiguity, override, 0, 0, - "quiet", 'q', - additional_error)) - goto failure; - - break; - case 'g': /* Print configuration and summary at end of scan. */ - - - if (update_arg( 0 , - 0 , &(args_info->summary_given), - &(local_args_info.summary_given), optarg, 0, 0, ARG_NO, - check_ambiguity, override, 0, 0, - "summary", 'g', - additional_error)) - goto failure; - - break; - case 'v': /* Level of log detail (0-5). */ - - - if (update_arg( (void *)&(args_info->verbosity_arg), - &(args_info->verbosity_orig), &(args_info->verbosity_given), - &(local_args_info.verbosity_given), optarg, 0, "3", ARG_INT, - check_ambiguity, override, 0, 0, - "verbosity", 'v', - additional_error)) - goto failure; - - break; - case 'h': /* Print help and exit. */ - - - if (update_arg( 0 , - 0 , &(args_info->help_given), - &(local_args_info.help_given), optarg, 0, 0, ARG_NO, - check_ambiguity, override, 0, 0, - "help", 'h', - additional_error)) - goto failure; - - break; - case 'V': /* Print version and exit. */ - - - if (update_arg( 0 , - 0 , &(args_info->version_given), - &(local_args_info.version_given), optarg, 0, 0, ARG_NO, - check_ambiguity, override, 0, 0, - "version", 'V', - additional_error)) - goto failure; - - break; - - case 0: /* Long option with no short option */ - /* Arguments to pass to probe module. */ - if (strcmp (long_options[option_index].name, "probe-args") == 0) - { - - - if (update_arg( (void *)&(args_info->probe_args_arg), - &(args_info->probe_args_orig), &(args_info->probe_args_given), - &(local_args_info.probe_args_given), optarg, 0, 0, ARG_STRING, - check_ambiguity, override, 0, 0, - "probe-args", '-', - additional_error)) - goto failure; - - } - /* Arguments to pass to output module. */ - else if (strcmp (long_options[option_index].name, "output-args") == 0) - { - - - if (update_arg( (void *)&(args_info->output_args_arg), - &(args_info->output_args_orig), &(args_info->output_args_given), - &(local_args_info.output_args_given), optarg, 0, 0, ARG_STRING, - check_ambiguity, override, 0, 0, - "output-args", '-', - additional_error)) - goto failure; - - } - /* List available output modules. */ - else if (strcmp (long_options[option_index].name, "list-output-modules") == 0) - { - - - if (update_arg( 0 , - 0 , &(args_info->list_output_modules_given), - &(local_args_info.list_output_modules_given), optarg, 0, 0, ARG_NO, - check_ambiguity, override, 0, 0, - "list-output-modules", '-', - additional_error)) - goto failure; - - } - /* List available probe modules. */ - else if (strcmp (long_options[option_index].name, "list-probe-modules") == 0) - { - - - if (update_arg( 0 , - 0 , &(args_info->list_probe_modules_given), - &(local_args_info.list_probe_modules_given), optarg, 0, 0, ARG_NO, - check_ambiguity, override, 0, 0, - "list-probe-modules", '-', - additional_error)) - goto failure; - - } - /* List all fields that can be output by selected probe module. */ - else if (strcmp (long_options[option_index].name, "list-output-fields") == 0) - { - - - if (update_arg( 0 , - 0 , &(args_info->list_output_fields_given), - &(local_args_info.list_output_fields_given), optarg, 0, 0, ARG_NO, - check_ambiguity, override, 0, 0, - "list-output-fields", '-', - additional_error)) - goto failure; - - } - - break; - case '?': /* Invalid option. */ - /* `getopt_long' already printed an error message. */ - goto failure; - - default: /* bug: option not considered. */ - fprintf (stderr, "%s: option unknown: %c%s\n", CMDLINE_PARSER_PACKAGE, c, (additional_error ? additional_error : "")); - abort (); - } /* switch */ - } /* while */ - - - - - cmdline_parser_release (&local_args_info); - - if ( error ) - return (EXIT_FAILURE); - - return 0; - -failure: - - cmdline_parser_release (&local_args_info); - return (EXIT_FAILURE); -} - -#ifndef CONFIG_FILE_LINE_SIZE -#define CONFIG_FILE_LINE_SIZE 2048 -#endif -#define ADDITIONAL_ERROR " in configuration file " - -#define CONFIG_FILE_LINE_BUFFER_SIZE (CONFIG_FILE_LINE_SIZE+3) -/* 3 is for "--" and "=" */ - -static int -_cmdline_parser_configfile (const char *filename, int *my_argc) -{ - FILE* file; - char my_argv[CONFIG_FILE_LINE_BUFFER_SIZE+1]; - char linebuf[CONFIG_FILE_LINE_SIZE]; - int line_num = 0; - int result = 0, equal; - char *fopt, *farg; - char *str_index; - size_t len, next_token; - char delimiter; - - if ((file = fopen(filename, "r")) == 0) - { - fprintf (stderr, "%s: Error opening configuration file '%s'\n", - CMDLINE_PARSER_PACKAGE, filename); - return EXIT_FAILURE; - } - - while ((fgets(linebuf, CONFIG_FILE_LINE_SIZE, file)) != 0) - { - ++line_num; - my_argv[0] = '\0'; - len = strlen(linebuf); - if (len > (CONFIG_FILE_LINE_BUFFER_SIZE-1)) - { - fprintf (stderr, "%s:%s:%d: Line too long in configuration file\n", - CMDLINE_PARSER_PACKAGE, filename, line_num); - result = EXIT_FAILURE; - break; - } - - /* find first non-whitespace character in the line */ - next_token = strspn (linebuf, " \t\r\n"); - str_index = linebuf + next_token; - - if ( str_index[0] == '\0' || str_index[0] == '#') - continue; /* empty line or comment line is skipped */ - - fopt = str_index; - - /* truncate fopt at the end of the first non-valid character */ - next_token = strcspn (fopt, " \t\r\n="); - - if (fopt[next_token] == '\0') /* the line is over */ - { - farg = 0; - equal = 0; - goto noarg; - } - - /* remember if equal sign is present */ - equal = (fopt[next_token] == '='); - fopt[next_token++] = '\0'; - - /* advance pointers to the next token after the end of fopt */ - next_token += strspn (fopt + next_token, " \t\r\n"); - - /* check for the presence of equal sign, and if so, skip it */ - if ( !equal ) - if ((equal = (fopt[next_token] == '='))) - { - next_token++; - next_token += strspn (fopt + next_token, " \t\r\n"); - } - str_index += next_token; - - /* find argument */ - farg = str_index; - if ( farg[0] == '\"' || farg[0] == '\'' ) - { /* quoted argument */ - str_index = strchr (++farg, str_index[0] ); /* skip opening quote */ - if (! str_index) - { - fprintf - (stderr, - "%s:%s:%d: unterminated string in configuration file\n", - CMDLINE_PARSER_PACKAGE, filename, line_num); - result = EXIT_FAILURE; - break; - } - } - else - { /* read up the remaining part up to a delimiter */ - next_token = strcspn (farg, " \t\r\n#\'\""); - str_index += next_token; - } - - /* truncate farg at the delimiter and store it for further check */ - delimiter = *str_index, *str_index++ = '\0'; - - /* everything but comment is illegal at the end of line */ - if (delimiter != '\0' && delimiter != '#') - { - str_index += strspn(str_index, " \t\r\n"); - if (*str_index != '\0' && *str_index != '#') - { - fprintf - (stderr, - "%s:%s:%d: malformed string in configuration file\n", - CMDLINE_PARSER_PACKAGE, filename, line_num); - result = EXIT_FAILURE; - break; - } - } - - noarg: - if (!strcmp(fopt,"include")) { - if (farg && *farg) { - result = _cmdline_parser_configfile(farg, my_argc); - } else { - fprintf(stderr, "%s:%s:%d: include requires a filename argument.\n", - CMDLINE_PARSER_PACKAGE, filename, line_num); - } - continue; - } - len = strlen(fopt); - strcat (my_argv, len > 1 ? "--" : "-"); - strcat (my_argv, fopt); - if (len > 1 && ((farg && *farg) || equal)) - strcat (my_argv, "="); - if (farg && *farg) - strcat (my_argv, farg); - ++(*my_argc); - - cmd_line_list_tmp = (struct line_list *) malloc (sizeof (struct line_list)); - cmd_line_list_tmp->next = cmd_line_list; - cmd_line_list = cmd_line_list_tmp; - cmd_line_list->string_arg = gengetopt_strdup(my_argv); - } /* while */ - - if (file) - fclose(file); - return result; -} - -int -cmdline_parser_configfile ( - const char *filename, - struct gengetopt_args_info *args_info, - int override, int initialize, int check_required) -{ - struct cmdline_parser_params params; - - params.override = override; - params.initialize = initialize; - params.check_required = check_required; - params.check_ambiguity = 0; - params.print_errors = 1; - - return cmdline_parser_config_file (filename, args_info, ¶ms); -} - -int -cmdline_parser_config_file (const char *filename, - struct gengetopt_args_info *args_info, - struct cmdline_parser_params *params) -{ - int i, result; - int my_argc = 1; - char **my_argv_arg; - char *additional_error; - - /* store the program name */ - cmd_line_list_tmp = (struct line_list *) malloc (sizeof (struct line_list)); - cmd_line_list_tmp->next = cmd_line_list; - cmd_line_list = cmd_line_list_tmp; - cmd_line_list->string_arg = gengetopt_strdup (CMDLINE_PARSER_PACKAGE); - - result = _cmdline_parser_configfile(filename, &my_argc); - - if (result != EXIT_FAILURE) { - my_argv_arg = (char **) malloc((my_argc+1) * sizeof(char *)); - cmd_line_list_tmp = cmd_line_list; - - for (i = my_argc - 1; i >= 0; --i) { - my_argv_arg[i] = cmd_line_list_tmp->string_arg; - cmd_line_list_tmp = cmd_line_list_tmp->next; - } - - my_argv_arg[my_argc] = 0; - - additional_error = (char *)malloc(strlen(filename) + strlen(ADDITIONAL_ERROR) + 1); - strcpy (additional_error, ADDITIONAL_ERROR); - strcat (additional_error, filename); - result = - cmdline_parser_internal (my_argc, my_argv_arg, args_info, - params, - additional_error); - - free (additional_error); - free (my_argv_arg); - } - - free_cmd_list(); - if (result == EXIT_FAILURE) - { - cmdline_parser_free (args_info); - exit (EXIT_FAILURE); - } - - return result; -} diff --git a/src/zopt.ggo b/src/zopt.ggo index 71f0aef..d1852aa 100644 --- a/src/zopt.ggo +++ b/src/zopt.ggo @@ -94,6 +94,9 @@ option "probe-args" - "Arguments to pass to probe module" option "output-args" - "Arguments to pass to output module" typestr="args" optional string +option "output-filter" - "Read a file containing an output filter on the first line" + typestr="filename" + optional string option "list-output-modules" - "List available output modules" optional option "list-probe-modules" - "List available probe modules" @@ -122,5 +125,6 @@ option "version" V "Print version and exit" text "\nExamples:\n\ zmap -p 443 (scans the whole Internet for hosts with port 443 open)\n\ - zmap -N 5 -B 10M -p 80 -o - (find 5 HTTP servers, scanning at 10 Mb/s)" - + zmap -N 5 -B 10M -p 80 -o - (find 5 HTTP servers, scanning at 10 Mb/s)\n + zmap -p 80 10.0.0.0/8 192.168.0.0/16 -o (scan 10.0.0.0/8 and 192.168.0.0/16 on port 80)\n + zmap -p 80 192.168.1.2 192.168.1.3 (scan 192.168.1.2 and 192.168.1.3 on port 80)" diff --git a/src/zopt.h b/src/zopt.h deleted file mode 100644 index d5bbead..0000000 --- a/src/zopt.h +++ /dev/null @@ -1,306 +0,0 @@ -/** @file zopt.h - * @brief The header file for the command line option parser - * generated by GNU Gengetopt version 2.22.5 - * http://www.gnu.org/software/gengetopt. - * DO NOT modify this file, since it can be overwritten - * @author GNU Gengetopt by Lorenzo Bettini */ - -#ifndef ZOPT_H -#define ZOPT_H - -/* If we use autoconf. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include /* for FILE */ - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#ifndef CMDLINE_PARSER_PACKAGE -/** @brief the program name (used for printing errors) */ -#define CMDLINE_PARSER_PACKAGE "zmap" -#endif - -#ifndef CMDLINE_PARSER_PACKAGE_NAME -/** @brief the complete program name (used for help and version) */ -#define CMDLINE_PARSER_PACKAGE_NAME "zmap" -#endif - -#ifndef CMDLINE_PARSER_VERSION -/** @brief the program version */ -#define CMDLINE_PARSER_VERSION "1.0.0" -#endif - -/** @brief Where the command line options are stored */ -struct gengetopt_args_info -{ - int target_port_arg; /**< @brief TCP port number to scan (for SYN scans). */ - char * target_port_orig; /**< @brief TCP port number to scan (for SYN scans) original value given at command line. */ - const char *target_port_help; /**< @brief TCP port number to scan (for SYN scans) help description. */ - char * output_file_arg; /**< @brief Output file. */ - char * output_file_orig; /**< @brief Output file original value given at command line. */ - const char *output_file_help; /**< @brief Output file help description. */ - char * blacklist_file_arg; /**< @brief File of subnets to exclude, in CIDR notation, e.g. 192.168.0.0/16. */ - char * blacklist_file_orig; /**< @brief File of subnets to exclude, in CIDR notation, e.g. 192.168.0.0/16 original value given at command line. */ - const char *blacklist_file_help; /**< @brief File of subnets to exclude, in CIDR notation, e.g. 192.168.0.0/16 help description. */ - char * whitelist_file_arg; /**< @brief File of subnets to constrain scan to, in CIDR notation, e.g. 192.168.0.0/16. */ - char * whitelist_file_orig; /**< @brief File of subnets to constrain scan to, in CIDR notation, e.g. 192.168.0.0/16 original value given at command line. */ - const char *whitelist_file_help; /**< @brief File of subnets to constrain scan to, in CIDR notation, e.g. 192.168.0.0/16 help description. */ - char * output_fields_arg; /**< @brief Fields that should be output in result set. */ - char * output_fields_orig; /**< @brief Fields that should be output in result set original value given at command line. */ - const char *output_fields_help; /**< @brief Fields that should be output in result set help description. */ - char * max_targets_arg; /**< @brief Cap number of targets to probe (as a number or a percentage of the address space). */ - char * max_targets_orig; /**< @brief Cap number of targets to probe (as a number or a percentage of the address space) original value given at command line. */ - const char *max_targets_help; /**< @brief Cap number of targets to probe (as a number or a percentage of the address space) help description. */ - int max_results_arg; /**< @brief Cap number of results to return. */ - char * max_results_orig; /**< @brief Cap number of results to return original value given at command line. */ - const char *max_results_help; /**< @brief Cap number of results to return help description. */ - int max_runtime_arg; /**< @brief Cap length of time for sending packets. */ - char * max_runtime_orig; /**< @brief Cap length of time for sending packets original value given at command line. */ - const char *max_runtime_help; /**< @brief Cap length of time for sending packets help description. */ - int rate_arg; /**< @brief Set send rate in packets/sec. */ - char * rate_orig; /**< @brief Set send rate in packets/sec original value given at command line. */ - const char *rate_help; /**< @brief Set send rate in packets/sec help description. */ - char * bandwidth_arg; /**< @brief Set send rate in bits/second (supports suffixes G, M and K). */ - char * bandwidth_orig; /**< @brief Set send rate in bits/second (supports suffixes G, M and K) original value given at command line. */ - const char *bandwidth_help; /**< @brief Set send rate in bits/second (supports suffixes G, M and K) help description. */ - int cooldown_time_arg; /**< @brief How long to continue receiving after sending last probe (default='8'). */ - char * cooldown_time_orig; /**< @brief How long to continue receiving after sending last probe original value given at command line. */ - const char *cooldown_time_help; /**< @brief How long to continue receiving after sending last probe help description. */ - int seed_arg; /**< @brief Seed used to select address permutation. */ - char * seed_orig; /**< @brief Seed used to select address permutation original value given at command line. */ - const char *seed_help; /**< @brief Seed used to select address permutation help description. */ - int sender_threads_arg; /**< @brief Threads used to send packets (default='1'). */ - char * sender_threads_orig; /**< @brief Threads used to send packets original value given at command line. */ - const char *sender_threads_help; /**< @brief Threads used to send packets help description. */ - int probes_arg; /**< @brief Number of probes to send to each IP (default='1'). */ - char * probes_orig; /**< @brief Number of probes to send to each IP original value given at command line. */ - const char *probes_help; /**< @brief Number of probes to send to each IP help description. */ - const char *dryrun_help; /**< @brief Don't actually send packets help description. */ - char * source_port_arg; /**< @brief Source port(s) for scan packets. */ - char * source_port_orig; /**< @brief Source port(s) for scan packets original value given at command line. */ - const char *source_port_help; /**< @brief Source port(s) for scan packets help description. */ - char * source_ip_arg; /**< @brief Source address(es) for scan packets. */ - char * source_ip_orig; /**< @brief Source address(es) for scan packets original value given at command line. */ - const char *source_ip_help; /**< @brief Source address(es) for scan packets help description. */ - char * gateway_mac_arg; /**< @brief Specify gateway MAC address. */ - char * gateway_mac_orig; /**< @brief Specify gateway MAC address original value given at command line. */ - const char *gateway_mac_help; /**< @brief Specify gateway MAC address help description. */ - char * interface_arg; /**< @brief Specify network interface to use. */ - char * interface_orig; /**< @brief Specify network interface to use original value given at command line. */ - const char *interface_help; /**< @brief Specify network interface to use help description. */ - const char *vpn_help; /**< @brief Sends IP packets instead of Ethernet (for VPNs) help description. */ - char * probe_module_arg; /**< @brief Select probe module (default='tcp_synscan'). */ - char * probe_module_orig; /**< @brief Select probe module original value given at command line. */ - const char *probe_module_help; /**< @brief Select probe module help description. */ - char * output_module_arg; /**< @brief Select output module (default='simple_file'). */ - char * output_module_orig; /**< @brief Select output module original value given at command line. */ - const char *output_module_help; /**< @brief Select output module help description. */ - char * probe_args_arg; /**< @brief Arguments to pass to probe module. */ - char * probe_args_orig; /**< @brief Arguments to pass to probe module original value given at command line. */ - const char *probe_args_help; /**< @brief Arguments to pass to probe module help description. */ - char * output_args_arg; /**< @brief Arguments to pass to output module. */ - char * output_args_orig; /**< @brief Arguments to pass to output module original value given at command line. */ - const char *output_args_help; /**< @brief Arguments to pass to output module help description. */ - const char *list_output_modules_help; /**< @brief List available output modules help description. */ - const char *list_probe_modules_help; /**< @brief List available probe modules help description. */ - const char *list_output_fields_help; /**< @brief List all fields that can be output by selected probe module help description. */ - char * config_arg; /**< @brief Read a configuration file, which can specify any of these options (default='/etc/zmap/zmap.conf'). */ - char * config_orig; /**< @brief Read a configuration file, which can specify any of these options original value given at command line. */ - const char *config_help; /**< @brief Read a configuration file, which can specify any of these options help description. */ - const char *quiet_help; /**< @brief Do not print status updates help description. */ - const char *summary_help; /**< @brief Print configuration and summary at end of scan help description. */ - int verbosity_arg; /**< @brief Level of log detail (0-5) (default='3'). */ - char * verbosity_orig; /**< @brief Level of log detail (0-5) original value given at command line. */ - const char *verbosity_help; /**< @brief Level of log detail (0-5) help description. */ - const char *help_help; /**< @brief Print help and exit help description. */ - const char *version_help; /**< @brief Print version and exit help description. */ - - unsigned int target_port_given ; /**< @brief Whether target-port was given. */ - unsigned int output_file_given ; /**< @brief Whether output-file was given. */ - unsigned int blacklist_file_given ; /**< @brief Whether blacklist-file was given. */ - unsigned int whitelist_file_given ; /**< @brief Whether whitelist-file was given. */ - unsigned int output_fields_given ; /**< @brief Whether output-fields was given. */ - unsigned int max_targets_given ; /**< @brief Whether max-targets was given. */ - unsigned int max_results_given ; /**< @brief Whether max-results was given. */ - unsigned int max_runtime_given ; /**< @brief Whether max-runtime was given. */ - unsigned int rate_given ; /**< @brief Whether rate was given. */ - unsigned int bandwidth_given ; /**< @brief Whether bandwidth was given. */ - unsigned int cooldown_time_given ; /**< @brief Whether cooldown-time was given. */ - unsigned int seed_given ; /**< @brief Whether seed was given. */ - unsigned int sender_threads_given ; /**< @brief Whether sender-threads was given. */ - unsigned int probes_given ; /**< @brief Whether probes was given. */ - unsigned int dryrun_given ; /**< @brief Whether dryrun was given. */ - unsigned int source_port_given ; /**< @brief Whether source-port was given. */ - unsigned int source_ip_given ; /**< @brief Whether source-ip was given. */ - unsigned int gateway_mac_given ; /**< @brief Whether gateway-mac was given. */ - unsigned int interface_given ; /**< @brief Whether interface was given. */ - unsigned int vpn_given ; /**< @brief Whether vpn was given. */ - unsigned int probe_module_given ; /**< @brief Whether probe-module was given. */ - unsigned int output_module_given ; /**< @brief Whether output-module was given. */ - unsigned int probe_args_given ; /**< @brief Whether probe-args was given. */ - unsigned int output_args_given ; /**< @brief Whether output-args was given. */ - unsigned int list_output_modules_given ; /**< @brief Whether list-output-modules was given. */ - unsigned int list_probe_modules_given ; /**< @brief Whether list-probe-modules was given. */ - unsigned int list_output_fields_given ; /**< @brief Whether list-output-fields was given. */ - unsigned int config_given ; /**< @brief Whether config was given. */ - unsigned int quiet_given ; /**< @brief Whether quiet was given. */ - unsigned int summary_given ; /**< @brief Whether summary was given. */ - unsigned int verbosity_given ; /**< @brief Whether verbosity was given. */ - unsigned int help_given ; /**< @brief Whether help was given. */ - unsigned int version_given ; /**< @brief Whether version was given. */ - -} ; - -/** @brief The additional parameters to pass to parser functions */ -struct cmdline_parser_params -{ - int override; /**< @brief whether to override possibly already present options (default 0) */ - int initialize; /**< @brief whether to initialize the option structure gengetopt_args_info (default 1) */ - int check_required; /**< @brief whether to check that all required options were provided (default 1) */ - int check_ambiguity; /**< @brief whether to check for options already specified in the option structure gengetopt_args_info (default 0) */ - int print_errors; /**< @brief whether getopt_long should print an error message for a bad option (default 1) */ -} ; - -/** @brief the purpose string of the program */ -extern const char *gengetopt_args_info_purpose; -/** @brief the usage string of the program */ -extern const char *gengetopt_args_info_usage; -/** @brief all the lines making the help output */ -extern const char *gengetopt_args_info_help[]; - -/** - * The command line parser - * @param argc the number of command line options - * @param argv the command line options - * @param args_info the structure where option information will be stored - * @return 0 if everything went fine, NON 0 if an error took place - */ -int cmdline_parser (int argc, char **argv, - struct gengetopt_args_info *args_info); - -/** - * The command line parser (version with additional parameters - deprecated) - * @param argc the number of command line options - * @param argv the command line options - * @param args_info the structure where option information will be stored - * @param override whether to override possibly already present options - * @param initialize whether to initialize the option structure my_args_info - * @param check_required whether to check that all required options were provided - * @return 0 if everything went fine, NON 0 if an error took place - * @deprecated use cmdline_parser_ext() instead - */ -int cmdline_parser2 (int argc, char **argv, - struct gengetopt_args_info *args_info, - int override, int initialize, int check_required); - -/** - * The command line parser (version with additional parameters) - * @param argc the number of command line options - * @param argv the command line options - * @param args_info the structure where option information will be stored - * @param params additional parameters for the parser - * @return 0 if everything went fine, NON 0 if an error took place - */ -int cmdline_parser_ext (int argc, char **argv, - struct gengetopt_args_info *args_info, - struct cmdline_parser_params *params); - -/** - * Save the contents of the option struct into an already open FILE stream. - * @param outfile the stream where to dump options - * @param args_info the option struct to dump - * @return 0 if everything went fine, NON 0 if an error took place - */ -int cmdline_parser_dump(FILE *outfile, - struct gengetopt_args_info *args_info); - -/** - * Save the contents of the option struct into a (text) file. - * This file can be read by the config file parser (if generated by gengetopt) - * @param filename the file where to save - * @param args_info the option struct to save - * @return 0 if everything went fine, NON 0 if an error took place - */ -int cmdline_parser_file_save(const char *filename, - struct gengetopt_args_info *args_info); - -/** - * Print the help - */ -void cmdline_parser_print_help(void); -/** - * Print the version - */ -void cmdline_parser_print_version(void); - -/** - * Initializes all the fields a cmdline_parser_params structure - * to their default values - * @param params the structure to initialize - */ -void cmdline_parser_params_init(struct cmdline_parser_params *params); - -/** - * Allocates dynamically a cmdline_parser_params structure and initializes - * all its fields to their default values - * @return the created and initialized cmdline_parser_params structure - */ -struct cmdline_parser_params *cmdline_parser_params_create(void); - -/** - * Initializes the passed gengetopt_args_info structure's fields - * (also set default values for options that have a default) - * @param args_info the structure to initialize - */ -void cmdline_parser_init (struct gengetopt_args_info *args_info); -/** - * Deallocates the string fields of the gengetopt_args_info structure - * (but does not deallocate the structure itself) - * @param args_info the structure to deallocate - */ -void cmdline_parser_free (struct gengetopt_args_info *args_info); - -/** - * The config file parser (deprecated version) - * @param filename the name of the config file - * @param args_info the structure where option information will be stored - * @param override whether to override possibly already present options - * @param initialize whether to initialize the option structure my_args_info - * @param check_required whether to check that all required options were provided - * @return 0 if everything went fine, NON 0 if an error took place - * @deprecated use cmdline_parser_config_file() instead - */ -int cmdline_parser_configfile (const char *filename, - struct gengetopt_args_info *args_info, - int override, int initialize, int check_required); - -/** - * The config file parser - * @param filename the name of the config file - * @param args_info the structure where option information will be stored - * @param params additional parameters for the parser - * @return 0 if everything went fine, NON 0 if an error took place - */ -int cmdline_parser_config_file (const char *filename, - struct gengetopt_args_info *args_info, - struct cmdline_parser_params *params); - -/** - * Checks that all the required options were specified - * @param args_info the structure to check - * @param prog_name the name of the program that will be used to print - * possible errors - * @return - */ -int cmdline_parser_required (struct gengetopt_args_info *args_info, - const char *prog_name); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* ZOPT_H */ diff --git a/zmap_conf_install.cmake.in b/zmap_conf_install.cmake.in new file mode 100644 index 0000000..358a97d --- /dev/null +++ b/zmap_conf_install.cmake.in @@ -0,0 +1,6 @@ +foreach(conf_file ${CONF_FILES}) + message(STATUS "${conf_file}") + if(NOT EXISTS "/etc/zmap/${conf_file}") + file(INSTALL "conf/${conf_file}" DESTINATION "/etc/zmap") + endif() +endforeach()