Magellan Linux

Diff of /trunk/mkinitrd-magellan/busybox/networking/httpd.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 983 by niro, Fri Apr 24 18:33:46 2009 UTC revision 984 by niro, Sun May 30 11:32:42 2010 UTC
# Line 20  Line 20 
20   * httpd -p 80 -u 80 -h /www -c /etc/httpd.conf -r "Web Server Authentication"   * httpd -p 80 -u 80 -h /www -c /etc/httpd.conf -r "Web Server Authentication"
21   *   *
22   *   *
23   * When a url starts by "/cgi-bin/" it is assumed to be a cgi script.  The   * When an url starts by "/cgi-bin/" it is assumed to be a cgi script.  The
24   * server changes directory to the location of the script and executes it   * server changes directory to the location of the script and executes it
25   * after setting QUERY_STRING and other environment variables.   * after setting QUERY_STRING and other environment variables.
26   *   *
27   * Doc:   * Doc:
28   * "CGI Environment Variables": http://hoohoo.ncsa.uiuc.edu/cgi/env.html   * "CGI Environment Variables": http://hoohoo.ncsa.uiuc.edu/cgi/env.html
29   *   *
30   * The applet can also be invoked as a url arg decoder and html text encoder   * The applet can also be invoked as an url arg decoder and html text encoder
31   * as follows:   * as follows:
32   *  foo=`httpd -d $foo`           # decode "Hello%20World" as "Hello World"   *  foo=`httpd -d $foo`           # decode "Hello%20World" as "Hello World"
33   *  bar=`httpd -e "<Hello World>"`  # encode as "&#60Hello&#32World&#62"   *  bar=`httpd -e "<Hello World>"`  # encode as "&#60Hello&#32World&#62"
34   * Note that url encoding for arguments is not the same as html encoding for   * Note that url encoding for arguments is not the same as html encoding for
35   * presentation.  -d decodes a url-encoded argument while -e encodes in html   * presentation.  -d decodes an url-encoded argument while -e encodes in html
36   * for page display.   * for page display.
37   *   *
38   * httpd.conf has the following format:   * httpd.conf has the following format:
# Line 54  Line 54 
54   * /adm:admin:setup  # Require user admin, pwd setup on urls starting with /adm/   * /adm:admin:setup  # Require user admin, pwd setup on urls starting with /adm/
55   * /adm:toor:PaSsWd  # or user toor, pwd PaSsWd on urls starting with /adm/   * /adm:toor:PaSsWd  # or user toor, pwd PaSsWd on urls starting with /adm/
56   * .au:audio/basic   # additional mime type for audio.au files   * .au:audio/basic   # additional mime type for audio.au files
57   * *.php:/path/php   # running cgi.php scripts through an interpreter   * *.php:/path/php   # run xxx.php through an interpreter
58   *   *
59   * A/D may be as a/d or allow/deny - only first char matters.   * A/D may be as a/d or allow/deny - only first char matters.
60   * Deny/Allow IP logic:   * Deny/Allow IP logic:
# Line 94  Line 94 
94   * server exits with an error.   * server exits with an error.
95   *   *
96   */   */
97     /* TODO: use TCP_CORK, parse_config() */
98    
99  #include "libbb.h"  #include "libbb.h"
100  #if ENABLE_FEATURE_HTTPD_USE_SENDFILE  #if ENABLE_FEATURE_HTTPD_USE_SENDFILE
101  #include <sys/sendfile.h>  # include <sys/sendfile.h>
102  #endif  #endif
103    
 //#define DEBUG 1  
104  #define DEBUG 0  #define DEBUG 0
105    
106  #define IOBUF_SIZE 8192    /* IO buffer */  #define IOBUF_SIZE 8192    /* IO buffer */
# Line 115  Line 115 
115    
116  #define HEADER_READ_TIMEOUT 60  #define HEADER_READ_TIMEOUT 60
117    
118  static const char default_path_httpd_conf[] ALIGN1 = "/etc";  static const char DEFAULT_PATH_HTTPD_CONF[] ALIGN1 = "/etc";
119  static const char httpd_conf[] ALIGN1 = "httpd.conf";  static const char HTTPD_CONF[] ALIGN1 = "httpd.conf";
120  static const char HTTP_200[] ALIGN1 = "HTTP/1.0 200 OK\r\n";  static const char HTTP_200[] ALIGN1 = "HTTP/1.0 200 OK\r\n";
121    
122  typedef struct has_next_ptr {  typedef struct has_next_ptr {
# Line 231  static const struct { Line 231  static const struct {
231  #endif  #endif
232  };  };
233    
234    static const char index_html[] ALIGN1 = "index.html";
235    
236    
237  struct globals {  struct globals {
238   int verbose;            /* must be int (used by getopt32) */   int verbose;            /* must be int (used by getopt32) */
# Line 242  struct globals { Line 244  struct globals {
244   const char *bind_addr_or_port;   const char *bind_addr_or_port;
245    
246   const char *g_query;   const char *g_query;
247   const char *configFile;   const char *opt_c_configFile;
248   const char *home_httpd;   const char *home_httpd;
249   const char *index_page;   const char *index_page;
250    
# Line 250  struct globals { Line 252  struct globals {
252   const char *found_moved_temporarily;   const char *found_moved_temporarily;
253   Htaccess_IP *ip_a_d;    /* config allow/deny lines */   Htaccess_IP *ip_a_d;    /* config allow/deny lines */
254    
255   USE_FEATURE_HTTPD_BASIC_AUTH(const char *g_realm;)   IF_FEATURE_HTTPD_BASIC_AUTH(const char *g_realm;)
256   USE_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;)   IF_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;)
257   USE_FEATURE_HTTPD_CGI(char *referer;)   IF_FEATURE_HTTPD_CGI(char *referer;)
258   USE_FEATURE_HTTPD_CGI(char *user_agent;)   IF_FEATURE_HTTPD_CGI(char *user_agent;)
259   USE_FEATURE_HTTPD_CGI(char *http_accept;)   IF_FEATURE_HTTPD_CGI(char *host;)
260   USE_FEATURE_HTTPD_CGI(char *http_accept_language;)   IF_FEATURE_HTTPD_CGI(char *http_accept;)
261     IF_FEATURE_HTTPD_CGI(char *http_accept_language;)
262    
263   off_t file_size;        /* -1 - unknown */   off_t file_size;        /* -1 - unknown */
264  #if ENABLE_FEATURE_HTTPD_RANGES  #if ENABLE_FEATURE_HTTPD_RANGES
# Line 267  struct globals { Line 270  struct globals {
270  #if ENABLE_FEATURE_HTTPD_BASIC_AUTH  #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
271   Htaccess *g_auth;       /* config user:password lines */   Htaccess *g_auth;       /* config user:password lines */
272  #endif  #endif
 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES  
273   Htaccess *mime_a;       /* config mime types */   Htaccess *mime_a;       /* config mime types */
 #endif  
