- patch from Denis Vlasenko to add and use bb_xchdir()
[oweals/busybox.git] / networking / httpd.c
index b44adf730389f56799cf93938cd04c29aa00dfc3..df280ccf554e2f6f88cb70004cde94ed06b3b373 100644 (file)
@@ -1,3 +1,4 @@
+/* vi: set sw=4 ts=4: */
 /*
  * httpd implementation for busybox
  *
@@ -6,19 +7,7 @@
  *
  * simplify patch stolen from libbb without using strdup
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  *
  *****************************************************************************
  *
@@ -54,6 +43,7 @@
  * /adm:admin:setup  # Require user admin, pwd setup on urls starting with /adm/
  * /adm:toor:PaSsWd  # or user toor, pwd PaSsWd on urls starting with /adm/
  * .au:audio/basic   # additional mime type for audio.au files
+ * *.php:/path/php   # running cgi.php scripts through an interpreter
  *
  * A/D may be as a/d or allow/deny - first char case insensitive
  * Deny IP rules take precedence over allow rules.
@@ -239,16 +229,12 @@ typedef struct
 {
   char buf[MAX_MEMORY_BUFF];
 
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
-  const char *realm;
-  char *remoteuser;
-#endif
+  USE_FEATURE_HTTPD_BASIC_AUTH(const char *realm;)
+  USE_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;)
 
   const char *query;
 
-#ifdef CONFIG_FEATURE_HTTPD_CGI
-  char *referer;
-#endif
+  USE_FEATURE_HTTPD_CGI(char *referer;)
 
   const char *configFile;
 
@@ -285,6 +271,9 @@ typedef struct
 #endif
   volatile int alarm_signaled;
 
+#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+  Htaccess *script_i;           /* config script interpreters */
+#endif
 } HttpdConfig;
 
 static HttpdConfig *config;
@@ -529,7 +518,7 @@ static void parse_conf(const char *path, int flag)
 
     config->flg_deny_all = 0;
 
-#if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES)
+#if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR)
     /* retain previous auth and mime config only for subdir parse */
     if(flag != SUBDIR_PARSE) {
 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
@@ -537,6 +526,9 @@ static void parse_conf(const char *path, int flag)
 #endif
 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
        free_config_lines(&config->mime_a);
+#endif
+#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+       free_config_lines(&config->script_i);
 #endif
     }
 #endif
@@ -600,6 +592,9 @@ static void parse_conf(const char *path, int flag)
 #endif
 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
           && *p0 != '.'
+#endif
+#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+          && *p0 != '*'
 #endif
          )
               continue;
@@ -672,7 +667,7 @@ static void parse_conf(const char *path, int flag)
        }
 #endif
 
-#if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES)
+#if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR)
        /* storing current config line */
        cur = calloc(1, sizeof(Htaccess) + strlen(p0));
        if(cur) {
@@ -688,6 +683,14 @@ static void parse_conf(const char *path, int flag)
                continue;
            }
 #endif
+#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+           if(*cf == '*' && cf[1] == '.') {
+               /* config script interpreter line move top for overwrite previous */
+               cur->next = config->script_i;
+               config->script_i = cur;
+               continue;
+           }
+#endif
 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
            free(p0);
            if(prev == NULL) {
@@ -945,26 +948,19 @@ static int openServer(void)
   memset(&lsocket, 0, sizeof(lsocket));
   lsocket.sin_family = AF_INET;
   lsocket.sin_addr.s_addr = INADDR_ANY;
-  lsocket.sin_port = htons(config->port) ;
-  fd = socket(AF_INET, SOCK_STREAM, 0);
-  if (fd >= 0) {
-    /* tell the OS it's OK to reuse a previous address even though */
-    /* it may still be in a close down state.  Allows bind to succeed. */
-    int on = 1;
+  lsocket.sin_port = htons(config->port);
+  fd = bb_xsocket(AF_INET, SOCK_STREAM, 0);
+  /* tell the OS it's OK to reuse a previous address even though */
+  /* it may still be in a close down state.  Allows bind to succeed. */
+  int on = 1;
 #ifdef SO_REUSEPORT
-    setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (void *)&on, sizeof(on)) ;
+  setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (void *)&on, sizeof(on)) ;
 #else
-    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)) ;
+  setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)) ;
 #endif
