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