274  #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR  #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
275   Htaccess *script_i;     /* config script interpreters */   Htaccess *script_i;     /* config script interpreters */
276  #endif  #endif
# Line 290  struct globals { Line 291  struct globals {
291  #define rmt_ip            (G.rmt_ip           )  #define rmt_ip            (G.rmt_ip           )
292  #define bind_addr_or_port (G.bind_addr_or_port)  #define bind_addr_or_port (G.bind_addr_or_port)
293  #define g_query           (G.g_query          )  #define g_query           (G.g_query          )
294  #define configFile        (G.configFile       )  #define opt_c_configFile  (G.opt_c_configFile )
295  #define home_httpd        (G.home_httpd       )  #define home_httpd        (G.home_httpd       )
296  #define index_page        (G.index_page       )  #define index_page        (G.index_page       )
297  #define found_mime_type   (G.found_mime_type  )  #define found_mime_type   (G.found_mime_type  )
# Line 301  struct globals { Line 302  struct globals {
302  #define remoteuser        (G.remoteuser       )  #define remoteuser        (G.remoteuser       )
303  #define referer           (G.referer          )  #define referer           (G.referer          )
304  #define user_agent        (G.user_agent       )  #define user_agent        (G.user_agent       )
305    #define host              (G.host             )
306  #define http_accept       (G.http_accept      )  #define http_accept       (G.http_accept      )
307  #define http_accept_language (G.http_accept_language)  #define http_accept_language (G.http_accept_language)
308  #define file_size         (G.file_size        )  #define file_size         (G.file_size        )
# Line 308  struct globals { Line 310  struct globals {
310  #define range_start       (G.range_start      )  #define range_start       (G.range_start      )
311  #define range_end         (G.range_end        )  #define range_end         (G.range_end        )
312  #define range_len         (G.range_len        )  #define range_len         (G.range_len        )
313    #else
314    enum {
315     range_start = 0,
316     range_end = MAXINT(off_t) - 1,
317     range_len = MAXINT(off_t),
318    };
319  #endif  #endif
320  #define rmt_ip_str        (G.rmt_ip_str       )  #define rmt_ip_str        (G.rmt_ip_str       )
321  #define g_auth            (G.g_auth           )  #define g_auth            (G.g_auth           )
# Line 320  struct globals { Line 328  struct globals {
328  #define proxy             (G.proxy            )  #define proxy             (G.proxy            )
329  #define INIT_G() do { \  #define INIT_G() do { \
330   SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \   SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
331   USE_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \   IF_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \
332   bind_addr_or_port = "80"; \   bind_addr_or_port = "80"; \
333   index_page = "index.html"; \   index_page = index_html; \
334   file_size = -1; \   file_size = -1; \
335  } while (0)  } while (0)
336    
 #if !ENABLE_FEATURE_HTTPD_RANGES  
 enum {  
  range_start = 0,  
  range_end = MAXINT(off_t) - 1,  
  range_len = MAXINT(off_t),  
 };  
 #endif  
   
337    
338  #define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1)  #define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1)
339    
# Line 356  static void free_llist(has_next_ptr **pp Line 356  static void free_llist(has_next_ptr **pp
356   *pptr = NULL;   *pptr = NULL;
357  }  }
358    
 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH \  
  || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \  
  || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR  
359  static ALWAYS_INLINE void free_Htaccess_list(Htaccess **pptr)  static ALWAYS_INLINE void free_Htaccess_list(Htaccess **pptr)
360  {  {
361   free_llist((has_next_ptr**)pptr);   free_llist((has_next_ptr**)pptr);
362  }  }
 #endif  
