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
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
start_snapshot_elem(struct snapshot_xml * sxml,const char ** attr)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
end_snapshot_elem(struct snapshot_xml * sxml)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
start_publish_elem(struct snapshot_xml * sxml,const char ** attr)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
end_publish_elem(struct snapshot_xml * sxml)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
snapshot_xml_elem_start(void * data,const char * el,const char ** attr)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
snapshot_xml_elem_end(void * data,const char * el)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
snapshot_content_handler(void * data,const char * content,int length)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, snapshot element for %s "
204 "too big", sxml->pxml->uri);
205 }
206
207 static void
snapshot_doctype_handler(void * data,const char * doctypeName,const char * sysid,const char * pubid,int subset)208 snapshot_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
217 struct snapshot_xml *
new_snapshot_xml(XML_Parser p,struct rrdp_session * rs,struct rrdp * r)218 new_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
241 void
free_snapshot_xml(struct snapshot_xml * sxml)242 free_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. */
253 void
log_snapshot_xml(struct snapshot_xml * sxml)254 log_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