1*1a5078e9Sjob /* $OpenBSD: rrdp_delta.c,v 1.14 2024/05/30 09:54:59 job Exp $ */
28ecbadc1Sclaudio /*
38ecbadc1Sclaudio * Copyright (c) 2020 Nils Fisher <nils_fisher@hotmail.com>
48ecbadc1Sclaudio * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
58ecbadc1Sclaudio *
68ecbadc1Sclaudio * Permission to use, copy, modify, and distribute this software for any
78ecbadc1Sclaudio * purpose with or without fee is hereby granted, provided that the above
88ecbadc1Sclaudio * copyright notice and this permission notice appear in all copies.
98ecbadc1Sclaudio *
108ecbadc1Sclaudio * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
118ecbadc1Sclaudio * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
128ecbadc1Sclaudio * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
138ecbadc1Sclaudio * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
148ecbadc1Sclaudio * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
158ecbadc1Sclaudio * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
168ecbadc1Sclaudio * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
178ecbadc1Sclaudio */
188ecbadc1Sclaudio
198ecbadc1Sclaudio #include <err.h>
208ecbadc1Sclaudio #include <limits.h>
218ecbadc1Sclaudio #include <stdlib.h>
228ecbadc1Sclaudio #include <string.h>
238ecbadc1Sclaudio #include <stdio.h>
248ecbadc1Sclaudio #include <unistd.h>
258ecbadc1Sclaudio
268ecbadc1Sclaudio #include <expat.h>
278ecbadc1Sclaudio #include <openssl/sha.h>
288ecbadc1Sclaudio
298ecbadc1Sclaudio #include "extern.h"
308ecbadc1Sclaudio #include "rrdp.h"
318ecbadc1Sclaudio
328ecbadc1Sclaudio enum delta_scope {
338ecbadc1Sclaudio DELTA_SCOPE_NONE,
34c35859a1Stb DELTA_SCOPE_EMPTY_DELTA,
358ecbadc1Sclaudio DELTA_SCOPE_DELTA,
368ecbadc1Sclaudio DELTA_SCOPE_PUBLISH,
378ecbadc1Sclaudio DELTA_SCOPE_END
388ecbadc1Sclaudio };
398ecbadc1Sclaudio
408ecbadc1Sclaudio struct delta_xml {
418ecbadc1Sclaudio XML_Parser parser;
428ecbadc1Sclaudio struct rrdp_session *current;
438ecbadc1Sclaudio struct rrdp *rrdp;
448ecbadc1Sclaudio struct publish_xml *pxml;
458ecbadc1Sclaudio char *session_id;
468ecbadc1Sclaudio long long serial;
478ecbadc1Sclaudio int version;
488ecbadc1Sclaudio enum delta_scope scope;
498ecbadc1Sclaudio };
508ecbadc1Sclaudio
518ecbadc1Sclaudio static void
start_delta_elem(struct delta_xml * dxml,const char ** attr)528ecbadc1Sclaudio start_delta_elem(struct delta_xml *dxml, const char **attr)
538ecbadc1Sclaudio {
548ecbadc1Sclaudio XML_Parser p = dxml->parser;
558ecbadc1Sclaudio int has_xmlns = 0;
568ecbadc1Sclaudio int i;
578ecbadc1Sclaudio
588ecbadc1Sclaudio if (dxml->scope != DELTA_SCOPE_NONE)
598ecbadc1Sclaudio PARSE_FAIL(p,
608ecbadc1Sclaudio "parse failed - entered delta elem unexpectedely");
618ecbadc1Sclaudio for (i = 0; attr[i]; i += 2) {
628ecbadc1Sclaudio const char *errstr;
638e901cb8Sclaudio if (strcmp("xmlns", attr[i]) == 0 &&
648e901cb8Sclaudio strcmp(RRDP_XMLNS, attr[i + 1]) == 0) {
658ecbadc1Sclaudio has_xmlns = 1;
668ecbadc1Sclaudio continue;
678ecbadc1Sclaudio }
688ecbadc1Sclaudio if (strcmp("version", attr[i]) == 0) {
698ecbadc1Sclaudio dxml->version = strtonum(attr[i + 1],
708ecbadc1Sclaudio 1, MAX_VERSION, &errstr);
718ecbadc1Sclaudio if (errstr == NULL)
728ecbadc1Sclaudio continue;
738ecbadc1Sclaudio }
7445735addSclaudio if (strcmp("session_id", attr[i]) == 0 &&
7545735addSclaudio valid_uuid(attr[i + 1])) {
768ecbadc1Sclaudio dxml->session_id = xstrdup(attr[i + 1]);
778ecbadc1Sclaudio continue;
788ecbadc1Sclaudio }
798ecbadc1Sclaudio if (strcmp("serial", attr[i]) == 0) {
808ecbadc1Sclaudio dxml->serial = strtonum(attr[i + 1],
818ecbadc1Sclaudio 1, LLONG_MAX, &errstr);
828ecbadc1Sclaudio if (errstr == NULL)
838ecbadc1Sclaudio continue;
848ecbadc1Sclaudio }
858ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - non conforming "
86ac21c2b5Sclaudio "attribute '%s' found in delta elem", attr[i]);
878ecbadc1Sclaudio }
888ecbadc1Sclaudio if (!(has_xmlns && dxml->version && dxml->session_id && dxml->serial))
898ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - incomplete delta attributes");
908ecbadc1Sclaudio if (strcmp(dxml->current->session_id, dxml->session_id) != 0)
918ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - session_id mismatch");
928ecbadc1Sclaudio if (dxml->current->serial != dxml->serial)
938ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - serial mismatch");
948ecbadc1Sclaudio
95c35859a1Stb dxml->scope = DELTA_SCOPE_EMPTY_DELTA;
968ecbadc1Sclaudio }
978ecbadc1Sclaudio
988ecbadc1Sclaudio static void
end_delta_elem(struct delta_xml * dxml)998ecbadc1Sclaudio end_delta_elem(struct delta_xml *dxml)
1008ecbadc1Sclaudio {
1018ecbadc1Sclaudio XML_Parser p = dxml->parser;
1028ecbadc1Sclaudio
103c35859a1Stb if (dxml->scope == DELTA_SCOPE_EMPTY_DELTA)
104c35859a1Stb PARSE_FAIL(p, "parse failed - empty delta");
1058ecbadc1Sclaudio if (dxml->scope != DELTA_SCOPE_DELTA)
1068ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - exited delta "
1078ecbadc1Sclaudio "elem unexpectedely");
1088ecbadc1Sclaudio dxml->scope = DELTA_SCOPE_END;
1098ecbadc1Sclaudio }
1108ecbadc1Sclaudio
1118ecbadc1Sclaudio static void
start_publish_withdraw_elem(struct delta_xml * dxml,const char ** attr,int withdraw)1128ecbadc1Sclaudio start_publish_withdraw_elem(struct delta_xml *dxml, const char **attr,
1138ecbadc1Sclaudio int withdraw)
1148ecbadc1Sclaudio {
1158ecbadc1Sclaudio XML_Parser p = dxml->parser;
116c9585076Sclaudio char *uri = NULL, hash[SHA256_DIGEST_LENGTH];
1178ecbadc1Sclaudio int i, hasUri = 0, hasHash = 0;
1188ecbadc1Sclaudio enum publish_type pub = PUB_UPD;
1198ecbadc1Sclaudio
120c35859a1Stb if (dxml->scope != DELTA_SCOPE_EMPTY_DELTA &&
121c35859a1Stb dxml->scope != DELTA_SCOPE_DELTA)
1228ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - entered publish/withdraw "
1238ecbadc1Sclaudio "elem unexpectedely");
1248ecbadc1Sclaudio for (i = 0; attr[i]; i += 2) {
1258ecbadc1Sclaudio if (strcmp("uri", attr[i]) == 0 && hasUri++ == 0) {
1268ecbadc1Sclaudio if (valid_uri(attr[i + 1], strlen(attr[i + 1]),
1270610060dSjob RSYNC_PROTO)) {
1288ecbadc1Sclaudio uri = xstrdup(attr[i + 1]);
1298ecbadc1Sclaudio continue;
1308ecbadc1Sclaudio }
1318ecbadc1Sclaudio }
1328ecbadc1Sclaudio if (strcmp("hash", attr[i]) == 0 && hasHash++ == 0) {
1338ecbadc1Sclaudio if (hex_decode(attr[i + 1], hash, sizeof(hash)) == 0)
1348ecbadc1Sclaudio continue;
1358ecbadc1Sclaudio }
1368ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - non conforming "
137ac21c2b5Sclaudio "attribute '%s' found in publish/withdraw elem", attr[i]);
1388ecbadc1Sclaudio }
1398ecbadc1Sclaudio if (hasUri != 1)
1408ecbadc1Sclaudio PARSE_FAIL(p,
1418ecbadc1Sclaudio "parse failed - incomplete publish/withdraw attributes");
1428ecbadc1Sclaudio if (withdraw && hasHash != 1)
1438ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - incomplete withdraw attributes");
1448ecbadc1Sclaudio
1458ecbadc1Sclaudio if (withdraw)
1468ecbadc1Sclaudio pub = PUB_DEL;
1478ecbadc1Sclaudio else if (hasHash == 0)
1488ecbadc1Sclaudio pub = PUB_ADD;
1498ecbadc1Sclaudio dxml->pxml = new_publish_xml(pub, uri, hash,
1508ecbadc1Sclaudio hasHash ? sizeof(hash) : 0);
1518ecbadc1Sclaudio dxml->scope = DELTA_SCOPE_PUBLISH;
1528ecbadc1Sclaudio }
1538ecbadc1Sclaudio
1548ecbadc1Sclaudio static void
end_publish_withdraw_elem(struct delta_xml * dxml,int withdraw)1558ecbadc1Sclaudio end_publish_withdraw_elem(struct delta_xml *dxml, int withdraw)
1568ecbadc1Sclaudio {
1578ecbadc1Sclaudio XML_Parser p = dxml->parser;
1588ecbadc1Sclaudio
1598ecbadc1Sclaudio if (dxml->scope != DELTA_SCOPE_PUBLISH)
1608ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - exited publish/withdraw "
1618ecbadc1Sclaudio "elem unexpectedely");
1628ecbadc1Sclaudio
1638ecbadc1Sclaudio if (publish_done(dxml->rrdp, dxml->pxml) != 0)
1648ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - bad publish/withdraw elem");
1658ecbadc1Sclaudio dxml->pxml = NULL;
1668ecbadc1Sclaudio
1678ecbadc1Sclaudio dxml->scope = DELTA_SCOPE_DELTA;
1688ecbadc1Sclaudio }
1698ecbadc1Sclaudio
1708ecbadc1Sclaudio static void
delta_xml_elem_start(void * data,const char * el,const char ** attr)1718ecbadc1Sclaudio delta_xml_elem_start(void *data, const char *el, const char **attr)
1728ecbadc1Sclaudio {
1738ecbadc1Sclaudio struct delta_xml *dxml = data;
1748ecbadc1Sclaudio XML_Parser p = dxml->parser;
1758ecbadc1Sclaudio
1768ecbadc1Sclaudio /*
1778ecbadc1Sclaudio * Can only enter here once as we should have no ways to get back to
1788ecbadc1Sclaudio * NONE scope
1798ecbadc1Sclaudio */
1808ecbadc1Sclaudio if (strcmp("delta", el) == 0)
1818ecbadc1Sclaudio start_delta_elem(dxml, attr);
1828ecbadc1Sclaudio /*
1838ecbadc1Sclaudio * Will enter here multiple times, BUT never nested. will start
1848ecbadc1Sclaudio * collecting character data in that handler
1858ecbadc1Sclaudio * mem is cleared in end block, (TODO or on parse failure)
1868ecbadc1Sclaudio */
1878ecbadc1Sclaudio else if (strcmp("publish", el) == 0)
1888ecbadc1Sclaudio start_publish_withdraw_elem(dxml, attr, 0);
1898ecbadc1Sclaudio else if (strcmp("withdraw", el) == 0)
1908ecbadc1Sclaudio start_publish_withdraw_elem(dxml, attr, 1);
1918ecbadc1Sclaudio else
1928ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - unexpected elem exit found");
1938ecbadc1Sclaudio }
1948ecbadc1Sclaudio
1958ecbadc1Sclaudio static void
delta_xml_elem_end(void * data,const char * el)1968ecbadc1Sclaudio delta_xml_elem_end(void *data, const char *el)
1978ecbadc1Sclaudio {
1988ecbadc1Sclaudio struct delta_xml *dxml = data;
1998ecbadc1Sclaudio XML_Parser p = dxml->parser;
2008ecbadc1Sclaudio
2018ecbadc1Sclaudio if (strcmp("delta", el) == 0)
2028ecbadc1Sclaudio end_delta_elem(dxml);
2038ecbadc1Sclaudio /*
2048ecbadc1Sclaudio * TODO does this allow <publish></withdraw> or is that caught by basic
2058ecbadc1Sclaudio * xml parsing
2068ecbadc1Sclaudio */
2078ecbadc1Sclaudio else if (strcmp("publish", el) == 0)
2088ecbadc1Sclaudio end_publish_withdraw_elem(dxml, 0);
2098ecbadc1Sclaudio else if (strcmp("withdraw", el) == 0)
2108ecbadc1Sclaudio end_publish_withdraw_elem(dxml, 1);
2118ecbadc1Sclaudio else
2128ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - unexpected elem exit found");
2138ecbadc1Sclaudio }
2148ecbadc1Sclaudio
2158ecbadc1Sclaudio static void
delta_content_handler(void * data,const char * content,int length)2168ecbadc1Sclaudio delta_content_handler(void *data, const char *content, int length)
2178ecbadc1Sclaudio {
2188ecbadc1Sclaudio struct delta_xml *dxml = data;
2191aea4e0eSclaudio XML_Parser p = dxml->parser;
2208ecbadc1Sclaudio
2218ecbadc1Sclaudio if (dxml->scope == DELTA_SCOPE_PUBLISH)
2221aea4e0eSclaudio if (publish_add_content(dxml->pxml, content, length) == -1)
223*1a5078e9Sjob PARSE_FAIL(p, "parse failed, delta element for %s too "
224*1a5078e9Sjob "big", dxml->pxml->uri);
2258ecbadc1Sclaudio }
2268ecbadc1Sclaudio
2278487774dSclaudio static void
delta_doctype_handler(void * data,const char * doctypeName,const char * sysid,const char * pubid,int subset)2288487774dSclaudio delta_doctype_handler(void *data, const char *doctypeName,
2298487774dSclaudio const char *sysid, const char *pubid, int subset)
2308487774dSclaudio {
2318487774dSclaudio struct delta_xml *dxml = data;
2328487774dSclaudio XML_Parser p = dxml->parser;
2338487774dSclaudio
2348487774dSclaudio PARSE_FAIL(p, "parse failed - DOCTYPE not allowed");
2358487774dSclaudio }
2368487774dSclaudio
2378ecbadc1Sclaudio struct delta_xml *
new_delta_xml(XML_Parser p,struct rrdp_session * rs,struct rrdp * r)2388ecbadc1Sclaudio new_delta_xml(XML_Parser p, struct rrdp_session *rs, struct rrdp *r)
2398ecbadc1Sclaudio {
2408ecbadc1Sclaudio struct delta_xml *dxml;
2418ecbadc1Sclaudio
2428ecbadc1Sclaudio if ((dxml = calloc(1, sizeof(*dxml))) == NULL)
2438ecbadc1Sclaudio err(1, "%s", __func__);
2448ecbadc1Sclaudio dxml->parser = p;
2458ecbadc1Sclaudio dxml->current = rs;
2468ecbadc1Sclaudio dxml->rrdp = r;
2478ecbadc1Sclaudio
2488ecbadc1Sclaudio if (XML_ParserReset(dxml->parser, "US-ASCII") != XML_TRUE)
2498ecbadc1Sclaudio errx(1, "%s: XML_ParserReset failed", __func__);
2508ecbadc1Sclaudio
2518ecbadc1Sclaudio XML_SetElementHandler(dxml->parser, delta_xml_elem_start,
2528ecbadc1Sclaudio delta_xml_elem_end);
2538ecbadc1Sclaudio XML_SetCharacterDataHandler(dxml->parser, delta_content_handler);
2548ecbadc1Sclaudio XML_SetUserData(dxml->parser, dxml);
2558487774dSclaudio XML_SetDoctypeDeclHandler(dxml->parser, delta_doctype_handler, NULL);
2568ecbadc1Sclaudio
2578ecbadc1Sclaudio return dxml;
2588ecbadc1Sclaudio }
2598ecbadc1Sclaudio
2608ecbadc1Sclaudio void
free_delta_xml(struct delta_xml * dxml)2618ecbadc1Sclaudio free_delta_xml(struct delta_xml *dxml)
2628ecbadc1Sclaudio {
2638ecbadc1Sclaudio if (dxml == NULL)
2648ecbadc1Sclaudio return;
2658ecbadc1Sclaudio
2668ecbadc1Sclaudio free(dxml->session_id);
2678ecbadc1Sclaudio free_publish_xml(dxml->pxml);
2688ecbadc1Sclaudio free(dxml);
2698ecbadc1Sclaudio }
2708ecbadc1Sclaudio
27158d5f018Stb /* Used in regress. */
2728ecbadc1Sclaudio void
log_delta_xml(struct delta_xml * dxml)2738ecbadc1Sclaudio log_delta_xml(struct delta_xml *dxml)
2748ecbadc1Sclaudio {
2758ecbadc1Sclaudio logx("version: %d", dxml->version);
2768ecbadc1Sclaudio logx("session_id: %s serial: %lld", dxml->session_id, dxml->serial);
2778ecbadc1Sclaudio }
278