#include <sys/stat.h>
#include <err.h>
#include <fcntl.h>
#include <unistd.h>
#include <netdb.h>
#include <signal.h>
#include <string.h>
#include <limits.h>
#include <time.h>
#include "extern.h"
int outformats;
static char output_tmpname[PATH_MAX];
static char output_name[PATH_MAX];
static const struct outputs {
int format;
char *name;
int (*fn)(FILE *, struct validation_data *, struct stats *);
} outputs[] = {
{ FORMAT_OPENBGPD, "openbgpd", output_bgpd },
{ FORMAT_BIRD, "bird", output_bird },
{ FORMAT_CSV, "csv", output_csv },
{ FORMAT_JSON, "json", output_json },
{ FORMAT_OMETRIC, "metrics", output_ometric },
{ FORMAT_CCR, "rpki.ccr", output_ccr_der },
{ 0, NULL, NULL }
};
static FILE *output_createtmp(char *);
static void output_cleantmp(void);
static int output_finish(FILE *, time_t);
static void sig_handler(int);
static void set_signal_handler(void);
static void
prune_as0_tals(struct vrp_tree *vrps)
{
struct vrp *v, *tv;
int talid;
int has_vrps[TALSZ_MAX] = { 0 };
int is_as0_tal[TALSZ_MAX] = { 0 };
for (talid = 0; talid < talsz; talid++)
is_as0_tal[talid] = 1;
RB_FOREACH(v, vrp_tree, vrps) {
has_vrps[v->talid] = 1;
if (v->asid != 0)
is_as0_tal[v->talid] = 0;
}
for (talid = 0; talid < talsz; talid++) {
if (is_as0_tal[talid] && has_vrps[talid]) {
warnx("%s: Detected AS0 TAL, pruning associated VRPs",
taldescs[talid]);
}
}
RB_FOREACH_SAFE(v, vrp_tree, vrps, tv) {
if (is_as0_tal[v->talid]) {
RB_REMOVE(vrp_tree, vrps, v);
free(v);
}
}
}
int
outputfiles(struct validation_data *vd, struct stats *st)
{
int i, rc = 0;
atexit(output_cleantmp);
set_signal_handler();
if (excludeas0)
prune_as0_tals(&vd->vrps);
for (i = 0; outputs[i].name; i++) {
FILE *fout;
if (!(outformats & outputs[i].format))
continue;
fout = output_createtmp(outputs[i].name);
if (fout == NULL) {
warn("cannot create %s", outputs[i].name);
rc = 1;
continue;
}
if ((*outputs[i].fn)(fout, vd, st) != 0) {
warn("output for %s format failed", outputs[i].name);
fclose(fout);
output_cleantmp();
rc = 1;
continue;
}
if (output_finish(fout, vd->buildtime) != 0) {
warn("finish for %s format failed", outputs[i].name);
output_cleantmp();
rc = 1;
continue;
}
}
return rc;
}
static FILE *
output_createtmp(char *name)
{
FILE *f;
int fd, r;
if (strlcpy(output_name, name, sizeof output_name) >=
sizeof output_name)
err(1, "path too long");
r = snprintf(output_tmpname, sizeof output_tmpname,
"%s.XXXXXXXXXXX", output_name);
if (r < 0 || r > (int)sizeof(output_tmpname))
err(1, "path too long");
fd = mkostemp(output_tmpname, O_CLOEXEC);
if (fd == -1)
err(1, "mkostemp: %s", output_tmpname);
(void) fchmod(fd, 0644);
f = fdopen(fd, "w");
if (f == NULL)
err(1, "fdopen");
return f;
}
static int
output_finish(FILE *out, time_t buildtime)
{
struct timespec ts[2];
if (fclose(out) != 0)
return -1;
ts[0].tv_nsec = UTIME_OMIT;
ts[1].tv_sec = buildtime;
ts[1].tv_nsec = 0;
if (utimensat(AT_FDCWD, output_tmpname, ts, 0) == -1)
return -1;
if (rename(output_tmpname, output_name) == -1)
return -1;
output_tmpname[0] = '\0';
return 0;
}
static void
output_cleantmp(void)
{
if (*output_tmpname)
unlink(output_tmpname);
output_tmpname[0] = '\0';
}
static void
sig_handler(int sig)
{
output_cleantmp();
_exit(2);
}
static void
set_signal_handler(void)
{
struct sigaction sa;
int i, signals[] = {SIGTERM, SIGHUP, SIGINT, SIGUSR1, SIGUSR2,
SIGPIPE, SIGXCPU, SIGXFSZ, 0};
memset(&sa, 0, sizeof(sa));
sigfillset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = sig_handler;
for (i = 0; signals[i] != 0; i++) {
if (sigaction(signals[i], &sa, NULL) == -1) {
warn("sigaction(%s)", strsignal(signals[i]));
continue;
}
}
}
int
outputheader(FILE *out, struct validation_data *vd, struct stats *st)
{
char hn[NI_MAXHOST], tbuf[80];
struct tm *tp;
int i;
tp = gmtime(&vd->buildtime);
strftime(tbuf, sizeof tbuf, "%a %b %e %H:%M:%S UTC %Y", tp);
gethostname(hn, sizeof hn);
if (fprintf(out,
"# Generated on host %s at %s\n"
"# Processing time %lld seconds (%llds user, %llds system)\n"
"# CCR manifest hash: %s\n"
"# CCR validated ROA payloads hash: %s\n"
"# CCR validated ASPA payloads hash: %s\n"
"# Route Origin Authorizations: %u (%u failed parse, %u invalid)\n"
"# BGPsec Router Certificates: %u\n"
"# Certificates: %u (%u invalid, %u non-functional)\n",
hn, tbuf, (long long)st->elapsed_time.tv_sec,
(long long)st->user_time.tv_sec, (long long)st->system_time.tv_sec,
vd->ccr.mfts_hash, vd->ccr.vrps_hash, vd->ccr.vaps_hash,
st->repo_tal_stats.roas, st->repo_tal_stats.roas_fail,
st->repo_tal_stats.roas_invalid, st->repo_tal_stats.brks,
st->repo_tal_stats.certs, st->repo_tal_stats.certs_fail,
st->repo_tal_stats.certs_nonfunc) < 0)
return -1;
if (fprintf(out,
"# Trust Anchor Locators: %u (%u invalid) [", st->tals,
talsz - st->tals) < 0)
return -1;
for (i = 0; i < talsz; i++)
if (fprintf(out, " %s", tals[i]) < 0)
return -1;
if (fprintf(out,
" ]\n"
"# Manifests: %u (%u failed parse)\n"
"# Certificate revocation lists: %u\n"
"# Repositories: %u\n"
"# VRP Entries: %u (%u unique)\n",
st->repo_tal_stats.mfts, st->repo_tal_stats.mfts_fail,
st->repo_tal_stats.crls,
st->repos,
st->repo_tal_stats.vrps, st->repo_tal_stats.vrps_uniqs) < 0)
return -1;
return 0;
}