363    
364  static ALWAYS_INLINE void free_Htaccess_IP_list(Htaccess_IP **pptr)  static ALWAYS_INLINE void free_Htaccess_IP_list(Htaccess_IP **pptr)
365  {  {
# Line 458  static int scan_ip_mask(const char *str, Line 454  static int scan_ip_mask(const char *str,
454  /*  /*
455   * Parse configuration file into in-memory linked list.   * Parse configuration file into in-memory linked list.
456   *   *
  * The first non-white character is examined to determine if the config line  
  * is one of the following:  
  *    .ext:mime/type   # new mime type not compiled into httpd  
  *    [adAD]:from      # ip address allow/deny, * for wildcard  
  *    /path:user:pass  # username/password  
  *    Ennn:error.html  # error page for status nnn  
  *    P:/url:[http://]hostname[:port]/new/path # reverse proxy  
  *  
457   * Any previous IP rules are discarded.   * Any previous IP rules are discarded.
458   * If the flag argument is not SUBDIR_PARSE then all /path and mime rules   * If the flag argument is not SUBDIR_PARSE then all /path and mime rules
459   * are also discarded.  That is, previous settings are retained if flag is   * are also discarded.  That is, previous settings are retained if flag is
# Line 475  static int scan_ip_mask(const char *str, Line 463  static int scan_ip_mask(const char *str,
463   * path   Path where to look for httpd.conf (without filename).   * path   Path where to look for httpd.conf (without filename).
464   * flag   Type of the parse request.   * flag   Type of the parse request.
465   */   */
466  /* flag */  /* flag param: */
467  #define FIRST_PARSE          0  enum {
468  #define SUBDIR_PARSE         1   FIRST_PARSE    = 0, /* path will be "/etc" */
469  #define SIGNALED_PARSE       2   SIGNALED_PARSE = 1, /* path will be "/etc" */
470  #define FIND_FROM_HTTPD_ROOT 3   SUBDIR_PARSE   = 2, /* path will be derived from URL */
471    };
472  static void parse_conf(const char *path, int flag)  static void parse_conf(const char *path, int flag)
473  {  {
474     /* internally used extra flag state */
475     enum { TRY_CURDIR_PARSE = 3 };
476    
477   FILE *f;   FILE *f;
478  #if ENABLE_FEATURE_HTTPD_BASIC_AUTH   const char *filename;
  Htaccess *prev;  
 #endif  
 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH \  
  || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \  
  || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR  
  Htaccess *cur;  
 #endif  
  const char *filename = configFile;  
479   char buf[160];   char buf[160];
  char *p, *p0;  
  char *after_colon;  
  Htaccess_IP *pip;  
480    
481   /* discard old rules */   /* discard old rules */
482   free_Htaccess_IP_list(&ip_a_d);   free_Htaccess_IP_list(&ip_a_d);
483   flg_deny_all = 0;   flg_deny_all = 0;
 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH \  
  || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \  
  || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR  
484   /* retain previous auth and mime config only for subdir parse */   /* retain previous auth and mime config only for subdir parse */
485   if (flag != SUBDIR_PARSE) {   if (flag != SUBDIR_PARSE) {
486     free_Htaccess_list(&mime_a);
487  #if ENABLE_FEATURE_HTTPD_BASIC_AUTH  #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
488   free_Htaccess_list(&g_auth);   free_Htaccess_list(&g_auth);
489  #endif  #endif
 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES  
  free_Htaccess_list(&mime_a);  
 #endif  
490  #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR  #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
491   free_Htaccess_list(&script_i);   free_Htaccess_list(&script_i);
492  #endif  #endif
493   }   }
 #endif  
494    
495     filename = opt_c_configFile;
496   if (flag == SUBDIR_PARSE || filename == NULL) {   if (flag == SUBDIR_PARSE || filename == NULL) {
497   filename = alloca(strlen(path) + sizeof(httpd_conf) + 2);   filename = alloca(strlen(path) + sizeof(HTTPD_CONF) + 2);
498   sprintf((char *)filename, "%s/%s", path, httpd_conf);   sprintf((char *)filename, "%s/%s", path, HTTPD_CONF);
499   }   }
500    
501   while ((f = fopen_for_read(filename)) == NULL) {   while ((f = fopen_for_read(filename)) == NULL) {
502   if (flag == SUBDIR_PARSE || flag == FIND_FROM_HTTPD_ROOT) {   if (flag >= SUBDIR_PARSE) { /* SUBDIR or TRY_CURDIR */
503   /* config file not found, no changes to config */   /* config file not found, no changes to config */
504   return;   return;
505   }   }
506   if (configFile && flag == FIRST_PARSE) /* if -c option given */   if (flag == FIRST_PARSE) {
507   bb_simple_perror_msg_and_die(filename);   /* -c CONFFILE given, but CONFFILE doesn't exist? */
508   flag = FIND_FROM_HTTPD_ROOT;   if (opt_c_configFile)
509   filename = httpd_conf;   bb_simple_perror_msg_and_die(opt_c_configFile);
510     /* else: no -c, thus we looked at /etc/httpd.conf,
511     * and it's not there. try ./httpd.conf: */
512     }
513     flag = TRY_CURDIR_PARSE;
514     filename = HTTPD_CONF;
515   }   }
516    
517  #if ENABLE_FEATURE_HTTPD_BASIC_AUTH  #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
518   prev = g_auth;   /* in "/file:user:pass" lines, we prepend path in subdirs */
519  #endif   if (flag != SUBDIR_PARSE)
520   /* This could stand some work */   path = "";
521   while ((p0 = fgets(buf, sizeof(buf), f)) != NULL) {  #endif
522   after_colon = NULL;   /* The lines can be:
523   for (p = p0; *p0 != '\0' && *p0 != '#'; p0++) {   *
524   if (!isspace(*p0)) {   * I:default_index_file
525   *p++ = *p0;   * H:http_home
526   if (*p0 == ':' && after_colon == NULL)   * [AD]:IP[/mask]   # allow/deny, * for wildcard
527   after_colon = p;   * Ennn:error.html  # error page for status nnn
528     * P:/url:[http://]hostname[:port]/new/path # reverse proxy
529     * .ext:mime/type   # mime type
530     * *.php:/path/php  # run xxx.php through an interpreter
531     * /file:user:pass  # username and password
532     */
533     while (fgets(buf, sizeof(buf), f) != NULL) {
534     unsigned strlen_buf;
535     unsigned char ch;
536     char *after_colon;
537    
538     { /* remove all whitespace, and # comments */
539     char *p, *p0;
540    
541     p0 = buf;
542     /* skip non-whitespace beginning. Often the whole line
543     * is non-whitespace. We want this case to work fast,
544     * without needless copying, therefore we don't merge
545     * this operation into next while loop. */
546     while ((ch = *p0) != '\0' && ch != '\n' && ch != '#'
547     && ch != ' ' && ch != '\t'
548     ) {
549     p0++;
550   }   }
551     p = p0;
552     /* if we enter this loop, we have some whitespace.
553     * discard it */
554     while (ch != '\0' && ch != '\n' && ch != '#') {
555     if (ch != ' ' && ch != '\t') {
556     *p++ = ch;
557     }
558     ch = *++p0;
559     }
560     *p = '\0';
561     strlen_buf = p - buf;
562     if (strlen_buf == 0)
563     continue; /* empty line */
564   }   }
  *p = '\0';  
565    
566   /* test for empty or strange line */   after_colon = strchr(buf, ':');
567   if (after_colon == NULL || *after_colon == '\0')   /* strange line? */
568     if (after_colon == NULL || *++after_colon == '\0')
569     goto config_error;
570    
571     ch = (buf[0] & ~0x20); /* toupper if it's a letter */
572    
573     if (ch == 'I') {
574     if (index_page != index_html)
575     free((char*)index_page);
576     index_page = xstrdup(after_colon);
577   continue;   continue;
578   p0 = buf;   }
579   if (*p0 == 'd' || *p0 == 'a')  
580   *p0 -= 0x20; /* a/d -> A/D */   /* do not allow jumping around using H in subdir's configs */
581   if (*after_colon == '*') {   if (flag == FIRST_PARSE && ch == 'H') {
582   if (*p0 == 'D') {   home_httpd = xstrdup(after_colon);
583   /* memorize "deny all" */   xchdir(home_httpd);
  flg_deny_all = 1;  
  }  
  /* skip assumed "A:*", it is a default anyway */  
584   continue;   continue;
585   }   }
586    
587   if (*p0 == 'A' || *p0 == 'D') {   if (ch == 'A' || ch == 'D') {
588   /* storing current config IP line */   Htaccess_IP *pip;
589   pip = xzalloc(sizeof(Htaccess_IP));  
590   if (scan_ip_mask(after_colon, &(pip->ip), &(pip->mask))) {   if (*after_colon == '*') {
591     if (ch == 'D') {
592     /* memorize "deny all" */
593     flg_deny_all = 1;
594     }
595     /* skip assumed "A:*", it is a default anyway */
596     continue;
597     }
598     /* store "allow/deny IP/mask" line */
599     pip = xzalloc(sizeof(*pip));
600     if (scan_ip_mask(after_colon, &pip->ip, &pip->mask)) {
601   /* IP{/mask} syntax error detected, protect all */   /* IP{/mask} syntax error detected, protect all */
602   *p0 = 'D';   ch = 'D';
603   pip->mask = 0;   pip->mask = 0;
604   }   }
605   pip->allow_deny = *p0;   pip->allow_deny = ch;
606   if (*p0 == 'D') {   if (ch == 'D') {
607   /* Deny:from_IP - prepend */   /* Deny:from_IP - prepend */
608   pip->next = ip_a_d;   pip->next = ip_a_d;
609   ip_a_d = pip;   ip_a_d = pip;
610   } else {   } else {
611   /* A:from_IP - append (thus D precedes A) */   /* A:from_IP - append (thus all D's precedes A's) */
612   Htaccess_IP *prev_IP = ip_a_d;   Htaccess_IP *prev_IP = ip_a_d;
613   if (prev_IP == NULL) {   if (prev_IP == NULL) {
614   ip_a_d = pip;   ip_a_d = pip;
# Line 591  static void parse_conf(const char *path, Line 622  static void parse_conf(const char *path,
622   }   }
623    
624  #if ENABLE_FEATURE_HTTPD_ERROR_PAGES  #if ENABLE_FEATURE_HTTPD_ERROR_PAGES
625   if (flag == FIRST_PARSE && *p0 == 'E') {   if (flag == FIRST_PARSE && ch == 'E') {
626   unsigned i;   unsigned i;
627   int status = atoi(++p0); /* error status code */   int status = atoi(buf + 1); /* error status code */
628    
629   if (status < HTTP_CONTINUE) {   if (status < HTTP_CONTINUE) {
630   bb_error_msg("config error '%s' in '%s'", buf, filename);   goto config_error;
  continue;  
631   }   }
632   /* then error page; find matching status */   /* then error page; find matching status */
633   for (i = 0; i < ARRAY_SIZE(http_response_type); i++) {   for (i = 0; i < ARRAY_SIZE(http_response_type); i++) {
# Line 613  static void parse_conf(const char *path, Line 644  static void parse_conf(const char *path,
644  #endif  #endif
645    
646  #if ENABLE_FEATURE_HTTPD_PROXY  #if ENABLE_FEATURE_HTTPD_PROXY
647   if (flag == FIRST_PARSE && *p0 == 'P') {   if (flag == FIRST_PARSE && ch == 'P') {
648   /* P:/url:[http://]hostname[:port]/new/path */   /* P:/url:[http://]hostname[:port]/new/path */
649   char *url_from, *host_port, *url_to;   char *url_from, *host_port, *url_to;
650   Htaccess_Proxy *proxy_entry;   Htaccess_Proxy *proxy_entry;
# Line 621  static void parse_conf(const char *path, Line 652  static void parse_conf(const char *path,
652   url_from = after_colon;   url_from = after_colon;
653   host_port = strchr(after_colon, ':');   host_port = strchr(after_colon, ':');
654   if (host_port == NULL) {   if (host_port == NULL) {
655   bb_error_msg("config error '%s' in '%s'", buf, filename);   goto config_error;
  continue;  
656   }   }
657   *host_port++ = '\0';   *host_port++ = '\0';
658   if (strncmp(host_port, "http://", 7) == 0)   if (strncmp(host_port, "http://", 7) == 0)
659   host_port += 7;   host_port += 7;
660   if (*host_port == '\0') {   if (*host_port == '\0') {
661   bb_error_msg("config error '%s' in '%s'", buf, filename);   goto config_error;
  continue;  
662   }   }
663   url_to = strchr(host_port, '/');   url_to = strchr(host_port, '/');
664   if (url_to == NULL) {   if (url_to == NULL) {
665   bb_error_msg("config error '%s' in '%s'", buf, filename);   goto config_error;
  continue;  
666   }   }
667   *url_to = '\0';   *url_to = '\0';
668   proxy_entry = xzalloc(sizeof(Htaccess_Proxy));   proxy_entry = xzalloc(sizeof(*proxy_entry));
669   proxy_entry->url_from = xstrdup(url_from);   proxy_entry->url_from = xstrdup(url_from);
670   proxy_entry->host_port = xstrdup(host_port);   proxy_entry->host_port = xstrdup(host_port);
671   *url_to = '/';   *url_to = '/';
# Line 647  static void parse_conf(const char *path, Line 675  static void parse_conf(const char *path,
675   continue;   continue;
676   }   }
677  #endif  #endif
678     /* the rest of directives are non-alphabetic,
679     * must avoid using "toupper'ed" ch */
680     ch = buf[0];
681    
682  #if ENABLE_FEATURE_HTTPD_BASIC_AUTH   if (ch == '.' /* ".ext:mime/type" */
683   if (*p0 == '/') {  #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
684   /* make full path from httpd root / current_path / config_line_path */   || (ch == '*' && buf[1] == '.') /* "*.php:/path/php" */
685   const char *tp = (flag == SUBDIR_PARSE ? path : "");  #endif
686   p0 = xmalloc(strlen(tp) + (after_colon - buf) + 2 + strlen(after_colon));   ) {
687   after_colon[-1] = '\0';   char *p;
688   sprintf(p0, "/%s%s", tp, buf);   Htaccess *cur;
   
  /* looks like bb_simplify_path... */  
  tp = p = p0;  
  do {  
  if (*p == '/') {  
  if (*tp == '/') {    /* skip duplicate (or initial) slash */  
  continue;  
  }  
  if (*tp == '.') {  
  if (tp[1] == '/' || tp[1] == '\0') { /* remove extra '.' */  
  continue;  
  }  
  if ((tp[1] == '.') && (tp[2] == '/' || tp[2] == '\0')) {  
  ++tp;  
  if (p > p0) {  
  while (*--p != '/') /* omit previous dir */  
  continue;  
  }  
  continue;  
  }  
  }  
  }  
  *++p = *tp;  
  } while (*++tp);  
689    
690   if ((p == p0) || (*p != '/')) { /* not a trailing slash */   cur = xzalloc(sizeof(*cur) /* includes space for NUL */ + strlen_buf);
691   ++p;                    /* so keep last character */   strcpy(cur->before_colon, buf);
692     p = cur->before_colon + (after_colon - buf);
693     p[-1] = '\0';
694     cur->after_colon = p;
695     if (ch == '.') {
696     /* .mime line: prepend to mime_a list */
697     cur->next = mime_a;
698     mime_a = cur;
699     }
700    #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
701     else {
702     /* script interpreter line: prepend to script_i list */
703     cur->next = script_i;
704     script_i = cur;
705   }   }
  *p = ':';  
  strcpy(p + 1, after_colon);  
  }  
706  #endif  #endif
  if (*p0 == 'I') {  
  index_page = xstrdup(after_colon);  
  continue;  
  }  
   
  /* Do not allow jumping around using H in subdir's configs */  
  if (flag == FIRST_PARSE && *p0 == 'H') {  
  home_httpd = xstrdup(after_colon);  
  xchdir(home_httpd);  
707   continue;   continue;
708   }   }
709    
 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH \  
  || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \  
  || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR  
  /* storing current config line */  
  cur = xzalloc(sizeof(Htaccess) + strlen(p0));  
  strcpy(cur->before_colon, p0);  
710  #if ENABLE_FEATURE_HTTPD_BASIC_AUTH  #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
711   if (*p0 == '/') /* was malloced - see above */   if (ch == '/') { /* "/file:user:pass" */
712   free(p0);   char *p;
713  #endif   Htaccess *cur;
714   cur->after_colon = strchr(cur->before_colon, ':');   unsigned file_len;
715   *cur->after_colon++ = '\0';  
716  #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES   /* note: path is "" unless we are in SUBDIR parse,
717   if (cur->before_colon[0] == '.') {   * otherwise it does NOT start with "/" */
718   /* .mime line: prepend to mime_a list */   cur = xzalloc(sizeof(*cur) /* includes space for NUL */
719   cur->next = mime_a;   + 1 + strlen(path)
720   mime_a = cur;   + strlen_buf
721   continue;   );
722   }   /* form "/path/file" */
723  #endif   sprintf(cur->before_colon, "/%s%.*s",
724  #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR   path,
725   if (cur->before_colon[0] == '*' && cur->before_colon[1] == '.') {   (int) (after_colon - buf - 1), /* includes "/", but not ":" */
726   /* script interpreter line: prepend to script_i list */   buf);
727   cur->next = script_i;   /* canonicalize it */
728   script_i = cur;   p = bb_simplify_abs_path_inplace(cur->before_colon);
729   continue;   file_len = p - cur->before_colon;
730   }   /* add "user:pass" after NUL */
731  #endif   strcpy(++p, after_colon);
732  #if ENABLE_FEATURE_HTTPD_BASIC_AUTH   cur->after_colon = p;
733  //TODO: we do not test for leading "/"??  
734  //also, do we leak cur if BASIC_AUTH is off?   /* insert cur into g_auth */
735   if (prev == NULL) {   /* g_auth is sorted by decreased filename length */
736   /* first line */   {
737   g_auth = prev = cur;   Htaccess *auth, **authp;
738   } else {  
739   /* sort path, if current length eq or bigger then move up */   authp = &g_auth;
740   Htaccess *prev_hti = g_auth;   while ((auth = *authp) != NULL) {
741   size_t l = strlen(cur->before_colon);   if (file_len >= strlen(auth->before_colon)) {
742   Htaccess *hti;   /* insert cur before auth */
743     cur->next = auth;
744   for (hti = prev_hti; hti; hti = hti->next) {   break;
  if (l >= strlen(hti->before_colon)) {  
  /* insert before hti */  
  cur->next = hti;  
  if (prev_hti != hti) {  
  prev_hti->next = cur;  
  } else {  
  /* insert as top */  
  g_auth = cur;  
745   }   }
746   break;   authp = &auth->next;
747   }   }
748   if (prev_hti != hti)   *authp = cur;
  prev_hti = prev_hti->next;  
  }  
  if (!hti) {       /* not inserted, add to bottom */  
  prev->next = cur;  
  prev = cur;  
749   }   }
750     continue;
751   }   }
752  #endif /* BASIC_AUTH */  #endif /* BASIC_AUTH */
753  #endif /* BASIC_AUTH || MIME_TYPES || SCRIPT_INTERPR */  
754     /* the line is not recognized */
755     config_error:
756     bb_error_msg("config error '%s' in '%s'", buf, filename);
757   } /* while (fgets) */   } /* while (fgets) */
758    
759   fclose(f);   fclose(f);
760  }  }
761    
# Line 967  static void send_headers(int responseNum Line 961  static void send_headers(int responseNum
961   const char *error_page = NULL;   const char *error_page = NULL;
962  #endif  #endif
963   unsigned i;   unsigned i;
964   time_t timer = time(0);   time_t timer = time(NULL);
965   char tmp_str[80];   char tmp_str[80];
966   int len;   int len;
967    
# Line 1027  static void send_headers(int responseNum Line 1021  static void send_headers(int responseNum
1021   strftime(tmp_str, sizeof(tmp_str), RFC1123FMT, gmtime(&last_mod));   strftime(tmp_str, sizeof(tmp_str), RFC1123FMT, gmtime(&last_mod));
1022  #if ENABLE_FEATURE_HTTPD_RANGES  #if ENABLE_FEATURE_HTTPD_RANGES
1023   if (responseNum == HTTP_PARTIAL_CONTENT) {   if (responseNum == HTTP_PARTIAL_CONTENT) {
1024   len += sprintf(iobuf + len, "Content-Range: bytes %"OFF_FMT"d-%"OFF_FMT"d/%"OFF_FMT"d\r\n",   len += sprintf(iobuf + len, "Content-Range: bytes %"OFF_FMT"u-%"OFF_FMT"u/%"OFF_FMT"u\r\n",
1025   range_start,   range_start,
1026   range_end,   range_end,
1027   file_size);   file_size);
# Line 1038  static void send_headers(int responseNum Line 1032  static void send_headers(int responseNum
1032  #if ENABLE_FEATURE_HTTPD_RANGES  #if ENABLE_FEATURE_HTTPD_RANGES
1033   "Accept-Ranges: bytes\r\n"   "Accept-Ranges: bytes\r\n"
1034  #endif  #endif
1035   "Last-Modified: %s\r\n%s %"OFF_FMT"d\r\n",   "Last-Modified: %s\r\n%s %"OFF_FMT"u\r\n",
1036   tmp_str,   tmp_str,
1037   "Content-length:",   "Content-length:",
1038   file_size   file_size
# Line 1081  static int get_line(void) Line 1075  static int get_line(void)
1075   int count = 0;   int count = 0;
1076   char c;   char c;
1077    
1078     alarm(HEADER_READ_TIMEOUT);
1079   while (1) {   while (1) {
1080   if (hdr_cnt <= 0) {   if (hdr_cnt <= 0) {
1081   hdr_cnt = safe_read(STDIN_FILENO, hdr_buf, sizeof(hdr_buf));   hdr_cnt = safe_read(STDIN_FILENO, hdr_buf, sizeof(hdr_buf));
# Line 1095  static int get_line(void) Line 1090  static int get_line(void)
1090   continue;   continue;
1091   if (c == '\n') {   if (c == '\n') {
1092   iobuf[count] = '\0';   iobuf[count] = '\0';
1093   return count;   break;
1094   }   }
1095   if (count < (IOBUF_SIZE - 1))      /* check overflow */   if (count < (IOBUF_SIZE - 1))      /* check overflow */
1096   count++;   count++;
# Line 1330  static void send_cgi_and_exit( Line 1325  static void send_cgi_and_exit(
1325   /* Check for [dirs/]script.cgi/PATH_INFO */   /* Check for [dirs/]script.cgi/PATH_INFO */
1326   script = (char*)url;   script = (char*)url;
1327   while ((script = strchr(script + 1, '/')) != NULL) {   while ((script = strchr(script + 1, '/')) != NULL) {
  struct stat sb;  
   
1328   *script = '\0';   *script = '\0';
1329   if (!is_directory(url + 1, 1, &sb)) {   if (!is_directory(url + 1, 1, NULL)) {
1330   /* not directory, found script.cgi/PATH_INFO */   /* not directory, found script.cgi/PATH_INFO */
1331   *script = '/';   *script = '/';
1332   break;   break;
# Line 1406  static void send_cgi_and_exit( Line 1399  static void send_cgi_and_exit(
1399  #endif  #endif
1400   if (referer)   if (referer)
1401   setenv1("HTTP_REFERER", referer);   setenv1("HTTP_REFERER", referer);
1402     setenv1("HTTP_HOST", host); /* set to "" if NULL */
1403     /* setenv1("SERVER_NAME", safe_gethostname()); - don't do this,
1404     * just run "env SERVER_NAME=xyz httpd ..." instead */
1405    
1406   xpiped_pair(fromCgi);   xpiped_pair(fromCgi);
1407   xpiped_pair(toCgi);   xpiped_pair(toCgi);
# Line 1504  static void send_cgi_and_exit( Line 1500  static void send_cgi_and_exit(
1500   * const char *url  The requested URL (with leading /).   * const char *url  The requested URL (with leading /).
1501   * what             What to send (headers/body/both).   * what             What to send (headers/body/both).
1502   */   */
1503  static void send_file_and_exit(const char *url, int what)  static NOINLINE void send_file_and_exit(const char *url, int what)
1504  {  {
  static const char *const suffixTable[] = {  
  /* Warning: shorter equivalent suffix in one line must be first */  
  ".htm.html", "text/html",  
  ".jpg.jpeg", "image/jpeg",  
  ".gif",      "image/gif",  
  ".png",      "image/png",  
  ".txt.h.c.cc.cpp", "text/plain",  
  ".css",      "text/css",  
  ".wav",      "audio/wav",  
  ".avi",      "video/x-msvideo",  
  ".qt.mov",   "video/quicktime",  
  ".mpe.mpeg", "video/mpeg",  
  ".mid.midi", "audio/midi",  
  ".mp3",      "audio/mpeg",  
 #if 0                        /* unpopular */  
  ".au",       "audio/basic",  
  ".pac",      "application/x-ns-proxy-autoconfig",  
  ".vrml.wrl", "model/vrml",  
 #endif  
  NULL  
  };  
   
1505   char *suffix;   char *suffix;
1506   int f;   int fd;
  const char *const *table;  
  const char *try_suffix;  
1507   ssize_t count;   ssize_t count;
 #if ENABLE_FEATURE_HTTPD_USE_SENDFILE  
  off_t offset;  
 #endif  
1508    
1509     fd = open(url, O_RDONLY);
1510     if (fd < 0) {
1511     if (DEBUG)
1512     bb_perror_msg("can't open '%s'", url);
1513     /* Error pages are sent by using send_file_and_exit(SEND_BODY).
1514     * IOW: it is unsafe to call send_headers_and_exit
1515     * if what is SEND_BODY! Can recurse! */
1516     if (what != SEND_BODY)
1517     send_headers_and_exit(HTTP_NOT_FOUND);
1518     log_and_exit();
1519     }
1520   /* If you want to know about EPIPE below   /* If you want to know about EPIPE below
1521   * (happens if you abort downloads from local httpd): */   * (happens if you abort downloads from local httpd): */
1522   signal(SIGPIPE, SIG_IGN);   signal(SIGPIPE, SIG_IGN);
1523    
1524   suffix = strrchr(url, '.');   /* If not found, default is "application/octet-stream" */
   
  /* If not found, set default as "application/octet-stream";  */  
1525   found_mime_type = "application/octet-stream";   found_mime_type = "application/octet-stream";
1526     suffix = strrchr(url, '.');
1527   if (suffix) {   if (suffix) {
1528  #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES   static const char suffixTable[] ALIGN1 =
1529   Htaccess *cur;   /* Shorter suffix must be first:
1530     * ".html.htm" will fail for ".htm"
1531     */
1532     ".txt.h.c.cc.cpp\0" "text/plain\0"
1533     /* .htm line must be after .h line */
1534     ".htm.html\0" "text/html\0"
1535     ".jpg.jpeg\0" "image/jpeg\0"
1536     ".gif\0"      "image/gif\0"
1537     ".png\0"      "image/png\0"
1538     /* .css line must be after .c line */
1539     ".css\0"      "text/css\0"
1540     ".wav\0"      "audio/wav\0"
1541     ".avi\0"      "video/x-msvideo\0"
1542     ".qt.mov\0"   "video/quicktime\0"
1543     ".mpe.mpeg\0" "video/mpeg\0"
1544     ".mid.midi\0" "audio/midi\0"
1545     ".mp3\0"      "audio/mpeg\0"
1546    #if 0  /* unpopular */
1547     ".au\0"       "audio/basic\0"
1548     ".pac\0"      "application/x-ns-proxy-autoconfig\0"
1549     ".vrml.wrl\0" "model/vrml\0"
1550  #endif  #endif
1551   for (table = suffixTable; *table; table += 2) {   /* compiler adds another "\0" here */
1552   try_suffix = strstr(table[0], suffix);   ;
1553   if (try_suffix) {   Htaccess *cur;
1554   try_suffix += strlen(suffix);  
1555   if (*try_suffix == '\0' || *try_suffix == '.') {   /* Examine built-in table */
1556   found_mime_type = table[1];   const char *table = suffixTable;
1557   break;   const char *table_next;
1558   }   for (; *table; table = table_next) {
1559     const char *try_suffix;
1560     const char *mime_type;
1561     mime_type  = table + strlen(table) + 1;
1562     table_next = mime_type + strlen(mime_type) + 1;
1563     try_suffix = strstr(table, suffix);
1564     if (!try_suffix)
1565     continue;
1566     try_suffix += strlen(suffix);
1567     if (*try_suffix == '\0' || *try_suffix == '.') {
1568     found_mime_type = mime_type;
1569     break;
1570   }   }
1571     /* Example: strstr(table, ".av") != NULL, but it
1572     * does not match ".avi" after all and we end up here.
1573     * The table is arranged so that in this case we know
1574     * that it can't match anything in the following lines,
1575     * and we stop the search: */
1576     break;
1577   }   }
1578  #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES   /* ...then user's table */
1579   for (cur = mime_a; cur; cur = cur->next) {   for (cur = mime_a; cur; cur = cur->next) {
1580   if (strcmp(cur->before_colon, suffix) == 0) {   if (strcmp(cur->before_colon, suffix) == 0) {
1581   found_mime_type = cur->after_colon;   found_mime_type = cur->after_colon;
1582   break;   break;
1583   }   }
1584   }   }
 #endif  
1585   }   }
1586    
1587   if (DEBUG)   if (DEBUG)
1588   bb_error_msg("sending file '%s' content-type: %s",   bb_error_msg("sending file '%s' content-type: %s",
1589   url, found_mime_type);   url, found_mime_type);
1590    
  f = open(url, O_RDONLY);  
  if (f < 0) {  
  if (DEBUG)  
  bb_perror_msg("can't open '%s'", url);  
  /* Error pages are sent by using send_file_and_exit(SEND_BODY).  
  * IOW: it is unsafe to call send_headers_and_exit  
  * if what is SEND_BODY! Can recurse! */  
  if (what != SEND_BODY)  
  send_headers_and_exit(HTTP_NOT_FOUND);  
  log_and_exit();  
  }  
1591  #if ENABLE_FEATURE_HTTPD_RANGES  #if ENABLE_FEATURE_HTTPD_RANGES
1592   if (what == SEND_BODY)   if (what == SEND_BODY)
1593   range_start = 0; /* err pages and ranges don't mix */   range_start = 0; /* err pages and ranges don't mix */
# Line 1593  static void send_file_and_exit(const cha Line 1597  static void send_file_and_exit(const cha
1597   range_end = file_size - 1;   range_end = file_size - 1;
1598   }   }
1599   if (range_end < range_start   if (range_end < range_start
1600   || lseek(f, range_start, SEEK_SET) != range_start   || lseek(fd, range_start, SEEK_SET) != range_start
1601   ) {   ) {
1602   lseek(f, 0, SEEK_SET);   lseek(fd, 0, SEEK_SET);
1603   range_start = 0;   range_start = 0;
1604   } else {   } else {
1605   range_len = range_end - range_start + 1;   range_len = range_end - range_start + 1;
# Line 1604  static void send_file_and_exit(const cha Line 1608  static void send_file_and_exit(const cha
1608   }   }
1609   }   }
1610  #endif  #endif
   
1611   if (what & SEND_HEADERS)   if (what & SEND_HEADERS)
1612   send_headers(HTTP_OK);   send_headers(HTTP_OK);
   
1613  #if ENABLE_FEATURE_HTTPD_USE_SENDFILE  #if ENABLE_FEATURE_HTTPD_USE_SENDFILE
1614   offset = range_start;   {
1615   do {   off_t offset = range_start;
1616   /* sz is rounded down to 64k */   while (1) {
1617   ssize_t sz = MAXINT(ssize_t) - 0xffff;   /* sz is rounded down to 64k */
1618   USE_FEATURE_HTTPD_RANGES(if (sz > range_len) sz = range_len;)   ssize_t sz = MAXINT(ssize_t) - 0xffff;
1619   count = sendfile(1, f, &offset, sz);   IF_FEATURE_HTTPD_RANGES(if (sz > range_len) sz = range_len;)
1620   if (count < 0) {   count = sendfile(STDOUT_FILENO, fd, &offset, sz);
1621   if (offset == range_start)   if (count < 0) {
1622   goto fallback;   if (offset == range_start)
1623   goto fin;   break; /* fall back to read/write loop */
1624     goto fin;
1625     }
1626     IF_FEATURE_HTTPD_RANGES(range_len -= sz;)
1627     if (count == 0 || range_len == 0)
1628     log_and_exit();
1629   }   }
1630   USE_FEATURE_HTTPD_RANGES(range_len -= sz;)   }
  } while (count > 0 && range_len);  
  log_and_exit();  
   
  fallback:  
1631  #endif  #endif
1632   while ((count = safe_read(f, iobuf, IOBUF_SIZE)) > 0) {   while ((count = safe_read(fd, iobuf, IOBUF_SIZE)) > 0) {
1633   ssize_t n;   ssize_t n;
1634   USE_FEATURE_HTTPD_RANGES(if (count > range_len) count = range_len;)   IF_FEATURE_HTTPD_RANGES(if (count > range_len) count = range_len;)
1635   n = full_write(STDOUT_FILENO, iobuf, count);   n = full_write(STDOUT_FILENO, iobuf, count);
1636   if (count != n)   if (count != n)
1637   break;   break;
1638   USE_FEATURE_HTTPD_RANGES(range_len -= count;)   IF_FEATURE_HTTPD_RANGES(range_len -= count;)
1639   if (!range_len)   if (range_len == 0)
1640   break;   break;
1641   }   }
1642  #if ENABLE_FEATURE_HTTPD_USE_SENDFILE   if (count < 0) {
1643   fin:   IF_FEATURE_HTTPD_USE_SENDFILE(fin:)
1644  #endif   if (verbose > 1)
1645   if (count < 0 && verbose > 1)   bb_perror_msg("error");
1646   bb_perror_msg("error");   }
1647   log_and_exit();   log_and_exit();
1648  }  }
1649    
# Line 1768  static Htaccess_Proxy *find_proxy_entry( Line 1771  static Htaccess_Proxy *find_proxy_entry(
1771  /*  /*
1772   * Handle timeouts   * Handle timeouts
1773   */   */
1774  static void exit_on_signal(int sig) NORETURN;  static void send_REQUEST_TIMEOUT_and_exit(int sig) NORETURN;
1775  static void exit_on_signal(int sig UNUSED_PARAM)  static void send_REQUEST_TIMEOUT_and_exit(int sig UNUSED_PARAM)
1776  {  {
1777   send_headers_and_exit(HTTP_REQUEST_TIMEOUT);   send_headers_and_exit(HTTP_REQUEST_TIMEOUT);
1778  }  }
# Line 1834  static void handle_incoming_and_exit(con Line 1837  static void handle_incoming_and_exit(con
1837   bb_error_msg("connected");   bb_error_msg("connected");
1838   }   }
1839    
1840   /* Install timeout handler */   /* Install timeout handler. get_line() needs it. */
1841   signal_no_SA_RESTART_empty_mask(SIGALRM, exit_on_signal);   signal(SIGALRM, send_REQUEST_TIMEOUT_and_exit);
  alarm(HEADER_READ_TIMEOUT);  
1842    
1843   if (!get_line()) /* EOF or error or empty line */   if (!get_line()) /* EOF or error or empty line */
1844   send_headers_and_exit(HTTP_BAD_REQUEST);   send_headers_and_exit(HTTP_BAD_REQUEST);
# Line 1866  static void handle_incoming_and_exit(con Line 1868  static void handle_incoming_and_exit(con
1868    
1869   /* Find end of URL and parse HTTP version, if any */   /* Find end of URL and parse HTTP version, if any */
1870   http_major_version = '0';   http_major_version = '0';
1871   USE_FEATURE_HTTPD_PROXY(http_minor_version = '0';)   IF_FEATURE_HTTPD_PROXY(http_minor_version = '0';)
1872   tptr = strchrnul(urlp, ' ');   tptr = strchrnul(urlp, ' ');
1873   /* Is it " HTTP/"? */   /* Is it " HTTP/"? */
1874   if (tptr[0] && strncmp(tptr + 1, HTTP_200, 5) == 0) {   if (tptr[0] && strncmp(tptr + 1, HTTP_200, 5) == 0) {
1875   http_major_version = tptr[6];   http_major_version = tptr[6];
1876   USE_FEATURE_HTTPD_PROXY(http_minor_version = tptr[8];)   IF_FEATURE_HTTPD_PROXY(http_minor_version = tptr[8];)
1877   }   }
1878   *tptr = '\0';   *tptr = '\0';
1879    
# Line 1930  static void handle_incoming_and_exit(con Line 1932  static void handle_incoming_and_exit(con
1932    
1933   /* If URL is a directory, add '/' */   /* If URL is a directory, add '/' */
1934   if (urlp[-1] != '/') {   if (urlp[-1] != '/') {
1935   if (is_directory(urlcopy + 1, 1, &sb)) {   if (is_directory(urlcopy + 1, 1, NULL)) {
1936   found_moved_temporarily = urlcopy;   found_moved_temporarily = urlcopy;
1937   }   }
1938   }   }
# Line 1944  static void handle_incoming_and_exit(con Line 1946  static void handle_incoming_and_exit(con
1946   while (ip_allowed && (tptr = strchr(tptr + 1, '/')) != NULL) {   while (ip_allowed && (tptr = strchr(tptr + 1, '/')) != NULL) {
1947   /* have path1/path2 */   /* have path1/path2 */
1948   *tptr = '\0';   *tptr = '\0';
1949   if (is_directory(urlcopy + 1, 1, &sb)) {   if (is_directory(urlcopy + 1, 1, NULL)) {
1950   /* may have subdir config */   /* may have subdir config */
1951   parse_conf(urlcopy + 1, SUBDIR_PARSE);   parse_conf(urlcopy + 1, SUBDIR_PARSE);
1952   ip_allowed = checkPermIP();   ip_allowed = checkPermIP();
# Line 1963  static void handle_incoming_and_exit(con Line 1965  static void handle_incoming_and_exit(con
1965    
1966   /* Read until blank line for HTTP version specified, else parse immediate */   /* Read until blank line for HTTP version specified, else parse immediate */
1967   while (1) {   while (1) {
  alarm(HEADER_READ_TIMEOUT);  
1968   if (!get_line())   if (!get_line())
1969   break; /* EOF or error or empty line */   break; /* EOF or error or empty line */
1970   if (DEBUG)   if (DEBUG)
# Line 2013  static void handle_incoming_and_exit(con Line 2014  static void handle_incoming_and_exit(con
2014   referer = xstrdup(skip_whitespace(iobuf + sizeof("Referer:")-1));   referer = xstrdup(skip_whitespace(iobuf + sizeof("Referer:")-1));
2015   } else if (STRNCASECMP(iobuf, "User-Agent:") == 0) {   } else if (STRNCASECMP(iobuf, "User-Agent:") == 0) {
2016   user_agent = xstrdup(skip_whitespace(iobuf + sizeof("User-Agent:")-1));   user_agent = xstrdup(skip_whitespace(iobuf + sizeof("User-Agent:")-1));
2017     } else if (STRNCASECMP(iobuf, "Host:") == 0) {
2018     host = xstrdup(skip_whitespace(iobuf + sizeof("Host:")-1));
2019   } else if (STRNCASECMP(iobuf, "Accept:") == 0) {   } else if (STRNCASECMP(iobuf, "Accept:") == 0) {
2020   http_accept = xstrdup(skip_whitespace(iobuf + sizeof("Accept:")-1));   http_accept = xstrdup(skip_whitespace(iobuf + sizeof("Accept:")-1));
2021   } else if (STRNCASECMP(iobuf, "Accept-Language:") == 0) {   } else if (STRNCASECMP(iobuf, "Accept-Language:") == 0) {
# Line 2057  static void handle_incoming_and_exit(con Line 2060  static void handle_incoming_and_exit(con
2060   /* We are done reading headers, disable peer timeout */   /* We are done reading headers, disable peer timeout */
2061   alarm(0);   alarm(0);
2062    
2063   if (strcmp(bb_basename(urlcopy), httpd_conf) == 0 || !ip_allowed) {   if (strcmp(bb_basename(urlcopy), HTTPD_CONF) == 0 || !ip_allowed) {
2064   /* protect listing [/path]/httpd_conf or IP deny */   /* protect listing [/path]/httpd.conf or IP deny */
2065   send_headers_and_exit(HTTP_FORBIDDEN);   send_headers_and_exit(HTTP_FORBIDDEN);
2066   }   }
2067    
# Line 2100  static void handle_incoming_and_exit(con Line 2103  static void handle_incoming_and_exit(con
2103   header_ptr += 2;   header_ptr += 2;
2104   write(proxy_fd, header_buf, header_ptr - header_buf);   write(proxy_fd, header_buf, header_ptr - header_buf);
2105   free(header_buf); /* on the order of 8k, free it */   free(header_buf); /* on the order of 8k, free it */
2106   /* cgi_io_loop_and_exit needs to have two disctinct fds */   /* cgi_io_loop_and_exit needs to have two distinct fds */
2107   cgi_io_loop_and_exit(proxy_fd, dup(proxy_fd), length);   cgi_io_loop_and_exit(proxy_fd, dup(proxy_fd), length);
2108   }   }
2109  #endif  #endif
# Line 2115  static void handle_incoming_and_exit(con Line 2118  static void handle_incoming_and_exit(con
2118   }   }
2119   send_cgi_and_exit(urlcopy, prequest, length, cookie, content_type);   send_cgi_and_exit(urlcopy, prequest, length, cookie, content_type);
2120   }   }
2121    #endif
2122    
2123     if (urlp[-1] == '/')
2124     strcpy(urlp, index_page);
2125     if (stat(tptr, &sb) == 0) {
2126  #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR  #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
  {  
2127   char *suffix = strrchr(tptr, '.');   char *suffix = strrchr(tptr, '.');
2128   if (suffix) {   if (suffix) {
2129   Htaccess *cur;   Htaccess *cur;
# Line 2126  static void handle_incoming_and_exit(con Line 2133  static void handle_incoming_and_exit(con
2133   }   }
2134   }   }
2135   }   }
  }  
2136  #endif  #endif
  if (prequest != request_GET && prequest != request_HEAD) {  
  send_headers_and_exit(HTTP_NOT_IMPLEMENTED);  
  }  
 #endif  /* FEATURE_HTTPD_CGI */  
   
  if (urlp[-1] == '/')  
  strcpy(urlp, index_page);  
  if (stat(tptr, &sb) == 0) {  
2137   file_size = sb.st_size;   file_size = sb.st_size;
2138   last_mod = sb.st_mtime;   last_mod = sb.st_mtime;
2139   }   }
# Line 2149  static void handle_incoming_and_exit(con Line 2147  static void handle_incoming_and_exit(con
2147   send_cgi_and_exit("/cgi-bin/index.cgi", prequest, length, cookie, content_type);   send_cgi_and_exit("/cgi-bin/index.cgi", prequest, length, cookie, content_type);
2148   }   }
2149   }   }
2150  #endif   /* else fall through to send_file, it errors out if open fails: */
  /* else {  
  * fall through to send_file, it errors out if open fails  
  * }  
  */  
