ind.c (10135B)
1 /* 2 * Copy me if you can. 3 * by 20h 4 */ 5 6 #include <unistd.h> 7 #include <stdarg.h> 8 #include <string.h> 9 #include <memory.h> 10 #include <fcntl.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <stdint.h> 14 #include <time.h> 15 #include <netdb.h> 16 #include <sys/socket.h> 17 #include <sys/stat.h> 18 #include <netinet/in.h> 19 #include <netinet/tcp.h> 20 #include <arpa/inet.h> 21 #include <sys/ioctl.h> 22 #include <limits.h> 23 24 #include "arg.h" 25 #include "ind.h" 26 #include "handlr.h" 27 28 /* 29 * Be careful, to look at handlerequest(), in case you add any executing 30 * handler, so nocgi will be valuable. 31 */ 32 33 filetype type[] = { 34 {"default", "9", handlebin}, 35 {"gph", "1", handlegph}, 36 {"cgi", "0", handlecgi}, 37 {"dcgi", "1", handledcgi}, 38 {"bin", "9", handlebin}, 39 {"tgz", "9", handlebin}, 40 {"gz", "9", handlebin}, 41 {"jpg", "I", handlebin}, 42 {"gif", "g", handlebin}, 43 {"png", "I", handlebin}, 44 {"bmp", "I", handlebin}, 45 {"txt", "0", handlebin}, 46 {"html", "0", handlebin}, 47 {"htm", "0", handlebin}, 48 {"xhtml", "0", handlebin}, 49 {"css", "0", handlebin}, 50 {"md", "0", handlebin}, 51 {"c", "0", handlebin}, 52 {"sh", "0", handlebin}, 53 {"patch", "0", handlebin}, 54 {"meme", "0", handlebin}, 55 {NULL, NULL, NULL}, 56 }; 57 58 int 59 pendingbytes(int sock) 60 { 61 int pending, rval; 62 63 pending = 0; 64 rval = 0; 65 #if defined(TIOCOUTQ) && !defined(__OpenBSD__) 66 rval = ioctl(sock, TIOCOUTQ, &pending); 67 #else 68 #ifdef SIOCOUTQ 69 rval = ioctl(sock, SIOCOUTQ, &pending); 70 #endif 71 #endif 72 73 if (rval != 0) 74 return 0; 75 76 return pending; 77 } 78 79 void 80 waitforpendingbytes(int sock) 81 { 82 int npending = 0, opending = 0; 83 useconds_t trytime = 10; 84 85 /* 86 * Wait until there is nothing pending or the connection stalled 87 * (nothing was sent) for around 40 seconds. Beware, trytime is 88 * an exponential wait. 89 */ 90 while ((npending = pendingbytes(sock)) > 0 && trytime < 20000000) { 91 if (opending != 0) { 92 if (opending != npending) { 93 trytime = 10; 94 } else { 95 /* 96 * Exponentially increase the usleep 97 * waiting time to not waste CPU 98 * resources. 99 */ 100 trytime += trytime; 101 } 102 } 103 opending = npending; 104 105 usleep(trytime); 106 } 107 } 108 109 int 110 xsendfile(int fd, int sock) 111 { 112 struct stat st; 113 char *sendb, *sendi; 114 size_t bufsiz = BUFSIZ; 115 int len, sent, optval; 116 117 USED(optval); 118 119 if (fstat(fd, &st) >= 0) 120 if ((bufsiz = st.st_blksize) < BUFSIZ) 121 bufsiz = BUFSIZ; 122 123 sendb = xmalloc(bufsiz); 124 while ((len = read(fd, sendb, bufsiz)) > 0) { 125 sendi = sendb; 126 while (len > 0) { 127 if ((sent = write(sock, sendi, len)) < 0) { 128 free(sendb); 129 return -1; 130 } 131 len -= sent; 132 sendi += sent; 133 } 134 } 135 free(sendb); 136 return 0; 137 } 138 139 void * 140 xcalloc(size_t nmemb, size_t size) 141 { 142 void *p; 143 144 if (!(p = calloc(nmemb, size))) { 145 perror("calloc"); 146 exit(1); 147 } 148 149 return p; 150 } 151 152 void * 153 xmalloc(size_t size) 154 { 155 void *p; 156 157 if (!(p = malloc(size))) { 158 perror("malloc"); 159 exit(1); 160 } 161 162 return p; 163 } 164 165 void * 166 xrealloc(void *ptr, size_t size) 167 { 168 if (!(ptr = realloc(ptr, size))) { 169 perror("realloc"); 170 exit(1); 171 } 172 173 return ptr; 174 } 175 176 char * 177 xstrdup(const char *str) 178 { 179 char *ret; 180 181 if (!(ret = strdup(str))) { 182 perror("strdup"); 183 exit(1); 184 } 185 186 return ret; 187 } 188 189 filetype * 190 gettype(char *filename) 191 { 192 char *end; 193 int i; 194 195 end = strrchr(filename, '.'); 196 if (end == NULL) 197 return &type[0]; 198 end++; 199 200 for (i = 0; type[i].end != NULL; i++) 201 if (!strcasecmp(end, type[i].end)) 202 return &type[i]; 203 204 return &type[0]; 205 } 206 207 void 208 freeelem(Elems *e) 209 { 210 if (e != NULL) { 211 if (e->e != NULL) { 212 for (;e->num > 0; e->num--) 213 if (e->e[e->num - 1] != NULL) 214 free(e->e[e->num - 1]); 215 free(e->e); 216 } 217 free(e); 218 } 219 return; 220 } 221 222 void 223 freeindex(Indexs *i) 224 { 225 if (i != NULL) { 226 if (i->n != NULL) { 227 for (;i->num > 0; i->num--) 228 freeelem(i->n[i->num - 1]); 229 free(i->n); 230 } 231 free(i); 232 } 233 234 return; 235 } 236 237 void 238 addelem(Elems *e, char *s) 239 { 240 e->num++; 241 e->e = xrealloc(e->e, sizeof(char *) * e->num); 242 e->e[e->num - 1] = xstrdup(s); 243 244 return; 245 } 246 247 Elems * 248 getadv(char *str) 249 { 250 char *b, *e, *o, *bo; 251 Elems *ret; 252 253 ret = xcalloc(1, sizeof(Elems)); 254 255 if (strchr(str, '\t')) { 256 addelem(ret, "i"); 257 addelem(ret, "Happy helping ☃ here: You tried to " 258 "output a spurious tab character. This will " 259 "break gopher. Please review your scripts. " 260 "Have a nice day!"); 261 addelem(ret, "Err"); 262 addelem(ret, "server"); 263 addelem(ret, "port"); 264 265 return ret; 266 } 267 268 if (str[0] == '[') { 269 o = xstrdup(str); 270 b = o + 1; 271 bo = b; 272 while ((e = strchr(bo, '|')) != NULL) { 273 if (e != bo && e[-1] == '\\') { 274 memmove(&e[-1], e, strlen(e)); 275 bo = e; 276 continue; 277 } 278 *e = '\0'; 279 e++; 280 addelem(ret, b); 281 b = e; 282 bo = b; 283 } 284 285 e = strchr(b, ']'); 286 if (e != NULL) { 287 *e = '\0'; 288 addelem(ret, b); 289 } 290 free(o); 291 292 if (ret->e != NULL && ret->e[0] != NULL && ret->e[0][0] == '\0') { 293 freeelem(ret); 294 ret = xcalloc(1, sizeof(Elems)); 295 296 addelem(ret, "i"); 297 addelem(ret, "Happy helping ☃ here: You did not " 298 "specify an item type on this line. Please " 299 "review your scripts. " 300 "Have a nice day!"); 301 addelem(ret, "Err"); 302 addelem(ret, "server"); 303 addelem(ret, "port"); 304 305 return ret; 306 } 307 308 if (ret->e != NULL && ret->num == 5) 309 return ret; 310 311 /* Invalid entry: Give back the whole line. */ 312 freeelem(ret); 313 ret = xcalloc(1, sizeof(Elems)); 314 } 315 316 b = str; 317 if (*str == 't') 318 b++; 319 addelem(ret, "i"); 320 addelem(ret, b); 321 addelem(ret, "Err"); 322 addelem(ret, "server"); 323 addelem(ret, "port"); 324 325 return ret; 326 } 327 328 void 329 addindexs(Indexs *idx, Elems *el) 330 { 331 idx->num++; 332 idx->n = xrealloc(idx->n, sizeof(Elems) * idx->num); 333 idx->n[idx->num - 1] = el; 334 335 return; 336 } 337 338 Indexs * 339 scanfile(char *fname) 340 { 341 char *ln = NULL; 342 size_t linesiz = 0; 343 ssize_t n; 344 FILE *fp; 345 Indexs *ret; 346 Elems *el; 347 348 if (!(fp = fopen(fname, "r"))) 349 return NULL; 350 351 ret = xcalloc(1, sizeof(Indexs)); 352 353 while ((n = getline(&ln, &linesiz, fp)) > 0) { 354 if (ln[n - 1] == '\n') 355 ln[--n] = '\0'; 356 el = getadv(ln); 357 if(el == NULL) 358 continue; 359 360 addindexs(ret, el); 361 } 362 if (ferror(fp)) 363 perror("getline"); 364 free(ln); 365 fclose(fp); 366 367 if (ret->n == NULL) { 368 free(ret); 369 return NULL; 370 } 371 372 return ret; 373 } 374 375 int 376 printelem(int fd, Elems *el, char *file, char *base, char *addr, char *port) 377 { 378 char *path, *p, buf[PATH_MAX+1]; 379 int len; 380 381 if (!strcmp(el->e[3], "server")) { 382 free(el->e[3]); 383 el->e[3] = xstrdup(addr); 384 } 385 if (!strcmp(el->e[4], "port")) { 386 free(el->e[4]); 387 el->e[4] = xstrdup(port); 388 } 389 390 /* 391 * Ignore if the path is from base, if it might be some h type with 392 * some URL and ignore various types that have different semantics but 393 * to point to some file or directory. 394 */ 395 if ((el->e[2][0] != '\0' 396 && el->e[2][0] != '/' 397 && el->e[0][0] != 'i' 398 && el->e[0][0] != '2' 399 && el->e[0][0] != '3' 400 && el->e[0][0] != '8' 401 && el->e[0][0] != 'w' 402 && el->e[0][0] != 'T') && 403 !(el->e[0][0] == 'h' && !strncmp(el->e[2], "URL:", 4))) { 404 path = file + strlen(base); 405 if ((p = strrchr(path, '/'))) 406 len = p - path; 407 else 408 len = strlen(path); 409 snprintf(buf, sizeof(buf), "%s%.*s/%s", base, len, path, el->e[2]); 410 411 if ((path = realpath(buf, NULL)) && 412 !strncmp(base, path, strlen(base))) { 413 p = path + strlen(base); 414 free(el->e[2]); 415 el->e[2] = xstrdup(p[0]? p : "/"); 416 } 417 free(path); 418 } 419 420 if (dprintf(fd, "%.1s%s\t%s\t%s\t%s\r\n", el->e[0], el->e[1], el->e[2], 421 el->e[3], el->e[4]) < 0) { 422 perror("printelem: dprintf"); 423 return -1; 424 } 425 return 0; 426 } 427 428 char * 429 smprintf(char *fmt, ...) 430 { 431 va_list fmtargs; 432 char *ret; 433 int size; 434 435 va_start(fmtargs, fmt); 436 size = vsnprintf(NULL, 0, fmt, fmtargs); 437 va_end(fmtargs); 438 439 ret = xcalloc(1, ++size); 440 va_start(fmtargs, fmt); 441 vsnprintf(ret, size, fmt, fmtargs); 442 va_end(fmtargs); 443 444 return ret; 445 } 446 447 char * 448 reverselookup(char *host) 449 { 450 struct in_addr hoststr; 451 struct hostent *client; 452 char *rethost; 453 454 rethost = NULL; 455 456 if (inet_pton(AF_INET, host, &hoststr)) { 457 client = gethostbyaddr((const void *)&hoststr, 458 sizeof(hoststr), AF_INET); 459 if (client != NULL) 460 rethost = xstrdup(client->h_name); 461 } 462 463 if (rethost == NULL) 464 rethost = xstrdup(host); 465 466 return rethost; 467 } 468 469 void 470 setcgienviron(char *file, char *path, char *port, char *base, char *args, 471 char *sear, char *ohost, char *chost, int istls) 472 { 473 /* 474 * TODO: Clean environment from possible unsafe environment variables. 475 * But then it is the responsibility of the script writer. 476 */ 477 unsetenv("AUTH_TYPE"); 478 unsetenv("CONTENT_LENGTH"); 479 unsetenv("CONTENT_TYPE"); 480 setenv("GATEWAY_INTERFACE", "CGI/1.1", 1); 481 /* TODO: Separate, if run like rest.dcgi. */ 482 setenv("PATH_INFO", file, 1); 483 setenv("PATH_TRANSLATED", path, 1); 484 485 setenv("QUERY_STRING", args, 1); 486 /* legacy compatibility */ 487 setenv("SELECTOR", args, 1); 488 setenv("REQUEST", args, 1); 489 490 setenv("REMOTE_ADDR", chost, 1); 491 /* 492 * Don't do a reverse lookup on every call. Only do when needed, in 493 * the script. The RFC allows us to set the IP to the value. 494 */ 495 setenv("REMOTE_HOST", chost, 1); 496 /* Please do not implement identd here. */ 497 unsetenv("REMOTE_IDENT"); 498 unsetenv("REMOTE_USER"); 499 /* Make PHP happy. */ 500 setenv("REDIRECT_STATUS", "", 1); 501 /* 502 * Only GET is possible in gopher. POST emulation would be really 503 * ugly. 504 */ 505 setenv("REQUEST_METHOD", "GET", 1); 506 setenv("SCRIPT_NAME", file, 1); 507 setenv("SERVER_NAME", ohost, 1); 508 setenv("SERVER_PORT", port, 1); 509 setenv("SERVER_PROTOCOL", "gopher/1.0", 1); 510 setenv("SERVER_SOFTWARE", "geomyidae", 1); 511 512 setenv("X_GOPHER_SEARCH", sear, 1); 513 /* legacy compatibility */ 514 setenv("SEARCHREQUEST", sear, 1); 515 516 if (istls) { 517 setenv("GOPHERS", "on", 1); 518 setenv("HTTPS", "on", 1); 519 } else { 520 unsetenv("GOPHERS"); 521 unsetenv("HTTPS"); 522 } 523 524 } 525 526 char * 527 humansize(off_t n) 528 { 529 static char buf[16]; 530 const char postfixes[] = "BKMGTPE"; 531 double size; 532 int i = 0; 533 534 for (size = n; size >= 1024 && i < strlen(postfixes); i++) 535 size /= 1024; 536 537 if (!i) { 538 snprintf(buf, sizeof(buf), "%ju%c", (uintmax_t)n, 539 postfixes[i]); 540 } else { 541 snprintf(buf, sizeof(buf), "%.1f%c", size, postfixes[i]); 542 } 543 544 return buf; 545 } 546 547 char * 548 humantime(const time_t *clock) 549 { 550 static char buf[32]; 551 struct tm *tm; 552 553 tm = localtime(clock); 554 strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M %Z", tm); 555 556 return buf; 557 } 558