xref: /openbsd-src/usr.sbin/rpki-client/main.c (revision 29bf64ca9b984c45cb3416a0bc49cef8859122f1)
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