| File: | src/usr.sbin/rpki-client/rrdp_notification.c |
| Warning: | line 478, column 8 Null pointer passed as 2nd argument to string comparison function |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: rrdp_notification.c,v 1.19 2023/12/27 07:17:39 tb Exp $ */ | |||
| 2 | /* | |||
| 3 | * Copyright (c) 2020 Nils Fisher <nils_fisher@hotmail.com> | |||
| 4 | * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org> | |||
| 5 | * | |||
| 6 | * Permission to use, copy, modify, and distribute this software for any | |||
| 7 | * purpose with or without fee is hereby granted, provided that the above | |||
| 8 | * copyright notice and this permission notice appear in all copies. | |||
| 9 | * | |||
| 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| 17 | */ | |||
| 18 | ||||
| 19 | #include <sys/stat.h> | |||
| 20 | ||||
| 21 | #include <assert.h> | |||
| 22 | #include <err.h> | |||
| 23 | #include <errno(*__errno()).h> | |||
| 24 | #include <limits.h> | |||
| 25 | #include <fcntl.h> | |||
| 26 | #include <string.h> | |||
| 27 | #include <unistd.h> | |||
| 28 | ||||
| 29 | #include <expat.h> | |||
| 30 | #include <openssl/sha.h> | |||
| 31 | ||||
| 32 | #include "extern.h" | |||
| 33 | #include "rrdp.h" | |||
| 34 | ||||
| 35 | enum notification_scope { | |||
| 36 | NOTIFICATION_SCOPE_START, | |||
| 37 | NOTIFICATION_SCOPE_NOTIFICATION, | |||
| 38 | NOTIFICATION_SCOPE_SNAPSHOT, | |||
| 39 | NOTIFICATION_SCOPE_NOTIFICATION_POST_SNAPSHOT, | |||
| 40 | NOTIFICATION_SCOPE_DELTA, | |||
| 41 | NOTIFICATION_SCOPE_END | |||
| 42 | }; | |||
| 43 | ||||
| 44 | struct delta_item { | |||
| 45 | char *uri; | |||
| 46 | char hash[SHA256_DIGEST_LENGTH32]; | |||
| 47 | long long serial; | |||
| 48 | TAILQ_ENTRY(delta_item)struct { struct delta_item *tqe_next; struct delta_item **tqe_prev ; } q; | |||
| 49 | }; | |||
| 50 | ||||
| 51 | TAILQ_HEAD(delta_q, delta_item)struct delta_q { struct delta_item *tqh_first; struct delta_item **tqh_last; }; | |||
| 52 | ||||
| 53 | struct notification_xml { | |||
| 54 | XML_Parser parser; | |||
| 55 | struct rrdp_session *repository; | |||
| 56 | struct rrdp_session *current; | |||
| 57 | const char *notifyuri; | |||
| 58 | char *session_id; | |||
| 59 | char *snapshot_uri; | |||
| 60 | char snapshot_hash[SHA256_DIGEST_LENGTH32]; | |||
| 61 | struct delta_q delta_q; | |||
| 62 | long long serial; | |||
| 63 | long long min_serial; | |||
| 64 | int version; | |||
| 65 | enum notification_scope scope; | |||
| 66 | }; | |||
| 67 | ||||
| 68 | static void free_delta(struct delta_item *); | |||
| 69 | ||||
| 70 | static int | |||
| 71 | add_delta(struct notification_xml *nxml, const char *uri, | |||
| 72 | const char hash[SHA256_DIGEST_LENGTH32], long long serial) | |||
| 73 | { | |||
| 74 | struct delta_item *d, *n; | |||
| 75 | ||||
| 76 | if ((d = calloc(1, sizeof(struct delta_item))) == NULL((void *)0)) | |||
| 77 | err(1, "%s - calloc", __func__); | |||
| 78 | ||||
| 79 | d->serial = serial; | |||
| 80 | d->uri = xstrdup(uri); | |||
| 81 | memcpy(d->hash, hash, sizeof(d->hash)); | |||
| 82 | ||||
| 83 | /* optimise for a sorted input */ | |||
| 84 | n = TAILQ_LAST(&nxml->delta_q, delta_q)(*(((struct delta_q *)((&nxml->delta_q)->tqh_last)) ->tqh_last)); | |||
| 85 | if (n == NULL((void *)0)) | |||
| 86 | TAILQ_INSERT_HEAD(&nxml->delta_q, d, q)do { if (((d)->q.tqe_next = (&nxml->delta_q)->tqh_first ) != ((void *)0)) (&nxml->delta_q)->tqh_first->q .tqe_prev = &(d)->q.tqe_next; else (&nxml->delta_q )->tqh_last = &(d)->q.tqe_next; (&nxml->delta_q )->tqh_first = (d); (d)->q.tqe_prev = &(&nxml-> delta_q)->tqh_first; } while (0); | |||
| 87 | else if (n->serial < serial) | |||
| 88 | TAILQ_INSERT_TAIL(&nxml->delta_q, d, q)do { (d)->q.tqe_next = ((void *)0); (d)->q.tqe_prev = ( &nxml->delta_q)->tqh_last; *(&nxml->delta_q) ->tqh_last = (d); (&nxml->delta_q)->tqh_last = & (d)->q.tqe_next; } while (0); | |||
| 89 | else | |||
| 90 | TAILQ_FOREACH(n, &nxml->delta_q, q)for((n) = ((&nxml->delta_q)->tqh_first); (n) != ((void *)0); (n) = ((n)->q.tqe_next)) { | |||
| 91 | if (n->serial == serial) { | |||
| 92 | warnx("duplicate delta serial %lld ", serial); | |||
| 93 | free_delta(d); | |||
| 94 | return 0; | |||
| 95 | } | |||
| 96 | if (n->serial > serial) { | |||
| 97 | TAILQ_INSERT_BEFORE(n, d, q)do { (d)->q.tqe_prev = (n)->q.tqe_prev; (d)->q.tqe_next = (n); *(n)->q.tqe_prev = (d); (n)->q.tqe_prev = & (d)->q.tqe_next; } while (0); | |||
| 98 | break; | |||
| 99 | } | |||
| 100 | } | |||
| 101 | ||||
| 102 | return 1; | |||
| 103 | } | |||
| 104 | ||||
| 105 | /* check that there are no holes in the list */ | |||
| 106 | static int | |||
| 107 | check_delta(struct notification_xml *nxml) | |||
| 108 | { | |||
| 109 | struct delta_item *d; | |||
| 110 | long long serial = 0; | |||
| 111 | ||||
| 112 | TAILQ_FOREACH(d, &nxml->delta_q, q)for((d) = ((&nxml->delta_q)->tqh_first); (d) != ((void *)0); (d) = ((d)->q.tqe_next)) { | |||
| 113 | if (serial != 0 && serial + 1 != d->serial) | |||
| 114 | return 0; | |||
| 115 | serial = d->serial; | |||
| 116 | } | |||
| 117 | return 1; | |||
| 118 | } | |||
| 119 | ||||
| 120 | static void | |||
| 121 | free_delta(struct delta_item *d) | |||
| 122 | { | |||
| 123 | free(d->uri); | |||
| 124 | free(d); | |||
| 125 | } | |||
| 126 | ||||
| 127 | /* | |||
| 128 | * Parse a delta serial and hash line at idx from the rrdp session state. | |||
| 129 | * Return the serial or 0 on error. If hash is non-NULL, it is set to the | |||
| 130 | * start of the hash string on success. | |||
| 131 | */ | |||
| 132 | static long long | |||
| 133 | delta_parse(struct rrdp_session *s, size_t idx, char **hash) | |||
| 134 | { | |||
| 135 | long long serial; | |||
| 136 | char *line, *ep; | |||
| 137 | ||||
| 138 | if (hash
| |||
| 139 | *hash = NULL((void *)0); | |||
| 140 | if (idx
| |||
| 141 | return 0; | |||
| 142 | if ((line = s->deltas[idx]) == NULL((void *)0)) | |||
| 143 | return 0; | |||
| 144 | ||||
| 145 | errno(*__errno()) = 0; | |||
| 146 | serial = strtoll(line, &ep, 10); | |||
| 147 | if (line[0] == '\0' || *ep != ' ') | |||
| 148 | return 0; | |||
| 149 | if (serial <= 0 || (errno(*__errno()) == ERANGE34 && serial == LLONG_MAX0x7fffffffffffffffLL)) | |||
| 150 | return 0; | |||
| 151 | ||||
| 152 | if (hash != NULL((void *)0)) | |||
| 153 | *hash = ep + 1; | |||
| 154 | return serial; | |||
| 155 | } | |||
| 156 | ||||
| 157 | static void | |||
| 158 | start_notification_elem(struct notification_xml *nxml, const char **attr) | |||
| 159 | { | |||
| 160 | XML_Parser p = nxml->parser; | |||
| 161 | int has_xmlns = 0; | |||
| 162 | size_t i; | |||
| 163 | ||||
| 164 | if (nxml->scope != NOTIFICATION_SCOPE_START) | |||
| 165 | PARSE_FAIL(p,do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - entered notification elem unexpectedely" ); return; } while (0) | |||
| 166 | "parse failed - entered notification elem unexpectedely")do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - entered notification elem unexpectedely" ); return; } while (0); | |||
| 167 | for (i = 0; attr[i]; i += 2) { | |||
| 168 | const char *errstr; | |||
| 169 | if (strcmp("xmlns", attr[i]) == 0 && | |||
| 170 | strcmp(RRDP_XMLNS"http://www.ripe.net/rpki/rrdp", attr[i + 1]) == 0) { | |||
| 171 | has_xmlns = 1; | |||
| 172 | continue; | |||
| 173 | } | |||
| 174 | if (strcmp("session_id", attr[i]) == 0 && | |||
| 175 | valid_uuid(attr[i + 1])) { | |||
| 176 | nxml->session_id = xstrdup(attr[i + 1]); | |||
| 177 | continue; | |||
| 178 | } | |||
| 179 | if (strcmp("version", attr[i]) == 0) { | |||
| 180 | nxml->version = strtonum(attr[i + 1], | |||
| 181 | 1, MAX_VERSION1, &errstr); | |||
| 182 | if (errstr == NULL((void *)0)) | |||
| 183 | continue; | |||
| 184 | } | |||
| 185 | if (strcmp("serial", attr[i]) == 0) { | |||
| 186 | nxml->serial = strtonum(attr[i + 1], | |||
| 187 | 1, LLONG_MAX0x7fffffffffffffffLL, &errstr); | |||
| 188 | if (errstr == NULL((void *)0)) | |||
| 189 | continue; | |||
| 190 | } | |||
| 191 | PARSE_FAIL(p, "parse failed - non conforming "do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - non conforming " "attribute '%s' found in notification elem", attr[i]); return ; } while (0) | |||
| 192 | "attribute '%s' found in notification elem", attr[i])do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - non conforming " "attribute '%s' found in notification elem", attr[i]); return ; } while (0); | |||
| 193 | } | |||
| 194 | if (!(has_xmlns && nxml->version && nxml->session_id && nxml->serial)) | |||
| 195 | PARSE_FAIL(p, "parse failed - incomplete "do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - incomplete " "notification attributes"); return; } while (0) | |||
| 196 | "notification attributes")do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - incomplete " "notification attributes"); return; } while (0); | |||
| 197 | ||||
| 198 | /* Limit deltas to the ones which matter for us. */ | |||
| 199 | if (nxml->min_serial == 0 && nxml->serial > MAX_RRDP_DELTAS300) | |||
| 200 | nxml->min_serial = nxml->serial - MAX_RRDP_DELTAS300; | |||
| 201 | ||||
| 202 | nxml->scope = NOTIFICATION_SCOPE_NOTIFICATION; | |||
| 203 | } | |||
| 204 | ||||
| 205 | static void | |||
| 206 | end_notification_elem(struct notification_xml *nxml) | |||
| 207 | { | |||
| 208 | XML_Parser p = nxml->parser; | |||
| 209 | ||||
| 210 | if (nxml->scope != NOTIFICATION_SCOPE_NOTIFICATION_POST_SNAPSHOT) | |||
| 211 | PARSE_FAIL(p, "parse failed - exited notification "do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - exited notification " "elem unexpectedely"); return; } while (0) | |||
| 212 | "elem unexpectedely")do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - exited notification " "elem unexpectedely"); return; } while (0); | |||
| 213 | nxml->scope = NOTIFICATION_SCOPE_END; | |||
| 214 | ||||
| 215 | if (!check_delta(nxml)) | |||
| 216 | PARSE_FAIL(p, "parse failed - delta list has holes")do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - delta list has holes" ); return; } while (0); | |||
| 217 | } | |||
| 218 | ||||
| 219 | static void | |||
| 220 | start_snapshot_elem(struct notification_xml *nxml, const char **attr) | |||
| 221 | { | |||
| 222 | XML_Parser p = nxml->parser; | |||
| 223 | int i, hasUri = 0, hasHash = 0; | |||
| 224 | ||||
| 225 | if (nxml->scope != NOTIFICATION_SCOPE_NOTIFICATION) | |||
| 226 | PARSE_FAIL(p,do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - entered snapshot elem unexpectedely" ); return; } while (0) | |||
| 227 | "parse failed - entered snapshot elem unexpectedely")do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - entered snapshot elem unexpectedely" ); return; } while (0); | |||
| 228 | for (i = 0; attr[i]; i += 2) { | |||
| 229 | if (strcmp("uri", attr[i]) == 0 && hasUri++ == 0) { | |||
| 230 | if (valid_uri(attr[i + 1], strlen(attr[i + 1]), | |||
| 231 | "https://") && | |||
| 232 | valid_origin(attr[i + 1], nxml->notifyuri)) { | |||
| 233 | nxml->snapshot_uri = xstrdup(attr[i + 1]); | |||
| 234 | continue; | |||
| 235 | } | |||
| 236 | } | |||
| 237 | if (strcmp("hash", attr[i]) == 0 && hasHash++ == 0) { | |||
| 238 | if (hex_decode(attr[i + 1], nxml->snapshot_hash, | |||
| 239 | sizeof(nxml->snapshot_hash)) == 0) | |||
| 240 | continue; | |||
| 241 | } | |||
| 242 | PARSE_FAIL(p, "parse failed - non conforming "do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - non conforming " "attribute '%s' found in snapshot elem", attr[i]); return; } while (0) | |||
| 243 | "attribute '%s' found in snapshot elem", attr[i])do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - non conforming " "attribute '%s' found in snapshot elem", attr[i]); return; } while (0); | |||
| 244 | } | |||
| 245 | if (hasUri != 1 || hasHash != 1) | |||
| 246 | PARSE_FAIL(p, "parse failed - incomplete snapshot attributes")do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - incomplete snapshot attributes" ); return; } while (0); | |||
| 247 | ||||
| 248 | nxml->scope = NOTIFICATION_SCOPE_SNAPSHOT; | |||
| 249 | } | |||
| 250 | ||||
| 251 | static void | |||
| 252 | end_snapshot_elem(struct notification_xml *nxml) | |||
| 253 | { | |||
| 254 | XML_Parser p = nxml->parser; | |||
| 255 | ||||
| 256 | if (nxml->scope != NOTIFICATION_SCOPE_SNAPSHOT) | |||
| 257 | PARSE_FAIL(p, "parse failed - exited snapshot "do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - exited snapshot " "elem unexpectedely"); return; } while (0) | |||
| 258 | "elem unexpectedely")do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - exited snapshot " "elem unexpectedely"); return; } while (0); | |||
| 259 | nxml->scope = NOTIFICATION_SCOPE_NOTIFICATION_POST_SNAPSHOT; | |||
| 260 | } | |||
| 261 | ||||
| 262 | static void | |||
| 263 | start_delta_elem(struct notification_xml *nxml, const char **attr) | |||
| 264 | { | |||
| 265 | XML_Parser p = nxml->parser; | |||
| 266 | int i, hasUri = 0, hasHash = 0; | |||
| 267 | const char *delta_uri = NULL((void *)0); | |||
| 268 | char delta_hash[SHA256_DIGEST_LENGTH32]; | |||
| 269 | long long delta_serial = 0; | |||
| 270 | ||||
| 271 | if (nxml->scope != NOTIFICATION_SCOPE_NOTIFICATION_POST_SNAPSHOT) | |||
| 272 | PARSE_FAIL(p, "parse failed - entered delta "do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - entered delta " "elem unexpectedely"); return; } while (0) | |||
| 273 | "elem unexpectedely")do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - entered delta " "elem unexpectedely"); return; } while (0); | |||
| 274 | for (i = 0; attr[i]; i += 2) { | |||
| 275 | if (strcmp("uri", attr[i]) == 0 && hasUri++ == 0) { | |||
| 276 | if (valid_uri(attr[i + 1], strlen(attr[i + 1]), | |||
| 277 | "https://") && | |||
| 278 | valid_origin(attr[i + 1], nxml->notifyuri)) { | |||
| 279 | delta_uri = attr[i + 1]; | |||
| 280 | continue; | |||
| 281 | } | |||
| 282 | } | |||
| 283 | if (strcmp("hash", attr[i]) == 0 && hasHash++ == 0) { | |||
| 284 | if (hex_decode(attr[i + 1], delta_hash, | |||
| 285 | sizeof(delta_hash)) == 0) | |||
| 286 | continue; | |||
| 287 | } | |||
| 288 | if (strcmp("serial", attr[i]) == 0 && delta_serial == 0) { | |||
| 289 | const char *errstr; | |||
| 290 | ||||
| 291 | delta_serial = strtonum(attr[i + 1], | |||
| 292 | 1, LLONG_MAX0x7fffffffffffffffLL, &errstr); | |||
| 293 | if (errstr == NULL((void *)0)) | |||
| 294 | continue; | |||
| 295 | } | |||
| 296 | PARSE_FAIL(p, "parse failed - non conforming "do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - non conforming " "attribute '%s' found in snapshot elem", attr[i]); return; } while (0) | |||
| 297 | "attribute '%s' found in snapshot elem", attr[i])do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - non conforming " "attribute '%s' found in snapshot elem", attr[i]); return; } while (0); | |||
| 298 | } | |||
| 299 | /* Only add to the list if we are relevant */ | |||
| 300 | if (hasUri != 1 || hasHash != 1 || delta_serial == 0) | |||
| 301 | PARSE_FAIL(p, "parse failed - incomplete delta attributes")do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - incomplete delta attributes" ); return; } while (0); | |||
| 302 | ||||
| 303 | /* Delta serial must be smaller or equal to the notification serial */ | |||
| 304 | if (nxml->serial < delta_serial) | |||
| 305 | PARSE_FAIL(p, "parse failed - bad delta serial")do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - bad delta serial" ); return; } while (0); | |||
| 306 | ||||
| 307 | /* optimisation, add only deltas that could be interesting */ | |||
| 308 | if (nxml->min_serial < delta_serial) { | |||
| 309 | if (add_delta(nxml, delta_uri, delta_hash, delta_serial) == 0) | |||
| 310 | PARSE_FAIL(p, "parse failed - adding delta failed")do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - adding delta failed" ); return; } while (0); | |||
| 311 | } | |||
| 312 | ||||
| 313 | nxml->scope = NOTIFICATION_SCOPE_DELTA; | |||
| 314 | } | |||
| 315 | ||||
| 316 | static void | |||
| 317 | end_delta_elem(struct notification_xml *nxml) | |||
| 318 | { | |||
| 319 | XML_Parser p = nxml->parser; | |||
| 320 | ||||
| 321 | if (nxml->scope != NOTIFICATION_SCOPE_DELTA) | |||
| 322 | PARSE_FAIL(p, "parse failed - exited delta elem unexpectedely")do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - exited delta elem unexpectedely" ); return; } while (0); | |||
| 323 | nxml->scope = NOTIFICATION_SCOPE_NOTIFICATION_POST_SNAPSHOT; | |||
| 324 | } | |||
| 325 | ||||
| 326 | static void | |||
| 327 | notification_xml_elem_start(void *data, const char *el, const char **attr) | |||
| 328 | { | |||
| 329 | struct notification_xml *nxml = data; | |||
| 330 | XML_Parser p = nxml->parser; | |||
| 331 | ||||
| 332 | /* | |||
| 333 | * Can only enter here once as we should have no ways to get back to | |||
| 334 | * START scope | |||
| 335 | */ | |||
| 336 | if (strcmp("notification", el) == 0) | |||
| 337 | start_notification_elem(nxml, attr); | |||
| 338 | /* | |||
| 339 | * Will enter here multiple times, BUT never nested. will start | |||
| 340 | * collecting character data in that handler | |||
| 341 | * mem is cleared in end block, (TODO or on parse failure) | |||
| 342 | */ | |||
| 343 | else if (strcmp("snapshot", el) == 0) | |||
| 344 | start_snapshot_elem(nxml, attr); | |||
| 345 | else if (strcmp("delta", el) == 0) | |||
| 346 | start_delta_elem(nxml, attr); | |||
| 347 | else | |||
| 348 | PARSE_FAIL(p, "parse failed - unexpected elem exit found")do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - unexpected elem exit found" ); return; } while (0); | |||
| 349 | } | |||
| 350 | ||||
| 351 | static void | |||
| 352 | notification_xml_elem_end(void *data, const char *el) | |||
| 353 | { | |||
| 354 | struct notification_xml *nxml = data; | |||
| 355 | XML_Parser p = nxml->parser; | |||
| 356 | ||||
| 357 | if (strcmp("notification", el) == 0) | |||
| 358 | end_notification_elem(nxml); | |||
| 359 | else if (strcmp("snapshot", el) == 0) | |||
| 360 | end_snapshot_elem(nxml); | |||
| 361 | else if (strcmp("delta", el) == 0) | |||
| 362 | end_delta_elem(nxml); | |||
| 363 | else | |||
| 364 | PARSE_FAIL(p, "parse failed - unexpected elem exit found")do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - unexpected elem exit found" ); return; } while (0); | |||
| 365 | } | |||
| 366 | ||||
| 367 | static void | |||
| 368 | notification_doctype_handler(void *data, const char *doctypeName, | |||
| 369 | const char *sysid, const char *pubid, int subset) | |||
| 370 | { | |||
| 371 | struct notification_xml *nxml = data; | |||
| 372 | XML_Parser p = nxml->parser; | |||
| 373 | ||||
| 374 | PARSE_FAIL(p, "parse failed - DOCTYPE not allowed")do { XML_StopParser(p, ((XML_Bool)0)); warnx("parse failed - DOCTYPE not allowed" ); return; } while (0); | |||
| 375 | } | |||
| 376 | ||||
| 377 | struct notification_xml * | |||
| 378 | new_notification_xml(XML_Parser p, struct rrdp_session *repository, | |||
| 379 | struct rrdp_session *current, const char *notifyuri) | |||
| 380 | { | |||
| 381 | struct notification_xml *nxml; | |||
| 382 | ||||
| 383 | if ((nxml = calloc(1, sizeof(*nxml))) == NULL((void *)0)) | |||
| 384 | err(1, "%s", __func__); | |||
| 385 | TAILQ_INIT(&(nxml->delta_q))do { (&(nxml->delta_q))->tqh_first = ((void *)0); ( &(nxml->delta_q))->tqh_last = &(&(nxml-> delta_q))->tqh_first; } while (0); | |||
| 386 | nxml->parser = p; | |||
| 387 | nxml->repository = repository; | |||
| 388 | nxml->current = current; | |||
| 389 | nxml->notifyuri = notifyuri; | |||
| 390 | nxml->min_serial = delta_parse(repository, 0, NULL((void *)0)); | |||
| 391 | ||||
| 392 | XML_SetElementHandler(nxml->parser, notification_xml_elem_start, | |||
| 393 | notification_xml_elem_end); | |||
| 394 | XML_SetUserData(nxml->parser, nxml); | |||
| 395 | XML_SetDoctypeDeclHandler(nxml->parser, notification_doctype_handler, | |||
| 396 | NULL((void *)0)); | |||
| 397 | ||||
| 398 | return nxml; | |||
| 399 | } | |||
| 400 | ||||
| 401 | static void | |||
| 402 | free_delta_queue(struct notification_xml *nxml) | |||
| 403 | { | |||
| 404 | while (!TAILQ_EMPTY(&nxml->delta_q)(((&nxml->delta_q)->tqh_first) == ((void *)0))) { | |||
| 405 | struct delta_item *d = TAILQ_FIRST(&nxml->delta_q)((&nxml->delta_q)->tqh_first); | |||
| 406 | TAILQ_REMOVE(&nxml->delta_q, d, q)do { if (((d)->q.tqe_next) != ((void *)0)) (d)->q.tqe_next ->q.tqe_prev = (d)->q.tqe_prev; else (&nxml->delta_q )->tqh_last = (d)->q.tqe_prev; *(d)->q.tqe_prev = (d )->q.tqe_next; ; ; } while (0); | |||
| 407 | free_delta(d); | |||
| 408 | } | |||
| 409 | } | |||
| 410 | ||||
| 411 | void | |||
| 412 | free_notification_xml(struct notification_xml *nxml) | |||
| 413 | { | |||
| 414 | if (nxml == NULL((void *)0)) | |||
| 415 | return; | |||
| 416 | ||||
| 417 | free(nxml->session_id); | |||
| 418 | free(nxml->snapshot_uri); | |||
| 419 | free_delta_queue(nxml); | |||
| 420 | free(nxml); | |||
| 421 | } | |||
| 422 | ||||
| 423 | /* | |||
| 424 | * Collect a list of deltas to store in the repository state. | |||
| 425 | */ | |||
| 426 | static void | |||
| 427 | notification_collect_deltas(struct notification_xml *nxml) | |||
| 428 | { | |||
| 429 | struct delta_item *d; | |||
| 430 | long long keep_serial = 0; | |||
| 431 | size_t cur_idx = 0, max_deltas; | |||
| 432 | char *hash; | |||
| 433 | ||||
| 434 | max_deltas = | |||
| 435 | sizeof(nxml->current->deltas) / sizeof(nxml->current->deltas[0]); | |||
| 436 | ||||
| 437 | if (nxml->serial > (long long)max_deltas) | |||
| 438 | keep_serial = nxml->serial - max_deltas + 1; | |||
| 439 | ||||
| 440 | TAILQ_FOREACH(d, &nxml->delta_q, q)for((d) = ((&nxml->delta_q)->tqh_first); (d) != ((void *)0); (d) = ((d)->q.tqe_next)) { | |||
| 441 | if (d->serial >= keep_serial) { | |||
| 442 | assert(cur_idx < max_deltas)((cur_idx < max_deltas) ? (void)0 : __assert2("/usr/src/usr.sbin/rpki-client/rrdp_notification.c" , 442, __func__, "cur_idx < max_deltas")); | |||
| 443 | hash = hex_encode(d->hash, sizeof(d->hash)); | |||
| 444 | if (asprintf(&nxml->current->deltas[cur_idx++], | |||
| 445 | "%lld %s", d->serial, hash) == -1) | |||
| 446 | err(1, NULL((void *)0)); | |||
| 447 | free(hash); | |||
| 448 | } | |||
| 449 | } | |||
| 450 | } | |||
| 451 | ||||
| 452 | /* | |||
| 453 | * Validate the delta list with the information from the repository state. | |||
| 454 | * Remove all obsolete deltas so that the list starts with the delta with | |||
| 455 | * serial nxml->repository->serial + 1. | |||
| 456 | * Returns 1 if all deltas were valid and 0 on failure. | |||
| 457 | */ | |||
| 458 | static int | |||
| 459 | notification_check_deltas(struct notification_xml *nxml) | |||
| 460 | { | |||
| 461 | struct delta_item *d, *nextd; | |||
| 462 | char *hash, *exp_hash; | |||
| 463 | long long exp_serial, new_serial; | |||
| 464 | size_t exp_idx = 0; | |||
| 465 | ||||
| 466 | exp_serial = delta_parse(nxml->repository, exp_idx++, &exp_hash); | |||
| 467 | new_serial = nxml->repository->serial + 1; | |||
| 468 | ||||
| 469 | /* compare hash of delta against repository state info */ | |||
| 470 | TAILQ_FOREACH_SAFE(d, &nxml->delta_q, q, nextd)for ((d) = ((&nxml->delta_q)->tqh_first); (d) != (( void *)0) && ((nextd) = ((d)->q.tqe_next), 1); (d) = (nextd)) { | |||
| 471 | while (exp_serial
| |||
| 472 | exp_serial = delta_parse(nxml->repository, | |||
| 473 | exp_idx++, &exp_hash); | |||
| 474 | } | |||
| 475 | ||||
| 476 | if (d->serial == exp_serial) { | |||
| 477 | hash = hex_encode(d->hash, sizeof(d->hash)); | |||
| 478 | if (strcmp(hash, exp_hash) != 0) { | |||
| ||||
| 479 | warnx("%s: %s#%lld unexpected delta " | |||
| 480 | "mutation (expected %s, got %s)", | |||
| 481 | nxml->notifyuri, nxml->session_id, | |||
| 482 | exp_serial, hash, exp_hash); | |||
| 483 | free(hash); | |||
| 484 | return 0; | |||
| 485 | } | |||
| 486 | free(hash); | |||
| 487 | exp_serial = delta_parse(nxml->repository, | |||
| 488 | exp_idx++, &exp_hash); | |||
| 489 | } | |||
| 490 | ||||
| 491 | /* is this delta needed? */ | |||
| 492 | if (d->serial < new_serial) { | |||
| 493 | TAILQ_REMOVE(&nxml->delta_q, d, q)do { if (((d)->q.tqe_next) != ((void *)0)) (d)->q.tqe_next ->q.tqe_prev = (d)->q.tqe_prev; else (&nxml->delta_q )->tqh_last = (d)->q.tqe_prev; *(d)->q.tqe_prev = (d )->q.tqe_next; ; ; } while (0); | |||
| 494 | free_delta(d); | |||
| 495 | } | |||
| 496 | } | |||
| 497 | ||||
| 498 | return 1; | |||
| 499 | } | |||
| 500 | ||||
| 501 | /* | |||
| 502 | * Finalize notification step, decide if a delta update is possible | |||
| 503 | * if either the session_id changed or the delta files fail to cover | |||
| 504 | * all the steps up to the new serial fall back to a snapshot. | |||
| 505 | * Return SNAPSHOT or DELTA for snapshot or delta processing. | |||
| 506 | * Return NOTIFICATION if repository is up to date. | |||
| 507 | */ | |||
| 508 | enum rrdp_task | |||
| 509 | notification_done(struct notification_xml *nxml, char *last_mod) | |||
| 510 | { | |||
| 511 | nxml->current->last_mod = last_mod; | |||
| 512 | nxml->current->session_id = xstrdup(nxml->session_id); | |||
| 513 | notification_collect_deltas(nxml); | |||
| 514 | ||||
| 515 | /* check the that the session_id was valid and still the same */ | |||
| 516 | if (nxml->repository->session_id == NULL((void *)0) || | |||
| ||||
| 517 | strcmp(nxml->session_id, nxml->repository->session_id) != 0) | |||
| 518 | goto snapshot; | |||
| 519 | ||||
| 520 | /* if repository serial is 0 fall back to snapshot */ | |||
| 521 | if (nxml->repository->serial == 0) | |||
| 522 | goto snapshot; | |||
| 523 | ||||
| 524 | /* check that all needed deltas are available and valid */ | |||
| 525 | if (!notification_check_deltas(nxml)) | |||
| 526 | goto snapshot; | |||
| 527 | ||||
| 528 | if (nxml->repository->serial > nxml->serial) | |||
| 529 | warnx("%s: serial number decreased from %lld to %lld", | |||
| 530 | nxml->notifyuri, nxml->repository->serial, nxml->serial); | |||
| 531 | ||||
| 532 | /* if our serial is equal or plus 2, the repo is up to date */ | |||
| 533 | if (nxml->repository->serial >= nxml->serial && | |||
| 534 | nxml->repository->serial - nxml->serial <= 2) { | |||
| 535 | nxml->current->serial = nxml->repository->serial; | |||
| 536 | return NOTIFICATION; | |||
| 537 | } | |||
| 538 | ||||
| 539 | /* it makes no sense to process too many deltas */ | |||
| 540 | if (nxml->serial - nxml->repository->serial > MAX_RRDP_DELTAS300) | |||
| 541 | goto snapshot; | |||
| 542 | ||||
| 543 | /* no deltas queued */ | |||
| 544 | if (TAILQ_EMPTY(&nxml->delta_q)(((&nxml->delta_q)->tqh_first) == ((void *)0))) | |||
| 545 | goto snapshot; | |||
| 546 | ||||
| 547 | /* first possible delta is no match */ | |||
| 548 | if (nxml->repository->serial + 1 != TAILQ_FIRST(&nxml->delta_q)((&nxml->delta_q)->tqh_first)->serial) | |||
| 549 | goto snapshot; | |||
| 550 | ||||
| 551 | /* update via delta possible */ | |||
| 552 | nxml->current->serial = nxml->repository->serial; | |||
| 553 | nxml->repository->serial = nxml->serial; | |||
| 554 | return DELTA; | |||
| 555 | ||||
| 556 | snapshot: | |||
| 557 | /* update via snapshot download */ | |||
| 558 | free_delta_queue(nxml); | |||
| 559 | nxml->current->serial = nxml->serial; | |||
| 560 | return SNAPSHOT; | |||
| 561 | } | |||
| 562 | ||||
| 563 | const char * | |||
| 564 | notification_get_next(struct notification_xml *nxml, char *hash, size_t hlen, | |||
| 565 | enum rrdp_task task) | |||
| 566 | { | |||
| 567 | struct delta_item *d; | |||
| 568 | ||||
| 569 | switch (task) { | |||
| 570 | case SNAPSHOT: | |||
| 571 | assert(hlen == sizeof(nxml->snapshot_hash))((hlen == sizeof(nxml->snapshot_hash)) ? (void)0 : __assert2 ("/usr/src/usr.sbin/rpki-client/rrdp_notification.c", 571, __func__ , "hlen == sizeof(nxml->snapshot_hash)")); | |||
| 572 | memcpy(hash, nxml->snapshot_hash, hlen); | |||
| 573 | /* | |||
| 574 | * Ensure that the serial is correct in case a previous | |||
| 575 | * delta request failed. | |||
| 576 | */ | |||
| 577 | nxml->current->serial = nxml->serial; | |||
| 578 | return nxml->snapshot_uri; | |||
| 579 | case DELTA: | |||
| 580 | /* first bump serial, then use first delta */ | |||
| 581 | nxml->current->serial += 1; | |||
| 582 | d = TAILQ_FIRST(&nxml->delta_q)((&nxml->delta_q)->tqh_first); | |||
| 583 | assert(d->serial == nxml->current->serial)((d->serial == nxml->current->serial) ? (void)0 : __assert2 ("/usr/src/usr.sbin/rpki-client/rrdp_notification.c", 583, __func__ , "d->serial == nxml->current->serial")); | |||
| 584 | assert(hlen == sizeof(d->hash))((hlen == sizeof(d->hash)) ? (void)0 : __assert2("/usr/src/usr.sbin/rpki-client/rrdp_notification.c" , 584, __func__, "hlen == sizeof(d->hash)")); | |||
| 585 | memcpy(hash, d->hash, hlen); | |||
| 586 | return d->uri; | |||
| 587 | default: | |||
| 588 | errx(1, "%s: bad task", __func__); | |||
| 589 | } | |||
| 590 | } | |||
| 591 | ||||
| 592 | /* | |||
| 593 | * Pop first element from the delta queue. Return non-0 if this was the last | |||
| 594 | * delta to fetch. | |||
| 595 | */ | |||
| 596 | int | |||
| 597 | notification_delta_done(struct notification_xml *nxml) | |||
| 598 | { | |||
| 599 | struct delta_item *d; | |||
| 600 | ||||
| 601 | d = TAILQ_FIRST(&nxml->delta_q)((&nxml->delta_q)->tqh_first); | |||
| 602 | assert(d->serial == nxml->current->serial)((d->serial == nxml->current->serial) ? (void)0 : __assert2 ("/usr/src/usr.sbin/rpki-client/rrdp_notification.c", 602, __func__ , "d->serial == nxml->current->serial")); | |||
| 603 | TAILQ_REMOVE(&nxml->delta_q, d, q)do { if (((d)->q.tqe_next) != ((void *)0)) (d)->q.tqe_next ->q.tqe_prev = (d)->q.tqe_prev; else (&nxml->delta_q )->tqh_last = (d)->q.tqe_prev; *(d)->q.tqe_prev = (d )->q.tqe_next; ; ; } while (0); | |||
| 604 | free_delta(d); | |||
| 605 | ||||
| 606 | assert(!TAILQ_EMPTY(&nxml->delta_q) ||((!(((&nxml->delta_q)->tqh_first) == ((void *)0)) || nxml->serial == nxml->current->serial) ? (void)0 : __assert2 ("/usr/src/usr.sbin/rpki-client/rrdp_notification.c", 607, __func__ , "!TAILQ_EMPTY(&nxml->delta_q) || nxml->serial == nxml->current->serial" )) | |||
| 607 | nxml->serial == nxml->current->serial)((!(((&nxml->delta_q)->tqh_first) == ((void *)0)) || nxml->serial == nxml->current->serial) ? (void)0 : __assert2 ("/usr/src/usr.sbin/rpki-client/rrdp_notification.c", 607, __func__ , "!TAILQ_EMPTY(&nxml->delta_q) || nxml->serial == nxml->current->serial" )); | |||
| 608 | return TAILQ_EMPTY(&nxml->delta_q)(((&nxml->delta_q)->tqh_first) == ((void *)0)); | |||
| 609 | } | |||
| 610 | ||||
| 611 | /* Used in regress. */ | |||
| 612 | void | |||
| 613 | log_notification_xml(struct notification_xml *nxml) | |||
| 614 | { | |||
| 615 | struct delta_item *d; | |||
| 616 | char *hash; | |||
| 617 | ||||
| 618 | logx("session_id: %s, serial: %lld", nxml->session_id, nxml->serial); | |||
| 619 | logx("snapshot_uri: %s", nxml->snapshot_uri); | |||
| 620 | hash = hex_encode(nxml->snapshot_hash, sizeof(nxml->snapshot_hash)); | |||
| 621 | logx("snapshot hash: %s", hash); | |||
| 622 | free(hash); | |||
| 623 | ||||
| 624 | TAILQ_FOREACH(d, &nxml->delta_q, q)for((d) = ((&nxml->delta_q)->tqh_first); (d) != ((void *)0); (d) = ((d)->q.tqe_next)) { | |||
| 625 | logx("delta serial %lld uri: %s", d->serial, d->uri); | |||
| 626 | hash = hex_encode(d->hash, sizeof(d->hash)); | |||
| 627 | logx("delta hash: %s", hash); | |||
| 628 | free(hash); | |||
| 629 | } | |||
| 630 | } |