xref: /openbsd/usr.sbin/bgplgd/qs.c (revision e8adb3e3)
1 /*	$OpenBSD: qs.c,v 1.5 2023/05/09 14:35:45 claudio Exp $ */
2 /*
3  * Copyright (c) 2020 Claudio Jeker <claudio@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <ctype.h>
21 #include <netdb.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include "bgplgd.h"
26 #include "slowcgi.h"
27 
28 enum qs_type {
29 	ONE,
30 	STRING,
31 	PREFIX,
32 	NUMBER,
33 	FAMILY,
34 	OVS,
35 	AVS,
36 };
37 
38 const struct qs {
39 	unsigned int	qs;
40 	const char	*key;
41 	enum qs_type	type;
42 } qsargs[] = {
43 	{ QS_NEIGHBOR, "neighbor", STRING, },
44 	{ QS_GROUP, "group", STRING },
45 	{ QS_AS, "as", NUMBER },
46 	{ QS_PREFIX, "prefix", PREFIX },
47 	{ QS_COMMUNITY, "community", STRING },
48 	{ QS_EXTCOMMUNITY, "ext-community", STRING },
49 	{ QS_LARGECOMMUNITY, "large-community", STRING },
50 	{ QS_AF, "af", FAMILY },
51 	{ QS_RIB, "rib", STRING },
52 	{ QS_OVS, "ovs", OVS },
53 	{ QS_BEST, "best", ONE },
54 	{ QS_ALL, "all", ONE },
55 	{ QS_SHORTER, "or-shorter", ONE },
56 	{ QS_ERROR, "error", ONE },
57 	{ QS_AVS, "avs", AVS },
58 	{ QS_INVALID, "invalid", ONE },
59 	{ QS_LEAKED, "leaked", ONE },
60 	{ 0, NULL }
61 };
62 
63 const char *qs2str(unsigned int qs);
64 
65 static int
hex(char x)66 hex(char x)
67 {
68 	if ('0' <= x && x <= '9')
69 		return x - '0';
70 	if ('a' <= x && x <= 'f')
71 		return x - 'a' + 10;
72 	else
73 		return x - 'A' + 10;
74 }
75 
76 static char *
urldecode(const char * s,size_t len)77 urldecode(const char *s, size_t len)
78 {
79 	static char buf[256];
80 	size_t i, blen = 0;
81 
82 	for (i = 0; i < len; i++) {
83 		if (blen >= sizeof(buf))
84 			return NULL;
85 		if (s[i] == '+') {
86 			buf[blen++] = ' ';
87 		} else if (s[i] == '%' && i + 2 < len) {
88 			if (isxdigit((unsigned char)s[i + 1]) &&
89 			    isxdigit((unsigned char)s[i + 2])) {
90 				char c;
91 				c = hex(s[i + 1]) << 4 | hex(s[i + 2]);
92 				/* replace NUL chars with space */
93 				if (c == 0)
94 					c = ' ';
95 				buf[blen++] = c;
96 				i += 2;
97 			} else
98 				buf[blen++] = s[i];
99 		} else {
100 			buf[blen++] = s[i];
101 		}
102 	}
103 	buf[blen] = '\0';
104 
105 	return buf;
106 }
107 
108 static int
valid_string(const char * str)109 valid_string(const char *str)
110 {
111 	unsigned char c;
112 
113 	while ((c = *str++) != '\0')
114 		if (!isalnum(c) && !ispunct(c) && c != ' ')
115 			return 0;
116 	return 1;
117 }
118 
119 /* validate that the input is pure decimal number */
120 static int
valid_number(const char * str)121 valid_number(const char *str)
122 {
123 	unsigned char c;
124 	int first = 1;
125 
126 	while ((c = *str++) != '\0') {
127 		/* special handling of 0 */
128 		if (first && c == '0') {
129 			if (*str != '\0')
130 				return 0;
131 		}
132 		first = 0;
133 		if (!isdigit(c))
134 			return 0;
135 	}
136 	return 1;
137 }
138 
139 /* validate a prefix, does not support old 10/8 notation but that is ok */
140 static int
valid_prefix(char * str)141 valid_prefix(char *str)
142 {
143 	struct addrinfo hints, *res;
144 	char *p;
145 	int mask;
146 
147 	if ((p = strrchr(str, '/')) != NULL) {
148 		const char *errstr;
149 		mask = strtonum(p+1, 0, 128, &errstr);
150 		if (errstr)
151 			return 0;
152 		p[0] = '\0';
153 	}
154 
155 	memset(&hints, 0, sizeof(hints));
156 	hints.ai_family = AF_UNSPEC;
157 	hints.ai_socktype = SOCK_DGRAM;
158 	hints.ai_flags = AI_NUMERICHOST;
159 	if (getaddrinfo(str, NULL, &hints, &res) != 0)
160 		return 0;
161 	if (p) {
162 		if (res->ai_family == AF_INET && mask > 32)
163 			return 0;
164 		p[0] = '/';
165 	}
166 	freeaddrinfo(res);
167 	return 1;
168 }
169 
170 static int
parse_value(struct lg_ctx * ctx,unsigned int qs,enum qs_type type,char * val)171 parse_value(struct lg_ctx *ctx, unsigned int qs, enum qs_type type, char *val)
172 {
173 	/* val can only be NULL if urldecode failed. */
174 	if (val == NULL) {
175 		lwarnx("urldecode of querystring failed");
176 		return 400;
177 	}
178 
179 	switch (type) {
180 	case ONE:
181 		if (strcmp("1", val) == 0) {
182 			ctx->qs_args[qs].one = 1;
183 		} else if (strcmp("0", val) == 0) {
184 			/* silently ignored */
185 		} else {
186 			lwarnx("%s: bad value %s expected 1", qs2str(qs), val);
187 			return 400;
188 		}
189 		break;
190 	case STRING:
191 		/* limit string to limited ascii chars */
192 		if (!valid_string(val)) {
193 			lwarnx("%s: bad string", qs2str(qs));
194 			return 400;
195 		}
196 		ctx->qs_args[qs].string = strdup(val);
197 		if (ctx->qs_args[qs].string == NULL) {
198 			lwarn("parse_value");
199 			return 500;
200 		}
201 		break;
202 	case NUMBER:
203 		if (!valid_number(val)) {
204 			lwarnx("%s: bad number", qs2str(qs));
205 			return 400;
206 		}
207 		ctx->qs_args[qs].string = strdup(val);
208 		if (ctx->qs_args[qs].string == NULL) {
209 			lwarn("parse_value");
210 			return 500;
211 		}
212 		break;
213 	case PREFIX:
214 		if (!valid_prefix(val)) {
215 			lwarnx("%s: bad prefix", qs2str(qs));
216 			return 400;
217 		}
218 		ctx->qs_args[qs].string = strdup(val);
219 		if (ctx->qs_args[qs].string == NULL) {
220 			lwarn("parse_value");
221 			return 500;
222 		}
223 		break;
224 	case FAMILY:
225 		if (strcasecmp("ipv4", val) == 0 ||
226 		    strcasecmp("ipv6", val) == 0 ||
227 		    strcasecmp("vpnv4", val) == 0 ||
228 		    strcasecmp("vpnv6", val) == 0) {
229 			ctx->qs_args[qs].string = strdup(val);
230 			if (ctx->qs_args[qs].string == NULL) {
231 				lwarn("parse_value");
232 				return 500;
233 			}
234 		} else {
235 			lwarnx("%s: bad value %s", qs2str(qs), val);
236 			return 400;
237 		}
238 		break;
239 	case OVS:
240 		if (strcmp("not-found", val) == 0 ||
241 		    strcmp("valid", val) == 0 ||
242 		    strcmp("invalid", val) == 0) {
243 			ctx->qs_args[qs].string = strdup(val);
244 			if (ctx->qs_args[qs].string == NULL) {
245 				lwarn("parse_value");
246 				return 500;
247 			}
248 		} else {
249 			lwarnx("%s: bad OVS value %s", qs2str(qs), val);
250 			return 400;
251 		}
252 		break;
253 	case AVS:
254 		if (strcmp("unknown", val) == 0 ||
255 		    strcmp("valid", val) == 0 ||
256 		    strcmp("invalid", val) == 0) {
257 			ctx->qs_args[qs].string = strdup(val);
258 			if (ctx->qs_args[qs].string == NULL) {
259 				lwarn("parse_value");
260 				return 500;
261 			}
262 		} else {
263 			lwarnx("%s: bad AVS value %s", qs2str(qs), val);
264 			return 400;
265 		}
266 		break;
267 	}
268 
269 	return 0;
270 }
271 
272 int
parse_querystring(const char * param,struct lg_ctx * ctx)273 parse_querystring(const char *param, struct lg_ctx *ctx)
274 {
275 	size_t len, i;
276 	int rv;
277 
278 	while (param && *param) {
279 		len = strcspn(param, "=");
280 		for (i = 0; qsargs[i].key != NULL; i++)
281 			if (strncmp(qsargs[i].key, param, len) == 0)
282 				break;
283 		if (qsargs[i].key == NULL) {
284 			lwarnx("unknown querystring key %.*s", (int)len, param);
285 			return 400;
286 		}
287 		if (((1 << qsargs[i].qs) & ctx->qs_mask) == 0) {
288 			lwarnx("querystring param %s not allowed for command",
289 			    qsargs[i].key);
290 			return 400;
291 		}
292 		if (((1 << qsargs[i].qs) & ctx->qs_set) != 0) {
293 			lwarnx("querystring param %s already set",
294 			    qsargs[i].key);
295 			return 400;
296 		}
297 		ctx->qs_set |= (1 << qsargs[i].qs);
298 
299 		if (param[len] != '=') {
300 			lwarnx("querystring %s without value", qsargs[i].key);
301 			return 400;
302 		}
303 
304 		param += len + 1;
305 		len = strcspn(param, "&");
306 
307 		if ((rv = parse_value(ctx, qsargs[i].qs, qsargs[i].type,
308 		    urldecode(param, len))) != 0)
309 			return rv;
310 
311 		param += len;
312 		if (*param == '&')
313 			param++;
314 	}
315 
316 	return 0;
317 }
318 
319 size_t
qs_argv(char ** argv,size_t argc,size_t len,struct lg_ctx * ctx,int barenbr)320 qs_argv(char **argv, size_t argc, size_t len, struct lg_ctx *ctx, int barenbr)
321 {
322 	/* keep space for the final NULL in argv */
323 	len -= 1;
324 
325 	/* NEIGHBOR and GROUP are exclusive */
326 	if (ctx->qs_set & (1 << QS_NEIGHBOR)) {
327 		if (!barenbr)
328 			if (argc < len)
329 				argv[argc++] = "neighbor";
330 		if (argc < len)
331 			argv[argc++] = ctx->qs_args[QS_NEIGHBOR].string;
332 	} else if (ctx->qs_set & (1 << QS_GROUP)) {
333 		if (argc < len)
334 			argv[argc++] = "group";
335 		if (argc < len)
336 			argv[argc++] = ctx->qs_args[QS_GROUP].string;
337 	}
338 
339 	if (ctx->qs_set & (1 << QS_AS)) {
340 		if (argc < len)
341 			argv[argc++] = "source-as";
342 		if (argc < len)
343 			argv[argc++] = ctx->qs_args[QS_AS].string;
344 	}
345 	if (ctx->qs_set & (1 << QS_COMMUNITY)) {
346 		if (argc < len)
347 			argv[argc++] = "community";
348 		if (argc < len)
349 			argv[argc++] = ctx->qs_args[QS_COMMUNITY].string;
350 	}
351 	if (ctx->qs_set & (1 << QS_EXTCOMMUNITY)) {
352 		if (argc < len)
353 			argv[argc++] = "ext-community";
354 		if (argc < len)
355 			argv[argc++] = ctx->qs_args[QS_EXTCOMMUNITY].string;
356 	}
357 	if (ctx->qs_set & (1 << QS_LARGECOMMUNITY)) {
358 		if (argc < len)
359 			argv[argc++] = "large-community";
360 		if (argc < len)
361 			argv[argc++] = ctx->qs_args[QS_LARGECOMMUNITY].string;
362 	}
363 	if (ctx->qs_set & (1 << QS_AF)) {
364 		if (argc < len)
365 			argv[argc++] = ctx->qs_args[QS_AF].string;
366 	}
367 	if (ctx->qs_set & (1 << QS_RIB)) {
368 		if (argc < len)
369 			argv[argc++] = "rib";
370 		if (argc < len)
371 			argv[argc++] = ctx->qs_args[QS_RIB].string;
372 	}
373 	if (ctx->qs_set & (1 << QS_OVS)) {
374 		if (argc < len)
375 			argv[argc++] = "ovs";
376 		if (argc < len)
377 			argv[argc++] = ctx->qs_args[QS_OVS].string;
378 	}
379 	if (ctx->qs_set & (1 << QS_AVS)) {
380 		if (argc < len)
381 			argv[argc++] = "avs";
382 		if (argc < len)
383 			argv[argc++] = ctx->qs_args[QS_AVS].string;
384 	}
385 	/* BEST, ERROR, INVALID and LEAKED are exclusive */
386 	if (ctx->qs_args[QS_BEST].one) {
387 		if (argc < len)
388 			argv[argc++] = "best";
389 	} else if (ctx->qs_args[QS_ERROR].one) {
390 		if (argc < len)
391 			argv[argc++] = "error";
392 	} else if (ctx->qs_args[QS_INVALID].one) {
393 		if (argc < len)
394 			argv[argc++] = "disqualified";
395 	} else if (ctx->qs_args[QS_LEAKED].one) {
396 		if (argc < len)
397 			argv[argc++] = "leaked";
398 	}
399 
400 	/* prefix must be last for show rib */
401 	if (ctx->qs_set & (1 << QS_PREFIX)) {
402 		if (argc < len)
403 			argv[argc++] = ctx->qs_args[QS_PREFIX].string;
404 
405 		/* ALL and SHORTER are exclusive */
406 		if (ctx->qs_args[QS_ALL].one) {
407 			if (argc < len)
408 				argv[argc++] = "all";
409 		} else if (ctx->qs_args[QS_SHORTER].one) {
410 			if (argc < len)
411 				argv[argc++] = "or-shorter";
412 		}
413 	}
414 
415 	if (argc >= len)
416 		lwarnx("hit limit of argv in qs_argv");
417 
418 	return argc;
419 }
420 
421 const char *
qs2str(unsigned int qs)422 qs2str(unsigned int qs)
423 {
424 	size_t i;
425 
426 	for (i = 0; qsargs[i].key != NULL; i++)
427 		if (qsargs[i].qs == qs)
428 			return qsargs[i].key;
429 
430 	lerrx(1, "unknown querystring param %d", qs);
431 }
432