351 lines
7.6 KiB
C
351 lines
7.6 KiB
C
/* $OpenBSD: test-rrdp.c,v 1.10 2024/04/22 05:54:01 claudio Exp $ */
|
|
/*
|
|
* Copyright (c) 2020 Nils Fisher <nils_fisher@hotmail.com>
|
|
* Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
#include <sys/queue.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <poll.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sha2.h>
|
|
|
|
#include <expat.h>
|
|
#include <openssl/sha.h>
|
|
|
|
#include "extern.h"
|
|
#include "rrdp.h"
|
|
|
|
int filemode;
|
|
int outformats;
|
|
int verbose;
|
|
int experimental;
|
|
|
|
#define REGRESS_NOTIFY_URI "https://rpki.example.com/notify.xml"
|
|
|
|
#define MAX_SESSIONS 12
|
|
#define READ_BUF_SIZE (32 * 1024)
|
|
|
|
#define RRDP_STATE_REQ 0x01
|
|
#define RRDP_STATE_WAIT 0x02
|
|
#define RRDP_STATE_PARSE 0x04
|
|
#define RRDP_STATE_PARSE_ERROR 0x08
|
|
#define RRDP_STATE_PARSE_DONE 0x10
|
|
#define RRDP_STATE_HTTP_DONE 0x20
|
|
#define RRDP_STATE_DONE (RRDP_STATE_PARSE_DONE | RRDP_STATE_HTTP_DONE)
|
|
|
|
struct rrdp {
|
|
TAILQ_ENTRY(rrdp) entry;
|
|
unsigned int id;
|
|
char *notifyuri;
|
|
char *local;
|
|
char *last_mod;
|
|
|
|
struct pollfd *pfd;
|
|
int infd;
|
|
int state;
|
|
unsigned int file_pending;
|
|
unsigned int file_failed;
|
|
enum http_result res;
|
|
enum rrdp_task task;
|
|
|
|
char hash[SHA256_DIGEST_LENGTH];
|
|
SHA256_CTX ctx;
|
|
|
|
struct rrdp_session repository;
|
|
struct rrdp_session current;
|
|
XML_Parser parser;
|
|
struct notification_xml *nxml;
|
|
struct snapshot_xml *sxml;
|
|
struct delta_xml *dxml;
|
|
};
|
|
|
|
void
|
|
logx(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
vwarnx(fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
char *
|
|
xstrdup(const char *s)
|
|
{
|
|
char *r;
|
|
if ((r = strdup(s)) == NULL)
|
|
err(1, "strdup");
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* Send a blob of data to the main process to store it in the repository.
|
|
*/
|
|
void
|
|
rrdp_publish_file(struct rrdp *s, struct publish_xml *pxml,
|
|
unsigned char *data, size_t datasz)
|
|
{
|
|
char buf[SHA256_DIGEST_STRING_LENGTH];
|
|
char *hash = NULL;
|
|
|
|
switch (pxml->type) {
|
|
case PUB_ADD:
|
|
logx("type: %s", "add");
|
|
break;
|
|
case PUB_UPD:
|
|
logx("type: %s", "update");
|
|
hash = hex_encode(pxml->hash, sizeof(pxml->hash));
|
|
break;
|
|
case PUB_DEL:
|
|
logx("type: %s", "delete");
|
|
hash = hex_encode(pxml->hash, sizeof(pxml->hash));
|
|
break;
|
|
default:
|
|
errx(1, "unknown publish type");
|
|
}
|
|
logx("uri: %s", pxml->uri);
|
|
SHA256Data(data, datasz, buf);
|
|
logx("data: %s", buf);
|
|
|
|
if (hash)
|
|
logx("hash: %s", hash);
|
|
free(hash);
|
|
}
|
|
|
|
static struct rrdp *
|
|
rrdp_new(unsigned int id, char *local, char *notify, char *session_id,
|
|
long long serial, char *last_mod)
|
|
{
|
|
struct rrdp *s;
|
|
|
|
if ((s = calloc(1, sizeof(*s))) == NULL)
|
|
err(1, NULL);
|
|
|
|
s->infd = 0; /* stdin */
|
|
s->id = id;
|
|
s->local = local;
|
|
s->notifyuri = notify;
|
|
s->repository.session_id = session_id;
|
|
s->repository.serial = serial;
|
|
s->repository.last_mod = last_mod;
|
|
|
|
s->state = RRDP_STATE_REQ;
|
|
if ((s->parser = XML_ParserCreate("US-ASCII")) == NULL)
|
|
err(1, "XML_ParserCreate");
|
|
|
|
return s;
|
|
}
|
|
|
|
static void
|
|
rrdp_free(struct rrdp *s)
|
|
{
|
|
if (s == NULL)
|
|
return;
|
|
|
|
free_notification_xml(s->nxml);
|
|
free_snapshot_xml(s->sxml);
|
|
free_delta_xml(s->dxml);
|
|
|
|
if (s->parser)
|
|
XML_ParserFree(s->parser);
|
|
if (s->infd != -1)
|
|
close(s->infd);
|
|
free(s->notifyuri);
|
|
free(s->local);
|
|
free(s->last_mod);
|
|
free(s->repository.last_mod);
|
|
free(s->repository.session_id);
|
|
free(s->current.last_mod);
|
|
free(s->current.session_id);
|
|
|
|
free(s);
|
|
}
|
|
|
|
static void
|
|
rrdp_finished(struct rrdp *s)
|
|
{
|
|
XML_Parser p = s->parser;
|
|
unsigned int id = s->id;
|
|
|
|
if (s->state & RRDP_STATE_PARSE_ERROR)
|
|
return;
|
|
|
|
/*
|
|
* Finalize parsing on success to be sure that
|
|
* all of the XML is correct. Needs to be done here
|
|
* since the call would most probably fail for non
|
|
* successful data fetches.
|
|
*/
|
|
if (XML_Parse(p, NULL, 0, 1) != XML_STATUS_OK) {
|
|
warnx("%s: XML error at line %llu: %s", s->local,
|
|
(unsigned long long)XML_GetCurrentLineNumber(p),
|
|
XML_ErrorString(XML_GetErrorCode(p)));
|
|
return;
|
|
}
|
|
|
|
switch (s->task) {
|
|
case NOTIFICATION:
|
|
notification_done(s->nxml, NULL);
|
|
log_notification_xml(s->nxml);
|
|
break;
|
|
case SNAPSHOT:
|
|
log_snapshot_xml(s->sxml);
|
|
break;
|
|
case DELTA:
|
|
log_delta_xml(s->dxml);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
rrdp_data_handler(struct rrdp *s)
|
|
{
|
|
char buf[READ_BUF_SIZE];
|
|
XML_Parser p = s->parser;
|
|
ssize_t len;
|
|
|
|
len = read(s->infd, buf, sizeof(buf));
|
|
if (len == -1) {
|
|
s->state |= RRDP_STATE_PARSE_ERROR;
|
|
warn("%s: read failure", s->local);
|
|
return;
|
|
}
|
|
if ((s->state & RRDP_STATE_PARSE) == 0)
|
|
errx(1, "%s: bad parser state", s->local);
|
|
if (len == 0) {
|
|
/* parser stage finished */
|
|
close(s->infd);
|
|
s->infd = -1;
|
|
|
|
if (s->task != NOTIFICATION) {
|
|
char h[SHA256_DIGEST_LENGTH];
|
|
|
|
SHA256_Final(h, &s->ctx);
|
|
if (memcmp(s->hash, h, sizeof(s->hash)) != 0) {
|
|
s->state |= RRDP_STATE_PARSE_ERROR;
|
|
warnx("%s: bad message digest", s->local);
|
|
}
|
|
}
|
|
|
|
s->state |= RRDP_STATE_PARSE_DONE;
|
|
rrdp_finished(s);
|
|
return;
|
|
}
|
|
|
|
/* parse and maybe hash the bytes just read */
|
|
if (s->task != NOTIFICATION)
|
|
SHA256_Update(&s->ctx, buf, len);
|
|
if ((s->state & RRDP_STATE_PARSE_ERROR) == 0 &&
|
|
XML_Parse(p, buf, len, 0) != XML_STATUS_OK) {
|
|
warnx("%s: parse error at line %llu: %s", s->local,
|
|
(unsigned long long)XML_GetCurrentLineNumber(p),
|
|
XML_ErrorString(XML_GetErrorCode(p)));
|
|
s->state |= RRDP_STATE_PARSE_ERROR;
|
|
}
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
struct rrdp *s = NULL;
|
|
const char *e;
|
|
char *session_id = NULL;
|
|
char hash[SHA256_DIGEST_LENGTH];
|
|
long long serial = 0;
|
|
int c;
|
|
|
|
|
|
while ((c = getopt(argc, argv, "dH:N:nS:s")) != -1)
|
|
switch (c) {
|
|
case 'd':
|
|
if (s)
|
|
goto usage;
|
|
s = rrdp_new(0, "stdin", REGRESS_NOTIFY_URI,
|
|
session_id, serial, NULL);
|
|
s->dxml = new_delta_xml(s->parser,
|
|
&s->repository, s);
|
|
s->task = DELTA;
|
|
SHA256_Init(&s->ctx);
|
|
memcpy(s->hash, hash, sizeof(s->hash));
|
|
break;
|
|
case 'H':
|
|
if (hex_decode(optarg, hash, sizeof(hash)) == -1)
|
|
errx(1, "bad hash");
|
|
break;
|
|
case 'N':
|
|
serial = strtonum(optarg, LLONG_MIN, LLONG_MAX, &e);
|
|
if (e != NULL)
|
|
errx(1, "serial is %s: %s", e, optarg);
|
|
break;
|
|
case 'n':
|
|
if (s)
|
|
goto usage;
|
|
s = rrdp_new(0, "stdin", REGRESS_NOTIFY_URI,
|
|
session_id, serial, NULL);
|
|
s->nxml = new_notification_xml(s->parser,
|
|
&s->repository, &s->current, s->notifyuri);
|
|
s->task = NOTIFICATION;
|
|
break;
|
|
case 'S':
|
|
session_id = optarg;
|
|
break;
|
|
case 's':
|
|
if (s)
|
|
goto usage;
|
|
s = rrdp_new(0, "stdin", REGRESS_NOTIFY_URI,
|
|
session_id, serial, NULL);
|
|
s->sxml = new_snapshot_xml(s->parser,
|
|
&s->repository, s);
|
|
s->task = SNAPSHOT;
|
|
SHA256_Init(&s->ctx);
|
|
memcpy(s->hash, hash, sizeof(s->hash));
|
|
break;
|
|
default:
|
|
goto usage;
|
|
}
|
|
|
|
s->state = RRDP_STATE_PARSE;
|
|
|
|
while (!(s->state & RRDP_STATE_PARSE_DONE)) {
|
|
rrdp_data_handler(s);
|
|
}
|
|
|
|
if ((s->state & RRDP_STATE_PARSE_ERROR) == 0) {
|
|
printf("OK\n");
|
|
return 0;
|
|
} else {
|
|
return 1;
|
|
}
|
|
|
|
usage:
|
|
fprintf(stderr, "usage: %s [-S session_id] [-N serial] [-H hash] "
|
|
"-d | -n | -s\n", "test-rrdp");
|
|
exit(1);
|
|
}
|
|
|
|
time_t
|
|
get_current_time(void)
|
|
{
|
|
return time(NULL);
|
|
}
|