1/* $OpenBSD: rrdp_snapshot.c,v 1.10 2024/05/30 09:54:59 job 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 <err.h> 20#include <limits.h> 21#include <stdlib.h> 22#include <string.h> 23#include <stdio.h> 24#include <unistd.h> 25 26#include <expat.h> 27 28#include "extern.h" 29#include "rrdp.h" 30 31enum snapshot_scope { 32 SNAPSHOT_SCOPE_NONE, 33 SNAPSHOT_SCOPE_SNAPSHOT, 34 SNAPSHOT_SCOPE_PUBLISH, 35 SNAPSHOT_SCOPE_END 36}; 37 38struct snapshot_xml { 39 XML_Parser parser; 40 struct rrdp_session *current; 41 struct rrdp *rrdp; 42 struct publish_xml *pxml; 43 char *session_id; 44 long long serial; 45 int version; 46 enum snapshot_scope scope; 47}; 48 49static void 50start_snapshot_elem(struct snapshot_xml *sxml, const char **attr) 51{ 52 XML_Parser p = sxml->parser; 53 int has_xmlns = 0; 54 int i; 55 56 if (sxml->scope != SNAPSHOT_SCOPE_NONE) 57 PARSE_FAIL(p, 58 "parse failed - entered snapshot elem unexpectedely"); 59 for (i = 0; attr[i]; i += 2) { 60 const char *errstr; 61 if (strcmp("xmlns", attr[i]) == 0 && 62 strcmp(RRDP_XMLNS, attr[i + 1]) == 0) { 63 has_xmlns = 1; 64 continue; 65 } 66 if (strcmp("version", attr[i]) == 0) { 67 sxml->version = strtonum(attr[i + 1], 68 1, MAX_VERSION, &errstr); 69 if (errstr == NULL) 70 continue; 71 } 72 if (strcmp("session_id", attr[i]) == 0 && 73 valid_uuid(attr[i + 1])) { 74 sxml->session_id = xstrdup(attr[i + 1]); 75 continue; 76 } 77 if (strcmp("serial", attr[i]) == 0) { 78 sxml->serial = strtonum(attr[i + 1], 79 1, LLONG_MAX, &errstr); 80 if (errstr == NULL) 81 continue; 82 } 83 PARSE_FAIL(p, 84 "parse failed - non conforming " 85 "attribute '%s' found in snapshot elem", attr[i]); 86 } 87 if (!(has_xmlns && sxml->version && sxml->session_id && sxml->serial)) 88 PARSE_FAIL(p, 89 "parse failed - incomplete snapshot attributes"); 90 if (strcmp(sxml->current->session_id, sxml->session_id) != 0) 91 PARSE_FAIL(p, "parse failed - session_id mismatch"); 92 if (sxml->current->serial != sxml->serial) 93 PARSE_FAIL(p, "parse failed - serial mismatch"); 94 95 sxml->scope = SNAPSHOT_SCOPE_SNAPSHOT; 96} 97 98static void 99end_snapshot_elem(struct snapshot_xml *sxml) 100{ 101 XML_Parser p = sxml->parser; 102 103 if (sxml->scope != SNAPSHOT_SCOPE_SNAPSHOT) 104 PARSE_FAIL(p, "parse failed - exited snapshot " 105 "elem unexpectedely"); 106 sxml->scope = SNAPSHOT_SCOPE_END; 107} 108 109static void 110start_publish_elem(struct snapshot_xml *sxml, const char **attr) 111{ 112 XML_Parser p = sxml->parser; 113 char *uri = NULL; 114 int i, hasUri = 0; 115 116 if (sxml->scope != SNAPSHOT_SCOPE_SNAPSHOT) 117 PARSE_FAIL(p, 118 "parse failed - entered publish elem unexpectedely"); 119 for (i = 0; attr[i]; i += 2) { 120 if (strcmp("uri", attr[i]) == 0 && hasUri++ == 0) { 121 if (valid_uri(attr[i + 1], strlen(attr[i + 1]), 122 RSYNC_PROTO)) { 123 uri = xstrdup(attr[i + 1]); 124 continue; 125 } 126 } 127 /* 128 * XXX it seems people can not write proper XML, ignore 129 * bogus xmlns attribute on publish elements. 130 */ 131 if (strcmp("xmlns", attr[i]) == 0) 132 continue; 133 PARSE_FAIL(p, "parse failed - non conforming" 134 " attribute '%s' found in publish elem", attr[i]); 135 } 136 if (hasUri != 1) 137 PARSE_FAIL(p, "parse failed - incomplete publish attributes"); 138 sxml->pxml = new_publish_xml(PUB_ADD, uri, NULL, 0); 139 sxml->scope = SNAPSHOT_SCOPE_PUBLISH; 140} 141 142static void 143end_publish_elem(struct snapshot_xml *sxml) 144{ 145 XML_Parser p = sxml->parser; 146 147 if (sxml->scope != SNAPSHOT_SCOPE_PUBLISH) 148 PARSE_FAIL(p, "parse failed - exited publish " 149 "elem unexpectedely"); 150 151 if (publish_done(sxml->rrdp, sxml->pxml) != 0) 152 PARSE_FAIL(p, "parse failed - bad publish elem"); 153 sxml->pxml = NULL; 154 155 sxml->scope = SNAPSHOT_SCOPE_SNAPSHOT; 156} 157 158static void 159snapshot_xml_elem_start(void *data, const char *el, const char **attr) 160{ 161 struct snapshot_xml *sxml = data; 162 XML_Parser p = sxml->parser; 163 164 /* 165 * Can only enter here once as we should have no ways to get back to 166 * NONE scope 167 */ 168 if (strcmp("snapshot", el) == 0) 169 start_snapshot_elem(sxml, attr); 170 /* 171 * Will enter here multiple times, BUT never nested. will start 172 * collecting character data in that handler mem is cleared in end 173 * block, (TODO or on parse failure) 174 */ 175 else if (strcmp("publish", el) == 0) 176 start_publish_elem(sxml, attr); 177 else 178 PARSE_FAIL(p, "parse failed - unexpected elem exit found"); 179} 180 181static void 182snapshot_xml_elem_end(void *data, const char *el) 183{ 184 struct snapshot_xml *sxml = data; 185 XML_Parser p = sxml->parser; 186 187 if (strcmp("snapshot", el) == 0) 188 end_snapshot_elem(sxml); 189 else if (strcmp("publish", el) == 0) 190 end_publish_elem(sxml); 191 else 192 PARSE_FAIL(p, "parse failed - unexpected elem exit found"); 193} 194 195static void 196snapshot_content_handler(void *data, const char *content, int length) 197{ 198 struct snapshot_xml *sxml = data; 199 XML_Parser p = sxml->parser; 200 201 if (sxml->scope == SNAPSHOT_SCOPE_PUBLISH) 202 if (publish_add_content(sxml->pxml, content, length) == -1) 203 PARSE_FAIL(p, "parse failed, snapshot element for %s " 204 "too big", sxml->pxml->uri); 205} 206 207static void 208snapshot_doctype_handler(void *data, const char *doctypeName, 209 const char *sysid, const char *pubid, int subset) 210{ 211 struct snapshot_xml *sxml = data; 212 XML_Parser p = sxml->parser; 213 214 PARSE_FAIL(p, "parse failed - DOCTYPE not allowed"); 215} 216 217struct snapshot_xml * 218new_snapshot_xml(XML_Parser p, struct rrdp_session *rs, struct rrdp *r) 219{ 220 struct snapshot_xml *sxml; 221 222 if ((sxml = calloc(1, sizeof(*sxml))) == NULL) 223 err(1, "%s", __func__); 224 sxml->parser = p; 225 sxml->current = rs; 226 sxml->rrdp = r; 227 228 if (XML_ParserReset(sxml->parser, "US-ASCII") != XML_TRUE) 229 errx(1, "%s: XML_ParserReset failed", __func__); 230 231 XML_SetElementHandler(sxml->parser, snapshot_xml_elem_start, 232 snapshot_xml_elem_end); 233 XML_SetCharacterDataHandler(sxml->parser, snapshot_content_handler); 234 XML_SetUserData(sxml->parser, sxml); 235 XML_SetDoctypeDeclHandler(sxml->parser, snapshot_doctype_handler, 236 NULL); 237 238 return sxml; 239} 240 241void 242free_snapshot_xml(struct snapshot_xml *sxml) 243{ 244 if (sxml == NULL) 245 return; 246 247 free(sxml->session_id); 248 free_publish_xml(sxml->pxml); 249 free(sxml); 250} 251 252/* Used in regress. */ 253void 254log_snapshot_xml(struct snapshot_xml *sxml) 255{ 256 logx("scope: %d", sxml->scope); 257 logx("version: %d", sxml->version); 258 logx("session_id: %s serial: %lld", sxml->session_id, sxml->serial); 259} 260