xref: /openbsd-src/usr.bin/bgplg/bgplg.c (revision b11663eac4ec9fc18af33d2a642b47c0be59642b)
1*b11663eaSclaudio /*	$OpenBSD: bgplg.c,v 1.20 2024/02/09 12:56:53 claudio Exp $	*/
2bc5366b8Sreyk 
3bc5366b8Sreyk /*
49cf8e0eaSreyk  * Copyright (c) 2005, 2006 Reyk Floeter <reyk@openbsd.org>
5bc5366b8Sreyk  *
6bc5366b8Sreyk  * Permission to use, copy, modify, and distribute this software for any
7bc5366b8Sreyk  * purpose with or without fee is hereby granted, provided that the above
8bc5366b8Sreyk  * copyright notice and this permission notice appear in all copies.
9bc5366b8Sreyk  *
10bc5366b8Sreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11bc5366b8Sreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12bc5366b8Sreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13bc5366b8Sreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14bc5366b8Sreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15bc5366b8Sreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16bc5366b8Sreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17bc5366b8Sreyk  */
18bc5366b8Sreyk 
19bc5366b8Sreyk #include <sys/types.h>
20b9fc9a72Sderaadt #include <sys/stat.h>
21bc5366b8Sreyk 
22bc5366b8Sreyk #include <stdio.h>
23bc5366b8Sreyk #include <stdlib.h>
24bc5366b8Sreyk #include <signal.h>
25bc5366b8Sreyk #include <string.h>
26bc5366b8Sreyk #include <unistd.h>
27b9fc9a72Sderaadt #include <limits.h>
28bc5366b8Sreyk #include <ctype.h>
29bc5366b8Sreyk #include <errno.h>
30bc5366b8Sreyk #include <fcntl.h>
319456c97fSbenno #include <err.h>
32bc5366b8Sreyk 
33bc5366b8Sreyk #include "bgplg.h"
34bc5366b8Sreyk 
35bc5366b8Sreyk #define INC_STYLE	"/conf/bgplg.css"
36bc5366b8Sreyk #define INC_HEAD	"/conf/bgplg.head"
37bc5366b8Sreyk #define INC_FOOT	"/conf/bgplg.foot"
38bc5366b8Sreyk 
3974abb244Sflorian #define BGPDSOCK	"/run/bgpd.rsock"
40bc5366b8Sreyk #define BGPCTL		"/bin/bgpctl", "-s", BGPDSOCK
41bc5366b8Sreyk #define PING		"/bin/ping"
42bc5366b8Sreyk #define TRACEROUTE	"/bin/traceroute"
43cc8d9beeSsthen #define PING6		"/bin/ping6"
44cc8d9beeSsthen #define TRACEROUTE6	"/bin/traceroute6"
456bc04b6bSreyk #define CONTENT_TYPE	"text/html"
46bc5366b8Sreyk 
47bc5366b8Sreyk static struct cmd cmds[] = CMDS;
48bc5366b8Sreyk 
49bc5366b8Sreyk char		 *lg_getenv(const char *, int *);
50bc5366b8Sreyk void		  lg_urldecode(char *);
51bc5366b8Sreyk char		**lg_arg2argv(char *, int *);
52bc5366b8Sreyk char		**lg_argextra(char **, int, struct cmd *);
53bc5366b8Sreyk char		 *lg_getarg(const char *, char *, int);
54bc5366b8Sreyk int		  lg_incl(const char *);
55bc5366b8Sreyk 
56bc5366b8Sreyk void
lg_urldecode(char * str)57bc5366b8Sreyk lg_urldecode(char *str)
58bc5366b8Sreyk {
59bc5366b8Sreyk 	size_t i, c, len;
60bc5366b8Sreyk 	char code[3];
61bc5366b8Sreyk 	long result;
62bc5366b8Sreyk 
63bc5366b8Sreyk 	if (str && *str) {
64bc5366b8Sreyk 		len = strlen(str);
65bc5366b8Sreyk 		i = c = 0;
66bc5366b8Sreyk 		while (i < len) {
67bc5366b8Sreyk 			if (str[i] == '%' && i <= (len - 2)) {
686d73225dSderaadt 				if (isxdigit((unsigned char)str[i + 1]) &&
696d73225dSderaadt 				    isxdigit((unsigned char)str[i + 2])) {
70bc5366b8Sreyk 					code[0] = str[i + 1];
71bc5366b8Sreyk 					code[1] = str[i + 2];
72bc5366b8Sreyk 					code[2] = 0;
73bc5366b8Sreyk 					result = strtol(code, NULL, 16);
74bc5366b8Sreyk 					/* Replace NUL chars with a space */
75bc5366b8Sreyk 					if (result == 0)
76bc5366b8Sreyk 						result = ' ';
77bc5366b8Sreyk 					str[c++] = result;
78bc5366b8Sreyk 					i += 3;
79bc5366b8Sreyk 				} else {
80bc5366b8Sreyk 					str[c++] = '%';
81bc5366b8Sreyk 					i++;
82bc5366b8Sreyk 				}
83bc5366b8Sreyk 			} else if (str[i] == '+') {
84bc5366b8Sreyk 				str[i] = ' ';
85bc5366b8Sreyk 			} else {
86bc5366b8Sreyk 				if (c != i)
87bc5366b8Sreyk 					str[c] = str[i];
88bc5366b8Sreyk 				c++;
89bc5366b8Sreyk 				i++;
90bc5366b8Sreyk 			}
91bc5366b8Sreyk 		}
92bc5366b8Sreyk 		str[c] = 0x0;
93bc5366b8Sreyk 	}
94bc5366b8Sreyk }
95bc5366b8Sreyk 
96bc5366b8Sreyk char *
lg_getenv(const char * name,int * lenp)97bc5366b8Sreyk lg_getenv(const char *name, int *lenp)
98bc5366b8Sreyk {
99bc5366b8Sreyk 	size_t len;
100bc5366b8Sreyk 	u_int i;
101bc5366b8Sreyk 	char *ptr;
102bc5366b8Sreyk 
103bc5366b8Sreyk 	if ((ptr = getenv(name)) == NULL)
104bc5366b8Sreyk 		return (NULL);
105bc5366b8Sreyk 
106bc5366b8Sreyk 	lg_urldecode(ptr);
107bc5366b8Sreyk 
108bc5366b8Sreyk 	if (!(len = strlen(ptr)))
109bc5366b8Sreyk 		return (NULL);
110bc5366b8Sreyk 
111bc5366b8Sreyk 	if (lenp != NULL)
112bc5366b8Sreyk 		*lenp = len;
113bc5366b8Sreyk 
114bc5366b8Sreyk #define allowed_in_string(_x)                                           \
1156d73225dSderaadt 	(isalnum((unsigned char)_x) || strchr("-_.:/= ", _x))
116bc5366b8Sreyk 
117bc5366b8Sreyk 	for (i = 0; i < len; i++) {
118243d9a3dSclaudio 		if (ptr[i] == '&')
119243d9a3dSclaudio 			ptr[i] = '\0';
120bc5366b8Sreyk 		if (!allowed_in_string(ptr[i])) {
121bc5366b8Sreyk 			printf("invalid character in input\n");
122bc5366b8Sreyk 			return (NULL);
123bc5366b8Sreyk 		}
124bc5366b8Sreyk 	}
125bc5366b8Sreyk 
126bc5366b8Sreyk 	return (ptr);
1276d73225dSderaadt #undef allowed_in_string
128bc5366b8Sreyk }
129bc5366b8Sreyk 
130bc5366b8Sreyk char *
lg_getarg(const char * name,char * arg,int len)131bc5366b8Sreyk lg_getarg(const char *name, char *arg, int len)
132bc5366b8Sreyk {
133bc5366b8Sreyk 	char *ptr = arg;
134bc5366b8Sreyk 	size_t namelen, ptrlen;
135bc5366b8Sreyk 	int i;
136bc5366b8Sreyk 
137bc5366b8Sreyk 	namelen = strlen(name);
138bc5366b8Sreyk 
139bc5366b8Sreyk 	for (i = 0; i < len; i++) {
140bc5366b8Sreyk 		if (arg[i] == '\0')
141bc5366b8Sreyk 			continue;
142bc5366b8Sreyk 		ptr = arg + i;
143bc5366b8Sreyk 		ptrlen = strlen(ptr);
144bc5366b8Sreyk 		if (namelen >= ptrlen)
145bc5366b8Sreyk 			continue;
146bc5366b8Sreyk 		if (strncmp(name, ptr, namelen) == 0)
147bc5366b8Sreyk 			return (ptr + namelen);
148bc5366b8Sreyk 	}
149bc5366b8Sreyk 
150bc5366b8Sreyk 	return (NULL);
151bc5366b8Sreyk }
152bc5366b8Sreyk 
153bc5366b8Sreyk char **
lg_arg2argv(char * arg,int * argc)154bc5366b8Sreyk lg_arg2argv(char *arg, int *argc)
155bc5366b8Sreyk {
156bc5366b8Sreyk 	char **argv, *ptr = arg;
157bc5366b8Sreyk 	size_t len;
158bc5366b8Sreyk 	u_int i, c = 1;
159bc5366b8Sreyk 
160bc5366b8Sreyk 	len = strlen(arg);
161bc5366b8Sreyk 
162bc5366b8Sreyk 	/* Count elements */
163b9d22517Sdenis 	for (i = 0; i < len; i++) {
1646d73225dSderaadt 		if (isspace((unsigned char)arg[i])) {
165bc5366b8Sreyk 			/* filter out additional options */
166bc5366b8Sreyk 			if (arg[i + 1] == '-') {
167bc5366b8Sreyk 				printf("invalid input\n");
168bc5366b8Sreyk 				return (NULL);
169bc5366b8Sreyk 			}
170bc5366b8Sreyk 			arg[i] = '\0';
171bc5366b8Sreyk 			c++;
172bc5366b8Sreyk 		}
173bc5366b8Sreyk 	}
174bc5366b8Sreyk 
175bc5366b8Sreyk 	/* Generate array */
176bc5366b8Sreyk 	if ((argv = calloc(c + 1, sizeof(char *))) == NULL) {
177bc5366b8Sreyk 		printf("fatal error: %s\n", strerror(errno));
178bc5366b8Sreyk 		return (NULL);
179bc5366b8Sreyk 	}
180bc5366b8Sreyk 
181bc5366b8Sreyk 	argv[c] = NULL;
182bc5366b8Sreyk 	*argc = c;
183bc5366b8Sreyk 
184bc5366b8Sreyk 	/* Fill array */
185b9d22517Sdenis 	for (i = c = 0; i < len; i++) {
186bc5366b8Sreyk 		if (arg[i] == '\0' || i == 0) {
187bc5366b8Sreyk 			if (i != 0)
188bc5366b8Sreyk 				ptr = &arg[i + 1];
189bc5366b8Sreyk 			argv[c++] = ptr;
190bc5366b8Sreyk 		}
191bc5366b8Sreyk 	}
192bc5366b8Sreyk 
193bc5366b8Sreyk 	return (argv);
194bc5366b8Sreyk }
195bc5366b8Sreyk 
196bc5366b8Sreyk char **
lg_argextra(char ** argv,int argc,struct cmd * cmdp)197bc5366b8Sreyk lg_argextra(char **argv, int argc, struct cmd *cmdp)
198bc5366b8Sreyk {
199bc5366b8Sreyk 	char **new_argv;
200bc5366b8Sreyk 	int i, c = 0;
201bc5366b8Sreyk 
202bc5366b8Sreyk 	/* Count elements */
203bc5366b8Sreyk 	for (i = 0; cmdp->earg[i] != NULL; i++)
204bc5366b8Sreyk 		c++;
205bc5366b8Sreyk 
206bc5366b8Sreyk 	/* Generate array */
207bc5366b8Sreyk 	if ((new_argv = calloc(c + argc + 1, sizeof(char *))) == NULL) {
208bc5366b8Sreyk 		printf("fatal error: %s\n", strerror(errno));
209bc5366b8Sreyk 		return (NULL);
210bc5366b8Sreyk 	}
211bc5366b8Sreyk 
212bc5366b8Sreyk 	/* Fill array */
213bc5366b8Sreyk 	for (i = c = 0; cmdp->earg[i] != NULL; i++)
214bc5366b8Sreyk 		new_argv[c++] = cmdp->earg[i];
215bc5366b8Sreyk 
216bc5366b8Sreyk 	/* Append old array */
217bc5366b8Sreyk 	for (i = 0; i < argc; i++)
218bc5366b8Sreyk 		new_argv[c++] = argv[i];
219bc5366b8Sreyk 
220bc5366b8Sreyk 	new_argv[c] = NULL;
221bc5366b8Sreyk 
222bc5366b8Sreyk 	free(argv);
223bc5366b8Sreyk 
224bc5366b8Sreyk 	return (new_argv);
225bc5366b8Sreyk }
226bc5366b8Sreyk 
227bc5366b8Sreyk int
lg_incl(const char * file)228bc5366b8Sreyk lg_incl(const char *file)
229bc5366b8Sreyk {
230bc5366b8Sreyk 	char buf[BUFSIZ];
231bc5366b8Sreyk 	int fd, len;
232bc5366b8Sreyk 
233bc5366b8Sreyk 	if ((fd = open(file, O_RDONLY)) == -1)
234bc5366b8Sreyk 		return (errno);
235bc5366b8Sreyk 
236bc5366b8Sreyk 	do {
237bc5366b8Sreyk 		len = read(fd, buf, sizeof(buf));
238bc5366b8Sreyk 		fwrite(buf, len, 1, stdout);
239bc5366b8Sreyk 	} while(len == BUFSIZ);
240bc5366b8Sreyk 
241bfa35f97Sclaudio 	close(fd);
242bc5366b8Sreyk 	return (0);
243bc5366b8Sreyk }
244bc5366b8Sreyk 
245bc5366b8Sreyk int
main(void)246bc5366b8Sreyk main(void)
247bc5366b8Sreyk {
24892c35e0eSsthen 	char *query, *myname, *self, *cmd = NULL, *req;
249bc5366b8Sreyk 	char **argv = NULL;
250bc5366b8Sreyk 	int ret = 1, argc = 0, query_length = 0;
251bc5366b8Sreyk 	struct stat st;
252bc5366b8Sreyk 	u_int i;
253bc5366b8Sreyk 	struct cmd *cmdp = NULL;
254bc5366b8Sreyk 
2559456c97fSbenno 	if (pledge("stdio rpath proc exec", NULL) == -1)
2569456c97fSbenno 		err(1, "pledge");
2579456c97fSbenno 
25892c35e0eSsthen 	if ((myname = lg_getenv("SERVER_NAME", NULL)) == NULL)
2596bc04b6bSreyk 		return (1);
2606bc04b6bSreyk 
2616bc04b6bSreyk 	printf("Content-Type: %s\n"
262bc5366b8Sreyk 	    "Cache-Control: no-cache\n\n"
263*b11663eaSclaudio 	    "<!doctype html>\n"
264*b11663eaSclaudio 	    "<html>\n"
265bc5366b8Sreyk 	    "<head>\n"
26616b8ccdcSjob 	    "<title>%s</title>\n",
26716b8ccdcSjob 	    CONTENT_TYPE, myname);
268bc5366b8Sreyk 	if (stat(INC_STYLE, &st) == 0) {
269*b11663eaSclaudio 		printf("<style>\n");
270bc5366b8Sreyk 		lg_incl(INC_STYLE);
271*b11663eaSclaudio 		printf("</style>\n");
272bc5366b8Sreyk 	}
273bc5366b8Sreyk 	if (stat(INC_HEAD, &st) != 0 || lg_incl(INC_HEAD) != 0) {
274bc5366b8Sreyk 		printf("</head>\n"
275bc5366b8Sreyk 		    "<body>\n");
276bc5366b8Sreyk 	}
277bc5366b8Sreyk 
278bc5366b8Sreyk 	/* print a form with possible options */
279bc5366b8Sreyk 	if ((self = lg_getenv("SCRIPT_NAME", NULL)) == NULL) {
280bc5366b8Sreyk 		printf("fatal error: invalid request\n");
281bc5366b8Sreyk 		goto err;
282bc5366b8Sreyk 	}
283bc5366b8Sreyk 	if ((query = lg_getenv("QUERY_STRING", &query_length)) != NULL)
284bc5366b8Sreyk 		cmd = lg_getarg("cmd=", query, query_length);
2854ff84f7aSreyk 	printf(
2864ff84f7aSreyk 	    "<form action='%s'>\n"
2874ff84f7aSreyk 	    "<div class=\"command\">\n"
288bc5366b8Sreyk 	    "<select name='cmd'>\n",
289bc5366b8Sreyk 	    self);
290bc5366b8Sreyk 	for (i = 0; cmds[i].name != NULL; i++) {
291bc5366b8Sreyk 		if (!lg_checkperm(&cmds[i]))
292bc5366b8Sreyk 			continue;
293bc5366b8Sreyk 
294bc5366b8Sreyk 		if (cmd != NULL && strcmp(cmd, cmds[i].name) == 0)
295bc5366b8Sreyk 			printf("<option value='%s' selected='selected'>%s"
296bc5366b8Sreyk 			    "</option>\n",
297bc5366b8Sreyk 			    cmds[i].name, cmds[i].name);
298bc5366b8Sreyk 		else
299bc5366b8Sreyk 			printf("<option value='%s'>%s</option>\n",
300bc5366b8Sreyk 			    cmds[i].name, cmds[i].name);
301bc5366b8Sreyk 	}
302a4478c91Sjob 
303a4478c91Sjob 	if ((req = lg_getarg("req=", query, query_length)) != NULL) {
304a4478c91Sjob 		/* Could be NULL */
305a4478c91Sjob 		argv = lg_arg2argv(req, &argc);
306a4478c91Sjob 	}
307a4478c91Sjob 
308bc5366b8Sreyk 	printf("</select>\n"
309*b11663eaSclaudio 	    "<input type='text' value='%s' name='req'>\n"
310*b11663eaSclaudio 	    "<input type='submit' value='submit'>\n"
3114ff84f7aSreyk 	    "</div>\n"
312bc5366b8Sreyk 	    "</form>\n"
313a4478c91Sjob 	    "<pre>\n", req ? req : "");
314bc5366b8Sreyk 	fflush(stdout);
315bc5366b8Sreyk 
316bc5366b8Sreyk #ifdef DEBUG
317bc5366b8Sreyk 	if (close(2) == -1 || dup2(1, 2) == -1)
318bc5366b8Sreyk #else
319bc5366b8Sreyk 	if (close(2) == -1)
320bc5366b8Sreyk #endif
321bc5366b8Sreyk 	{
322bc5366b8Sreyk 		printf("fatal error: %s\n", strerror(errno));
323bc5366b8Sreyk 		goto err;
324bc5366b8Sreyk 	}
325bc5366b8Sreyk 
326bc5366b8Sreyk 	if (query == NULL)
327bc5366b8Sreyk 		goto err;
328bc5366b8Sreyk 	if (cmd == NULL) {
329bc5366b8Sreyk 		printf("unspecified command\n");
330bc5366b8Sreyk 		goto err;
331bc5366b8Sreyk 	}
332bc5366b8Sreyk 
333bc5366b8Sreyk 	for (i = 0; cmds[i].name != NULL; i++) {
334bc5366b8Sreyk 		if (strcmp(cmd, cmds[i].name) == 0) {
335bc5366b8Sreyk 			cmdp = &cmds[i];
336bc5366b8Sreyk 			break;
337bc5366b8Sreyk 		}
338bc5366b8Sreyk 	}
339bc5366b8Sreyk 
340bc5366b8Sreyk 	if (cmdp == NULL) {
341bc5366b8Sreyk 		printf("invalid command: %s\n", cmd);
342bc5366b8Sreyk 		goto err;
343bc5366b8Sreyk 	}
344bc5366b8Sreyk 	if (argc > cmdp->maxargs) {
345bc5366b8Sreyk 		printf("superfluous argument(s): %s %s\n",
346bc5366b8Sreyk 		    cmd, cmdp->args ? cmdp->args : "");
347bc5366b8Sreyk 		goto err;
348bc5366b8Sreyk 	}
349bc5366b8Sreyk 	if (argc < cmdp->minargs) {
350bc5366b8Sreyk 		printf("missing argument(s): %s %s\n", cmd, cmdp->args);
351bc5366b8Sreyk 		goto err;
352bc5366b8Sreyk 	}
353bc5366b8Sreyk 
354bc5366b8Sreyk 	if (cmdp->func != NULL) {
355bc5366b8Sreyk 		ret = cmdp->func(cmds, argv);
356bc5366b8Sreyk 	} else {
357bc5366b8Sreyk 		if ((argv = lg_argextra(argv, argc, cmdp)) == NULL)
358bc5366b8Sreyk 			goto err;
359bc5366b8Sreyk 		ret = lg_exec(cmdp->earg[0], argv);
360bc5366b8Sreyk 	}
361bc5366b8Sreyk 	if (ret != 0)
362bc5366b8Sreyk 		printf("\nfailed%s\n", ret == 127 ? ": file not found" : ".");
363bc5366b8Sreyk 	else
364bc5366b8Sreyk 		printf("\nsuccess.\n");
365bc5366b8Sreyk 
366bc5366b8Sreyk  err:
367bc5366b8Sreyk 	fflush(stdout);
368bc5366b8Sreyk 
369bc5366b8Sreyk 	free(argv);
370bc5366b8Sreyk 
371bc5366b8Sreyk 	printf("</pre>\n");
372bc5366b8Sreyk 
373bc5366b8Sreyk 	if (stat(INC_FOOT, &st) != 0 || lg_incl(INC_FOOT) != 0)
374bc5366b8Sreyk 		printf("<hr/>\n");
375bc5366b8Sreyk 
376bc5366b8Sreyk 	printf("<div class='footer'>\n"
377bc5366b8Sreyk 	    "</div>\n"
378bc5366b8Sreyk 	    "</body>\n"
37916b8ccdcSjob 	    "</html>\n");
380bc5366b8Sreyk 
381bc5366b8Sreyk 	return (ret);
382bc5366b8Sreyk }
383