-    if (bind(fd, (struct sockaddr *)&lsocket, sizeof(lsocket)) == 0) {
-      listen(fd, 9);
-      signal(SIGCHLD, SIG_IGN);   /* prevent zombie (defunct) processes */
-    } else {
-       bb_perror_msg_and_die("bind");
-    }
-  } else {
-       bb_perror_msg_and_die("create socket");
-  }
+  bb_xbind(fd, (struct sockaddr *)&lsocket, sizeof(lsocket));
+  listen(fd, 9); /* bb_xlisten? */
+  signal(SIGCHLD, SIG_IGN);   /* prevent zombie (defunct) processes */
   return fd;
 }
 #endif  /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */
@@ -1219,11 +1215,29 @@ static int sendCgi(const char *url,
            if(script) {
                *script = '\0';
                if(chdir(realpath_buff) == 0) {
-                 *script = '/';
                  // now run the program.  If it fails,
                  // use _exit() so no destructors
                  // get called and make a mess.
-                 execv(realpath_buff, argp);
+#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+                 char *interpr = NULL;
+                 char *suffix = strrchr(purl, '.');
+
+                 if(suffix) {
+                       Htaccess * cur;
+                       for (cur = config->script_i; cur; cur = cur->next)
+                               if(strcmp(cur->before_colon + 1, suffix) == 0) {
+                                       interpr = cur->after_colon;
+                                       break;
+                               }
+                 }
+#endif
+                 *script = '/';
+#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+                 if (interpr)
+                       execv(interpr, argp);
+                 else
+#endif
+                       execv(realpath_buff, argp);
                }
            }
       }
@@ -1570,11 +1584,9 @@ static void handleIncoming(void)
   char *cookie = 0;
   char *content_type = 0;
 #endif
-#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
   fd_set s_fd;
   struct timeval tv;
   int retval;
-#endif
   struct sigaction sa;
 
 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
@@ -1820,19 +1832,21 @@ FORBIDDEN:      /* protect listing /cgi-bin */
   free(config->remoteuser);
 #endif
 # endif
+#endif  /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */
   shutdown(a_c_w, SHUT_WR);
 
   /* Properly wait for remote to closed */
   FD_ZERO (&s_fd) ;
-  FD_SET (a_c_w, &s_fd) ;
+  FD_SET (a_c_r, &s_fd) ;
 
   do {
     tv.tv_sec = 2 ;
     tv.tv_usec = 0 ;
-    retval = select (a_c_w + 1, &s_fd, NULL, NULL, &tv);
-  } while (retval > 0 && (read (a_c_w, buf, sizeof (config->buf)) > 0));
+    retval = select (a_c_r + 1, &s_fd, NULL, NULL, &tv);
+  } while (retval > 0 && (read (a_c_r, buf, sizeof (config->buf)) > 0));
 
   shutdown(a_c_r, SHUT_RD);
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
   close(config->accepted_socket);
 #endif  /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */
 }
@@ -1955,39 +1969,42 @@ static void sighup_handler(int sig)
 }
 #endif
 
+enum httpd_opts_nums {
+       c_opt_config_file = 0,
+       d_opt_decode_url,
+       h_opt_home_httpd,
+       USE_FEATURE_HTTPD_ENCODE_URL_STR(e_opt_encode_url,)
+       USE_FEATURE_HTTPD_BASIC_AUTH(r_opt_realm,)
+       USE_FEATURE_HTTPD_AUTH_MD5(m_opt_md5,)
+       USE_FEATURE_HTTPD_SETUID(u_opt_setuid,)
+       SKIP_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY(p_opt_port,)
+};
 
 static const char httpd_opts[]="c:d:h:"
-#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
-                               "e:"
-#endif
-#define OPT_INC_1 ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
+       USE_FEATURE_HTTPD_ENCODE_URL_STR("e:")
+       USE_FEATURE_HTTPD_BASIC_AUTH("r:")
+       USE_FEATURE_HTTPD_AUTH_MD5("m:")
+       USE_FEATURE_HTTPD_SETUID("u:")
+       SKIP_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY("p:");
 
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
-                               "r:"
-#endif
-#define OPT_INC_2 ENABLE_FEATURE_HTTPD_BASIC_AUTH
+#define OPT_CONFIG_FILE (1<<c_opt_config_file)
+#define OPT_DECODE_URL  (1<<d_opt_decode_url)
+#define OPT_HOME_HTTPD  (1<<h_opt_home_httpd)
 
-#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
-                               "m:"
-#endif
-#define OPT_INC_3 ENABLE_FEATURE_HTTPD_AUTH_MD5
+#define OPT_ENCODE_URL  USE_FEATURE_HTTPD_ENCODE_URL_STR((1<<e_opt_encode_url)) \
+                       SKIP_FEATURE_HTTPD_ENCODE_URL_STR(0)
 
