1*29bf64caSjob /* $OpenBSD: main.c,v 1.278 2025/01/03 10:14:32 job Exp $ */ 29a7e9e7fSjob /* 3a945dbeeSclaudio * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org> 49a7e9e7fSjob * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> 59a7e9e7fSjob * 69a7e9e7fSjob * Permission to use, copy, modify, and distribute this software for any 79a7e9e7fSjob * purpose with or without fee is hereby granted, provided that the above 89a7e9e7fSjob * copyright notice and this permission notice appear in all copies. 99a7e9e7fSjob * 109a7e9e7fSjob * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 119a7e9e7fSjob * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 129a7e9e7fSjob * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 139a7e9e7fSjob * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 149a7e9e7fSjob * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 159a7e9e7fSjob * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 169a7e9e7fSjob * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 179a7e9e7fSjob */ 189a7e9e7fSjob 19c97c4358Sclaudio #include <sys/types.h> 209a7e9e7fSjob #include <sys/queue.h> 21b0404f1fSderaadt #include <sys/resource.h> 2265c1ceceSclaudio #include <sys/socket.h> 23c97c4358Sclaudio #include <sys/statvfs.h> 2465c1ceceSclaudio #include <sys/time.h> 2551b3988bSbenno #include <sys/tree.h> 269a7e9e7fSjob #include <sys/wait.h> 279a7e9e7fSjob 289a7e9e7fSjob #include <assert.h> 2965c1ceceSclaudio #include <dirent.h> 309a7e9e7fSjob #include <err.h> 31b43cdfc8Sclaudio #include <errno.h> 32a0dad605Sclaudio #include <fcntl.h> 332d310113Sclaudio #include <fnmatch.h> 3465c1ceceSclaudio #include <limits.h> 359a7e9e7fSjob #include <poll.h> 3643dfc6a8Sderaadt #include <pwd.h> 3765c1ceceSclaudio #include <signal.h> 3877e4b1bdSjob #include <stdarg.h> 399a7e9e7fSjob #include <stdio.h> 409a7e9e7fSjob #include <stdlib.h> 419a7e9e7fSjob #include <string.h> 423fb1f343Sderaadt #include <syslog.h> 4365c1ceceSclaudio #include <time.h> 449a7e9e7fSjob #include <unistd.h> 4565c1ceceSclaudio 4608db1177Sclaudio #include <imsg.h> 479a7e9e7fSjob 489a7e9e7fSjob #include "extern.h" 490bef1a86Sbenno #include "version.h" 509a7e9e7fSjob 51dc508150Sclaudio const char *tals[TALSZ_MAX]; 52dc508150Sclaudio const char *taldescs[TALSZ_MAX]; 537af68c5cSclaudio unsigned int talrepocnt[TALSZ_MAX]; 541fc2657fSclaudio struct repotalstats talstats[TALSZ_MAX]; 55389eb209Sclaudio int talsz; 56dc508150Sclaudio 577690b5f4Sclaudio size_t entity_queue; 583fb1f343Sderaadt int timeout = 60*60; 599f431460Sclaudio volatile sig_atomic_t killme; 603fb1f343Sderaadt void suicide(int sig); 613fb1f343Sderaadt 625ff9ae98Sclaudio static struct filepath_tree fpt = RB_INITIALIZER(&fpt); 6325d36c5cSclaudio static struct msgbuf *procq, *rsyncq, *httpq, *rrdpq; 64b015ea56Sclaudio static int cachefd, outdirfd; 659a7e9e7fSjob 668825d988Sclaudio int verbose; 67348a9529Sclaudio int noop; 68bf5a499bSjob int excludeas0 = 1; 69acb55ac2Sclaudio int excludeaspa; 70f43c4d92Sclaudio int filemode; 710570e8b4Sjob int shortlistmode; 7286e26441Sderaadt int rrdpon = 1; 734aefc495Sclaudio int repo_timeout; 749463abd5Stb int experimental; 7547de54a6Sclaudio time_t deadline; 769a7e9e7fSjob 77d3c630adStb /* 9999-12-31 23:59:59 UTC */ 78d3c630adStb #define X509_TIME_MAX 253402300799LL 79d3c630adStb /* 0000-01-01 00:00:00 UTC */ 80d3c630adStb #define X509_TIME_MIN -62167219200LL 81d3c630adStb 820876134dSclaudio int64_t evaluation_time = X509_TIME_MIN; 83298d2ca0Sbeck 84b0404f1fSderaadt struct stats stats; 85b0404f1fSderaadt 86087f54cdStb struct fqdnlistentry { 87087f54cdStb LIST_ENTRY(fqdnlistentry) entry; 88087f54cdStb char *fqdn; 890570e8b4Sjob }; 90087f54cdStb LIST_HEAD(fqdns, fqdnlistentry); 91087f54cdStb 92087f54cdStb struct fqdns shortlist = LIST_HEAD_INITIALIZER(fqdns); 93087f54cdStb struct fqdns skiplist = LIST_HEAD_INITIALIZER(fqdns); 940570e8b4Sjob 959a7e9e7fSjob /* 969a7e9e7fSjob * Log a message to stderr if and only if "verbose" is non-zero. 979a7e9e7fSjob * This uses the err(3) functionality. 989a7e9e7fSjob */ 991f9a8b94Sderaadt void 1009a7e9e7fSjob logx(const char *fmt, ...) 1019a7e9e7fSjob { 1029a7e9e7fSjob va_list ap; 1039a7e9e7fSjob 1049a7e9e7fSjob if (verbose && fmt != NULL) { 1059a7e9e7fSjob va_start(ap, fmt); 1069a7e9e7fSjob vwarnx(fmt, ap); 1079a7e9e7fSjob va_end(ap); 1089a7e9e7fSjob } 1099a7e9e7fSjob } 1109a7e9e7fSjob 1112cc3b5f1Sclaudio time_t 1122cc3b5f1Sclaudio getmonotime(void) 1132cc3b5f1Sclaudio { 1142cc3b5f1Sclaudio struct timespec ts; 1152cc3b5f1Sclaudio 1162cc3b5f1Sclaudio if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) 1172cc3b5f1Sclaudio err(1, "clock_gettime"); 1182cc3b5f1Sclaudio return (ts.tv_sec); 1192cc3b5f1Sclaudio } 1202cc3b5f1Sclaudio 121d3c630adStb /* 122d3c630adStb * Time - Evaluation time is used as the current time if it is 123d3c630adStb * larger than X509_TIME_MIN, otherwise the system time is used. 124d3c630adStb */ 125d3c630adStb time_t 126d3c630adStb get_current_time(void) 127d3c630adStb { 128d3c630adStb if (evaluation_time > X509_TIME_MIN) 129d3c630adStb return (time_t)evaluation_time; 130d3c630adStb return time(NULL); 131d3c630adStb } 132d3c630adStb 133eae58378Sclaudio void 1349a7e9e7fSjob entity_free(struct entity *ent) 1359a7e9e7fSjob { 1369a7e9e7fSjob if (ent == NULL) 1379a7e9e7fSjob return; 1389a7e9e7fSjob 139100ded9eSclaudio free(ent->path); 14082d29657Sclaudio free(ent->file); 14132c8d2feSjob free(ent->mftaki); 142100ded9eSclaudio free(ent->data); 1439a7e9e7fSjob free(ent); 1449a7e9e7fSjob } 1459a7e9e7fSjob 1469a7e9e7fSjob /* 1479a7e9e7fSjob * Read a queue entity from the descriptor. 1483b77ffafSjob * Matched by entity_write_req(). 1499a7e9e7fSjob * The pointer must be passed entity_free(). 1509a7e9e7fSjob */ 151eae58378Sclaudio void 1527eb79a4aSclaudio entity_read_req(struct ibuf *b, struct entity *ent) 1539a7e9e7fSjob { 1547eb79a4aSclaudio io_read_buf(b, &ent->type, sizeof(ent->type)); 155df512fbcSclaudio io_read_buf(b, &ent->location, sizeof(ent->location)); 156100ded9eSclaudio io_read_buf(b, &ent->repoid, sizeof(ent->repoid)); 157dc508150Sclaudio io_read_buf(b, &ent->talid, sizeof(ent->talid)); 1580bc420b9Sclaudio io_read_buf(b, &ent->certid, sizeof(ent->certid)); 159100ded9eSclaudio io_read_str(b, &ent->path); 1607eb79a4aSclaudio io_read_str(b, &ent->file); 16132c8d2feSjob io_read_str(b, &ent->mftaki); 16241edc670Sclaudio io_read_buf_alloc(b, (void **)&ent->data, &ent->datasz); 1639a7e9e7fSjob } 1649a7e9e7fSjob 1659a7e9e7fSjob /* 1665ff9ae98Sclaudio * Write the queue entity. 167cfc09c7bSclaudio * Matched by entity_read_req(). 168cfc09c7bSclaudio */ 169cfc09c7bSclaudio static void 1705ff9ae98Sclaudio entity_write_req(const struct entity *ent) 171cfc09c7bSclaudio { 1725ff9ae98Sclaudio struct ibuf *b; 173cfc09c7bSclaudio 17425f7afeeSclaudio b = io_new_buffer(); 17508db1177Sclaudio io_simple_buffer(b, &ent->type, sizeof(ent->type)); 176df512fbcSclaudio io_simple_buffer(b, &ent->location, sizeof(ent->location)); 177100ded9eSclaudio io_simple_buffer(b, &ent->repoid, sizeof(ent->repoid)); 178dc508150Sclaudio io_simple_buffer(b, &ent->talid, sizeof(ent->talid)); 1790bc420b9Sclaudio io_simple_buffer(b, &ent->certid, sizeof(ent->certid)); 180100ded9eSclaudio io_str_buffer(b, ent->path); 18182d29657Sclaudio io_str_buffer(b, ent->file); 18232c8d2feSjob io_str_buffer(b, ent->mftaki); 18341edc670Sclaudio io_buf_buffer(b, ent->data, ent->datasz); 18425d36c5cSclaudio io_close_buffer(procq, b); 185cfc09c7bSclaudio } 186cfc09c7bSclaudio 187100ded9eSclaudio static void 18824b7a7c9Sjob entity_write_repo(const struct repo *rp) 189100ded9eSclaudio { 190100ded9eSclaudio struct ibuf *b; 191100ded9eSclaudio enum rtype type = RTYPE_REPO; 192df512fbcSclaudio enum location loc = DIR_UNKNOWN; 193100ded9eSclaudio unsigned int repoid; 1940c3a2335Sclaudio char *path, *altpath; 1950bc420b9Sclaudio int talid = 0, certid = 0; 196100ded9eSclaudio 197100ded9eSclaudio repoid = repo_id(rp); 1980c3a2335Sclaudio path = repo_basedir(rp, 0); 1990c3a2335Sclaudio altpath = repo_basedir(rp, 1); 200100ded9eSclaudio b = io_new_buffer(); 201100ded9eSclaudio io_simple_buffer(b, &type, sizeof(type)); 202df512fbcSclaudio io_simple_buffer(b, &loc, sizeof(loc)); 203100ded9eSclaudio io_simple_buffer(b, &repoid, sizeof(repoid)); 204100ded9eSclaudio io_simple_buffer(b, &talid, sizeof(talid)); 2050bc420b9Sclaudio io_simple_buffer(b, &certid, sizeof(certid)); 206100ded9eSclaudio io_str_buffer(b, path); 2070c3a2335Sclaudio io_str_buffer(b, altpath); 20832c8d2feSjob io_buf_buffer(b, NULL, 0); /* ent->mftaki */ 20932c8d2feSjob io_buf_buffer(b, NULL, 0); /* ent->data */ 21025d36c5cSclaudio io_close_buffer(procq, b); 211100ded9eSclaudio free(path); 2120c3a2335Sclaudio free(altpath); 213100ded9eSclaudio } 214100ded9eSclaudio 215cfc09c7bSclaudio /* 216cfc09c7bSclaudio * Scan through all queued requests and see which ones are in the given 217cfc09c7bSclaudio * repo, then flush those into the parser process. 218cfc09c7bSclaudio */ 2198ecbadc1Sclaudio void 2208ecbadc1Sclaudio entityq_flush(struct entityq *q, struct repo *rp) 221cfc09c7bSclaudio { 2227690b5f4Sclaudio struct entity *p, *np; 223cfc09c7bSclaudio 224100ded9eSclaudio entity_write_repo(rp); 225100ded9eSclaudio 2268ecbadc1Sclaudio TAILQ_FOREACH_SAFE(p, q, entries, np) { 2275ff9ae98Sclaudio entity_write_req(p); 2288ecbadc1Sclaudio TAILQ_REMOVE(q, p, entries); 2297690b5f4Sclaudio entity_free(p); 230cfc09c7bSclaudio } 231cfc09c7bSclaudio } 232cfc09c7bSclaudio 233cfc09c7bSclaudio /* 2345ff9ae98Sclaudio * Add the heap-allocated file to the queue for processing. 2355ff9ae98Sclaudio */ 2365ff9ae98Sclaudio static void 237df512fbcSclaudio entityq_add(char *path, char *file, enum rtype type, enum location loc, 2380bc420b9Sclaudio struct repo *rp, unsigned char *data, size_t datasz, int talid, int certid, 23932c8d2feSjob char *mftaki) 2405ff9ae98Sclaudio { 2415ff9ae98Sclaudio struct entity *p; 2425ff9ae98Sclaudio 2435ff9ae98Sclaudio if ((p = calloc(1, sizeof(struct entity))) == NULL) 2442c3e7bceSclaudio err(1, NULL); 2455ff9ae98Sclaudio 2465ff9ae98Sclaudio p->type = type; 247df512fbcSclaudio p->location = loc; 248dc508150Sclaudio p->talid = talid; 2490bc420b9Sclaudio p->certid = certid; 25032c8d2feSjob p->mftaki = mftaki; 251100ded9eSclaudio p->path = path; 252100ded9eSclaudio if (rp != NULL) 253100ded9eSclaudio p->repoid = repo_id(rp); 25482d29657Sclaudio p->file = file; 25541edc670Sclaudio p->data = data; 25608df5e84Sclaudio p->datasz = (data != NULL) ? datasz : 0; 2575ff9ae98Sclaudio 2585ff9ae98Sclaudio entity_queue++; 2595ff9ae98Sclaudio 2605ff9ae98Sclaudio /* 2615ff9ae98Sclaudio * Write to the queue if there's no repo or the repo has already 2625ff9ae98Sclaudio * been loaded else enqueue it for later. 2635ff9ae98Sclaudio */ 2645ff9ae98Sclaudio 2658ecbadc1Sclaudio if (rp == NULL || !repo_queued(rp, p)) { 2665ff9ae98Sclaudio entity_write_req(p); 2675ff9ae98Sclaudio entity_free(p); 2685ff9ae98Sclaudio } 269bc7143cdSclaudio } 270bc7143cdSclaudio 271402543e6Sclaudio static void 272b6884e9fSclaudio rrdp_file_resp(unsigned int id, int ok) 2731ef5b48aSclaudio { 2748ecbadc1Sclaudio enum rrdp_msg type = RRDP_FILE; 2751ef5b48aSclaudio struct ibuf *b; 2761ef5b48aSclaudio 27725f7afeeSclaudio b = io_new_buffer(); 2788ecbadc1Sclaudio io_simple_buffer(b, &type, sizeof(type)); 2798ecbadc1Sclaudio io_simple_buffer(b, &id, sizeof(id)); 2808ecbadc1Sclaudio io_simple_buffer(b, &ok, sizeof(ok)); 28125d36c5cSclaudio io_close_buffer(rrdpq, b); 2828ecbadc1Sclaudio } 2831ef5b48aSclaudio 2848ecbadc1Sclaudio void 285b6884e9fSclaudio rrdp_fetch(unsigned int id, const char *uri, const char *local, 2868ecbadc1Sclaudio struct rrdp_session *s) 2878ecbadc1Sclaudio { 2888ecbadc1Sclaudio enum rrdp_msg type = RRDP_START; 2898ecbadc1Sclaudio struct ibuf *b; 2901ef5b48aSclaudio 29125f7afeeSclaudio b = io_new_buffer(); 2928ecbadc1Sclaudio io_simple_buffer(b, &type, sizeof(type)); 2938ecbadc1Sclaudio io_simple_buffer(b, &id, sizeof(id)); 2948ecbadc1Sclaudio io_str_buffer(b, local); 2958ecbadc1Sclaudio io_str_buffer(b, uri); 296b268327aSclaudio 297b268327aSclaudio rrdp_session_buffer(b, s); 29825d36c5cSclaudio io_close_buffer(rrdpq, b); 2998ecbadc1Sclaudio } 3008ecbadc1Sclaudio 3010cda9bffSclaudio void 3020cda9bffSclaudio rrdp_abort(unsigned int id) 3030cda9bffSclaudio { 3040cda9bffSclaudio enum rrdp_msg type = RRDP_ABORT; 3050cda9bffSclaudio struct ibuf *b; 3060cda9bffSclaudio 3070cda9bffSclaudio b = io_new_buffer(); 3080cda9bffSclaudio io_simple_buffer(b, &type, sizeof(type)); 3090cda9bffSclaudio io_simple_buffer(b, &id, sizeof(id)); 31025d36c5cSclaudio io_close_buffer(rrdpq, b); 3110cda9bffSclaudio } 3120cda9bffSclaudio 3138ecbadc1Sclaudio /* 3148ecbadc1Sclaudio * Request a repository sync via rsync URI to directory local. 3158ecbadc1Sclaudio */ 3168ecbadc1Sclaudio void 3170c3a2335Sclaudio rsync_fetch(unsigned int id, const char *uri, const char *local, 3180c3a2335Sclaudio const char *base) 3198ecbadc1Sclaudio { 3208ecbadc1Sclaudio struct ibuf *b; 3218ecbadc1Sclaudio 32225f7afeeSclaudio b = io_new_buffer(); 3238ecbadc1Sclaudio io_simple_buffer(b, &id, sizeof(id)); 3248ecbadc1Sclaudio io_str_buffer(b, local); 3250c3a2335Sclaudio io_str_buffer(b, base); 3268ecbadc1Sclaudio io_str_buffer(b, uri); 32725d36c5cSclaudio io_close_buffer(rsyncq, b); 3288ecbadc1Sclaudio } 3298ecbadc1Sclaudio 3300cda9bffSclaudio void 3310cda9bffSclaudio rsync_abort(unsigned int id) 3320cda9bffSclaudio { 3330cda9bffSclaudio struct ibuf *b; 3340cda9bffSclaudio 3350cda9bffSclaudio b = io_new_buffer(); 3360cda9bffSclaudio io_simple_buffer(b, &id, sizeof(id)); 3370cda9bffSclaudio io_str_buffer(b, NULL); 3380cda9bffSclaudio io_str_buffer(b, NULL); 3390cda9bffSclaudio io_str_buffer(b, NULL); 34025d36c5cSclaudio io_close_buffer(rsyncq, b); 3410cda9bffSclaudio } 3420cda9bffSclaudio 3438ecbadc1Sclaudio /* 3448ecbadc1Sclaudio * Request a file from a https uri, data is written to the file descriptor fd. 3458ecbadc1Sclaudio */ 3468ecbadc1Sclaudio void 347b6884e9fSclaudio http_fetch(unsigned int id, const char *uri, const char *last_mod, int fd) 3488ecbadc1Sclaudio { 3498ecbadc1Sclaudio struct ibuf *b; 3508ecbadc1Sclaudio 35125f7afeeSclaudio b = io_new_buffer(); 3528ecbadc1Sclaudio io_simple_buffer(b, &id, sizeof(id)); 3538ecbadc1Sclaudio io_str_buffer(b, uri); 3548ecbadc1Sclaudio io_str_buffer(b, last_mod); 3551ef5b48aSclaudio /* pass file as fd */ 356cf954fffSclaudio ibuf_fd_set(b, fd); 35725d36c5cSclaudio io_close_buffer(httpq, b); 3581ef5b48aSclaudio } 3591ef5b48aSclaudio 3608ecbadc1Sclaudio /* 3618ecbadc1Sclaudio * Request some XML file on behalf of the rrdp parser. 3628ecbadc1Sclaudio * Create a pipe and pass the pipe endpoints to the http and rrdp process. 3638ecbadc1Sclaudio */ 3641ef5b48aSclaudio static void 365b6884e9fSclaudio rrdp_http_fetch(unsigned int id, const char *uri, const char *last_mod) 366402543e6Sclaudio { 3678ecbadc1Sclaudio enum rrdp_msg type = RRDP_HTTP_INI; 3688ecbadc1Sclaudio struct ibuf *b; 3698ecbadc1Sclaudio int pi[2]; 3708ecbadc1Sclaudio 3718ecbadc1Sclaudio if (pipe2(pi, O_CLOEXEC | O_NONBLOCK) == -1) 3728ecbadc1Sclaudio err(1, "pipe"); 3738ecbadc1Sclaudio 37425f7afeeSclaudio b = io_new_buffer(); 3758ecbadc1Sclaudio io_simple_buffer(b, &type, sizeof(type)); 3768ecbadc1Sclaudio io_simple_buffer(b, &id, sizeof(id)); 377cf954fffSclaudio ibuf_fd_set(b, pi[0]); 37825d36c5cSclaudio io_close_buffer(rrdpq, b); 3798ecbadc1Sclaudio 3808ecbadc1Sclaudio http_fetch(id, uri, last_mod, pi[1]); 3818ecbadc1Sclaudio } 3828ecbadc1Sclaudio 3838ecbadc1Sclaudio void 384b6884e9fSclaudio rrdp_http_done(unsigned int id, enum http_result res, const char *last_mod) 3858ecbadc1Sclaudio { 3868ecbadc1Sclaudio enum rrdp_msg type = RRDP_HTTP_FIN; 387402543e6Sclaudio struct ibuf *b; 388402543e6Sclaudio 3898ecbadc1Sclaudio /* RRDP request, relay response over to the rrdp process */ 39025f7afeeSclaudio b = io_new_buffer(); 3918ecbadc1Sclaudio io_simple_buffer(b, &type, sizeof(type)); 3928ecbadc1Sclaudio io_simple_buffer(b, &id, sizeof(id)); 3938ecbadc1Sclaudio io_simple_buffer(b, &res, sizeof(res)); 3948ecbadc1Sclaudio io_str_buffer(b, last_mod); 39525d36c5cSclaudio io_close_buffer(rrdpq, b); 39613a30d46Sclaudio } 39713a30d46Sclaudio 39813a30d46Sclaudio /* 3999a7e9e7fSjob * Add a file (CER, ROA, CRL) from an MFT file, RFC 6486. 4009a7e9e7fSjob * These are always relative to the directory in which "mft" sits. 4019a7e9e7fSjob */ 4029a7e9e7fSjob static void 4030df89876Sclaudio queue_add_from_mft(const struct mft *mft) 4049a7e9e7fSjob { 40522cec6c4Stb size_t i; 4060df89876Sclaudio struct repo *rp; 4079a7e9e7fSjob const struct mftfile *f; 40832c8d2feSjob char *mftaki, *nfile, *npath = NULL; 4099a7e9e7fSjob 4100df89876Sclaudio rp = repo_byid(mft->repoid); 4119a7e9e7fSjob for (i = 0; i < mft->filesz; i++) { 4129a7e9e7fSjob f = &mft->files[i]; 413940dae96Stb 414e038f1a1Sclaudio if (f->type == RTYPE_INVALID || f->type == RTYPE_CRL) 415940dae96Stb continue; 416940dae96Stb 41723bc08f8Sclaudio if (mft->path != NULL) 41823bc08f8Sclaudio if ((npath = strdup(mft->path)) == NULL) 41923bc08f8Sclaudio err(1, NULL); 42023bc08f8Sclaudio if ((nfile = strdup(f->file)) == NULL) 42123bc08f8Sclaudio err(1, NULL); 42232c8d2feSjob if ((mftaki = strdup(mft->aki)) == NULL) 42332c8d2feSjob err(1, NULL); 42423bc08f8Sclaudio entityq_add(npath, nfile, f->type, f->location, rp, NULL, 0, 4250bc420b9Sclaudio mft->talid, mft->certid, mftaki); 4264be5941aSclaudio } 4279a7e9e7fSjob } 4289a7e9e7fSjob 4299a7e9e7fSjob /* 430f43c4d92Sclaudio * Add a local file to the queue of files to fetch. 4319a7e9e7fSjob */ 4329a7e9e7fSjob static void 433f43c4d92Sclaudio queue_add_file(const char *file, enum rtype type, int talid) 4349a7e9e7fSjob { 435ff683e69Sclaudio unsigned char *buf = NULL; 43641edc670Sclaudio char *nfile; 437ff683e69Sclaudio size_t len = 0; 4389a7e9e7fSjob 4390610060dSjob if (!filemode || strncmp(file, RSYNC_PROTO, RSYNC_PROTO_LEN) != 0) { 44041edc670Sclaudio buf = load_file(file, &len); 441f43c4d92Sclaudio if (buf == NULL) 442f43c4d92Sclaudio err(1, "%s", file); 443ff683e69Sclaudio } 4449a7e9e7fSjob 44566698d98Sclaudio if ((nfile = strdup(file)) == NULL) 44666698d98Sclaudio err(1, NULL); 4479a7e9e7fSjob /* Not in a repository, so directly add to queue. */ 4480bc420b9Sclaudio entityq_add(NULL, nfile, type, DIR_UNKNOWN, NULL, buf, len, talid, 0, 44932c8d2feSjob NULL); 4509a7e9e7fSjob } 4519a7e9e7fSjob 4529a7e9e7fSjob /* 45313a30d46Sclaudio * Add URIs (CER) from a TAL file, RFC 8630. 4549a7e9e7fSjob */ 4559a7e9e7fSjob static void 4568ecbadc1Sclaudio queue_add_from_tal(struct tal *tal) 4579a7e9e7fSjob { 458bc7143cdSclaudio struct repo *repo; 45941edc670Sclaudio unsigned char *data; 460100ded9eSclaudio char *nfile; 4619a7e9e7fSjob 46230a08502Stb assert(tal->num_uris > 0); 46313a30d46Sclaudio 464dc508150Sclaudio if ((taldescs[tal->id] = strdup(tal->descr)) == NULL) 465dc508150Sclaudio err(1, NULL); 466dc508150Sclaudio 467100ded9eSclaudio /* figure out the TA filename, must be done before repo lookup */ 468100ded9eSclaudio nfile = strrchr(tal->uri[0], '/'); 469100ded9eSclaudio assert(nfile != NULL); 470100ded9eSclaudio if ((nfile = strdup(nfile + 1)) == NULL) 471100ded9eSclaudio err(1, NULL); 472100ded9eSclaudio 4739a7e9e7fSjob /* Look up the repository. */ 4747af68c5cSclaudio repo = ta_lookup(tal->id, tal); 4757a81b39dStb if (repo == NULL) { 4767a81b39dStb free(nfile); 4777af68c5cSclaudio return; 4787a81b39dStb } 479402543e6Sclaudio 48041edc670Sclaudio /* steal the pkey from the tal structure */ 48141edc670Sclaudio data = tal->pkey; 48241edc670Sclaudio tal->pkey = NULL; 4832674db3cSclaudio entityq_add(NULL, nfile, RTYPE_CER, DIR_UNKNOWN, repo, data, 4840bc420b9Sclaudio tal->pkeysz, tal->id, tal->id, NULL); 4859a7e9e7fSjob } 4869a7e9e7fSjob 4879a7e9e7fSjob /* 4881d6c3027Sclaudio * Add a manifest (MFT) found in an X509 certificate, RFC 6487. 4899a7e9e7fSjob */ 4909a7e9e7fSjob static void 491bc7143cdSclaudio queue_add_from_cert(const struct cert *cert) 4929a7e9e7fSjob { 493bc7143cdSclaudio struct repo *repo; 494087f54cdStb struct fqdnlistentry *le; 4955a2857b6Sjob char *nfile, *npath, *host; 496100ded9eSclaudio const char *uri, *repouri, *file; 497100ded9eSclaudio size_t repourisz; 4980570e8b4Sjob int shortlisted = 0; 4999a7e9e7fSjob 5000610060dSjob if (strncmp(cert->repo, RSYNC_PROTO, RSYNC_PROTO_LEN) != 0) 5015a2857b6Sjob errx(1, "unexpected protocol"); 502b4964d69Stb host = cert->repo + RSYNC_PROTO_LEN; 5035a2857b6Sjob 504087f54cdStb LIST_FOREACH(le, &skiplist, entry) { 505087f54cdStb if (strncasecmp(host, le->fqdn, strcspn(host, "/")) == 0) { 5065a2857b6Sjob warnx("skipping %s (listed in skiplist)", cert->repo); 5075a2857b6Sjob return; 5085a2857b6Sjob } 5095a2857b6Sjob } 5105a2857b6Sjob 511087f54cdStb LIST_FOREACH(le, &shortlist, entry) { 512087f54cdStb if (strncasecmp(host, le->fqdn, strcspn(host, "/")) == 0) { 5130570e8b4Sjob shortlisted = 1; 5140570e8b4Sjob break; 5150570e8b4Sjob } 5160570e8b4Sjob } 5170570e8b4Sjob if (shortlistmode && shortlisted == 0) { 5180570e8b4Sjob if (verbose) 5190570e8b4Sjob warnx("skipping %s (not shortlisted)", cert->repo); 5200570e8b4Sjob return; 5210570e8b4Sjob } 5220570e8b4Sjob 5237af68c5cSclaudio repo = repo_lookup(cert->talid, cert->repo, 5247af68c5cSclaudio rrdpon ? cert->notify : NULL); 5257af68c5cSclaudio if (repo == NULL) 526402543e6Sclaudio return; 527402543e6Sclaudio 528100ded9eSclaudio /* 529100ded9eSclaudio * Figure out the cert filename and path by chopping up the 530100ded9eSclaudio * MFT URI in the cert based on the repo base URI. 531100ded9eSclaudio */ 532100ded9eSclaudio uri = cert->mft; 533100ded9eSclaudio repouri = repo_uri(repo); 534100ded9eSclaudio repourisz = strlen(repouri); 535100ded9eSclaudio if (strncmp(repouri, cert->mft, repourisz) != 0) { 536100ded9eSclaudio warnx("%s: URI %s outside of repository", repouri, uri); 537100ded9eSclaudio return; 538100ded9eSclaudio } 539100ded9eSclaudio uri += repourisz + 1; /* skip base and '/' */ 540100ded9eSclaudio file = strrchr(uri, '/'); 541100ded9eSclaudio if (file == NULL) { 542100ded9eSclaudio npath = NULL; 543100ded9eSclaudio if ((nfile = strdup(uri)) == NULL) 5448ecbadc1Sclaudio err(1, NULL); 545100ded9eSclaudio } else { 546100ded9eSclaudio if ((npath = strndup(uri, file - uri)) == NULL) 547100ded9eSclaudio err(1, NULL); 548100ded9eSclaudio if ((nfile = strdup(file + 1)) == NULL) 549100ded9eSclaudio err(1, NULL); 550100ded9eSclaudio } 551100ded9eSclaudio 5521fc2657fSclaudio entityq_add(npath, nfile, RTYPE_MFT, DIR_UNKNOWN, repo, NULL, 0, 5530bc420b9Sclaudio cert->talid, cert->certid, NULL); 5549a7e9e7fSjob } 5559a7e9e7fSjob 5569a7e9e7fSjob /* 5579a7e9e7fSjob * Process parsed content. 5589a7e9e7fSjob * For non-ROAs, we grok for more data. 5599a7e9e7fSjob * For ROAs, we want to extract the valid info. 5609a7e9e7fSjob * In all cases, we gather statistics. 5619a7e9e7fSjob */ 5629a7e9e7fSjob static void 5637eb79a4aSclaudio entity_process(struct ibuf *b, struct stats *st, struct vrp_tree *tree, 564d4be4cdeSjob struct brk_tree *brktree, struct vap_tree *vaptree, 565d4be4cdeSjob struct vsp_tree *vsptree) 5669a7e9e7fSjob { 5677690b5f4Sclaudio enum rtype type; 5689a7e9e7fSjob struct tal *tal; 5699a7e9e7fSjob struct cert *cert; 5709a7e9e7fSjob struct mft *mft; 5719a7e9e7fSjob struct roa *roa; 572a29ddfd5Sjob struct aspa *aspa; 573d4be4cdeSjob struct spl *spl; 5744f5f25cbSclaudio struct repo *rp; 575de22d368Sclaudio char *file; 57608ac1330Sjob time_t mtime; 5774f5f25cbSclaudio unsigned int id; 5781fc2657fSclaudio int talid; 5792a5028c1Sclaudio int ok = 1; 5809a7e9e7fSjob 5819a7e9e7fSjob /* 5829a7e9e7fSjob * For most of these, we first read whether there's any content 5839a7e9e7fSjob * at all---this means that the syntactic parse failed (X509 5849a7e9e7fSjob * certificate, for example). 5859a7e9e7fSjob * We follow that up with whether the resources didn't parse. 5869a7e9e7fSjob */ 5877eb79a4aSclaudio io_read_buf(b, &type, sizeof(type)); 58839c0924aSclaudio io_read_buf(b, &id, sizeof(id)); 5891fc2657fSclaudio io_read_buf(b, &talid, sizeof(talid)); 590de22d368Sclaudio io_read_str(b, &file); 59108ac1330Sjob io_read_buf(b, &mtime, sizeof(mtime)); 592de22d368Sclaudio 593f43c4d92Sclaudio /* in filemode messages can be ignored, only the accounting matters */ 594f43c4d92Sclaudio if (filemode) 595f43c4d92Sclaudio goto done; 596f43c4d92Sclaudio 59792360713Sclaudio if (filepath_valid(&fpt, file, talid)) { 598de22d368Sclaudio warnx("%s: File already visited", file); 599f43c4d92Sclaudio goto done; 600de22d368Sclaudio } 6019a7e9e7fSjob 6024f5f25cbSclaudio rp = repo_byid(id); 6031fc2657fSclaudio repo_stat_inc(rp, talid, type, STYPE_OK); 604a5f50487Sjob repostats_new_files_inc(rp, file); 6057690b5f4Sclaudio switch (type) { 6069a7e9e7fSjob case RTYPE_TAL: 6079a7e9e7fSjob st->tals++; 6087eb79a4aSclaudio tal = tal_read(b); 609bc7143cdSclaudio queue_add_from_tal(tal); 6109a7e9e7fSjob tal_free(tal); 6119a7e9e7fSjob break; 6129a7e9e7fSjob case RTYPE_CER: 6132a5028c1Sclaudio io_read_buf(b, &ok, sizeof(ok)); 6142a5028c1Sclaudio if (ok == 0) { 6151fc2657fSclaudio repo_stat_inc(rp, talid, type, STYPE_FAIL); 6169a7e9e7fSjob break; 6179a7e9e7fSjob } 6187eb79a4aSclaudio cert = cert_read(b); 619820799d0Sjob switch (cert->purpose) { 620eb6f3761Stb case CERT_PURPOSE_TA: 621820799d0Sjob case CERT_PURPOSE_CA: 622bc7143cdSclaudio queue_add_from_cert(cert); 623820799d0Sjob break; 624820799d0Sjob case CERT_PURPOSE_BGPSEC_ROUTER: 6256b83d8e3Sjob cert_insert_brks(brktree, cert); 6261fc2657fSclaudio repo_stat_inc(rp, talid, type, STYPE_BGPSEC); 627820799d0Sjob break; 628820799d0Sjob default: 629eb6f3761Stb errx(1, "unexpected %s", purpose2str(cert->purpose)); 630820799d0Sjob break; 631820799d0Sjob } 6329a7e9e7fSjob cert_free(cert); 6339a7e9e7fSjob break; 6349a7e9e7fSjob case RTYPE_MFT: 6352a5028c1Sclaudio io_read_buf(b, &ok, sizeof(ok)); 6362a5028c1Sclaudio if (ok == 0) { 6371fc2657fSclaudio repo_stat_inc(rp, talid, type, STYPE_FAIL); 6389a7e9e7fSjob break; 6399a7e9e7fSjob } 6407eb79a4aSclaudio mft = mft_read(b); 641318f0572Sjob if (mft->seqnum_gap) 642318f0572Sjob repo_stat_inc(rp, talid, type, STYPE_SEQNUM_GAP); 6430df89876Sclaudio queue_add_from_mft(mft); 6449a7e9e7fSjob mft_free(mft); 6459a7e9e7fSjob break; 6469a7e9e7fSjob case RTYPE_CRL: 647e038f1a1Sclaudio /* CRLs are sent together with MFT and not accounted for */ 648e038f1a1Sclaudio entity_queue++; 6499a7e9e7fSjob break; 6509a7e9e7fSjob case RTYPE_ROA: 6512a5028c1Sclaudio io_read_buf(b, &ok, sizeof(ok)); 6522a5028c1Sclaudio if (ok == 0) { 6531fc2657fSclaudio repo_stat_inc(rp, talid, type, STYPE_FAIL); 6549a7e9e7fSjob break; 6559a7e9e7fSjob } 6567eb79a4aSclaudio roa = roa_read(b); 657a382efa2Sclaudio if (roa->valid) 6584f5f25cbSclaudio roa_insert_vrps(tree, roa, rp); 659a382efa2Sclaudio else 6601fc2657fSclaudio repo_stat_inc(rp, talid, type, STYPE_INVALID); 6619a7e9e7fSjob roa_free(roa); 6629a7e9e7fSjob break; 6634be5941aSclaudio case RTYPE_GBR: 6644be5941aSclaudio break; 665a29ddfd5Sjob case RTYPE_ASPA: 6662a5028c1Sclaudio io_read_buf(b, &ok, sizeof(ok)); 6672a5028c1Sclaudio if (ok == 0) { 6681fc2657fSclaudio repo_stat_inc(rp, talid, type, STYPE_FAIL); 669a29ddfd5Sjob break; 670a29ddfd5Sjob } 671a29ddfd5Sjob aspa = aspa_read(b); 672a29ddfd5Sjob if (aspa->valid) 673cd55b6bdSjob aspa_insert_vaps(file, vaptree, aspa, rp); 674a29ddfd5Sjob else 6751fc2657fSclaudio repo_stat_inc(rp, talid, type, STYPE_INVALID); 676a29ddfd5Sjob aspa_free(aspa); 677a29ddfd5Sjob break; 678d4be4cdeSjob case RTYPE_SPL: 6792a5028c1Sclaudio io_read_buf(b, &ok, sizeof(ok)); 6802a5028c1Sclaudio if (ok == 0) { 6819463abd5Stb if (experimental) 682d4be4cdeSjob repo_stat_inc(rp, talid, type, STYPE_FAIL); 683d4be4cdeSjob break; 684d4be4cdeSjob } 685d4be4cdeSjob spl = spl_read(b); 686d4be4cdeSjob if (spl->valid) 687d4be4cdeSjob spl_insert_vsps(vsptree, spl, rp); 688d4be4cdeSjob else 689d4be4cdeSjob repo_stat_inc(rp, talid, type, STYPE_INVALID); 690d4be4cdeSjob spl_free(spl); 691d4be4cdeSjob break; 692ee2a33daSjob case RTYPE_TAK: 693ee2a33daSjob break; 69412a5f4e3Sclaudio case RTYPE_FILE: 69512a5f4e3Sclaudio break; 6969a7e9e7fSjob default: 69739c0924aSclaudio warnx("%s: unknown entity type %d", file, type); 69839c0924aSclaudio break; 6999a7e9e7fSjob } 7007690b5f4Sclaudio 70192360713Sclaudio if (filepath_add(&fpt, file, talid, mtime, ok) == 0) 70292360713Sclaudio errx(1, "%s: File already in tree", file); 70392360713Sclaudio 704f43c4d92Sclaudio done: 705de22d368Sclaudio free(file); 7067690b5f4Sclaudio entity_queue--; 7079a7e9e7fSjob } 7089a7e9e7fSjob 7097eb79a4aSclaudio static void 7107eb79a4aSclaudio rrdp_process(struct ibuf *b) 7117eb79a4aSclaudio { 7127eb79a4aSclaudio enum rrdp_msg type; 7137eb79a4aSclaudio enum publish_type pt; 714b268327aSclaudio struct rrdp_session *s; 7157eb79a4aSclaudio char *uri, *last_mod, *data; 7167eb79a4aSclaudio char hash[SHA256_DIGEST_LENGTH]; 717b6884e9fSclaudio size_t dsz; 718b6884e9fSclaudio unsigned int id; 7197eb79a4aSclaudio int ok; 7207eb79a4aSclaudio 7217eb79a4aSclaudio io_read_buf(b, &type, sizeof(type)); 7227eb79a4aSclaudio io_read_buf(b, &id, sizeof(id)); 7237eb79a4aSclaudio 7247eb79a4aSclaudio switch (type) { 7257eb79a4aSclaudio case RRDP_END: 7267eb79a4aSclaudio io_read_buf(b, &ok, sizeof(ok)); 7277eb79a4aSclaudio rrdp_finish(id, ok); 7287eb79a4aSclaudio break; 7297eb79a4aSclaudio case RRDP_HTTP_REQ: 7307eb79a4aSclaudio io_read_str(b, &uri); 7317eb79a4aSclaudio io_read_str(b, &last_mod); 7327eb79a4aSclaudio rrdp_http_fetch(id, uri, last_mod); 7337eb79a4aSclaudio break; 7347eb79a4aSclaudio case RRDP_SESSION: 735b268327aSclaudio s = rrdp_session_read(b); 736b268327aSclaudio rrdp_session_save(id, s); 737b268327aSclaudio rrdp_session_free(s); 7387eb79a4aSclaudio break; 7397eb79a4aSclaudio case RRDP_FILE: 7407eb79a4aSclaudio io_read_buf(b, &pt, sizeof(pt)); 7417eb79a4aSclaudio if (pt != PUB_ADD) 7427eb79a4aSclaudio io_read_buf(b, &hash, sizeof(hash)); 7437eb79a4aSclaudio io_read_str(b, &uri); 7447eb79a4aSclaudio io_read_buf_alloc(b, (void **)&data, &dsz); 7457eb79a4aSclaudio 7467eb79a4aSclaudio ok = rrdp_handle_file(id, pt, uri, hash, sizeof(hash), 7477eb79a4aSclaudio data, dsz); 7487eb79a4aSclaudio rrdp_file_resp(id, ok); 7497eb79a4aSclaudio 7507eb79a4aSclaudio free(uri); 7517eb79a4aSclaudio free(data); 7527eb79a4aSclaudio break; 753264f4ef9Sclaudio case RRDP_CLEAR: 754264f4ef9Sclaudio rrdp_clear(id); 755264f4ef9Sclaudio break; 7567eb79a4aSclaudio default: 7577eb79a4aSclaudio errx(1, "unexpected rrdp response"); 7587eb79a4aSclaudio } 7597eb79a4aSclaudio } 7607eb79a4aSclaudio 7614f5f25cbSclaudio static void 7621fc2657fSclaudio sum_stats(const struct repo *rp, const struct repotalstats *in, void *arg) 7634f5f25cbSclaudio { 7641fc2657fSclaudio struct repotalstats *out = arg; 7654f5f25cbSclaudio 7664f5f25cbSclaudio out->mfts += in->mfts; 7674f5f25cbSclaudio out->mfts_fail += in->mfts_fail; 768318f0572Sjob out->mfts_gap += in->mfts_gap; 7694f5f25cbSclaudio out->certs += in->certs; 7704f5f25cbSclaudio out->certs_fail += in->certs_fail; 7714f5f25cbSclaudio out->roas += in->roas; 7724f5f25cbSclaudio out->roas_fail += in->roas_fail; 7734f5f25cbSclaudio out->roas_invalid += in->roas_invalid; 7744f5f25cbSclaudio out->aspas += in->aspas; 7754f5f25cbSclaudio out->aspas_fail += in->aspas_fail; 7764f5f25cbSclaudio out->aspas_invalid += in->aspas_invalid; 7774f5f25cbSclaudio out->brks += in->brks; 7784f5f25cbSclaudio out->crls += in->crls; 7794f5f25cbSclaudio out->gbrs += in->gbrs; 7804f5f25cbSclaudio out->taks += in->taks; 7814f5f25cbSclaudio out->vrps += in->vrps; 7824f5f25cbSclaudio out->vrps_uniqs += in->vrps_uniqs; 7834f5f25cbSclaudio out->vaps += in->vaps; 7844f5f25cbSclaudio out->vaps_uniqs += in->vaps_uniqs; 7854f5f25cbSclaudio out->vaps_pas += in->vaps_pas; 7867e284d50Stb out->vaps_overflowed += in->vaps_overflowed; 787d4be4cdeSjob out->spls += in->spls; 788d4be4cdeSjob out->spls_fail += in->spls_fail; 789d4be4cdeSjob out->spls_invalid += in->spls_invalid; 790d4be4cdeSjob out->vsps += in->vsps; 791d4be4cdeSjob out->vsps_uniqs += in->vsps_uniqs; 7921fc2657fSclaudio } 7934f5f25cbSclaudio 7941fc2657fSclaudio static void 7951fc2657fSclaudio sum_repostats(const struct repo *rp, const struct repostats *in, void *arg) 7961fc2657fSclaudio { 7971fc2657fSclaudio struct repostats *out = arg; 7981fc2657fSclaudio 7991fc2657fSclaudio out->del_files += in->del_files; 8001fc2657fSclaudio out->extra_files += in->extra_files; 8011438586aSclaudio out->del_extra_files += in->del_extra_files; 8021fc2657fSclaudio out->del_dirs += in->del_dirs; 803a5f50487Sjob out->new_files += in->new_files; 8044f5f25cbSclaudio timespecadd(&in->sync_time, &out->sync_time, &out->sync_time); 8054f5f25cbSclaudio } 8064f5f25cbSclaudio 807fc577bf8Sclaudio /* 808fc577bf8Sclaudio * Assign filenames ending in ".tal" in "/etc/rpki" into "tals", 809fc577bf8Sclaudio * returning the number of files found and filled-in. 810fc577bf8Sclaudio * This may be zero. 81121fcc688Stb * Don't exceed "max" filenames. 812fc577bf8Sclaudio */ 813389eb209Sclaudio static int 814dc508150Sclaudio tal_load_default(void) 8152d310113Sclaudio { 8165b613a61Sclaudio static const char *confdir = "/etc/rpki"; 817389eb209Sclaudio int s = 0; 8182d310113Sclaudio char *path; 8192d310113Sclaudio DIR *dirp; 8202d310113Sclaudio struct dirent *dp; 8212d310113Sclaudio 8225b613a61Sclaudio dirp = opendir(confdir); 8232d310113Sclaudio if (dirp == NULL) 8245b613a61Sclaudio err(1, "open %s", confdir); 8252d310113Sclaudio while ((dp = readdir(dirp)) != NULL) { 8262d310113Sclaudio if (fnmatch("*.tal", dp->d_name, FNM_PERIOD) == FNM_NOMATCH) 8272d310113Sclaudio continue; 828dc508150Sclaudio if (s >= TALSZ_MAX) 8294e226c0bSbenno err(1, "too many tal files found in %s", 8305b613a61Sclaudio confdir); 8315b613a61Sclaudio if (asprintf(&path, "%s/%s", confdir, dp->d_name) == -1) 8322c3e7bceSclaudio err(1, NULL); 8332d310113Sclaudio tals[s++] = path; 8342d310113Sclaudio } 8352d310113Sclaudio closedir(dirp); 8363a8a5c8aSderaadt return s; 8372d310113Sclaudio } 8382d310113Sclaudio 8395a2857b6Sjob /* 8405a2857b6Sjob * Load the list of FQDNs from the skiplist which are to be distrusted. 8415a2857b6Sjob * Return 0 on success. 8425a2857b6Sjob */ 8435a2857b6Sjob static void 8445a2857b6Sjob load_skiplist(const char *slf) 8455a2857b6Sjob { 846087f54cdStb struct fqdnlistentry *le; 8475a2857b6Sjob FILE *fp; 8485a2857b6Sjob char *line = NULL; 8496cff47fcStb size_t linesize = 0, linelen; 8505a2857b6Sjob 8515a2857b6Sjob if ((fp = fopen(slf, "r")) == NULL) { 852b764f8f6Stb if (errno == ENOENT && strcmp(slf, DEFAULT_SKIPLIST_FILE) == 0) 8535a2857b6Sjob return; 854b764f8f6Stb err(1, "failed to open %s", slf); 8555a2857b6Sjob } 8565a2857b6Sjob 857b764f8f6Stb while (getline(&line, &linesize, fp) != -1) { 8585a2857b6Sjob /* just eat comment lines or empty lines*/ 8595a2857b6Sjob if (line[0] == '#' || line[0] == '\n') 8605a2857b6Sjob continue; 8615a2857b6Sjob 8625a2857b6Sjob if (line[0] == ' ' || line[0] == '\t') 8635a2857b6Sjob errx(1, "invalid entry in skiplist: %s", line); 8645a2857b6Sjob 8655a2857b6Sjob /* 8665a2857b6Sjob * Ignore anything after comment sign, whitespaces, 8675a2857b6Sjob * also chop off LF or CR. 8685a2857b6Sjob */ 869b764f8f6Stb linelen = strcspn(line, " #\r\n\t"); 870b764f8f6Stb line[linelen] = '\0'; 8715a2857b6Sjob 8726cff47fcStb if (!valid_uri(line, linelen, NULL)) 8735a2857b6Sjob errx(1, "invalid entry in skiplist: %s", line); 8745a2857b6Sjob 875087f54cdStb if ((le = malloc(sizeof(struct fqdnlistentry))) == NULL) 8765a2857b6Sjob err(1, NULL); 877087f54cdStb if ((le->fqdn = strdup(line)) == NULL) 8785a2857b6Sjob err(1, NULL); 8795a2857b6Sjob 880087f54cdStb LIST_INSERT_HEAD(&skiplist, le, entry); 8815a2857b6Sjob stats.skiplistentries++; 8825a2857b6Sjob } 88327e47f52Stb if (ferror(fp)) 88427e47f52Stb err(1, "error reading %s", slf); 8855a2857b6Sjob 8865a2857b6Sjob fclose(fp); 8875a2857b6Sjob free(line); 8885a2857b6Sjob } 8895a2857b6Sjob 8900570e8b4Sjob /* 8910570e8b4Sjob * Load shortlist entries. 8920570e8b4Sjob */ 8930570e8b4Sjob static void 8940570e8b4Sjob load_shortlist(const char *fqdn) 8950570e8b4Sjob { 896087f54cdStb struct fqdnlistentry *le; 8970570e8b4Sjob 8980570e8b4Sjob if (!valid_uri(fqdn, strlen(fqdn), NULL)) 8990570e8b4Sjob errx(1, "invalid fqdn passed to -q: %s", fqdn); 9000570e8b4Sjob 901087f54cdStb if ((le = malloc(sizeof(struct fqdnlistentry))) == NULL) 9020570e8b4Sjob err(1, NULL); 9030570e8b4Sjob 904087f54cdStb if ((le->fqdn = strdup(fqdn)) == NULL) 9050570e8b4Sjob err(1, NULL); 9060570e8b4Sjob 907087f54cdStb LIST_INSERT_HEAD(&shortlist, le, entry); 9080570e8b4Sjob } 9090570e8b4Sjob 910c97c4358Sclaudio static void 911c97c4358Sclaudio check_fs_size(int fd, const char *cachedir) 912c97c4358Sclaudio { 913c97c4358Sclaudio struct statvfs fs; 914e7075852Sjob unsigned long long minsize = 500 * 1024 * 1024; 915e7075852Sjob unsigned long long minnode = 300 * 1000; 916c97c4358Sclaudio 917c97c4358Sclaudio if (fstatvfs(fd, &fs) == -1) 918c97c4358Sclaudio err(1, "statfs %s", cachedir); 919c97c4358Sclaudio 920dadcb260Stb if (fs.f_bavail < minsize / fs.f_frsize || 921bdd1f630Stb (fs.f_ffree > 0 && fs.f_favail < minnode)) { 922c97c4358Sclaudio fprintf(stderr, "WARNING: rpki-client may need more than " 923a94e758dSjob "the available disk space\n" 924c97c4358Sclaudio "on the file-system holding %s.\n", cachedir); 925e7075852Sjob fprintf(stderr, "available space: %llukB, " 926e7075852Sjob "suggested minimum %llukB\n", 927e7075852Sjob (unsigned long long)fs.f_bavail * fs.f_frsize / 1024, 928c97c4358Sclaudio minsize / 1024); 929e7075852Sjob fprintf(stderr, "available inodes: %llu, " 930e7075852Sjob "suggested minimum: %llu\n\n", 931e7075852Sjob (unsigned long long)fs.f_favail, minnode); 932c97c4358Sclaudio fflush(stderr); 933c97c4358Sclaudio } 934c97c4358Sclaudio } 935c97c4358Sclaudio 9361db5fd2bSclaudio static pid_t 9371db5fd2bSclaudio process_start(const char *title, int *fd) 9381db5fd2bSclaudio { 9391db5fd2bSclaudio int fl = SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK; 9401db5fd2bSclaudio pid_t pid; 9411db5fd2bSclaudio int pair[2]; 9421db5fd2bSclaudio 9431db5fd2bSclaudio if (socketpair(AF_UNIX, fl, 0, pair) == -1) 9441db5fd2bSclaudio err(1, "socketpair"); 9451db5fd2bSclaudio if ((pid = fork()) == -1) 9461db5fd2bSclaudio err(1, "fork"); 9471db5fd2bSclaudio 9481db5fd2bSclaudio if (pid == 0) { 9491db5fd2bSclaudio setproctitle("%s", title); 9501db5fd2bSclaudio /* change working directory to the cache directory */ 9511db5fd2bSclaudio if (fchdir(cachefd) == -1) 9521db5fd2bSclaudio err(1, "fchdir"); 9535887f5d9Stb if (!filemode && timeout > 0) 9541db5fd2bSclaudio alarm(timeout); 9551db5fd2bSclaudio close(pair[1]); 9561db5fd2bSclaudio *fd = pair[0]; 9571db5fd2bSclaudio } else { 9581db5fd2bSclaudio close(pair[0]); 9591db5fd2bSclaudio *fd = pair[1]; 9601db5fd2bSclaudio } 9611db5fd2bSclaudio return pid; 9621db5fd2bSclaudio } 9631db5fd2bSclaudio 9643fb1f343Sderaadt void 9653fb1f343Sderaadt suicide(int sig __attribute__((unused))) 9663fb1f343Sderaadt { 9679f431460Sclaudio killme = 1; 9683fb1f343Sderaadt } 9693fb1f343Sderaadt 9708ecbadc1Sclaudio #define NPFD 4 971173bcbfaSclaudio 9729a7e9e7fSjob int 9739a7e9e7fSjob main(int argc, char *argv[]) 9749a7e9e7fSjob { 975bf431fffSclaudio int rc, c, i, st, hangup = 0; 976bf431fffSclaudio int procfd, rsyncfd, httpfd, rrdpfd; 977c89f2969Sclaudio pid_t pid, procpid, rsyncpid, httppid, rrdppid; 978173bcbfaSclaudio struct pollfd pfd[NPFD]; 979173bcbfaSclaudio struct msgbuf *queues[NPFD]; 980b5fa5d51Sclaudio struct ibuf *b; 981b58ccec3Sbenno char *rsync_prog = "openrsync"; 982b58ccec3Sbenno char *bind_addr = NULL; 983b015ea56Sclaudio const char *cachedir = NULL, *outputdir = NULL; 984dc508150Sclaudio const char *errs, *name; 9855a2857b6Sjob const char *skiplistfile = NULL; 9867eb79a4aSclaudio struct vrp_tree vrps = RB_INITIALIZER(&vrps); 987d4be4cdeSjob struct vsp_tree vsps = RB_INITIALIZER(&vsps); 9887eb79a4aSclaudio struct brk_tree brks = RB_INITIALIZER(&brks); 989a29ddfd5Sjob struct vap_tree vaps = RB_INITIALIZER(&vaps); 990b0404f1fSderaadt struct rusage ru; 99165c1ceceSclaudio struct timespec start_time, now_time; 992b0404f1fSderaadt 99365c1ceceSclaudio clock_gettime(CLOCK_MONOTONIC, &start_time); 9949a7e9e7fSjob 99543dfc6a8Sderaadt /* If started as root, priv-drop to _rpki-client */ 99643dfc6a8Sderaadt if (getuid() == 0) { 99743dfc6a8Sderaadt struct passwd *pw; 99843dfc6a8Sderaadt 99943dfc6a8Sderaadt pw = getpwnam("_rpki-client"); 100043dfc6a8Sderaadt if (!pw) 100143dfc6a8Sderaadt errx(1, "no _rpki-client user to revoke to"); 100243dfc6a8Sderaadt if (setgroups(1, &pw->pw_gid) == -1 || 100343dfc6a8Sderaadt setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 || 100443dfc6a8Sderaadt setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) 100543dfc6a8Sderaadt err(1, "unable to revoke privs"); 100635c92996Sbenno } 10075b613a61Sclaudio cachedir = RPKI_PATH_BASE_DIR; 10085b613a61Sclaudio outputdir = RPKI_PATH_OUT_DIR; 10094aefc495Sclaudio repo_timeout = timeout / 4; 10105a2857b6Sjob skiplistfile = DEFAULT_SKIPLIST_FILE; 101143dfc6a8Sderaadt 10121ef5b48aSclaudio if (pledge("stdio rpath wpath cpath inet fattr dns sendfd recvfd " 10131ef5b48aSclaudio "proc exec unveil", NULL) == -1) 10144e226c0bSbenno err(1, "pledge"); 10159a7e9e7fSjob 1016bf5a499bSjob while ((c = 1017*29bf64caSjob getopt(argc, argv, "0Ab:Bcd:e:fH:jmnoP:Rs:S:t:vVx")) != -1) 10189a7e9e7fSjob switch (c) { 1019bf5a499bSjob case '0': 1020bf5a499bSjob excludeas0 = 0; 1021bf5a499bSjob break; 1022acb55ac2Sclaudio case 'A': 1023acb55ac2Sclaudio excludeaspa = 1; 1024acb55ac2Sclaudio break; 1025e1db9aaeSclaudio case 'b': 1026e1db9aaeSclaudio bind_addr = optarg; 1027e1db9aaeSclaudio break; 10285a1f8137Sclaudio case 'B': 1029ed26e04bSderaadt outformats |= FORMAT_BIRD; 10305a1f8137Sclaudio break; 10315a1f8137Sclaudio case 'c': 1032ed26e04bSderaadt outformats |= FORMAT_CSV; 10335a1f8137Sclaudio break; 10345b613a61Sclaudio case 'd': 10355b613a61Sclaudio cachedir = optarg; 10365b613a61Sclaudio break; 10379a7e9e7fSjob case 'e': 10389a7e9e7fSjob rsync_prog = optarg; 10399a7e9e7fSjob break; 104041edc670Sclaudio case 'f': 1041f43c4d92Sclaudio filemode = 1; 104241edc670Sclaudio noop = 1; 104341edc670Sclaudio break; 10440570e8b4Sjob case 'H': 10450570e8b4Sjob shortlistmode = 1; 10460570e8b4Sjob load_shortlist(optarg); 10470570e8b4Sjob break; 1048a382efa2Sclaudio case 'j': 1049e0ca9872Sderaadt outformats |= FORMAT_JSON; 1050a382efa2Sclaudio break; 10514f5f25cbSclaudio case 'm': 10524f5f25cbSclaudio outformats |= FORMAT_OMETRIC; 10534f5f25cbSclaudio break; 10549a7e9e7fSjob case 'n': 10559a7e9e7fSjob noop = 1; 10569a7e9e7fSjob break; 1057ed26e04bSderaadt case 'o': 1058ed26e04bSderaadt outformats |= FORMAT_OPENBGPD; 1059ed26e04bSderaadt break; 1060298d2ca0Sbeck case 'P': 10610876134dSclaudio evaluation_time = strtonum(optarg, X509_TIME_MIN + 1, 1062298d2ca0Sbeck X509_TIME_MAX, &errs); 1063298d2ca0Sbeck if (errs) 1064298d2ca0Sbeck errx(1, "-P: time in seconds %s", errs); 1065298d2ca0Sbeck break; 10668ecbadc1Sclaudio case 'R': 10678ecbadc1Sclaudio rrdpon = 0; 10688ecbadc1Sclaudio break; 10693fb1f343Sderaadt case 's': 10703fb1f343Sderaadt timeout = strtonum(optarg, 0, 24*60*60, &errs); 10713fb1f343Sderaadt if (errs) 107285a6ba82Sclaudio errx(1, "-s: %s", errs); 1073cecb0802Sjob if (timeout == 0) 1074cecb0802Sjob repo_timeout = 24*60*60; 1075cecb0802Sjob else 1076cecb0802Sjob repo_timeout = timeout / 4; 10773fb1f343Sderaadt break; 10785a2857b6Sjob case 'S': 10795a2857b6Sjob skiplistfile = optarg; 10805a2857b6Sjob break; 10812d310113Sclaudio case 't': 10822d310113Sclaudio if (talsz >= TALSZ_MAX) 108343281945Stb err(1, "too many tal files specified"); 10842d310113Sclaudio tals[talsz++] = optarg; 10852d310113Sclaudio break; 10869a7e9e7fSjob case 'v': 10879a7e9e7fSjob verbose++; 10889a7e9e7fSjob break; 1089c4f4bcd5Sclaudio case 'V': 109022e6bf8aSclaudio fprintf(stderr, "rpki-client %s\n", RPKI_VERSION); 109122e6bf8aSclaudio return 0; 10929463abd5Stb case 'x': 10939463abd5Stb experimental = 1; 10949463abd5Stb break; 10959a7e9e7fSjob default: 10969a7e9e7fSjob goto usage; 10979a7e9e7fSjob } 10989a7e9e7fSjob 10999a7e9e7fSjob argv += optind; 11002d310113Sclaudio argc -= optind; 110158516d64Sclaudio 110258516d64Sclaudio if (!filemode) { 110343dfc6a8Sderaadt if (argc == 1) 1104ed26e04bSderaadt outputdir = argv[0]; 110543dfc6a8Sderaadt else if (argc > 1) 11069a7e9e7fSjob goto usage; 11072d310113Sclaudio 110858516d64Sclaudio if (outputdir == NULL) { 110958516d64Sclaudio warnx("output directory required"); 111058516d64Sclaudio goto usage; 111158516d64Sclaudio } 111258516d64Sclaudio } else { 111358516d64Sclaudio if (argc == 0) 111458516d64Sclaudio goto usage; 111558516d64Sclaudio outputdir = NULL; 111658516d64Sclaudio } 1117cec4d91cSclaudio 11185b613a61Sclaudio if (cachedir == NULL) { 11195b613a61Sclaudio warnx("cache directory required"); 11205b613a61Sclaudio goto usage; 11215b613a61Sclaudio } 1122f43c4d92Sclaudio 112358516d64Sclaudio signal(SIGPIPE, SIG_IGN); 11245b613a61Sclaudio 1125b7041c07Sderaadt if ((cachefd = open(cachedir, O_RDONLY | O_DIRECTORY)) == -1) 1126a0dad605Sclaudio err(1, "cache directory %s", cachedir); 1127f43c4d92Sclaudio if (outputdir != NULL) { 1128b7041c07Sderaadt if ((outdirfd = open(outputdir, O_RDONLY | O_DIRECTORY)) == -1) 1129b015ea56Sclaudio err(1, "output directory %s", outputdir); 1130f43c4d92Sclaudio if (outformats == 0) 1131f43c4d92Sclaudio outformats = FORMAT_OPENBGPD; 1132f43c4d92Sclaudio } 1133a0dad605Sclaudio 1134c97c4358Sclaudio check_fs_size(cachefd, cachedir); 1135c97c4358Sclaudio 11362d310113Sclaudio if (talsz == 0) 1137dc508150Sclaudio talsz = tal_load_default(); 11382d310113Sclaudio if (talsz == 0) 11394e226c0bSbenno err(1, "no TAL files found in %s", "/etc/rpki"); 11409a7e9e7fSjob 1141891d6bceSjob /* Load optional constraint files sitting next to the TALs. */ 1142891d6bceSjob constraints_load(); 1143891d6bceSjob 11449a7e9e7fSjob /* 11459a7e9e7fSjob * Create the file reader as a jailed child process. 11469a7e9e7fSjob * It will be responsible for reading all of the files (ROAs, 11479a7e9e7fSjob * manifests, certificates, etc.) and returning contents. 11489a7e9e7fSjob */ 11499a7e9e7fSjob 1150bf431fffSclaudio procpid = process_start("parser", &procfd); 11519a7e9e7fSjob if (procpid == 0) { 1152c4a9443cSclaudio if (!filemode) 1153bf431fffSclaudio proc_parser(procfd); 1154c4a9443cSclaudio else 1155bf431fffSclaudio proc_filemode(procfd); 11569a7e9e7fSjob } 11579a7e9e7fSjob 1158891d6bceSjob /* Constraints are only needed in the filemode and parser processes. */ 1159891d6bceSjob constraints_unload(); 1160891d6bceSjob 11619a7e9e7fSjob /* 11629a7e9e7fSjob * Create a process that will do the rsync'ing. 11639a7e9e7fSjob * This process is responsible for making sure that all the 11649a7e9e7fSjob * repositories referenced by a certificate manifest (or the 11659a7e9e7fSjob * TAL) exists and has been downloaded. 11669a7e9e7fSjob */ 11679a7e9e7fSjob 1168348a9529Sclaudio if (!noop) { 1169bf431fffSclaudio rsyncpid = process_start("rsync", &rsyncfd); 11709a7e9e7fSjob if (rsyncpid == 0) { 1171bf431fffSclaudio close(procfd); 1172bf431fffSclaudio proc_rsync(rsync_prog, bind_addr, rsyncfd); 11739a7e9e7fSjob } 117438177ce4Sclaudio } else { 1175bf431fffSclaudio rsyncfd = -1; 117638177ce4Sclaudio rsyncpid = -1; 117738177ce4Sclaudio } 11789a7e9e7fSjob 11791ef5b48aSclaudio /* 11801ef5b48aSclaudio * Create a process that will fetch data via https. 11811ef5b48aSclaudio * With every request the http process receives a file descriptor 11821ef5b48aSclaudio * where the data should be written to. 11831ef5b48aSclaudio */ 11849a7e9e7fSjob 11851db5fd2bSclaudio if (!noop && rrdpon) { 1186bf431fffSclaudio httppid = process_start("http", &httpfd); 11871ef5b48aSclaudio 11881ef5b48aSclaudio if (httppid == 0) { 1189bf431fffSclaudio close(procfd); 1190bf431fffSclaudio close(rsyncfd); 1191bf431fffSclaudio proc_http(bind_addr, httpfd); 11921ef5b48aSclaudio } 119338177ce4Sclaudio } else { 1194bf431fffSclaudio httpfd = -1; 119538177ce4Sclaudio httppid = -1; 119638177ce4Sclaudio } 11971ef5b48aSclaudio 11988ecbadc1Sclaudio /* 11998ecbadc1Sclaudio * Create a process that will process RRDP. 12008ecbadc1Sclaudio * The rrdp process requires the http process to fetch the various 12018ecbadc1Sclaudio * XML files and does this via the main process. 12028ecbadc1Sclaudio */ 12038ecbadc1Sclaudio 12048ecbadc1Sclaudio if (!noop && rrdpon) { 1205bf431fffSclaudio rrdppid = process_start("rrdp", &rrdpfd); 12068ecbadc1Sclaudio if (rrdppid == 0) { 1207bf431fffSclaudio close(procfd); 1208bf431fffSclaudio close(rsyncfd); 1209bf431fffSclaudio close(httpfd); 1210bf431fffSclaudio proc_rrdp(rrdpfd); 12118ecbadc1Sclaudio } 1212c9585076Sclaudio } else { 1213bf431fffSclaudio rrdpfd = -1; 1214c9585076Sclaudio rrdppid = -1; 1215c9585076Sclaudio } 12168ecbadc1Sclaudio 12175887f5d9Stb if (!filemode && timeout > 0) { 1218e6ebc744Sclaudio /* 1219e6ebc744Sclaudio * Commit suicide eventually 1220e6ebc744Sclaudio * cron will normally start a new one 1221e6ebc744Sclaudio */ 1222e6ebc744Sclaudio alarm(timeout); 1223e6ebc744Sclaudio signal(SIGALRM, suicide); 122447de54a6Sclaudio 122547de54a6Sclaudio /* give up a bit before the hard timeout and try to finish up */ 122647de54a6Sclaudio if (!noop) 122747de54a6Sclaudio deadline = getmonotime() + timeout - repo_timeout / 2; 1228e6ebc744Sclaudio } 1229e6ebc744Sclaudio 1230d96abf21Sclaudio if (pledge("stdio rpath wpath cpath fattr sendfd unveil", NULL) == -1) 12314e226c0bSbenno err(1, "pledge"); 12329a7e9e7fSjob 1233b5fa5d51Sclaudio if ((procq = msgbuf_new_reader(sizeof(size_t), io_parse_hdr, NULL)) == 1234b5fa5d51Sclaudio NULL) 123525d36c5cSclaudio err(1, NULL); 1236b5fa5d51Sclaudio if ((rsyncq = msgbuf_new_reader(sizeof(size_t), io_parse_hdr, NULL)) == 1237b5fa5d51Sclaudio NULL) 123825d36c5cSclaudio err(1, NULL); 1239b5fa5d51Sclaudio if ((httpq = msgbuf_new_reader(sizeof(size_t), io_parse_hdr, NULL)) == 1240b5fa5d51Sclaudio NULL) 124125d36c5cSclaudio err(1, NULL); 1242b5fa5d51Sclaudio if ((rrdpq = msgbuf_new_reader(sizeof(size_t), io_parse_hdr, NULL)) == 1243b5fa5d51Sclaudio NULL) 124425d36c5cSclaudio err(1, NULL); 12459a7e9e7fSjob 1246397f8a58Sclaudio /* 1247397f8a58Sclaudio * The main process drives the top-down scan to leaf ROAs using 1248397f8a58Sclaudio * data downloaded by the rsync process and parsed by the 1249397f8a58Sclaudio * parsing process. 1250397f8a58Sclaudio */ 1251397f8a58Sclaudio 1252bf431fffSclaudio pfd[0].fd = procfd; 125325d36c5cSclaudio queues[0] = procq; 1254bf431fffSclaudio pfd[1].fd = rsyncfd; 125525d36c5cSclaudio queues[1] = rsyncq; 1256bf431fffSclaudio pfd[2].fd = httpfd; 125725d36c5cSclaudio queues[2] = httpq; 1258bf431fffSclaudio pfd[3].fd = rrdpfd; 125925d36c5cSclaudio queues[3] = rrdpq; 126008db1177Sclaudio 12615a2857b6Sjob load_skiplist(skiplistfile); 12625a2857b6Sjob 126308db1177Sclaudio /* 126473a49dedStb * Prime the process with our TAL files. 126573a49dedStb * These will (hopefully) contain links to manifests and we 126608db1177Sclaudio * can get the ball rolling. 126708db1177Sclaudio */ 126808db1177Sclaudio 126908db1177Sclaudio for (i = 0; i < talsz; i++) 1270f43c4d92Sclaudio queue_add_file(tals[i], RTYPE_TAL, i); 1271f43c4d92Sclaudio 127258516d64Sclaudio if (filemode) { 127358516d64Sclaudio while (*argv != NULL) 127458516d64Sclaudio queue_add_file(*argv++, RTYPE_FILE, 0); 1275d96abf21Sclaudio 1276d96abf21Sclaudio if (unveil(cachedir, "r") == -1) 1277d96abf21Sclaudio err(1, "unveil cachedir"); 1278d96abf21Sclaudio } else { 1279d96abf21Sclaudio if (unveil(outputdir, "rwc") == -1) 1280d96abf21Sclaudio err(1, "unveil outputdir"); 1281d96abf21Sclaudio if (unveil(cachedir, "rwc") == -1) 1282d96abf21Sclaudio err(1, "unveil cachedir"); 128358516d64Sclaudio } 1284d96abf21Sclaudio if (pledge("stdio rpath wpath cpath fattr sendfd", NULL) == -1) 1285d96abf21Sclaudio err(1, "unveil"); 12869a7e9e7fSjob 1287e80bbcffSclaudio /* change working directory to the cache directory */ 1288e80bbcffSclaudio if (fchdir(cachefd) == -1) 1289e80bbcffSclaudio err(1, "fchdir"); 1290e80bbcffSclaudio 12917690b5f4Sclaudio while (entity_queue > 0 && !killme) { 12927af68c5cSclaudio int polltim; 12937af68c5cSclaudio 1294d389b51dSclaudio polltim = repo_check_timeout(INFTIM); 1295d389b51dSclaudio 1296173bcbfaSclaudio for (i = 0; i < NPFD; i++) { 1297173bcbfaSclaudio pfd[i].events = POLLIN; 129853f63e9dSclaudio if (msgbuf_queuelen(queues[i]) > 0) 1299173bcbfaSclaudio pfd[i].events |= POLLOUT; 1300173bcbfaSclaudio } 130108db1177Sclaudio 1302d38aa931Stb if (poll(pfd, NPFD, polltim) == -1) { 13039f431460Sclaudio if (errno == EINTR) 13049f431460Sclaudio continue; 13054e226c0bSbenno err(1, "poll"); 13069f431460Sclaudio } 13079a7e9e7fSjob 1308173bcbfaSclaudio for (i = 0; i < NPFD; i++) { 130901362062Sclaudio if (pfd[i].revents & (POLLERR|POLLNVAL)) { 1310389eb209Sclaudio warnx("poll[%d]: bad fd", i); 1311c89f2969Sclaudio hangup = 1; 1312c89f2969Sclaudio } 131301362062Sclaudio if (pfd[i].revents & POLLHUP) 131401362062Sclaudio hangup = 1; 1315173bcbfaSclaudio if (pfd[i].revents & POLLOUT) { 1316a6a6bc2cSclaudio if (msgbuf_write(pfd[i].fd, queues[i]) == -1) { 13179aadc625Sclaudio if (errno == EPIPE) 1318389eb209Sclaudio warnx("write[%d]: " 1319173bcbfaSclaudio "connection closed", i); 13209aadc625Sclaudio else 1321389eb209Sclaudio warn("write[%d]", i); 132201362062Sclaudio hangup = 1; 132308db1177Sclaudio } 132408db1177Sclaudio } 13251ef5b48aSclaudio } 1326c89f2969Sclaudio if (hangup) 1327c89f2969Sclaudio break; 132808db1177Sclaudio 13299a7e9e7fSjob /* 13301ef5b48aSclaudio * Check the rsync and http process. 13319a7e9e7fSjob * This means that one of our modules has completed 13329a7e9e7fSjob * downloading and we can flush the module requests into 13339a7e9e7fSjob * the parser process. 13349a7e9e7fSjob */ 13359a7e9e7fSjob 1336c89f2969Sclaudio if ((pfd[1].revents & POLLIN)) { 1337b5fa5d51Sclaudio switch (ibuf_read(pfd[1].fd, queues[1])) { 1338b5fa5d51Sclaudio case -1: 1339b5fa5d51Sclaudio err(1, "ibuf_read"); 1340b5fa5d51Sclaudio case 0: 1341b5fa5d51Sclaudio errx(1, "ibuf_read: connection closed"); 1342b5fa5d51Sclaudio } 1343b5fa5d51Sclaudio while ((b = io_buf_get(queues[1])) != NULL) { 1344b6884e9fSclaudio unsigned int id; 1345b6884e9fSclaudio int ok; 1346b6884e9fSclaudio 13477eb79a4aSclaudio io_read_buf(b, &id, sizeof(id)); 13487eb79a4aSclaudio io_read_buf(b, &ok, sizeof(ok)); 13498ecbadc1Sclaudio rsync_finish(id, ok); 13507eb79a4aSclaudio ibuf_free(b); 13517eb79a4aSclaudio } 13529a7e9e7fSjob } 13539a7e9e7fSjob 13541ef5b48aSclaudio if ((pfd[2].revents & POLLIN)) { 1355b5fa5d51Sclaudio switch (ibuf_read(pfd[2].fd, queues[2])) { 1356b5fa5d51Sclaudio case -1: 1357b5fa5d51Sclaudio err(1, "ibuf_read"); 1358b5fa5d51Sclaudio case 0: 1359b5fa5d51Sclaudio errx(1, "ibuf_read: connection closed"); 1360b5fa5d51Sclaudio } 1361b5fa5d51Sclaudio while ((b = io_buf_get(queues[2])) != NULL) { 1362b6884e9fSclaudio unsigned int id; 136309b708f5Sclaudio enum http_result res; 136409b708f5Sclaudio char *last_mod; 136509b708f5Sclaudio 13667eb79a4aSclaudio io_read_buf(b, &id, sizeof(id)); 13677eb79a4aSclaudio io_read_buf(b, &res, sizeof(res)); 13687eb79a4aSclaudio io_read_str(b, &last_mod); 13698ecbadc1Sclaudio http_finish(id, res, last_mod); 137009b708f5Sclaudio free(last_mod); 13717eb79a4aSclaudio ibuf_free(b); 13727eb79a4aSclaudio } 13731ef5b48aSclaudio } 13741ef5b48aSclaudio 13759a7e9e7fSjob /* 13768ecbadc1Sclaudio * Handle RRDP requests here. 13778ecbadc1Sclaudio */ 13788ecbadc1Sclaudio if ((pfd[3].revents & POLLIN)) { 1379b5fa5d51Sclaudio switch (ibuf_read(pfd[3].fd, queues[3])) { 1380b5fa5d51Sclaudio case -1: 1381b5fa5d51Sclaudio abort(); 1382b5fa5d51Sclaudio err(1, "ibuf_read"); 1383b5fa5d51Sclaudio case 0: 1384b5fa5d51Sclaudio errx(1, "ibuf_read: connection closed"); 1385b5fa5d51Sclaudio } 1386b5fa5d51Sclaudio while ((b = io_buf_get(queues[3])) != NULL) { 13877eb79a4aSclaudio rrdp_process(b); 13887eb79a4aSclaudio ibuf_free(b); 13898ecbadc1Sclaudio } 13908ecbadc1Sclaudio } 13918ecbadc1Sclaudio 13928ecbadc1Sclaudio /* 13939a7e9e7fSjob * The parser has finished something for us. 13949a7e9e7fSjob * Dequeue these one by one. 13959a7e9e7fSjob */ 13969a7e9e7fSjob 1397c89f2969Sclaudio if ((pfd[0].revents & POLLIN)) { 1398b5fa5d51Sclaudio switch (ibuf_read(pfd[0].fd, queues[0])) { 1399b5fa5d51Sclaudio case -1: 1400b5fa5d51Sclaudio err(1, "ibuf_read"); 1401b5fa5d51Sclaudio case 0: 1402b5fa5d51Sclaudio errx(1, "ibuf_read: connection closed"); 1403b5fa5d51Sclaudio } 1404b5fa5d51Sclaudio while ((b = io_buf_get(queues[0])) != NULL) { 1405d4be4cdeSjob entity_process(b, &stats, &vrps, &brks, &vaps, 1406d4be4cdeSjob &vsps); 14077eb79a4aSclaudio ibuf_free(b); 14087eb79a4aSclaudio } 14099a7e9e7fSjob } 14109a7e9e7fSjob } 14119a7e9e7fSjob 1412e6ebc744Sclaudio signal(SIGALRM, SIG_DFL); 14139f431460Sclaudio if (killme) { 14149f431460Sclaudio syslog(LOG_CRIT|LOG_DAEMON, 14159f431460Sclaudio "excessive runtime (%d seconds), giving up", timeout); 14169f431460Sclaudio errx(1, "excessive runtime (%d seconds), giving up", timeout); 14179f431460Sclaudio } 14189f431460Sclaudio 14199a7e9e7fSjob /* 14209a7e9e7fSjob * For clean-up, close the input for the parser and rsync 14219a7e9e7fSjob * process. 14229a7e9e7fSjob * This will cause them to exit, then we reap them. 14239a7e9e7fSjob */ 14249a7e9e7fSjob 1425bf431fffSclaudio close(procfd); 1426bf431fffSclaudio close(rsyncfd); 1427bf431fffSclaudio close(httpfd); 1428bf431fffSclaudio close(rrdpfd); 14299a7e9e7fSjob 1430c89f2969Sclaudio rc = 0; 1431c89f2969Sclaudio for (;;) { 1432c89f2969Sclaudio pid = waitpid(WAIT_ANY, &st, 0); 1433c89f2969Sclaudio if (pid == -1) { 1434c89f2969Sclaudio if (errno == EINTR) 1435c89f2969Sclaudio continue; 143658a21141Sclaudio if (errno == ECHILD) 1437c89f2969Sclaudio break; 143858a21141Sclaudio err(1, "wait"); 14399a7e9e7fSjob } 14401ef5b48aSclaudio 1441c89f2969Sclaudio if (pid == procpid) 1442c89f2969Sclaudio name = "parser"; 1443c89f2969Sclaudio else if (pid == rsyncpid) 1444c89f2969Sclaudio name = "rsync"; 1445c89f2969Sclaudio else if (pid == httppid) 1446c89f2969Sclaudio name = "http"; 1447c89f2969Sclaudio else if (pid == rrdppid) 1448c89f2969Sclaudio name = "rrdp"; 1449c89f2969Sclaudio else 1450c89f2969Sclaudio name = "unknown"; 1451c89f2969Sclaudio 1452c89f2969Sclaudio if (WIFSIGNALED(st)) { 1453c89f2969Sclaudio warnx("%s terminated signal %d", name, WTERMSIG(st)); 14541ef5b48aSclaudio rc = 1; 1455c89f2969Sclaudio } else if (!WIFEXITED(st) || WEXITSTATUS(st) != 0) { 1456c89f2969Sclaudio warnx("%s process exited abnormally", name); 1457c89f2969Sclaudio rc = 1; 1458c89f2969Sclaudio } 14591ef5b48aSclaudio } 14608ecbadc1Sclaudio 1461c89f2969Sclaudio /* processing did not finish because of error */ 1462c89f2969Sclaudio if (entity_queue != 0) 146301362062Sclaudio errx(1, "not all files processed, giving up"); 1464c89f2969Sclaudio 1465f43c4d92Sclaudio /* if processing in filemode the process is done, no cleanup */ 1466f43c4d92Sclaudio if (filemode) 1467f43c4d92Sclaudio return rc; 1468f43c4d92Sclaudio 1469c89f2969Sclaudio logx("all files parsed: generating output"); 1470b015ea56Sclaudio 14716cf9bac2Sclaudio if (!noop) 14726cf9bac2Sclaudio repo_cleanup(&fpt, cachefd); 1473b015ea56Sclaudio 147465c1ceceSclaudio clock_gettime(CLOCK_MONOTONIC, &now_time); 147565c1ceceSclaudio timespecsub(&now_time, &start_time, &stats.elapsed_time); 1476b0404f1fSderaadt if (getrusage(RUSAGE_SELF, &ru) == 0) { 147765c1ceceSclaudio TIMEVAL_TO_TIMESPEC(&ru.ru_utime, &stats.user_time); 147865c1ceceSclaudio TIMEVAL_TO_TIMESPEC(&ru.ru_stime, &stats.system_time); 1479b0404f1fSderaadt } 1480b0404f1fSderaadt if (getrusage(RUSAGE_CHILDREN, &ru) == 0) { 148165c1ceceSclaudio struct timespec ts; 148265c1ceceSclaudio 148365c1ceceSclaudio TIMEVAL_TO_TIMESPEC(&ru.ru_utime, &ts); 148465c1ceceSclaudio timespecadd(&stats.user_time, &ts, &stats.user_time); 148565c1ceceSclaudio TIMEVAL_TO_TIMESPEC(&ru.ru_stime, &ts); 148665c1ceceSclaudio timespecadd(&stats.system_time, &ts, &stats.system_time); 1487b0404f1fSderaadt } 1488b0404f1fSderaadt 1489b26df1b3Sjob /* change working directory to the output directory */ 1490b015ea56Sclaudio if (fchdir(outdirfd) == -1) 1491b015ea56Sclaudio err(1, "fchdir output dir"); 1492b015ea56Sclaudio 14931fc2657fSclaudio for (i = 0; i < talsz; i++) { 14941fc2657fSclaudio repo_tal_stats_collect(sum_stats, i, &talstats[i]); 14951fc2657fSclaudio repo_tal_stats_collect(sum_stats, i, &stats.repo_tal_stats); 14961fc2657fSclaudio } 14971fc2657fSclaudio repo_stats_collect(sum_repostats, &stats.repo_stats); 14984f5f25cbSclaudio 1499d4be4cdeSjob if (outputfiles(&vrps, &brks, &vaps, &vsps, &stats)) 15001f9a8b94Sderaadt rc = 1; 15014cefd947Sderaadt 15028adacb6eSclaudio printf("Processing time %lld seconds " 15038adacb6eSclaudio "(%lld seconds user, %lld seconds system)\n", 150486287cb6Sclaudio (long long)stats.elapsed_time.tv_sec, 150586287cb6Sclaudio (long long)stats.user_time.tv_sec, 150686287cb6Sclaudio (long long)stats.system_time.tv_sec); 15074f5f25cbSclaudio printf("Skiplist entries: %u\n", stats.skiplistentries); 15084f5f25cbSclaudio printf("Route Origin Authorizations: %u (%u failed parse, %u " 15091fc2657fSclaudio "invalid)\n", stats.repo_tal_stats.roas, 15101fc2657fSclaudio stats.repo_tal_stats.roas_fail, 15111fc2657fSclaudio stats.repo_tal_stats.roas_invalid); 15124f5f25cbSclaudio printf("AS Provider Attestations: %u (%u failed parse, %u " 15131fc2657fSclaudio "invalid)\n", stats.repo_tal_stats.aspas, 15141fc2657fSclaudio stats.repo_tal_stats.aspas_fail, 15151fc2657fSclaudio stats.repo_tal_stats.aspas_invalid); 151620c31060Sjob if (experimental) { 151720c31060Sjob printf("Signed Prefix Lists: %u " 151820c31060Sjob "(%u failed parse, %u invalid)\n", 1519d4be4cdeSjob stats.repo_tal_stats.spls, stats.repo_tal_stats.spls_fail, 1520d4be4cdeSjob stats.repo_tal_stats.spls_invalid); 152120c31060Sjob } 15221fc2657fSclaudio printf("BGPsec Router Certificates: %u\n", stats.repo_tal_stats.brks); 15234f5f25cbSclaudio printf("Certificates: %u (%u invalid)\n", 15241fc2657fSclaudio stats.repo_tal_stats.certs, stats.repo_tal_stats.certs_fail); 15254f5f25cbSclaudio printf("Trust Anchor Locators: %u (%u invalid)\n", 1526dc508150Sclaudio stats.tals, talsz - stats.tals); 1527318f0572Sjob printf("Manifests: %u (%u failed parse, %u seqnum gaps)\n", 1528318f0572Sjob stats.repo_tal_stats.mfts, stats.repo_tal_stats.mfts_fail, 1529318f0572Sjob stats.repo_tal_stats.mfts_gap); 15301fc2657fSclaudio printf("Certificate revocation lists: %u\n", stats.repo_tal_stats.crls); 15311fc2657fSclaudio printf("Ghostbuster records: %u\n", stats.repo_tal_stats.gbrs); 15321fc2657fSclaudio printf("Trust Anchor Keys: %u\n", stats.repo_tal_stats.taks); 15334f5f25cbSclaudio printf("Repositories: %u\n", stats.repos); 1534a5f50487Sjob printf("New files moved into validated cache: %u\n", 1535a5f50487Sjob stats.repo_stats.new_files); 1536dce4b958Sclaudio printf("Cleanup: removed %u files, %u directories\n" 1537dce4b958Sclaudio "Repository cleanup: kept %u and removed %u superfluous files\n", 15381fc2657fSclaudio stats.repo_stats.del_files, stats.repo_stats.del_dirs, 1539dce4b958Sclaudio stats.repo_stats.extra_files, stats.repo_stats.del_extra_files); 15401fc2657fSclaudio printf("VRP Entries: %u (%u unique)\n", stats.repo_tal_stats.vrps, 15411fc2657fSclaudio stats.repo_tal_stats.vrps_uniqs); 15427e284d50Stb printf("VAP Entries: %u (%u unique, %u overflowed)\n", 15437e284d50Stb stats.repo_tal_stats.vaps, stats.repo_tal_stats.vaps_uniqs, 15447e284d50Stb stats.repo_tal_stats.vaps_overflowed); 1545d4be4cdeSjob printf("VSP Entries: %u (%u unique)\n", stats.repo_tal_stats.vsps, 1546d4be4cdeSjob stats.repo_tal_stats.vsps_uniqs); 15479a7e9e7fSjob 15489a7e9e7fSjob /* Memory cleanup. */ 15498ecbadc1Sclaudio repo_free(); 15509a7e9e7fSjob 1551db934cc7Sbenno return rc; 15529a7e9e7fSjob 15539a7e9e7fSjob usage: 155480272c49Sderaadt fprintf(stderr, 1555bf5a499bSjob "usage: rpki-client [-0ABcjmnoRVvx] [-b sourceaddr] [-d cachedir]" 15565818b3ebSjmc " [-e rsync_prog]\n" 155731d17708Sclaudio " [-H fqdn] [-P epoch] [-S skiplist] [-s timeout]" 1558*29bf64caSjob " [-t tal]\n" 1559*29bf64caSjob " [outputdir]\n" 1560d53c6c47Sjob " rpki-client [-Vv] [-d cachedir] [-j] [-t tal] -f file ..." 1561d53c6c47Sjob "\n"); 15624e226c0bSbenno return 1; 15639a7e9e7fSjob } 1564