1*5af82120Sclaudio /* $OpenBSD: test-rrdp.c,v 1.10 2024/04/22 05:54:01 claudio Exp $ */
24d27a18aSclaudio /*
34d27a18aSclaudio * Copyright (c) 2020 Nils Fisher <nils_fisher@hotmail.com>
44d27a18aSclaudio * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
54d27a18aSclaudio *
64d27a18aSclaudio * Permission to use, copy, modify, and distribute this software for any
74d27a18aSclaudio * purpose with or without fee is hereby granted, provided that the above
84d27a18aSclaudio * copyright notice and this permission notice appear in all copies.
94d27a18aSclaudio *
104d27a18aSclaudio * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
114d27a18aSclaudio * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
124d27a18aSclaudio * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
134d27a18aSclaudio * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
144d27a18aSclaudio * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
154d27a18aSclaudio * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
164d27a18aSclaudio * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
174d27a18aSclaudio */
184d27a18aSclaudio #include <sys/queue.h>
194d27a18aSclaudio #include <sys/stat.h>
204d27a18aSclaudio
214d27a18aSclaudio #include <assert.h>
224d27a18aSclaudio #include <ctype.h>
234d27a18aSclaudio #include <err.h>
244d27a18aSclaudio #include <errno.h>
254d27a18aSclaudio #include <fcntl.h>
264d27a18aSclaudio #include <limits.h>
274d27a18aSclaudio #include <poll.h>
284d27a18aSclaudio #include <string.h>
294d27a18aSclaudio #include <unistd.h>
304d27a18aSclaudio #include <sha2.h>
314d27a18aSclaudio
324d27a18aSclaudio #include <expat.h>
334d27a18aSclaudio #include <openssl/sha.h>
344d27a18aSclaudio
354d27a18aSclaudio #include "extern.h"
364d27a18aSclaudio #include "rrdp.h"
374d27a18aSclaudio
38edf59932Sjob int filemode;
3943e17b8aStb int outformats;
4043e17b8aStb int verbose;
41*5af82120Sclaudio int experimental;
42a27b1349Stb
434d27a18aSclaudio #define REGRESS_NOTIFY_URI "https://rpki.example.com/notify.xml"
444d27a18aSclaudio
454d27a18aSclaudio #define MAX_SESSIONS 12
464d27a18aSclaudio #define READ_BUF_SIZE (32 * 1024)
474d27a18aSclaudio
484d27a18aSclaudio #define RRDP_STATE_REQ 0x01
494d27a18aSclaudio #define RRDP_STATE_WAIT 0x02
504d27a18aSclaudio #define RRDP_STATE_PARSE 0x04
514d27a18aSclaudio #define RRDP_STATE_PARSE_ERROR 0x08
524d27a18aSclaudio #define RRDP_STATE_PARSE_DONE 0x10
534d27a18aSclaudio #define RRDP_STATE_HTTP_DONE 0x20
544d27a18aSclaudio #define RRDP_STATE_DONE (RRDP_STATE_PARSE_DONE | RRDP_STATE_HTTP_DONE)
554d27a18aSclaudio
564d27a18aSclaudio struct rrdp {
574d27a18aSclaudio TAILQ_ENTRY(rrdp) entry;
58f4525c43Sclaudio unsigned int id;
594d27a18aSclaudio char *notifyuri;
604d27a18aSclaudio char *local;
614d27a18aSclaudio char *last_mod;
624d27a18aSclaudio
634d27a18aSclaudio struct pollfd *pfd;
644d27a18aSclaudio int infd;
654d27a18aSclaudio int state;
664d27a18aSclaudio unsigned int file_pending;
674d27a18aSclaudio unsigned int file_failed;
684d27a18aSclaudio enum http_result res;
694d27a18aSclaudio enum rrdp_task task;
704d27a18aSclaudio
714d27a18aSclaudio char hash[SHA256_DIGEST_LENGTH];
724d27a18aSclaudio SHA256_CTX ctx;
734d27a18aSclaudio
744d27a18aSclaudio struct rrdp_session repository;
754d27a18aSclaudio struct rrdp_session current;
764d27a18aSclaudio XML_Parser parser;
774d27a18aSclaudio struct notification_xml *nxml;
784d27a18aSclaudio struct snapshot_xml *sxml;
794d27a18aSclaudio struct delta_xml *dxml;
804d27a18aSclaudio };
814d27a18aSclaudio
824d27a18aSclaudio void
logx(const char * fmt,...)834d27a18aSclaudio logx(const char *fmt, ...)
844d27a18aSclaudio {
854d27a18aSclaudio va_list ap;
864d27a18aSclaudio
874d27a18aSclaudio va_start(ap, fmt);
884d27a18aSclaudio vwarnx(fmt, ap);
894d27a18aSclaudio va_end(ap);
904d27a18aSclaudio }
914d27a18aSclaudio
924d27a18aSclaudio char *
xstrdup(const char * s)934d27a18aSclaudio xstrdup(const char *s)
944d27a18aSclaudio {
954d27a18aSclaudio char *r;
964d27a18aSclaudio if ((r = strdup(s)) == NULL)
974d27a18aSclaudio err(1, "strdup");
984d27a18aSclaudio return r;
994d27a18aSclaudio }
1004d27a18aSclaudio
1014d27a18aSclaudio /*
1024d27a18aSclaudio * Send a blob of data to the main process to store it in the repository.
1034d27a18aSclaudio */
1044d27a18aSclaudio void
rrdp_publish_file(struct rrdp * s,struct publish_xml * pxml,unsigned char * data,size_t datasz)1054d27a18aSclaudio rrdp_publish_file(struct rrdp *s, struct publish_xml *pxml,
1064d27a18aSclaudio unsigned char *data, size_t datasz)
1074d27a18aSclaudio {
1084d27a18aSclaudio char buf[SHA256_DIGEST_STRING_LENGTH];
1094d27a18aSclaudio char *hash = NULL;
1104d27a18aSclaudio
1114d27a18aSclaudio switch (pxml->type) {
1124d27a18aSclaudio case PUB_ADD:
1134d27a18aSclaudio logx("type: %s", "add");
1144d27a18aSclaudio break;
1154d27a18aSclaudio case PUB_UPD:
1164d27a18aSclaudio logx("type: %s", "update");
1174d27a18aSclaudio hash = hex_encode(pxml->hash, sizeof(pxml->hash));
1184d27a18aSclaudio break;
1194d27a18aSclaudio case PUB_DEL:
1204d27a18aSclaudio logx("type: %s", "delete");
1214d27a18aSclaudio hash = hex_encode(pxml->hash, sizeof(pxml->hash));
1224d27a18aSclaudio break;
1234d27a18aSclaudio default:
1244d27a18aSclaudio errx(1, "unknown publish type");
1254d27a18aSclaudio }
1264d27a18aSclaudio logx("uri: %s", pxml->uri);
1274d27a18aSclaudio SHA256Data(data, datasz, buf);
1284d27a18aSclaudio logx("data: %s", buf);
1294d27a18aSclaudio
1304d27a18aSclaudio if (hash)
1314d27a18aSclaudio logx("hash: %s", hash);
1324d27a18aSclaudio free(hash);
1334d27a18aSclaudio }
1344d27a18aSclaudio
1354d27a18aSclaudio static struct rrdp *
rrdp_new(unsigned int id,char * local,char * notify,char * session_id,long long serial,char * last_mod)136f4525c43Sclaudio rrdp_new(unsigned int id, char *local, char *notify, char *session_id,
1374d27a18aSclaudio long long serial, char *last_mod)
1384d27a18aSclaudio {
1394d27a18aSclaudio struct rrdp *s;
1404d27a18aSclaudio
1414d27a18aSclaudio if ((s = calloc(1, sizeof(*s))) == NULL)
1424d27a18aSclaudio err(1, NULL);
1434d27a18aSclaudio
1444d27a18aSclaudio s->infd = 0; /* stdin */
1454d27a18aSclaudio s->id = id;
1464d27a18aSclaudio s->local = local;
1474d27a18aSclaudio s->notifyuri = notify;
1484d27a18aSclaudio s->repository.session_id = session_id;
1494d27a18aSclaudio s->repository.serial = serial;
1504d27a18aSclaudio s->repository.last_mod = last_mod;
1514d27a18aSclaudio
1524d27a18aSclaudio s->state = RRDP_STATE_REQ;
1534d27a18aSclaudio if ((s->parser = XML_ParserCreate("US-ASCII")) == NULL)
1544d27a18aSclaudio err(1, "XML_ParserCreate");
1554d27a18aSclaudio
1564d27a18aSclaudio return s;
1574d27a18aSclaudio }
1584d27a18aSclaudio
1594d27a18aSclaudio static void
rrdp_free(struct rrdp * s)1604d27a18aSclaudio rrdp_free(struct rrdp *s)
1614d27a18aSclaudio {
1624d27a18aSclaudio if (s == NULL)
1634d27a18aSclaudio return;
1644d27a18aSclaudio
1654d27a18aSclaudio free_notification_xml(s->nxml);
1664d27a18aSclaudio free_snapshot_xml(s->sxml);
1674d27a18aSclaudio free_delta_xml(s->dxml);
1684d27a18aSclaudio
1694d27a18aSclaudio if (s->parser)
1704d27a18aSclaudio XML_ParserFree(s->parser);
1714d27a18aSclaudio if (s->infd != -1)
1724d27a18aSclaudio close(s->infd);
1734d27a18aSclaudio free(s->notifyuri);
1744d27a18aSclaudio free(s->local);
1754d27a18aSclaudio free(s->last_mod);
1764d27a18aSclaudio free(s->repository.last_mod);
1774d27a18aSclaudio free(s->repository.session_id);
1784d27a18aSclaudio free(s->current.last_mod);
1794d27a18aSclaudio free(s->current.session_id);
1804d27a18aSclaudio
1814d27a18aSclaudio free(s);
1824d27a18aSclaudio }
1834d27a18aSclaudio
1844d27a18aSclaudio static void
rrdp_finished(struct rrdp * s)1854d27a18aSclaudio rrdp_finished(struct rrdp *s)
1864d27a18aSclaudio {
1874d27a18aSclaudio XML_Parser p = s->parser;
188f4525c43Sclaudio unsigned int id = s->id;
1894d27a18aSclaudio
1904d27a18aSclaudio if (s->state & RRDP_STATE_PARSE_ERROR)
1914d27a18aSclaudio return;
1924d27a18aSclaudio
1934d27a18aSclaudio /*
1944d27a18aSclaudio * Finalize parsing on success to be sure that
1954d27a18aSclaudio * all of the XML is correct. Needs to be done here
1964d27a18aSclaudio * since the call would most probably fail for non
1974d27a18aSclaudio * successful data fetches.
1984d27a18aSclaudio */
1994d27a18aSclaudio if (XML_Parse(p, NULL, 0, 1) != XML_STATUS_OK) {
2004d27a18aSclaudio warnx("%s: XML error at line %llu: %s", s->local,
2014d27a18aSclaudio (unsigned long long)XML_GetCurrentLineNumber(p),
2024d27a18aSclaudio XML_ErrorString(XML_GetErrorCode(p)));
2034d27a18aSclaudio return;
2044d27a18aSclaudio }
2054d27a18aSclaudio
2064d27a18aSclaudio switch (s->task) {
2074d27a18aSclaudio case NOTIFICATION:
208628a100dSclaudio notification_done(s->nxml, NULL);
2094d27a18aSclaudio log_notification_xml(s->nxml);
2104d27a18aSclaudio break;
2114d27a18aSclaudio case SNAPSHOT:
2124d27a18aSclaudio log_snapshot_xml(s->sxml);
2134d27a18aSclaudio break;
2144d27a18aSclaudio case DELTA:
2154d27a18aSclaudio log_delta_xml(s->dxml);
2164d27a18aSclaudio break;
2174d27a18aSclaudio }
2184d27a18aSclaudio }
2194d27a18aSclaudio
2204d27a18aSclaudio static void
rrdp_data_handler(struct rrdp * s)2214d27a18aSclaudio rrdp_data_handler(struct rrdp *s)
2224d27a18aSclaudio {
2234d27a18aSclaudio char buf[READ_BUF_SIZE];
2244d27a18aSclaudio XML_Parser p = s->parser;
2254d27a18aSclaudio ssize_t len;
2264d27a18aSclaudio
2274d27a18aSclaudio len = read(s->infd, buf, sizeof(buf));
2284d27a18aSclaudio if (len == -1) {
2294d27a18aSclaudio s->state |= RRDP_STATE_PARSE_ERROR;
2304d27a18aSclaudio warn("%s: read failure", s->local);
2314d27a18aSclaudio return;
2324d27a18aSclaudio }
2334d27a18aSclaudio if ((s->state & RRDP_STATE_PARSE) == 0)
2344d27a18aSclaudio errx(1, "%s: bad parser state", s->local);
2354d27a18aSclaudio if (len == 0) {
2364d27a18aSclaudio /* parser stage finished */
2374d27a18aSclaudio close(s->infd);
2384d27a18aSclaudio s->infd = -1;
2394d27a18aSclaudio
2404d27a18aSclaudio if (s->task != NOTIFICATION) {
2414d27a18aSclaudio char h[SHA256_DIGEST_LENGTH];
2424d27a18aSclaudio
2434d27a18aSclaudio SHA256_Final(h, &s->ctx);
2444d27a18aSclaudio if (memcmp(s->hash, h, sizeof(s->hash)) != 0) {
2454d27a18aSclaudio s->state |= RRDP_STATE_PARSE_ERROR;
2464d27a18aSclaudio warnx("%s: bad message digest", s->local);
2474d27a18aSclaudio }
2484d27a18aSclaudio }
2494d27a18aSclaudio
2504d27a18aSclaudio s->state |= RRDP_STATE_PARSE_DONE;
2514d27a18aSclaudio rrdp_finished(s);
2524d27a18aSclaudio return;
2534d27a18aSclaudio }
2544d27a18aSclaudio
2554d27a18aSclaudio /* parse and maybe hash the bytes just read */
2564d27a18aSclaudio if (s->task != NOTIFICATION)
2574d27a18aSclaudio SHA256_Update(&s->ctx, buf, len);
2584d27a18aSclaudio if ((s->state & RRDP_STATE_PARSE_ERROR) == 0 &&
2594d27a18aSclaudio XML_Parse(p, buf, len, 0) != XML_STATUS_OK) {
2604d27a18aSclaudio warnx("%s: parse error at line %llu: %s", s->local,
2614d27a18aSclaudio (unsigned long long)XML_GetCurrentLineNumber(p),
2624d27a18aSclaudio XML_ErrorString(XML_GetErrorCode(p)));
2634d27a18aSclaudio s->state |= RRDP_STATE_PARSE_ERROR;
2644d27a18aSclaudio }
2654d27a18aSclaudio }
2664d27a18aSclaudio
2674d27a18aSclaudio int
main(int argc,char ** argv)2684d27a18aSclaudio main(int argc, char **argv)
2694d27a18aSclaudio {
2704d27a18aSclaudio struct rrdp *s = NULL;
2714d27a18aSclaudio const char *e;
2724d27a18aSclaudio char *session_id = NULL;
2734d27a18aSclaudio char hash[SHA256_DIGEST_LENGTH];
2744d27a18aSclaudio long long serial = 0;
2754d27a18aSclaudio int c;
2764d27a18aSclaudio
2774d27a18aSclaudio
2784d27a18aSclaudio while ((c = getopt(argc, argv, "dH:N:nS:s")) != -1)
2794d27a18aSclaudio switch (c) {
2804d27a18aSclaudio case 'd':
2814d27a18aSclaudio if (s)
2824d27a18aSclaudio goto usage;
2834d27a18aSclaudio s = rrdp_new(0, "stdin", REGRESS_NOTIFY_URI,
2844d27a18aSclaudio session_id, serial, NULL);
2854d27a18aSclaudio s->dxml = new_delta_xml(s->parser,
2864d27a18aSclaudio &s->repository, s);
2874d27a18aSclaudio s->task = DELTA;
2884d27a18aSclaudio SHA256_Init(&s->ctx);
2894d27a18aSclaudio memcpy(s->hash, hash, sizeof(s->hash));
2904d27a18aSclaudio break;
2914d27a18aSclaudio case 'H':
2924d27a18aSclaudio if (hex_decode(optarg, hash, sizeof(hash)) == -1)
2934d27a18aSclaudio errx(1, "bad hash");
2944d27a18aSclaudio break;
2954d27a18aSclaudio case 'N':
2964d27a18aSclaudio serial = strtonum(optarg, LLONG_MIN, LLONG_MAX, &e);
2974d27a18aSclaudio if (e != NULL)
2984d27a18aSclaudio errx(1, "serial is %s: %s", e, optarg);
2994d27a18aSclaudio break;
3004d27a18aSclaudio case 'n':
3014d27a18aSclaudio if (s)
3024d27a18aSclaudio goto usage;
3034d27a18aSclaudio s = rrdp_new(0, "stdin", REGRESS_NOTIFY_URI,
3044d27a18aSclaudio session_id, serial, NULL);
3054d27a18aSclaudio s->nxml = new_notification_xml(s->parser,
3064d27a18aSclaudio &s->repository, &s->current, s->notifyuri);
3074d27a18aSclaudio s->task = NOTIFICATION;
3084d27a18aSclaudio break;
3094d27a18aSclaudio case 'S':
3104d27a18aSclaudio session_id = optarg;
3114d27a18aSclaudio break;
3124d27a18aSclaudio case 's':
3134d27a18aSclaudio if (s)
3144d27a18aSclaudio goto usage;
3154d27a18aSclaudio s = rrdp_new(0, "stdin", REGRESS_NOTIFY_URI,
3164d27a18aSclaudio session_id, serial, NULL);
3174d27a18aSclaudio s->sxml = new_snapshot_xml(s->parser,
3184d27a18aSclaudio &s->repository, s);
3194d27a18aSclaudio s->task = SNAPSHOT;
3204d27a18aSclaudio SHA256_Init(&s->ctx);
3214d27a18aSclaudio memcpy(s->hash, hash, sizeof(s->hash));
3224d27a18aSclaudio break;
3234d27a18aSclaudio default:
3244d27a18aSclaudio goto usage;
3254d27a18aSclaudio }
3264d27a18aSclaudio
3274d27a18aSclaudio s->state = RRDP_STATE_PARSE;
3284d27a18aSclaudio
3294d27a18aSclaudio while (!(s->state & RRDP_STATE_PARSE_DONE)) {
3304d27a18aSclaudio rrdp_data_handler(s);
3314d27a18aSclaudio }
3324d27a18aSclaudio
3334d27a18aSclaudio if ((s->state & RRDP_STATE_PARSE_ERROR) == 0) {
3344d27a18aSclaudio printf("OK\n");
3354d27a18aSclaudio return 0;
3364d27a18aSclaudio } else {
3374d27a18aSclaudio return 1;
3384d27a18aSclaudio }
3394d27a18aSclaudio
3404d27a18aSclaudio usage:
3414d27a18aSclaudio fprintf(stderr, "usage: %s [-S session_id] [-N serial] [-H hash] "
3424d27a18aSclaudio "-d | -n | -s\n", "test-rrdp");
3434d27a18aSclaudio exit(1);
3444d27a18aSclaudio }
3450876134dSclaudio
3460876134dSclaudio time_t
get_current_time(void)3470876134dSclaudio get_current_time(void)
3480876134dSclaudio {
3490876134dSclaudio return time(NULL);
3500876134dSclaudio }
351