statzone

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

statzone.c (5531B)


      1 /*
      2  * StatZone 1.0.4
      3  * Copyright (c) 2012-2020, Frederic Cambus
      4  * https://www.statdns.com
      5  *
      6  * Created: 2012-02-13
      7  * Last Updated: 2020-06-12
      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 <inttypes.h>
     16 #include <signal.h>
     17 #include <stdlib.h>
     18 #include <stdio.h>
     19 #include <string.h>
     20 #include <sys/stat.h>
     21 #include <sys/time.h>
     22 #include <sys/types.h>
     23 #include <time.h>
     24 
     25 #ifdef HAVE_SECCOMP
     26 #include <sys/prctl.h>
     27 #include <linux/seccomp.h>
     28 #include "seccomp.h"
     29 #endif
     30 
     31 #include <uthash.h>
     32 
     33 #include "compat.h"
     34 #include "config.h"
     35 #include "strtolower.h"
     36 
     37 struct timespec begin, end, elapsed;
     38 
     39 char lineBuffer[LINE_LENGTH_MAX];
     40 
     41 struct results results;
     42 
     43 FILE *zoneFile;
     44 struct stat zoneFileStat;
     45 
     46 int8_t getoptFlag;
     47 
     48 char *intputFile;
     49 
     50 char *domain;
     51 char *previousDomain;
     52 char *rdata;
     53 
     54 struct my_struct {
     55 	char *domain;
     56 	UT_hash_handle hh;
     57 };
     58 
     59 struct my_struct *signedDomains = NULL;
     60 struct my_struct *ds;
     61 struct my_struct *uniqueNS = NULL;
     62 struct my_struct *ns;
     63 
     64 static void
     65 displayUsage()
     66 {
     67 	printf("USAGE: statzone [options] inputfile\n\n" \
     68 	    "Options are:\n\n" \
     69 	    "	-h Display usage\n" \
     70 	    "	-v Display version\n");
     71 }
     72 
     73 static void
     74 displaySummary()
     75 {
     76 	/* Stopping timer */
     77 	clock_gettime(CLOCK_MONOTONIC, &end);
     78 	timespecsub(&end, &begin, &elapsed);
     79 
     80 	/* Print summary */
     81 	fprintf(stderr, "Processed %" PRIu64 " lines in %f seconds.\n",
     82 	    results.processedLines,
     83 	    elapsed.tv_sec + elapsed.tv_nsec / 1E9);
     84 }
     85 
     86 int
     87 main(int argc, char *argv[])
     88 {
     89 	char *token = NULL;
     90 	char *token_lc = NULL;
     91 	int token_count;
     92 
     93 	if (pledge("stdio rpath", NULL) == -1) {
     94 		err(EXIT_FAILURE, "pledge");
     95 	}
     96 
     97 #ifdef HAVE_SECCOMP
     98 	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
     99 		perror("Can't initialize seccomp");
    100 		return EXIT_FAILURE;
    101 	}
    102 
    103 	if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &statzone)) {
    104 		perror("Can't load seccomp filter");
    105 		return EXIT_FAILURE;
    106 	}
    107 #endif
    108 
    109 #ifdef SIGINFO
    110 	signal(SIGINFO, displaySummary);
    111 #endif
    112 
    113 	while ((getoptFlag = getopt(argc, argv, "hv")) != -1) {
    114 		switch (getoptFlag) {
    115 
    116 		case 'h':
    117 			displayUsage();
    118 			return EXIT_SUCCESS;
    119 
    120 		case 'v':
    121 			printf("%s\n", VERSION);
    122 			return EXIT_SUCCESS;
    123 		}
    124 	}
    125 
    126 	if (optind < argc) {
    127 		intputFile = argv[optind];
    128 	} else {
    129 		displayUsage();
    130 		return EXIT_SUCCESS;
    131 	}
    132 
    133 	argc -= optind;
    134 	argv += optind;
    135 
    136 	/* Starting timer */
    137 	clock_gettime(CLOCK_MONOTONIC, &begin);
    138 
    139 	/* Open zone file */
    140 	if (!strcmp(intputFile, "-")) {
    141 		/* Read from standard input */
    142 		zoneFile = stdin;
    143 	} else {
    144 		/* Attempt to read from file */
    145 		if (!(zoneFile = fopen(intputFile, "r"))) {
    146 			perror("Can't open zone file");
    147 			return EXIT_FAILURE;
    148 		}
    149 	}
    150 
    151 	/* Get zone file size */
    152 	if (fstat(fileno(zoneFile), &zoneFileStat)) {
    153 		perror("Can't stat zone file");
    154 		return EXIT_FAILURE;
    155 	}
    156 
    157 	previousDomain = strdup("");
    158 
    159 	while (fgets(lineBuffer, LINE_LENGTH_MAX, zoneFile)) {
    160 		if (*lineBuffer == ';') /* Comments */
    161 			continue;
    162 
    163 		if (*lineBuffer == '$') /* Directives */
    164 			continue;
    165 
    166 		if (*lineBuffer) {
    167 			token_count = 0;
    168 			token = strtok(lineBuffer, " \t");
    169 
    170 			if (token)
    171 				domain = strtolower(token);
    172 
    173 			while (token) {
    174 				if (*token == ';') { /* Comments */
    175 					token = NULL;
    176 					continue;
    177 				}
    178 
    179 				token_lc = strtolower(token);
    180 				if (token_count && !strcmp(token_lc, "nsec")) {
    181 					token = NULL;
    182 					continue;
    183 				}
    184 
    185 				if (token_count && !strcmp(token_lc, "nsec3")) {
    186 					token = NULL;
    187 					continue;
    188 				}
    189 
    190 				if (token_count && !strcmp(token_lc, "rrsig")) {
    191 					token = NULL;
    192 					continue;
    193 				}
    194 
    195 				if (token_count && !strcmp(token_lc, "a")) {
    196 					results.a++;
    197 				}
    198 
    199 				if (token_count && !strcmp(token_lc, "aaaa")) {
    200 					results.aaaa++;
    201 				}
    202 
    203 				if (token_count && !strcmp(token_lc, "ds")) {
    204 					results.ds++;
    205 
    206 					HASH_FIND_STR(signedDomains, domain, ds);
    207 
    208 					if (!ds) {
    209 						ds = malloc(sizeof (struct my_struct));
    210 						ds->domain = strdup(domain);
    211 						HASH_ADD_STR(signedDomains, domain, ds);
    212 					}
    213 				}
    214 
    215 				if (!strcmp(token_lc, "ns")) {
    216 					results.ns++;
    217 
    218 					if (strlen(previousDomain) != strlen(domain) ||
    219 					    strncmp(domain, previousDomain, strlen(domain))) {
    220 						results.domains++;
    221 						free(previousDomain);
    222 						previousDomain = strdup(domain);
    223 						if (!strncmp(domain, "xn--", 4))
    224 							results.idn++;
    225 					}
    226 
    227 					rdata = strtok(NULL, "\n");
    228 
    229 					if (rdata && strchr(rdata, ' '))
    230 						rdata = strtok(NULL, "\n");
    231 
    232 					if (rdata) {
    233 						HASH_FIND_STR(uniqueNS, rdata, ns);
    234 
    235 						if (!ns) {
    236 							ns = malloc(sizeof (struct my_struct));
    237 							ns->domain = strdup(rdata);
    238 							HASH_ADD_STR(uniqueNS, domain, ns);
    239 						}
    240 					}
    241 				}
    242 
    243 				token = strtok(NULL, " \t");
    244 				token_count++;
    245 			}
    246 		}
    247 
    248 		results.processedLines++;
    249 	}
    250 
    251 	/* Don't count origin */
    252 	if (results.domains)
    253 		results.domains--;
    254 
    255 	/* Printing CVS values */
    256 	fprintf(stdout, "---[ CSV values ]--------------------------------------------------------------\n");
    257 	fprintf(stdout, "IPv4 Glue,IPv6 Glue,NS,Unique NS,DS,Signed,IDNs,Domains\n");
    258 	fprintf(stdout, "%" PRIu64 ",", results.a);
    259 	fprintf(stdout, "%" PRIu64 ",", results.aaaa);
    260 	fprintf(stdout, "%" PRIu64 ",", results.ns);
    261 	fprintf(stdout, "%u,", HASH_COUNT(uniqueNS));
    262 	fprintf(stdout, "%" PRIu64 ",", results.ds);
    263 	fprintf(stdout, "%u,", HASH_COUNT(signedDomains));
    264 	fprintf(stdout, "%" PRIu64 ",", results.idn);
    265 	fprintf(stdout, "%" PRIu64 "\n", results.domains);
    266 
    267 	/* Printing results */
    268 	displaySummary();
    269 
    270 	/* Clean up */
    271 	fclose(zoneFile);
    272 
    273 	return EXIT_SUCCESS;
    274 }