1 /* $OpenBSD: rrdp_snapshot.c,v 1.9 2024/03/22 03:38:12 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 31 enum snapshot_scope { 32 SNAPSHOT_SCOPE_NONE, 33 SNAPSHOT_SCOPE_SNAPSHOT, 34 SNAPSHOT_SCOPE_PUBLISH, 35 SNAPSHOT_SCOPE_END 36 }; 37 38 struct 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 49 static void 50 start_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 98 static void 99 end_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 109 static void 110 start_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 142 static void 143 end_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 158 static void 159 snapshot_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 181 static void 182 snapshot_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 195 static void 196 snapshot_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 - content too big"); 204 } 205 206 static void 207 snapshot_doctype_handler(void *data, const char *doctypeName, 208 const char *sysid, const char *pubid, int subset) 209 { 210 struct snapshot_xml *sxml = data; 211 XML_Parser p = sxml->parser; 212 213 PARSE_FAIL(p, "parse failed - DOCTYPE not allowed"); 214 } 215 216 struct snapshot_xml * 217 new_snapshot_xml(XML_Parser p, struct rrdp_session *rs, struct rrdp *r) 218 { 219 struct snapshot_xml *sxml; 220 221 if ((sxml = calloc(1, sizeof(*sxml))) == NULL) 222 err(1, "%s", __func__); 223 sxml->parser = p; 224 sxml->current = rs; 225 sxml->rrdp = r; 226 227 if (XML_ParserReset(sxml->parser, "US-ASCII") != XML_TRUE) 228 errx(1, "%s: XML_ParserReset failed", __func__); 229 230 XML_SetElementHandler(sxml->parser, snapshot_xml_elem_start, 231 snapshot_xml_elem_end); 232 XML_SetCharacterDataHandler(sxml->parser, snapshot_content_handler); 233 XML_SetUserData(sxml->parser, sxml); 234 XML_SetDoctypeDeclHandler(sxml->parser, snapshot_doctype_handler, 235 NULL); 236 237 return sxml; 238 } 239 240 void 241 free_snapshot_xml(struct snapshot_xml *sxml) 242 { 243 if (sxml == NULL) 244 return; 245 246 free(sxml->session_id); 247 free_publish_xml(sxml->pxml); 248 free(sxml); 249 } 250 251 /* Used in regress. */ 252 void 253 log_snapshot_xml(struct snapshot_xml *sxml) 254 { 255 logx("scope: %d", sxml->scope); 256 logx("version: %d", sxml->version); 257 logx("session_id: %s serial: %lld", sxml->session_id, sxml->serial); 258 } 259