Annotation of embedaddon/axTLS/httpd/axhttpd.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (c) Cameron Rich
! 3: *
! 4: * All rights reserved.
! 5: *
! 6: * Redistribution and use in source and binary forms, with or without
! 7: * modification, are permitted provided that the following conditions are met:
! 8: *
! 9: * * Redistributions of source code must retain the above copyright notice,
! 10: * this list of conditions and the following disclaimer.
! 11: * * Redistributions in binary form must reproduce the above copyright notice,
! 12: * this list of conditions and the following disclaimer in the documentation
! 13: * and/or other materials provided with the distribution.
! 14: * * Neither the name of the axTLS project nor the names of its contributors
! 15: * may be used to endorse or promote products derived from this software
! 16: * without specific prior written permission.
! 17: *
! 18: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
! 19: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
! 20: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
! 21: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
! 22: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
! 23: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
! 24: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
! 25: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
! 26: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
! 27: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
! 28: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 29: */
! 30:
! 31: #include <stdio.h>
! 32: #include <string.h>
! 33: #include <sys/types.h>
! 34: #include <signal.h>
! 35: #include <stdlib.h>
! 36: #include <sys/stat.h>
! 37:
! 38: #if !defined(WIN32)
! 39: #include <pwd.h>
! 40: #endif
! 41: #include "axhttp.h"
! 42:
! 43: struct serverstruct *servers;
! 44: struct connstruct *usedconns;
! 45: struct connstruct *freeconns;
! 46: const char * const server_version = "axhttpd/"AXTLS_VERSION;
! 47: static const char *webroot = CONFIG_HTTP_WEBROOT;
! 48:
! 49: static void addtoservers(int sd);
! 50: static int openlistener(char *address, int port);
! 51: static void handlenewconnection(int listenfd, int is_ssl);
! 52: static void addconnection(int sd, char *ip, int is_ssl);
! 53: static void ax_chdir(void);
! 54:
! 55: #if defined(CONFIG_HTTP_HAS_CGI)
! 56: struct cgiextstruct *cgiexts;
! 57: static void addcgiext(const char *tp);
! 58:
! 59: #if !defined(WIN32)
! 60: static void reaper(int sigtype)
! 61: {
! 62: while (wait3(NULL, WNOHANG, NULL) > 0)
! 63: continue;
! 64: }
! 65: #endif
! 66: #endif
! 67:
! 68: #ifdef CONFIG_HTTP_VERBOSE /* should really be in debug mode or something */
! 69: /* clean up memory for valgrind */
! 70: static void sigint_cleanup(int sig)
! 71: {
! 72: struct serverstruct *sp;
! 73: struct connstruct *tp;
! 74:
! 75: while (servers != NULL)
! 76: {
! 77: if (servers->is_ssl)
! 78: ssl_ctx_free(servers->ssl_ctx);
! 79:
! 80: sp = servers->next;
! 81: free(servers);
! 82: servers = sp;
! 83: }
! 84:
! 85: while (freeconns != NULL)
! 86: {
! 87: tp = freeconns->next;
! 88: free(freeconns);
! 89: freeconns = tp;
! 90: }
! 91:
! 92: while (usedconns != NULL)
! 93: {
! 94: tp = usedconns->next;
! 95: free(usedconns);
! 96: usedconns = tp;
! 97: }
! 98:
! 99: #if defined(CONFIG_HTTP_HAS_CGI)
! 100: while (cgiexts)
! 101: {
! 102: struct cgiextstruct *cp = cgiexts->next;
! 103: if (cp == NULL) /* last entry */
! 104: free(cgiexts->ext);
! 105: free(cgiexts);
! 106: cgiexts = cp;
! 107: }
! 108: #endif
! 109:
! 110: exit(0);
! 111: }
! 112:
! 113: static void die(int sigtype)
! 114: {
! 115: exit(0);
! 116: }
! 117: #endif
! 118:
! 119: int main(int argc, char *argv[])
! 120: {
! 121: fd_set rfds, wfds;
! 122: struct connstruct *tp, *to;
! 123: struct serverstruct *sp;
! 124: int rnum, wnum, active;
! 125: int i = 1;
! 126: time_t currtime;
! 127: char *httpAddress = NULL;
! 128: int httpPort = CONFIG_HTTP_PORT;
! 129: char *httpsAddress = NULL;
! 130: int httpsPort = CONFIG_HTTP_HTTPS_PORT;
! 131: char *portStr;
! 132:
! 133: #ifdef WIN32
! 134: WORD wVersionRequested = MAKEWORD(2, 2);
! 135: WSADATA wsaData;
! 136: WSAStartup(wVersionRequested,&wsaData);
! 137: #else
! 138: signal(SIGPIPE, SIG_IGN);
! 139: #if defined(CONFIG_HTTP_HAS_CGI)
! 140: signal(SIGCHLD, reaper);
! 141: #endif
! 142: #ifdef CONFIG_HTTP_VERBOSE
! 143: signal(SIGQUIT, die);
! 144: #endif
! 145: #endif
! 146:
! 147: #ifdef CONFIG_HTTP_VERBOSE
! 148: signal(SIGTERM, die);
! 149: signal(SIGINT, sigint_cleanup);
! 150: #endif
! 151: tdate_init();
! 152:
! 153: /* get some command-line parameters */
! 154: while (argv[i] != NULL)
! 155: {
! 156: if (strcmp(argv[i], "-p") == 0 && argv[i+1] != NULL)
! 157: {
! 158: if ((portStr = strchr(argv[i+1], ':')) != NULL)
! 159: {
! 160: httpAddress = argv[i+1];
! 161: *portStr = 0;
! 162: httpPort = atoi(portStr + 1);
! 163: }
! 164: else
! 165: httpPort = atoi(argv[i+1]);
! 166:
! 167: i += 2;
! 168: continue;
! 169: }
! 170:
! 171: if (strcmp(argv[i], "-s") == 0 && argv[i+1] != NULL)
! 172: {
! 173: if ((portStr = strchr(argv[i+1], ':')) != NULL)
! 174: {
! 175: httpsAddress = argv[i+1];
! 176: *portStr = 0;
! 177: httpsPort = atoi(portStr + 1);
! 178: }
! 179: else
! 180: httpsPort = atoi(argv[i+1]);
! 181:
! 182: i += 2;
! 183: continue;
! 184: }
! 185:
! 186: if (strcmp(argv[i], "-w") == 0 && argv[i+1] != NULL)
! 187: {
! 188: webroot = argv[i+1];
! 189: i += 2;
! 190: continue;
! 191: }
! 192:
! 193: printf("%s:\n"
! 194: " [-p [address:]httpport]\n"
! 195: " [-s [address:]httpsport]\n"
! 196: " [-w webroot]\n", argv[0]);
! 197: exit(1);
! 198: }
! 199:
! 200: for (i = 0; i < INITIAL_CONNECTION_SLOTS; i++)
! 201: {
! 202: tp = freeconns;
! 203: freeconns = (struct connstruct *)calloc(1, sizeof(struct connstruct));
! 204: freeconns->next = tp;
! 205: }
! 206:
! 207: if ((active = openlistener(httpAddress, httpPort)) == -1)
! 208: {
! 209: #ifdef CONFIG_HTTP_VERBOSE
! 210: fprintf(stderr, "ERR: Couldn't bind to port %d\n", httpPort);
! 211: #endif
! 212: exit(1);
! 213: }
! 214:
! 215: addtoservers(active);
! 216:
! 217: if ((active = openlistener(httpsAddress, httpsPort)) == -1)
! 218: {
! 219: #ifdef CONFIG_HTTP_VERBOSE
! 220: fprintf(stderr, "ERR: Couldn't bind to port %d\n", httpsPort);
! 221: #endif
! 222: exit(1);
! 223: }
! 224:
! 225: addtoservers(active);
! 226: servers->ssl_ctx = ssl_ctx_new(CONFIG_HTTP_DEFAULT_SSL_OPTIONS,
! 227: CONFIG_HTTP_SESSION_CACHE_SIZE);
! 228: servers->is_ssl = 1;
! 229:
! 230: #if defined(CONFIG_HTTP_HAS_CGI)
! 231: addcgiext(CONFIG_HTTP_CGI_EXTENSIONS);
! 232: #endif
! 233:
! 234: #if defined(CONFIG_HTTP_VERBOSE)
! 235: #if defined(CONFIG_HTTP_HAS_CGI)
! 236: printf("addcgiext %s\n", CONFIG_HTTP_CGI_EXTENSIONS);
! 237: #endif
! 238: printf("%s: listening on ports %d (http) and %d (https)\n",
! 239: server_version, httpPort, httpsPort);
! 240: TTY_FLUSH();
! 241: #endif
! 242:
! 243: ax_chdir();
! 244:
! 245: #ifdef CONFIG_HTTP_ENABLE_DIFFERENT_USER
! 246: {
! 247: struct passwd *pd = getpwnam(CONFIG_HTTP_USER);
! 248:
! 249: if (pd != NULL)
! 250: {
! 251: int res = setuid(pd->pw_uid);
! 252: res |= setgid(pd->pw_gid);
! 253:
! 254: #if defined(CONFIG_HTTP_VERBOSE)
! 255: if (res == 0)
! 256: {
! 257: printf("change to '%s' successful\n", CONFIG_HTTP_USER);
! 258: TTY_FLUSH();
! 259: }
! 260: #endif
! 261: }
! 262:
! 263: }
! 264: #endif
! 265:
! 266:
! 267: #ifndef WIN32
! 268: #ifdef CONFIG_HTTP_IS_DAEMON
! 269: if (fork() > 0) /* parent will die */
! 270: exit(0);
! 271:
! 272: setsid();
! 273: #endif
! 274: #endif
! 275:
! 276: /* main loop */
! 277: while (1)
! 278: {
! 279: struct timeval tv = { 10, 0 };
! 280: FD_ZERO(&rfds);
! 281: FD_ZERO(&wfds);
! 282: rnum = wnum = -1;
! 283: sp = servers;
! 284:
! 285: while (sp != NULL) /* read each server port */
! 286: {
! 287: FD_SET(sp->sd, &rfds);
! 288:
! 289: if (sp->sd > rnum)
! 290: rnum = sp->sd;
! 291: sp = sp->next;
! 292: }
! 293:
! 294: /* Add the established sockets */
! 295: tp = usedconns;
! 296: currtime = time(NULL);
! 297:
! 298: while (tp != NULL)
! 299: {
! 300: if (currtime > tp->timeout) /* timed out? Kill it. */
! 301: {
! 302: to = tp;
! 303: tp = tp->next;
! 304: removeconnection(to);
! 305: continue;
! 306: }
! 307:
! 308: if (tp->state == STATE_WANT_TO_READ_HEAD)
! 309: {
! 310: FD_SET(tp->networkdesc, &rfds);
! 311: if (tp->networkdesc > rnum)
! 312: rnum = tp->networkdesc;
! 313: }
! 314:
! 315: if (tp->state == STATE_WANT_TO_SEND_HEAD)
! 316: {
! 317: FD_SET(tp->networkdesc, &wfds);
! 318: if (tp->networkdesc > wnum)
! 319: wnum = tp->networkdesc;
! 320: }
! 321:
! 322: if (tp->state == STATE_WANT_TO_READ_FILE)
! 323: {
! 324: FD_SET(tp->filedesc, &rfds);
! 325: if (tp->filedesc > rnum)
! 326: rnum = tp->filedesc;
! 327: }
! 328:
! 329: if (tp->state == STATE_WANT_TO_SEND_FILE)
! 330: {
! 331: FD_SET(tp->networkdesc, &wfds);
! 332: if (tp->networkdesc > wnum)
! 333: wnum = tp->networkdesc;
! 334: }
! 335:
! 336: #if defined(CONFIG_HTTP_DIRECTORIES)
! 337: if (tp->state == STATE_DOING_DIR)
! 338: {
! 339: FD_SET(tp->networkdesc, &wfds);
! 340: if (tp->networkdesc > wnum)
! 341: wnum = tp->networkdesc;
! 342: }
! 343: #endif
! 344: tp = tp->next;
! 345: }
! 346:
! 347: active = select(wnum > rnum ? wnum+1 : rnum+1,
! 348: rnum != -1 ? &rfds : NULL,
! 349: wnum != -1 ? &wfds : NULL,
! 350: NULL, usedconns ? &tv : NULL);
! 351:
! 352: /* timeout? */
! 353: if (active == 0)
! 354: continue;
! 355:
! 356: /* New connection? */
! 357: sp = servers;
! 358: while (active > 0 && sp != NULL)
! 359: {
! 360: if (FD_ISSET(sp->sd, &rfds))
! 361: {
! 362: handlenewconnection(sp->sd, sp->is_ssl);
! 363: active--;
! 364: }
! 365:
! 366: sp = sp->next;
! 367: }
! 368:
! 369: /* Handle the established sockets */
! 370: tp = usedconns;
! 371:
! 372: while (active > 0 && tp != NULL)
! 373: {
! 374: to = tp;
! 375: tp = tp->next;
! 376:
! 377: if (to->state == STATE_WANT_TO_READ_HEAD &&
! 378: FD_ISSET(to->networkdesc, &rfds))
! 379: {
! 380: active--;
! 381: #if defined(CONFIG_HTTP_HAS_CGI)
! 382: if (to->post_state)
! 383: read_post_data(to);
! 384: else
! 385: #endif
! 386: procreadhead(to);
! 387: }
! 388:
! 389: if (to->state == STATE_WANT_TO_SEND_HEAD &&
! 390: FD_ISSET(to->networkdesc, &wfds))
! 391: {
! 392: active--;
! 393: procsendhead(to);
! 394: }
! 395:
! 396: if (to->state == STATE_WANT_TO_READ_FILE &&
! 397: FD_ISSET(to->filedesc, &rfds))
! 398: {
! 399: active--;
! 400: procreadfile(to);
! 401: }
! 402:
! 403: if (to->state == STATE_WANT_TO_SEND_FILE &&
! 404: FD_ISSET(to->networkdesc, &wfds))
! 405: {
! 406: active--;
! 407: procsendfile(to);
! 408: }
! 409:
! 410: #if defined(CONFIG_HTTP_DIRECTORIES)
! 411: if (to->state == STATE_DOING_DIR &&
! 412: FD_ISSET(to->networkdesc, &wfds))
! 413: {
! 414: active--;
! 415: procdodir(to);
! 416: }
! 417: #endif
! 418: }
! 419: }
! 420:
! 421: return 0;
! 422: }
! 423:
! 424: #if defined(CONFIG_HTTP_HAS_CGI)
! 425: static void addcgiext(const char *cgi_exts)
! 426: {
! 427: char *cp = strdup(cgi_exts);
! 428:
! 429: /* extenstions are comma separated */
! 430: do
! 431: {
! 432: struct cgiextstruct *ex = (struct cgiextstruct *)
! 433: malloc(sizeof(struct cgiextstruct));
! 434: ex->ext = cp;
! 435: ex->next = cgiexts;
! 436: cgiexts = ex;
! 437: if ((cp = strchr(cp, ',')) != NULL)
! 438: *cp++ = 0;
! 439: } while (cp != NULL);
! 440: }
! 441: #endif
! 442:
! 443: static void addtoservers(int sd)
! 444: {
! 445: struct serverstruct *tp = (struct serverstruct *)
! 446: calloc(1, sizeof(struct serverstruct));
! 447: tp->next = servers;
! 448: tp->sd = sd;
! 449: servers = tp;
! 450: }
! 451:
! 452: #ifdef HAVE_IPV6
! 453: static void handlenewconnection(int listenfd, int is_ssl)
! 454: {
! 455: struct sockaddr_in6 their_addr;
! 456: socklen_t tp = sizeof(their_addr);
! 457: char ipbuf[100];
! 458: int connfd = accept(listenfd, (struct sockaddr *)&their_addr, &tp);
! 459:
! 460: if (tp == sizeof(struct sockaddr_in6))
! 461: inet_ntop(AF_INET6, &their_addr.sin6_addr, ipbuf, sizeof(ipbuf));
! 462: else if (tp == sizeof(struct sockaddr_in))
! 463: inet_ntop(AF_INET, &(((struct sockaddr_in *)&their_addr)->sin_addr),
! 464: ipbuf, sizeof(ipbuf));
! 465: else
! 466: *ipbuf = '\0';
! 467:
! 468: if (connfd != -1) /* check for error condition */
! 469: addconnection(connfd, ipbuf, is_ssl);
! 470: }
! 471:
! 472: #else
! 473: static void handlenewconnection(int listenfd, int is_ssl)
! 474: {
! 475: struct sockaddr_in their_addr;
! 476: socklen_t tp = sizeof(struct sockaddr_in);
! 477: int connfd = accept(listenfd, (struct sockaddr *)&their_addr, &tp);
! 478: addconnection(connfd, inet_ntoa(their_addr.sin_addr), is_ssl);
! 479: }
! 480: #endif
! 481:
! 482: static int openlistener(char *address, int port)
! 483: {
! 484: int sd;
! 485: #ifdef WIN32
! 486: char tp = 1;
! 487: #else
! 488: int tp = 1;
! 489: #endif
! 490: #ifndef HAVE_IPV6
! 491: struct sockaddr_in my_addr;
! 492:
! 493: if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
! 494: return -1;
! 495:
! 496: memset(&my_addr, 0, sizeof(my_addr));
! 497: my_addr.sin_family = AF_INET;
! 498: my_addr.sin_port = htons((short)port);
! 499: my_addr.sin_addr.s_addr = address == NULL ?
! 500: INADDR_ANY : inet_addr(address);
! 501: #else
! 502: struct sockaddr_in6 my_addr;
! 503:
! 504: if ((sd = socket(AF_INET6, SOCK_STREAM, 0)) == -1)
! 505: return -1;
! 506:
! 507: my_addr.sin6_family = AF_INET6;
! 508: my_addr.sin6_port = htons(port);
! 509:
! 510: if (address == NULL)
! 511: my_addr.sin6_addr = in6addr_any;
! 512: else
! 513: inet_pton(AF_INET6, address, &my_addr.sin6_addr);
! 514: #endif
! 515:
! 516: setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &tp, sizeof(tp));
! 517: if (bind(sd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1)
! 518: {
! 519: close(sd);
! 520: return -1;
! 521: }
! 522:
! 523: listen(sd, BACKLOG);
! 524: return sd;
! 525: }
! 526:
! 527: /* Wrapper function for strncpy() that guarantees
! 528: a null-terminated string. This is to avoid any possible
! 529: issues due to strncpy()'s behaviour.
! 530: */
! 531: char *my_strncpy(char *dest, const char *src, size_t n)
! 532: {
! 533: strncpy(dest, src, n);
! 534: dest[n-1] = '\0';
! 535: return dest;
! 536: }
! 537:
! 538: int isdir(const char *tpbuf)
! 539: {
! 540: struct stat st;
! 541: char path[MAXREQUESTLENGTH];
! 542: strcpy(path, tpbuf);
! 543:
! 544: #ifdef WIN32 /* win32 stat() can't handle trailing '\' */
! 545: if (path[strlen(path)-1] == '\\')
! 546: path[strlen(path)-1] = 0;
! 547: #endif
! 548:
! 549: if (stat(path, &st) == -1)
! 550: return 0;
! 551:
! 552: if ((st.st_mode & S_IFMT) == S_IFDIR)
! 553: return 1;
! 554:
! 555: return 0;
! 556: }
! 557:
! 558: static void addconnection(int sd, char *ip, int is_ssl)
! 559: {
! 560: struct connstruct *tp;
! 561:
! 562: /* Get ourselves a connstruct */
! 563: if (freeconns == NULL)
! 564: tp = (struct connstruct *)calloc(1, sizeof(struct connstruct));
! 565: else
! 566: {
! 567: tp = freeconns;
! 568: freeconns = tp->next;
! 569: }
! 570:
! 571: /* Attach it to the used list */
! 572: tp->next = usedconns;
! 573: usedconns = tp;
! 574: tp->networkdesc = sd;
! 575:
! 576: if (is_ssl)
! 577: tp->ssl = ssl_server_new(servers->ssl_ctx, sd);
! 578:
! 579: tp->is_ssl = is_ssl;
! 580: tp->filedesc = -1;
! 581: #if defined(CONFIG_HTTP_HAS_DIRECTORIES)
! 582: tp->dirp = NULL;
! 583: #endif
! 584: *tp->actualfile = '\0';
! 585: *tp->filereq = '\0';
! 586: tp->state = STATE_WANT_TO_READ_HEAD;
! 587: tp->reqtype = TYPE_GET;
! 588: tp->close_when_done = 0;
! 589: tp->timeout = time(NULL) + CONFIG_HTTP_TIMEOUT;
! 590: #if defined(CONFIG_HTTP_HAS_CGI)
! 591: strcpy(tp->remote_addr, ip);
! 592: #endif
! 593: }
! 594:
! 595: void removeconnection(struct connstruct *cn)
! 596: {
! 597: struct connstruct *tp;
! 598: int shouldret = 0;
! 599:
! 600: tp = usedconns;
! 601:
! 602: if (tp == NULL || cn == NULL)
! 603: shouldret = 1;
! 604: else if (tp == cn)
! 605: usedconns = tp->next;
! 606: else
! 607: {
! 608: while (tp != NULL)
! 609: {
! 610: if (tp->next == cn)
! 611: {
! 612: tp->next = (tp->next)->next;
! 613: shouldret = 0;
! 614: break;
! 615: }
! 616:
! 617: tp = tp->next;
! 618: shouldret = 1;
! 619: }
! 620: }
! 621:
! 622: if (shouldret)
! 623: return;
! 624:
! 625: /* If we did, add it to the free list */
! 626: cn->next = freeconns;
! 627: freeconns = cn;
! 628:
! 629: /* Close it all down */
! 630: if (cn->networkdesc != -1)
! 631: {
! 632: if (cn->is_ssl)
! 633: {
! 634: ssl_free(cn->ssl);
! 635: cn->ssl = NULL;
! 636: }
! 637:
! 638: #ifndef WIN32
! 639: shutdown(cn->networkdesc, SHUT_WR);
! 640: #endif
! 641: SOCKET_CLOSE(cn->networkdesc);
! 642: }
! 643:
! 644: if (cn->filedesc != -1)
! 645: close(cn->filedesc);
! 646:
! 647: #if defined(CONFIG_HTTP_HAS_DIRECTORIES)
! 648: if (cn->dirp != NULL)
! 649: #ifdef WIN32
! 650: FindClose(cn->dirp);
! 651: #else
! 652: closedir(cn->dirp);
! 653: #endif
! 654: #endif
! 655: }
! 656:
! 657: /*
! 658: * Change directories one way or the other.
! 659: */
! 660:
! 661: static void ax_chdir(void)
! 662: {
! 663: if (chdir(webroot))
! 664: {
! 665: #ifdef CONFIG_HTTP_VERBOSE
! 666: fprintf(stderr, "'%s' is not a directory\n", webroot);
! 667: #endif
! 668: exit(1);
! 669: }
! 670: }
! 671:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>