File: | src/usr.sbin/rpki-client/rrdp_notification.c |
Warning: | line 102, column 9 Potential leak of memory pointed to by 'd' |
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 != NULL((void *)0)) | ||||
139 | *hash = NULL((void *)0); | ||||
140 | if (idx < 0 || idx >= sizeof(s->deltas) / sizeof(s->deltas[0])) | ||||
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
| ||||
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
| ||||
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 != 0 && exp_serial < d->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 | } |