1 /* $OpenBSD: repo.c,v 1.69 2024/11/02 12:30:28 job Exp $ */
2 /*
3 * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
4 * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/queue.h>
20 #include <sys/tree.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23
24 #include <assert.h>
25 #include <err.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <fts.h>
29 #include <limits.h>
30 #include <poll.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35
36 #include <imsg.h>
37
38 #include "extern.h"
39
40 extern struct stats stats;
41 extern int rrdpon;
42 extern int repo_timeout;
43 extern time_t deadline;
44 int nofetch;
45
46 enum repo_state {
47 REPO_LOADING = 0,
48 REPO_DONE = 1,
49 REPO_FAILED = -1,
50 };
51
52 /*
53 * A ta, rsync or rrdp repository.
54 * Depending on what is needed the generic repository is backed by
55 * a ta, rsync or rrdp repository. Multiple repositories can use the
56 * same backend.
57 */
58 struct rrdprepo {
59 SLIST_ENTRY(rrdprepo) entry;
60 char *notifyuri;
61 char *basedir;
62 struct filepath_tree deleted;
63 unsigned int id;
64 enum repo_state state;
65 time_t last_reset;
66 };
67 static SLIST_HEAD(, rrdprepo) rrdprepos = SLIST_HEAD_INITIALIZER(rrdprepos);
68
69 struct rsyncrepo {
70 SLIST_ENTRY(rsyncrepo) entry;
71 char *repouri;
72 char *basedir;
73 unsigned int id;
74 enum repo_state state;
75 };
76 static SLIST_HEAD(, rsyncrepo) rsyncrepos = SLIST_HEAD_INITIALIZER(rsyncrepos);
77
78 struct tarepo {
79 SLIST_ENTRY(tarepo) entry;
80 char *descr;
81 char *basedir;
82 char **uri;
83 size_t urisz;
84 size_t uriidx;
85 unsigned int id;
86 enum repo_state state;
87 };
88 static SLIST_HEAD(, tarepo) tarepos = SLIST_HEAD_INITIALIZER(tarepos);
89
90 struct repo {
91 SLIST_ENTRY(repo) entry;
92 char *repouri;
93 char *notifyuri;
94 char *basedir;
95 const struct rrdprepo *rrdp;
96 const struct rsyncrepo *rsync;
97 const struct tarepo *ta;
98 struct entityq queue; /* files waiting for repo */
99 struct repotalstats stats[TALSZ_MAX];
100 struct repostats repostats;
101 struct timespec start_time;
102 time_t alarm; /* sync timeout */
103 int talid;
104 int stats_used[TALSZ_MAX];
105 unsigned int id; /* identifier */
106 };
107 static SLIST_HEAD(, repo) repos = SLIST_HEAD_INITIALIZER(repos);
108
109 /* counter for unique repo id */
110 unsigned int repoid;
111
112 static struct rsyncrepo *rsync_get(const char *, const char *);
113 static void remove_contents(char *);
114
115 /*
116 * Database of all file path accessed during a run.
117 */
118 struct filepath {
119 RB_ENTRY(filepath) entry;
120 char *file;
121 time_t mtime;
122 unsigned int talmask;
123 };
124
125 static inline int
filepathcmp(const struct filepath * a,const struct filepath * b)126 filepathcmp(const struct filepath *a, const struct filepath *b)
127 {
128 return strcmp(a->file, b->file);
129 }
130
131 RB_PROTOTYPE(filepath_tree, filepath, entry, filepathcmp);
132
133 /*
134 * Functions to lookup which files have been accessed during computation.
135 */
136 int
filepath_add(struct filepath_tree * tree,char * file,int id,time_t mtime,int ok)137 filepath_add(struct filepath_tree *tree, char *file, int id, time_t mtime,
138 int ok)
139 {
140 struct filepath *fp, *rfp;
141
142 CTASSERT(TALSZ_MAX < 8 * sizeof(fp->talmask));
143 assert(id >= 0 && id < 8 * (int)sizeof(fp->talmask));
144
145 if ((fp = calloc(1, sizeof(*fp))) == NULL)
146 err(1, NULL);
147 if ((fp->file = strdup(file)) == NULL)
148 err(1, NULL);
149 fp->mtime = mtime;
150
151 if ((rfp = RB_INSERT(filepath_tree, tree, fp)) != NULL) {
152 /* already in the tree */
153 free(fp->file);
154 free(fp);
155 if (rfp->talmask & (1 << id))
156 return 0;
157 fp = rfp;
158 }
159 if (ok)
160 fp->talmask |= (1 << id);
161
162 return 1;
163 }
164
165 /*
166 * Lookup a file path in the tree and return the object if found or NULL.
167 */
168 static struct filepath *
filepath_find(struct filepath_tree * tree,char * file)169 filepath_find(struct filepath_tree *tree, char *file)
170 {
171 struct filepath needle = { .file = file };
172
173 return RB_FIND(filepath_tree, tree, &needle);
174 }
175
176 /*
177 * Returns true if file exists in the tree.
178 */
179 static int
filepath_exists(struct filepath_tree * tree,char * file)180 filepath_exists(struct filepath_tree *tree, char *file)
181 {
182 return filepath_find(tree, file) != NULL;
183 }
184
185 /*
186 * Returns true if file exists and the id bit is set and ok flag is true.
187 */
188 int
filepath_valid(struct filepath_tree * tree,char * file,int id)189 filepath_valid(struct filepath_tree *tree, char *file, int id)
190 {
191 struct filepath *fp;
192
193 if ((fp = filepath_find(tree, file)) == NULL)
194 return 0;
195 return (fp->talmask & (1 << id)) != 0;
196 }
197
198 /*
199 * Remove entry from tree and free it.
200 */
201 static void
filepath_put(struct filepath_tree * tree,struct filepath * fp)202 filepath_put(struct filepath_tree *tree, struct filepath *fp)
203 {
204 RB_REMOVE(filepath_tree, tree, fp);
205 free((void *)fp->file);
206 free(fp);
207 }
208
209 /*
210 * Free all elements of a filepath tree.
211 */
212 static void
filepath_free(struct filepath_tree * tree)213 filepath_free(struct filepath_tree *tree)
214 {
215 struct filepath *fp, *nfp;
216
217 RB_FOREACH_SAFE(fp, filepath_tree, tree, nfp)
218 filepath_put(tree, fp);
219 }
220
221 RB_GENERATE(filepath_tree, filepath, entry, filepathcmp);
222
223 /*
224 * Function to hash a string into a unique directory name.
225 * Returned hash needs to be freed.
226 */
227 static char *
hash_dir(const char * uri)228 hash_dir(const char *uri)
229 {
230 unsigned char m[SHA256_DIGEST_LENGTH];
231
232 SHA256(uri, strlen(uri), m);
233 return hex_encode(m, sizeof(m));
234 }
235
236 /*
237 * Function to build the directory name based on URI and a directory
238 * as prefix. Skip the proto:// in URI but keep everything else.
239 */
240 static char *
repo_dir(const char * uri,const char * dir,int hash)241 repo_dir(const char *uri, const char *dir, int hash)
242 {
243 const char *local;
244 char *out, *hdir = NULL;
245
246 if (hash) {
247 local = hdir = hash_dir(uri);
248 } else {
249 local = strchr(uri, ':');
250 if (local != NULL)
251 local += strlen("://");
252 else
253 local = uri;
254 }
255
256 if (dir == NULL) {
257 if ((out = strdup(local)) == NULL)
258 err(1, NULL);
259 } else {
260 if (asprintf(&out, "%s/%s", dir, local) == -1)
261 err(1, NULL);
262 }
263
264 free(hdir);
265 return out;
266 }
267
268 /*
269 * Function to create all missing directories to a path.
270 * This functions alters the path temporarily.
271 */
272 static int
repo_mkpath(int fd,char * file)273 repo_mkpath(int fd, char *file)
274 {
275 char *slash;
276
277 /* build directory hierarchy */
278 slash = strrchr(file, '/');
279 assert(slash != NULL);
280 *slash = '\0';
281 if (mkpathat(fd, file) == -1) {
282 warn("mkpath %s", file);
283 return -1;
284 }
285 *slash = '/';
286 return 0;
287 }
288
289 /*
290 * Return the state of a repository.
291 */
292 static enum repo_state
repo_state(const struct repo * rp)293 repo_state(const struct repo *rp)
294 {
295 if (rp->ta)
296 return rp->ta->state;
297 if (rp->rsync)
298 return rp->rsync->state;
299 if (rp->rrdp)
300 return rp->rrdp->state;
301 /* No backend so sync is by definition done. */
302 return REPO_DONE;
303 }
304
305 /*
306 * Function called once a repository is done with the sync. Either
307 * successfully or after failure.
308 */
309 static void
repo_done(const void * vp,int ok)310 repo_done(const void *vp, int ok)
311 {
312 struct repo *rp;
313 struct timespec flush_time;
314
315 SLIST_FOREACH(rp, &repos, entry) {
316 if (vp != rp->ta && vp != rp->rsync && vp != rp->rrdp)
317 continue;
318
319 /* for rrdp try to fall back to rsync */
320 if (vp == rp->rrdp && !ok && !nofetch) {
321 rp->rrdp = NULL;
322 rp->rsync = rsync_get(rp->repouri, rp->basedir);
323 /* need to check if it was already loaded */
324 if (repo_state(rp) == REPO_LOADING)
325 continue;
326 }
327
328 entityq_flush(&rp->queue, rp);
329 clock_gettime(CLOCK_MONOTONIC, &flush_time);
330 timespecsub(&flush_time, &rp->start_time,
331 &rp->repostats.sync_time);
332 }
333 }
334
335 /*
336 * Build TA file name based on the repo info.
337 * If temp is set add Xs for mkostemp.
338 */
339 static char *
ta_filename(const struct tarepo * tr)340 ta_filename(const struct tarepo *tr)
341 {
342 const char *file;
343 char *nfile;
344
345 /* does not matter which URI, all end with same filename */
346 file = strrchr(tr->uri[0], '/');
347 assert(file);
348
349 if (asprintf(&nfile, "%s%s", tr->basedir, file) == -1)
350 err(1, NULL);
351
352 return nfile;
353 }
354
355 static void
ta_fetch(struct tarepo * tr)356 ta_fetch(struct tarepo *tr)
357 {
358 if (!rrdpon) {
359 for (; tr->uriidx < tr->urisz; tr->uriidx++) {
360 if (strncasecmp(tr->uri[tr->uriidx],
361 RSYNC_PROTO, RSYNC_PROTO_LEN) == 0)
362 break;
363 }
364 }
365
366 if (tr->uriidx >= tr->urisz) {
367 tr->state = REPO_FAILED;
368 logx("ta/%s: fallback to cache", tr->descr);
369
370 repo_done(tr, 0);
371 return;
372 }
373
374 logx("ta/%s: pulling from %s", tr->descr, tr->uri[tr->uriidx]);
375
376 if (strncasecmp(tr->uri[tr->uriidx], RSYNC_PROTO,
377 RSYNC_PROTO_LEN) == 0) {
378 /*
379 * Create destination location.
380 * Build up the tree to this point.
381 */
382 rsync_fetch(tr->id, tr->uri[tr->uriidx], tr->basedir, NULL);
383 } else {
384 char *temp;
385 int fd;
386
387 temp = ta_filename(tr);
388 fd = open(temp,
389 O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW | O_CLOEXEC,
390 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
391 if (fd == -1) {
392 warn("open: %s", temp);
393 free(temp);
394 http_finish(tr->id, HTTP_FAILED, NULL);
395 return;
396 }
397
398 free(temp);
399 http_fetch(tr->id, tr->uri[tr->uriidx], NULL, fd);
400 }
401 }
402
403 static struct tarepo *
ta_get(struct tal * tal)404 ta_get(struct tal *tal)
405 {
406 struct tarepo *tr;
407
408 /* no need to look for possible other repo */
409
410 if ((tr = calloc(1, sizeof(*tr))) == NULL)
411 err(1, NULL);
412
413 tr->id = ++repoid;
414 SLIST_INSERT_HEAD(&tarepos, tr, entry);
415
416 if ((tr->descr = strdup(tal->descr)) == NULL)
417 err(1, NULL);
418 tr->basedir = repo_dir(tal->descr, ".ta", 0);
419
420 /* create base directory */
421 if (mkpath(tr->basedir) == -1) {
422 warn("mkpath %s", tr->basedir);
423 tr->state = REPO_FAILED;
424 repo_done(tr, 0);
425 return tr;
426 }
427
428 /* steal URI information from TAL */
429 tr->urisz = tal->urisz;
430 tr->uri = tal->uri;
431 tal->urisz = 0;
432 tal->uri = NULL;
433
434 ta_fetch(tr);
435
436 return tr;
437 }
438
439 static struct tarepo *
ta_find(unsigned int id)440 ta_find(unsigned int id)
441 {
442 struct tarepo *tr;
443
444 SLIST_FOREACH(tr, &tarepos, entry)
445 if (id == tr->id)
446 break;
447 return tr;
448 }
449
450 static void
ta_free(void)451 ta_free(void)
452 {
453 struct tarepo *tr;
454
455 while ((tr = SLIST_FIRST(&tarepos)) != NULL) {
456 SLIST_REMOVE_HEAD(&tarepos, entry);
457 free(tr->descr);
458 free(tr->basedir);
459 free(tr->uri);
460 free(tr);
461 }
462 }
463
464 static struct rsyncrepo *
rsync_get(const char * uri,const char * validdir)465 rsync_get(const char *uri, const char *validdir)
466 {
467 struct rsyncrepo *rr;
468 char *repo;
469
470 if ((repo = rsync_base_uri(uri)) == NULL)
471 errx(1, "bad caRepository URI: %s", uri);
472
473 SLIST_FOREACH(rr, &rsyncrepos, entry)
474 if (strcmp(rr->repouri, repo) == 0) {
475 free(repo);
476 return rr;
477 }
478
479 if ((rr = calloc(1, sizeof(*rr))) == NULL)
480 err(1, NULL);
481
482 rr->id = ++repoid;
483 SLIST_INSERT_HEAD(&rsyncrepos, rr, entry);
484
485 rr->repouri = repo;
486 rr->basedir = repo_dir(repo, ".rsync", 0);
487
488 /* create base directory */
489 if (mkpath(rr->basedir) == -1) {
490 warn("mkpath %s", rr->basedir);
491 rsync_finish(rr->id, 0);
492 return rr;
493 }
494
495 logx("%s: pulling from %s", rr->basedir, rr->repouri);
496 rsync_fetch(rr->id, rr->repouri, rr->basedir, validdir);
497
498 return rr;
499 }
500
501 static struct rsyncrepo *
rsync_find(unsigned int id)502 rsync_find(unsigned int id)
503 {
504 struct rsyncrepo *rr;
505
506 SLIST_FOREACH(rr, &rsyncrepos, entry)
507 if (id == rr->id)
508 break;
509 return rr;
510 }
511
512 static void
rsync_free(void)513 rsync_free(void)
514 {
515 struct rsyncrepo *rr;
516
517 while ((rr = SLIST_FIRST(&rsyncrepos)) != NULL) {
518 SLIST_REMOVE_HEAD(&rsyncrepos, entry);
519 free(rr->repouri);
520 free(rr->basedir);
521 free(rr);
522 }
523 }
524
525 /*
526 * Build local file name base on the URI and the rrdprepo info.
527 */
528 static char *
rrdp_filename(const struct rrdprepo * rr,const char * uri,int valid)529 rrdp_filename(const struct rrdprepo *rr, const char *uri, int valid)
530 {
531 char *nfile;
532 const char *dir = rr->basedir;
533
534 if (!valid_uri(uri, strlen(uri), RSYNC_PROTO))
535 errx(1, "%s: bad URI %s", rr->basedir, uri);
536 uri += RSYNC_PROTO_LEN; /* skip proto */
537 if (valid) {
538 if ((nfile = strdup(uri)) == NULL)
539 err(1, NULL);
540 } else {
541 if (asprintf(&nfile, "%s/%s", dir, uri) == -1)
542 err(1, NULL);
543 }
544 return nfile;
545 }
546
547 /*
548 * Build RRDP state file name based on the repo info.
549 * If temp is set add Xs for mkostemp.
550 */
551 static char *
rrdp_state_filename(const struct rrdprepo * rr,int temp)552 rrdp_state_filename(const struct rrdprepo *rr, int temp)
553 {
554 char *nfile;
555
556 if (asprintf(&nfile, "%s/.state%s", rr->basedir,
557 temp ? ".XXXXXXXX" : "") == -1)
558 err(1, NULL);
559
560 return nfile;
561 }
562
563 static struct rrdprepo *
rrdp_find(unsigned int id)564 rrdp_find(unsigned int id)
565 {
566 struct rrdprepo *rr;
567
568 SLIST_FOREACH(rr, &rrdprepos, entry)
569 if (id == rr->id)
570 break;
571 return rr;
572 }
573
574 static void
rrdp_free(void)575 rrdp_free(void)
576 {
577 struct rrdprepo *rr;
578
579 while ((rr = SLIST_FIRST(&rrdprepos)) != NULL) {
580 SLIST_REMOVE_HEAD(&rrdprepos, entry);
581
582 free(rr->notifyuri);
583 free(rr->basedir);
584
585 filepath_free(&rr->deleted);
586
587 free(rr);
588 }
589 }
590
591 /*
592 * Check if a directory is an active rrdp repository.
593 * Returns 1 if found else 0.
594 */
595 static struct repo *
repo_rrdp_bypath(const char * dir)596 repo_rrdp_bypath(const char *dir)
597 {
598 struct repo *rp;
599
600 SLIST_FOREACH(rp, &repos, entry) {
601 if (rp->rrdp == NULL)
602 continue;
603 if (strcmp(dir, rp->rrdp->basedir) == 0)
604 return rp;
605 }
606 return NULL;
607 }
608
609 /*
610 * Check if the URI is actually covered by one of the repositories
611 * that depend on this RRDP repository.
612 * Returns 1 if the URI is valid, 0 if no repouri matches the URI.
613 */
614 static int
rrdp_uri_valid(struct rrdprepo * rr,const char * uri)615 rrdp_uri_valid(struct rrdprepo *rr, const char *uri)
616 {
617 struct repo *rp;
618
619 SLIST_FOREACH(rp, &repos, entry) {
620 if (rp->rrdp != rr)
621 continue;
622 if (strncmp(uri, rp->repouri, strlen(rp->repouri)) == 0)
623 return 1;
624 }
625 return 0;
626 }
627
628 /*
629 * Allocate and insert a new repository.
630 */
631 static struct repo *
repo_alloc(int talid)632 repo_alloc(int talid)
633 {
634 struct repo *rp;
635
636 if ((rp = calloc(1, sizeof(*rp))) == NULL)
637 err(1, NULL);
638
639 rp->id = ++repoid;
640 rp->talid = talid;
641 rp->alarm = getmonotime() + repo_timeout;
642 TAILQ_INIT(&rp->queue);
643 SLIST_INSERT_HEAD(&repos, rp, entry);
644 clock_gettime(CLOCK_MONOTONIC, &rp->start_time);
645
646 stats.repos++;
647 return rp;
648 }
649
650 /*
651 * Parse the RRDP state file if it exists and set the session struct
652 * based on that information.
653 */
654 static struct rrdp_session *
rrdp_session_parse(struct rrdprepo * rr)655 rrdp_session_parse(struct rrdprepo *rr)
656 {
657 FILE *f;
658 struct rrdp_session *state;
659 int fd, ln = 0, deltacnt = 0;
660 const char *errstr;
661 char *line = NULL, *file;
662 size_t i, len = 0;
663 ssize_t n;
664 time_t now, weeks;
665
666 now = time(NULL);
667
668 if ((state = calloc(1, sizeof(*state))) == NULL)
669 err(1, NULL);
670
671 file = rrdp_state_filename(rr, 0);
672 if ((fd = open(file, O_RDONLY)) == -1) {
673 if (errno != ENOENT)
674 warn("%s: open state file", rr->basedir);
675 free(file);
676 rr->last_reset = now;
677 return state;
678 }
679 free(file);
680 f = fdopen(fd, "r");
681 if (f == NULL)
682 err(1, "fdopen");
683
684 while ((n = getline(&line, &len, f)) != -1) {
685 if (line[n - 1] == '\n')
686 line[n - 1] = '\0';
687 switch (ln) {
688 case 0:
689 if ((state->session_id = strdup(line)) == NULL)
690 err(1, NULL);
691 break;
692 case 1:
693 state->serial = strtonum(line, 1, LLONG_MAX, &errstr);
694 if (errstr) {
695 warnx("%s: state file: serial is %s: %s",
696 rr->basedir, errstr, line);
697 goto reset;
698 }
699 break;
700 case 2:
701 rr->last_reset = strtonum(line, 1, LLONG_MAX, &errstr);
702 if (errstr) {
703 warnx("%s: state file: last_reset is %s: %s",
704 rr->basedir, errstr, line);
705 goto reset;
706 }
707 break;
708 case 3:
709 if (strcmp(line, "-") == 0)
710 break;
711 if ((state->last_mod = strdup(line)) == NULL)
712 err(1, NULL);
713 break;
714 default:
715 if (deltacnt >= MAX_RRDP_DELTAS) {
716 warnx("%s: state file: too many deltas: %d",
717 rr->basedir, deltacnt);
718 goto reset;
719 }
720 if ((state->deltas[deltacnt++] = strdup(line)) == NULL)
721 err(1, NULL);
722 break;
723 }
724 ln++;
725 }
726
727 if (ferror(f)) {
728 warn("%s: error reading state file", rr->basedir);
729 goto reset;
730 }
731
732 /* check if it's time for reinitialization */
733 weeks = (now - rr->last_reset) / (86400 * 7);
734 if (now <= rr->last_reset || weeks > RRDP_RANDOM_REINIT_MAX) {
735 warnx("%s: reinitializing", rr->notifyuri);
736 goto reset;
737 }
738 if (arc4random_uniform(1U << RRDP_RANDOM_REINIT_MAX) < (1U << weeks)) {
739 warnx("%s: reinitializing", rr->notifyuri);
740 goto reset;
741 }
742
743 fclose(f);
744 free(line);
745 return state;
746
747 reset:
748 fclose(f);
749 free(line);
750 free(state->session_id);
751 free(state->last_mod);
752 for (i = 0; i < sizeof(state->deltas) / sizeof(state->deltas[0]); i++)
753 free(state->deltas[i]);
754 memset(state, 0, sizeof(*state));
755 rr->last_reset = now;
756 return state;
757 }
758
759 /*
760 * Carefully write the RRDP session state file back.
761 */
762 void
rrdp_session_save(unsigned int id,struct rrdp_session * state)763 rrdp_session_save(unsigned int id, struct rrdp_session *state)
764 {
765 struct rrdprepo *rr;
766 char *temp, *file;
767 FILE *f = NULL;
768 int fd, i;
769
770 rr = rrdp_find(id);
771 if (rr == NULL)
772 errx(1, "non-existent rrdp repo %u", id);
773
774 file = rrdp_state_filename(rr, 0);
775 temp = rrdp_state_filename(rr, 1);
776
777 if ((fd = mkostemp(temp, O_CLOEXEC)) == -1)
778 goto fail;
779 (void)fchmod(fd, 0644);
780 f = fdopen(fd, "w");
781 if (f == NULL)
782 err(1, "fdopen");
783
784 /* write session state file out */
785 if (fprintf(f, "%s\n%lld\n%lld\n", state->session_id,
786 state->serial, (long long)rr->last_reset) < 0)
787 goto fail;
788
789 if (state->last_mod != NULL) {
790 if (fprintf(f, "%s\n", state->last_mod) < 0)
791 goto fail;
792 } else {
793 if (fprintf(f, "-\n") < 0)
794 goto fail;
795 }
796 for (i = 0; i < MAX_RRDP_DELTAS && state->deltas[i] != NULL; i++) {
797 if (fprintf(f, "%s\n", state->deltas[i]) < 0)
798 goto fail;
799 }
800 if (fclose(f) != 0) {
801 f = NULL;
802 goto fail;
803 }
804
805 if (rename(temp, file) == -1) {
806 warn("%s: rename %s to %s", rr->basedir, temp, file);
807 unlink(temp);
808 }
809
810 free(temp);
811 free(file);
812 return;
813
814 fail:
815 warn("%s: save state to %s", rr->basedir, temp);
816 if (f != NULL)
817 fclose(f);
818 unlink(temp);
819 free(temp);
820 free(file);
821 }
822
823 /*
824 * Free an rrdp_session pointer. Safe to call with NULL.
825 */
826 void
rrdp_session_free(struct rrdp_session * s)827 rrdp_session_free(struct rrdp_session *s)
828 {
829 size_t i;
830
831 if (s == NULL)
832 return;
833 free(s->session_id);
834 free(s->last_mod);
835 for (i = 0; i < sizeof(s->deltas) / sizeof(s->deltas[0]); i++)
836 free(s->deltas[i]);
837 free(s);
838 }
839
840 void
rrdp_session_buffer(struct ibuf * b,const struct rrdp_session * s)841 rrdp_session_buffer(struct ibuf *b, const struct rrdp_session *s)
842 {
843 size_t i;
844
845 io_str_buffer(b, s->session_id);
846 io_simple_buffer(b, &s->serial, sizeof(s->serial));
847 io_str_buffer(b, s->last_mod);
848 for (i = 0; i < sizeof(s->deltas) / sizeof(s->deltas[0]); i++)
849 io_str_buffer(b, s->deltas[i]);
850 }
851
852 struct rrdp_session *
rrdp_session_read(struct ibuf * b)853 rrdp_session_read(struct ibuf *b)
854 {
855 struct rrdp_session *s;
856 size_t i;
857
858 if ((s = calloc(1, sizeof(*s))) == NULL)
859 err(1, NULL);
860
861 io_read_str(b, &s->session_id);
862 io_read_buf(b, &s->serial, sizeof(s->serial));
863 io_read_str(b, &s->last_mod);
864 for (i = 0; i < sizeof(s->deltas) / sizeof(s->deltas[0]); i++)
865 io_read_str(b, &s->deltas[i]);
866
867 return s;
868 }
869
870 static struct rrdprepo *
rrdp_get(const char * uri)871 rrdp_get(const char *uri)
872 {
873 struct rrdp_session *state;
874 struct rrdprepo *rr;
875
876 SLIST_FOREACH(rr, &rrdprepos, entry)
877 if (strcmp(rr->notifyuri, uri) == 0) {
878 if (rr->state == REPO_FAILED)
879 return NULL;
880 return rr;
881 }
882
883 if ((rr = calloc(1, sizeof(*rr))) == NULL)
884 err(1, NULL);
885
886 rr->id = ++repoid;
887 SLIST_INSERT_HEAD(&rrdprepos, rr, entry);
888
889 if ((rr->notifyuri = strdup(uri)) == NULL)
890 err(1, NULL);
891 rr->basedir = repo_dir(uri, ".rrdp", 1);
892
893 RB_INIT(&rr->deleted);
894
895 /* create base directory */
896 if (mkpath(rr->basedir) == -1) {
897 warn("mkpath %s", rr->basedir);
898 rrdp_finish(rr->id, 0);
899 return rr;
900 }
901
902 /* parse state and start the sync */
903 state = rrdp_session_parse(rr);
904 rrdp_fetch(rr->id, rr->notifyuri, rr->notifyuri, state);
905 rrdp_session_free(state);
906
907 logx("%s: pulling from %s", rr->notifyuri, "network");
908
909 return rr;
910 }
911
912 /*
913 * Remove RRDP repo and start over.
914 */
915 void
rrdp_clear(unsigned int id)916 rrdp_clear(unsigned int id)
917 {
918 struct rrdprepo *rr;
919
920 rr = rrdp_find(id);
921 if (rr == NULL)
922 errx(1, "non-existent rrdp repo %u", id);
923
924 /* remove rrdp repository contents */
925 remove_contents(rr->basedir);
926 }
927
928 /*
929 * Write a file into the temporary RRDP dir but only after checking
930 * its hash (if required). The function also makes sure that the file
931 * tracking is properly adjusted.
932 * Returns 1 on success, 0 if the repo is corrupt, -1 on IO error
933 */
934 int
rrdp_handle_file(unsigned int id,enum publish_type pt,char * uri,char * hash,size_t hlen,char * data,size_t dlen)935 rrdp_handle_file(unsigned int id, enum publish_type pt, char *uri,
936 char *hash, size_t hlen, char *data, size_t dlen)
937 {
938 struct rrdprepo *rr;
939 struct filepath *fp;
940 ssize_t s;
941 char *fn = NULL;
942 int fd = -1, try = 0, deleted = 0;
943 int flags;
944
945 rr = rrdp_find(id);
946 if (rr == NULL)
947 errx(1, "non-existent rrdp repo %u", id);
948 if (rr->state == REPO_FAILED)
949 return -1;
950
951 /* check hash of original file for updates and deletes */
952 if (pt == PUB_UPD || pt == PUB_DEL) {
953 if (filepath_exists(&rr->deleted, uri)) {
954 warnx("%s: already deleted", uri);
955 return 0;
956 }
957 /* try to open file first in rrdp then in valid repo */
958 do {
959 free(fn);
960 if ((fn = rrdp_filename(rr, uri, try++)) == NULL)
961 return 0;
962 fd = open(fn, O_RDONLY);
963 } while (fd == -1 && try < 2);
964
965 if (!valid_filehash(fd, hash, hlen)) {
966 warnx("%s: bad file digest for %s", rr->notifyuri, fn);
967 free(fn);
968 return 0;
969 }
970 free(fn);
971 }
972
973 /* write new content or mark uri as deleted. */
974 if (pt == PUB_DEL) {
975 filepath_add(&rr->deleted, uri, 0, 0, 1);
976 } else {
977 fp = filepath_find(&rr->deleted, uri);
978 if (fp != NULL) {
979 filepath_put(&rr->deleted, fp);
980 deleted = 1;
981 }
982
983 /* add new file to rrdp dir */
984 if ((fn = rrdp_filename(rr, uri, 0)) == NULL)
985 return 0;
986
987 if (repo_mkpath(AT_FDCWD, fn) == -1)
988 goto fail;
989
990 flags = O_WRONLY|O_CREAT|O_TRUNC;
991 if (pt == PUB_ADD && !deleted)
992 flags |= O_EXCL;
993 fd = open(fn, flags, 0644);
994 if (fd == -1) {
995 if (errno == EEXIST) {
996 warnx("%s: duplicate publish element for %s",
997 rr->notifyuri, fn);
998 free(fn);
999 return 0;
1000 }
1001 warn("open %s", fn);
1002 goto fail;
1003 }
1004
1005 if ((s = write(fd, data, dlen)) == -1) {
1006 warn("write %s", fn);
1007 goto fail;
1008 }
1009 close(fd);
1010 if ((size_t)s != dlen) /* impossible */
1011 errx(1, "short write %s", fn);
1012 free(fn);
1013 }
1014
1015 return 1;
1016
1017 fail:
1018 rr->state = REPO_FAILED;
1019 if (fd != -1)
1020 close(fd);
1021 free(fn);
1022 return -1;
1023 }
1024
1025 /*
1026 * RSYNC sync finished, either with or without success.
1027 */
1028 void
rsync_finish(unsigned int id,int ok)1029 rsync_finish(unsigned int id, int ok)
1030 {
1031 struct rsyncrepo *rr;
1032 struct tarepo *tr;
1033
1034 tr = ta_find(id);
1035 if (tr != NULL) {
1036 /* repository changed state already, ignore request */
1037 if (tr->state != REPO_LOADING)
1038 return;
1039 if (ok) {
1040 logx("ta/%s: loaded from network", tr->descr);
1041 stats.rsync_repos++;
1042 tr->state = REPO_DONE;
1043 repo_done(tr, 1);
1044 } else {
1045 warnx("ta/%s: load from network failed", tr->descr);
1046 stats.rsync_fails++;
1047 tr->uriidx++;
1048 ta_fetch(tr);
1049 }
1050 return;
1051 }
1052
1053 rr = rsync_find(id);
1054 if (rr == NULL)
1055 errx(1, "unknown rsync repo %u", id);
1056 /* repository changed state already, ignore request */
1057 if (rr->state != REPO_LOADING)
1058 return;
1059
1060 if (ok) {
1061 logx("%s: loaded from network", rr->basedir);
1062 stats.rsync_repos++;
1063 rr->state = REPO_DONE;
1064 } else {
1065 warnx("%s: load from network failed, fallback to cache",
1066 rr->basedir);
1067 stats.rsync_fails++;
1068 rr->state = REPO_FAILED;
1069 /* clear rsync repo since it failed */
1070 remove_contents(rr->basedir);
1071 }
1072
1073 repo_done(rr, ok);
1074 }
1075
1076 /*
1077 * RRDP sync finished, either with or without success.
1078 */
1079 void
rrdp_finish(unsigned int id,int ok)1080 rrdp_finish(unsigned int id, int ok)
1081 {
1082 struct rrdprepo *rr;
1083
1084 rr = rrdp_find(id);
1085 if (rr == NULL)
1086 errx(1, "unknown RRDP repo %u", id);
1087 /* repository changed state already, ignore request */
1088 if (rr->state != REPO_LOADING)
1089 return;
1090
1091 if (ok) {
1092 logx("%s: loaded from network", rr->notifyuri);
1093 stats.rrdp_repos++;
1094 rr->state = REPO_DONE;
1095 } else {
1096 warnx("%s: load from network failed, fallback to %s",
1097 rr->notifyuri, nofetch ? "cache" : "rsync");
1098 stats.rrdp_fails++;
1099 rr->state = REPO_FAILED;
1100 /* clear the RRDP repo since it failed */
1101 remove_contents(rr->basedir);
1102 /* also clear the list of deleted files */
1103 filepath_free(&rr->deleted);
1104 }
1105
1106 repo_done(rr, ok);
1107 }
1108
1109 /*
1110 * Handle responses from the http process. For TA file, either rename
1111 * or delete the temporary file. For RRDP requests relay the request
1112 * over to the rrdp process.
1113 */
1114 void
http_finish(unsigned int id,enum http_result res,const char * last_mod)1115 http_finish(unsigned int id, enum http_result res, const char *last_mod)
1116 {
1117 struct tarepo *tr;
1118
1119 tr = ta_find(id);
1120 if (tr == NULL) {
1121 /* not a TA fetch therefore RRDP */
1122 rrdp_http_done(id, res, last_mod);
1123 return;
1124 }
1125
1126 /* repository changed state already, ignore request */
1127 if (tr->state != REPO_LOADING)
1128 return;
1129
1130 /* Move downloaded TA file into place, or unlink on failure. */
1131 if (res == HTTP_OK) {
1132 logx("ta/%s: loaded from network", tr->descr);
1133 tr->state = REPO_DONE;
1134 stats.http_repos++;
1135 repo_done(tr, 1);
1136 } else {
1137 remove_contents(tr->basedir);
1138
1139 tr->uriidx++;
1140 warnx("ta/%s: load from network failed", tr->descr);
1141 ta_fetch(tr);
1142 }
1143 }
1144
1145 /*
1146 * Look up a trust anchor, queueing it for download if not found.
1147 */
1148 struct repo *
ta_lookup(int id,struct tal * tal)1149 ta_lookup(int id, struct tal *tal)
1150 {
1151 struct repo *rp;
1152
1153 if (tal->urisz == 0)
1154 errx(1, "TAL %s has no URI", tal->descr);
1155
1156 /* Look up in repository table. (Lookup should actually fail here) */
1157 SLIST_FOREACH(rp, &repos, entry) {
1158 if (strcmp(rp->repouri, tal->uri[0]) == 0)
1159 return rp;
1160 }
1161
1162 rp = repo_alloc(id);
1163 rp->basedir = repo_dir(tal->descr, "ta", 0);
1164 if ((rp->repouri = strdup(tal->uri[0])) == NULL)
1165 err(1, NULL);
1166
1167 /* check if sync disabled ... */
1168 if (noop) {
1169 logx("%s: using cache", rp->basedir);
1170 entityq_flush(&rp->queue, rp);
1171 return rp;
1172 }
1173
1174 /* try to create base directory */
1175 if (mkpath(rp->basedir) == -1)
1176 warn("mkpath %s", rp->basedir);
1177
1178 rp->ta = ta_get(tal);
1179
1180 /* need to check if it was already loaded */
1181 if (repo_state(rp) != REPO_LOADING)
1182 entityq_flush(&rp->queue, rp);
1183
1184 return rp;
1185 }
1186
1187 /*
1188 * Look up a repository, queueing it for discovery if not found.
1189 */
1190 struct repo *
repo_lookup(int talid,const char * uri,const char * notify)1191 repo_lookup(int talid, const char *uri, const char *notify)
1192 {
1193 struct repo *rp;
1194 char *repouri;
1195
1196 if ((repouri = rsync_base_uri(uri)) == NULL)
1197 errx(1, "bad caRepository URI: %s", uri);
1198
1199 /* Look up in repository table. */
1200 SLIST_FOREACH(rp, &repos, entry) {
1201 if (strcmp(rp->repouri, repouri) != 0)
1202 continue;
1203 if (rp->notifyuri != NULL) {
1204 if (notify == NULL)
1205 continue;
1206 if (strcmp(rp->notifyuri, notify) != 0)
1207 continue;
1208 } else if (notify != NULL)
1209 continue;
1210 /* found matching repo */
1211 free(repouri);
1212 return rp;
1213 }
1214
1215 rp = repo_alloc(talid);
1216 rp->basedir = repo_dir(repouri, NULL, 0);
1217 rp->repouri = repouri;
1218 if (notify != NULL)
1219 if ((rp->notifyuri = strdup(notify)) == NULL)
1220 err(1, NULL);
1221
1222 if (++talrepocnt[talid] >= MAX_REPO_PER_TAL) {
1223 if (talrepocnt[talid] == MAX_REPO_PER_TAL)
1224 warnx("too many repositories under %s", tals[talid]);
1225 nofetch = 1;
1226 }
1227
1228 /* check if sync disabled ... */
1229 if (noop || nofetch) {
1230 logx("%s: using cache", rp->basedir);
1231 entityq_flush(&rp->queue, rp);
1232 return rp;
1233 }
1234
1235 /* try to create base directory */
1236 if (mkpath(rp->basedir) == -1)
1237 warn("mkpath %s", rp->basedir);
1238
1239 /* ... else try RRDP first if available then rsync */
1240 if (notify != NULL)
1241 rp->rrdp = rrdp_get(notify);
1242 if (rp->rrdp == NULL)
1243 rp->rsync = rsync_get(uri, rp->basedir);
1244
1245 /* need to check if it was already loaded */
1246 if (repo_state(rp) != REPO_LOADING)
1247 entityq_flush(&rp->queue, rp);
1248
1249 return rp;
1250 }
1251
1252 /*
1253 * Find repository by identifier.
1254 */
1255 struct repo *
repo_byid(unsigned int id)1256 repo_byid(unsigned int id)
1257 {
1258 struct repo *rp;
1259
1260 SLIST_FOREACH(rp, &repos, entry) {
1261 if (rp->id == id)
1262 return rp;
1263 }
1264 return NULL;
1265 }
1266
1267 /*
1268 * Find repository by base path.
1269 */
1270 static struct repo *
repo_bypath(const char * path)1271 repo_bypath(const char *path)
1272 {
1273 struct repo *rp;
1274
1275 SLIST_FOREACH(rp, &repos, entry) {
1276 if (strcmp(rp->basedir, path) == 0)
1277 return rp;
1278 }
1279 return NULL;
1280 }
1281
1282 /*
1283 * Return the repository base or alternate directory.
1284 * Returned string must be freed by caller.
1285 */
1286 char *
repo_basedir(const struct repo * rp,int wantvalid)1287 repo_basedir(const struct repo *rp, int wantvalid)
1288 {
1289 char *path = NULL;
1290
1291 if (!wantvalid) {
1292 if (rp->ta) {
1293 if ((path = strdup(rp->ta->basedir)) == NULL)
1294 err(1, NULL);
1295 } else if (rp->rsync) {
1296 if ((path = strdup(rp->rsync->basedir)) == NULL)
1297 err(1, NULL);
1298 } else if (rp->rrdp) {
1299 path = rrdp_filename(rp->rrdp, rp->repouri, 0);
1300 } else
1301 path = NULL; /* only valid repo available */
1302 } else if (rp->basedir != NULL) {
1303 if ((path = strdup(rp->basedir)) == NULL)
1304 err(1, NULL);
1305 }
1306
1307 return path;
1308 }
1309
1310 /*
1311 * Return the repository identifier.
1312 */
1313 unsigned int
repo_id(const struct repo * rp)1314 repo_id(const struct repo *rp)
1315 {
1316 return rp->id;
1317 }
1318
1319 /*
1320 * Return the repository URI.
1321 */
1322 const char *
repo_uri(const struct repo * rp)1323 repo_uri(const struct repo *rp)
1324 {
1325 return rp->repouri;
1326 }
1327
1328 /*
1329 * Return the repository URI.
1330 */
1331 void
repo_fetch_uris(const struct repo * rp,const char ** carepo,const char ** notifyuri)1332 repo_fetch_uris(const struct repo *rp, const char **carepo,
1333 const char **notifyuri)
1334 {
1335 *carepo = rp->repouri;
1336 *notifyuri = rp->notifyuri;
1337 }
1338
1339 /*
1340 * Return 1 if repository is synced else 0.
1341 */
1342 int
repo_synced(const struct repo * rp)1343 repo_synced(const struct repo *rp)
1344 {
1345 if (repo_state(rp) == REPO_DONE &&
1346 !(rp->rrdp == NULL && rp->rsync == NULL && rp->ta == NULL))
1347 return 1;
1348 return 0;
1349 }
1350
1351 /*
1352 * Return the protocol string "rrdp", "rsync", "https" which was used to sync.
1353 * Result is only correct if repository was properly synced.
1354 */
1355 const char *
repo_proto(const struct repo * rp)1356 repo_proto(const struct repo *rp)
1357 {
1358
1359 if (rp->ta != NULL) {
1360 const struct tarepo *tr = rp->ta;
1361 if (tr->uriidx < tr->urisz &&
1362 strncasecmp(tr->uri[tr->uriidx], RSYNC_PROTO,
1363 RSYNC_PROTO_LEN) == 0)
1364 return "rsync";
1365 else
1366 return "https";
1367 }
1368 if (rp->rrdp != NULL)
1369 return "rrdp";
1370 return "rsync";
1371 }
1372
1373 /*
1374 * Return the repository tal ID.
1375 */
1376 int
repo_talid(const struct repo * rp)1377 repo_talid(const struct repo *rp)
1378 {
1379 return rp->talid;
1380 }
1381
1382 int
repo_queued(struct repo * rp,struct entity * p)1383 repo_queued(struct repo *rp, struct entity *p)
1384 {
1385 if (repo_state(rp) == REPO_LOADING) {
1386 TAILQ_INSERT_TAIL(&rp->queue, p, entries);
1387 return 1;
1388 }
1389 return 0;
1390 }
1391
1392 static void
repo_fail(struct repo * rp)1393 repo_fail(struct repo *rp)
1394 {
1395 /* reset the alarm since code may fallback to rsync */
1396 rp->alarm = getmonotime() + repo_timeout;
1397
1398 if (rp->ta)
1399 http_finish(rp->ta->id, HTTP_FAILED, NULL);
1400 else if (rp->rsync)
1401 rsync_finish(rp->rsync->id, 0);
1402 else if (rp->rrdp)
1403 rrdp_finish(rp->rrdp->id, 0);
1404 else
1405 errx(1, "%s: bad repo", rp->repouri);
1406 }
1407
1408 static void
repo_abort(struct repo * rp)1409 repo_abort(struct repo *rp)
1410 {
1411 /* reset the alarm */
1412 rp->alarm = getmonotime() + repo_timeout;
1413
1414 if (rp->rsync) {
1415 warnx("%s: synchronisation timeout", rp->repouri);
1416 rsync_abort(rp->rsync->id);
1417 } else if (rp->rrdp) {
1418 warnx("%s: synchronisation timeout", rp->notifyuri);
1419 rrdp_abort(rp->rrdp->id);
1420 }
1421
1422 repo_fail(rp);
1423 }
1424
1425 int
repo_check_timeout(int timeout)1426 repo_check_timeout(int timeout)
1427 {
1428 struct repo *rp;
1429 time_t now;
1430 int diff;
1431
1432 now = getmonotime();
1433
1434 /* check against our runtime deadline first */
1435 if (deadline != 0) {
1436 if (deadline <= now) {
1437 warnx("deadline reached, giving up on repository sync");
1438 nofetch = 1;
1439 /* clear deadline since nofetch is set */
1440 deadline = 0;
1441 /* increase now enough so that all pending repos fail */
1442 now += repo_timeout;
1443 } else {
1444 diff = deadline - now;
1445 diff *= 1000;
1446 if (timeout == INFTIM || diff < timeout)
1447 timeout = diff;
1448 }
1449 }
1450 /* Look up in repository table. (Lookup should actually fail here) */
1451 SLIST_FOREACH(rp, &repos, entry) {
1452 if (repo_state(rp) == REPO_LOADING) {
1453 if (rp->alarm <= now)
1454 repo_abort(rp);
1455 else {
1456 diff = rp->alarm - now;
1457 diff *= 1000;
1458 if (timeout == INFTIM || diff < timeout)
1459 timeout = diff;
1460 }
1461 }
1462 }
1463 return timeout;
1464 }
1465
1466 /*
1467 * Update repo-specific stats when files are going to be moved
1468 * from DIR_TEMP to DIR_VALID.
1469 */
1470 void
repostats_new_files_inc(struct repo * rp,const char * file)1471 repostats_new_files_inc(struct repo *rp, const char *file)
1472 {
1473 if (strncmp(file, ".rsync/", strlen(".rsync/")) == 0 ||
1474 strncmp(file, ".rrdp/", strlen(".rrdp/")) == 0)
1475 rp->repostats.new_files++;
1476 }
1477
1478 /*
1479 * Update stats object of repository depending on rtype and subtype.
1480 */
1481 void
repo_stat_inc(struct repo * rp,int talid,enum rtype type,enum stype subtype)1482 repo_stat_inc(struct repo *rp, int talid, enum rtype type, enum stype subtype)
1483 {
1484 if (rp == NULL)
1485 return;
1486 rp->stats_used[talid] = 1;
1487 switch (type) {
1488 case RTYPE_CER:
1489 if (subtype == STYPE_OK)
1490 rp->stats[talid].certs++;
1491 if (subtype == STYPE_FAIL)
1492 rp->stats[talid].certs_fail++;
1493 if (subtype == STYPE_BGPSEC) {
1494 rp->stats[talid].certs--;
1495 rp->stats[talid].brks++;
1496 }
1497 break;
1498 case RTYPE_MFT:
1499 if (subtype == STYPE_OK)
1500 rp->stats[talid].mfts++;
1501 if (subtype == STYPE_FAIL)
1502 rp->stats[talid].mfts_fail++;
1503 if (subtype == STYPE_SEQNUM_GAP)
1504 rp->stats[talid].mfts_gap++;
1505 break;
1506 case RTYPE_ROA:
1507 switch (subtype) {
1508 case STYPE_OK:
1509 rp->stats[talid].roas++;
1510 break;
1511 case STYPE_FAIL:
1512 rp->stats[talid].roas_fail++;
1513 break;
1514 case STYPE_INVALID:
1515 rp->stats[talid].roas_invalid++;
1516 break;
1517 case STYPE_TOTAL:
1518 rp->stats[talid].vrps++;
1519 break;
1520 case STYPE_UNIQUE:
1521 rp->stats[talid].vrps_uniqs++;
1522 break;
1523 case STYPE_DEC_UNIQUE:
1524 rp->stats[talid].vrps_uniqs--;
1525 break;
1526 default:
1527 break;
1528 }
1529 break;
1530 case RTYPE_ASPA:
1531 switch (subtype) {
1532 case STYPE_OK:
1533 rp->stats[talid].aspas++;
1534 break;
1535 case STYPE_FAIL:
1536 rp->stats[talid].aspas_fail++;
1537 break;
1538 case STYPE_INVALID:
1539 rp->stats[talid].aspas_invalid++;
1540 break;
1541 case STYPE_TOTAL:
1542 rp->stats[talid].vaps++;
1543 break;
1544 case STYPE_UNIQUE:
1545 rp->stats[talid].vaps_uniqs++;
1546 break;
1547 case STYPE_DEC_UNIQUE:
1548 rp->stats[talid].vaps_uniqs--;
1549 break;
1550 case STYPE_PROVIDERS:
1551 rp->stats[talid].vaps_pas++;
1552 break;
1553 case STYPE_OVERFLOW:
1554 rp->stats[talid].vaps_overflowed++;
1555 break;
1556 default:
1557 break;
1558 }
1559 break;
1560 case RTYPE_SPL:
1561 switch (subtype) {
1562 case STYPE_OK:
1563 rp->stats[talid].spls++;
1564 break;
1565 case STYPE_FAIL:
1566 rp->stats[talid].spls_fail++;
1567 break;
1568 case STYPE_INVALID:
1569 rp->stats[talid].spls_invalid++;
1570 break;
1571 case STYPE_TOTAL:
1572 rp->stats[talid].vsps++;
1573 break;
1574 case STYPE_UNIQUE:
1575 rp->stats[talid].vsps_uniqs++;
1576 break;
1577 case STYPE_DEC_UNIQUE:
1578 rp->stats[talid].vsps_uniqs--;
1579 break;
1580 default:
1581 break;
1582 }
1583 break;
1584 case RTYPE_CRL:
1585 rp->stats[talid].crls++;
1586 break;
1587 case RTYPE_GBR:
1588 rp->stats[talid].gbrs++;
1589 break;
1590 case RTYPE_TAK:
1591 rp->stats[talid].taks++;
1592 break;
1593 default:
1594 break;
1595 }
1596 }
1597
1598 void
repo_tal_stats_collect(void (* cb)(const struct repo *,const struct repotalstats *,void *),int talid,void * arg)1599 repo_tal_stats_collect(void (*cb)(const struct repo *,
1600 const struct repotalstats *, void *), int talid, void *arg)
1601 {
1602 struct repo *rp;
1603
1604 SLIST_FOREACH(rp, &repos, entry) {
1605 if (rp->stats_used[talid])
1606 cb(rp, &rp->stats[talid], arg);
1607 }
1608 }
1609
1610 void
repo_stats_collect(void (* cb)(const struct repo *,const struct repostats *,void *),void * arg)1611 repo_stats_collect(void (*cb)(const struct repo *, const struct repostats *,
1612 void *), void *arg)
1613 {
1614 struct repo *rp;
1615
1616 SLIST_FOREACH(rp, &repos, entry)
1617 cb(rp, &rp->repostats, arg);
1618 }
1619
1620 /*
1621 * Delayed delete of files from RRDP. Since RRDP has no security built-in
1622 * this code needs to check if this RRDP repository is actually allowed to
1623 * remove the file referenced by the URI.
1624 */
1625 static void
repo_cleanup_rrdp(struct filepath_tree * tree)1626 repo_cleanup_rrdp(struct filepath_tree *tree)
1627 {
1628 struct repo *rp;
1629 struct rrdprepo *rr;
1630 struct filepath *fp, *nfp;
1631 char *fn;
1632
1633 SLIST_FOREACH(rp, &repos, entry) {
1634 if (rp->rrdp == NULL)
1635 continue;
1636 rr = (struct rrdprepo *)rp->rrdp;
1637 RB_FOREACH_SAFE(fp, filepath_tree, &rr->deleted, nfp) {
1638 if (!rrdp_uri_valid(rr, fp->file)) {
1639 warnx("%s: external URI %s", rr->notifyuri,
1640 fp->file);
1641 filepath_put(&rr->deleted, fp);
1642 continue;
1643 }
1644 /* try to remove file from rrdp repo ... */
1645 fn = rrdp_filename(rr, fp->file, 0);
1646
1647 if (unlink(fn) == -1) {
1648 if (errno != ENOENT)
1649 warn("unlink %s", fn);
1650 } else {
1651 if (verbose > 1)
1652 logx("deleted %s", fn);
1653 rp->repostats.del_files++;
1654 }
1655 free(fn);
1656
1657 /* ... and from the valid repository if unused. */
1658 fn = rrdp_filename(rr, fp->file, 1);
1659 if (!filepath_exists(tree, fn)) {
1660 if (unlink(fn) == -1) {
1661 if (errno != ENOENT)
1662 warn("unlink %s", fn);
1663 } else {
1664 if (verbose > 1)
1665 logx("deleted %s", fn);
1666 rp->repostats.del_files++;
1667 }
1668 } else
1669 warnx("%s: referenced file supposed to be "
1670 "deleted", fn);
1671
1672 free(fn);
1673 filepath_put(&rr->deleted, fp);
1674 }
1675 }
1676 }
1677
1678 /*
1679 * All files in tree are valid and should be moved to the valid repository
1680 * if not already there. Rename the files to the new path and readd the
1681 * filepath entry with the new path if successful.
1682 */
1683 static void
repo_move_valid(struct filepath_tree * tree)1684 repo_move_valid(struct filepath_tree *tree)
1685 {
1686 struct filepath *fp, *nfp, *ofp;
1687 size_t rsyncsz = strlen(".rsync/");
1688 size_t rrdpsz = strlen(".rrdp/");
1689 size_t tasz = strlen(".ta/");
1690 char *fn, *base;
1691
1692 RB_FOREACH_SAFE(fp, filepath_tree, tree, nfp) {
1693 if (strncmp(fp->file, ".rsync/", rsyncsz) != 0 &&
1694 strncmp(fp->file, ".rrdp/", rrdpsz) != 0 &&
1695 strncmp(fp->file, ".ta/", tasz) != 0)
1696 continue; /* not a temporary file path */
1697
1698 if (strncmp(fp->file, ".rsync/", rsyncsz) == 0) {
1699 fn = fp->file + rsyncsz;
1700 } else if (strncmp(fp->file, ".ta/", tasz) == 0) {
1701 fn = fp->file + 1; /* just skip the '.' */
1702 } else {
1703 base = strchr(fp->file + rrdpsz, '/');
1704 assert(base != NULL);
1705 fn = base + 1;
1706
1707 /*
1708 * Adjust file last modification time in order to
1709 * minimize RSYNC synchronization load after transport
1710 * failover.
1711 * While serializing RRDP datastructures to disk, set
1712 * the last modified timestamp to the CMS signing-time,
1713 * the X.509 notBefore, or CRL lastUpdate timestamp.
1714 */
1715 if (fp->mtime != 0) {
1716 int ret;
1717 struct timespec ts[2];
1718
1719 ts[0].tv_nsec = UTIME_OMIT;
1720 ts[1].tv_sec = fp->mtime;
1721 ts[1].tv_nsec = 0;
1722 ret = utimensat(AT_FDCWD, fp->file, ts, 0);
1723 if (ret == -1) {
1724 warn("utimensat %s", fp->file);
1725 continue;
1726 }
1727 }
1728 }
1729
1730 if (repo_mkpath(AT_FDCWD, fn) == -1)
1731 continue;
1732
1733 /* switch filepath node to new path */
1734 RB_REMOVE(filepath_tree, tree, fp);
1735 base = fp->file;
1736 if ((fp->file = strdup(fn)) == NULL)
1737 err(1, NULL);
1738
1739 again:
1740 if ((ofp = RB_INSERT(filepath_tree, tree, fp)) != NULL) {
1741 if (ofp->talmask == 0) {
1742 /* conflicting path is not valid, drop it */
1743 filepath_put(tree, ofp);
1744 goto again;
1745 }
1746 if (fp->talmask != 0) {
1747 warnx("%s: file already present in "
1748 "validated cache", fp->file);
1749 }
1750 free(fp->file);
1751 free(fp);
1752 free(base);
1753 continue;
1754 }
1755
1756 if (rename(base, fp->file) == -1)
1757 warn("rename to %s", fp->file);
1758
1759 free(base);
1760 }
1761 }
1762
1763 struct fts_state {
1764 enum { BASE_DIR, RSYNC_DIR, TA_DIR, RRDP_DIR } type;
1765 struct repo *rp;
1766 } fts_state;
1767
1768 static const struct rrdprepo *
repo_is_rrdp(struct repo * rp)1769 repo_is_rrdp(struct repo *rp)
1770 {
1771 /* check for special pointers first these are not a repository */
1772 if (rp != NULL && rp->rrdp != NULL)
1773 return rp->rrdp->state == REPO_DONE ? rp->rrdp : NULL;
1774 return NULL;
1775 }
1776
1777 static inline char *
skip_dotslash(char * in)1778 skip_dotslash(char *in)
1779 {
1780 if (memcmp(in, "./", 2) == 0)
1781 return in + 2;
1782 return in;
1783 }
1784
1785 static void
repo_cleanup_entry(FTSENT * e,struct filepath_tree * tree,int cachefd)1786 repo_cleanup_entry(FTSENT *e, struct filepath_tree *tree, int cachefd)
1787 {
1788 const struct rrdprepo *rr;
1789 char *path;
1790
1791 path = skip_dotslash(e->fts_path);
1792 switch (e->fts_info) {
1793 case FTS_NSOK:
1794 if (filepath_exists(tree, path)) {
1795 e->fts_parent->fts_number++;
1796 break;
1797 }
1798 if (fts_state.type == RRDP_DIR && fts_state.rp != NULL) {
1799 e->fts_parent->fts_number++;
1800 /* handle rrdp .state files explicitly */
1801 if (e->fts_level == 3 &&
1802 strcmp(e->fts_name, ".state") == 0)
1803 break;
1804 /* can't delete these extra files */
1805 fts_state.rp->repostats.extra_files++;
1806 if (verbose > 1)
1807 logx("superfluous %s", path);
1808 break;
1809 }
1810 rr = repo_is_rrdp(fts_state.rp);
1811 if (rr != NULL) {
1812 struct stat st;
1813 char *fn;
1814
1815 if (asprintf(&fn, "%s/%s", rr->basedir, path) == -1)
1816 err(1, NULL);
1817
1818 /*
1819 * If the file exists in the rrdp dir
1820 * that file is newer and needs to be kept
1821 * so unlink this file instead of moving
1822 * it over the file in the rrdp dir.
1823 */
1824 if (fstatat(cachefd, fn, &st, 0) == 0 &&
1825 S_ISREG(st.st_mode)) {
1826 free(fn);
1827 goto unlink;
1828 }
1829 if (repo_mkpath(cachefd, fn) == 0) {
1830 if (renameat(AT_FDCWD, e->fts_accpath,
1831 cachefd, fn) == -1)
1832 warn("rename %s to %s", path, fn);
1833 else if (verbose > 1)
1834 logx("moved %s", path);
1835 fts_state.rp->repostats.extra_files++;
1836 }
1837 free(fn);
1838 } else {
1839 unlink:
1840 if (unlink(e->fts_accpath) == -1) {
1841 warn("unlink %s", path);
1842 } else if (fts_state.type == RSYNC_DIR ||
1843 fts_state.type == TA_DIR) {
1844 /* no need to keep rsync or ta files */
1845 if (verbose > 1)
1846 logx("deleted superfluous %s", path);
1847 if (fts_state.rp != NULL)
1848 fts_state.rp->repostats.del_extra_files++;
1849 else
1850 stats.repo_stats.del_extra_files++;
1851 } else {
1852 if (verbose > 1)
1853 logx("deleted %s", path);
1854 if (fts_state.rp != NULL)
1855 fts_state.rp->repostats.del_files++;
1856 else
1857 stats.repo_stats.del_files++;
1858 }
1859 }
1860 break;
1861 case FTS_D:
1862 if (e->fts_level == FTS_ROOTLEVEL) {
1863 fts_state.type = BASE_DIR;
1864 fts_state.rp = NULL;
1865 }
1866 if (e->fts_level == 1) {
1867 /* rpki.example.org or .rrdp / .rsync / .ta */
1868 if (strcmp(".rsync", e->fts_name) == 0)
1869 fts_state.type = RSYNC_DIR;
1870 else if (strcmp(".ta", e->fts_name) == 0)
1871 fts_state.type = TA_DIR;
1872 else if (strcmp(".rrdp", e->fts_name) == 0)
1873 fts_state.type = RRDP_DIR;
1874 else
1875 fts_state.type = BASE_DIR;
1876 fts_state.rp = NULL;
1877 }
1878 if (e->fts_level == 2) {
1879 /* rpki.example.org/repository or .rrdp/hashdir */
1880 if (fts_state.type == BASE_DIR)
1881 fts_state.rp = repo_bypath(path);
1882 if (fts_state.type == TA_DIR)
1883 fts_state.rp = repo_bypath(path + 1);
1884 /*
1885 * special handling for rrdp directories,
1886 * clear them if they are not used anymore but
1887 * only if rrdp is active.
1888 * Look them up just using the hash.
1889 */
1890 if (fts_state.type == RRDP_DIR)
1891 fts_state.rp = repo_rrdp_bypath(path);
1892 }
1893 if (e->fts_level == 3 && fts_state.type == RSYNC_DIR) {
1894 /* .rsync/rpki.example.org/repository */
1895 fts_state.rp = repo_bypath(path + strlen(".rsync/"));
1896 }
1897 break;
1898 case FTS_DP:
1899 if (e->fts_level == FTS_ROOTLEVEL)
1900 break;
1901 if (e->fts_level == 1) {
1902 /* do not remove .rsync and .rrdp */
1903 fts_state.rp = NULL;
1904 if (fts_state.type == RRDP_DIR ||
1905 fts_state.type == RSYNC_DIR ||
1906 fts_state.type == TA_DIR)
1907 break;
1908 }
1909
1910 e->fts_parent->fts_number += e->fts_number;
1911
1912 if (e->fts_number == 0) {
1913 if (rmdir(e->fts_accpath) == -1)
1914 warn("rmdir %s", path);
1915 if (fts_state.rp != NULL)
1916 fts_state.rp->repostats.del_dirs++;
1917 else
1918 stats.repo_stats.del_dirs++;
1919 }
1920 break;
1921 case FTS_SL:
1922 case FTS_SLNONE:
1923 warnx("symlink %s", path);
1924 if (unlink(e->fts_accpath) == -1)
1925 warn("unlink %s", path);
1926 stats.repo_stats.del_extra_files++;
1927 break;
1928 case FTS_NS:
1929 case FTS_ERR:
1930 if (e->fts_errno == ENOENT && e->fts_level == FTS_ROOTLEVEL)
1931 break;
1932 warnx("fts_read %s: %s", path, strerror(e->fts_errno));
1933 break;
1934 default:
1935 warnx("fts_read %s: unhandled[%x]", path, e->fts_info);
1936 break;
1937 }
1938 }
1939
1940 void
repo_cleanup(struct filepath_tree * tree,int cachefd)1941 repo_cleanup(struct filepath_tree *tree, int cachefd)
1942 {
1943 char *argv[2] = { ".", NULL };
1944 FTS *fts;
1945 FTSENT *e;
1946
1947 /* first move temp files which have been used to valid dir */
1948 repo_move_valid(tree);
1949 /* then delete files requested by rrdp */
1950 repo_cleanup_rrdp(tree);
1951
1952 if ((fts = fts_open(argv, FTS_PHYSICAL | FTS_NOSTAT, NULL)) == NULL)
1953 err(1, "fts_open");
1954 errno = 0;
1955 while ((e = fts_read(fts)) != NULL) {
1956 repo_cleanup_entry(e, tree, cachefd);
1957 errno = 0;
1958 }
1959 if (errno)
1960 err(1, "fts_read");
1961 if (fts_close(fts) == -1)
1962 err(1, "fts_close");
1963 }
1964
1965 void
repo_free(void)1966 repo_free(void)
1967 {
1968 struct repo *rp;
1969
1970 while ((rp = SLIST_FIRST(&repos)) != NULL) {
1971 SLIST_REMOVE_HEAD(&repos, entry);
1972 free(rp->repouri);
1973 free(rp->notifyuri);
1974 free(rp->basedir);
1975 free(rp);
1976 }
1977
1978 ta_free();
1979 rrdp_free();
1980 rsync_free();
1981 }
1982
1983 /*
1984 * Remove all files and directories under base.
1985 * Do not remove base directory itself and the .state file.
1986 */
1987 static void
remove_contents(char * base)1988 remove_contents(char *base)
1989 {
1990 char *argv[2] = { base, NULL };
1991 FTS *fts;
1992 FTSENT *e;
1993
1994 if ((fts = fts_open(argv, FTS_PHYSICAL | FTS_NOSTAT, NULL)) == NULL)
1995 err(1, "fts_open");
1996 errno = 0;
1997 while ((e = fts_read(fts)) != NULL) {
1998 switch (e->fts_info) {
1999 case FTS_NSOK:
2000 case FTS_SL:
2001 case FTS_SLNONE:
2002 if (e->fts_level == 1 &&
2003 strcmp(e->fts_name, ".state") == 0)
2004 break;
2005 if (unlink(e->fts_accpath) == -1)
2006 warn("unlink %s", e->fts_path);
2007 break;
2008 case FTS_D:
2009 break;
2010 case FTS_DP:
2011 /* keep root directory */
2012 if (e->fts_level == FTS_ROOTLEVEL)
2013 break;
2014 if (rmdir(e->fts_accpath) == -1)
2015 warn("rmdir %s", e->fts_path);
2016 break;
2017 case FTS_NS:
2018 case FTS_ERR:
2019 warnx("fts_read %s: %s", e->fts_path,
2020 strerror(e->fts_errno));
2021 break;
2022 default:
2023 warnx("unhandled[%x] %s", e->fts_info,
2024 e->fts_path);
2025 break;
2026 }
2027 errno = 0;
2028 }
2029 if (errno)
2030 err(1, "fts_read");
2031 if (fts_close(fts) == -1)
2032 err(1, "fts_close");
2033 }
2034