1*1a5078e9Sjob /* $OpenBSD: rrdp_snapshot.c,v 1.10 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
288ecbadc1Sclaudio #include "extern.h"
298ecbadc1Sclaudio #include "rrdp.h"
308ecbadc1Sclaudio
318ecbadc1Sclaudio enum snapshot_scope {
328ecbadc1Sclaudio SNAPSHOT_SCOPE_NONE,
338ecbadc1Sclaudio SNAPSHOT_SCOPE_SNAPSHOT,
348ecbadc1Sclaudio SNAPSHOT_SCOPE_PUBLISH,
358ecbadc1Sclaudio SNAPSHOT_SCOPE_END
368ecbadc1Sclaudio };
378ecbadc1Sclaudio
388ecbadc1Sclaudio struct snapshot_xml {
398ecbadc1Sclaudio XML_Parser parser;
408ecbadc1Sclaudio struct rrdp_session *current;
418ecbadc1Sclaudio struct rrdp *rrdp;
428ecbadc1Sclaudio struct publish_xml *pxml;
438ecbadc1Sclaudio char *session_id;
448ecbadc1Sclaudio long long serial;
458ecbadc1Sclaudio int version;
468ecbadc1Sclaudio enum snapshot_scope scope;
478ecbadc1Sclaudio };
488ecbadc1Sclaudio
498ecbadc1Sclaudio static void
start_snapshot_elem(struct snapshot_xml * sxml,const char ** attr)508ecbadc1Sclaudio start_snapshot_elem(struct snapshot_xml *sxml, const char **attr)
518ecbadc1Sclaudio {
528ecbadc1Sclaudio XML_Parser p = sxml->parser;
538ecbadc1Sclaudio int has_xmlns = 0;
548ecbadc1Sclaudio int i;
558ecbadc1Sclaudio
568ecbadc1Sclaudio if (sxml->scope != SNAPSHOT_SCOPE_NONE)
578ecbadc1Sclaudio PARSE_FAIL(p,
588ecbadc1Sclaudio "parse failed - entered snapshot elem unexpectedely");
598ecbadc1Sclaudio for (i = 0; attr[i]; i += 2) {
608ecbadc1Sclaudio const char *errstr;
618e901cb8Sclaudio if (strcmp("xmlns", attr[i]) == 0 &&
628e901cb8Sclaudio strcmp(RRDP_XMLNS, attr[i + 1]) == 0) {
638ecbadc1Sclaudio has_xmlns = 1;
648ecbadc1Sclaudio continue;
658ecbadc1Sclaudio }
668ecbadc1Sclaudio if (strcmp("version", attr[i]) == 0) {
678ecbadc1Sclaudio sxml->version = strtonum(attr[i + 1],
688ecbadc1Sclaudio 1, MAX_VERSION, &errstr);
698ecbadc1Sclaudio if (errstr == NULL)
708ecbadc1Sclaudio continue;
718ecbadc1Sclaudio }
7245735addSclaudio if (strcmp("session_id", attr[i]) == 0 &&
7345735addSclaudio valid_uuid(attr[i + 1])) {
748ecbadc1Sclaudio sxml->session_id = xstrdup(attr[i + 1]);
758ecbadc1Sclaudio continue;
768ecbadc1Sclaudio }
778ecbadc1Sclaudio if (strcmp("serial", attr[i]) == 0) {
788ecbadc1Sclaudio sxml->serial = strtonum(attr[i + 1],
798ecbadc1Sclaudio 1, LLONG_MAX, &errstr);
808ecbadc1Sclaudio if (errstr == NULL)
818ecbadc1Sclaudio continue;
828ecbadc1Sclaudio }
838ecbadc1Sclaudio PARSE_FAIL(p,
848ecbadc1Sclaudio "parse failed - non conforming "
85ac21c2b5Sclaudio "attribute '%s' found in snapshot elem", attr[i]);
868ecbadc1Sclaudio }
878ecbadc1Sclaudio if (!(has_xmlns && sxml->version && sxml->session_id && sxml->serial))
888ecbadc1Sclaudio PARSE_FAIL(p,
898ecbadc1Sclaudio "parse failed - incomplete snapshot attributes");
908ecbadc1Sclaudio if (strcmp(sxml->current->session_id, sxml->session_id) != 0)
918ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - session_id mismatch");
928ecbadc1Sclaudio if (sxml->current->serial != sxml->serial)
938ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - serial mismatch");
948ecbadc1Sclaudio
958ecbadc1Sclaudio sxml->scope = SNAPSHOT_SCOPE_SNAPSHOT;
968ecbadc1Sclaudio }
978ecbadc1Sclaudio
988ecbadc1Sclaudio static void
end_snapshot_elem(struct snapshot_xml * sxml)998ecbadc1Sclaudio end_snapshot_elem(struct snapshot_xml *sxml)
1008ecbadc1Sclaudio {
1018ecbadc1Sclaudio XML_Parser p = sxml->parser;
1028ecbadc1Sclaudio
1038ecbadc1Sclaudio if (sxml->scope != SNAPSHOT_SCOPE_SNAPSHOT)
1048ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - exited snapshot "
1058ecbadc1Sclaudio "elem unexpectedely");
1068ecbadc1Sclaudio sxml->scope = SNAPSHOT_SCOPE_END;
1078ecbadc1Sclaudio }
1088ecbadc1Sclaudio
1098ecbadc1Sclaudio static void
start_publish_elem(struct snapshot_xml * sxml,const char ** attr)1108ecbadc1Sclaudio start_publish_elem(struct snapshot_xml *sxml, const char **attr)
1118ecbadc1Sclaudio {
1128ecbadc1Sclaudio XML_Parser p = sxml->parser;
1138ecbadc1Sclaudio char *uri = NULL;
1148ecbadc1Sclaudio int i, hasUri = 0;
1158ecbadc1Sclaudio
1168ecbadc1Sclaudio if (sxml->scope != SNAPSHOT_SCOPE_SNAPSHOT)
1178ecbadc1Sclaudio PARSE_FAIL(p,
1188ecbadc1Sclaudio "parse failed - entered publish elem unexpectedely");
1198ecbadc1Sclaudio for (i = 0; attr[i]; i += 2) {
1208ecbadc1Sclaudio if (strcmp("uri", attr[i]) == 0 && hasUri++ == 0) {
1218ecbadc1Sclaudio if (valid_uri(attr[i + 1], strlen(attr[i + 1]),
1220610060dSjob RSYNC_PROTO)) {
1238ecbadc1Sclaudio uri = xstrdup(attr[i + 1]);
1248ecbadc1Sclaudio continue;
1258ecbadc1Sclaudio }
1268ecbadc1Sclaudio }
1278ecbadc1Sclaudio /*
1288ecbadc1Sclaudio * XXX it seems people can not write proper XML, ignore
1298ecbadc1Sclaudio * bogus xmlns attribute on publish elements.
1308ecbadc1Sclaudio */
1318ecbadc1Sclaudio if (strcmp("xmlns", attr[i]) == 0)
1328ecbadc1Sclaudio continue;
1338ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - non conforming"
1348ecbadc1Sclaudio " attribute '%s' found in publish elem", attr[i]);
1358ecbadc1Sclaudio }
1368ecbadc1Sclaudio if (hasUri != 1)
1378ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - incomplete publish attributes");
1388ecbadc1Sclaudio sxml->pxml = new_publish_xml(PUB_ADD, uri, NULL, 0);
1398ecbadc1Sclaudio sxml->scope = SNAPSHOT_SCOPE_PUBLISH;
1408ecbadc1Sclaudio }
1418ecbadc1Sclaudio
1428ecbadc1Sclaudio static void
end_publish_elem(struct snapshot_xml * sxml)1438ecbadc1Sclaudio end_publish_elem(struct snapshot_xml *sxml)
1448ecbadc1Sclaudio {
1458ecbadc1Sclaudio XML_Parser p = sxml->parser;
1468ecbadc1Sclaudio
1478ecbadc1Sclaudio if (sxml->scope != SNAPSHOT_SCOPE_PUBLISH)
1488ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - exited publish "
1498ecbadc1Sclaudio "elem unexpectedely");
1508ecbadc1Sclaudio
1518ecbadc1Sclaudio if (publish_done(sxml->rrdp, sxml->pxml) != 0)
1528ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - bad publish elem");
1538ecbadc1Sclaudio sxml->pxml = NULL;
1548ecbadc1Sclaudio
1558ecbadc1Sclaudio sxml->scope = SNAPSHOT_SCOPE_SNAPSHOT;
1568ecbadc1Sclaudio }
1578ecbadc1Sclaudio
1588ecbadc1Sclaudio static void
snapshot_xml_elem_start(void * data,const char * el,const char ** attr)1598ecbadc1Sclaudio snapshot_xml_elem_start(void *data, const char *el, const char **attr)
1608ecbadc1Sclaudio {
1618ecbadc1Sclaudio struct snapshot_xml *sxml = data;
1628ecbadc1Sclaudio XML_Parser p = sxml->parser;
1638ecbadc1Sclaudio
1648ecbadc1Sclaudio /*
1658ecbadc1Sclaudio * Can only enter here once as we should have no ways to get back to
1668ecbadc1Sclaudio * NONE scope
1678ecbadc1Sclaudio */
1688ecbadc1Sclaudio if (strcmp("snapshot", el) == 0)
1698ecbadc1Sclaudio start_snapshot_elem(sxml, attr);
1708ecbadc1Sclaudio /*
1718ecbadc1Sclaudio * Will enter here multiple times, BUT never nested. will start
1728ecbadc1Sclaudio * collecting character data in that handler mem is cleared in end
1738ecbadc1Sclaudio * block, (TODO or on parse failure)
1748ecbadc1Sclaudio */
1758ecbadc1Sclaudio else if (strcmp("publish", el) == 0)
1768ecbadc1Sclaudio start_publish_elem(sxml, attr);
1778ecbadc1Sclaudio else
1788ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - unexpected elem exit found");
1798ecbadc1Sclaudio }
1808ecbadc1Sclaudio
1818ecbadc1Sclaudio static void
snapshot_xml_elem_end(void * data,const char * el)1828ecbadc1Sclaudio snapshot_xml_elem_end(void *data, const char *el)
1838ecbadc1Sclaudio {
1848ecbadc1Sclaudio struct snapshot_xml *sxml = data;
1858ecbadc1Sclaudio XML_Parser p = sxml->parser;
1868ecbadc1Sclaudio
1878ecbadc1Sclaudio if (strcmp("snapshot", el) == 0)
1888ecbadc1Sclaudio end_snapshot_elem(sxml);
1898ecbadc1Sclaudio else if (strcmp("publish", el) == 0)
1908ecbadc1Sclaudio end_publish_elem(sxml);
1918ecbadc1Sclaudio else
1928ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - unexpected elem exit found");
1938ecbadc1Sclaudio }
1948ecbadc1Sclaudio
1958ecbadc1Sclaudio static void
snapshot_content_handler(void * data,const char * content,int length)1968ecbadc1Sclaudio snapshot_content_handler(void *data, const char *content, int length)
1978ecbadc1Sclaudio {
1988ecbadc1Sclaudio struct snapshot_xml *sxml = data;
1991aea4e0eSclaudio XML_Parser p = sxml->parser;
2008ecbadc1Sclaudio
2018ecbadc1Sclaudio if (sxml->scope == SNAPSHOT_SCOPE_PUBLISH)
2021aea4e0eSclaudio if (publish_add_content(sxml->pxml, content, length) == -1)
203*1a5078e9Sjob PARSE_FAIL(p, "parse failed, snapshot element for %s "
204*1a5078e9Sjob "too big", sxml->pxml->uri);
2058ecbadc1Sclaudio }
2068ecbadc1Sclaudio
2078487774dSclaudio static void
snapshot_doctype_handler(void * data,const char * doctypeName,const char * sysid,const char * pubid,int subset)2088487774dSclaudio snapshot_doctype_handler(void *data, const char *doctypeName,
2098487774dSclaudio const char *sysid, const char *pubid, int subset)
2108487774dSclaudio {
2118487774dSclaudio struct snapshot_xml *sxml = data;
2128487774dSclaudio XML_Parser p = sxml->parser;
2138487774dSclaudio
2148487774dSclaudio PARSE_FAIL(p, "parse failed - DOCTYPE not allowed");
2158487774dSclaudio }
2168487774dSclaudio
2178ecbadc1Sclaudio struct snapshot_xml *
new_snapshot_xml(XML_Parser p,struct rrdp_session * rs,struct rrdp * r)2188ecbadc1Sclaudio new_snapshot_xml(XML_Parser p, struct rrdp_session *rs, struct rrdp *r)
2198ecbadc1Sclaudio {
2208ecbadc1Sclaudio struct snapshot_xml *sxml;
2218ecbadc1Sclaudio
2228ecbadc1Sclaudio if ((sxml = calloc(1, sizeof(*sxml))) == NULL)
2238ecbadc1Sclaudio err(1, "%s", __func__);
2248ecbadc1Sclaudio sxml->parser = p;
2258ecbadc1Sclaudio sxml->current = rs;
2268ecbadc1Sclaudio sxml->rrdp = r;
2278ecbadc1Sclaudio
2288ecbadc1Sclaudio if (XML_ParserReset(sxml->parser, "US-ASCII") != XML_TRUE)
2298ecbadc1Sclaudio errx(1, "%s: XML_ParserReset failed", __func__);
2308ecbadc1Sclaudio
2318ecbadc1Sclaudio XML_SetElementHandler(sxml->parser, snapshot_xml_elem_start,
2328ecbadc1Sclaudio snapshot_xml_elem_end);
2338ecbadc1Sclaudio XML_SetCharacterDataHandler(sxml->parser, snapshot_content_handler);
2348ecbadc1Sclaudio XML_SetUserData(sxml->parser, sxml);
2358487774dSclaudio XML_SetDoctypeDeclHandler(sxml->parser, snapshot_doctype_handler,
2368487774dSclaudio NULL);
2378ecbadc1Sclaudio
2388ecbadc1Sclaudio return sxml;
2398ecbadc1Sclaudio }
2408ecbadc1Sclaudio
2418ecbadc1Sclaudio void
free_snapshot_xml(struct snapshot_xml * sxml)2428ecbadc1Sclaudio free_snapshot_xml(struct snapshot_xml *sxml)
2438ecbadc1Sclaudio {
2448ecbadc1Sclaudio if (sxml == NULL)
2458ecbadc1Sclaudio return;
2468ecbadc1Sclaudio
2478ecbadc1Sclaudio free(sxml->session_id);
2488ecbadc1Sclaudio free_publish_xml(sxml->pxml);
2498ecbadc1Sclaudio free(sxml);
2508ecbadc1Sclaudio }
2518ecbadc1Sclaudio
25258d5f018Stb /* Used in regress. */
2538ecbadc1Sclaudio void
log_snapshot_xml(struct snapshot_xml * sxml)2548ecbadc1Sclaudio log_snapshot_xml(struct snapshot_xml *sxml)
2558ecbadc1Sclaudio {
2568ecbadc1Sclaudio logx("scope: %d", sxml->scope);
2578ecbadc1Sclaudio logx("version: %d", sxml->version);
2588ecbadc1Sclaudio logx("session_id: %s serial: %lld", sxml->session_id, sxml->serial);
2598ecbadc1Sclaudio }
260