File: | src/usr.sbin/rpki-client/main.c |
Warning: | line 1060, column 8 Although the value stored to 'c' is used in the enclosing expression, the value is never actually read from 'c' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: main.c,v 1.173 2022/01/11 13:06:07 claudio Exp $ */ |
2 | /* |
3 | * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org> |
4 | * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> |
5 | * |
6 | * Permission to use, copy, modify, and distribute this software for any |
7 | * purpose with or without fee is hereby granted, provided that the above |
8 | * copyright notice and this permission notice appear in all copies. |
9 | * |
10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 | */ |
18 | |
19 | #include <sys/types.h> |
20 | #include <sys/queue.h> |
21 | #include <sys/socket.h> |
22 | #include <sys/resource.h> |
23 | #include <sys/statvfs.h> |
24 | #include <sys/tree.h> |
25 | #include <sys/wait.h> |
26 | |
27 | #include <assert.h> |
28 | #include <err.h> |
29 | #include <errno(*__errno()).h> |
30 | #include <dirent.h> |
31 | #include <fcntl.h> |
32 | #include <fnmatch.h> |
33 | #include <poll.h> |
34 | #include <pwd.h> |
35 | #include <stdio.h> |
36 | #include <stdlib.h> |
37 | #include <signal.h> |
38 | #include <string.h> |
39 | #include <limits.h> |
40 | #include <syslog.h> |
41 | #include <unistd.h> |
42 | #include <imsg.h> |
43 | |
44 | #include "extern.h" |
45 | #include "version.h" |
46 | |
47 | /* |
48 | * Maximum number of TAL files we'll load. |
49 | */ |
50 | #define TALSZ_MAX8 8 |
51 | |
52 | const char *tals[TALSZ_MAX8]; |
53 | const char *taldescs[TALSZ_MAX8]; |
54 | unsigned int talrepocnt[TALSZ_MAX8]; |
55 | size_t talsz; |
56 | |
57 | size_t entity_queue; |
58 | int timeout = 60*60; |
59 | volatile sig_atomic_t killme; |
60 | void suicide(int sig); |
61 | |
62 | static struct filepath_tree fpt = RB_INITIALIZER(&fpt){ ((void*)0) }; |
63 | static struct msgbuf procq, rsyncq, httpq, rrdpq; |
64 | static int cachefd, outdirfd; |
65 | |
66 | const char *bird_tablename = "ROAS"; |
67 | |
68 | int verbose; |
69 | int noop; |
70 | int rrdpon = 1; |
71 | int repo_timeout; |
72 | |
73 | struct stats stats; |
74 | |
75 | /* |
76 | * Log a message to stderr if and only if "verbose" is non-zero. |
77 | * This uses the err(3) functionality. |
78 | */ |
79 | void |
80 | logx(const char *fmt, ...) |
81 | { |
82 | va_list ap; |
83 | |
84 | if (verbose && fmt != NULL((void*)0)) { |
85 | va_start(ap, fmt)__builtin_va_start(ap, fmt); |
86 | vwarnx(fmt, ap); |
87 | va_end(ap)__builtin_va_end(ap); |
88 | } |
89 | } |
90 | |
91 | time_t |
92 | getmonotime(void) |
93 | { |
94 | struct timespec ts; |
95 | |
96 | if (clock_gettime(CLOCK_MONOTONIC3, &ts) != 0) |
97 | err(1, "clock_gettime"); |
98 | return (ts.tv_sec); |
99 | } |
100 | |
101 | void |
102 | entity_free(struct entity *ent) |
103 | { |
104 | if (ent == NULL((void*)0)) |
105 | return; |
106 | |
107 | free(ent->path); |
108 | free(ent->file); |
109 | free(ent->data); |
110 | free(ent); |
111 | } |
112 | |
113 | /* |
114 | * Read a queue entity from the descriptor. |
115 | * Matched by entity_buffer_req(). |
116 | * The pointer must be passed entity_free(). |
117 | */ |
118 | void |
119 | entity_read_req(struct ibuf *b, struct entity *ent) |
120 | { |
121 | io_read_buf(b, &ent->type, sizeof(ent->type)); |
122 | io_read_buf(b, &ent->repoid, sizeof(ent->repoid)); |
123 | io_read_buf(b, &ent->talid, sizeof(ent->talid)); |
124 | io_read_str(b, &ent->path); |
125 | io_read_str(b, &ent->file); |
126 | io_read_buf_alloc(b, (void **)&ent->data, &ent->datasz); |
127 | } |
128 | |
129 | /* |
130 | * Write the queue entity. |
131 | * Matched by entity_read_req(). |
132 | */ |
133 | static void |
134 | entity_write_req(const struct entity *ent) |
135 | { |
136 | struct ibuf *b; |
137 | |
138 | b = io_new_buffer(); |
139 | io_simple_buffer(b, &ent->type, sizeof(ent->type)); |
140 | io_simple_buffer(b, &ent->repoid, sizeof(ent->repoid)); |
141 | io_simple_buffer(b, &ent->talid, sizeof(ent->talid)); |
142 | io_str_buffer(b, ent->path); |
143 | io_str_buffer(b, ent->file); |
144 | io_buf_buffer(b, ent->data, ent->datasz); |
145 | io_close_buffer(&procq, b); |
146 | } |
147 | |
148 | static void |
149 | entity_write_repo(struct repo *rp) |
150 | { |
151 | struct ibuf *b; |
152 | enum rtype type = RTYPE_REPO; |
153 | unsigned int repoid; |
154 | char *path; |
155 | int talid = 0; |
156 | |
157 | repoid = repo_id(rp); |
158 | path = repo_basedir(rp); |
159 | b = io_new_buffer(); |
160 | io_simple_buffer(b, &type, sizeof(type)); |
161 | io_simple_buffer(b, &repoid, sizeof(repoid)); |
162 | io_simple_buffer(b, &talid, sizeof(talid)); |
163 | io_str_buffer(b, path); |
164 | io_str_buffer(b, NULL((void*)0)); |
165 | io_buf_buffer(b, NULL((void*)0), 0); |
166 | io_close_buffer(&procq, b); |
167 | free(path); |
168 | } |
169 | |
170 | /* |
171 | * Scan through all queued requests and see which ones are in the given |
172 | * repo, then flush those into the parser process. |
173 | */ |
174 | void |
175 | entityq_flush(struct entityq *q, struct repo *rp) |
176 | { |
177 | struct entity *p, *np; |
178 | |
179 | entity_write_repo(rp); |
180 | |
181 | TAILQ_FOREACH_SAFE(p, q, entries, np)for ((p) = ((q)->tqh_first); (p) != ((void*)0) && ( (np) = ((p)->entries.tqe_next), 1); (p) = (np)) { |
182 | entity_write_req(p); |
183 | TAILQ_REMOVE(q, p, entries)do { if (((p)->entries.tqe_next) != ((void*)0)) (p)->entries .tqe_next->entries.tqe_prev = (p)->entries.tqe_prev; else (q)->tqh_last = (p)->entries.tqe_prev; *(p)->entries .tqe_prev = (p)->entries.tqe_next; ; ; } while (0); |
184 | entity_free(p); |
185 | } |
186 | } |
187 | |
188 | /* |
189 | * Add the heap-allocated file to the queue for processing. |
190 | */ |
191 | static void |
192 | entityq_add(char *path, char *file, enum rtype type, struct repo *rp, |
193 | unsigned char *data, size_t datasz, int talid) |
194 | { |
195 | struct entity *p; |
196 | |
197 | if ((p = calloc(1, sizeof(struct entity))) == NULL((void*)0)) |
198 | err(1, NULL((void*)0)); |
199 | |
200 | p->type = type; |
201 | p->talid = talid; |
202 | p->path = path; |
203 | if (rp != NULL((void*)0)) |
204 | p->repoid = repo_id(rp); |
205 | p->file = file; |
206 | p->data = data; |
207 | p->datasz = (data != NULL((void*)0)) ? datasz : 0; |
208 | |
209 | entity_queue++; |
210 | |
211 | /* |
212 | * Write to the queue if there's no repo or the repo has already |
213 | * been loaded else enqueue it for later. |
214 | */ |
215 | |
216 | if (rp == NULL((void*)0) || !repo_queued(rp, p)) { |
217 | entity_write_req(p); |
218 | entity_free(p); |
219 | } |
220 | } |
221 | |
222 | static void |
223 | rrdp_file_resp(unsigned int id, int ok) |
224 | { |
225 | enum rrdp_msg type = RRDP_FILE; |
226 | struct ibuf *b; |
227 | |
228 | b = io_new_buffer(); |
229 | io_simple_buffer(b, &type, sizeof(type)); |
230 | io_simple_buffer(b, &id, sizeof(id)); |
231 | io_simple_buffer(b, &ok, sizeof(ok)); |
232 | io_close_buffer(&rrdpq, b); |
233 | } |
234 | |
235 | void |
236 | rrdp_fetch(unsigned int id, const char *uri, const char *local, |
237 | struct rrdp_session *s) |
238 | { |
239 | enum rrdp_msg type = RRDP_START; |
240 | struct ibuf *b; |
241 | |
242 | b = io_new_buffer(); |
243 | io_simple_buffer(b, &type, sizeof(type)); |
244 | io_simple_buffer(b, &id, sizeof(id)); |
245 | io_str_buffer(b, local); |
246 | io_str_buffer(b, uri); |
247 | io_str_buffer(b, s->session_id); |
248 | io_simple_buffer(b, &s->serial, sizeof(s->serial)); |
249 | io_str_buffer(b, s->last_mod); |
250 | io_close_buffer(&rrdpq, b); |
251 | } |
252 | |
253 | /* |
254 | * Request a repository sync via rsync URI to directory local. |
255 | */ |
256 | void |
257 | rsync_fetch(unsigned int id, const char *uri, const char *local) |
258 | { |
259 | struct ibuf *b; |
260 | |
261 | b = io_new_buffer(); |
262 | io_simple_buffer(b, &id, sizeof(id)); |
263 | io_str_buffer(b, local); |
264 | io_str_buffer(b, uri); |
265 | io_close_buffer(&rsyncq, b); |
266 | } |
267 | |
268 | /* |
269 | * Request a file from a https uri, data is written to the file descriptor fd. |
270 | */ |
271 | void |
272 | http_fetch(unsigned int id, const char *uri, const char *last_mod, int fd) |
273 | { |
274 | struct ibuf *b; |
275 | |
276 | b = io_new_buffer(); |
277 | io_simple_buffer(b, &id, sizeof(id)); |
278 | io_str_buffer(b, uri); |
279 | io_str_buffer(b, last_mod); |
280 | /* pass file as fd */ |
281 | b->fd = fd; |
282 | io_close_buffer(&httpq, b); |
283 | } |
284 | |
285 | /* |
286 | * Request some XML file on behalf of the rrdp parser. |
287 | * Create a pipe and pass the pipe endpoints to the http and rrdp process. |
288 | */ |
289 | static void |
290 | rrdp_http_fetch(unsigned int id, const char *uri, const char *last_mod) |
291 | { |
292 | enum rrdp_msg type = RRDP_HTTP_INI; |
293 | struct ibuf *b; |
294 | int pi[2]; |
295 | |
296 | if (pipe2(pi, O_CLOEXEC0x10000 | O_NONBLOCK0x0004) == -1) |
297 | err(1, "pipe"); |
298 | |
299 | b = io_new_buffer(); |
300 | io_simple_buffer(b, &type, sizeof(type)); |
301 | io_simple_buffer(b, &id, sizeof(id)); |
302 | b->fd = pi[0]; |
303 | io_close_buffer(&rrdpq, b); |
304 | |
305 | http_fetch(id, uri, last_mod, pi[1]); |
306 | } |
307 | |
308 | void |
309 | rrdp_http_done(unsigned int id, enum http_result res, const char *last_mod) |
310 | { |
311 | enum rrdp_msg type = RRDP_HTTP_FIN; |
312 | struct ibuf *b; |
313 | |
314 | /* RRDP request, relay response over to the rrdp process */ |
315 | b = io_new_buffer(); |
316 | io_simple_buffer(b, &type, sizeof(type)); |
317 | io_simple_buffer(b, &id, sizeof(id)); |
318 | io_simple_buffer(b, &res, sizeof(res)); |
319 | io_str_buffer(b, last_mod); |
320 | io_close_buffer(&rrdpq, b); |
321 | } |
322 | |
323 | /* |
324 | * Add a file (CER, ROA, CRL) from an MFT file, RFC 6486. |
325 | * These are always relative to the directory in which "mft" sits. |
326 | */ |
327 | static void |
328 | queue_add_from_mft(const char *path, const struct mftfile *file, |
329 | enum rtype type, struct repo *rp) |
330 | { |
331 | char *nfile, *npath = NULL((void*)0); |
332 | |
333 | if (path != NULL((void*)0)) |
334 | if ((npath = strdup(path)) == NULL((void*)0)) |
335 | err(1, NULL((void*)0)); |
336 | if ((nfile = strdup(file->file)) == NULL((void*)0)) |
337 | err(1, NULL((void*)0)); |
338 | |
339 | entityq_add(npath, nfile, type, rp, NULL((void*)0), 0, -1); |
340 | } |
341 | |
342 | /* |
343 | * Loops over queue_add_from_mft() for all files. |
344 | * The order here is important: we want to parse the revocation |
345 | * list *before* we parse anything else. |
346 | * FIXME: set the type of file in the mftfile so that we don't need to |
347 | * keep doing the check (this should be done in the parser, where we |
348 | * check the suffix anyway). |
349 | */ |
350 | static void |
351 | queue_add_from_mft_set(const struct mft *mft, const char *name, struct repo *rp) |
352 | { |
353 | size_t i, sz; |
354 | const struct mftfile *f; |
355 | |
356 | for (i = 0; i < mft->filesz; i++) { |
357 | f = &mft->files[i]; |
358 | sz = strlen(f->file); |
359 | assert(sz > 4)((sz > 4) ? (void)0 : __assert2("/usr/src/usr.sbin/rpki-client/main.c" , 359, __func__, "sz > 4")); |
360 | if (strcasecmp(f->file + sz - 4, ".crl") != 0) |
361 | continue; |
362 | queue_add_from_mft(mft->path, f, RTYPE_CRL, rp); |
363 | } |
364 | |
365 | for (i = 0; i < mft->filesz; i++) { |
366 | f = &mft->files[i]; |
367 | sz = strlen(f->file); |
368 | assert(sz > 4)((sz > 4) ? (void)0 : __assert2("/usr/src/usr.sbin/rpki-client/main.c" , 368, __func__, "sz > 4")); |
369 | if (strcasecmp(f->file + sz - 4, ".crl") == 0) |
370 | continue; |
371 | else if (strcasecmp(f->file + sz - 4, ".cer") == 0) |
372 | queue_add_from_mft(mft->path, f, RTYPE_CER, rp); |
373 | else if (strcasecmp(f->file + sz - 4, ".roa") == 0) |
374 | queue_add_from_mft(mft->path, f, RTYPE_ROA, rp); |
375 | else if (strcasecmp(f->file + sz - 4, ".gbr") == 0) |
376 | queue_add_from_mft(mft->path, f, RTYPE_GBR, rp); |
377 | else |
378 | logx("%s: unsupported file type: %s", name, |
379 | f->file); |
380 | } |
381 | } |
382 | |
383 | /* |
384 | * Add a local TAL file (RFC 7730) to the queue of files to fetch. |
385 | */ |
386 | static void |
387 | queue_add_tal(const char *file, int talid) |
388 | { |
389 | unsigned char *buf; |
390 | char *nfile; |
391 | size_t len; |
392 | |
393 | buf = load_file(file, &len); |
394 | if (buf == NULL((void*)0)) { |
395 | warn("%s", file); |
396 | return; |
397 | } |
398 | |
399 | if ((nfile = strdup(file)) == NULL((void*)0)) |
400 | err(1, NULL((void*)0)); |
401 | /* Not in a repository, so directly add to queue. */ |
402 | entityq_add(NULL((void*)0), nfile, RTYPE_TAL, NULL((void*)0), buf, len, talid); |
403 | } |
404 | |
405 | /* |
406 | * Add URIs (CER) from a TAL file, RFC 8630. |
407 | */ |
408 | static void |
409 | queue_add_from_tal(struct tal *tal) |
410 | { |
411 | struct repo *repo; |
412 | unsigned char *data; |
413 | char *nfile; |
414 | |
415 | assert(tal->urisz)((tal->urisz) ? (void)0 : __assert2("/usr/src/usr.sbin/rpki-client/main.c" , 415, __func__, "tal->urisz")); |
416 | |
417 | if ((taldescs[tal->id] = strdup(tal->descr)) == NULL((void*)0)) |
418 | err(1, NULL((void*)0)); |
419 | |
420 | /* figure out the TA filename, must be done before repo lookup */ |
421 | nfile = strrchr(tal->uri[0], '/'); |
422 | assert(nfile != NULL)((nfile != ((void*)0)) ? (void)0 : __assert2("/usr/src/usr.sbin/rpki-client/main.c" , 422, __func__, "nfile != NULL")); |
423 | if ((nfile = strdup(nfile + 1)) == NULL((void*)0)) |
424 | err(1, NULL((void*)0)); |
425 | |
426 | /* Look up the repository. */ |
427 | repo = ta_lookup(tal->id, tal); |
428 | if (repo == NULL((void*)0)) |
429 | return; |
430 | |
431 | /* steal the pkey from the tal structure */ |
432 | data = tal->pkey; |
433 | tal->pkey = NULL((void*)0); |
434 | entityq_add(NULL((void*)0), nfile, RTYPE_CER, repo, data, tal->pkeysz, tal->id); |
435 | } |
436 | |
437 | /* |
438 | * Add a manifest (MFT) found in an X509 certificate, RFC 6487. |
439 | */ |
440 | static void |
441 | queue_add_from_cert(const struct cert *cert) |
442 | { |
443 | struct repo *repo; |
444 | char *nfile, *npath; |
445 | const char *uri, *repouri, *file; |
446 | size_t repourisz; |
447 | |
448 | repo = repo_lookup(cert->talid, cert->repo, |
449 | rrdpon ? cert->notify : NULL((void*)0)); |
450 | if (repo == NULL((void*)0)) |
451 | return; |
452 | |
453 | /* |
454 | * Figure out the cert filename and path by chopping up the |
455 | * MFT URI in the cert based on the repo base URI. |
456 | */ |
457 | uri = cert->mft; |
458 | repouri = repo_uri(repo); |
459 | repourisz = strlen(repouri); |
460 | if (strncmp(repouri, cert->mft, repourisz) != 0) { |
461 | warnx("%s: URI %s outside of repository", repouri, uri); |
462 | return; |
463 | } |
464 | uri += repourisz + 1; /* skip base and '/' */ |
465 | file = strrchr(uri, '/'); |
466 | if (file == NULL((void*)0)) { |
467 | npath = NULL((void*)0); |
468 | if ((nfile = strdup(uri)) == NULL((void*)0)) |
469 | err(1, NULL((void*)0)); |
470 | } else { |
471 | if ((npath = strndup(uri, file - uri)) == NULL((void*)0)) |
472 | err(1, NULL((void*)0)); |
473 | if ((nfile = strdup(file + 1)) == NULL((void*)0)) |
474 | err(1, NULL((void*)0)); |
475 | } |
476 | |
477 | entityq_add(npath, nfile, RTYPE_MFT, repo, NULL((void*)0), 0, -1); |
478 | } |
479 | |
480 | /* |
481 | * Process parsed content. |
482 | * For non-ROAs, we grok for more data. |
483 | * For ROAs, we want to extract the valid info. |
484 | * In all cases, we gather statistics. |
485 | */ |
486 | static void |
487 | entity_process(struct ibuf *b, struct stats *st, struct vrp_tree *tree, |
488 | struct brk_tree *brktree) |
489 | { |
490 | enum rtype type; |
491 | struct tal *tal; |
492 | struct cert *cert; |
493 | struct mft *mft; |
494 | struct roa *roa; |
495 | char *file; |
496 | int c; |
497 | |
498 | /* |
499 | * For most of these, we first read whether there's any content |
500 | * at all---this means that the syntactic parse failed (X509 |
501 | * certificate, for example). |
502 | * We follow that up with whether the resources didn't parse. |
503 | */ |
504 | io_read_buf(b, &type, sizeof(type)); |
505 | io_read_str(b, &file); |
506 | |
507 | if (filepath_add(&fpt, file) == 0) { |
508 | warnx("%s: File already visited", file); |
509 | free(file); |
510 | entity_queue--; |
511 | return; |
512 | } |
513 | |
514 | switch (type) { |
515 | case RTYPE_TAL: |
516 | st->tals++; |
517 | tal = tal_read(b); |
518 | queue_add_from_tal(tal); |
519 | tal_free(tal); |
520 | break; |
521 | case RTYPE_CER: |
522 | st->certs++; |
523 | io_read_buf(b, &c, sizeof(c)); |
524 | if (c == 0) { |
525 | st->certs_fail++; |
526 | break; |
527 | } |
528 | cert = cert_read(b); |
529 | if (cert->purpose == CERT_PURPOSE_CA) { |
530 | /* |
531 | * Process the revocation list from the |
532 | * certificate *first*, since it might mark that |
533 | * we're revoked and then we don't want to |
534 | * process the MFT. |
535 | */ |
536 | queue_add_from_cert(cert); |
537 | } else if (cert->purpose == CERT_PURPOSE_BGPSEC_ROUTER) { |
538 | cert_insert_brks(brktree, cert); |
539 | st->brks++; |
540 | } else |
541 | st->certs_fail++; |
542 | cert_free(cert); |
543 | break; |
544 | case RTYPE_MFT: |
545 | st->mfts++; |
546 | io_read_buf(b, &c, sizeof(c)); |
547 | if (c == 0) { |
548 | st->mfts_fail++; |
549 | break; |
550 | } |
551 | mft = mft_read(b); |
552 | if (!mft->stale) |
553 | queue_add_from_mft_set(mft, file, |
554 | repo_byid(mft->repoid)); |
555 | else |
556 | st->mfts_stale++; |
557 | mft_free(mft); |
558 | break; |
559 | case RTYPE_CRL: |
560 | st->crls++; |
561 | break; |
562 | case RTYPE_ROA: |
563 | st->roas++; |
564 | io_read_buf(b, &c, sizeof(c)); |
565 | if (c == 0) { |
566 | st->roas_fail++; |
567 | break; |
568 | } |
569 | roa = roa_read(b); |
570 | if (roa->valid) |
571 | roa_insert_vrps(tree, roa, &st->vrps, &st->uniqs); |
572 | else |
573 | st->roas_invalid++; |
574 | roa_free(roa); |
575 | break; |
576 | case RTYPE_GBR: |
577 | st->gbrs++; |
578 | break; |
579 | default: |
580 | errx(1, "unknown entity type %d", type); |
581 | } |
582 | |
583 | free(file); |
584 | entity_queue--; |
585 | } |
586 | |
587 | static void |
588 | rrdp_process(struct ibuf *b) |
589 | { |
590 | enum rrdp_msg type; |
591 | enum publish_type pt; |
592 | struct rrdp_session s; |
593 | char *uri, *last_mod, *data; |
594 | char hash[SHA256_DIGEST_LENGTH32]; |
595 | size_t dsz; |
596 | unsigned int id; |
597 | int ok; |
598 | |
599 | io_read_buf(b, &type, sizeof(type)); |
600 | io_read_buf(b, &id, sizeof(id)); |
601 | |
602 | switch (type) { |
603 | case RRDP_END: |
604 | io_read_buf(b, &ok, sizeof(ok)); |
605 | rrdp_finish(id, ok); |
606 | break; |
607 | case RRDP_HTTP_REQ: |
608 | io_read_str(b, &uri); |
609 | io_read_str(b, &last_mod); |
610 | rrdp_http_fetch(id, uri, last_mod); |
611 | break; |
612 | case RRDP_SESSION: |
613 | io_read_str(b, &s.session_id); |
614 | io_read_buf(b, &s.serial, sizeof(s.serial)); |
615 | io_read_str(b, &s.last_mod); |
616 | rrdp_save_state(id, &s); |
617 | free(s.session_id); |
618 | free(s.last_mod); |
619 | break; |
620 | case RRDP_FILE: |
621 | io_read_buf(b, &pt, sizeof(pt)); |
622 | if (pt != PUB_ADD) |
623 | io_read_buf(b, &hash, sizeof(hash)); |
624 | io_read_str(b, &uri); |
625 | io_read_buf_alloc(b, (void **)&data, &dsz); |
626 | |
627 | ok = rrdp_handle_file(id, pt, uri, hash, sizeof(hash), |
628 | data, dsz); |
629 | rrdp_file_resp(id, ok); |
630 | |
631 | free(uri); |
632 | free(data); |
633 | break; |
634 | default: |
635 | errx(1, "unexpected rrdp response"); |
636 | } |
637 | } |
638 | |
639 | /* |
640 | * Assign filenames ending in ".tal" in "/etc/rpki" into "tals", |
641 | * returning the number of files found and filled-in. |
642 | * This may be zero. |
643 | * Don't exceded "max" filenames. |
644 | */ |
645 | static size_t |
646 | tal_load_default(void) |
647 | { |
648 | static const char *confdir = "/etc/rpki"; |
649 | size_t s = 0; |
650 | char *path; |
651 | DIR *dirp; |
652 | struct dirent *dp; |
653 | |
654 | dirp = opendir(confdir); |
655 | if (dirp == NULL((void*)0)) |
656 | err(1, "open %s", confdir); |
657 | while ((dp = readdir(dirp)) != NULL((void*)0)) { |
658 | if (fnmatch("*.tal", dp->d_name, FNM_PERIOD0x04) == FNM_NOMATCH1) |
659 | continue; |
660 | if (s >= TALSZ_MAX8) |
661 | err(1, "too many tal files found in %s", |
662 | confdir); |
663 | if (asprintf(&path, "%s/%s", confdir, dp->d_name) == -1) |
664 | err(1, NULL((void*)0)); |
665 | tals[s++] = path; |
666 | } |
667 | closedir(dirp); |
668 | return s; |
669 | } |
670 | |
671 | static void |
672 | check_fs_size(int fd, const char *cachedir) |
673 | { |
674 | struct statvfs fs; |
675 | const long long minsize = 500 * 1024 * 1024; |
676 | const long long minnode = 300 * 1000; |
677 | |
678 | if (fstatvfs(fd, &fs) == -1) |
679 | err(1, "statfs %s", cachedir); |
680 | |
681 | if (fs.f_bavail < minsize / fs.f_frsize || fs.f_favail < minnode) { |
682 | fprintf(stderr(&__sF[2]), "WARNING: rpki-client may need more than " |
683 | "the available disk space\n" |
684 | "on the file-system holding %s.\n", cachedir); |
685 | fprintf(stderr(&__sF[2]), "available space: %lldkB, " |
686 | "suggested minimum %lldkB\n", |
687 | (long long)fs.f_bavail * fs.f_frsize / 1024, |
688 | minsize / 1024); |
689 | fprintf(stderr(&__sF[2]), "available inodes %lld, " |
690 | "suggested minimum %lld\n\n", |
691 | (long long)fs.f_favail, minnode); |
692 | fflush(stderr(&__sF[2])); |
693 | } |
694 | } |
695 | |
696 | void |
697 | suicide(int sig __attribute__((unused))) |
698 | { |
699 | killme = 1; |
700 | } |
701 | |
702 | #define NPFD4 4 |
703 | |
704 | int |
705 | main(int argc, char *argv[]) |
706 | { |
707 | int rc, c, st, proc, rsync, http, rrdp, hangup = 0; |
708 | int fl = SOCK_STREAM1 | SOCK_CLOEXEC0x8000 | SOCK_NONBLOCK0x4000; |
709 | size_t i; |
710 | pid_t pid, procpid, rsyncpid, httppid, rrdppid; |
711 | int fd[2]; |
712 | struct pollfd pfd[NPFD4]; |
713 | struct msgbuf *queues[NPFD4]; |
714 | struct ibuf *b, *httpbuf = NULL((void*)0), *procbuf = NULL((void*)0); |
715 | struct ibuf *rrdpbuf = NULL((void*)0), *rsyncbuf = NULL((void*)0); |
716 | char *rsync_prog = "openrsync"; |
717 | char *bind_addr = NULL((void*)0); |
718 | const char *cachedir = NULL((void*)0), *outputdir = NULL((void*)0); |
719 | const char *errs, *name; |
720 | const char *file = NULL((void*)0); |
721 | struct vrp_tree vrps = RB_INITIALIZER(&vrps){ ((void*)0) }; |
722 | struct brk_tree brks = RB_INITIALIZER(&brks){ ((void*)0) }; |
723 | struct rusage ru; |
724 | struct timeval start_time, now_time; |
725 | |
726 | gettimeofday(&start_time, NULL((void*)0)); |
727 | |
728 | /* If started as root, priv-drop to _rpki-client */ |
729 | if (getuid() == 0) { |
730 | struct passwd *pw; |
731 | |
732 | pw = getpwnam("_rpki-client"); |
733 | if (!pw) |
734 | errx(1, "no _rpki-client user to revoke to"); |
735 | if (setgroups(1, &pw->pw_gid) == -1 || |
736 | setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 || |
737 | setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) |
738 | err(1, "unable to revoke privs"); |
739 | } |
740 | cachedir = RPKI_PATH_BASE_DIR"/var/cache/rpki-client"; |
741 | outputdir = RPKI_PATH_OUT_DIR"/var/db/rpki-client"; |
742 | repo_timeout = timeout / 4; |
743 | |
744 | if (pledge("stdio rpath wpath cpath inet fattr dns sendfd recvfd " |
745 | "proc exec unveil", NULL((void*)0)) == -1) |
746 | err(1, "pledge"); |
747 | |
748 | while ((c = getopt(argc, argv, "b:Bcd:e:f:jnorRs:t:T:vV")) != -1) |
749 | switch (c) { |
750 | case 'b': |
751 | bind_addr = optarg; |
752 | break; |
753 | case 'B': |
754 | outformats |= FORMAT_BIRD0x02; |
755 | break; |
756 | case 'c': |
757 | outformats |= FORMAT_CSV0x04; |
758 | break; |
759 | case 'd': |
760 | cachedir = optarg; |
761 | break; |
762 | case 'e': |
763 | rsync_prog = optarg; |
764 | break; |
765 | case 'f': |
766 | file = optarg; |
767 | noop = 1; |
768 | break; |
769 | case 'j': |
770 | outformats |= FORMAT_JSON0x08; |
771 | break; |
772 | case 'n': |
773 | noop = 1; |
774 | break; |
775 | case 'o': |
776 | outformats |= FORMAT_OPENBGPD0x01; |
777 | break; |
778 | case 'R': |
779 | rrdpon = 0; |
780 | break; |
781 | case 'r': |
782 | rrdpon = 1; |
783 | break; |
784 | case 's': |
785 | timeout = strtonum(optarg, 0, 24*60*60, &errs); |
786 | if (errs) |
787 | errx(1, "-s: %s", errs); |
788 | if (timeout == 0) |
789 | repo_timeout = 24*60*60; |
790 | else if (timeout < 1) |
791 | errx(1, "-s: %i too small", timeout); |
792 | else |
793 | repo_timeout = timeout / 4; |
794 | break; |
795 | case 't': |
796 | if (talsz >= TALSZ_MAX8) |
797 | err(1, |
798 | "too many tal files specified"); |
799 | tals[talsz++] = optarg; |
800 | break; |
801 | case 'T': |
802 | bird_tablename = optarg; |
803 | break; |
804 | case 'v': |
805 | verbose++; |
806 | break; |
807 | case 'V': |
808 | fprintf(stderr(&__sF[2]), "rpki-client %s\n", RPKI_VERSION"7.5"); |
809 | return 0; |
810 | default: |
811 | goto usage; |
812 | } |
813 | |
814 | argv += optind; |
815 | argc -= optind; |
816 | if (argc == 1) |
817 | outputdir = argv[0]; |
818 | else if (argc > 1) |
819 | goto usage; |
820 | |
821 | signal(SIGPIPE13, SIG_IGN(void (*)(int))1); |
822 | |
823 | if (cachedir == NULL((void*)0)) { |
824 | warnx("cache directory required"); |
825 | goto usage; |
826 | } |
827 | if (outputdir == NULL((void*)0)) { |
828 | warnx("output directory required"); |
829 | goto usage; |
830 | } |
831 | |
832 | if ((cachefd = open(cachedir, O_RDONLY0x0000 | O_DIRECTORY0x20000)) == -1) |
833 | err(1, "cache directory %s", cachedir); |
834 | if ((outdirfd = open(outputdir, O_RDONLY0x0000 | O_DIRECTORY0x20000)) == -1) |
835 | err(1, "output directory %s", outputdir); |
836 | |
837 | check_fs_size(cachefd, cachedir); |
838 | |
839 | if (outformats == 0) |
840 | outformats = FORMAT_OPENBGPD0x01; |
841 | |
842 | if (talsz == 0) |
843 | talsz = tal_load_default(); |
844 | if (talsz == 0) |
845 | err(1, "no TAL files found in %s", "/etc/rpki"); |
846 | |
847 | /* |
848 | * Create the file reader as a jailed child process. |
849 | * It will be responsible for reading all of the files (ROAs, |
850 | * manifests, certificates, etc.) and returning contents. |
851 | */ |
852 | |
853 | if (socketpair(AF_UNIX1, fl, 0, fd) == -1) |
854 | err(1, "socketpair"); |
855 | if ((procpid = fork()) == -1) |
856 | err(1, "fork"); |
857 | |
858 | if (procpid == 0) { |
859 | close(fd[1]); |
860 | |
861 | setproctitle("parser"); |
862 | /* change working directory to the cache directory */ |
863 | if (fchdir(cachefd) == -1) |
864 | err(1, "fchdir"); |
865 | |
866 | if (timeout) |
867 | alarm(timeout); |
868 | |
869 | /* Only allow access to the cache directory. */ |
870 | if (unveil(".", "r") == -1) |
871 | err(1, "%s: unveil", cachedir); |
872 | if (pledge("stdio rpath", NULL((void*)0)) == -1) |
873 | err(1, "pledge"); |
874 | proc_parser(fd[0]); |
875 | errx(1, "parser process returned"); |
876 | } |
877 | |
878 | close(fd[0]); |
879 | proc = fd[1]; |
880 | |
881 | /* |
882 | * Create a process that will do the rsync'ing. |
883 | * This process is responsible for making sure that all the |
884 | * repositories referenced by a certificate manifest (or the |
885 | * TAL) exists and has been downloaded. |
886 | */ |
887 | |
888 | if (!noop) { |
889 | if (socketpair(AF_UNIX1, fl, 0, fd) == -1) |
890 | err(1, "socketpair"); |
891 | if ((rsyncpid = fork()) == -1) |
892 | err(1, "fork"); |
893 | |
894 | if (rsyncpid == 0) { |
895 | close(proc); |
896 | close(fd[1]); |
897 | |
898 | setproctitle("rsync"); |
899 | /* change working directory to the cache directory */ |
900 | if (fchdir(cachefd) == -1) |
901 | err(1, "fchdir"); |
902 | |
903 | if (timeout) |
904 | alarm(timeout); |
905 | |
906 | if (pledge("stdio rpath proc exec unveil", NULL((void*)0)) == -1) |
907 | err(1, "pledge"); |
908 | |
909 | proc_rsync(rsync_prog, bind_addr, fd[0]); |
910 | errx(1, "rsync process returned"); |
911 | } |
912 | |
913 | close(fd[0]); |
914 | rsync = fd[1]; |
915 | } else { |
916 | rsync = -1; |
917 | rsyncpid = -1; |
918 | } |
919 | |
920 | /* |
921 | * Create a process that will fetch data via https. |
922 | * With every request the http process receives a file descriptor |
923 | * where the data should be written to. |
924 | */ |
925 | |
926 | if (!noop) { |
927 | if (socketpair(AF_UNIX1, fl, 0, fd) == -1) |
928 | err(1, "socketpair"); |
929 | if ((httppid = fork()) == -1) |
930 | err(1, "fork"); |
931 | |
932 | if (httppid == 0) { |
933 | close(proc); |
934 | close(rsync); |
935 | close(fd[1]); |
936 | |
937 | setproctitle("http"); |
938 | /* change working directory to the cache directory */ |
939 | if (fchdir(cachefd) == -1) |
940 | err(1, "fchdir"); |
941 | |
942 | if (timeout) |
943 | alarm(timeout); |
944 | |
945 | if (pledge("stdio rpath inet dns recvfd", NULL((void*)0)) == -1) |
946 | err(1, "pledge"); |
947 | |
948 | proc_http(bind_addr, fd[0]); |
949 | errx(1, "http process returned"); |
950 | } |
951 | |
952 | close(fd[0]); |
953 | http = fd[1]; |
954 | } else { |
955 | http = -1; |
956 | httppid = -1; |
957 | } |
958 | |
959 | /* |
960 | * Create a process that will process RRDP. |
961 | * The rrdp process requires the http process to fetch the various |
962 | * XML files and does this via the main process. |
963 | */ |
964 | |
965 | if (!noop && rrdpon) { |
966 | if (socketpair(AF_UNIX1, fl, 0, fd) == -1) |
967 | err(1, "socketpair"); |
968 | if ((rrdppid = fork()) == -1) |
969 | err(1, "fork"); |
970 | |
971 | if (rrdppid == 0) { |
972 | close(proc); |
973 | close(rsync); |
974 | close(http); |
975 | close(fd[1]); |
976 | |
977 | setproctitle("rrdp"); |
978 | /* change working directory to the cache directory */ |
979 | if (fchdir(cachefd) == -1) |
980 | err(1, "fchdir"); |
981 | |
982 | if (timeout) |
983 | alarm(timeout); |
984 | |
985 | if (pledge("stdio recvfd", NULL((void*)0)) == -1) |
986 | err(1, "pledge"); |
987 | |
988 | proc_rrdp(fd[0]); |
989 | /* NOTREACHED */ |
990 | } |
991 | |
992 | close(fd[0]); |
993 | rrdp = fd[1]; |
994 | } else { |
995 | rrdp = -1; |
996 | rrdppid = -1; |
997 | } |
998 | |
999 | if (timeout) { |
1000 | /* |
1001 | * Commit suicide eventually |
1002 | * cron will normally start a new one |
1003 | */ |
1004 | alarm(timeout); |
1005 | signal(SIGALRM14, suicide); |
1006 | } |
1007 | |
1008 | /* TODO unveil cachedir and outputdir, no other access allowed */ |
1009 | if (pledge("stdio rpath wpath cpath fattr sendfd", NULL((void*)0)) == -1) |
1010 | err(1, "pledge"); |
1011 | |
1012 | msgbuf_init(&procq); |
1013 | msgbuf_init(&rsyncq); |
1014 | msgbuf_init(&httpq); |
1015 | msgbuf_init(&rrdpq); |
1016 | procq.fd = proc; |
1017 | rsyncq.fd = rsync; |
1018 | httpq.fd = http; |
1019 | rrdpq.fd = rrdp; |
1020 | |
1021 | /* |
1022 | * The main process drives the top-down scan to leaf ROAs using |
1023 | * data downloaded by the rsync process and parsed by the |
1024 | * parsing process. |
1025 | */ |
1026 | |
1027 | pfd[0].fd = proc; |
1028 | queues[0] = &procq; |
1029 | pfd[1].fd = rsync; |
1030 | queues[1] = &rsyncq; |
1031 | pfd[2].fd = http; |
1032 | queues[2] = &httpq; |
1033 | pfd[3].fd = rrdp; |
1034 | queues[3] = &rrdpq; |
1035 | |
1036 | /* |
1037 | * Prime the process with our TAL file. |
1038 | * This will contain (hopefully) links to our manifest and we |
1039 | * can get the ball rolling. |
1040 | */ |
1041 | |
1042 | for (i = 0; i < talsz; i++) |
1043 | queue_add_tal(tals[i], i); |
1044 | |
1045 | /* change working directory to the cache directory */ |
1046 | if (fchdir(cachefd) == -1) |
1047 | err(1, "fchdir"); |
1048 | |
1049 | while (entity_queue > 0 && !killme) { |
1050 | int polltim; |
1051 | |
1052 | for (i = 0; i < NPFD4; i++) { |
1053 | pfd[i].events = POLLIN0x0001; |
1054 | if (queues[i]->queued) |
1055 | pfd[i].events |= POLLOUT0x0004; |
1056 | } |
1057 | |
1058 | polltim = repo_next_timeout(INFTIM(-1)); |
1059 | |
1060 | if ((c = poll(pfd, NPFD4, polltim)) == -1) { |
Although the value stored to 'c' is used in the enclosing expression, the value is never actually read from 'c' | |
1061 | if (errno(*__errno()) == EINTR4) |
1062 | continue; |
1063 | err(1, "poll"); |
1064 | } |
1065 | |
1066 | for (i = 0; i < NPFD4; i++) { |
1067 | if (pfd[i].revents & (POLLERR0x0008|POLLNVAL0x0020)) { |
1068 | warnx("poll[%zu]: bad fd", i); |
1069 | hangup = 1; |
1070 | } |
1071 | if (pfd[i].revents & POLLHUP0x0010) |
1072 | hangup = 1; |
1073 | if (pfd[i].revents & POLLOUT0x0004) { |
1074 | switch (msgbuf_write(queues[i])) { |
1075 | case 0: |
1076 | warnx("write[%zu]: " |
1077 | "connection closed", i); |
1078 | hangup = 1; |
1079 | break; |
1080 | case -1: |
1081 | warn("write[%zu]", i); |
1082 | hangup = 1; |
1083 | break; |
1084 | } |
1085 | } |
1086 | } |
1087 | if (hangup) |
1088 | break; |
1089 | |
1090 | repo_check_timeout(); |
1091 | |
1092 | /* |
1093 | * Check the rsync and http process. |
1094 | * This means that one of our modules has completed |
1095 | * downloading and we can flush the module requests into |
1096 | * the parser process. |
1097 | */ |
1098 | |
1099 | if ((pfd[1].revents & POLLIN0x0001)) { |
1100 | b = io_buf_read(rsync, &rsyncbuf); |
1101 | if (b != NULL((void*)0)) { |
1102 | unsigned int id; |
1103 | int ok; |
1104 | |
1105 | io_read_buf(b, &id, sizeof(id)); |
1106 | io_read_buf(b, &ok, sizeof(ok)); |
1107 | rsync_finish(id, ok); |
1108 | ibuf_free(b); |
1109 | } |
1110 | } |
1111 | |
1112 | if ((pfd[2].revents & POLLIN0x0001)) { |
1113 | b = io_buf_read(http, &httpbuf); |
1114 | if (b != NULL((void*)0)) { |
1115 | unsigned int id; |
1116 | enum http_result res; |
1117 | char *last_mod; |
1118 | |
1119 | io_read_buf(b, &id, sizeof(id)); |
1120 | io_read_buf(b, &res, sizeof(res)); |
1121 | io_read_str(b, &last_mod); |
1122 | http_finish(id, res, last_mod); |
1123 | free(last_mod); |
1124 | ibuf_free(b); |
1125 | } |
1126 | } |
1127 | |
1128 | /* |
1129 | * Handle RRDP requests here. |
1130 | */ |
1131 | if ((pfd[3].revents & POLLIN0x0001)) { |
1132 | b = io_buf_read(rrdp, &rrdpbuf); |
1133 | if (b != NULL((void*)0)) { |
1134 | rrdp_process(b); |
1135 | ibuf_free(b); |
1136 | } |
1137 | } |
1138 | |
1139 | /* |
1140 | * The parser has finished something for us. |
1141 | * Dequeue these one by one. |
1142 | */ |
1143 | |
1144 | if ((pfd[0].revents & POLLIN0x0001)) { |
1145 | b = io_buf_read(proc, &procbuf); |
1146 | if (b != NULL((void*)0)) { |
1147 | entity_process(b, &stats, &vrps, &brks); |
1148 | ibuf_free(b); |
1149 | } |
1150 | } |
1151 | } |
1152 | |
1153 | signal(SIGALRM14, SIG_DFL(void (*)(int))0); |
1154 | if (killme) { |
1155 | syslog(LOG_CRIT2|LOG_DAEMON(3<<3), |
1156 | "excessive runtime (%d seconds), giving up", timeout); |
1157 | errx(1, "excessive runtime (%d seconds), giving up", timeout); |
1158 | } |
1159 | |
1160 | /* |
1161 | * For clean-up, close the input for the parser and rsync |
1162 | * process. |
1163 | * This will cause them to exit, then we reap them. |
1164 | */ |
1165 | |
1166 | close(proc); |
1167 | close(rsync); |
1168 | close(http); |
1169 | close(rrdp); |
1170 | |
1171 | rc = 0; |
1172 | for (;;) { |
1173 | pid = waitpid(WAIT_ANY(-1), &st, 0); |
1174 | if (pid == -1) { |
1175 | if (errno(*__errno()) == EINTR4) |
1176 | continue; |
1177 | if (errno(*__errno()) == ECHILD10) |
1178 | break; |
1179 | err(1, "wait"); |
1180 | } |
1181 | |
1182 | if (pid == procpid) |
1183 | name = "parser"; |
1184 | else if (pid == rsyncpid) |
1185 | name = "rsync"; |
1186 | else if (pid == httppid) |
1187 | name = "http"; |
1188 | else if (pid == rrdppid) |
1189 | name = "rrdp"; |
1190 | else |
1191 | name = "unknown"; |
1192 | |
1193 | if (WIFSIGNALED(st)(((st) & 0177) != 0177 && ((st) & 0177) != 0)) { |
1194 | warnx("%s terminated signal %d", name, WTERMSIG(st)(((st) & 0177))); |
1195 | rc = 1; |
1196 | } else if (!WIFEXITED(st)(((st) & 0177) == 0) || WEXITSTATUS(st)(int)(((unsigned)(st) >> 8) & 0xff) != 0) { |
1197 | warnx("%s process exited abnormally", name); |
1198 | rc = 1; |
1199 | } |
1200 | } |
1201 | |
1202 | /* processing did not finish because of error */ |
1203 | if (entity_queue != 0) |
1204 | errx(1, "not all files processed, giving up"); |
1205 | |
1206 | logx("all files parsed: generating output"); |
1207 | |
1208 | repo_cleanup(&fpt); |
1209 | |
1210 | gettimeofday(&now_time, NULL((void*)0)); |
1211 | timersub(&now_time, &start_time, &stats.elapsed_time)do { (&stats.elapsed_time)->tv_sec = (&now_time)-> tv_sec - (&start_time)->tv_sec; (&stats.elapsed_time )->tv_usec = (&now_time)->tv_usec - (&start_time )->tv_usec; if ((&stats.elapsed_time)->tv_usec < 0) { (&stats.elapsed_time)->tv_sec--; (&stats.elapsed_time )->tv_usec += 1000000; } } while (0); |
1212 | if (getrusage(RUSAGE_SELF0, &ru) == 0) { |
1213 | stats.user_time = ru.ru_utime; |
1214 | stats.system_time = ru.ru_stime; |
1215 | } |
1216 | if (getrusage(RUSAGE_CHILDREN(-1), &ru) == 0) { |
1217 | timeradd(&stats.user_time, &ru.ru_utime, &stats.user_time)do { (&stats.user_time)->tv_sec = (&stats.user_time )->tv_sec + (&ru.ru_utime)->tv_sec; (&stats.user_time )->tv_usec = (&stats.user_time)->tv_usec + (&ru .ru_utime)->tv_usec; if ((&stats.user_time)->tv_usec >= 1000000) { (&stats.user_time)->tv_sec++; (& stats.user_time)->tv_usec -= 1000000; } } while (0); |
1218 | timeradd(&stats.system_time, &ru.ru_stime, &stats.system_time)do { (&stats.system_time)->tv_sec = (&stats.system_time )->tv_sec + (&ru.ru_stime)->tv_sec; (&stats.system_time )->tv_usec = (&stats.system_time)->tv_usec + (& ru.ru_stime)->tv_usec; if ((&stats.system_time)->tv_usec >= 1000000) { (&stats.system_time)->tv_sec++; (& stats.system_time)->tv_usec -= 1000000; } } while (0); |
1219 | } |
1220 | |
1221 | /* change working directory to the output directory */ |
1222 | if (fchdir(outdirfd) == -1) |
1223 | err(1, "fchdir output dir"); |
1224 | |
1225 | if (outputfiles(&vrps, &brks, &stats)) |
1226 | rc = 1; |
1227 | |
1228 | logx("Processing time %lld seconds " |
1229 | "(%lld seconds user, %lld seconds system)", |
1230 | (long long)stats.elapsed_time.tv_sec, |
1231 | (long long)stats.user_time.tv_sec, |
1232 | (long long)stats.system_time.tv_sec); |
1233 | logx("Route Origin Authorizations: %zu (%zu failed parse, %zu invalid)", |
1234 | stats.roas, stats.roas_fail, stats.roas_invalid); |
1235 | logx("BGPsec Router Certificates: %zu", stats.brks); |
1236 | logx("Certificates: %zu (%zu invalid)", |
1237 | stats.certs, stats.certs_fail); |
1238 | logx("Trust Anchor Locators: %zu (%zu invalid)", |
1239 | stats.tals, talsz - stats.tals); |
1240 | logx("Manifests: %zu (%zu failed parse, %zu stale)", |
1241 | stats.mfts, stats.mfts_fail, stats.mfts_stale); |
1242 | logx("Certificate revocation lists: %zu", stats.crls); |
1243 | logx("Ghostbuster records: %zu", stats.gbrs); |
1244 | logx("Repositories: %zu", stats.repos); |
1245 | logx("Cleanup: removed %zu files, %zu directories", |
1246 | stats.del_files, stats.del_dirs); |
1247 | logx("VRP Entries: %zu (%zu unique)", stats.vrps, stats.uniqs); |
1248 | |
1249 | /* Memory cleanup. */ |
1250 | repo_free(); |
1251 | |
1252 | return rc; |
1253 | |
1254 | usage: |
1255 | fprintf(stderr(&__sF[2]), |
1256 | "usage: rpki-client [-BcjnoRrVv] [-b sourceaddr] [-d cachedir]" |
1257 | " [-e rsync_prog]\n" |
1258 | " [-s timeout] [-T table] [-t tal]" |
1259 | " [outputdir]\n"); |
1260 | return 1; |
1261 | } |