1*222e275fSjob /* $OpenBSD: rrdp_notification.c,v 1.21 2024/04/12 11:50:29 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 <sys/stat.h>
208ecbadc1Sclaudio
218ecbadc1Sclaudio #include <assert.h>
228ecbadc1Sclaudio #include <err.h>
238ecbadc1Sclaudio #include <errno.h>
248ecbadc1Sclaudio #include <limits.h>
258ecbadc1Sclaudio #include <fcntl.h>
268ecbadc1Sclaudio #include <string.h>
278ecbadc1Sclaudio #include <unistd.h>
288ecbadc1Sclaudio
298ecbadc1Sclaudio #include <expat.h>
308ecbadc1Sclaudio #include <openssl/sha.h>
318ecbadc1Sclaudio
328ecbadc1Sclaudio #include "extern.h"
338ecbadc1Sclaudio #include "rrdp.h"
348ecbadc1Sclaudio
358ecbadc1Sclaudio enum notification_scope {
368ecbadc1Sclaudio NOTIFICATION_SCOPE_START,
378ecbadc1Sclaudio NOTIFICATION_SCOPE_NOTIFICATION,
388ecbadc1Sclaudio NOTIFICATION_SCOPE_SNAPSHOT,
398ecbadc1Sclaudio NOTIFICATION_SCOPE_NOTIFICATION_POST_SNAPSHOT,
408ecbadc1Sclaudio NOTIFICATION_SCOPE_DELTA,
418ecbadc1Sclaudio NOTIFICATION_SCOPE_END
428ecbadc1Sclaudio };
438ecbadc1Sclaudio
448ecbadc1Sclaudio struct delta_item {
458ecbadc1Sclaudio char *uri;
468ecbadc1Sclaudio char hash[SHA256_DIGEST_LENGTH];
478ecbadc1Sclaudio long long serial;
488ecbadc1Sclaudio TAILQ_ENTRY(delta_item) q;
498ecbadc1Sclaudio };
508ecbadc1Sclaudio
518ecbadc1Sclaudio TAILQ_HEAD(delta_q, delta_item);
528ecbadc1Sclaudio
538ecbadc1Sclaudio struct notification_xml {
548ecbadc1Sclaudio XML_Parser parser;
558ecbadc1Sclaudio struct rrdp_session *repository;
568ecbadc1Sclaudio struct rrdp_session *current;
5793d9375cSclaudio const char *notifyuri;
588ecbadc1Sclaudio char *session_id;
598ecbadc1Sclaudio char *snapshot_uri;
608ecbadc1Sclaudio char snapshot_hash[SHA256_DIGEST_LENGTH];
618ecbadc1Sclaudio struct delta_q delta_q;
628ecbadc1Sclaudio long long serial;
63b268327aSclaudio long long min_serial;
648ecbadc1Sclaudio int version;
658ecbadc1Sclaudio enum notification_scope scope;
668ecbadc1Sclaudio };
678ecbadc1Sclaudio
68a6271a62Stb static void free_delta(struct delta_item *);
69a6271a62Stb
708ecbadc1Sclaudio static int
add_delta(struct notification_xml * nxml,const char * uri,const char hash[SHA256_DIGEST_LENGTH],long long serial)718ecbadc1Sclaudio add_delta(struct notification_xml *nxml, const char *uri,
728ecbadc1Sclaudio const char hash[SHA256_DIGEST_LENGTH], long long serial)
738ecbadc1Sclaudio {
748ecbadc1Sclaudio struct delta_item *d, *n;
758ecbadc1Sclaudio
768ecbadc1Sclaudio if ((d = calloc(1, sizeof(struct delta_item))) == NULL)
778ecbadc1Sclaudio err(1, "%s - calloc", __func__);
788ecbadc1Sclaudio
798ecbadc1Sclaudio d->serial = serial;
808ecbadc1Sclaudio d->uri = xstrdup(uri);
818ecbadc1Sclaudio memcpy(d->hash, hash, sizeof(d->hash));
828ecbadc1Sclaudio
838ecbadc1Sclaudio /* optimise for a sorted input */
848ecbadc1Sclaudio n = TAILQ_LAST(&nxml->delta_q, delta_q);
858ecbadc1Sclaudio if (n == NULL)
868ecbadc1Sclaudio TAILQ_INSERT_HEAD(&nxml->delta_q, d, q);
878ecbadc1Sclaudio else if (n->serial < serial)
888ecbadc1Sclaudio TAILQ_INSERT_TAIL(&nxml->delta_q, d, q);
898ecbadc1Sclaudio else
908ecbadc1Sclaudio TAILQ_FOREACH(n, &nxml->delta_q, q) {
918ecbadc1Sclaudio if (n->serial == serial) {
928ecbadc1Sclaudio warnx("duplicate delta serial %lld ", serial);
93a6271a62Stb free_delta(d);
948ecbadc1Sclaudio return 0;
958ecbadc1Sclaudio }
968ecbadc1Sclaudio if (n->serial > serial) {
978ecbadc1Sclaudio TAILQ_INSERT_BEFORE(n, d, q);
988ecbadc1Sclaudio break;
998ecbadc1Sclaudio }
1008ecbadc1Sclaudio }
1018ecbadc1Sclaudio
1028ecbadc1Sclaudio return 1;
1038ecbadc1Sclaudio }
1048ecbadc1Sclaudio
105b268327aSclaudio /* check that there are no holes in the list */
106b268327aSclaudio static int
check_delta(struct notification_xml * nxml)107b268327aSclaudio check_delta(struct notification_xml *nxml)
108b268327aSclaudio {
109b268327aSclaudio struct delta_item *d;
110b268327aSclaudio long long serial = 0;
111b268327aSclaudio
112b268327aSclaudio TAILQ_FOREACH(d, &nxml->delta_q, q) {
113b268327aSclaudio if (serial != 0 && serial + 1 != d->serial)
114b268327aSclaudio return 0;
115b268327aSclaudio serial = d->serial;
116b268327aSclaudio }
117b268327aSclaudio return 1;
118b268327aSclaudio }
119b268327aSclaudio
1208ecbadc1Sclaudio static void
free_delta(struct delta_item * d)1218ecbadc1Sclaudio free_delta(struct delta_item *d)
1228ecbadc1Sclaudio {
1238ecbadc1Sclaudio free(d->uri);
1248ecbadc1Sclaudio free(d);
1258ecbadc1Sclaudio }
1268ecbadc1Sclaudio
127b268327aSclaudio /*
128b268327aSclaudio * Parse a delta serial and hash line at idx from the rrdp session state.
129b268327aSclaudio * Return the serial or 0 on error. If hash is non-NULL, it is set to the
130b268327aSclaudio * start of the hash string on success.
131b268327aSclaudio */
132b268327aSclaudio static long long
delta_parse(struct rrdp_session * s,size_t idx,char ** hash)133b268327aSclaudio delta_parse(struct rrdp_session *s, size_t idx, char **hash)
134b268327aSclaudio {
135b268327aSclaudio long long serial;
136b268327aSclaudio char *line, *ep;
137b268327aSclaudio
138b268327aSclaudio if (hash != NULL)
139b268327aSclaudio *hash = NULL;
140b268327aSclaudio if (idx < 0 || idx >= sizeof(s->deltas) / sizeof(s->deltas[0]))
141b268327aSclaudio return 0;
142b268327aSclaudio if ((line = s->deltas[idx]) == NULL)
143b268327aSclaudio return 0;
144b268327aSclaudio
145b268327aSclaudio errno = 0;
146b268327aSclaudio serial = strtoll(line, &ep, 10);
147b268327aSclaudio if (line[0] == '\0' || *ep != ' ')
148b268327aSclaudio return 0;
149b268327aSclaudio if (serial <= 0 || (errno == ERANGE && serial == LLONG_MAX))
150b268327aSclaudio return 0;
151b268327aSclaudio
152b268327aSclaudio if (hash != NULL)
153b268327aSclaudio *hash = ep + 1;
154b268327aSclaudio return serial;
155b268327aSclaudio }
156b268327aSclaudio
1578ecbadc1Sclaudio static void
start_notification_elem(struct notification_xml * nxml,const char ** attr)1588ecbadc1Sclaudio start_notification_elem(struct notification_xml *nxml, const char **attr)
1598ecbadc1Sclaudio {
1608ecbadc1Sclaudio XML_Parser p = nxml->parser;
1618ecbadc1Sclaudio int has_xmlns = 0;
1628ecbadc1Sclaudio size_t i;
1638ecbadc1Sclaudio
1648ecbadc1Sclaudio if (nxml->scope != NOTIFICATION_SCOPE_START)
1658ecbadc1Sclaudio PARSE_FAIL(p,
1668ecbadc1Sclaudio "parse failed - entered notification elem unexpectedely");
1678ecbadc1Sclaudio for (i = 0; attr[i]; i += 2) {
1688ecbadc1Sclaudio const char *errstr;
1698e901cb8Sclaudio if (strcmp("xmlns", attr[i]) == 0 &&
1708e901cb8Sclaudio strcmp(RRDP_XMLNS, attr[i + 1]) == 0) {
1718ecbadc1Sclaudio has_xmlns = 1;
1728ecbadc1Sclaudio continue;
1738ecbadc1Sclaudio }
17445735addSclaudio if (strcmp("session_id", attr[i]) == 0 &&
17545735addSclaudio valid_uuid(attr[i + 1])) {
1768ecbadc1Sclaudio nxml->session_id = xstrdup(attr[i + 1]);
1778ecbadc1Sclaudio continue;
1788ecbadc1Sclaudio }
1798ecbadc1Sclaudio if (strcmp("version", attr[i]) == 0) {
1808ecbadc1Sclaudio nxml->version = strtonum(attr[i + 1],
1818ecbadc1Sclaudio 1, MAX_VERSION, &errstr);
1828ecbadc1Sclaudio if (errstr == NULL)
1838ecbadc1Sclaudio continue;
1848ecbadc1Sclaudio }
1858ecbadc1Sclaudio if (strcmp("serial", attr[i]) == 0) {
1868ecbadc1Sclaudio nxml->serial = strtonum(attr[i + 1],
1878ecbadc1Sclaudio 1, LLONG_MAX, &errstr);
1888ecbadc1Sclaudio if (errstr == NULL)
1898ecbadc1Sclaudio continue;
1908ecbadc1Sclaudio }
1918ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - non conforming "
192da205960Sclaudio "attribute '%s' found in notification elem", attr[i]);
1938ecbadc1Sclaudio }
1948ecbadc1Sclaudio if (!(has_xmlns && nxml->version && nxml->session_id && nxml->serial))
1958ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - incomplete "
1968ecbadc1Sclaudio "notification attributes");
1978ecbadc1Sclaudio
198b268327aSclaudio /* Limit deltas to the ones which matter for us. */
199b268327aSclaudio if (nxml->min_serial == 0 && nxml->serial > MAX_RRDP_DELTAS)
200b268327aSclaudio nxml->min_serial = nxml->serial - MAX_RRDP_DELTAS;
201b268327aSclaudio
2028ecbadc1Sclaudio nxml->scope = NOTIFICATION_SCOPE_NOTIFICATION;
2038ecbadc1Sclaudio }
2048ecbadc1Sclaudio
2058ecbadc1Sclaudio static void
end_notification_elem(struct notification_xml * nxml)2068ecbadc1Sclaudio end_notification_elem(struct notification_xml *nxml)
2078ecbadc1Sclaudio {
2088ecbadc1Sclaudio XML_Parser p = nxml->parser;
2098ecbadc1Sclaudio
2108ecbadc1Sclaudio if (nxml->scope != NOTIFICATION_SCOPE_NOTIFICATION_POST_SNAPSHOT)
2118ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - exited notification "
2128ecbadc1Sclaudio "elem unexpectedely");
2138ecbadc1Sclaudio nxml->scope = NOTIFICATION_SCOPE_END;
214b268327aSclaudio
215b268327aSclaudio if (!check_delta(nxml))
216b268327aSclaudio PARSE_FAIL(p, "parse failed - delta list has holes");
2178ecbadc1Sclaudio }
2188ecbadc1Sclaudio
2198ecbadc1Sclaudio static void
start_snapshot_elem(struct notification_xml * nxml,const char ** attr)2208ecbadc1Sclaudio start_snapshot_elem(struct notification_xml *nxml, const char **attr)
2218ecbadc1Sclaudio {
2228ecbadc1Sclaudio XML_Parser p = nxml->parser;
2238ecbadc1Sclaudio int i, hasUri = 0, hasHash = 0;
2248ecbadc1Sclaudio
2258ecbadc1Sclaudio if (nxml->scope != NOTIFICATION_SCOPE_NOTIFICATION)
2268ecbadc1Sclaudio PARSE_FAIL(p,
2278ecbadc1Sclaudio "parse failed - entered snapshot elem unexpectedely");
2288ecbadc1Sclaudio for (i = 0; attr[i]; i += 2) {
2298ecbadc1Sclaudio if (strcmp("uri", attr[i]) == 0 && hasUri++ == 0) {
2308ecbadc1Sclaudio if (valid_uri(attr[i + 1], strlen(attr[i + 1]),
2310610060dSjob HTTPS_PROTO) &&
23293d9375cSclaudio valid_origin(attr[i + 1], nxml->notifyuri)) {
2338ecbadc1Sclaudio nxml->snapshot_uri = xstrdup(attr[i + 1]);
2348ecbadc1Sclaudio continue;
2358ecbadc1Sclaudio }
2368ecbadc1Sclaudio }
2378ecbadc1Sclaudio if (strcmp("hash", attr[i]) == 0 && hasHash++ == 0) {
2388ecbadc1Sclaudio if (hex_decode(attr[i + 1], nxml->snapshot_hash,
2398ecbadc1Sclaudio sizeof(nxml->snapshot_hash)) == 0)
2408ecbadc1Sclaudio continue;
2418ecbadc1Sclaudio }
2428ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - non conforming "
243da205960Sclaudio "attribute '%s' found in snapshot elem", attr[i]);
2448ecbadc1Sclaudio }
2458ecbadc1Sclaudio if (hasUri != 1 || hasHash != 1)
2468ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - incomplete snapshot attributes");
2478ecbadc1Sclaudio
2488ecbadc1Sclaudio nxml->scope = NOTIFICATION_SCOPE_SNAPSHOT;
2498ecbadc1Sclaudio }
2508ecbadc1Sclaudio
2518ecbadc1Sclaudio static void
end_snapshot_elem(struct notification_xml * nxml)2528ecbadc1Sclaudio end_snapshot_elem(struct notification_xml *nxml)
2538ecbadc1Sclaudio {
2548ecbadc1Sclaudio XML_Parser p = nxml->parser;
2558ecbadc1Sclaudio
2568ecbadc1Sclaudio if (nxml->scope != NOTIFICATION_SCOPE_SNAPSHOT)
2578ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - exited snapshot "
2588ecbadc1Sclaudio "elem unexpectedely");
2598ecbadc1Sclaudio nxml->scope = NOTIFICATION_SCOPE_NOTIFICATION_POST_SNAPSHOT;
2608ecbadc1Sclaudio }
2618ecbadc1Sclaudio
2628ecbadc1Sclaudio static void
start_delta_elem(struct notification_xml * nxml,const char ** attr)2638ecbadc1Sclaudio start_delta_elem(struct notification_xml *nxml, const char **attr)
2648ecbadc1Sclaudio {
2658ecbadc1Sclaudio XML_Parser p = nxml->parser;
2668ecbadc1Sclaudio int i, hasUri = 0, hasHash = 0;
2678ecbadc1Sclaudio const char *delta_uri = NULL;
2688ecbadc1Sclaudio char delta_hash[SHA256_DIGEST_LENGTH];
2698ecbadc1Sclaudio long long delta_serial = 0;
2708ecbadc1Sclaudio
2718ecbadc1Sclaudio if (nxml->scope != NOTIFICATION_SCOPE_NOTIFICATION_POST_SNAPSHOT)
2728ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - entered delta "
2738ecbadc1Sclaudio "elem unexpectedely");
2748ecbadc1Sclaudio for (i = 0; attr[i]; i += 2) {
2758ecbadc1Sclaudio if (strcmp("uri", attr[i]) == 0 && hasUri++ == 0) {
2768ecbadc1Sclaudio if (valid_uri(attr[i + 1], strlen(attr[i + 1]),
2770610060dSjob HTTPS_PROTO) &&
27893d9375cSclaudio valid_origin(attr[i + 1], nxml->notifyuri)) {
2798ecbadc1Sclaudio delta_uri = attr[i + 1];
2808ecbadc1Sclaudio continue;
2818ecbadc1Sclaudio }
2828ecbadc1Sclaudio }
2838ecbadc1Sclaudio if (strcmp("hash", attr[i]) == 0 && hasHash++ == 0) {
2848ecbadc1Sclaudio if (hex_decode(attr[i + 1], delta_hash,
2858ecbadc1Sclaudio sizeof(delta_hash)) == 0)
2868ecbadc1Sclaudio continue;
2878ecbadc1Sclaudio }
2888ecbadc1Sclaudio if (strcmp("serial", attr[i]) == 0 && delta_serial == 0) {
2898ecbadc1Sclaudio const char *errstr;
2908ecbadc1Sclaudio
2918ecbadc1Sclaudio delta_serial = strtonum(attr[i + 1],
2928ecbadc1Sclaudio 1, LLONG_MAX, &errstr);
2938ecbadc1Sclaudio if (errstr == NULL)
2948ecbadc1Sclaudio continue;
2958ecbadc1Sclaudio }
2968ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - non conforming "
297*222e275fSjob "attribute '%s' found in delta elem", attr[i]);
2988ecbadc1Sclaudio }
2998ecbadc1Sclaudio /* Only add to the list if we are relevant */
3008ecbadc1Sclaudio if (hasUri != 1 || hasHash != 1 || delta_serial == 0)
3018ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - incomplete delta attributes");
3028ecbadc1Sclaudio
303b268327aSclaudio /* Delta serial must be smaller or equal to the notification serial */
304b268327aSclaudio if (nxml->serial < delta_serial)
305b268327aSclaudio PARSE_FAIL(p, "parse failed - bad delta serial");
306b268327aSclaudio
3078ecbadc1Sclaudio /* optimisation, add only deltas that could be interesting */
308b268327aSclaudio if (nxml->min_serial < delta_serial) {
3098ecbadc1Sclaudio if (add_delta(nxml, delta_uri, delta_hash, delta_serial) == 0)
3108ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - adding delta failed");
3118ecbadc1Sclaudio }
3128ecbadc1Sclaudio
3138ecbadc1Sclaudio nxml->scope = NOTIFICATION_SCOPE_DELTA;
3148ecbadc1Sclaudio }
3158ecbadc1Sclaudio
3168ecbadc1Sclaudio static void
end_delta_elem(struct notification_xml * nxml)3178ecbadc1Sclaudio end_delta_elem(struct notification_xml *nxml)
3188ecbadc1Sclaudio {
3198ecbadc1Sclaudio XML_Parser p = nxml->parser;
3208ecbadc1Sclaudio
3218ecbadc1Sclaudio if (nxml->scope != NOTIFICATION_SCOPE_DELTA)
3228ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - exited delta elem unexpectedely");
3238ecbadc1Sclaudio nxml->scope = NOTIFICATION_SCOPE_NOTIFICATION_POST_SNAPSHOT;
3248ecbadc1Sclaudio }
3258ecbadc1Sclaudio
3268ecbadc1Sclaudio static void
notification_xml_elem_start(void * data,const char * el,const char ** attr)3278ecbadc1Sclaudio notification_xml_elem_start(void *data, const char *el, const char **attr)
3288ecbadc1Sclaudio {
3298ecbadc1Sclaudio struct notification_xml *nxml = data;
3308ecbadc1Sclaudio XML_Parser p = nxml->parser;
3318ecbadc1Sclaudio
3328ecbadc1Sclaudio /*
3338ecbadc1Sclaudio * Can only enter here once as we should have no ways to get back to
3348ecbadc1Sclaudio * START scope
3358ecbadc1Sclaudio */
3368ecbadc1Sclaudio if (strcmp("notification", el) == 0)
3378ecbadc1Sclaudio start_notification_elem(nxml, attr);
3388ecbadc1Sclaudio /*
3398ecbadc1Sclaudio * Will enter here multiple times, BUT never nested. will start
3408ecbadc1Sclaudio * collecting character data in that handler
3418ecbadc1Sclaudio * mem is cleared in end block, (TODO or on parse failure)
3428ecbadc1Sclaudio */
3438ecbadc1Sclaudio else if (strcmp("snapshot", el) == 0)
3448ecbadc1Sclaudio start_snapshot_elem(nxml, attr);
3458ecbadc1Sclaudio else if (strcmp("delta", el) == 0)
3468ecbadc1Sclaudio start_delta_elem(nxml, attr);
3478ecbadc1Sclaudio else
3488ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - unexpected elem exit found");
3498ecbadc1Sclaudio }
3508ecbadc1Sclaudio
3518ecbadc1Sclaudio static void
notification_xml_elem_end(void * data,const char * el)3528ecbadc1Sclaudio notification_xml_elem_end(void *data, const char *el)
3538ecbadc1Sclaudio {
3548ecbadc1Sclaudio struct notification_xml *nxml = data;
3558ecbadc1Sclaudio XML_Parser p = nxml->parser;
3568ecbadc1Sclaudio
3578ecbadc1Sclaudio if (strcmp("notification", el) == 0)
3588ecbadc1Sclaudio end_notification_elem(nxml);
3598ecbadc1Sclaudio else if (strcmp("snapshot", el) == 0)
3608ecbadc1Sclaudio end_snapshot_elem(nxml);
3618ecbadc1Sclaudio else if (strcmp("delta", el) == 0)
3628ecbadc1Sclaudio end_delta_elem(nxml);
3638ecbadc1Sclaudio else
3648ecbadc1Sclaudio PARSE_FAIL(p, "parse failed - unexpected elem exit found");
3658ecbadc1Sclaudio }
3668ecbadc1Sclaudio
3678487774dSclaudio static void
notification_doctype_handler(void * data,const char * doctypeName,const char * sysid,const char * pubid,int subset)3688487774dSclaudio notification_doctype_handler(void *data, const char *doctypeName,
3698487774dSclaudio const char *sysid, const char *pubid, int subset)
3708487774dSclaudio {
3718487774dSclaudio struct notification_xml *nxml = data;
3728487774dSclaudio XML_Parser p = nxml->parser;
3738487774dSclaudio
3748487774dSclaudio PARSE_FAIL(p, "parse failed - DOCTYPE not allowed");
3758487774dSclaudio }
3768487774dSclaudio
3778ecbadc1Sclaudio struct notification_xml *
new_notification_xml(XML_Parser p,struct rrdp_session * repository,struct rrdp_session * current,const char * notifyuri)3788ecbadc1Sclaudio new_notification_xml(XML_Parser p, struct rrdp_session *repository,
37993d9375cSclaudio struct rrdp_session *current, const char *notifyuri)
3808ecbadc1Sclaudio {
3818ecbadc1Sclaudio struct notification_xml *nxml;
3828ecbadc1Sclaudio
3838ecbadc1Sclaudio if ((nxml = calloc(1, sizeof(*nxml))) == NULL)
3848ecbadc1Sclaudio err(1, "%s", __func__);
3858ecbadc1Sclaudio TAILQ_INIT(&(nxml->delta_q));
3868ecbadc1Sclaudio nxml->parser = p;
3878ecbadc1Sclaudio nxml->repository = repository;
3888ecbadc1Sclaudio nxml->current = current;
38993d9375cSclaudio nxml->notifyuri = notifyuri;
390b268327aSclaudio nxml->min_serial = delta_parse(repository, 0, NULL);
3918ecbadc1Sclaudio
3928ecbadc1Sclaudio XML_SetElementHandler(nxml->parser, notification_xml_elem_start,
3938ecbadc1Sclaudio notification_xml_elem_end);
3948ecbadc1Sclaudio XML_SetUserData(nxml->parser, nxml);
3958487774dSclaudio XML_SetDoctypeDeclHandler(nxml->parser, notification_doctype_handler,
3968487774dSclaudio NULL);
3978ecbadc1Sclaudio
3988ecbadc1Sclaudio return nxml;
3998ecbadc1Sclaudio }
4008ecbadc1Sclaudio
401b268327aSclaudio static void
free_delta_queue(struct notification_xml * nxml)402b268327aSclaudio free_delta_queue(struct notification_xml *nxml)
403b268327aSclaudio {
404b268327aSclaudio while (!TAILQ_EMPTY(&nxml->delta_q)) {
405b268327aSclaudio struct delta_item *d = TAILQ_FIRST(&nxml->delta_q);
406b268327aSclaudio TAILQ_REMOVE(&nxml->delta_q, d, q);
407b268327aSclaudio free_delta(d);
408b268327aSclaudio }
409b268327aSclaudio }
410b268327aSclaudio
4118ecbadc1Sclaudio void
free_notification_xml(struct notification_xml * nxml)4128ecbadc1Sclaudio free_notification_xml(struct notification_xml *nxml)
4138ecbadc1Sclaudio {
4148ecbadc1Sclaudio if (nxml == NULL)
4158ecbadc1Sclaudio return;
4168ecbadc1Sclaudio
4178ecbadc1Sclaudio free(nxml->session_id);
4188ecbadc1Sclaudio free(nxml->snapshot_uri);
419b268327aSclaudio free_delta_queue(nxml);
420b268327aSclaudio free(nxml);
421b268327aSclaudio }
422b268327aSclaudio
423b268327aSclaudio /*
424b268327aSclaudio * Collect a list of deltas to store in the repository state.
425b268327aSclaudio */
426b268327aSclaudio static void
notification_collect_deltas(struct notification_xml * nxml)427b268327aSclaudio notification_collect_deltas(struct notification_xml *nxml)
428b268327aSclaudio {
429b268327aSclaudio struct delta_item *d;
430b268327aSclaudio long long keep_serial = 0;
431b268327aSclaudio size_t cur_idx = 0, max_deltas;
432b268327aSclaudio char *hash;
433b268327aSclaudio
434b268327aSclaudio max_deltas =
435b268327aSclaudio sizeof(nxml->current->deltas) / sizeof(nxml->current->deltas[0]);
436b268327aSclaudio
437b268327aSclaudio if (nxml->serial > (long long)max_deltas)
438b268327aSclaudio keep_serial = nxml->serial - max_deltas + 1;
439b268327aSclaudio
440b268327aSclaudio TAILQ_FOREACH(d, &nxml->delta_q, q) {
441b268327aSclaudio if (d->serial >= keep_serial) {
442b268327aSclaudio assert(cur_idx < max_deltas);
443b268327aSclaudio hash = hex_encode(d->hash, sizeof(d->hash));
444b268327aSclaudio if (asprintf(&nxml->current->deltas[cur_idx++],
445b268327aSclaudio "%lld %s", d->serial, hash) == -1)
446b268327aSclaudio err(1, NULL);
447b268327aSclaudio free(hash);
448b268327aSclaudio }
449b268327aSclaudio }
450b268327aSclaudio }
451b268327aSclaudio
452b268327aSclaudio /*
453b268327aSclaudio * Validate the delta list with the information from the repository state.
454b268327aSclaudio * Remove all obsolete deltas so that the list starts with the delta with
455b268327aSclaudio * serial nxml->repository->serial + 1.
456b268327aSclaudio * Returns 1 if all deltas were valid and 0 on failure.
457b268327aSclaudio */
458b268327aSclaudio static int
notification_check_deltas(struct notification_xml * nxml)459b268327aSclaudio notification_check_deltas(struct notification_xml *nxml)
460b268327aSclaudio {
461b268327aSclaudio struct delta_item *d, *nextd;
462b268327aSclaudio char *hash, *exp_hash;
463b268327aSclaudio long long exp_serial, new_serial;
464b268327aSclaudio size_t exp_idx = 0;
465b268327aSclaudio
466b268327aSclaudio exp_serial = delta_parse(nxml->repository, exp_idx++, &exp_hash);
467b268327aSclaudio new_serial = nxml->repository->serial + 1;
468b268327aSclaudio
469b268327aSclaudio /* compare hash of delta against repository state info */
470b268327aSclaudio TAILQ_FOREACH_SAFE(d, &nxml->delta_q, q, nextd) {
471b268327aSclaudio while (exp_serial != 0 && exp_serial < d->serial) {
472b268327aSclaudio exp_serial = delta_parse(nxml->repository,
473b268327aSclaudio exp_idx++, &exp_hash);
474b268327aSclaudio }
475b268327aSclaudio
476b268327aSclaudio if (d->serial == exp_serial) {
477b268327aSclaudio hash = hex_encode(d->hash, sizeof(d->hash));
478b268327aSclaudio if (strcmp(hash, exp_hash) != 0) {
479b268327aSclaudio warnx("%s: %s#%lld unexpected delta "
480b268327aSclaudio "mutation (expected %s, got %s)",
481b268327aSclaudio nxml->notifyuri, nxml->session_id,
482b268327aSclaudio exp_serial, hash, exp_hash);
483b268327aSclaudio free(hash);
484b268327aSclaudio return 0;
485b268327aSclaudio }
486b268327aSclaudio free(hash);
487b268327aSclaudio exp_serial = delta_parse(nxml->repository,
488b268327aSclaudio exp_idx++, &exp_hash);
489b268327aSclaudio }
490b268327aSclaudio
491b268327aSclaudio /* is this delta needed? */
492b268327aSclaudio if (d->serial < new_serial) {
4938ecbadc1Sclaudio TAILQ_REMOVE(&nxml->delta_q, d, q);
4948ecbadc1Sclaudio free_delta(d);
4958ecbadc1Sclaudio }
496b268327aSclaudio }
497b268327aSclaudio
498b268327aSclaudio return 1;
4998ecbadc1Sclaudio }
5008ecbadc1Sclaudio
5018ecbadc1Sclaudio /*
5028ecbadc1Sclaudio * Finalize notification step, decide if a delta update is possible
5038ecbadc1Sclaudio * if either the session_id changed or the delta files fail to cover
5048ecbadc1Sclaudio * all the steps up to the new serial fall back to a snapshot.
5058ecbadc1Sclaudio * Return SNAPSHOT or DELTA for snapshot or delta processing.
5068ecbadc1Sclaudio * Return NOTIFICATION if repository is up to date.
5078ecbadc1Sclaudio */
5088ecbadc1Sclaudio enum rrdp_task
notification_done(struct notification_xml * nxml,char * last_mod)5098ecbadc1Sclaudio notification_done(struct notification_xml *nxml, char *last_mod)
5108ecbadc1Sclaudio {
5118ecbadc1Sclaudio nxml->current->last_mod = last_mod;
5128ecbadc1Sclaudio nxml->current->session_id = xstrdup(nxml->session_id);
513b268327aSclaudio notification_collect_deltas(nxml);
5148ecbadc1Sclaudio
5158ecbadc1Sclaudio /* check the that the session_id was valid and still the same */
5168ecbadc1Sclaudio if (nxml->repository->session_id == NULL ||
5178ecbadc1Sclaudio strcmp(nxml->session_id, nxml->repository->session_id) != 0)
5188ecbadc1Sclaudio goto snapshot;
5198ecbadc1Sclaudio
5208ecbadc1Sclaudio /* if repository serial is 0 fall back to snapshot */
5218ecbadc1Sclaudio if (nxml->repository->serial == 0)
5228ecbadc1Sclaudio goto snapshot;
5238ecbadc1Sclaudio
524b268327aSclaudio /* check that all needed deltas are available and valid */
525b268327aSclaudio if (!notification_check_deltas(nxml))
526b268327aSclaudio goto snapshot;
527b268327aSclaudio
52879ab6f25Sclaudio if (nxml->repository->serial > nxml->serial)
52979ab6f25Sclaudio warnx("%s: serial number decreased from %lld to %lld",
53079ab6f25Sclaudio nxml->notifyuri, nxml->repository->serial, nxml->serial);
53179ab6f25Sclaudio
53279ab6f25Sclaudio /* if our serial is equal or plus 2, the repo is up to date */
53379ab6f25Sclaudio if (nxml->repository->serial >= nxml->serial &&
53479ab6f25Sclaudio nxml->repository->serial - nxml->serial <= 2) {
535573c72b7Sclaudio nxml->current->serial = nxml->repository->serial;
5368ecbadc1Sclaudio return NOTIFICATION;
5378ecbadc1Sclaudio }
5388ecbadc1Sclaudio
5395b4fd51cSclaudio /* it makes no sense to process too many deltas */
5409363012eSjob if (nxml->serial - nxml->repository->serial > MAX_RRDP_DELTAS)
5415b4fd51cSclaudio goto snapshot;
5425b4fd51cSclaudio
543b268327aSclaudio /* no deltas queued */
544b268327aSclaudio if (TAILQ_EMPTY(&nxml->delta_q))
5458ecbadc1Sclaudio goto snapshot;
546b268327aSclaudio
547b268327aSclaudio /* first possible delta is no match */
548b268327aSclaudio if (nxml->repository->serial + 1 != TAILQ_FIRST(&nxml->delta_q)->serial)
5498ecbadc1Sclaudio goto snapshot;
5508ecbadc1Sclaudio
5518ecbadc1Sclaudio /* update via delta possible */
5528ecbadc1Sclaudio nxml->current->serial = nxml->repository->serial;
5538ecbadc1Sclaudio nxml->repository->serial = nxml->serial;
5548ecbadc1Sclaudio return DELTA;
5558ecbadc1Sclaudio
5568ecbadc1Sclaudio snapshot:
5578ecbadc1Sclaudio /* update via snapshot download */
558b268327aSclaudio free_delta_queue(nxml);
5598ecbadc1Sclaudio nxml->current->serial = nxml->serial;
5608ecbadc1Sclaudio return SNAPSHOT;
5618ecbadc1Sclaudio }
5628ecbadc1Sclaudio
5638ecbadc1Sclaudio const char *
notification_get_next(struct notification_xml * nxml,char * hash,size_t hlen,enum rrdp_task task)5648ecbadc1Sclaudio notification_get_next(struct notification_xml *nxml, char *hash, size_t hlen,
5658ecbadc1Sclaudio enum rrdp_task task)
5668ecbadc1Sclaudio {
5678ecbadc1Sclaudio struct delta_item *d;
5688ecbadc1Sclaudio
5698ecbadc1Sclaudio switch (task) {
5708ecbadc1Sclaudio case SNAPSHOT:
5718ecbadc1Sclaudio assert(hlen == sizeof(nxml->snapshot_hash));
5728ecbadc1Sclaudio memcpy(hash, nxml->snapshot_hash, hlen);
5738ecbadc1Sclaudio /*
5748ecbadc1Sclaudio * Ensure that the serial is correct in case a previous
5758ecbadc1Sclaudio * delta request failed.
5768ecbadc1Sclaudio */
5778ecbadc1Sclaudio nxml->current->serial = nxml->serial;
5788ecbadc1Sclaudio return nxml->snapshot_uri;
5798ecbadc1Sclaudio case DELTA:
5808ecbadc1Sclaudio /* first bump serial, then use first delta */
5818ecbadc1Sclaudio nxml->current->serial += 1;
5828ecbadc1Sclaudio d = TAILQ_FIRST(&nxml->delta_q);
5838ecbadc1Sclaudio assert(d->serial == nxml->current->serial);
5848ecbadc1Sclaudio assert(hlen == sizeof(d->hash));
5858ecbadc1Sclaudio memcpy(hash, d->hash, hlen);
5868ecbadc1Sclaudio return d->uri;
5878ecbadc1Sclaudio default:
5888ecbadc1Sclaudio errx(1, "%s: bad task", __func__);
5898ecbadc1Sclaudio }
5908ecbadc1Sclaudio }
5918ecbadc1Sclaudio
5928ecbadc1Sclaudio /*
5938ecbadc1Sclaudio * Pop first element from the delta queue. Return non-0 if this was the last
5948ecbadc1Sclaudio * delta to fetch.
5958ecbadc1Sclaudio */
5968ecbadc1Sclaudio int
notification_delta_done(struct notification_xml * nxml)5978ecbadc1Sclaudio notification_delta_done(struct notification_xml *nxml)
5988ecbadc1Sclaudio {
5998ecbadc1Sclaudio struct delta_item *d;
6008ecbadc1Sclaudio
6018ecbadc1Sclaudio d = TAILQ_FIRST(&nxml->delta_q);
6028ecbadc1Sclaudio assert(d->serial == nxml->current->serial);
6038ecbadc1Sclaudio TAILQ_REMOVE(&nxml->delta_q, d, q);
6048ecbadc1Sclaudio free_delta(d);
6058ecbadc1Sclaudio
6068ecbadc1Sclaudio assert(!TAILQ_EMPTY(&nxml->delta_q) ||
6078ecbadc1Sclaudio nxml->serial == nxml->current->serial);
6088ecbadc1Sclaudio return TAILQ_EMPTY(&nxml->delta_q);
6098ecbadc1Sclaudio }
6108ecbadc1Sclaudio
61158d5f018Stb /* Used in regress. */
6128ecbadc1Sclaudio void
log_notification_xml(struct notification_xml * nxml)6138ecbadc1Sclaudio log_notification_xml(struct notification_xml *nxml)
6148ecbadc1Sclaudio {
615aef00ae0Sclaudio struct delta_item *d;
616aef00ae0Sclaudio char *hash;
617aef00ae0Sclaudio
6188ecbadc1Sclaudio logx("session_id: %s, serial: %lld", nxml->session_id, nxml->serial);
6198ecbadc1Sclaudio logx("snapshot_uri: %s", nxml->snapshot_uri);
620aef00ae0Sclaudio hash = hex_encode(nxml->snapshot_hash, sizeof(nxml->snapshot_hash));
621aef00ae0Sclaudio logx("snapshot hash: %s", hash);
622aef00ae0Sclaudio free(hash);
623aef00ae0Sclaudio
624aef00ae0Sclaudio TAILQ_FOREACH(d, &nxml->delta_q, q) {
625aef00ae0Sclaudio logx("delta serial %lld uri: %s", d->serial, d->uri);
626aef00ae0Sclaudio hash = hex_encode(d->hash, sizeof(d->hash));
627aef00ae0Sclaudio logx("delta hash: %s", hash);
628aef00ae0Sclaudio free(hash);
629aef00ae0Sclaudio }
6308ecbadc1Sclaudio }
631