xref: /openbsd-src/usr.sbin/rpki-client/rrdp.c (revision b5fa5d51bd4733ed62a748e18f6a838408441343)
1*b5fa5d51Sclaudio /*	$OpenBSD: rrdp.c,v 1.39 2024/11/21 13:32:27 claudio 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 #include <sys/queue.h>
198ecbadc1Sclaudio #include <sys/stat.h>
208ecbadc1Sclaudio 
218ecbadc1Sclaudio #include <err.h>
228ecbadc1Sclaudio #include <errno.h>
238ecbadc1Sclaudio #include <fcntl.h>
248ecbadc1Sclaudio #include <limits.h>
258ecbadc1Sclaudio #include <poll.h>
268ecbadc1Sclaudio #include <string.h>
278ecbadc1Sclaudio #include <unistd.h>
288ecbadc1Sclaudio #include <imsg.h>
298ecbadc1Sclaudio 
308ecbadc1Sclaudio #include <expat.h>
318ecbadc1Sclaudio #include <openssl/sha.h>
328ecbadc1Sclaudio 
338ecbadc1Sclaudio #include "extern.h"
348ecbadc1Sclaudio #include "rrdp.h"
358ecbadc1Sclaudio 
366dd0ae03Sjob #define MAX_SESSIONS	32
378ecbadc1Sclaudio #define	READ_BUF_SIZE	(32 * 1024)
388ecbadc1Sclaudio 
3925d36c5cSclaudio static struct msgbuf	*msgq;
408ecbadc1Sclaudio 
418ecbadc1Sclaudio #define RRDP_STATE_REQ		0x01
428ecbadc1Sclaudio #define RRDP_STATE_WAIT		0x02
438ecbadc1Sclaudio #define RRDP_STATE_PARSE	0x04
448ecbadc1Sclaudio #define RRDP_STATE_PARSE_ERROR	0x08
458ecbadc1Sclaudio #define RRDP_STATE_PARSE_DONE	0x10
468ecbadc1Sclaudio #define RRDP_STATE_HTTP_DONE	0x20
478ecbadc1Sclaudio #define RRDP_STATE_DONE		(RRDP_STATE_PARSE_DONE | RRDP_STATE_HTTP_DONE)
488ecbadc1Sclaudio 
498ecbadc1Sclaudio struct rrdp {
508ecbadc1Sclaudio 	TAILQ_ENTRY(rrdp)	 entry;
51b6884e9fSclaudio 	unsigned int		 id;
528ecbadc1Sclaudio 	char			*notifyuri;
538ecbadc1Sclaudio 	char			*local;
548ecbadc1Sclaudio 	char			*last_mod;
558ecbadc1Sclaudio 
568ecbadc1Sclaudio 	struct pollfd		*pfd;
578ecbadc1Sclaudio 	int			 infd;
588ecbadc1Sclaudio 	int			 state;
594673c683Sclaudio 	int			 aborted;
608ecbadc1Sclaudio 	unsigned int		 file_pending;
618ecbadc1Sclaudio 	unsigned int		 file_failed;
628ecbadc1Sclaudio 	enum http_result	 res;
638ecbadc1Sclaudio 	enum rrdp_task		 task;
648ecbadc1Sclaudio 
658ecbadc1Sclaudio 	char			 hash[SHA256_DIGEST_LENGTH];
668ecbadc1Sclaudio 	SHA256_CTX		 ctx;
678ecbadc1Sclaudio 
68b268327aSclaudio 	struct rrdp_session	*repository;
69b268327aSclaudio 	struct rrdp_session	*current;
708ecbadc1Sclaudio 	XML_Parser		 parser;
718ecbadc1Sclaudio 	struct notification_xml	*nxml;
728ecbadc1Sclaudio 	struct snapshot_xml	*sxml;
738ecbadc1Sclaudio 	struct delta_xml	*dxml;
748ecbadc1Sclaudio };
758ecbadc1Sclaudio 
764673c683Sclaudio static TAILQ_HEAD(, rrdp)	states = TAILQ_HEAD_INITIALIZER(states);
778ecbadc1Sclaudio 
788ecbadc1Sclaudio char *
798ecbadc1Sclaudio xstrdup(const char *s)
808ecbadc1Sclaudio {
818ecbadc1Sclaudio 	char *r;
828ecbadc1Sclaudio 	if ((r = strdup(s)) == NULL)
838ecbadc1Sclaudio 		err(1, "strdup");
848ecbadc1Sclaudio 	return r;
858ecbadc1Sclaudio }
868ecbadc1Sclaudio 
878ecbadc1Sclaudio /*
888ecbadc1Sclaudio  * Report back that a RRDP request finished.
898ecbadc1Sclaudio  * ok should only be set to 1 if the cache is now up-to-date.
908ecbadc1Sclaudio  */
918ecbadc1Sclaudio static void
92b6884e9fSclaudio rrdp_done(unsigned int id, int ok)
938ecbadc1Sclaudio {
948ecbadc1Sclaudio 	enum rrdp_msg type = RRDP_END;
958ecbadc1Sclaudio 	struct ibuf *b;
968ecbadc1Sclaudio 
9725f7afeeSclaudio 	b = io_new_buffer();
988ecbadc1Sclaudio 	io_simple_buffer(b, &type, sizeof(type));
998ecbadc1Sclaudio 	io_simple_buffer(b, &id, sizeof(id));
1008ecbadc1Sclaudio 	io_simple_buffer(b, &ok, sizeof(ok));
10125d36c5cSclaudio 	io_close_buffer(msgq, b);
1028ecbadc1Sclaudio }
1038ecbadc1Sclaudio 
1048ecbadc1Sclaudio /*
1058ecbadc1Sclaudio  * Request an URI to be fetched via HTTPS.
1068ecbadc1Sclaudio  * The main process will respond with a RRDP_HTTP_INI which includes
1078ecbadc1Sclaudio  * the file descriptor to read from. RRDP_HTTP_FIN is sent at the
1088ecbadc1Sclaudio  * end of the request with the HTTP status code and last modified timestamp.
1098ecbadc1Sclaudio  * If the request should not set the If-Modified-Since: header then last_mod
1108ecbadc1Sclaudio  * should be set to NULL, else it should point to a proper date string.
1118ecbadc1Sclaudio  */
1128ecbadc1Sclaudio static void
113b6884e9fSclaudio rrdp_http_req(unsigned int id, const char *uri, const char *last_mod)
1148ecbadc1Sclaudio {
1158ecbadc1Sclaudio 	enum rrdp_msg type = RRDP_HTTP_REQ;
1168ecbadc1Sclaudio 	struct ibuf *b;
1178ecbadc1Sclaudio 
11825f7afeeSclaudio 	b = io_new_buffer();
1198ecbadc1Sclaudio 	io_simple_buffer(b, &type, sizeof(type));
1208ecbadc1Sclaudio 	io_simple_buffer(b, &id, sizeof(id));
1218ecbadc1Sclaudio 	io_str_buffer(b, uri);
1228ecbadc1Sclaudio 	io_str_buffer(b, last_mod);
12325d36c5cSclaudio 	io_close_buffer(msgq, b);
1248ecbadc1Sclaudio }
1258ecbadc1Sclaudio 
1268ecbadc1Sclaudio /*
1278ecbadc1Sclaudio  * Send the session state to the main process so it gets stored.
1288ecbadc1Sclaudio  */
1298ecbadc1Sclaudio static void
1308ecbadc1Sclaudio rrdp_state_send(struct rrdp *s)
1318ecbadc1Sclaudio {
1328ecbadc1Sclaudio 	enum rrdp_msg type = RRDP_SESSION;
1338ecbadc1Sclaudio 	struct ibuf *b;
1348ecbadc1Sclaudio 
13525f7afeeSclaudio 	b = io_new_buffer();
1368ecbadc1Sclaudio 	io_simple_buffer(b, &type, sizeof(type));
1378ecbadc1Sclaudio 	io_simple_buffer(b, &s->id, sizeof(s->id));
138b268327aSclaudio 	rrdp_session_buffer(b, s->current);
13925d36c5cSclaudio 	io_close_buffer(msgq, b);
1408ecbadc1Sclaudio }
1418ecbadc1Sclaudio 
142aef00ae0Sclaudio /*
143264f4ef9Sclaudio  * Inform parent to clear the RRDP repository before start of snapshot.
144264f4ef9Sclaudio  */
145264f4ef9Sclaudio static void
146264f4ef9Sclaudio rrdp_clear_repo(struct rrdp *s)
147264f4ef9Sclaudio {
148264f4ef9Sclaudio 	enum rrdp_msg type = RRDP_CLEAR;
149264f4ef9Sclaudio 	struct ibuf *b;
150264f4ef9Sclaudio 
151264f4ef9Sclaudio 	b = io_new_buffer();
152264f4ef9Sclaudio 	io_simple_buffer(b, &type, sizeof(type));
153264f4ef9Sclaudio 	io_simple_buffer(b, &s->id, sizeof(s->id));
15425d36c5cSclaudio 	io_close_buffer(msgq, b);
155264f4ef9Sclaudio }
156264f4ef9Sclaudio 
157264f4ef9Sclaudio /*
158aef00ae0Sclaudio  * Send a blob of data to the main process to store it in the repository.
159aef00ae0Sclaudio  */
160aef00ae0Sclaudio void
161aef00ae0Sclaudio rrdp_publish_file(struct rrdp *s, struct publish_xml *pxml,
162aef00ae0Sclaudio     unsigned char *data, size_t datasz)
163aef00ae0Sclaudio {
164aef00ae0Sclaudio 	enum rrdp_msg type = RRDP_FILE;
165aef00ae0Sclaudio 	struct ibuf *b;
166aef00ae0Sclaudio 
167aef00ae0Sclaudio 	/* only send files if the fetch did not fail already */
168aef00ae0Sclaudio 	if (s->file_failed == 0) {
169aef00ae0Sclaudio 		b = io_new_buffer();
170aef00ae0Sclaudio 		io_simple_buffer(b, &type, sizeof(type));
171aef00ae0Sclaudio 		io_simple_buffer(b, &s->id, sizeof(s->id));
172aef00ae0Sclaudio 		io_simple_buffer(b, &pxml->type, sizeof(pxml->type));
173aef00ae0Sclaudio 		if (pxml->type != PUB_ADD)
174aef00ae0Sclaudio 			io_simple_buffer(b, &pxml->hash, sizeof(pxml->hash));
175aef00ae0Sclaudio 		io_str_buffer(b, pxml->uri);
176aef00ae0Sclaudio 		io_buf_buffer(b, data, datasz);
17725d36c5cSclaudio 		io_close_buffer(msgq, b);
178aef00ae0Sclaudio 		s->file_pending++;
179aef00ae0Sclaudio 	}
180aef00ae0Sclaudio }
181aef00ae0Sclaudio 
18298efa068Stb static void
183b268327aSclaudio rrdp_new(unsigned int id, char *local, char *notify, struct rrdp_session *state)
1848ecbadc1Sclaudio {
1858ecbadc1Sclaudio 	struct rrdp *s;
1868ecbadc1Sclaudio 
1878ecbadc1Sclaudio 	if ((s = calloc(1, sizeof(*s))) == NULL)
1888ecbadc1Sclaudio 		err(1, NULL);
1898ecbadc1Sclaudio 
1908ecbadc1Sclaudio 	s->infd = -1;
1918ecbadc1Sclaudio 	s->id = id;
1928ecbadc1Sclaudio 	s->local = local;
1938ecbadc1Sclaudio 	s->notifyuri = notify;
194b268327aSclaudio 	s->repository = state;
195b268327aSclaudio 	if ((s->current = calloc(1, sizeof(*s->current))) == NULL)
196b268327aSclaudio 		err(1, NULL);
1978ecbadc1Sclaudio 
1988ecbadc1Sclaudio 	s->state = RRDP_STATE_REQ;
1998ecbadc1Sclaudio 	if ((s->parser = XML_ParserCreate("US-ASCII")) == NULL)
2008ecbadc1Sclaudio 		err(1, "XML_ParserCreate");
2018ecbadc1Sclaudio 
202b268327aSclaudio 	s->nxml = new_notification_xml(s->parser, s->repository, s->current,
20393d9375cSclaudio 	    notify);
2048ecbadc1Sclaudio 
2058ecbadc1Sclaudio 	TAILQ_INSERT_TAIL(&states, s, entry);
2068ecbadc1Sclaudio }
2078ecbadc1Sclaudio 
2088ecbadc1Sclaudio static void
2098ecbadc1Sclaudio rrdp_free(struct rrdp *s)
2108ecbadc1Sclaudio {
2118ecbadc1Sclaudio 	if (s == NULL)
2128ecbadc1Sclaudio 		return;
2138ecbadc1Sclaudio 
2148ecbadc1Sclaudio 	TAILQ_REMOVE(&states, s, entry);
2158ecbadc1Sclaudio 
2168ecbadc1Sclaudio 	free_notification_xml(s->nxml);
2178ecbadc1Sclaudio 	free_snapshot_xml(s->sxml);
2188ecbadc1Sclaudio 	free_delta_xml(s->dxml);
2198ecbadc1Sclaudio 
2208ecbadc1Sclaudio 	if (s->parser)
2218ecbadc1Sclaudio 		XML_ParserFree(s->parser);
222fef832f0Sclaudio 	if (s->infd != -1)
223fef832f0Sclaudio 		close(s->infd);
2248ecbadc1Sclaudio 	free(s->notifyuri);
2258ecbadc1Sclaudio 	free(s->local);
2268ecbadc1Sclaudio 	free(s->last_mod);
227b268327aSclaudio 	rrdp_session_free(s->repository);
228b268327aSclaudio 	rrdp_session_free(s->current);
2298ecbadc1Sclaudio 
2308ecbadc1Sclaudio 	free(s);
2318ecbadc1Sclaudio }
2328ecbadc1Sclaudio 
2338ecbadc1Sclaudio static struct rrdp *
234b6884e9fSclaudio rrdp_get(unsigned int id)
2358ecbadc1Sclaudio {
2368ecbadc1Sclaudio 	struct rrdp *s;
2378ecbadc1Sclaudio 
2388ecbadc1Sclaudio 	TAILQ_FOREACH(s, &states, entry)
2398ecbadc1Sclaudio 		if (s->id == id)
2408ecbadc1Sclaudio 			break;
2418ecbadc1Sclaudio 	return s;
2428ecbadc1Sclaudio }
2438ecbadc1Sclaudio 
2448ecbadc1Sclaudio static void
2458ecbadc1Sclaudio rrdp_failed(struct rrdp *s)
2468ecbadc1Sclaudio {
247b6884e9fSclaudio 	unsigned int id = s->id;
2488ecbadc1Sclaudio 
2498ecbadc1Sclaudio 	/* reset file state before retrying */
2508ecbadc1Sclaudio 	s->file_failed = 0;
2518ecbadc1Sclaudio 
2524673c683Sclaudio 	if (s->task == DELTA && !s->aborted) {
2538ecbadc1Sclaudio 		/* fallback to a snapshot as per RFC8182 */
2548ecbadc1Sclaudio 		free_delta_xml(s->dxml);
2558ecbadc1Sclaudio 		s->dxml = NULL;
256264f4ef9Sclaudio 		rrdp_clear_repo(s);
257b268327aSclaudio 		s->sxml = new_snapshot_xml(s->parser, s->current, s);
2588ecbadc1Sclaudio 		s->task = SNAPSHOT;
2598ecbadc1Sclaudio 		s->state = RRDP_STATE_REQ;
2601648e347Sclaudio 		logx("%s: delta sync failed, fallback to snapshot", s->local);
2618ecbadc1Sclaudio 	} else {
2628ecbadc1Sclaudio 		/*
2638ecbadc1Sclaudio 		 * TODO: update state to track recurring failures
2648ecbadc1Sclaudio 		 * and fall back to rsync after a while.
2658ecbadc1Sclaudio 		 * This should probably happen in the main process.
2668ecbadc1Sclaudio 		 */
2678ecbadc1Sclaudio 		rrdp_free(s);
2688ecbadc1Sclaudio 		rrdp_done(id, 0);
2698ecbadc1Sclaudio 	}
2708ecbadc1Sclaudio }
2718ecbadc1Sclaudio 
2728ecbadc1Sclaudio static void
2738ecbadc1Sclaudio rrdp_finished(struct rrdp *s)
2748ecbadc1Sclaudio {
275b6884e9fSclaudio 	unsigned int id = s->id;
2768ecbadc1Sclaudio 
2778ecbadc1Sclaudio 	/* check if all parts of the process have finished */
2788ecbadc1Sclaudio 	if ((s->state & RRDP_STATE_DONE) != RRDP_STATE_DONE)
2798ecbadc1Sclaudio 		return;
2808ecbadc1Sclaudio 
2818ecbadc1Sclaudio 	/* still some files pending */
2828ecbadc1Sclaudio 	if (s->file_pending > 0)
2838ecbadc1Sclaudio 		return;
2848ecbadc1Sclaudio 
2854673c683Sclaudio 	if (s->state & RRDP_STATE_PARSE_ERROR || s->aborted) {
2868ecbadc1Sclaudio 		rrdp_failed(s);
2878ecbadc1Sclaudio 		return;
2888ecbadc1Sclaudio 	}
2898ecbadc1Sclaudio 
2908ecbadc1Sclaudio 	if (s->res == HTTP_OK) {
2910ba844fdSclaudio 		XML_Parser p = s->parser;
2920ba844fdSclaudio 
2938ecbadc1Sclaudio 		/*
2948ecbadc1Sclaudio 		 * Finalize parsing on success to be sure that
2958ecbadc1Sclaudio 		 * all of the XML is correct. Needs to be done here
2968ecbadc1Sclaudio 		 * since the call would most probably fail for non
2978ecbadc1Sclaudio 		 * successful data fetches.
2988ecbadc1Sclaudio 		 */
2990ba844fdSclaudio 		if (XML_Parse(p, NULL, 0, 1) != XML_STATUS_OK) {
3000ba844fdSclaudio 			warnx("%s: XML error at line %llu: %s", s->local,
3010ba844fdSclaudio 			    (unsigned long long)XML_GetCurrentLineNumber(p),
3020ba844fdSclaudio 			    XML_ErrorString(XML_GetErrorCode(p)));
3038ecbadc1Sclaudio 			rrdp_failed(s);
3048ecbadc1Sclaudio 			return;
3058ecbadc1Sclaudio 		}
3068ecbadc1Sclaudio 
3078ecbadc1Sclaudio 		/* If a file caused an error fail the update */
3088ecbadc1Sclaudio 		if (s->file_failed > 0) {
3098ecbadc1Sclaudio 			rrdp_failed(s);
3108ecbadc1Sclaudio 			return;
3118ecbadc1Sclaudio 		}
3128ecbadc1Sclaudio 
3138ecbadc1Sclaudio 		switch (s->task) {
3148ecbadc1Sclaudio 		case NOTIFICATION:
3158ecbadc1Sclaudio 			s->task = notification_done(s->nxml, s->last_mod);
3168ecbadc1Sclaudio 			s->last_mod = NULL;
3178ecbadc1Sclaudio 			switch (s->task) {
3188ecbadc1Sclaudio 			case NOTIFICATION:
319f5209848Sjob 				logx("%s: repository not modified (%s#%lld)",
320b268327aSclaudio 				    s->local, s->repository->session_id,
321b268327aSclaudio 				    s->repository->serial);
3228ecbadc1Sclaudio 				rrdp_state_send(s);
3238ecbadc1Sclaudio 				rrdp_free(s);
3248ecbadc1Sclaudio 				rrdp_done(id, 1);
3258ecbadc1Sclaudio 				break;
3268ecbadc1Sclaudio 			case SNAPSHOT:
327f5209848Sjob 				logx("%s: downloading snapshot (%s#%lld)",
328b268327aSclaudio 				    s->local, s->current->session_id,
329b268327aSclaudio 				    s->current->serial);
330264f4ef9Sclaudio 				rrdp_clear_repo(s);
331b268327aSclaudio 				s->sxml = new_snapshot_xml(p, s->current, s);
3328ecbadc1Sclaudio 				s->state = RRDP_STATE_REQ;
3338ecbadc1Sclaudio 				break;
3348ecbadc1Sclaudio 			case DELTA:
335f5209848Sjob 				logx("%s: downloading %lld deltas (%s#%lld)",
336f5209848Sjob 				    s->local,
337b268327aSclaudio 				    s->repository->serial - s->current->serial,
338b268327aSclaudio 				    s->current->session_id, s->current->serial);
339b268327aSclaudio 				s->dxml = new_delta_xml(p, s->current, s);
3408ecbadc1Sclaudio 				s->state = RRDP_STATE_REQ;
3418ecbadc1Sclaudio 				break;
3428ecbadc1Sclaudio 			}
3438ecbadc1Sclaudio 			break;
3448ecbadc1Sclaudio 		case SNAPSHOT:
3458ecbadc1Sclaudio 			rrdp_state_send(s);
3468ecbadc1Sclaudio 			rrdp_free(s);
3478ecbadc1Sclaudio 			rrdp_done(id, 1);
3488ecbadc1Sclaudio 			break;
3498ecbadc1Sclaudio 		case DELTA:
3508ecbadc1Sclaudio 			if (notification_delta_done(s->nxml)) {
3518ecbadc1Sclaudio 				/* finished */
3528ecbadc1Sclaudio 				rrdp_state_send(s);
3538ecbadc1Sclaudio 				rrdp_free(s);
3548ecbadc1Sclaudio 				rrdp_done(id, 1);
3558ecbadc1Sclaudio 			} else {
3568ecbadc1Sclaudio 				/* reset delta parser for next delta */
3578ecbadc1Sclaudio 				free_delta_xml(s->dxml);
358b268327aSclaudio 				s->dxml = new_delta_xml(p, s->current, s);
3598ecbadc1Sclaudio 				s->state = RRDP_STATE_REQ;
3608ecbadc1Sclaudio 			}
3618ecbadc1Sclaudio 			break;
3628ecbadc1Sclaudio 		}
3638ecbadc1Sclaudio 	} else if (s->res == HTTP_NOT_MOD && s->task == NOTIFICATION) {
364f49178b1Sjob 		logx("%s: notification file not modified (%s#%lld)", s->local,
365b268327aSclaudio 		    s->repository->session_id, s->repository->serial);
3668ecbadc1Sclaudio 		/* no need to update state file */
3678ecbadc1Sclaudio 		rrdp_free(s);
3688ecbadc1Sclaudio 		rrdp_done(id, 1);
3698ecbadc1Sclaudio 	} else {
3708ecbadc1Sclaudio 		rrdp_failed(s);
3718ecbadc1Sclaudio 	}
3728ecbadc1Sclaudio }
3738ecbadc1Sclaudio 
3748ecbadc1Sclaudio static void
3754673c683Sclaudio rrdp_abort_req(struct rrdp *s)
3764673c683Sclaudio {
3774673c683Sclaudio 	unsigned int id = s->id;
3784673c683Sclaudio 
3794673c683Sclaudio 	s->aborted = 1;
3804673c683Sclaudio 	if (s->state == RRDP_STATE_REQ) {
3814673c683Sclaudio 		/* nothing is pending, just abort */
3824673c683Sclaudio 		rrdp_free(s);
3834673c683Sclaudio 		rrdp_done(id, 1);
3844673c683Sclaudio 		return;
3854673c683Sclaudio 	}
3864673c683Sclaudio 	if (s->state == RRDP_STATE_WAIT)
3874673c683Sclaudio 		/* wait for HTTP_INI which will progress the state */
3884673c683Sclaudio 		return;
3894673c683Sclaudio 
3904673c683Sclaudio 	/*
3914673c683Sclaudio 	 * RRDP_STATE_PARSE or later, close infd, abort parser but
3924673c683Sclaudio 	 * wait for HTTP_FIN and file_pending to drop to 0.
3934673c683Sclaudio 	 */
3944673c683Sclaudio 	if (s->infd != -1) {
3954673c683Sclaudio 		close(s->infd);
3964673c683Sclaudio 		s->infd = -1;
3974673c683Sclaudio 		s->state |= RRDP_STATE_PARSE_DONE | RRDP_STATE_PARSE_ERROR;
3984673c683Sclaudio 	}
3994673c683Sclaudio 	rrdp_finished(s);
4004673c683Sclaudio }
4014673c683Sclaudio 
4024673c683Sclaudio static void
403*b5fa5d51Sclaudio rrdp_input_handler(struct ibuf *b)
4048ecbadc1Sclaudio {
405b268327aSclaudio 	struct rrdp_session *state;
406b268327aSclaudio 	char *local, *notify, *last_mod;
4078ecbadc1Sclaudio 	struct rrdp *s;
4088ecbadc1Sclaudio 	enum rrdp_msg type;
4098ecbadc1Sclaudio 	enum http_result res;
410b6884e9fSclaudio 	unsigned int id;
4117eb79a4aSclaudio 	int ok;
4128ecbadc1Sclaudio 
4137eb79a4aSclaudio 	io_read_buf(b, &type, sizeof(type));
4147eb79a4aSclaudio 	io_read_buf(b, &id, sizeof(id));
4158ecbadc1Sclaudio 
4168ecbadc1Sclaudio 	switch (type) {
4178ecbadc1Sclaudio 	case RRDP_START:
418cf954fffSclaudio 		if (ibuf_fd_avail(b))
4197eb79a4aSclaudio 			errx(1, "received unexpected fd");
420b268327aSclaudio 		io_read_str(b, &local);
421b268327aSclaudio 		io_read_str(b, &notify);
422b268327aSclaudio 		state = rrdp_session_read(b);
423b268327aSclaudio 		rrdp_new(id, local, notify, state);
4248ecbadc1Sclaudio 		break;
4258ecbadc1Sclaudio 	case RRDP_HTTP_INI:
4268ecbadc1Sclaudio 		s = rrdp_get(id);
4278ecbadc1Sclaudio 		if (s == NULL)
4284673c683Sclaudio 			errx(1, "http ini, rrdp session %u does not exist", id);
4298ecbadc1Sclaudio 		if (s->state != RRDP_STATE_WAIT)
4308ecbadc1Sclaudio 			errx(1, "%s: bad internal state", s->local);
431cf954fffSclaudio 		s->infd = ibuf_fd_get(b);
432cf954fffSclaudio 		if (s->infd == -1)
433cf954fffSclaudio 			errx(1, "expected fd not received");
4348ecbadc1Sclaudio 		s->state = RRDP_STATE_PARSE;
4354673c683Sclaudio 		if (s->aborted) {
4364673c683Sclaudio 			rrdp_abort_req(s);
4374673c683Sclaudio 			break;
4384673c683Sclaudio 		}
4398ecbadc1Sclaudio 		break;
4408ecbadc1Sclaudio 	case RRDP_HTTP_FIN:
4417eb79a4aSclaudio 		io_read_buf(b, &res, sizeof(res));
4427eb79a4aSclaudio 		io_read_str(b, &last_mod);
443cf954fffSclaudio 		if (ibuf_fd_avail(b))
4448ecbadc1Sclaudio 			errx(1, "received unexpected fd");
4458ecbadc1Sclaudio 
4468ecbadc1Sclaudio 		s = rrdp_get(id);
4478ecbadc1Sclaudio 		if (s == NULL)
4484673c683Sclaudio 			errx(1, "http fin, rrdp session %u does not exist", id);
4498ecbadc1Sclaudio 		if (!(s->state & RRDP_STATE_PARSE))
4508ecbadc1Sclaudio 			errx(1, "%s: bad internal state", s->local);
4514673c683Sclaudio 		s->state |= RRDP_STATE_HTTP_DONE;
4528ecbadc1Sclaudio 		s->res = res;
453dcaa8a7cStb 		free(s->last_mod);
4548ecbadc1Sclaudio 		s->last_mod = last_mod;
4558ecbadc1Sclaudio 		rrdp_finished(s);
4568ecbadc1Sclaudio 		break;
4578ecbadc1Sclaudio 	case RRDP_FILE:
4588ecbadc1Sclaudio 		s = rrdp_get(id);
4598ecbadc1Sclaudio 		if (s == NULL)
460e3070a02Stb 			errx(1, "file, rrdp session %u does not exist", id);
461cf954fffSclaudio 		if (ibuf_fd_avail(b))
4627eb79a4aSclaudio 			errx(1, "received unexpected fd");
4637eb79a4aSclaudio 		io_read_buf(b, &ok, sizeof(ok));
4647eb79a4aSclaudio 		if (ok != 1)
4658ecbadc1Sclaudio 			s->file_failed++;
4668ecbadc1Sclaudio 		s->file_pending--;
4678ecbadc1Sclaudio 		if (s->file_pending == 0)
4688ecbadc1Sclaudio 			rrdp_finished(s);
4698ecbadc1Sclaudio 		break;
4704673c683Sclaudio 	case RRDP_ABORT:
471cf954fffSclaudio 		if (ibuf_fd_avail(b))
4724673c683Sclaudio 			errx(1, "received unexpected fd");
4734673c683Sclaudio 		s = rrdp_get(id);
4744673c683Sclaudio 		if (s != NULL)
4754673c683Sclaudio 			rrdp_abort_req(s);
4764673c683Sclaudio 		break;
4778ecbadc1Sclaudio 	default:
4788ecbadc1Sclaudio 		errx(1, "unexpected message %d", type);
4798ecbadc1Sclaudio 	}
4808ecbadc1Sclaudio }
4818ecbadc1Sclaudio 
4828ecbadc1Sclaudio static void
4838ecbadc1Sclaudio rrdp_data_handler(struct rrdp *s)
4848ecbadc1Sclaudio {
4858ecbadc1Sclaudio 	char buf[READ_BUF_SIZE];
4868ecbadc1Sclaudio 	XML_Parser p = s->parser;
4878ecbadc1Sclaudio 	ssize_t len;
4888ecbadc1Sclaudio 
4898ecbadc1Sclaudio 	len = read(s->infd, buf, sizeof(buf));
4908ecbadc1Sclaudio 	if (len == -1) {
4918ecbadc1Sclaudio 		warn("%s: read failure", s->local);
492334f0ce9Sclaudio 		rrdp_abort_req(s);
4938ecbadc1Sclaudio 		return;
4948ecbadc1Sclaudio 	}
4958ecbadc1Sclaudio 	if ((s->state & RRDP_STATE_PARSE) == 0)
4968ecbadc1Sclaudio 		errx(1, "%s: bad parser state", s->local);
4978ecbadc1Sclaudio 	if (len == 0) {
4988ecbadc1Sclaudio 		/* parser stage finished */
4998ecbadc1Sclaudio 		close(s->infd);
5008ecbadc1Sclaudio 		s->infd = -1;
5018ecbadc1Sclaudio 
5028ecbadc1Sclaudio 		if (s->task != NOTIFICATION) {
5038ecbadc1Sclaudio 			char h[SHA256_DIGEST_LENGTH];
5048ecbadc1Sclaudio 
5058ecbadc1Sclaudio 			SHA256_Final(h, &s->ctx);
5068ecbadc1Sclaudio 			if (memcmp(s->hash, h, sizeof(s->hash)) != 0) {
5078ecbadc1Sclaudio 				s->state |= RRDP_STATE_PARSE_ERROR;
5088ecbadc1Sclaudio 				warnx("%s: bad message digest", s->local);
5098ecbadc1Sclaudio 			}
5108ecbadc1Sclaudio 		}
5118ecbadc1Sclaudio 
5128ecbadc1Sclaudio 		s->state |= RRDP_STATE_PARSE_DONE;
5138ecbadc1Sclaudio 		rrdp_finished(s);
5148ecbadc1Sclaudio 		return;
5158ecbadc1Sclaudio 	}
5168ecbadc1Sclaudio 
5178ecbadc1Sclaudio 	/* parse and maybe hash the bytes just read */
5188ecbadc1Sclaudio 	if (s->task != NOTIFICATION)
5198ecbadc1Sclaudio 		SHA256_Update(&s->ctx, buf, len);
5208ecbadc1Sclaudio 	if ((s->state & RRDP_STATE_PARSE_ERROR) == 0 &&
5218ecbadc1Sclaudio 	    XML_Parse(p, buf, len, 0) != XML_STATUS_OK) {
5220ba844fdSclaudio 		warnx("%s: parse error at line %llu: %s", s->local,
5230ba844fdSclaudio 		    (unsigned long long)XML_GetCurrentLineNumber(p),
5248ecbadc1Sclaudio 		    XML_ErrorString(XML_GetErrorCode(p)));
5250ba844fdSclaudio 		s->state |= RRDP_STATE_PARSE_ERROR;
5268ecbadc1Sclaudio 	}
5278ecbadc1Sclaudio }
5288ecbadc1Sclaudio 
5298ecbadc1Sclaudio void
5308ecbadc1Sclaudio proc_rrdp(int fd)
5318ecbadc1Sclaudio {
5328ecbadc1Sclaudio 	struct pollfd pfds[MAX_SESSIONS + 1];
5338ecbadc1Sclaudio 	struct rrdp *s, *ns;
534*b5fa5d51Sclaudio 	struct ibuf *b;
5358ecbadc1Sclaudio 	size_t i;
5368ecbadc1Sclaudio 
5378ecbadc1Sclaudio 	if (pledge("stdio recvfd", NULL) == -1)
5388ecbadc1Sclaudio 		err(1, "pledge");
5398ecbadc1Sclaudio 
540*b5fa5d51Sclaudio 	if ((msgq = msgbuf_new_reader(sizeof(size_t), io_parse_hdr, NULL)) ==
541*b5fa5d51Sclaudio 	    NULL)
54225d36c5cSclaudio 		err(1, NULL);
5438ecbadc1Sclaudio 
5448ecbadc1Sclaudio 	for (;;) {
5458ecbadc1Sclaudio 		i = 1;
54672429ad6Sclaudio 		memset(&pfds, 0, sizeof(pfds));
5478ecbadc1Sclaudio 		TAILQ_FOREACH(s, &states, entry) {
5488ecbadc1Sclaudio 			if (i >= MAX_SESSIONS + 1) {
5498ecbadc1Sclaudio 				/* not enough sessions, wait for better times */
5508ecbadc1Sclaudio 				s->pfd = NULL;
5518ecbadc1Sclaudio 				continue;
5528ecbadc1Sclaudio 			}
5538ecbadc1Sclaudio 			/* request new assets when there are free sessions */
5548ecbadc1Sclaudio 			if (s->state == RRDP_STATE_REQ) {
5558ecbadc1Sclaudio 				const char *uri;
5568ecbadc1Sclaudio 				switch (s->task) {
5578ecbadc1Sclaudio 				case NOTIFICATION:
5588ecbadc1Sclaudio 					rrdp_http_req(s->id, s->notifyuri,
559b268327aSclaudio 					    s->repository->last_mod);
5608ecbadc1Sclaudio 					break;
5618ecbadc1Sclaudio 				case SNAPSHOT:
5628ecbadc1Sclaudio 				case DELTA:
5638ecbadc1Sclaudio 					uri = notification_get_next(s->nxml,
5648ecbadc1Sclaudio 					    s->hash, sizeof(s->hash),
5658ecbadc1Sclaudio 					    s->task);
5668ecbadc1Sclaudio 					SHA256_Init(&s->ctx);
5678ecbadc1Sclaudio 					rrdp_http_req(s->id, uri, NULL);
5688ecbadc1Sclaudio 					break;
5698ecbadc1Sclaudio 				}
5708ecbadc1Sclaudio 				s->state = RRDP_STATE_WAIT;
5718ecbadc1Sclaudio 			}
5728ecbadc1Sclaudio 			s->pfd = pfds + i++;
5738ecbadc1Sclaudio 			s->pfd->fd = s->infd;
5748ecbadc1Sclaudio 			s->pfd->events = POLLIN;
5758ecbadc1Sclaudio 		}
5768ecbadc1Sclaudio 
5778ecbadc1Sclaudio 		/*
5788ecbadc1Sclaudio 		 * Update main fd last.
5798ecbadc1Sclaudio 		 * The previous loop may have enqueue messages.
5808ecbadc1Sclaudio 		 */
5818ecbadc1Sclaudio 		pfds[0].fd = fd;
5828ecbadc1Sclaudio 		pfds[0].events = POLLIN;
58325d36c5cSclaudio 		if (msgbuf_queuelen(msgq) > 0)
5848ecbadc1Sclaudio 			pfds[0].events |= POLLOUT;
5858ecbadc1Sclaudio 
5867ba5db23Sclaudio 		if (poll(pfds, i, INFTIM) == -1) {
5877ba5db23Sclaudio 			if (errno == EINTR)
5887ba5db23Sclaudio 				continue;
5898ecbadc1Sclaudio 			err(1, "poll");
5907ba5db23Sclaudio 		}
5918ecbadc1Sclaudio 
5928ecbadc1Sclaudio 		if (pfds[0].revents & POLLHUP)
5938ecbadc1Sclaudio 			break;
5948ecbadc1Sclaudio 		if (pfds[0].revents & POLLOUT) {
59525d36c5cSclaudio 			if (msgbuf_write(fd, msgq) == -1) {
5969aadc625Sclaudio 				if (errno == EPIPE)
5978ecbadc1Sclaudio 					errx(1, "write: connection closed");
5989aadc625Sclaudio 				else
5998ecbadc1Sclaudio 					err(1, "write");
6008ecbadc1Sclaudio 			}
6018ecbadc1Sclaudio 		}
602*b5fa5d51Sclaudio 		if (pfds[0].revents & POLLIN) {
603*b5fa5d51Sclaudio 			switch (msgbuf_read(fd, msgq)) {
604*b5fa5d51Sclaudio 			case -1:
605*b5fa5d51Sclaudio 				err(1, "msgbuf_read");
606*b5fa5d51Sclaudio 			case 0:
607*b5fa5d51Sclaudio 				errx(1, "msgbuf_read: connection closed");
608*b5fa5d51Sclaudio 			}
609*b5fa5d51Sclaudio 			while ((b = io_buf_get(msgq)) != NULL) {
610*b5fa5d51Sclaudio 				rrdp_input_handler(b);
611*b5fa5d51Sclaudio 				ibuf_free(b);
612*b5fa5d51Sclaudio 			}
613*b5fa5d51Sclaudio 		}
6148ecbadc1Sclaudio 
6158ecbadc1Sclaudio 		TAILQ_FOREACH_SAFE(s, &states, entry, ns) {
6168ecbadc1Sclaudio 			if (s->pfd == NULL)
6178ecbadc1Sclaudio 				continue;
618c494faa7Sclaudio 			if (s->pfd->revents != 0)
6198ecbadc1Sclaudio 				rrdp_data_handler(s);
6208ecbadc1Sclaudio 		}
6218ecbadc1Sclaudio 	}
6228ecbadc1Sclaudio 
6238ecbadc1Sclaudio 	exit(0);
6248ecbadc1Sclaudio }
625