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