2151    
2152     if (prequest != request_GET && prequest != request_HEAD) {
2153     /* POST for files does not make sense */
2154     send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
2155     }
2156   send_file_and_exit(tptr,   send_file_and_exit(tptr,
 #if ENABLE_FEATURE_HTTPD_CGI  
2157   (prequest != request_HEAD ? SEND_HEADERS_AND_BODY : SEND_HEADERS)   (prequest != request_HEAD ? SEND_HEADERS_AND_BODY : SEND_HEADERS)
2158     );
2159  #else  #else
2160   SEND_HEADERS_AND_BODY   send_file_and_exit(tptr, SEND_HEADERS_AND_BODY);
2161  #endif  #endif
  );  
2162  }  }
2163    
2164  /*  /*
# Line 2194  static void mini_httpd(int server_socket Line 2191  static void mini_httpd(int server_socket
2191    
2192   if (fork() == 0) {   if (fork() == 0) {
2193   /* child */   /* child */
 #if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP  
2194   /* Do not reload config on HUP */   /* Do not reload config on HUP */
2195   signal(SIGHUP, SIG_IGN);   signal(SIGHUP, SIG_IGN);
 #endif  
2196   close(server_socket);   close(server_socket);
2197   xmove_fd(n, 0);   xmove_fd(n, 0);
2198   xdup2(0, 1);   xdup2(0, 1);
# Line 2239  static void mini_httpd_nommu(int server_ Line 2234  static void mini_httpd_nommu(int server_
2234    
2235   if (vfork() == 0) {   if (vfork() == 0) {
2236   /* child */   /* child */
 #if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP  
2237   /* Do not reload config on HUP */   /* Do not reload config on HUP */
2238   signal(SIGHUP, SIG_IGN);   signal(SIGHUP, SIG_IGN);
 #endif  
2239   close(server_socket);   close(server_socket);
2240   xmove_fd(n, 0);   xmove_fd(n, 0);
2241   xdup2(0, 1);   xdup2(0, 1);
# Line 2273  static void mini_httpd_inetd(void) Line 2266  static void mini_httpd_inetd(void)
2266   handle_incoming_and_exit(&fromAddr);   handle_incoming_and_exit(&fromAddr);
2267  }  }
2268    
2269  #if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP  static void sighup_handler(int sig UNUSED_PARAM)
 static void sighup_handler(int sig)  
