1 /* $OpenBSD: main.c,v 1.257 2024/04/08 14:02:13 tb Exp $ */
2 /*
3 * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
4 * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/types.h>
20 #include <sys/queue.h>
21 #include <sys/resource.h>
22 #include <sys/socket.h>
23 #include <sys/statvfs.h>
24 #include <sys/time.h>
25 #include <sys/tree.h>
26 #include <sys/wait.h>
27
28 #include <assert.h>
29 #include <dirent.h>
30 #include <err.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <fnmatch.h>
34 #include <limits.h>
35 #include <poll.h>
36 #include <pwd.h>
37 #include <signal.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <syslog.h>
43 #include <time.h>
44 #include <unistd.h>
45
46 #include <imsg.h>
47
48 #include "extern.h"
49 #include "version.h"
50
51 const char *tals[TALSZ_MAX];
52 const char *taldescs[TALSZ_MAX];
53 unsigned int talrepocnt[TALSZ_MAX];
54 struct repotalstats talstats[TALSZ_MAX];
55 int talsz;
56
57 size_t entity_queue;
58 int timeout = 60*60;
59 volatile sig_atomic_t killme;
60 void suicide(int sig);
61
62 static struct filepath_tree fpt = RB_INITIALIZER(&fpt);
63 static struct msgbuf procq, rsyncq, httpq, rrdpq;
64 static int cachefd, outdirfd;
65
66 const char *bird_tablename = "ROAS";
67
68 int verbose;
69 int noop;
70 int excludeaspa;
71 int filemode;
72 int shortlistmode;
73 int rrdpon = 1;
74 int repo_timeout;
75 int experimental;
76 time_t deadline;
77
78 /* 9999-12-31 23:59:59 UTC */
79 #define X509_TIME_MAX 253402300799LL
80 /* 0000-01-01 00:00:00 UTC */
81 #define X509_TIME_MIN -62167219200LL
82
83 int64_t evaluation_time = X509_TIME_MIN;
84
85 struct stats stats;
86
87 struct fqdnlistentry {
88 LIST_ENTRY(fqdnlistentry) entry;
89 char *fqdn;
90 };
91 LIST_HEAD(fqdns, fqdnlistentry);
92
93 struct fqdns shortlist = LIST_HEAD_INITIALIZER(fqdns);
94 struct fqdns skiplist = LIST_HEAD_INITIALIZER(fqdns);
95
96 /*
97 * Log a message to stderr if and only if "verbose" is non-zero.
98 * This uses the err(3) functionality.
99 */
100 void
logx(const char * fmt,...)101 logx(const char *fmt, ...)
102 {
103 va_list ap;
104
105 if (verbose && fmt != NULL) {
106 va_start(ap, fmt);
107 vwarnx(fmt, ap);
108 va_end(ap);
109 }
110 }
111
112 time_t
getmonotime(void)113 getmonotime(void)
114 {
115 struct timespec ts;
116
117 if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
118 err(1, "clock_gettime");
119 return (ts.tv_sec);
120 }
121
122 /*
123 * Time - Evaluation time is used as the current time if it is
124 * larger than X509_TIME_MIN, otherwise the system time is used.
125 */
126 time_t
get_current_time(void)127 get_current_time(void)
128 {
129 if (evaluation_time > X509_TIME_MIN)
130 return (time_t)evaluation_time;
131 return time(NULL);
132 }
133
134 void
entity_free(struct entity * ent)135 entity_free(struct entity *ent)
136 {
137 if (ent == NULL)
138 return;
139
140 free(ent->path);
141 free(ent->file);
142 free(ent->mftaki);
143 free(ent->data);
144 free(ent);
145 }
146
147 /*
148 * Read a queue entity from the descriptor.
149 * Matched by entity_write_req().
150 * The pointer must be passed entity_free().
151 */
152 void
entity_read_req(struct ibuf * b,struct entity * ent)153 entity_read_req(struct ibuf *b, struct entity *ent)
154 {
155 io_read_buf(b, &ent->type, sizeof(ent->type));
156 io_read_buf(b, &ent->location, sizeof(ent->location));
157 io_read_buf(b, &ent->repoid, sizeof(ent->repoid));
158 io_read_buf(b, &ent->talid, sizeof(ent->talid));
159 io_read_str(b, &ent->path);
160 io_read_str(b, &ent->file);
161 io_read_str(b, &ent->mftaki);
162 io_read_buf_alloc(b, (void **)&ent->data, &ent->datasz);
163 }
164
165 /*
166 * Write the queue entity.
167 * Matched by entity_read_req().
168 */
169 static void
entity_write_req(const struct entity * ent)170 entity_write_req(const struct entity *ent)
171 {
172 struct ibuf *b;
173
174 b = io_new_buffer();
175 io_simple_buffer(b, &ent->type, sizeof(ent->type));
176 io_simple_buffer(b, &ent->location, sizeof(ent->location));
177 io_simple_buffer(b, &ent->repoid, sizeof(ent->repoid));
178 io_simple_buffer(b, &ent->talid, sizeof(ent->talid));
179 io_str_buffer(b, ent->path);
180 io_str_buffer(b, ent->file);
181 io_str_buffer(b, ent->mftaki);
182 io_buf_buffer(b, ent->data, ent->datasz);
183 io_close_buffer(&procq, b);
184 }
185
186 static void
entity_write_repo(const struct repo * rp)187 entity_write_repo(const struct repo *rp)
188 {
189 struct ibuf *b;
190 enum rtype type = RTYPE_REPO;
191 enum location loc = DIR_UNKNOWN;
192 unsigned int repoid;
193 char *path, *altpath;
194 int talid = 0;
195
196 repoid = repo_id(rp);
197 path = repo_basedir(rp, 0);
198 altpath = repo_basedir(rp, 1);
199 b = io_new_buffer();
200 io_simple_buffer(b, &type, sizeof(type));
201 io_simple_buffer(b, &loc, sizeof(loc));
202 io_simple_buffer(b, &repoid, sizeof(repoid));
203 io_simple_buffer(b, &talid, sizeof(talid));
204 io_str_buffer(b, path);
205 io_str_buffer(b, altpath);
206 io_buf_buffer(b, NULL, 0); /* ent->mftaki */
207 io_buf_buffer(b, NULL, 0); /* ent->data */
208 io_close_buffer(&procq, b);
209 free(path);
210 free(altpath);
211 }
212
213 /*
214 * Scan through all queued requests and see which ones are in the given
215 * repo, then flush those into the parser process.
216 */
217 void
entityq_flush(struct entityq * q,struct repo * rp)218 entityq_flush(struct entityq *q, struct repo *rp)
219 {
220 struct entity *p, *np;
221
222 entity_write_repo(rp);
223
224 TAILQ_FOREACH_SAFE(p, q, entries, np) {
225 entity_write_req(p);
226 TAILQ_REMOVE(q, p, entries);
227 entity_free(p);
228 }
229 }
230
231 /*
232 * Add the heap-allocated file to the queue for processing.
233 */
234 static void
entityq_add(char * path,char * file,enum rtype type,enum location loc,struct repo * rp,unsigned char * data,size_t datasz,int talid,char * mftaki)235 entityq_add(char *path, char *file, enum rtype type, enum location loc,
236 struct repo *rp, unsigned char *data, size_t datasz, int talid,
237 char *mftaki)
238 {
239 struct entity *p;
240
241 if ((p = calloc(1, sizeof(struct entity))) == NULL)
242 err(1, NULL);
243
244 p->type = type;
245 p->location = loc;
246 p->talid = talid;
247 p->mftaki = mftaki;
248 p->path = path;
249 if (rp != NULL)
250 p->repoid = repo_id(rp);
251 p->file = file;
252 p->data = data;
253 p->datasz = (data != NULL) ? datasz : 0;
254
255 entity_queue++;
256
257 /*
258 * Write to the queue if there's no repo or the repo has already
259 * been loaded else enqueue it for later.
260 */
261
262 if (rp == NULL || !repo_queued(rp, p)) {
263 entity_write_req(p);
264 entity_free(p);
265 }
266 }
267
268 static void
rrdp_file_resp(unsigned int id,int ok)269 rrdp_file_resp(unsigned int id, int ok)
270 {
271 enum rrdp_msg type = RRDP_FILE;
272 struct ibuf *b;
273
274 b = io_new_buffer();
275 io_simple_buffer(b, &type, sizeof(type));
276 io_simple_buffer(b, &id, sizeof(id));
277 io_simple_buffer(b, &ok, sizeof(ok));
278 io_close_buffer(&rrdpq, b);
279 }
280
281 void
rrdp_fetch(unsigned int id,const char * uri,const char * local,struct rrdp_session * s)282 rrdp_fetch(unsigned int id, const char *uri, const char *local,
283 struct rrdp_session *s)
284 {
285 enum rrdp_msg type = RRDP_START;
286 struct ibuf *b;
287
288 b = io_new_buffer();
289 io_simple_buffer(b, &type, sizeof(type));
290 io_simple_buffer(b, &id, sizeof(id));
291 io_str_buffer(b, local);
292 io_str_buffer(b, uri);
293
294 rrdp_session_buffer(b, s);
295 io_close_buffer(&rrdpq, b);
296 }
297
298 void
rrdp_abort(unsigned int id)299 rrdp_abort(unsigned int id)
300 {
301 enum rrdp_msg type = RRDP_ABORT;
302 struct ibuf *b;
303
304 b = io_new_buffer();
305 io_simple_buffer(b, &type, sizeof(type));
306 io_simple_buffer(b, &id, sizeof(id));
307 io_close_buffer(&rrdpq, b);
308 }
309
310 /*
311 * Request a repository sync via rsync URI to directory local.
312 */
313 void
rsync_fetch(unsigned int id,const char * uri,const char * local,const char * base)314 rsync_fetch(unsigned int id, const char *uri, const char *local,
315 const char *base)
316 {
317 struct ibuf *b;
318
319 b = io_new_buffer();
320 io_simple_buffer(b, &id, sizeof(id));
321 io_str_buffer(b, local);
322 io_str_buffer(b, base);
323 io_str_buffer(b, uri);
324 io_close_buffer(&rsyncq, b);
325 }
326
327 void
rsync_abort(unsigned int id)328 rsync_abort(unsigned int id)
329 {
330 struct ibuf *b;
331
332 b = io_new_buffer();
333 io_simple_buffer(b, &id, sizeof(id));
334 io_str_buffer(b, NULL);
335 io_str_buffer(b, NULL);
336 io_str_buffer(b, NULL);
337 io_close_buffer(&rsyncq, b);
338 }
339
340 /*
341 * Request a file from a https uri, data is written to the file descriptor fd.
342 */
343 void
http_fetch(unsigned int id,const char * uri,const char * last_mod,int fd)344 http_fetch(unsigned int id, const char *uri, const char *last_mod, int fd)
345 {
346 struct ibuf *b;
347
348 b = io_new_buffer();
349 io_simple_buffer(b, &id, sizeof(id));
350 io_str_buffer(b, uri);
351 io_str_buffer(b, last_mod);
352 /* pass file as fd */
353 ibuf_fd_set(b, fd);
354 io_close_buffer(&httpq, b);
355 }
356
357 /*
358 * Request some XML file on behalf of the rrdp parser.
359 * Create a pipe and pass the pipe endpoints to the http and rrdp process.
360 */
361 static void
rrdp_http_fetch(unsigned int id,const char * uri,const char * last_mod)362 rrdp_http_fetch(unsigned int id, const char *uri, const char *last_mod)
363 {
364 enum rrdp_msg type = RRDP_HTTP_INI;
365 struct ibuf *b;
366 int pi[2];
367
368 if (pipe2(pi, O_CLOEXEC | O_NONBLOCK) == -1)
369 err(1, "pipe");
370
371 b = io_new_buffer();
372 io_simple_buffer(b, &type, sizeof(type));
373 io_simple_buffer(b, &id, sizeof(id));
374 ibuf_fd_set(b, pi[0]);
375 io_close_buffer(&rrdpq, b);
376
377 http_fetch(id, uri, last_mod, pi[1]);
378 }
379
380 void
rrdp_http_done(unsigned int id,enum http_result res,const char * last_mod)381 rrdp_http_done(unsigned int id, enum http_result res, const char *last_mod)
382 {
383 enum rrdp_msg type = RRDP_HTTP_FIN;
384 struct ibuf *b;
385
386 /* RRDP request, relay response over to the rrdp process */
387 b = io_new_buffer();
388 io_simple_buffer(b, &type, sizeof(type));
389 io_simple_buffer(b, &id, sizeof(id));
390 io_simple_buffer(b, &res, sizeof(res));
391 io_str_buffer(b, last_mod);
392 io_close_buffer(&rrdpq, b);
393 }
394
395 /*
396 * Add a file (CER, ROA, CRL) from an MFT file, RFC 6486.
397 * These are always relative to the directory in which "mft" sits.
398 */
399 static void
queue_add_from_mft(const struct mft * mft)400 queue_add_from_mft(const struct mft *mft)
401 {
402 size_t i;
403 struct repo *rp;
404 const struct mftfile *f;
405 char *mftaki, *nfile, *npath = NULL;
406
407 rp = repo_byid(mft->repoid);
408 for (i = 0; i < mft->filesz; i++) {
409 f = &mft->files[i];
410
411 if (f->type == RTYPE_INVALID || f->type == RTYPE_CRL)
412 continue;
413
414 if (mft->path != NULL)
415 if ((npath = strdup(mft->path)) == NULL)
416 err(1, NULL);
417 if ((nfile = strdup(f->file)) == NULL)
418 err(1, NULL);
419 if ((mftaki = strdup(mft->aki)) == NULL)
420 err(1, NULL);
421 entityq_add(npath, nfile, f->type, f->location, rp, NULL, 0,
422 mft->talid, mftaki);
423 }
424 }
425
426 /*
427 * Add a local file to the queue of files to fetch.
428 */
429 static void
queue_add_file(const char * file,enum rtype type,int talid)430 queue_add_file(const char *file, enum rtype type, int talid)
431 {
432 unsigned char *buf = NULL;
433 char *nfile;
434 size_t len = 0;
435
436 if (!filemode || strncmp(file, RSYNC_PROTO, RSYNC_PROTO_LEN) != 0) {
437 buf = load_file(file, &len);
438 if (buf == NULL)
439 err(1, "%s", file);
440 }
441
442 if ((nfile = strdup(file)) == NULL)
443 err(1, NULL);
444 /* Not in a repository, so directly add to queue. */
445 entityq_add(NULL, nfile, type, DIR_UNKNOWN, NULL, buf, len, talid,
446 NULL);
447 }
448
449 /*
450 * Add URIs (CER) from a TAL file, RFC 8630.
451 */
452 static void
queue_add_from_tal(struct tal * tal)453 queue_add_from_tal(struct tal *tal)
454 {
455 struct repo *repo;
456 unsigned char *data;
457 char *nfile;
458
459 assert(tal->urisz);
460
461 if ((taldescs[tal->id] = strdup(tal->descr)) == NULL)
462 err(1, NULL);
463
464 /* figure out the TA filename, must be done before repo lookup */
465 nfile = strrchr(tal->uri[0], '/');
466 assert(nfile != NULL);
467 if ((nfile = strdup(nfile + 1)) == NULL)
468 err(1, NULL);
469
470 /* Look up the repository. */
471 repo = ta_lookup(tal->id, tal);
472 if (repo == NULL) {
473 free(nfile);
474 return;
475 }
476
477 /* steal the pkey from the tal structure */
478 data = tal->pkey;
479 tal->pkey = NULL;
480 entityq_add(NULL, nfile, RTYPE_CER, DIR_VALID, repo, data,
481 tal->pkeysz, tal->id, NULL);
482 }
483
484 /*
485 * Add a manifest (MFT) found in an X509 certificate, RFC 6487.
486 */
487 static void
queue_add_from_cert(const struct cert * cert)488 queue_add_from_cert(const struct cert *cert)
489 {
490 struct repo *repo;
491 struct fqdnlistentry *le;
492 char *nfile, *npath, *host;
493 const char *uri, *repouri, *file;
494 size_t repourisz;
495 int shortlisted = 0;
496
497 if (strncmp(cert->repo, RSYNC_PROTO, RSYNC_PROTO_LEN) != 0)
498 errx(1, "unexpected protocol");
499 host = cert->repo + 8;
500
501 LIST_FOREACH(le, &skiplist, entry) {
502 if (strncasecmp(host, le->fqdn, strcspn(host, "/")) == 0) {
503 warnx("skipping %s (listed in skiplist)", cert->repo);
504 return;
505 }
506 }
507
508 LIST_FOREACH(le, &shortlist, entry) {
509 if (strncasecmp(host, le->fqdn, strcspn(host, "/")) == 0) {
510 shortlisted = 1;
511 break;
512 }
513 }
514 if (shortlistmode && shortlisted == 0) {
515 if (verbose)
516 warnx("skipping %s (not shortlisted)", cert->repo);
517 return;
518 }
519
520 repo = repo_lookup(cert->talid, cert->repo,
521 rrdpon ? cert->notify : NULL);
522 if (repo == NULL)
523 return;
524
525 /*
526 * Figure out the cert filename and path by chopping up the
527 * MFT URI in the cert based on the repo base URI.
528 */
529 uri = cert->mft;
530 repouri = repo_uri(repo);
531 repourisz = strlen(repouri);
532 if (strncmp(repouri, cert->mft, repourisz) != 0) {
533 warnx("%s: URI %s outside of repository", repouri, uri);
534 return;
535 }
536 uri += repourisz + 1; /* skip base and '/' */
537 file = strrchr(uri, '/');
538 if (file == NULL) {
539 npath = NULL;
540 if ((nfile = strdup(uri)) == NULL)
541 err(1, NULL);
542 } else {
543 if ((npath = strndup(uri, file - uri)) == NULL)
544 err(1, NULL);
545 if ((nfile = strdup(file + 1)) == NULL)
546 err(1, NULL);
547 }
548
549 entityq_add(npath, nfile, RTYPE_MFT, DIR_UNKNOWN, repo, NULL, 0,
550 cert->talid, NULL);
551 }
552
553 /*
554 * Process parsed content.
555 * For non-ROAs, we grok for more data.
556 * For ROAs, we want to extract the valid info.
557 * In all cases, we gather statistics.
558 */
559 static void
entity_process(struct ibuf * b,struct stats * st,struct vrp_tree * tree,struct brk_tree * brktree,struct vap_tree * vaptree,struct vsp_tree * vsptree)560 entity_process(struct ibuf *b, struct stats *st, struct vrp_tree *tree,
561 struct brk_tree *brktree, struct vap_tree *vaptree,
562 struct vsp_tree *vsptree)
563 {
564 enum rtype type;
565 struct tal *tal;
566 struct cert *cert;
567 struct mft *mft;
568 struct roa *roa;
569 struct aspa *aspa;
570 struct spl *spl;
571 struct repo *rp;
572 char *file;
573 time_t mtime;
574 unsigned int id;
575 int talid;
576 int c;
577
578 /*
579 * For most of these, we first read whether there's any content
580 * at all---this means that the syntactic parse failed (X509
581 * certificate, for example).
582 * We follow that up with whether the resources didn't parse.
583 */
584 io_read_buf(b, &type, sizeof(type));
585 io_read_buf(b, &id, sizeof(id));
586 io_read_buf(b, &talid, sizeof(talid));
587 io_read_str(b, &file);
588 io_read_buf(b, &mtime, sizeof(mtime));
589
590 /* in filemode messages can be ignored, only the accounting matters */
591 if (filemode)
592 goto done;
593
594 if (filepath_add(&fpt, file, mtime) == 0) {
595 warnx("%s: File already visited", file);
596 goto done;
597 }
598
599 rp = repo_byid(id);
600 repo_stat_inc(rp, talid, type, STYPE_OK);
601 repostats_new_files_inc(rp, file);
602 switch (type) {
603 case RTYPE_TAL:
604 st->tals++;
605 tal = tal_read(b);
606 queue_add_from_tal(tal);
607 tal_free(tal);
608 break;
609 case RTYPE_CER:
610 io_read_buf(b, &c, sizeof(c));
611 if (c == 0) {
612 repo_stat_inc(rp, talid, type, STYPE_FAIL);
613 break;
614 }
615 cert = cert_read(b);
616 switch (cert->purpose) {
617 case CERT_PURPOSE_CA:
618 queue_add_from_cert(cert);
619 break;
620 case CERT_PURPOSE_BGPSEC_ROUTER:
621 cert_insert_brks(brktree, cert);
622 repo_stat_inc(rp, talid, type, STYPE_BGPSEC);
623 break;
624 default:
625 errx(1, "unexpected cert purpose received");
626 break;
627 }
628 cert_free(cert);
629 break;
630 case RTYPE_MFT:
631 io_read_buf(b, &c, sizeof(c));
632 if (c == 0) {
633 repo_stat_inc(rp, talid, type, STYPE_FAIL);
634 break;
635 }
636 mft = mft_read(b);
637 queue_add_from_mft(mft);
638 mft_free(mft);
639 break;
640 case RTYPE_CRL:
641 /* CRLs are sent together with MFT and not accounted for */
642 entity_queue++;
643 break;
644 case RTYPE_ROA:
645 io_read_buf(b, &c, sizeof(c));
646 if (c == 0) {
647 repo_stat_inc(rp, talid, type, STYPE_FAIL);
648 break;
649 }
650 roa = roa_read(b);
651 if (roa->valid)
652 roa_insert_vrps(tree, roa, rp);
653 else
654 repo_stat_inc(rp, talid, type, STYPE_INVALID);
655 roa_free(roa);
656 break;
657 case RTYPE_GBR:
658 break;
659 case RTYPE_ASPA:
660 io_read_buf(b, &c, sizeof(c));
661 if (c == 0) {
662 repo_stat_inc(rp, talid, type, STYPE_FAIL);
663 break;
664 }
665 aspa = aspa_read(b);
666 if (aspa->valid)
667 aspa_insert_vaps(file, vaptree, aspa, rp);
668 else
669 repo_stat_inc(rp, talid, type, STYPE_INVALID);
670 aspa_free(aspa);
671 break;
672 case RTYPE_SPL:
673 io_read_buf(b, &c, sizeof(c));
674 if (c == 0) {
675 if (experimental)
676 repo_stat_inc(rp, talid, type, STYPE_FAIL);
677 break;
678 }
679 spl = spl_read(b);
680 if (spl->valid)
681 spl_insert_vsps(vsptree, spl, rp);
682 else
683 repo_stat_inc(rp, talid, type, STYPE_INVALID);
684 spl_free(spl);
685 break;
686 case RTYPE_TAK:
687 break;
688 case RTYPE_FILE:
689 break;
690 default:
691 warnx("%s: unknown entity type %d", file, type);
692 break;
693 }
694
695 done:
696 free(file);
697 entity_queue--;
698 }
699
700 static void
rrdp_process(struct ibuf * b)701 rrdp_process(struct ibuf *b)
702 {
703 enum rrdp_msg type;
704 enum publish_type pt;
705 struct rrdp_session *s;
706 char *uri, *last_mod, *data;
707 char hash[SHA256_DIGEST_LENGTH];
708 size_t dsz;
709 unsigned int id;
710 int ok;
711
712 io_read_buf(b, &type, sizeof(type));
713 io_read_buf(b, &id, sizeof(id));
714
715 switch (type) {
716 case RRDP_END:
717 io_read_buf(b, &ok, sizeof(ok));
718 rrdp_finish(id, ok);
719 break;
720 case RRDP_HTTP_REQ:
721 io_read_str(b, &uri);
722 io_read_str(b, &last_mod);
723 rrdp_http_fetch(id, uri, last_mod);
724 break;
725 case RRDP_SESSION:
726 s = rrdp_session_read(b);
727 rrdp_session_save(id, s);
728 rrdp_session_free(s);
729 break;
730 case RRDP_FILE:
731 io_read_buf(b, &pt, sizeof(pt));
732 if (pt != PUB_ADD)
733 io_read_buf(b, &hash, sizeof(hash));
734 io_read_str(b, &uri);
735 io_read_buf_alloc(b, (void **)&data, &dsz);
736
737 ok = rrdp_handle_file(id, pt, uri, hash, sizeof(hash),
738 data, dsz);
739 rrdp_file_resp(id, ok);
740
741 free(uri);
742 free(data);
743 break;
744 case RRDP_CLEAR:
745 rrdp_clear(id);
746 break;
747 default:
748 errx(1, "unexpected rrdp response");
749 }
750 }
751
752 static void
sum_stats(const struct repo * rp,const struct repotalstats * in,void * arg)753 sum_stats(const struct repo *rp, const struct repotalstats *in, void *arg)
754 {
755 struct repotalstats *out = arg;
756
757 out->mfts += in->mfts;
758 out->mfts_fail += in->mfts_fail;
759 out->certs += in->certs;
760 out->certs_fail += in->certs_fail;
761 out->roas += in->roas;
762 out->roas_fail += in->roas_fail;
763 out->roas_invalid += in->roas_invalid;
764 out->aspas += in->aspas;
765 out->aspas_fail += in->aspas_fail;
766 out->aspas_invalid += in->aspas_invalid;
767 out->brks += in->brks;
768 out->crls += in->crls;
769 out->gbrs += in->gbrs;
770 out->taks += in->taks;
771 out->vrps += in->vrps;
772 out->vrps_uniqs += in->vrps_uniqs;
773 out->vaps += in->vaps;
774 out->vaps_uniqs += in->vaps_uniqs;
775 out->vaps_pas += in->vaps_pas;
776 out->vaps_overflowed += in->vaps_overflowed;
777 out->spls += in->spls;
778 out->spls_fail += in->spls_fail;
779 out->spls_invalid += in->spls_invalid;
780 out->vsps += in->vsps;
781 out->vsps_uniqs += in->vsps_uniqs;
782 }
783
784 static void
sum_repostats(const struct repo * rp,const struct repostats * in,void * arg)785 sum_repostats(const struct repo *rp, const struct repostats *in, void *arg)
786 {
787 struct repostats *out = arg;
788
789 out->del_files += in->del_files;
790 out->extra_files += in->extra_files;
791 out->del_extra_files += in->del_extra_files;
792 out->del_dirs += in->del_dirs;
793 out->new_files += in->new_files;
794 timespecadd(&in->sync_time, &out->sync_time, &out->sync_time);
795 }
796
797 /*
798 * Assign filenames ending in ".tal" in "/etc/rpki" into "tals",
799 * returning the number of files found and filled-in.
800 * This may be zero.
801 * Don't exceed "max" filenames.
802 */
803 static int
tal_load_default(void)804 tal_load_default(void)
805 {
806 static const char *confdir = "/etc/rpki";
807 int s = 0;
808 char *path;
809 DIR *dirp;
810 struct dirent *dp;
811
812 dirp = opendir(confdir);
813 if (dirp == NULL)
814 err(1, "open %s", confdir);
815 while ((dp = readdir(dirp)) != NULL) {
816 if (fnmatch("*.tal", dp->d_name, FNM_PERIOD) == FNM_NOMATCH)
817 continue;
818 if (s >= TALSZ_MAX)
819 err(1, "too many tal files found in %s",
820 confdir);
821 if (asprintf(&path, "%s/%s", confdir, dp->d_name) == -1)
822 err(1, NULL);
823 tals[s++] = path;
824 }
825 closedir(dirp);
826 return s;
827 }
828
829 /*
830 * Load the list of FQDNs from the skiplist which are to be distrusted.
831 * Return 0 on success.
832 */
833 static void
load_skiplist(const char * slf)834 load_skiplist(const char *slf)
835 {
836 struct fqdnlistentry *le;
837 FILE *fp;
838 char *line = NULL;
839 size_t linesize = 0, linelen;
840
841 if ((fp = fopen(slf, "r")) == NULL) {
842 if (errno == ENOENT && strcmp(slf, DEFAULT_SKIPLIST_FILE) == 0)
843 return;
844 err(1, "failed to open %s", slf);
845 }
846
847 while (getline(&line, &linesize, fp) != -1) {
848 /* just eat comment lines or empty lines*/
849 if (line[0] == '#' || line[0] == '\n')
850 continue;
851
852 if (line[0] == ' ' || line[0] == '\t')
853 errx(1, "invalid entry in skiplist: %s", line);
854
855 /*
856 * Ignore anything after comment sign, whitespaces,
857 * also chop off LF or CR.
858 */
859 linelen = strcspn(line, " #\r\n\t");
860 line[linelen] = '\0';
861
862 if (!valid_uri(line, linelen, NULL))
863 errx(1, "invalid entry in skiplist: %s", line);
864
865 if ((le = malloc(sizeof(struct fqdnlistentry))) == NULL)
866 err(1, NULL);
867 if ((le->fqdn = strdup(line)) == NULL)
868 err(1, NULL);
869
870 LIST_INSERT_HEAD(&skiplist, le, entry);
871 stats.skiplistentries++;
872 }
873
874 fclose(fp);
875 free(line);
876 }
877
878 /*
879 * Load shortlist entries.
880 */
881 static void
load_shortlist(const char * fqdn)882 load_shortlist(const char *fqdn)
883 {
884 struct fqdnlistentry *le;
885
886 if (!valid_uri(fqdn, strlen(fqdn), NULL))
887 errx(1, "invalid fqdn passed to -q: %s", fqdn);
888
889 if ((le = malloc(sizeof(struct fqdnlistentry))) == NULL)
890 err(1, NULL);
891
892 if ((le->fqdn = strdup(fqdn)) == NULL)
893 err(1, NULL);
894
895 LIST_INSERT_HEAD(&shortlist, le, entry);
896 }
897
898 static void
check_fs_size(int fd,const char * cachedir)899 check_fs_size(int fd, const char *cachedir)
900 {
901 struct statvfs fs;
902 unsigned long long minsize = 500 * 1024 * 1024;
903 unsigned long long minnode = 300 * 1000;
904
905 if (fstatvfs(fd, &fs) == -1)
906 err(1, "statfs %s", cachedir);
907
908 if (fs.f_bavail < minsize / fs.f_frsize ||
909 (fs.f_ffree > 0 && fs.f_favail < minnode)) {
910 fprintf(stderr, "WARNING: rpki-client may need more than "
911 "the available disk space\n"
912 "on the file-system holding %s.\n", cachedir);
913 fprintf(stderr, "available space: %llukB, "
914 "suggested minimum %llukB\n",
915 (unsigned long long)fs.f_bavail * fs.f_frsize / 1024,
916 minsize / 1024);
917 fprintf(stderr, "available inodes: %llu, "
918 "suggested minimum: %llu\n\n",
919 (unsigned long long)fs.f_favail, minnode);
920 fflush(stderr);
921 }
922 }
923
924 static pid_t
process_start(const char * title,int * fd)925 process_start(const char *title, int *fd)
926 {
927 int fl = SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK;
928 pid_t pid;
929 int pair[2];
930
931 if (socketpair(AF_UNIX, fl, 0, pair) == -1)
932 err(1, "socketpair");
933 if ((pid = fork()) == -1)
934 err(1, "fork");
935
936 if (pid == 0) {
937 setproctitle("%s", title);
938 /* change working directory to the cache directory */
939 if (fchdir(cachefd) == -1)
940 err(1, "fchdir");
941 if (!filemode && timeout > 0)
942 alarm(timeout);
943 close(pair[1]);
944 *fd = pair[0];
945 } else {
946 close(pair[0]);
947 *fd = pair[1];
948 }
949 return pid;
950 }
951
952 void
suicide(int sig)953 suicide(int sig __attribute__((unused)))
954 {
955 killme = 1;
956 }
957
958 #define NPFD 4
959
960 int
main(int argc,char * argv[])961 main(int argc, char *argv[])
962 {
963 int rc, c, i, st, proc, rsync, http, rrdp, hangup = 0;
964 pid_t pid, procpid, rsyncpid, httppid, rrdppid;
965 struct pollfd pfd[NPFD];
966 struct msgbuf *queues[NPFD];
967 struct ibuf *b, *httpbuf = NULL, *procbuf = NULL;
968 struct ibuf *rrdpbuf = NULL, *rsyncbuf = NULL;
969 char *rsync_prog = "openrsync";
970 char *bind_addr = NULL;
971 const char *cachedir = NULL, *outputdir = NULL;
972 const char *errs, *name;
973 const char *skiplistfile = NULL;
974 struct vrp_tree vrps = RB_INITIALIZER(&vrps);
975 struct vsp_tree vsps = RB_INITIALIZER(&vsps);
976 struct brk_tree brks = RB_INITIALIZER(&brks);
977 struct vap_tree vaps = RB_INITIALIZER(&vaps);
978 struct rusage ru;
979 struct timespec start_time, now_time;
980
981 clock_gettime(CLOCK_MONOTONIC, &start_time);
982
983 /* If started as root, priv-drop to _rpki-client */
984 if (getuid() == 0) {
985 struct passwd *pw;
986
987 pw = getpwnam("_rpki-client");
988 if (!pw)
989 errx(1, "no _rpki-client user to revoke to");
990 if (setgroups(1, &pw->pw_gid) == -1 ||
991 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 ||
992 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
993 err(1, "unable to revoke privs");
994 }
995 cachedir = RPKI_PATH_BASE_DIR;
996 outputdir = RPKI_PATH_OUT_DIR;
997 repo_timeout = timeout / 4;
998 skiplistfile = DEFAULT_SKIPLIST_FILE;
999
1000 if (pledge("stdio rpath wpath cpath inet fattr dns sendfd recvfd "
1001 "proc exec unveil", NULL) == -1)
1002 err(1, "pledge");
1003
1004 while ((c = getopt(argc, argv, "Ab:Bcd:e:fH:jmnoP:rRs:S:t:T:vVx")) != -1)
1005 switch (c) {
1006 case 'A':
1007 excludeaspa = 1;
1008 break;
1009 case 'b':
1010 bind_addr = optarg;
1011 break;
1012 case 'B':
1013 outformats |= FORMAT_BIRD;
1014 break;
1015 case 'c':
1016 outformats |= FORMAT_CSV;
1017 break;
1018 case 'd':
1019 cachedir = optarg;
1020 break;
1021 case 'e':
1022 rsync_prog = optarg;
1023 break;
1024 case 'f':
1025 filemode = 1;
1026 noop = 1;
1027 break;
1028 case 'H':
1029 shortlistmode = 1;
1030 load_shortlist(optarg);
1031 break;
1032 case 'j':
1033 outformats |= FORMAT_JSON;
1034 break;
1035 case 'm':
1036 outformats |= FORMAT_OMETRIC;
1037 break;
1038 case 'n':
1039 noop = 1;
1040 break;
1041 case 'o':
1042 outformats |= FORMAT_OPENBGPD;
1043 break;
1044 case 'P':
1045 evaluation_time = strtonum(optarg, X509_TIME_MIN + 1,
1046 X509_TIME_MAX, &errs);
1047 if (errs)
1048 errx(1, "-P: time in seconds %s", errs);
1049 break;
1050 case 'R':
1051 rrdpon = 0;
1052 break;
1053 case 'r': /* Remove after OpenBSD 7.3 */
1054 rrdpon = 1;
1055 break;
1056 case 's':
1057 timeout = strtonum(optarg, 0, 24*60*60, &errs);
1058 if (errs)
1059 errx(1, "-s: %s", errs);
1060 if (timeout == 0)
1061 repo_timeout = 24*60*60;
1062 else
1063 repo_timeout = timeout / 4;
1064 break;
1065 case 'S':
1066 skiplistfile = optarg;
1067 break;
1068 case 't':
1069 if (talsz >= TALSZ_MAX)
1070 err(1, "too many tal files specified");
1071 tals[talsz++] = optarg;
1072 break;
1073 case 'T':
1074 bird_tablename = optarg;
1075 break;
1076 case 'v':
1077 verbose++;
1078 break;
1079 case 'V':
1080 fprintf(stderr, "rpki-client %s\n", RPKI_VERSION);
1081 return 0;
1082 case 'x':
1083 experimental = 1;
1084 break;
1085 default:
1086 goto usage;
1087 }
1088
1089 argv += optind;
1090 argc -= optind;
1091
1092 if (!filemode) {
1093 if (argc == 1)
1094 outputdir = argv[0];
1095 else if (argc > 1)
1096 goto usage;
1097
1098 if (outputdir == NULL) {
1099 warnx("output directory required");
1100 goto usage;
1101 }
1102 } else {
1103 if (argc == 0)
1104 goto usage;
1105 outputdir = NULL;
1106 }
1107
1108 if (cachedir == NULL) {
1109 warnx("cache directory required");
1110 goto usage;
1111 }
1112
1113 signal(SIGPIPE, SIG_IGN);
1114
1115 if ((cachefd = open(cachedir, O_RDONLY | O_DIRECTORY)) == -1)
1116 err(1, "cache directory %s", cachedir);
1117 if (outputdir != NULL) {
1118 if ((outdirfd = open(outputdir, O_RDONLY | O_DIRECTORY)) == -1)
1119 err(1, "output directory %s", outputdir);
1120 if (outformats == 0)
1121 outformats = FORMAT_OPENBGPD;
1122 }
1123
1124 check_fs_size(cachefd, cachedir);
1125
1126 if (talsz == 0)
1127 talsz = tal_load_default();
1128 if (talsz == 0)
1129 err(1, "no TAL files found in %s", "/etc/rpki");
1130
1131 /* Load optional constraint files sitting next to the TALs. */
1132 constraints_load();
1133
1134 /*
1135 * Create the file reader as a jailed child process.
1136 * It will be responsible for reading all of the files (ROAs,
1137 * manifests, certificates, etc.) and returning contents.
1138 */
1139
1140 procpid = process_start("parser", &proc);
1141 if (procpid == 0) {
1142 if (!filemode)
1143 proc_parser(proc);
1144 else
1145 proc_filemode(proc);
1146 }
1147
1148 /* Constraints are only needed in the filemode and parser processes. */
1149 constraints_unload();
1150
1151 /*
1152 * Create a process that will do the rsync'ing.
1153 * This process is responsible for making sure that all the
1154 * repositories referenced by a certificate manifest (or the
1155 * TAL) exists and has been downloaded.
1156 */
1157
1158 if (!noop) {
1159 rsyncpid = process_start("rsync", &rsync);
1160 if (rsyncpid == 0) {
1161 close(proc);
1162 proc_rsync(rsync_prog, bind_addr, rsync);
1163 }
1164 } else {
1165 rsync = -1;
1166 rsyncpid = -1;
1167 }
1168
1169 /*
1170 * Create a process that will fetch data via https.
1171 * With every request the http process receives a file descriptor
1172 * where the data should be written to.
1173 */
1174
1175 if (!noop && rrdpon) {
1176 httppid = process_start("http", &http);
1177
1178 if (httppid == 0) {
1179 close(proc);
1180 close(rsync);
1181 proc_http(bind_addr, http);
1182 }
1183 } else {
1184 http = -1;
1185 httppid = -1;
1186 }
1187
1188 /*
1189 * Create a process that will process RRDP.
1190 * The rrdp process requires the http process to fetch the various
1191 * XML files and does this via the main process.
1192 */
1193
1194 if (!noop && rrdpon) {
1195 rrdppid = process_start("rrdp", &rrdp);
1196 if (rrdppid == 0) {
1197 close(proc);
1198 close(rsync);
1199 close(http);
1200 proc_rrdp(rrdp);
1201 }
1202 } else {
1203 rrdp = -1;
1204 rrdppid = -1;
1205 }
1206
1207 if (!filemode && timeout > 0) {
1208 /*
1209 * Commit suicide eventually
1210 * cron will normally start a new one
1211 */
1212 alarm(timeout);
1213 signal(SIGALRM, suicide);
1214
1215 /* give up a bit before the hard timeout and try to finish up */
1216 if (!noop)
1217 deadline = getmonotime() + timeout - repo_timeout / 2;
1218 }
1219
1220 if (pledge("stdio rpath wpath cpath fattr sendfd unveil", NULL) == -1)
1221 err(1, "pledge");
1222
1223 msgbuf_init(&procq);
1224 msgbuf_init(&rsyncq);
1225 msgbuf_init(&httpq);
1226 msgbuf_init(&rrdpq);
1227 procq.fd = proc;
1228 rsyncq.fd = rsync;
1229 httpq.fd = http;
1230 rrdpq.fd = rrdp;
1231
1232 /*
1233 * The main process drives the top-down scan to leaf ROAs using
1234 * data downloaded by the rsync process and parsed by the
1235 * parsing process.
1236 */
1237
1238 pfd[0].fd = proc;
1239 queues[0] = &procq;
1240 pfd[1].fd = rsync;
1241 queues[1] = &rsyncq;
1242 pfd[2].fd = http;
1243 queues[2] = &httpq;
1244 pfd[3].fd = rrdp;
1245 queues[3] = &rrdpq;
1246
1247 load_skiplist(skiplistfile);
1248
1249 /*
1250 * Prime the process with our TAL files.
1251 * These will (hopefully) contain links to manifests and we
1252 * can get the ball rolling.
1253 */
1254
1255 for (i = 0; i < talsz; i++)
1256 queue_add_file(tals[i], RTYPE_TAL, i);
1257
1258 if (filemode) {
1259 while (*argv != NULL)
1260 queue_add_file(*argv++, RTYPE_FILE, 0);
1261
1262 if (unveil(cachedir, "r") == -1)
1263 err(1, "unveil cachedir");
1264 } else {
1265 if (unveil(outputdir, "rwc") == -1)
1266 err(1, "unveil outputdir");
1267 if (unveil(cachedir, "rwc") == -1)
1268 err(1, "unveil cachedir");
1269 }
1270 if (pledge("stdio rpath wpath cpath fattr sendfd", NULL) == -1)
1271 err(1, "unveil");
1272
1273 /* change working directory to the cache directory */
1274 if (fchdir(cachefd) == -1)
1275 err(1, "fchdir");
1276
1277 while (entity_queue > 0 && !killme) {
1278 int polltim;
1279
1280 for (i = 0; i < NPFD; i++) {
1281 pfd[i].events = POLLIN;
1282 if (queues[i]->queued)
1283 pfd[i].events |= POLLOUT;
1284 }
1285
1286 polltim = repo_check_timeout(INFTIM);
1287
1288 if (poll(pfd, NPFD, polltim) == -1) {
1289 if (errno == EINTR)
1290 continue;
1291 err(1, "poll");
1292 }
1293
1294 for (i = 0; i < NPFD; i++) {
1295 if (pfd[i].revents & (POLLERR|POLLNVAL)) {
1296 warnx("poll[%d]: bad fd", i);
1297 hangup = 1;
1298 }
1299 if (pfd[i].revents & POLLHUP)
1300 hangup = 1;
1301 if (pfd[i].revents & POLLOUT) {
1302 switch (msgbuf_write(queues[i])) {
1303 case 0:
1304 warnx("write[%d]: "
1305 "connection closed", i);
1306 hangup = 1;
1307 break;
1308 case -1:
1309 warn("write[%d]", i);
1310 hangup = 1;
1311 break;
1312 }
1313 }
1314 }
1315 if (hangup)
1316 break;
1317
1318 /*
1319 * Check the rsync and http process.
1320 * This means that one of our modules has completed
1321 * downloading and we can flush the module requests into
1322 * the parser process.
1323 */
1324
1325 if ((pfd[1].revents & POLLIN)) {
1326 b = io_buf_read(rsync, &rsyncbuf);
1327 if (b != NULL) {
1328 unsigned int id;
1329 int ok;
1330
1331 io_read_buf(b, &id, sizeof(id));
1332 io_read_buf(b, &ok, sizeof(ok));
1333 rsync_finish(id, ok);
1334 ibuf_free(b);
1335 }
1336 }
1337
1338 if ((pfd[2].revents & POLLIN)) {
1339 b = io_buf_read(http, &httpbuf);
1340 if (b != NULL) {
1341 unsigned int id;
1342 enum http_result res;
1343 char *last_mod;
1344
1345 io_read_buf(b, &id, sizeof(id));
1346 io_read_buf(b, &res, sizeof(res));
1347 io_read_str(b, &last_mod);
1348 http_finish(id, res, last_mod);
1349 free(last_mod);
1350 ibuf_free(b);
1351 }
1352 }
1353
1354 /*
1355 * Handle RRDP requests here.
1356 */
1357 if ((pfd[3].revents & POLLIN)) {
1358 b = io_buf_read(rrdp, &rrdpbuf);
1359 if (b != NULL) {
1360 rrdp_process(b);
1361 ibuf_free(b);
1362 }
1363 }
1364
1365 /*
1366 * The parser has finished something for us.
1367 * Dequeue these one by one.
1368 */
1369
1370 if ((pfd[0].revents & POLLIN)) {
1371 b = io_buf_read(proc, &procbuf);
1372 if (b != NULL) {
1373 entity_process(b, &stats, &vrps, &brks, &vaps,
1374 &vsps);
1375 ibuf_free(b);
1376 }
1377 }
1378 }
1379
1380 signal(SIGALRM, SIG_DFL);
1381 if (killme) {
1382 syslog(LOG_CRIT|LOG_DAEMON,
1383 "excessive runtime (%d seconds), giving up", timeout);
1384 errx(1, "excessive runtime (%d seconds), giving up", timeout);
1385 }
1386
1387 /*
1388 * For clean-up, close the input for the parser and rsync
1389 * process.
1390 * This will cause them to exit, then we reap them.
1391 */
1392
1393 close(proc);
1394 close(rsync);
1395 close(http);
1396 close(rrdp);
1397
1398 rc = 0;
1399 for (;;) {
1400 pid = waitpid(WAIT_ANY, &st, 0);
1401 if (pid == -1) {
1402 if (errno == EINTR)
1403 continue;
1404 if (errno == ECHILD)
1405 break;
1406 err(1, "wait");
1407 }
1408
1409 if (pid == procpid)
1410 name = "parser";
1411 else if (pid == rsyncpid)
1412 name = "rsync";
1413 else if (pid == httppid)
1414 name = "http";
1415 else if (pid == rrdppid)
1416 name = "rrdp";
1417 else
1418 name = "unknown";
1419
1420 if (WIFSIGNALED(st)) {
1421 warnx("%s terminated signal %d", name, WTERMSIG(st));
1422 rc = 1;
1423 } else if (!WIFEXITED(st) || WEXITSTATUS(st) != 0) {
1424 warnx("%s process exited abnormally", name);
1425 rc = 1;
1426 }
1427 }
1428
1429 /* processing did not finish because of error */
1430 if (entity_queue != 0)
1431 errx(1, "not all files processed, giving up");
1432
1433 /* if processing in filemode the process is done, no cleanup */
1434 if (filemode)
1435 return rc;
1436
1437 logx("all files parsed: generating output");
1438
1439 if (!noop)
1440 repo_cleanup(&fpt, cachefd);
1441
1442 clock_gettime(CLOCK_MONOTONIC, &now_time);
1443 timespecsub(&now_time, &start_time, &stats.elapsed_time);
1444 if (getrusage(RUSAGE_SELF, &ru) == 0) {
1445 TIMEVAL_TO_TIMESPEC(&ru.ru_utime, &stats.user_time);
1446 TIMEVAL_TO_TIMESPEC(&ru.ru_stime, &stats.system_time);
1447 }
1448 if (getrusage(RUSAGE_CHILDREN, &ru) == 0) {
1449 struct timespec ts;
1450
1451 TIMEVAL_TO_TIMESPEC(&ru.ru_utime, &ts);
1452 timespecadd(&stats.user_time, &ts, &stats.user_time);
1453 TIMEVAL_TO_TIMESPEC(&ru.ru_stime, &ts);
1454 timespecadd(&stats.system_time, &ts, &stats.system_time);
1455 }
1456
1457 /* change working directory to the output directory */
1458 if (fchdir(outdirfd) == -1)
1459 err(1, "fchdir output dir");
1460
1461 for (i = 0; i < talsz; i++) {
1462 repo_tal_stats_collect(sum_stats, i, &talstats[i]);
1463 repo_tal_stats_collect(sum_stats, i, &stats.repo_tal_stats);
1464 }
1465 repo_stats_collect(sum_repostats, &stats.repo_stats);
1466
1467 if (outputfiles(&vrps, &brks, &vaps, &vsps, &stats))
1468 rc = 1;
1469
1470 printf("Processing time %lld seconds "
1471 "(%lld seconds user, %lld seconds system)\n",
1472 (long long)stats.elapsed_time.tv_sec,
1473 (long long)stats.user_time.tv_sec,
1474 (long long)stats.system_time.tv_sec);
1475 printf("Skiplist entries: %u\n", stats.skiplistentries);
1476 printf("Route Origin Authorizations: %u (%u failed parse, %u "
1477 "invalid)\n", stats.repo_tal_stats.roas,
1478 stats.repo_tal_stats.roas_fail,
1479 stats.repo_tal_stats.roas_invalid);
1480 printf("AS Provider Attestations: %u (%u failed parse, %u "
1481 "invalid)\n", stats.repo_tal_stats.aspas,
1482 stats.repo_tal_stats.aspas_fail,
1483 stats.repo_tal_stats.aspas_invalid);
1484 printf("Signed Prefix Lists: %u (%u failed parse, %u invalid)\n",
1485 stats.repo_tal_stats.spls, stats.repo_tal_stats.spls_fail,
1486 stats.repo_tal_stats.spls_invalid);
1487 printf("BGPsec Router Certificates: %u\n", stats.repo_tal_stats.brks);
1488 printf("Certificates: %u (%u invalid)\n",
1489 stats.repo_tal_stats.certs, stats.repo_tal_stats.certs_fail);
1490 printf("Trust Anchor Locators: %u (%u invalid)\n",
1491 stats.tals, talsz - stats.tals);
1492 printf("Manifests: %u (%u failed parse)\n",
1493 stats.repo_tal_stats.mfts, stats.repo_tal_stats.mfts_fail);
1494 printf("Certificate revocation lists: %u\n", stats.repo_tal_stats.crls);
1495 printf("Ghostbuster records: %u\n", stats.repo_tal_stats.gbrs);
1496 printf("Trust Anchor Keys: %u\n", stats.repo_tal_stats.taks);
1497 printf("Repositories: %u\n", stats.repos);
1498 printf("New files moved into validated cache: %u\n",
1499 stats.repo_stats.new_files);
1500 printf("Cleanup: removed %u files, %u directories\n"
1501 "Repository cleanup: kept %u and removed %u superfluous files\n",
1502 stats.repo_stats.del_files, stats.repo_stats.del_dirs,
1503 stats.repo_stats.extra_files, stats.repo_stats.del_extra_files);
1504 printf("VRP Entries: %u (%u unique)\n", stats.repo_tal_stats.vrps,
1505 stats.repo_tal_stats.vrps_uniqs);
1506 printf("VAP Entries: %u (%u unique, %u overflowed)\n",
1507 stats.repo_tal_stats.vaps, stats.repo_tal_stats.vaps_uniqs,
1508 stats.repo_tal_stats.vaps_overflowed);
1509 printf("VSP Entries: %u (%u unique)\n", stats.repo_tal_stats.vsps,
1510 stats.repo_tal_stats.vsps_uniqs);
1511
1512 /* Memory cleanup. */
1513 repo_free();
1514
1515 return rc;
1516
1517 usage:
1518 fprintf(stderr,
1519 "usage: rpki-client [-ABcjmnoRrVvx] [-b sourceaddr] [-d cachedir]"
1520 " [-e rsync_prog]\n"
1521 " [-H fqdn] [-P epoch] [-S skiplist] [-s timeout]"
1522 " [-T table]\n"
1523 " [-t tal] [outputdir]\n"
1524 " rpki-client [-Vv] [-d cachedir] [-j] [-t tal] -f file ..."
1525 "\n");
1526 return 1;
1527 }
1528