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, ¬ify); 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