diff options
Diffstat (limited to 'src/proone-recon.c')
-rw-r--r-- | src/proone-recon.c | 291 |
1 files changed, 291 insertions, 0 deletions
diff --git a/src/proone-recon.c b/src/proone-recon.c new file mode 100644 index 0000000..d8aa7aa --- /dev/null +++ b/src/proone-recon.c @@ -0,0 +1,291 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <inttypes.h> +#include <ctype.h> +#include <string.h> +#include <errno.h> + +#include <regex.h> +#include <arpa/inet.h> + +#include <mbedtls/entropy.h> +#include <mbedtls/ctr_drbg.h> + +#include "recon.h" +#include "util_ct.h" +#include "util_rt.h" +#include "inet.h" + + +static regex_t re_entry, re_comment, re_empty; + +static void print_help (FILE *o, const char *prog) { + fprintf( + stderr, + "Usage: %s <conf> <port 1> [port 2] ... [port n]\n" + "Options:\n" + "\t<conf>: Path to config file. Pass \"-\" to read stdin\n" + "Config Format: <ENTRY SPEC> <NET SPEC>\n" + "\t<ENTRY SPEC>: \"T\" for target or \"BL\" for blacklist\n" + "\t<NET SPEC>: <IPv4 or IPv6 Address>/<CIDR>\n" + "\tNote:\n" + "\t\t- Parsed case-insensitively\n" + "\t\t- Lines start with \"#\" are ignored\n" + "Config Example:\n" + "\t# Test pool A\n" + "\tT\t192.18.0.0/24\n" + "\t# Test pool B\n" + "\tT\t192.18.1.0/24\n" + "\t# Test pool C\n" + "\tT\tfc00:A::/96\n" + "\t# Test pool D\n" + "\tT\tfc00:B::/96\n" + "\t# My Private Net (IPv4)\n" + "\tBL\t192.168.0.1/24\n" + "\t# My Private Net (IPv6)\n" + "\tBL\tfd00:ABBA::/64\n", + prog); +} + +static int do_parse_conf (FILE *file, prne_recon_param_t *param) { + static const size_t RM_SIZE = 8; + regmatch_t rm[RM_SIZE]; + char line[2][1024]; + size_t nr_line = 0; + char *ent_spec, *ent_addr, *ent_cidr; + uint8_t cidr; + prne_recon_network_t net; + + while (true) { + if (fgets(line[0], sizeof(line[0]), file) == NULL) { + break; + } + nr_line += 1; + + if (regexec(&re_empty, line[0], RM_SIZE, rm, 0) == 0 || + regexec(&re_comment, line[0], RM_SIZE, rm, 0) == 0) + { + continue; + } + if (regexec(&re_entry, line[0], RM_SIZE, rm, 0) != 0) { + goto INV_LINE; + } + + strcpy(line[1], line[0]); + prne_memzero(&net, sizeof(prne_recon_network_t)); + + line[1][rm[2].rm_eo] = 0; // terminate ENTRY SPEC + line[1][rm[4].rm_eo] = 0; // terminate address + line[1][rm[5].rm_eo] = 0; // terminate CIDR + ent_spec = line[1] + rm[2].rm_so; + ent_addr = line[1] + rm[4].rm_so; + ent_cidr = line[1] + rm[5].rm_so; + prne_transstr(ent_spec, toupper); + prne_transstr(ent_addr, tolower); + + if (inet_pton(AF_INET6, ent_addr, net.addr.addr)) { + net.addr.ver = PRNE_IPV_6; + } + else if (inet_pton(AF_INET, ent_addr, net.addr.addr)) { + net.addr.ver = PRNE_IPV_4; + } + else { + goto INV_LINE; + } + + if (sscanf(ent_cidr, "%"SCNu8, &cidr) != 1 || + (net.addr.ver == PRNE_IPV_6 && cidr > 128) || + (net.addr.ver == PRNE_IPV_4 && cidr > 32)) + { + goto INV_LINE; + } + prne_netmask_from_cidr(net.mask, cidr); + + if (strcmp(ent_spec, "T") == 0) { + prne_assert(prne_alloc_recon_param( + param, + param->blist.cnt, + param->target.cnt + 1, + param->ports.cnt)); + param->target.arr[param->target.cnt - 1] = net; + } + else if (strcmp(ent_spec, "BL") == 0) { + prne_assert(prne_alloc_recon_param( + param, + param->blist.cnt + 1, + param->target.cnt, + param->ports.cnt)); + param->blist.arr[param->blist.cnt - 1] = net; + } + else { + abort(); + } + } + + return 0; +INV_LINE: + fprintf( + stderr, + "*** Invalid entry at line %zu: %s\n", + nr_line, + line[0]); + return 2; +} + +static void evt_cb (const prne_net_endpoint_t *ep) { + char addr_str[prne_op_max(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)]; + const char *fmt; + + switch (ep->addr.ver) { + case PRNE_IPV_4: + inet_ntop(AF_INET, ep->addr.addr, addr_str, sizeof(addr_str)); + fmt = "%s:%"PRIu16"\n"; + break; + case PRNE_IPV_6: + inet_ntop(AF_INET6, ep->addr.addr, addr_str, sizeof(addr_str)); + fmt = "[%s]:%"PRIu16"\n"; + break; + default: abort(); + } + + printf(fmt, addr_str, ep->port); +} + +int main (const int argc, const char **args) { + int ret = 0; + prne_recon_param_t param; + FILE *conf_f; + bool own_conf_f = false; + prne_worker_t wkr; + prne_recon_t *recon; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + sigset_t ss_term; + int caught_sig; + + prne_assert(regcomp( + &re_entry, + // ^(\s+)?(T|BL)(\s+)?([0-9a-f:.]+)\/([0-9]{1,3})(\s+)?(#.*)?$ + // number of captures: 7 + // significant groups: 2, 4, 5 + "^(\\s+)?(T|BL)(\\s+)?([0-9a-f:.]+)\\/([0-9]{1,3})(\\s+)?(#.*)?$", + REG_EXTENDED | REG_ICASE) == 0); + prne_assert(regcomp( + &re_comment, + // ^(\s+)?#.*$ + "^(\\s+)?#.*$", + REG_EXTENDED | REG_ICASE) == 0); + prne_assert(regcomp( + &re_empty, + // ^(\s+)?$ + "^(\\s+)?$", + REG_EXTENDED | REG_ICASE) == 0); + + prne_init_recon_param(¶m); + param.evt_cb = evt_cb; + prne_init_worker(&wkr); + mbedtls_entropy_init(&entropy); + mbedtls_ctr_drbg_init(&ctr_drbg); + sigemptyset(&ss_term); + sigaddset(&ss_term, SIGTERM); + sigaddset(&ss_term, SIGINT); + + // parse args + if (argc < 3) { + print_help(stderr, args[0]); + ret = 2; + goto END; + } + + if (prne_nstreq(args[1], "-")) { + conf_f = stdin; + } + else { + own_conf_f = true; + conf_f = fopen(args[1], "r"); + + if (conf_f == NULL) { + perror(args[1]); + ret = 1; + goto END; + } + } + + for (int i = 2; i < argc; i += 1) { + uint16_t port; + + if (sscanf(args[i], "%"SCNu16, &port) != 1 || port == 0) { + fprintf(stderr, "*** %s: invalid port value\n", args[i]); + ret = 2; + goto END; + } + prne_assert(prne_alloc_recon_param( + ¶m, + param.blist.cnt, + param.target.cnt, + param.ports.cnt + 1)); + param.ports.arr[param.ports.cnt - 1] = port; + } + + prne_assert(pth_init()); + + // try-catch init + prne_assert(mbedtls_ctr_drbg_seed( + &ctr_drbg, + mbedtls_entropy_func, + &entropy, + NULL, + 0) == 0); + + // parse conf + ret = do_parse_conf(conf_f, ¶m); + if (ret != 0) { + goto END; + } + if (param.target.cnt == 0) { + fprintf(stderr, "*** No target network configured\n"); + ret = 2; + goto END; + } + + // alloc recon + recon = prne_alloc_recon( + &wkr, + &ctr_drbg, + prne_own_recon_param(¶m, false)); + if (recon == NULL) { + perror("prne_alloc_recon()"); + ret = 2; + goto END; + } + wkr.pth = pth_spawn(PTH_ATTR_DEFAULT, wkr.entry, wkr.ctx); + prne_assert(wkr.pth != NULL); + + // wait for termination + prne_assert(sigprocmask(SIG_BLOCK, &ss_term, NULL) == 0); + pth_sigwait(&ss_term, &caught_sig); + sigprocmask(SIG_UNBLOCK, &ss_term, NULL); + + // fin worker + wkr.fin(wkr.ctx); + pth_join(wkr.pth, NULL); + wkr.free_ctx(wkr.ctx); + +END: + // clean up + regfree(&re_entry); + regfree(&re_comment); + regfree(&re_empty); + prne_free_recon_param(¶m); + if (own_conf_f && conf_f != NULL) { + fclose(conf_f); + } + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + + pth_kill(); + + return ret; +} |