-#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
-                               "p:"
-#endif
-#ifdef CONFIG_FEATURE_HTTPD_SETUID
-                               "u:"
-#endif
-                                       ;
+#define OPT_REALM       USE_FEATURE_HTTPD_BASIC_AUTH((1<<r_opt_realm)) \
+                       SKIP_FEATURE_HTTPD_BASIC_AUTH(0)
+
+#define OPT_MD5         USE_FEATURE_HTTPD_AUTH_MD5((1<<m_opt_md5)) \
+                       SKIP_FEATURE_HTTPD_AUTH_MD5(0)
 
-#define OPT_CONFIG_FILE (1<<0)                                    /* c */
-#define OPT_DECODE_URL  (1<<1)                                    /* d */
-#define OPT_HOME_HTTPD  (1<<2)                                    /* h */
-#define OPT_ENCODE_URL  (1<<(2+OPT_INC_1))                        /* e */
-#define OPT_REALM       (1<<(2+OPT_INC_1+OPT_INC_2))              /* r */
-#define OPT_MD5         (1<<(2+OPT_INC_1+OPT_INC_2+OPT_INC_3))    /* m */
-#define OPT_PORT        (1<<(3+OPT_INC_1+OPT_INC_2+OPT_INC_3))    /* p */
-#define OPT_SETUID      (1<<(4+OPT_INC_1+OPT_INC_2+OPT_INC_3))    /* u */
+#define OPT_SETUID      USE_FEATURE_HTTPD_SETUID((1<<u_opt_setuid)) \
+                       SKIP_FEATURE_HTTPD_SETUID(0)
+
+#define OPT_PORT        SKIP_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY((1<<p_opt_port)) \
+                       USE_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY(0)
 
 
 #ifdef HTTPD_STANDALONE
@@ -1999,22 +2016,14 @@ int httpd_main(int argc, char *argv[])
   unsigned long opt;
   const char *home_httpd = home;
   char *url_for_decode;
-#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
-  const char *url_for_encode;
-#endif
-#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
-  const char *s_port;
-  int server;
-#endif
+  USE_FEATURE_HTTPD_ENCODE_URL_STR(const char *url_for_encode;)
+  SKIP_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY(const char *s_port;)
+  SKIP_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY(int server;)
 
-#ifdef CONFIG_FEATURE_HTTPD_SETUID
-  const char *s_uid;
-  long uid = -1;
-#endif
+  USE_FEATURE_HTTPD_SETUID(const char *s_uid;)
+  USE_FEATURE_HTTPD_SETUID(long uid = -1;)
 
-#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
-  const char *pass;
-#endif
+  USE_FEATURE_HTTPD_AUTH_MD5(const char *pass;)
 
   config = xcalloc(1, sizeof(*config));
 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
@@ -2029,22 +2038,12 @@ int httpd_main(int argc, char *argv[])
 
   opt = bb_getopt_ulflags(argc, argv, httpd_opts,
                        &(config->configFile), &url_for_decode, &home_httpd
-#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
-                       , &url_for_encode
-#endif
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
-                       , &(config->realm)
-# ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
-                       , &pass
-# endif
-#endif
-#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
-                       , &s_port
-#endif
-#ifdef CONFIG_FEATURE_HTTPD_SETUID
-                       , &s_uid
-#endif
-    );
+                       USE_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode)
+                       USE_FEATURE_HTTPD_BASIC_AUTH(, &(config->realm))
+                       USE_FEATURE_HTTPD_AUTH_MD5(, &pass)
+                       USE_FEATURE_HTTPD_SETUID(, &s_uid)
+                       SKIP_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY(, &s_port)
+       );
 
   if(opt & OPT_DECODE_URL) {
       printf("%s", decodeString(url_for_decode, 1));
@@ -2078,9 +2077,7 @@ int httpd_main(int argc, char *argv[])
 #endif
 #endif
 
-  if(chdir(home_httpd)) {
-    bb_perror_msg_and_die("can`t chdir to %s", home_httpd);
-  }
+  bb_xchdir(home_httpd);
 #ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
   server = openServer();
 # ifdef CONFIG_FEATURE_HTTPD_SETUID
@@ -2113,8 +2110,7 @@ int httpd_main(int argc, char *argv[])
 
 #if !ENABLE_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
 # if !DEBUG
-  if (daemon(1, 0) < 0)     /* don`t change curent directory */
-       bb_perror_msg_and_die("daemon");
+  bb_xdaemon(1, 0);     /* don`t change curent directory */
 # endif
   return miniHttpd(server);
 #else