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