2270  {  {
2271   parse_conf(default_path_httpd_conf, sig ? SIGNALED_PARSE : FIRST_PARSE);   parse_conf(DEFAULT_PATH_HTTPD_CONF, SIGNALED_PARSE);
  signal_SA_RESTART_empty_mask(SIGHUP, sighup_handler);  
2272  }  }
 #endif  
2273    
2274  enum {  enum {
2275   c_opt_config_file = 0,   c_opt_config_file = 0,
2276   d_opt_decode_url,   d_opt_decode_url,
2277   h_opt_home_httpd,   h_opt_home_httpd,
2278   USE_FEATURE_HTTPD_ENCODE_URL_STR(e_opt_encode_url,)   IF_FEATURE_HTTPD_ENCODE_URL_STR(e_opt_encode_url,)
2279   USE_FEATURE_HTTPD_BASIC_AUTH(    r_opt_realm     ,)   IF_FEATURE_HTTPD_BASIC_AUTH(    r_opt_realm     ,)
2280   USE_FEATURE_HTTPD_AUTH_MD5(      m_opt_md5       ,)   IF_FEATURE_HTTPD_AUTH_MD5(      m_opt_md5       ,)
2281   USE_FEATURE_HTTPD_SETUID(        u_opt_setuid    ,)   IF_FEATURE_HTTPD_SETUID(        u_opt_setuid    ,)
2282   p_opt_port      ,   p_opt_port      ,
2283   p_opt_inetd     ,   p_opt_inetd     ,
2284   p_opt_foreground,   p_opt_foreground,
# Line 2296  enum { Line 2286  enum {
2286   OPT_CONFIG_FILE = 1 << c_opt_config_file,   OPT_CONFIG_FILE = 1 << c_opt_config_file,
2287   OPT_DECODE_URL  = 1 << d_opt_decode_url,   OPT_DECODE_URL  = 1 << d_opt_decode_url,
2288   OPT_HOME_HTTPD  = 1 << h_opt_home_httpd,   OPT_HOME_HTTPD  = 1 << h_opt_home_httpd,
2289   OPT_ENCODE_URL  = USE_FEATURE_HTTPD_ENCODE_URL_STR((1 << e_opt_encode_url)) + 0,   OPT_ENCODE_URL  = IF_FEATURE_HTTPD_ENCODE_URL_STR((1 << e_opt_encode_url)) + 0,
2290   OPT_REALM       = USE_FEATURE_HTTPD_BASIC_AUTH(    (1 << r_opt_realm     )) + 0,   OPT_REALM       = IF_FEATURE_HTTPD_BASIC_AUTH(    (1 << r_opt_realm     )) + 0,
2291   OPT_MD5         = USE_FEATURE_HTTPD_AUTH_MD5(      (1 << m_opt_md5       )) + 0,   OPT_MD5         = IF_FEATURE_HTTPD_AUTH_MD5(      (1 << m_opt_md5       )) + 0,
2292   OPT_SETUID      = USE_FEATURE_HTTPD_SETUID(        (1 << u_opt_setuid    )) + 0,   OPT_SETUID      = IF_FEATURE_HTTPD_SETUID(        (1 << u_opt_setuid    )) + 0,
2293   OPT_PORT        = 1 << p_opt_port,   OPT_PORT        = 1 << p_opt_port,
2294   OPT_INETD       = 1 << p_opt_inetd,   OPT_INETD       = 1 << p_opt_inetd,
2295   OPT_FOREGROUND  = 1 << p_opt_foreground,   OPT_FOREGROUND  = 1 << p_opt_foreground,
# Line 2313  int httpd_main(int argc UNUSED_PARAM, ch Line 2303  int httpd_main(int argc UNUSED_PARAM, ch
2303   int server_socket = server_socket; /* for gcc */   int server_socket = server_socket; /* for gcc */
2304   unsigned opt;   unsigned opt;
2305   char *url_for_decode;   char *url_for_decode;
2306   USE_FEATURE_HTTPD_ENCODE_URL_STR(const char *url_for_encode;)   IF_FEATURE_HTTPD_ENCODE_URL_STR(const char *url_for_encode;)
2307   USE_FEATURE_HTTPD_SETUID(const char *s_ugid = NULL;)   IF_FEATURE_HTTPD_SETUID(const char *s_ugid = NULL;)
2308   USE_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;)   IF_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;)
2309   USE_FEATURE_HTTPD_AUTH_MD5(const char *pass;)   IF_FEATURE_HTTPD_AUTH_MD5(const char *pass;)
2310    
2311   INIT_G();   INIT_G();
2312    
# Line 2332  int httpd_main(int argc UNUSED_PARAM, ch Line 2322  int httpd_main(int argc UNUSED_PARAM, ch
2322   * If user gives relative path in -h,   * If user gives relative path in -h,
2323   * $SCRIPT_FILENAME will not be set. */   * $SCRIPT_FILENAME will not be set. */
2324   opt = getopt32(argv, "c:d:h:"   opt = getopt32(argv, "c:d:h:"
2325   USE_FEATURE_HTTPD_ENCODE_URL_STR("e:")   IF_FEATURE_HTTPD_ENCODE_URL_STR("e:")
2326   USE_FEATURE_HTTPD_BASIC_AUTH("r:")   IF_FEATURE_HTTPD_BASIC_AUTH("r:")
2327   USE_FEATURE_HTTPD_AUTH_MD5("m:")   IF_FEATURE_HTTPD_AUTH_MD5("m:")
2328   USE_FEATURE_HTTPD_SETUID("u:")   IF_FEATURE_HTTPD_SETUID("u:")
2329   "p:ifv",   "p:ifv",
2330   &configFile, &url_for_decode, &home_httpd   &opt_c_configFile, &url_for_decode, &home_httpd
2331   USE_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode)   IF_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode)
2332   USE_FEATURE_HTTPD_BASIC_AUTH(, &g_realm)   IF_FEATURE_HTTPD_BASIC_AUTH(, &g_realm)
2333   USE_FEATURE_HTTPD_AUTH_MD5(, &pass)   IF_FEATURE_HTTPD_AUTH_MD5(, &pass)
2334   USE_FEATURE_HTTPD_SETUID(, &s_ugid)   IF_FEATURE_HTTPD_SETUID(, &s_ugid)
2335   , &bind_addr_or_port   , &bind_addr_or_port
2336   , &verbose   , &verbose
2337   );   );
# Line 2357  int httpd_main(int argc UNUSED_PARAM, ch Line 2347  int httpd_main(int argc UNUSED_PARAM, ch
2347  #endif  #endif
2348  #if ENABLE_FEATURE_HTTPD_AUTH_MD5  #if ENABLE_FEATURE_HTTPD_AUTH_MD5
2349   if (opt & OPT_MD5) {   if (opt & OPT_MD5) {
2350   puts(pw_encrypt(pass, "$1$", 1));   char salt[sizeof("$1$XXXXXXXX")];
2351     salt[0] = '$';
2352     salt[1] = '1';
2353     salt[2] = '$';
2354     crypt_make_salt(salt + 3, 4, 0);
2355     puts(pw_encrypt(pass, salt, 1));
2356   return 0;   return 0;
2357   }   }
2358  #endif  #endif
# Line 2390  int httpd_main(int argc UNUSED_PARAM, ch Line 2385  int httpd_main(int argc UNUSED_PARAM, ch
2385  #endif  #endif
2386   }   }
2387    
2388  #if 0 /*was #if ENABLE_FEATURE_HTTPD_CGI*/  #if 0
2389   /* User can do it himself: 'env - PATH="$PATH" httpd'   /* User can do it himself: 'env - PATH="$PATH" httpd'
2390   * We don't do it because we don't want to screw users   * We don't do it because we don't want to screw users
2391   * which want to do   * which want to do
# Line 2408  int httpd_main(int argc UNUSED_PARAM, ch Line 2403  int httpd_main(int argc UNUSED_PARAM, ch
2403   }   }
2404  #endif  #endif
2405    
2406  #if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP   parse_conf(DEFAULT_PATH_HTTPD_CONF, FIRST_PARSE);
2407   if (!(opt & OPT_INETD)) {   if (!(opt & OPT_INETD))
2408   /* runs parse_conf() inside */   signal(SIGHUP, sighup_handler);
  sighup_handler(0);  
  } else  
 #endif  
  {  
  parse_conf(default_path_httpd_conf, FIRST_PARSE);  
  }  
2409    
2410   xfunc_error_retval = 0;   xfunc_error_retval = 0;
2411   if (opt & OPT_INETD)   if (opt & OPT_INETD)

Legend:
Removed from v.983  
changed lines
  Added in v.984