1 /* $OpenBSD: bgplg.c,v 1.20 2024/02/09 12:56:53 claudio Exp $ */
2
3 /*
4 * Copyright (c) 2005, 2006 Reyk Floeter <reyk@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 <sys/types.h>
20 #include <sys/stat.h>
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <signal.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <limits.h>
28 #include <ctype.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <err.h>
32
33 #include "bgplg.h"
34
35 #define INC_STYLE "/conf/bgplg.css"
36 #define INC_HEAD "/conf/bgplg.head"
37 #define INC_FOOT "/conf/bgplg.foot"
38
39 #define BGPDSOCK "/run/bgpd.rsock"
40 #define BGPCTL "/bin/bgpctl", "-s", BGPDSOCK
41 #define PING "/bin/ping"
42 #define TRACEROUTE "/bin/traceroute"
43 #define PING6 "/bin/ping6"
44 #define TRACEROUTE6 "/bin/traceroute6"
45 #define CONTENT_TYPE "text/html"
46
47 static struct cmd cmds[] = CMDS;
48
49 char *lg_getenv(const char *, int *);
50 void lg_urldecode(char *);
51 char **lg_arg2argv(char *, int *);
52 char **lg_argextra(char **, int, struct cmd *);
53 char *lg_getarg(const char *, char *, int);
54 int lg_incl(const char *);
55
56 void
lg_urldecode(char * str)57 lg_urldecode(char *str)
58 {
59 size_t i, c, len;
60 char code[3];
61 long result;
62
63 if (str && *str) {
64 len = strlen(str);
65 i = c = 0;
66 while (i < len) {
67 if (str[i] == '%' && i <= (len - 2)) {
68 if (isxdigit((unsigned char)str[i + 1]) &&
69 isxdigit((unsigned char)str[i + 2])) {
70 code[0] = str[i + 1];
71 code[1] = str[i + 2];
72 code[2] = 0;
73 result = strtol(code, NULL, 16);
74 /* Replace NUL chars with a space */
75 if (result == 0)
76 result = ' ';
77 str[c++] = result;
78 i += 3;
79 } else {
80 str[c++] = '%';
81 i++;
82 }
83 } else if (str[i] == '+') {
84 str[i] = ' ';
85 } else {
86 if (c != i)
87 str[c] = str[i];
88 c++;
89 i++;
90 }
91 }
92 str[c] = 0x0;
93 }
94 }
95
96 char *
lg_getenv(const char * name,int * lenp)97 lg_getenv(const char *name, int *lenp)
98 {
99 size_t len;
100 u_int i;
101 char *ptr;
102
103 if ((ptr = getenv(name)) == NULL)
104 return (NULL);
105
106 lg_urldecode(ptr);
107
108 if (!(len = strlen(ptr)))
109 return (NULL);
110
111 if (lenp != NULL)
112 *lenp = len;
113
114 #define allowed_in_string(_x) \
115 (isalnum((unsigned char)_x) || strchr("-_.:/= ", _x))
116
117 for (i = 0; i < len; i++) {
118 if (ptr[i] == '&')
119 ptr[i] = '\0';
120 if (!allowed_in_string(ptr[i])) {
121 printf("invalid character in input\n");
122 return (NULL);
123 }
124 }
125
126 return (ptr);
127 #undef allowed_in_string
128 }
129
130 char *
lg_getarg(const char * name,char * arg,int len)131 lg_getarg(const char *name, char *arg, int len)
132 {
133 char *ptr = arg;
134 size_t namelen, ptrlen;
135 int i;
136
137 namelen = strlen(name);
138
139 for (i = 0; i < len; i++) {
140 if (arg[i] == '\0')
141 continue;
142 ptr = arg + i;
143 ptrlen = strlen(ptr);
144 if (namelen >= ptrlen)
145 continue;
146 if (strncmp(name, ptr, namelen) == 0)
147 return (ptr + namelen);
148 }
149
150 return (NULL);
151 }
152
153 char **
lg_arg2argv(char * arg,int * argc)154 lg_arg2argv(char *arg, int *argc)
155 {
156 char **argv, *ptr = arg;
157 size_t len;
158 u_int i, c = 1;
159
160 len = strlen(arg);
161
162 /* Count elements */
163 for (i = 0; i < len; i++) {
164 if (isspace((unsigned char)arg[i])) {
165 /* filter out additional options */
166 if (arg[i + 1] == '-') {
167 printf("invalid input\n");
168 return (NULL);
169 }
170 arg[i] = '\0';
171 c++;
172 }
173 }
174
175 /* Generate array */
176 if ((argv = calloc(c + 1, sizeof(char *))) == NULL) {
177 printf("fatal error: %s\n", strerror(errno));
178 return (NULL);
179 }
180
181 argv[c] = NULL;
182 *argc = c;
183
184 /* Fill array */
185 for (i = c = 0; i < len; i++) {
186 if (arg[i] == '\0' || i == 0) {
187 if (i != 0)
188 ptr = &arg[i + 1];
189 argv[c++] = ptr;
190 }
191 }
192
193 return (argv);
194 }
195
196 char **
lg_argextra(char ** argv,int argc,struct cmd * cmdp)197 lg_argextra(char **argv, int argc, struct cmd *cmdp)
198 {
199 char **new_argv;
200 int i, c = 0;
201
202 /* Count elements */
203 for (i = 0; cmdp->earg[i] != NULL; i++)
204 c++;
205
206 /* Generate array */
207 if ((new_argv = calloc(c + argc + 1, sizeof(char *))) == NULL) {
208 printf("fatal error: %s\n", strerror(errno));
209 return (NULL);
210 }
211
212 /* Fill array */
213 for (i = c = 0; cmdp->earg[i] != NULL; i++)
214 new_argv[c++] = cmdp->earg[i];
215
216 /* Append old array */
217 for (i = 0; i < argc; i++)
218 new_argv[c++] = argv[i];
219
220 new_argv[c] = NULL;
221
222 free(argv);
223
224 return (new_argv);
225 }
226
227 int
lg_incl(const char * file)228 lg_incl(const char *file)
229 {
230 char buf[BUFSIZ];
231 int fd, len;
232
233 if ((fd = open(file, O_RDONLY)) == -1)
234 return (errno);
235
236 do {
237 len = read(fd, buf, sizeof(buf));
238 fwrite(buf, len, 1, stdout);
239 } while(len == BUFSIZ);
240
241 close(fd);
242 return (0);
243 }
244
245 int
main(void)246 main(void)
247 {
248 char *query, *myname, *self, *cmd = NULL, *req;
249 char **argv = NULL;
250 int ret = 1, argc = 0, query_length = 0;
251 struct stat st;
252 u_int i;
253 struct cmd *cmdp = NULL;
254
255 if (pledge("stdio rpath proc exec", NULL) == -1)
256 err(1, "pledge");
257
258 if ((myname = lg_getenv("SERVER_NAME", NULL)) == NULL)
259 return (1);
260
261 printf("Content-Type: %s\n"
262 "Cache-Control: no-cache\n\n"
263 "<!doctype html>\n"
264 "<html>\n"
265 "<head>\n"
266 "<title>%s</title>\n",
267 CONTENT_TYPE, myname);
268 if (stat(INC_STYLE, &st) == 0) {
269 printf("<style>\n");
270 lg_incl(INC_STYLE);
271 printf("</style>\n");
272 }
273 if (stat(INC_HEAD, &st) != 0 || lg_incl(INC_HEAD) != 0) {
274 printf("</head>\n"
275 "<body>\n");
276 }
277
278 /* print a form with possible options */
279 if ((self = lg_getenv("SCRIPT_NAME", NULL)) == NULL) {
280 printf("fatal error: invalid request\n");
281 goto err;
282 }
283 if ((query = lg_getenv("QUERY_STRING", &query_length)) != NULL)
284 cmd = lg_getarg("cmd=", query, query_length);
285 printf(
286 "<form action='%s'>\n"
287 "<div class=\"command\">\n"
288 "<select name='cmd'>\n",
289 self);
290 for (i = 0; cmds[i].name != NULL; i++) {
291 if (!lg_checkperm(&cmds[i]))
292 continue;
293
294 if (cmd != NULL && strcmp(cmd, cmds[i].name) == 0)
295 printf("<option value='%s' selected='selected'>%s"
296 "</option>\n",
297 cmds[i].name, cmds[i].name);
298 else
299 printf("<option value='%s'>%s</option>\n",
300 cmds[i].name, cmds[i].name);
301 }
302
303 if ((req = lg_getarg("req=", query, query_length)) != NULL) {
304 /* Could be NULL */
305 argv = lg_arg2argv(req, &argc);
306 }
307
308 printf("</select>\n"
309 "<input type='text' value='%s' name='req'>\n"
310 "<input type='submit' value='submit'>\n"
311 "</div>\n"
312 "</form>\n"
313 "<pre>\n", req ? req : "");
314 fflush(stdout);
315
316 #ifdef DEBUG
317 if (close(2) == -1 || dup2(1, 2) == -1)
318 #else
319 if (close(2) == -1)
320 #endif
321 {
322 printf("fatal error: %s\n", strerror(errno));
323 goto err;
324 }
325
326 if (query == NULL)
327 goto err;
328 if (cmd == NULL) {
329 printf("unspecified command\n");
330 goto err;
331 }
332
333 for (i = 0; cmds[i].name != NULL; i++) {
334 if (strcmp(cmd, cmds[i].name) == 0) {
335 cmdp = &cmds[i];
336 break;
337 }
338 }
339
340 if (cmdp == NULL) {
341 printf("invalid command: %s\n", cmd);
342 goto err;
343 }
344 if (argc > cmdp->maxargs) {
345 printf("superfluous argument(s): %s %s\n",
346 cmd, cmdp->args ? cmdp->args : "");
347 goto err;
348 }
349 if (argc < cmdp->minargs) {
350 printf("missing argument(s): %s %s\n", cmd, cmdp->args);
351 goto err;
352 }
353
354 if (cmdp->func != NULL) {
355 ret = cmdp->func(cmds, argv);
356 } else {
357 if ((argv = lg_argextra(argv, argc, cmdp)) == NULL)
358 goto err;
359 ret = lg_exec(cmdp->earg[0], argv);
360 }
361 if (ret != 0)
362 printf("\nfailed%s\n", ret == 127 ? ": file not found" : ".");
363 else
364 printf("\nsuccess.\n");
365
366 err:
367 fflush(stdout);
368
369 free(argv);
370
371 printf("</pre>\n");
372
373 if (stat(INC_FOOT, &st) != 0 || lg_incl(INC_FOOT) != 0)
374 printf("<hr/>\n");
375
376 printf("<div class='footer'>\n"
377 "</div>\n"
378 "</body>\n"
379 "</html>\n");
380
381 return (ret);
382 }
383