statzone

DNS zone file analyzer targeted at TLD zones
Log | Files | Refs | README | LICENSE

statzone.cpp (4752B)


      1 /*
      2  * StatZone 1.1.0
      3  * Copyright (c) 2012-2021, Frederic Cambus
      4  * https://www.statdns.com
      5  *
      6  * Created: 2012-02-13
      7  * Last Updated: 2021-04-03
      8  *
      9  * StatZone is released under the BSD 2-Clause license
     10  * See LICENSE file for details.
     11  */
     12 
     13 #include <err.h>
     14 #include <getopt.h>
     15 #include <string.h>
     16 #include <sys/stat.h>
     17 
     18 #include <chrono>
     19 #include <csignal>
     20 #include <iostream>
     21 #include <string>
     22 #include <unordered_set>
     23 
     24 #ifdef HAVE_SECCOMP
     25 #include <sys/prctl.h>
     26 #include <linux/seccomp.h>
     27 #include "seccomp.h"
     28 #endif
     29 
     30 #include "config.hpp"
     31 #include "strtolower.hpp"
     32 
     33 std::chrono::steady_clock::time_point begin, current, elapsed;
     34 struct results results;
     35 
     36 static void
     37 usage()
     38 {
     39 	printf("statzone [-hv] zonefile\n\n" \
     40 	    "The options are as follows:\n\n" \
     41 	    "	-h	Display usage.\n" \
     42 	    "	-v	Display version.\n");
     43 }
     44 
     45 static void
     46 summary()
     47 {
     48 	/* Get current timer value */
     49 	current = std::chrono::steady_clock::now();
     50 
     51 	/* Print summary */
     52 	std::cerr << "Processed " << results.processedLines << " lines in ";
     53 	std::cerr << std::chrono::duration_cast<std::chrono::microseconds>(current - begin).count() / 1E6;
     54 	std::cerr << " seconds." << std::endl;
     55 }
     56 
     57 #ifdef SIGINFO
     58 void
     59 siginfo_handler(int signum)
     60 {
     61 	summary();
     62 }
     63 #endif
     64 
     65 int
     66 main(int argc, char *argv[])
     67 {
     68 	struct stat zonefile_stat;
     69 
     70 	std::unordered_set<std::string> signed_domains;
     71 	std::unordered_set<std::string> unique_ns;
     72 
     73 	int opt, token_count;
     74 
     75 	char linebuffer[LINE_LENGTH_MAX];
     76 	char *input;
     77 	std::string domain, previous_domain;
     78 	char *rdata, *token = nullptr, *token_lc = nullptr;
     79 
     80 	FILE *zonefile;
     81 
     82 #ifdef HAVE_SECCOMP
     83 	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
     84 		perror("Can't initialize seccomp");
     85 		return EXIT_FAILURE;
     86 	}
     87 
     88 	if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &statzone)) {
     89 		perror("Can't load seccomp filter");
     90 		return EXIT_FAILURE;
     91 	}
     92 #endif
     93 
     94 #ifdef SIGINFO
     95 	signal(SIGINFO, siginfo_handler);
     96 #endif
     97 
     98 	while ((opt = getopt(argc, argv, "hv")) != -1) {
     99 		switch (opt) {
    100 
    101 		case 'h':
    102 			usage();
    103 			return EXIT_SUCCESS;
    104 
    105 		case 'v':
    106 			printf("%s\n", VERSION);
    107 			return EXIT_SUCCESS;
    108 		}
    109 	}
    110 
    111 	if (optind < argc) {
    112 		input = argv[optind];
    113 	} else {
    114 		usage();
    115 		return EXIT_SUCCESS;
    116 	}
    117 
    118 	/* Starting timer */
    119 	begin = std::chrono::steady_clock::now();
    120 
    121 	/* Open zone file */
    122 	if (!strcmp(input, "-")) {
    123 		/* Read from standard input */
    124 		zonefile = stdin;
    125 	} else {
    126 		/* Attempt to read from file */
    127 		if (!(zonefile = fopen(input, "r"))) {
    128 			perror("Can't open zone file");
    129 			return EXIT_FAILURE;
    130 		}
    131 	}
    132 
    133 	/* Get zone file size */
    134 	if (fstat(fileno(zonefile), &zonefile_stat)) {
    135 		perror("Can't stat zone file");
    136 		return EXIT_FAILURE;
    137 	}
    138 
    139 	while (fgets(linebuffer, LINE_LENGTH_MAX, zonefile)) {
    140 		if (!*linebuffer)
    141 			continue;
    142 
    143 		if (*linebuffer == ';') /* Comments */
    144 			continue;
    145 
    146 		if (*linebuffer == '$') /* Directives */
    147 			continue;
    148 
    149 		token_count = 0;
    150 		token = strtok(linebuffer, " \t");
    151 
    152 		if (token)
    153 			domain = strtolower(token);
    154 
    155 		while (token) {
    156 			if (*token == ';') { /* Comments */
    157 				token = nullptr;
    158 				continue;
    159 			}
    160 
    161 			token_lc = strtolower(token);
    162 			if (token_count && !strcmp(token_lc, "nsec")) {
    163 				token = nullptr;
    164 				continue;
    165 			}
    166 
    167 			if (token_count && !strcmp(token_lc, "nsec3")) {
    168 				token = nullptr;
    169 				continue;
    170 			}
    171 
    172 			if (token_count && !strcmp(token_lc, "rrsig")) {
    173 				token = nullptr;
    174 				continue;
    175 			}
    176 
    177 			if (token_count && !strcmp(token_lc, "a"))
    178 				results.a++;
    179 
    180 			if (token_count && !strcmp(token_lc, "aaaa"))
    181 				results.aaaa++;
    182 
    183 			if (token_count && !strcmp(token_lc, "ds")) {
    184 				results.ds++;
    185 
    186 				signed_domains.insert(domain);
    187 			}
    188 
    189 			if (!strcmp(token_lc, "ns")) {
    190 				results.ns++;
    191 
    192 				if (domain.compare(previous_domain)) {
    193 					results.domains++;
    194 
    195 					previous_domain = domain;
    196 
    197 					if (!domain.compare(0, 4, "xn--"))
    198 						results.idn++;
    199 				}
    200 
    201 				rdata = strtok(nullptr, "\n");
    202 
    203 				if (rdata && strchr(rdata, ' '))
    204 					rdata = strtok(nullptr, "\n");
    205 
    206 				if (rdata)
    207 					unique_ns.insert(rdata);
    208 			}
    209 
    210 			token = strtok(nullptr, " \t");
    211 			token_count++;
    212 		}
    213 
    214 		results.processedLines++;
    215 	}
    216 
    217 	/* Don't count origin */
    218 	if (results.domains)
    219 		results.domains--;
    220 
    221 	/* Printing CVS values */
    222 	std::cout << "---[ CSV values ]--------------------------------------------------------------" << std::endl;
    223 	std::cout << "IPv4 Glue,IPv6 Glue,NS,Unique NS,DS,Signed,IDNs,Domains" << std::endl;
    224 	std::cout << results.a << ",";
    225 	std::cout << results.aaaa << ",";
    226 	std::cout << results.ns << ",";
    227 	std::cout << unique_ns.size() << ",";
    228 	std::cout << results.ds << ",";
    229 	std::cout << signed_domains.size() << ",";
    230 	std::cout << results.idn << ",";
    231 	std::cout << results.domains << std::endl;
    232 
    233 	/* Printing results */
    234 	summary();
    235 
    236 	/* Clean up */
    237 	fclose(zonefile);
    238 
    239 	return EXIT_SUCCESS;
    240 }