Commit 58f5f41e authored by Mark Andrews's avatar Mark Andrews

Merge branch '16-return-json-instead-of-plain-text' into 'master'

Resolve "Return json instead of plain text"

Closes #16

See merge request !11
parents a9697646 f0750073
Pipeline #5339 passed with stages
in 1 minute and 11 seconds
...@@ -6,6 +6,7 @@ AC_CONFIG_SRCDIR([genreport.c]) ...@@ -6,6 +6,7 @@ AC_CONFIG_SRCDIR([genreport.c])
AC_CONFIG_HEADER([config.h]) AC_CONFIG_HEADER([config.h])
AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([Makefile])
AC_CANONICAL_HOST AC_CANONICAL_HOST
AC_CHECK_FUNCS(strlcpy strlcat)
AC_SEARCH_LIBS([res_mkquery],[resolv bind]) AC_SEARCH_LIBS([res_mkquery],[resolv bind])
AC_SEARCH_LIBS([res_9_mkquery],[resolv bind]) AC_SEARCH_LIBS([res_9_mkquery],[resolv bind])
AC_SEARCH_LIBS([__res_mkquery],[resolv bind]) AC_SEARCH_LIBS([__res_mkquery],[resolv bind])
......
...@@ -50,6 +50,9 @@ ...@@ -50,6 +50,9 @@
#define HMAC_CTX_free(ptr) HMAC_CTX_cleanup(ptr) #define HMAC_CTX_free(ptr) HMAC_CTX_cleanup(ptr)
#endif #endif
#ifndef HAVE_STRLCPY
#define strlcpy(dst, src, len) snprintf(dst, len, "%s", src)
#endif
#ifndef FD_COPY #ifndef FD_COPY
#define FD_COPY(x, y) memmove(y, x, sizeof(*x)) #define FD_COPY(x, y) memmove(y, x, sizeof(*x))
...@@ -119,7 +122,10 @@ static int serial = 0; ...@@ -119,7 +122,10 @@ static int serial = 0;
static int printnsid = 0; static int printnsid = 0;
static int recursive = 0; static int recursive = 0;
static long long sent; static long long sent;
static int json = 0;
static char *jdata = NULL;
static size_t jdata_len = 0;
#ifdef HAVE_RES_GETSERVERS #ifdef HAVE_RES_GETSERVERS
static union res_sockaddr_union servers[10]; static union res_sockaddr_union servers[10];
...@@ -672,6 +678,91 @@ connecttoserver(struct workitem *item); ...@@ -672,6 +678,91 @@ connecttoserver(struct workitem *item);
static void static void
report(struct summary *summary); report(struct summary *summary);
void
jsonadd(char **json, const char *str, size_t *len) {
if (*json == NULL) {
*json = calloc(1, 10240);
if (*json == NULL)
return;
*len = 10240;
}
if (strlen(*json) + strlen(str) + 1 > *len) {
char *tmp = realloc(*json, *len + 10240);
if (tmp == NULL)
return;
*json = tmp;
*len += 10240;
}
#ifdef HAVE_STRLCAT
strlcat(*json, str, *len);
#else
strncat(*json, str, *len - strlen(*json) - 1);
#endif
}
void
jsonsafe(const char *str, char *safe, size_t len) {
char c;
while (len > 1) {
switch ((c = *str++)) {
case '\\':
strlcpy(safe, "\\\\", len);
len -= strlen(safe);
safe += strlen(safe);
break;
case '"':
strlcpy(safe, "\\\"", len);
len -= strlen(safe);
safe += strlen(safe);
break;
case '\b':
strlcpy(safe, "\\b", len);
len -= strlen(safe);
safe += strlen(safe);
break;
case '\f':
strlcpy(safe, "\\f", len);
len -= strlen(safe);
safe += strlen(safe);
break;
case '\n':
strlcpy(safe, "\\n", len);
len -= strlen(safe);
safe += strlen(safe);
break;
case '\r':
strlcpy(safe, "\\r", len);
len -= strlen(safe);
safe += strlen(safe);
break;
case '\t':
strlcpy(safe, "\\t", len);
len -= strlen(safe);
safe += strlen(safe);
break;
case '\0':
*safe = c;
len = 0;
break;
default:
if (c > 0 && c < ' ') {
snprintf(safe, len, "\\u%04x", (c & 0xff));
len -= strlen(safe);
safe += strlen(safe);
break;
} else {
*safe++ = c;
*safe = '\0';
len--;
}
break;
}
}
if (len > 0)
*safe = '\0';
}
static int static int
storage_equal(struct sockaddr_storage *s1, struct sockaddr_storage *s2) { storage_equal(struct sockaddr_storage *s1, struct sockaddr_storage *s2) {
struct sockaddr_in *sin1, *sin2; struct sockaddr_in *sin1, *sin2;
...@@ -737,6 +828,34 @@ freesummary(struct summary *summary) { ...@@ -737,6 +828,34 @@ freesummary(struct summary *summary) {
free(summary); free(summary);
} }
static void
emiterr(const char *zone, const char *ns, const char *str) {
char safe[1024];
if (json) {
if (jdata)
jsonadd(&jdata, ", ", &jdata_len);
jsonsafe(zone[0] ? zone : ".", safe, sizeof(safe));
jsonadd(&jdata, "{ \"zone\": \"", &jdata_len);
jsonadd(&jdata, safe, &jdata_len);
jsonadd(&jdata, "\"", &jdata_len);
if (ns) {
jsonsafe(ns, safe, sizeof(safe));
jsonadd(&jdata, ", \"servername\": \"", &jdata_len);
jsonadd(&jdata, safe, &jdata_len);
jsonadd(&jdata, "\"", &jdata_len);
}
jsonadd(&jdata, ", \"error\": \"", &jdata_len);
jsonadd(&jdata, str, &jdata_len);
jsonadd(&jdata, "\" }", &jdata_len);
} else {
printf("%s.%s%s: %s\n",
zone, ns ? " " : "", ns ? ns : "", str);
}
}
/* /*
* Generate a report line. * Generate a report line.
*/ */
...@@ -745,47 +864,45 @@ printandfree(struct summary *summary) { ...@@ -745,47 +864,45 @@ printandfree(struct summary *summary) {
struct sockaddr_in *s = (struct sockaddr_in *)&summary->storage; struct sockaddr_in *s = (struct sockaddr_in *)&summary->storage;
struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)&summary->storage;; struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)&summary->storage;;
char addrbuf[64]; char addrbuf[64];
char buf[2048];
char safe[1024];
void *addr; void *addr;
unsigned int i; unsigned int i;
int x; int x;
if ((summary->type == ns_t_a || summary->type == ns_t_aaaa) && if ((summary->type == ns_t_a || summary->type == ns_t_aaaa) &&
summary->nodataa && summary->nodataaaaa) { summary->nodataa && summary->nodataaaaa) {
printf("%s. %s: no address records found\n", emiterr(summary->zone, summary->ns, "no address records found");
summary->zone, summary->ns);
freesummary(summary); freesummary(summary);
return; return;
} }
if ((summary->type == ns_t_a || summary->type == ns_t_aaaa) && if ((summary->type == ns_t_a || summary->type == ns_t_aaaa) &&
summary->nxdomaina && summary->nxdomainaaaa) { summary->nxdomaina && summary->nxdomainaaaa) {
printf("%s. %s: no address records found (NXDOMAIN)\n", emiterr(summary->zone, summary->ns, "no address records found (NXDOMAIN)");
summary->zone, summary->ns);
freesummary(summary); freesummary(summary);
return; return;
} }
if ((summary->type == ns_t_a || summary->type == ns_t_aaaa) && if ((summary->type == ns_t_a || summary->type == ns_t_aaaa) &&
summary->faileda && summary->failedaaaa) { summary->faileda && summary->failedaaaa) {
printf("%s. %s: address lookups failed\n", emiterr(summary->zone, summary->ns, "address lookups failed");
summary->zone, summary->ns);
freesummary(summary); freesummary(summary);
return; return;
} }
if ((summary->type == ns_t_a || summary->type == ns_t_aaaa) && if ((summary->type == ns_t_a || summary->type == ns_t_aaaa) &&
(summary->cnamea || summary->cnameaaaa)) { (summary->cnamea || summary->cnameaaaa)) {
printf("%s. %s: nameserver is a CNAME%s%s%s\n", snprintf(buf, sizeof(buf), "nameserver is a CNAME%s%s\n",
summary->zone, summary->ns,
summary->targetok ? " to '" : "",
summary->targetok ? summary->target : "", summary->targetok ? summary->target : "",
summary->targetok ? "'" : ""); summary->targetok ? "'" : "");
emiterr(summary->zone, summary->ns, buf);
freesummary(summary); freesummary(summary);
return; return;
} }
if ((summary->type == ns_t_ns) && summary->cname) { if ((summary->type == ns_t_ns) && summary->cname) {
printf("%s.: zone is a CNAME\n", summary->zone); emiterr(summary->zone, summary->ns, "zone is a CNAME");
freesummary(summary); freesummary(summary);
return; return;
} }
...@@ -795,35 +912,30 @@ printandfree(struct summary *summary) { ...@@ -795,35 +912,30 @@ printandfree(struct summary *summary) {
*/ */
if (summary->type == ns_t_a && if (summary->type == ns_t_a &&
summary->nodataa && summary->failedaaaa) { summary->nodataa && summary->failedaaaa) {
printf("%s. %s:", summary->zone, summary->ns); emiterr(summary->zone, summary->ns, "AAAA lookup failed");
printf(" AAAAA lookup failed\n");
freesummary(summary); freesummary(summary);
return; return;
} }
if (summary->type == ns_t_aaaa && if (summary->type == ns_t_aaaa &&
summary->nodataaaaa && summary->faileda) { summary->nodataaaaa && summary->faileda) {
printf("%s. %s:", summary->zone, summary->ns); emiterr(summary->zone, summary->ns, "A lookup failed");
printf(" A lookup failed\n");
freesummary(summary); freesummary(summary);
return; return;
} }
if (summary->type == ns_t_a && if (summary->type == ns_t_a &&
summary->faileda && summary->nxdomainaaaa) { summary->faileda && summary->nxdomainaaaa) {
printf("%s. %s:", summary->zone, summary->ns); emiterr(summary->zone, summary->ns, "AAAA nxdomain");
printf(" AAAAA nxdomain\n");
freesummary(summary); freesummary(summary);
return; return;
} }
if (summary->type == ns_t_aaaa && if (summary->type == ns_t_aaaa &&
summary->failedaaaa && summary->nxdomaina) { summary->failedaaaa && summary->nxdomaina) {
printf("%s. %s:", summary->zone, summary->ns); emiterr(summary->zone, summary->ns, "A nxdomain");
printf(" A nxdomain\n");
freesummary(summary); freesummary(summary);
return; return;
} }
if (summary->type == ns_t_ns && summary->nodata) { if (summary->type == ns_t_ns && summary->nodata) {
printf("%s.:", summary->zone); emiterr(summary->zone, NULL, "no NS records found");
printf(" no NS records found\n");
freesummary(summary); freesummary(summary);
return; return;
} }
...@@ -834,33 +946,27 @@ printandfree(struct summary *summary) { ...@@ -834,33 +946,27 @@ printandfree(struct summary *summary) {
} }
if (summary->type != 0 && summary->nxdomain) { if (summary->type != 0 && summary->nxdomain) {
if (summary->type == ns_t_ns) { if (summary->type == ns_t_a)
printf("%s.: NS nxdomain\n", summary->zone); emiterr(summary->zone, NULL, "NS nxdomain");
freesummary(summary); if (summary->type == ns_t_a)
return; emiterr(summary->zone, summary->ns, "A nxdomain");
} if (summary->type == ns_t_aaaa)
printf("%s. %s:", summary->zone, summary->ns); emiterr(summary->zone, summary->ns, "AAAA nxdomain");
if (summary->type == ns_t_a) printf(" A");
if (summary->type == ns_t_aaaa) printf(" AAAA");
printf(" nxdomain\n");
freesummary(summary); freesummary(summary);
return; return;
} }
if (summary->type == ns_t_a) { if (summary->type == ns_t_a) {
printf("%s. %s:", summary->zone, summary->ns); emiterr(summary->zone, summary->ns, "A lookup failed");
printf(" A lookup failed\n");
freesummary(summary); freesummary(summary);
return; return;
} }
if (summary->type == ns_t_aaaa) { if (summary->type == ns_t_aaaa) {
printf("%s. %s:", summary->zone, summary->ns); emiterr(summary->zone, summary->ns, "AAAA lookup failed");
printf(" AAAA lookup failed\n");
freesummary(summary); freesummary(summary);
return; return;
} }
if (summary->type == ns_t_ns) { if (summary->type == ns_t_ns) {
printf("%s.:", summary->zone); emiterr(summary->zone, NULL, "NS lookup failed");
printf(" NS lookup failed\n");
freesummary(summary); freesummary(summary);
return; return;
} }
...@@ -887,6 +993,68 @@ printandfree(struct summary *summary) { ...@@ -887,6 +993,68 @@ printandfree(struct summary *summary) {
addrbuf, sizeof(addrbuf)); addrbuf, sizeof(addrbuf));
x = -1; x = -1;
if (json) {
int first = 1;
if (jdata)
jsonadd(&jdata, ",\n", &jdata_len);
jsonsafe(summary->zone[0] ? summary->zone : ".", safe, sizeof(safe));
snprintf(buf, sizeof(buf), "{ \"zone\": \"%s\"", safe);
jsonadd(&jdata, buf, &jdata_len);
snprintf(buf, sizeof(buf), ", \"address\": \"%s\"", addrbuf);
jsonadd(&jdata, buf, &jdata_len);
if (strcmp(summary->ns, ".") != 0) {
jsonsafe(summary->ns, safe, sizeof(safe));
snprintf(buf, sizeof(buf), ", \"servername\": \"%s\"", safe);
jsonadd(&jdata, buf, &jdata_len);
}
if (allok && summary->allok) {
jsonadd(&jdata, ", \"summary\": \"all ok\" }", &jdata_len);
freesummary(summary);
return;
}
jsonadd(&jdata, ", \"tests\": { ", &jdata_len);
for (i = 0; i < sizeof(opts)/sizeof(opts[0]); i++) {
if ((opts[i].what & what) == 0)
continue;
if (summary->results[i][0] == 0)
strncat(summary->results[i], "skipped", 100);
first = 0;
if (strcmp(opts[i].name, "do") == 0)
x = i;
if (strcmp(opts[i].name, "ednstcp") == 0 && x != -1 &&
(!badtag || (strcmp(summary->results[x], "ok") != 0 &&
strncmp(summary->results[x], "ok,", 3) != 0)))
{
if (!first)
jsonadd(&jdata, ", ", &jdata_len);
snprintf(buf, sizeof(buf), "\"signed\": \"%s%s\"",
summary->results[x],
summary->seenrrsig ? ",yes" : "");
jsonadd(&jdata, buf, &jdata_len);
first = 0;
}
if (badtag) {
if (strcmp(summary->results[i], "ok") == 0 ||
strncmp(summary->results[i], "ok,", 3) == 0)
continue;
}
if (!first)
jsonadd(&jdata, ", ", &jdata_len);
snprintf(buf, sizeof(buf), "\"%s\": \"%s\"", opts[i].name,
summary->results[i]);
jsonadd(&jdata, buf, &jdata_len);
first = 0;
}
jsonadd(&jdata, " }", &jdata_len);
if (printnsid && summary->nsidlen != 0U) {
jsonsafe(summary->nsid, safe, sizeof(safe));
snprintf(buf, sizeof(buf), ", \"nsid\": \"%s\"", safe);
jsonadd(&jdata, buf, &jdata_len);
}
jsonadd(&jdata, " }", &jdata_len);
freesummary(summary);
return;
}
printf("%s. @%s (%s.):", summary->zone, addrbuf, summary->ns); printf("%s. @%s (%s.):", summary->zone, addrbuf, summary->ns);
if (allok && summary->allok) if (allok && summary->allok)
printf(" all ok"); printf(" all ok");
...@@ -3463,7 +3631,7 @@ main(int argc, char **argv) { ...@@ -3463,7 +3631,7 @@ main(int argc, char **argv) {
int on = 1; int on = 1;
int port = 53; int port = 53;
while ((n = getopt(argc, argv, "46abBcdDeEfi:I:Lm:nopP:r:RstT")) != -1) { while ((n = getopt(argc, argv, "46abBcdDeEfi:I:jLm:nopP:r:RstT")) != -1) {
switch (n) { switch (n) {
case '4': ipv4only = 1; ipv6only = 0; break; case '4': ipv4only = 1; ipv6only = 0; break;
case '6': ipv6only = 1; ipv4only = 0; break; case '6': ipv6only = 1; ipv4only = 0; break;
...@@ -3496,6 +3664,9 @@ main(int argc, char **argv) { ...@@ -3496,6 +3664,9 @@ main(int argc, char **argv) {
} }
what = EXPL; what = EXPL;
break; break;
case 'j':
json = 1;
break;
case 'L': case 'L':
for (i = 0; i < sizeof(opts)/sizeof(opts[0]); i++) { for (i = 0; i < sizeof(opts)/sizeof(opts[0]); i++) {
printf("%s", opts[i].name); printf("%s", opts[i].name);
...@@ -3528,7 +3699,7 @@ main(int argc, char **argv) { ...@@ -3528,7 +3699,7 @@ main(int argc, char **argv) {
} }
exit (0); exit (0);
default: default:
printf("usage: genreport [-46abBcdeEfLnopstT] " printf("usage: genreport [-46abBcdeEfjLnopstT] "
"[-i test] [-I test] [-m maxoutstanding] " "[-i test] [-I test] [-m maxoutstanding] "
"[-r server]\n"); "[-r server]\n");
printf("\t-4: IPv4 servers only\n"); printf("\t-4: IPv4 servers only\n");
...@@ -3544,6 +3715,7 @@ main(int argc, char **argv) { ...@@ -3544,6 +3715,7 @@ main(int argc, char **argv) {
printf("\t-f: add full mode tests (incl edns)\n"); printf("\t-f: add full mode tests (incl edns)\n");
printf("\t-i: individual test\n"); printf("\t-i: individual test\n");
printf("\t-I: remove individual test\n"); printf("\t-I: remove individual test\n");
printf("\t-j: emit json\n");
printf("\t-L: list tests and their grouping\n"); printf("\t-L: list tests and their grouping\n");
printf("\t-m: set maxoutstanding\n"); printf("\t-m: set maxoutstanding\n");
printf("\t-n: printnsid\n"); printf("\t-n: printnsid\n");
...@@ -3868,4 +4040,10 @@ main(int argc, char **argv) { ...@@ -3868,4 +4040,10 @@ main(int argc, char **argv) {
if (eof && item == NULL) if (eof && item == NULL)
done = 1; done = 1;
} while (!done); } while (!done);
if (json) {
printf("{\n%s%s%s\n}\n",
jdata ? "\"data\": [ " : "",
jdata ? jdata : "",
jdata ? " ]" : "");
}
} }
...@@ -8,7 +8,7 @@ genreport - generate a report about DNS server compliance. ...@@ -8,7 +8,7 @@ genreport - generate a report about DNS server compliance.
# SYNOPSIS # SYNOPSIS
**genreport** **[-46abBcdDeEfLnopPRstT]** **[-i *test*]** **[-I *test*]** **[-m *maxoutstanding*]** **[-r *server*]** **genreport** **[-46abBcdDeEfjLnopPRstT]** **[-i *test*]** **[-I *test*]** **[-m *maxoutstanding*]** **[-r *server*]**
# DESCRIPTION # DESCRIPTION
...@@ -71,6 +71,9 @@ Options are order dependent. ...@@ -71,6 +71,9 @@ Options are order dependent.
**-I test** **-I test**
: remove a individual test. : remove a individual test.
**-h**
: emit json.
**-L** **-L**
: list tests and their grouping. : list tests and their grouping.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment