From ef08b147149367498fbcd47ec740598d6d3f757a Mon Sep 17 00:00:00 2001 From: ngn Date: Sun, 16 Jun 2024 19:54:43 +0300 Subject: [PATCH] add mongodb functions --- scanner/.clang-format | 15 +-- scanner/.gitignore | 2 + scanner/Makefile | 7 +- scanner/inc/api.h | 11 -- scanner/inc/db.h | 12 +++ scanner/inc/log.h | 14 +-- scanner/inc/net.h | 16 +-- scanner/inc/op.h | 14 +-- scanner/inc/pool.h | 10 +- scanner/inc/util.h | 17 ++-- scanner/main.c | 228 ++++++++++++++++++++++-------------------- scanner/util/api.c | 107 -------------------- scanner/util/db.c | 209 ++++++++++++++++++++++++++++++++++++++ scanner/util/log.c | 19 ++-- scanner/util/net.c | 138 ++++++++++++++----------- scanner/util/op.c | 104 +++++-------------- scanner/util/pool.c | 18 ++-- scanner/util/util.c | 205 +++++++++++++++++++------------------ 18 files changed, 620 insertions(+), 526 deletions(-) delete mode 100644 scanner/inc/api.h create mode 100644 scanner/inc/db.h delete mode 100644 scanner/util/api.c create mode 100644 scanner/util/db.c diff --git a/scanner/.clang-format b/scanner/.clang-format index 0822db5..9fa0ea7 100644 --- a/scanner/.clang-format +++ b/scanner/.clang-format @@ -2,10 +2,10 @@ Language: Cpp # BasedOnStyle: LLVM AccessModifierOffset: -2 -AlignAfterOpenBracket: Align -AlignArrayOfStructures: None +AlignAfterOpenBracket: DontAlign +AlignArrayOfStructures: Left AlignConsecutiveAssignments: - Enabled: false + Enabled: true AcrossEmptyLines: false AcrossComments: false AlignCompound: false @@ -17,7 +17,7 @@ AlignConsecutiveBitFields: AlignCompound: false PadOperators: false AlignConsecutiveDeclarations: - Enabled: false + Enabled: true AcrossEmptyLines: false AcrossComments: false AlignCompound: false @@ -38,7 +38,7 @@ AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: Never AllowShortCaseLabelsOnASingleLine: false AllowShortEnumsOnASingleLine: true -AllowShortFunctionsOnASingleLine: All +AllowShortFunctionsOnASingleLine: None AllowShortIfStatementsOnASingleLine: Never AllowShortLambdasOnASingleLine: All AllowShortLoopsOnASingleLine: false @@ -48,7 +48,7 @@ AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: MultiLine AttributeMacros: - __capability -BinPackArguments: true +BinPackArguments: false BinPackParameters: true BitFieldColonSpacing: Both BraceWrapping: @@ -81,7 +81,7 @@ BreakBeforeTernaryOperators: true BreakConstructorInitializers: BeforeColon BreakInheritanceList: BeforeColon BreakStringLiterals: true -ColumnLimit: 80 +ColumnLimit: 120 CommentPragmas: '^ IWYU pragma:' CompactNamespaces: false ConstructorInitializerIndentWidth: 4 @@ -223,3 +223,4 @@ WhitespaceSensitiveMacros: - STRINGIZE ... + diff --git a/scanner/.gitignore b/scanner/.gitignore index d98a8e0..84c3e1e 100644 --- a/scanner/.gitignore +++ b/scanner/.gitignore @@ -1 +1,3 @@ +compile_commands.json massacr +.cache diff --git a/scanner/Makefile b/scanner/Makefile index 6669ba6..0b1028d 100644 --- a/scanner/Makefile +++ b/scanner/Makefile @@ -1,11 +1,14 @@ HEADERS = $(wildcard inc/*.h) CSRCS = $(wildcard *.c) $(wildcard */*.c) -VERSION = 1.2 +CFLAGS = -O3 -march=native -fstack-protector-strong -fcf-protection=full -fstack-clash-protection +LIBS = -lpthread $(shell pkg-config --libs --cflags libnet) $(shell pkg-config --libs --cflags libmongoc-1.0) + +VERSION = 1.3 prefix = /usr CC = gcc massacr: $(CSRCS) $(HEADERS) - $(CC) $(CFLAGS) $(CSRCS) -o $@ -DVERSION=\"${VERSION}\" -lnet -lcurl + $(CC) $(CFLAGS) $(CSRCS) -o $@ -DVERSION=\"${VERSION}\" $(LIBS) install: install -v -m755 massacr $(DESTDIR)$(prefix)/bin/massacr diff --git a/scanner/inc/api.h b/scanner/inc/api.h deleted file mode 100644 index 0ea1a1c..0000000 --- a/scanner/inc/api.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once -#include "../inc/log.h" -#include - -typedef struct APIArgs { - char *ip; - int port; -} api_args_t; - -bool api_check(); -bool api_add(void *); diff --git a/scanner/inc/db.h b/scanner/inc/db.h new file mode 100644 index 0000000..0db8907 --- /dev/null +++ b/scanner/inc/db.h @@ -0,0 +1,12 @@ +#pragma once +#include +#include + +typedef struct db_args { + uint16_t port; + uint32_t ipv4; +} db_args_t; + +bool db_init(char *); +void db_add(void *); +void db_free(); diff --git a/scanner/inc/log.h b/scanner/inc/log.h index e9ef387..ec0c2fa 100644 --- a/scanner/inc/log.h +++ b/scanner/inc/log.h @@ -6,13 +6,13 @@ #include "../inc/op.h" -#define RED "\x1b[31m" -#define BOLD "\x1b[1m" -#define BLUE "\x1b[34m" -#define GRAY "\x1b[37m" -#define RESET "\x1b[0m" -#define GREEN "\x1b[32m" -#define SPACE " " +#define FG_RED "\x1b[31m" +#define FG_BOLD "\x1b[1m" +#define FG_BLUE "\x1b[34m" +#define FG_GRAY "\x1b[37m" +#define FG_RESET "\x1b[0m" +#define FG_GREEN "\x1b[32m" +#define FG_SPACE " " void info(const char *, ...); void error(const char *, ...); diff --git a/scanner/inc/net.h b/scanner/inc/net.h index 751e1bc..a3d1bdb 100644 --- a/scanner/inc/net.h +++ b/scanner/inc/net.h @@ -8,8 +8,8 @@ struct tcphdr_ { struct { uint16_t th_sport; /* source port */ uint16_t th_dport; /* destination port */ - tcp_seq th_seq; /* sequence number */ - tcp_seq th_ack; /* acknowledgement number */ + tcp_seq th_seq; /* sequence number */ + tcp_seq th_ack; /* acknowledgement number */ #if __BYTE_ORDER == __LITTLE_ENDIAN uint8_t th_x2 : 4; /* (unused) */ uint8_t th_off : 4; /* data offset */ @@ -64,11 +64,15 @@ struct tcphdr_ { }; }; -typedef struct RecvArgs { +typedef struct net_recv_args { uint16_t port; - bool should_run; + bool should_run; + int threads; } recv_args_t; extern pthread_mutex_t net_lock; -bool net_syn(char *, int, int); -bool net_receive(void *); + +bool net_init(); +void net_free(); +bool net_syn(uint32_t, int, int); +void net_receive(void *); diff --git a/scanner/inc/op.h b/scanner/inc/op.h index d7cc9b7..82f90d2 100644 --- a/scanner/inc/op.h +++ b/scanner/inc/op.h @@ -8,13 +8,13 @@ typedef struct Option { char *value; char *name; char *desc; - int type; + int type; } option_t; extern option_t options[]; -char *extract_value(char *); -bool parse_opt(char *); -bool get_bool(char *); -char *get_str(char *); -int get_int(char *); -void print_opts(); +char *extract_value(char *); +bool parse_opt(char *); +bool get_bool(char *); +char *get_str(char *); +int get_int(char *); +void print_opts(); diff --git a/scanner/inc/pool.h b/scanner/inc/pool.h index a1e11fd..061c8ba 100644 --- a/scanner/inc/pool.h +++ b/scanner/inc/pool.h @@ -4,8 +4,8 @@ typedef void (*func_t)(void *arg); typedef struct work_t { - func_t func; - void *arg; + func_t func; + void *arg; struct work_t *next; } work_t; @@ -20,9 +20,9 @@ typedef struct pool_t { work_t *first; work_t *last; - bool stop; + bool stop; } pool_t; pool_t *pool_init(int); -bool pool_add(pool_t *, func_t, void *); -void pool_stop(pool_t *); +bool pool_add(pool_t *, func_t, void *); +void pool_stop(pool_t *); diff --git a/scanner/inc/util.h b/scanner/inc/util.h index 3faa8d2..e56d4c8 100644 --- a/scanner/inc/util.h +++ b/scanner/inc/util.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include #define contains(str, sub) (strchr(str, sub) != NULL) @@ -7,17 +8,13 @@ #define neq(s1, s2) (strcmp(s1, s2) != 0) #define eq(s1, s2) (strcmp(s1, s2) == 0) -typedef struct Range { +typedef struct subnet { uint8_t ip[4]; uint8_t net; -} range_t; +} subnet_t; -int *parse_ports(char *, int *); -void clean_ports(int *); +uint16_t *parse_ports(char *); +void clean_ports(uint16_t *); -bool get_range(range_t *, char *); -bool skip_range(range_t *, uint8_t *); -void urljoin(char *, char *, char *); -extern int common_ports[]; -extern char *bad_ranges[]; -extern int bad_range_size; +bool get_subnet(subnet_t *, char *); +bool subnet_contains(uint32_t, subnet_t *, size_t); diff --git a/scanner/main.c b/scanner/main.c index a09bf9f..95b6a7d 100644 --- a/scanner/main.c +++ b/scanner/main.c @@ -26,41 +26,57 @@ #include #include -#include "inc/api.h" +#include "inc/db.h" #include "inc/log.h" #include "inc/net.h" #include "inc/op.h" #include "inc/util.h" -recv_args_t *args = NULL; -int ports_size, recvport, *ports; +recv_args_t args; + +// https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml +char *bad_nets[] = { + "0.0.0.0/8", + "10.0.0.0/8", + "100.64.0.0/10", + "127.0.0.0/8", + "169.254.0.0/16", + "172.16.0.0/12", + "192.0.0.0/24", + "192.0.0.0/29", + "192.0.0.8/32", + "192.0.0.9/32", + "192.0.0.170/32", + "192.0.0.171/32", + "192.0.2.0/24", + "192.31.196.0/24", + "192.52.193.0/24", + "192.88.99.0/24", + "192.168.0.0/16", + "192.175.48.0/24", + "198.18.0.0/15", + "198.51.100.0/24", + "203.0.113.0/24", + "240.0.0.0/4", + "255.255.255.255/32", + NULL, +}; void handle_int() { - info("Got interrupt, give me a second for cleanup"); + info("Got interrupt, cleaning up"); - if (NULL != args) { - pthread_mutex_lock(&net_lock); - args->should_run = false; - pthread_mutex_unlock(&net_lock); - } + pthread_mutex_lock(&net_lock); + args.should_run = false; + pthread_mutex_unlock(&net_lock); + net_free(); + db_free(); exit(1); } -bool scan(char *ipstr) { - for (int i = 0; i < ports_size; i++) { - int cur = ports[i]; - - if (!net_syn(ipstr, cur, recvport)) { - error("Failed to send SYN to %s:%d", ipstr, ports[i]); - return false; - } - } - - return true; -} - int main(int argc, char **argv) { + int ret = EXIT_FAILURE; + // parse options and log them out for (int i = 1; i < argc; i++) { if (!parse_opt(argv[i])) { @@ -68,116 +84,116 @@ int main(int argc, char **argv) { } } - info("massacr scanner %s: https://github.com/ngn13/massacr", VERSION); + info("massacr scanner (%s)", VERSION); info("Running with following options:"); print_opts(); // check if running as root if (0 != getuid()) { error("Cannot create raw sockets without root"); - return EXIT_FAILURE; + return ret; } - // init threading and other stuff + // setup signal handler signal(SIGINT, handle_int); - if (!api_check()) - return EXIT_FAILURE; - ports = parse_ports(get_str("ports"), &ports_size); + // check and parse params + int int_limit = get_int("limit"); + if (int_limit <= 0) { + error("Invalid limit: %d", int_limit); + return ret; + } + + float limit = 1.0 / int_limit; + limit = (limit) * 1000000; + + int recvport = get_int("recvport"); + if (recvport <= 0 || recvport > UINT16_MAX) { + error("Invalid receiver port number: %d", recvport); + return ret; + } + + int threads = get_int("threads"); + if (threads <= 0) { + error("Invalid database thread count: %d", threads); + return ret; + } + + uint16_t *ports = parse_ports(get_str("ports")); if (ports == NULL) - return EXIT_FAILURE; - if (ports_size > 1) - info("Scanning for %d ports", ports_size); - else - info("Scanning for 1 port", ports_size); - recvport = get_int("recvport"); - - // converts string non public IP ranges to Range struct - struct Range *ranges = NULL; - for (int i = 0; i < bad_range_size; i++) { - if (ranges == NULL) - ranges = malloc(sizeof(struct Range)); - else - ranges = realloc(ranges, sizeof(struct Range) * (i + 1)); - - if (!get_range(&ranges[i], bad_ranges[i])) { - error("Invalid range: %s", bad_ranges[i]); - goto FAIL; - } + return ret; + + // convert non public IP ranges to ipv4_t struct + subnet_t *invalid = NULL; + size_t invalid_size = 0; + + for (int i = 0; bad_nets[i] != NULL; i++) + invalid_size++; + + subnet_t _invalid[invalid_size]; + invalid = _invalid; + + for (int i = 0; bad_nets[i] != NULL; i++) { + if (get_subnet(&invalid[i], bad_nets[i])) + continue; + error("Invalid IPv4 subnet: %s", bad_nets[i]); + goto fail; } - // start receiver thread + // init stuff + if (!db_init(get_str("mongo"))) + goto fail; + + if (!net_init()) + goto fail; + + // start net receiver thread pthread_t recv; - args = malloc(sizeof(recv_args_t)); - args->should_run = true; - args->port = recvport; + args.should_run = true; + args.threads = threads; + args.port = recvport; - pthread_mutex_init(&net_lock, NULL); - pthread_create(&recv, NULL, (void *)net_receive, args); + pthread_create(&recv, NULL, (void *)net_receive, &args); pthread_detach(recv); - // calculating limits - float limit = 1.0 / get_int("limit"); - limit = (limit) * 1000000; - - // loops over all the IPs - uint8_t cur[4] = {1, 1, 1, 0}; - while (cur[0] <= 255) { - while (cur[1] <= 255) { - while (cur[2] <= 255) { - while (cur[3] <= 255) { - if (skip_range(ranges, cur)) - goto NEXT; - - char ipstr[16]; - sprintf(ipstr, "%d.%d.%d.%d", cur[0], cur[1], cur[2], cur[3]); - - scan(ipstr); - usleep(limit); - NEXT: - if (cur[3] == 255) { - cur[3] = 0; - break; - } - cur[3]++; - } - if (cur[2] == 255) { - cur[2] = 0; - break; - } - cur[2]++; - } - info("Scan completed for the %d.%d.0.0/16 range", cur[0], cur[1]); - if (cur[1] == 255) { - cur[1] = 0; - break; - } - cur[1]++; - } - if (cur[0] == 255) - break; - cur[0]++; - } + // start looping over all the IPs + uint32_t current = 0; + + do { + // if the ip is in an invalid subnet, continue + if (subnet_contains(current, invalid, invalid_size)) + continue; + // send syn packets + for (int i = 0; ports[i] != 0; i++) + net_syn(current, ports[i], recvport); + + // sleep according to the limit + usleep(limit); + } while ((current++) != UINT32_MAX); + + // wait for receiver thread info("Scan completed!"); info("Now waiting for receiver timeout (%ds)", get_int("timeout")); sleep(get_int("timeout")); - pthread_mutex_lock(&net_lock); - args->should_run = false; - pthread_mutex_unlock(&net_lock); - pthread_mutex_destroy(&net_lock); - free(ranges); - clean_ports(ports); + // set the return value + ret = EXIT_SUCCESS; info("Scan completed"); - return EXIT_SUCCESS; -FAIL: +fail: + // stop the receiver thread pthread_mutex_lock(&net_lock); - args->should_run = false; + args.should_run = false; pthread_mutex_unlock(&net_lock); + + // destroy the receiver mutex pthread_mutex_destroy(&net_lock); - free(ranges); + + // free the resources and return + net_free(); + db_free(); + clean_ports(ports); - return EXIT_FAILURE; + return ret; } diff --git a/scanner/util/api.c b/scanner/util/api.c deleted file mode 100644 index 9ea36ec..0000000 --- a/scanner/util/api.c +++ /dev/null @@ -1,107 +0,0 @@ -#include "../inc/api.h" -#include "../inc/log.h" -#include "../inc/util.h" - -#include -#include -#include - -bool api_check() { - CURL *curl = curl_easy_init(); - FILE *null = fopen("/dev/null", "wb"); - bool ret = true; - - char *password = get_str("password"); - char *url = get_str("url"); - - char path[strlen(password) + 30]; - sprintf(path, "scanner/check?pass=%s", password); - - char newurl[strlen(url) + strlen(path) + 5]; - urljoin(newurl, url, path); - - curl_easy_setopt(curl, CURLOPT_URL, newurl); - curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl/massacr"); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, null); - CURLcode res = curl_easy_perform(curl); - - if (res != CURLE_OK) { - error("API connection failed"); - ret = false; - goto DONE; - } - - long code; - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); - - if (code == 403) { - error("Bad password for API"); - ret = false; - goto DONE; - } - - if (code != 200) { - error("Bad status code from API (%d)", code); - ret = false; - goto DONE; - } - -DONE: - curl_easy_cleanup(curl); - fclose(null); - return ret; -} - -bool api_add(void *ap) { - api_args_t *args = (api_args_t *)ap; - if (NULL == args) - return false; - - CURL *curl = curl_easy_init(); - FILE *null = fopen("/dev/null", "wb"); - bool ret = true; - - char *password = get_str("password"); - char *url = get_str("url"); - - char path[strlen(password) + strlen(args->ip) + 55]; - sprintf(path, "scanner/add?pass=%s&ip=%s&port=%d", password, args->ip, - args->port); - - char newurl[strlen(url) + strlen(path) + 10]; - urljoin(newurl, url, path); - - curl_easy_setopt(curl, CURLOPT_URL, newurl); - curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl/massacr"); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, null); - CURLcode res = curl_easy_perform(curl); - - if (res != CURLE_OK) { - error("API connection failed"); - ret = false; - goto DONE; - } - - long code; - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); - - if (code == 403) { - error("Bad password for API"); - ret = false; - goto DONE; - } - - if (code != 200) { - error("Bad status code from API (%d)", code); - ret = false; - goto DONE; - } - -DONE: - free(ap); - curl_easy_cleanup(curl); - fclose(null); - return ret; -} diff --git a/scanner/util/db.c b/scanner/util/db.c new file mode 100644 index 0000000..9a4c9b5 --- /dev/null +++ b/scanner/util/db.c @@ -0,0 +1,209 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "../inc/db.h" +#include "../inc/log.h" + +mongoc_client_pool_t *pool = NULL; + +bool db_init(char *mongo) { + mongoc_client_t *client; + mongoc_collection_t *collection; + mongoc_index_model_t *im; + bson_error_t err; + mongoc_uri_t *uri = NULL; + bson_t *cmd = NULL, *keys = NULL, *opts = NULL, reply; + bool ret = false; + + // init mongoc + mongoc_init(); + + // parse the URI + uri = mongoc_uri_new_with_error(mongo, &err); + if (NULL == uri) { + error("Failed to parse the URI: %s", err.message); + goto fail; + } + + // setup the client pool + pool = mongoc_client_pool_new(uri); + if (NULL == pool) { + error("Failed to create the pool"); + goto fail; + } + mongoc_client_pool_set_appname(pool, "massacr-scanner"); + + // pop and setup the client + client = mongoc_client_pool_pop(pool); + if (NULL == client) { + error("Failed to create the client"); + goto fail; + } + + collection = mongoc_client_get_collection(client, "massacr", "data"); + if (NULL == collection) { + error("Failed to access data collection"); + goto fail; + } + + keys = BCON_NEW("ipv4", BCON_INT32(1)); + opts = BCON_NEW("unique", BCON_BOOL(true)); + + im = mongoc_index_model_new(keys, opts); + if (!mongoc_collection_create_indexes_with_opts(collection, &im, 1, NULL, NULL, &err)) { + error("Failed to create indexes: %s", err.message); + goto fail; + } + + // ping the "admin" database + cmd = BCON_NEW("ping", BCON_INT32(1)); + if (!mongoc_client_command_simple(client, "admin", cmd, NULL, &reply, &err)) + error("Failed to ping to admin database: %s", err.message); + else + ret = true; + bson_destroy(&reply); + + // cleanup +fail: + if (NULL != im) + mongoc_index_model_destroy(im); + + if (NULL != collection) + mongoc_collection_destroy(collection); + + if (NULL != client) + mongoc_client_pool_push(pool, client); + + if (NULL != keys) + bson_destroy(keys); + + if (NULL != opts) + bson_destroy(opts); + + if (NULL != cmd) + bson_destroy(cmd); + + if (NULL != uri) + mongoc_uri_destroy(uri); + + return ret; +} + +void db_free() { + if (NULL != pool) + mongoc_client_pool_destroy(pool); + mongoc_cleanup(); +} + +void db_add(void *ap) { + db_args_t *args = (db_args_t *)ap; + char ipstr[INET_ADDRSTRLEN]; + bool exists = false; + + mongoc_collection_t *collection; + mongoc_client_t *client; + bson_array_builder_t *bab; + const bson_t *found; + bson_error_t err; + bson_iter_t iter; + bson_t *query = NULL, *doc = NULL, child; + + snprintf(ipstr, + INET_ADDRSTRLEN, + "%d.%d.%d.%d", + (args->ipv4 >> 24) & 255, + (args->ipv4 >> 16) & 255, + (args->ipv4 >> 8) & 255, + (args->ipv4 & 255)); + + client = mongoc_client_pool_pop(pool); + collection = mongoc_client_get_collection(client, "massacr", "data"); + if (NULL == collection) { + error("Failed to access data collection"); + goto end; + } + +again: + query = BCON_NEW("ipv4", BCON_UTF8(ipstr)); + + mongoc_cursor_t *cursor = mongoc_collection_find_with_opts(collection, query, NULL, NULL); + if (mongoc_cursor_next(cursor, &found)) + exists = true; + mongoc_cursor_destroy(cursor); + + bson_destroy(query); + query = NULL; + + if (!exists) { + // create new BSON document + doc = bson_new(); + + // "_id": "[new oid]" + bson_oid_t oid; + bson_oid_init(&oid, NULL); + BSON_APPEND_OID(doc, "_id", &oid); + + // "ipv4": "1.2.3.4" + BSON_APPEND_UTF8(doc, "ipv4", ipstr); + + // "ports": [80] + BSON_APPEND_ARRAY_BUILDER_BEGIN(doc, "ports", &bab); + bson_array_builder_append_int32(bab, args->port); + bson_append_array_builder_end(doc, bab); + + // insert the document + if (mongoc_collection_insert_one(collection, doc, NULL, NULL, &err)) + goto end; + + if (err.code != MONGOC_ERROR_DUPLICATE_KEY) { + error("Failed to update document for %s: %s", ipstr, err.message); + goto end; + } + + bson_destroy(doc); + doc = NULL; + + goto again; + } + + // find the existing document's "_id" + bson_iter_init_find(&iter, found, "_id"); + if (!BSON_ITER_HOLDS_OID(&iter)) { + error("Failed to update existing document for %s: OID not found"); + goto end; + } + + // create the query with "_id": "[found oid]" + const bson_oid_t *oid = bson_iter_oid(&iter); + query = BCON_NEW("_id", BCON_OID(oid)); + + // create the update document with "$push": { "ports": 22 } + doc = bson_new(); + BSON_APPEND_DOCUMENT_BEGIN(doc, "$addToSet", &child); + BSON_APPEND_INT32(&child, "ports", args->port); + bson_append_document_end(doc, &child); + + // update the document + if (!mongoc_collection_update_one(collection, query, doc, NULL, NULL, &err)) + error("Failed to update document for %s: %s", ipstr, err.message); + +end: + if (NULL != client) + mongoc_client_pool_push(pool, client); + + if (NULL != collection) + mongoc_collection_destroy(collection); + + if (NULL != query) + bson_destroy(query); + + if (NULL != doc) + bson_destroy(doc); + + free(args); +} diff --git a/scanner/util/log.c b/scanner/util/log.c index e43bbd5..aa7a7cb 100644 --- a/scanner/util/log.c +++ b/scanner/util/log.c @@ -7,11 +7,10 @@ #include "../inc/op.h" void get_time(char *res) { - time_t now = time(NULL); - struct tm tm = *localtime(&now); + time_t now = time(NULL); + struct tm tm = *localtime(&now); - sprintf(res, "%02d/%02d-%02d:%02d", tm.tm_mday, tm.tm_mon + 1, tm.tm_min, - tm.tm_hour); + sprintf(res, "%02d/%02d-%02d:%02d", tm.tm_mday, tm.tm_mon + 1, tm.tm_hour, tm.tm_min); } void info(const char *msg, ...) { @@ -29,9 +28,9 @@ void info(const char *msg, ...) { return; } - printf(BOLD BLUE "[%s] INFO # " RESET, tstr); + printf(FG_BOLD FG_BLUE "[%s] INFO # " FG_RESET, tstr); vprintf(msg, args); - printf(RESET "\n"); + printf(FG_RESET "\n"); va_end(args); } @@ -50,9 +49,9 @@ void error(const char *msg, ...) { return; } - printf(BOLD RED "[%s] ERROR # " RESET, tstr); + printf(FG_BOLD FG_RED "[%s] ERROR # " FG_RESET, tstr); vprintf(msg, args); - printf(RESET "\n"); + printf(FG_RESET "\n"); va_end(args); } @@ -75,9 +74,9 @@ void debug(const char *msg, ...) { return; } - printf(BOLD GRAY "[%s] DEBUG # " RESET, tstr); + printf(FG_BOLD FG_GRAY "[%s] DEBUG # " FG_RESET, tstr); vprintf(msg, args); - printf(RESET "\n"); + printf(FG_RESET "\n"); va_end(args); } diff --git a/scanner/util/net.c b/scanner/util/net.c index b080901..59f690d 100644 --- a/scanner/util/net.c +++ b/scanner/util/net.c @@ -5,96 +5,122 @@ #include #include -#include "../inc/api.h" +#include "../inc/db.h" #include "../inc/log.h" #include "../inc/net.h" #include "../inc/pool.h" pthread_mutex_t net_lock; +libnet_t *ctx = NULL; -bool net_receive(void *ap) { - recv_args_t *args = (recv_args_t *)ap; - pool_t *pool = pool_init(10); +bool net_init() { + if (pthread_mutex_init(&net_lock, NULL) != 0) { + error("Failed to init receiver thread lock"); + return false; + } - int s = socket(AF_INET, SOCK_RAW, IPPROTO_TCP); - if (s < 0) { - debug("Cannot create raw TCP socket"); + if ((ctx = libnet_init(LIBNET_RAW4, NULL, NULL)) == NULL) { + error("Failed to init libnet"); return false; } - struct timeval timeout; + return true; +} + +void net_free() { + pthread_mutex_destroy(&net_lock); + if (NULL != ctx) + libnet_destroy(ctx); +} + +void net_receive(void *ap) { + // load thread args, create the database pool + recv_args_t *args = (recv_args_t *)ap; + pool_t *pool = pool_init(args->threads); + + // create raw socket + int raw = socket(AF_INET, SOCK_RAW, IPPROTO_TCP); + if (raw < 0) { + error("Failed to create raw TCP socket (are you running as root?)"); + return; + } + + // setup args used for "recvfrom" + struct timeval timeout; struct sockaddr_in addr; - uint addrlen = sizeof(addr); + socklen_t addrlen = sizeof(addr); - char buff[sizeof(struct tcphdr_) + sizeof(struct iphdr)] = {0}; + // create recv buffer + size_t bufsize = sizeof(struct tcphdr_) + sizeof(struct iphdr); + char buff[bufsize]; + bzero(buff, bufsize); + // logging + pthread_mutex_lock(&net_lock); info("Started receiver on port %d", args->port); + pthread_mutex_unlock(&net_lock); + + // start receiver loop while (args->should_run) { - int res = - recvfrom(s, &buff, sizeof(buff), 0, (struct sockaddr *)&addr, &addrlen); + // recvfrom the raw socket + if (recvfrom(raw, buff, bufsize, 0, (struct sockaddr *)&addr, &addrlen) <= 0) + continue; - if (res <= 0) + // make sure this is connection is received over IPv4 + if (addr.sin_family != AF_INET) continue; + // parse the received data, make sure its SYN+ACK struct tcphdr_ *tcph = (struct tcphdr_ *)(buff + sizeof(struct iphdr)); if (tcph->ack != 1 || tcph->syn != 1 || tcph->rst == 1) continue; + // make sure we received from the correct port if (htons(tcph->dest) != args->port) continue; - char ipstr[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, &(addr.sin_addr), ipstr, INET_ADDRSTRLEN); - - pthread_t t; - api_args_t *data = malloc(sizeof(api_args_t)); - data->port = htons(tcph->source); - data->ip = ipstr; - - pool_add(pool, (void *)api_add, data); - pthread_mutex_lock(&net_lock); - debug("Received SYN+ACK from %s:%d", ipstr, htons(tcph->source)); - pthread_mutex_unlock(&net_lock); + // send the info to the database + db_args_t *data = malloc(sizeof(db_args_t)); + data->port = htons(tcph->source); + data->ipv4 = addr.sin_addr.s_addr; + pool_add(pool, (void *)db_add, data); } - pthread_mutex_lock(&net_lock); - debug("Closing thread"); - pthread_mutex_unlock(&net_lock); - + // logging & stop the pool + debug("Closing database pool"); pool_stop(pool); - free(args); - close(s); - return true; -} - -bool net_syn(char *ipstr, int port, int recvport) { - char *payload = "massacr scanner IGNORE"; - int payload_s = strlen(payload); - libnet_t *handle = libnet_init(LIBNET_RAW4, NULL, NULL); + // close the socket + debug("Closing receiver socket"); + close(raw); +} - libnet_ptag_t ret = libnet_build_tcp( - recvport, port, 0x01010101, 0x02020202, TH_SYN, 32767, 0, 10, - LIBNET_TCP_H + payload_s, (uint8_t *)payload, payload_s, handle, 0); +bool net_syn(uint32_t ip, int port, int recvport) { + bool ret = false; + libnet_ptag_t r = + libnet_build_tcp(recvport, port, 0x01010101, 0x02020202, TH_SYN, 32767, 0, 10, LIBNET_TCP_H, NULL, 0, ctx, 0); - if (ret < 0) - goto FAIL; + if (r < 0) { + error("Failed to build TCP packet: %s", libnet_geterror(ctx)); + goto end; + } - ret = libnet_build_ipv4(LIBNET_IPV4_H + LIBNET_TCP_H + payload_s, 0, 242, 0, - 64, IPPROTO_TCP, 0, libnet_get_ipaddr4(handle), - libnet_name2addr4(handle, ipstr, LIBNET_RESOLVE), - NULL, 0, handle, 0); + r = libnet_build_ipv4( + LIBNET_IPV4_H + LIBNET_TCP_H, 0, 242, 0, 64, IPPROTO_TCP, 0, libnet_get_ipaddr4(ctx), ip, NULL, 0, ctx, 0); - if (ret < 0) - goto FAIL; + if (r < 0) { + error("Failed to build IPv4 header: %s", libnet_geterror(ctx)); + goto end; + } - if (libnet_write(handle) < 0) - goto FAIL; + if (libnet_write(ctx) < 0) { + error("Failed to send the packet: %s", libnet_geterror(ctx)); + goto end; + } - libnet_destroy(handle); - return true; + ret = true; -FAIL: - libnet_destroy(handle); - return false; +end: + libnet_clear_packet(ctx); + return ret; } diff --git a/scanner/util/op.c b/scanner/util/op.c index 335c702..5bf0d3e 100644 --- a/scanner/util/op.c +++ b/scanner/util/op.c @@ -9,38 +9,14 @@ #include "../inc/util.h" option_t options[] = { - {.name = "no-color", - .type = TYPE_BOOL, - .value = "false", - .desc = "Do not print colored output"}, - {.name = "recvport", - .type = TYPE_INT, - .value = "13421", - .desc = "Source port for TCP packets"}, - {.name = "timeout", - .type = TYPE_INT, - .value = "10", - .desc = "Timeout for receiver thread"}, - {.name = "ports", - .type = TYPE_STR, - .value = "common", - .desc = "Ports to scan for"}, - {.name = "limit", - .type = TYPE_INT, - .value = "20", - .desc = "Packets per second limit"}, - {.name = "debug", - .type = TYPE_BOOL, - .value = "false", - .desc = "Enable debug output"}, - {.name = "url", - .type = TYPE_STR, - .value = "http://localhost:5000", - .desc = "API HTTP(S) URL"}, - {.name = "password", - .type = TYPE_STR, - .value = "default", - .desc = "API password"}, + {.name = "no-color", .type = TYPE_BOOL, .value = "false", .desc = "Do not print colored output"}, + {.name = "recvport", .type = TYPE_INT, .value = "1337", .desc = "Source port for TCP packets"}, + {.name = "timeout", .type = TYPE_INT, .value = "600", .desc = "Timeout for receiver thread"}, + {.name = "ports", .type = TYPE_STR, .value = "common", .desc = "Ports to scan for" }, + {.name = "limit", .type = TYPE_INT, .value = "20", .desc = "Packets per second limit" }, + {.name = "debug", .type = TYPE_BOOL, .value = "false", .desc = "Enable debug output" }, + {.name = "mongo", .type = TYPE_STR, .value = "mongodb://localhost:27017", .desc = "MongoDB URL" }, + {.name = "threads", .type = TYPE_INT, .value = "10", .desc = "Database thread count" }, }; char *extract_value(char *o) { @@ -68,17 +44,13 @@ void print_opts() { spacebuf[i] = ' '; spacebuf[sizeof(spacebuf) - 1] = '\0'; - if (get_bool("no-color")) { - printf(SPACE "%s%s=> %s\n", options[i].name, spacebuf, options[i].value); - continue; - } - if (options[i].type == TYPE_BOOL) - printf(SPACE BOLD "%s" RESET "%s=> %s\n" RESET, options[i].name, spacebuf, - get_bool(options[i].name) ? GREEN "true" : RED "false"); + printf(FG_BOLD " %s" FG_RESET "%s=> %s\n" FG_RESET, + options[i].name, + spacebuf, + get_bool(options[i].name) ? FG_GREEN "true" : FG_RED "false"); else - printf(SPACE BOLD "%s" RESET "%s=> %s\n" RESET, options[i].name, spacebuf, - options[i].value); + printf(FG_BOLD " %s" FG_RESET "%s=> %s\n" FG_RESET, options[i].name, spacebuf, options[i].value); } } @@ -110,7 +82,6 @@ char *get_str(char *name) { } void print_help() { - info("massacr scanner %s (https://github.com/ngn13/massacr)", VERSION); info("Listing available options:"); int max_len = 0; @@ -120,50 +91,21 @@ void print_help() { } for (int i = 0; i < sizeof(options) / sizeof(option_t); i++) { - char spacebuf[(max_len - strlen(options[i].name)) + 2]; - for (int i = 0; i < sizeof(spacebuf); i++) - spacebuf[i] = ' '; - spacebuf[sizeof(spacebuf) - 1] = '\0'; + int spacesz = (max_len - strlen(options[i].name)) + 2; + char spacebuf[spacesz]; - if (get_bool("no-color")) { - printf(SPACE "--%s%s=> %s\n", options[i].name, spacebuf, - options[i].value); - continue; - } + for (int e = 0; e < spacesz; e++) + spacebuf[e] = ' '; + spacebuf[spacesz - 1] = '\0'; - printf(SPACE BOLD "--%s" RESET "%s=> %s\n" RESET, options[i].name, spacebuf, - options[i].desc); + printf(FG_BOLD " --%s" FG_RESET "%s=> %s\n" FG_RESET, options[i].name, spacebuf, options[i].desc); } - info("Example: " BOLD - "massacr --no-color --recvport=1234 --limit=200 --ports=80,443" RESET); - info("For more examples and details checkout the README"); - - printf("\n"); - info("This program is free software: you can redistribute it and/or modify"); - printf( - SPACE - "it under the terms of the GNU General Public License as published by\n"); - printf(SPACE - "the Free Software Foundation, either version 3 of the License, or\n"); - printf(SPACE "(at your option) any later version.\n"); - printf("\n"); - printf(SPACE - "This program is distributed in the hope that it will be useful\n"); - printf(SPACE - "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"); - printf(SPACE - "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"); - printf(SPACE "GNU General Public License for more details.\n"); printf("\n"); - printf(SPACE - "You should have received a copy of the GNU General Public License\n"); - printf(SPACE "along with this program. If not, see " - ".\n"); } bool parse_opt(char *o) { - if (eq(o, "--help") || eq(o, "--h")) { + if (eq(o, "--help") || eq(o, "-h")) { print_help(); exit(EXIT_SUCCESS); } @@ -176,8 +118,8 @@ bool parse_opt(char *o) { switch (options[i].type) { case TYPE_BOOL: - if (neq(o, fullop)) - goto UNKNOWN; + if (!eq(o, fullop)) + goto unknown; options[i].value = "true"; return true; break; @@ -206,7 +148,7 @@ bool parse_opt(char *o) { } } -UNKNOWN: +unknown: error("Unknown option: %s", o); return false; } diff --git a/scanner/util/pool.c b/scanner/util/pool.c index ad06fab..1f05682 100644 --- a/scanner/util/pool.c +++ b/scanner/util/pool.c @@ -3,13 +3,15 @@ work_t *pool_work(func_t func, void *arg) { work_t *work = malloc(sizeof(work_t)); - work->next = NULL; - work->func = func; - work->arg = arg; + work->next = NULL; + work->func = func; + work->arg = arg; return work; } -void pool_free(work_t *work) { free(work); } +void pool_free(work_t *work) { + free(work); +} work_t *pool_get(pool_t *tp) { work_t *work; @@ -61,14 +63,14 @@ void *pool_worker(void *arg) { pool_t *pool_init(int n) { pool_t *tp = calloc(1, sizeof(pool_t)); - tp->all = n; + tp->all = n; pthread_mutex_init(&(tp->mutex), NULL); pthread_cond_init(&(tp->work_lock), NULL); pthread_cond_init(&(tp->thread_lock), NULL); tp->first = NULL; - tp->last = NULL; + tp->last = NULL; pthread_t handle; for (int i = 0; i < n; i++) { @@ -86,10 +88,10 @@ bool pool_add(pool_t *tp, func_t func, void *arg) { pthread_mutex_lock(&(tp->mutex)); if (tp->first == NULL) { tp->first = work; - tp->last = tp->first; + tp->last = tp->first; } else { tp->last->next = work; - tp->last = work; + tp->last = work; } pthread_cond_broadcast(&(tp->work_lock)); diff --git a/scanner/util/util.c b/scanner/util/util.c index d9f8735..5eaaee4 100644 --- a/scanner/util/util.c +++ b/scanner/util/util.c @@ -7,69 +7,115 @@ #include "../inc/log.h" #include "../inc/util.h" -int common_ports[] = { - 21, 990, // ftp/ftps - 23, 22, // telnet/ssh - 25, 465, // smtp/smtps - 80, 443, // http/https - 110, 995, // pop3/pop3s - 119, 563, // nntp/nntps - 143, 993, // imap/imaps - 6667, 6697, // irc - 137, 138, 139, // netbios stuff - 53, // dns - 69, // tftp - 902, // vmware - 873, // rsync - 445, // smb - 3389, // rdp +uint16_t common_ports[] = { + 21, // ftp + 990, // ftps + 23, // telnet + 22, // ssh + 25, // smtp + 465, // smtps + 80, // http + 443, // https + 110, // pop3 + 995, // pop3s + 119, // nntp + 563, // nntps + 143, // imap + 993, // imaps + 6667, // irc + 6697, // ircs + 137, // netbios stuff + 138, // more netbios stuff + 139, // even more netbios stuff + 53, // dns + 69, // tftp + 902, // vmware + 873, // rsync + 445, // smb + 3389, // rdp + 0, // marks the end of the list }; -// https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml -char *bad_ranges[] = { - "0.0.0.0/8", "10.0.0.0/8", "100.64.0.0/10", "127.0.0.0/8", - "169.254.0.0/16", "172.16.0.0/12", "192.0.0.0/24", "192.0.0.0/29", - "192.0.0.8/32", "192.0.0.9/32", "192.0.0.170/32", "192.0.0.171/32", - "192.0.2.0/24", "192.31.196.0/24", "192.52.193.0/24", "192.88.99.0/24", - "192.168.0.0/16", "192.175.48.0/24", "198.18.0.0/15", "198.51.100.0/24", - "203.0.113.0/24", "240.0.0.0/4", "255.255.255.255/32"}; -int bad_range_size = sizeof(bad_ranges) / sizeof(char *); - -bool get_range(range_t *res, char *ipnet) { - char *cpy = strdup(ipnet); - char *ip = strtok(cpy, "/"); +bool get_subnet(subnet_t *res, char *sub) { + size_t ipnet_len = strlen(sub); + char ipnet_copy[ipnet_len], *save; + + memcpy(ipnet_copy, sub, ipnet_len + 1); + + char *ip = strtok_r(ipnet_copy, "/", &save); if (NULL == ip) return false; - char *net = strtok(NULL, "/"); + char *net = strtok_r(NULL, "/", &save); if (NULL == net || (res->net = atoi(net)) < 0 || res->net > 255) return false; - char *ip0 = strtok(ip, "."); + char *ip0 = strtok_r(ip, ".", &save); if (NULL == ip0 || (res->ip[0] = atoi(ip0)) < 0 || res->ip[0] > 255) return false; - char *ip1 = strtok(NULL, "."); + char *ip1 = strtok_r(NULL, ".", &save); if (NULL == ip1 || (res->ip[1] = atoi(ip1)) < 0 || res->ip[1] > 255) return false; - char *ip2 = strtok(NULL, "."); + char *ip2 = strtok_r(NULL, ".", &save); if (NULL == ip2 || (res->ip[2] = atoi(ip2)) < 0 || res->ip[2] > 255) return false; - char *ip3 = strtok(NULL, "."); + char *ip3 = strtok_r(NULL, ".", &save); if (NULL == ip3 || (res->ip[3] = atoi(ip3)) < 0 || res->ip[3] > 255) return false; return true; } -int *parse_ports(char *ports, int *size) { - if (eq(ports, "common") || eq(ports, "c") || eq(ports, "top")) { - *size = sizeof(common_ports) / sizeof(int); - return common_ports; +bool subnet_contains(uint32_t ipv4, subnet_t *subnets, size_t subnet_size) { + subnet_t cur = {.net = 0}; + cur.ip[0] = (ipv4 >> 24) & 255; + cur.ip[1] = (ipv4 >> 16) & 255; + cur.ip[2] = (ipv4 >> 8) & 255; + cur.ip[3] = ipv4 & 255; + + for (size_t i = 0; i < subnet_size; i++) { + // subnet is smaller then 8, we should make sure + // that first octet is smaller/equal to the first octet + if (subnets[i].net < 8) + return subnets[i].ip[0] <= cur.ip[0]; + + // subnet is equal to/larger than 8, so we should + // make sure that the first 8 bits match + if (subnets[i].ip[0] != cur.ip[0]) + return false; + + // do the same for all the other octets + if (subnets[i].net < 16) + return subnets[i].ip[1] <= cur.ip[1]; + + if (subnets[i].ip[1] != cur.ip[1]) + return false; + + if (subnets[i].net < 24) + return subnets[i].ip[2] <= cur.ip[2]; + + if (subnets[i].ip[2] != cur.ip[2]) + return false; + + if (subnets[i].net < 32) + return subnets[i].ip[3] <= cur.ip[3]; + + if (subnets[i].ip[3] != cur.ip[3]) + return false; + + return true; } + return false; +} + +uint16_t *parse_ports(char *ports) { + if (eq(ports, "common") || eq(ports, "c") || eq(ports, "top")) + return common_ports; + if (!contains(ports, '-') && !contains(ports, ',')) { int port = atoi(ports); if (port <= 0) { @@ -77,9 +123,9 @@ int *parse_ports(char *ports, int *size) { return NULL; } - int *portlist = malloc(sizeof(int)); - portlist[0] = port; - *size = 1; + uint16_t *portlist = malloc(sizeof(uint16_t) * 2); + portlist[0] = port; + portlist[1] = 0; return portlist; } @@ -99,7 +145,7 @@ int *parse_ports(char *ports, int *size) { int r1 = atoi(rstart); int r2 = atoi(rstop); - if (r1 <= 0 || r2 <= 0) { + if (r1 <= 0 || r2 <= 0 || r1 > UINT16_MAX || r2 > UINT16_MAX) { error("Bad port range numbers"); return NULL; } @@ -109,16 +155,15 @@ int *parse_ports(char *ports, int *size) { return NULL; } - int *portlist = malloc(sizeof(int)); - int portsize = 0; + uint16_t *portlist = malloc(sizeof(int)); + size_t indx = 0; for (; r1 < r2; r1++) { - portlist[portsize] = r1; - portsize++; - portlist = realloc(portlist, sizeof(int) * (portsize + 1)); + portlist[indx++] = r1; + portlist = realloc(portlist, sizeof(int) * (indx + 1)); } - *size = portsize; + portlist[indx] = 0; return portlist; } @@ -129,23 +174,22 @@ int *parse_ports(char *ports, int *size) { return NULL; } - int *portlist = malloc(sizeof(int)); - int portsize = 0; + uint16_t *portlist = malloc(sizeof(int)); + int index = 0; do { int curi = atoi(cur); - if (curi <= 0) { - error("Bad port number"); + if (curi <= 0 || curi > UINT16_MAX) { + error("Bad port number: %s", cur); return NULL; } - portlist[portsize] = atoi(cur); - portsize++; - portlist = realloc(portlist, sizeof(int) * (portsize + 1)); - cur = strtok(NULL, ","); + portlist[index++] = curi; + portlist = realloc(portlist, sizeof(int) * (index + 1)); + cur = strtok(NULL, ","); } while (cur != NULL); - *size = portsize; + portlist[index] = 0; return portlist; } @@ -153,53 +197,8 @@ int *parse_ports(char *ports, int *size) { return NULL; } -void clean_ports(int *ports) { +void clean_ports(uint16_t *ports) { if (ports == common_ports) return; free(ports); } - -bool skip_range(range_t *ranges, uint8_t *cur) { - for (int i = 0; i < bad_range_size; i++) { - if (ranges[i].net == 8 && ranges[i].ip[0] == cur[0]) - return true; - - else if (ranges[i].net < 8 && ranges[i].ip[0] <= cur[0]) - return true; - - else if (ranges[i].net == 16 && ranges[i].ip[0] == cur[0] && - ranges[i].ip[1] == cur[1]) - return true; - - else if (ranges[i].net < 16 && ranges[i].ip[0] == cur[0] && - ranges[i].ip[1] <= cur[1]) - return true; - - else if (ranges[i].net == 24 && ranges[i].ip[0] == cur[0] && - ranges[i].ip[1] == cur[1] && ranges[i].ip[2] == cur[2]) - return true; - - else if (ranges[i].net < 24 && ranges[i].ip[0] == cur[0] && - ranges[i].ip[1] == cur[1] && ranges[i].ip[2] <= cur[2]) - return true; - - else if (ranges[i].net == 32 && ranges[i].ip[0] == cur[0] && - ranges[i].ip[1] == cur[1] && ranges[i].ip[2] == cur[2] && - ranges[i].ip[3] == cur[3]) - return true; - - else if (ranges[i].net < 32 && ranges[i].ip[0] == cur[0] && - ranges[i].ip[1] == cur[1] && ranges[i].ip[2] == cur[2] && - ranges[i].ip[3] <= cur[3]) - return true; - } - - return false; -} - -void urljoin(char *newurl, char *url, char *path) { - if (url[strlen(url) - 1] == '/') - sprintf(newurl, "%s%s", url, path); - else - sprintf(newurl, "%s/%s", url, path); -}