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