statzone

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

statzone.cpp (4611B)


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