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