statzone

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

statzone.cpp (4624B)


      1 /*
      2  * StatZone 1.1.1
      3  * Copyright (c) 2012-2022, Frederic Cambus
      4  * https://www.statdns.com
      5  *
      6  * Created: 2012-02-13
      7  * Last Updated: 2022-02-25
      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 <getopt.h>
     16 #include <string.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 	std::unordered_set<std::string> signed_domains;
     69 	std::unordered_set<std::string> unique_ns;
     70 
     71 	int opt, token_count;
     72 
     73 	char *linebuffer = NULL;
     74 	size_t linesize = 0;
     75 
     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 	while (getline(&linebuffer, &linesize, zonefile) != -1) {
    134 		if (!*linebuffer)
    135 			continue;
    136 
    137 		if (*linebuffer == ';') /* Comments */
    138 			continue;
    139 
    140 		if (*linebuffer == '$') /* Directives */
    141 			continue;
    142 
    143 		token_count = 0;
    144 		token = strtok(linebuffer, " \t");
    145 
    146 		if (token)
    147 			domain = strtolower(token);
    148 
    149 		while (token) {
    150 			if (*token == ';') { /* Comments */
    151 				token = nullptr;
    152 				continue;
    153 			}
    154 
    155 			token_lc = strtolower(token);
    156 			if (token_count && !strcmp(token_lc, "nsec")) {
    157 				token = nullptr;
    158 				continue;
    159 			}
    160 
    161 			if (token_count && !strcmp(token_lc, "nsec3")) {
    162 				token = nullptr;
    163 				continue;
    164 			}
    165 
    166 			if (token_count && !strcmp(token_lc, "rrsig")) {
    167 				token = nullptr;
    168 				continue;
    169 			}
    170 
    171 			if (token_count && !strcmp(token_lc, "a"))
    172 				results.a++;
    173 
    174 			if (token_count && !strcmp(token_lc, "aaaa"))
    175 				results.aaaa++;
    176 
    177 			if (token_count && !strcmp(token_lc, "ds")) {
    178 				results.ds++;
    179 
    180 				signed_domains.insert(domain);
    181 			}
    182 
    183 			if (!strcmp(token_lc, "ns")) {
    184 				results.ns++;
    185 
    186 				if (domain.compare(previous_domain)) {
    187 					results.domains++;
    188 
    189 					previous_domain = domain;
    190 
    191 					if (!domain.compare(0, 4, "xn--"))
    192 						results.idn++;
    193 				}
    194 
    195 				rdata = strtok(nullptr, "\n");
    196 
    197 				if (rdata && strchr(rdata, ' '))
    198 					rdata = strtok(nullptr, "\n");
    199 
    200 				if (rdata)
    201 					unique_ns.insert(rdata);
    202 			}
    203 
    204 			token = strtok(nullptr, " \t");
    205 			token_count++;
    206 		}
    207 
    208 		results.processedLines++;
    209 	}
    210 
    211 	/* Don't count origin */
    212 	if (results.domains)
    213 		results.domains--;
    214 
    215 	/* Printing CVS values */
    216 	std::cout << "---[ CSV values ]--------------------------------------------------------------" << std::endl;
    217 	std::cout << "IPv4 Glue,IPv6 Glue,NS,Unique NS,DS,Signed,IDNs,Domains" << std::endl;
    218 	std::cout << results.a << ",";
    219 	std::cout << results.aaaa << ",";
    220 	std::cout << results.ns << ",";
    221 	std::cout << unique_ns.size() << ",";
    222 	std::cout << results.ds << ",";
    223 	std::cout << signed_domains.size() << ",";
    224 	std::cout << results.idn << ",";
    225 	std::cout << results.domains << std::endl;
    226 
    227 	/* Printing results */
    228 	summary();
    229 
    230 	/* Clean up */
    231 	free(linebuffer);
    232 	fclose(zonefile);
    233 
    234 	return EXIT_SUCCESS;
    235 }