Ignore:
Timestamp:
Nov 4, 2007, 3:16:40 AM (16 years ago)
Author:
Bruno Cornec
Message:

Update to busybox 1.7.2

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/2.2.5/mindi-busybox/networking/httpd.c

    r902 r1765  
    2121 *
    2222 *
    23  * When a url contains "cgi-bin" it is assumed to be a cgi script.  The
     23 * When a url starts by "/cgi-bin/" it is assumed to be a cgi script.  The
    2424 * server changes directory to the location of the script and executes it
    2525 * after setting QUERY_STRING and other environment variables.
     26 *
     27 * Doc:
     28 * "CGI Environment Variables": http://hoohoo.ncsa.uiuc.edu/cgi/env.html
    2629 *
    2730 * The server can also be invoked as a url arg decoder and html text encoder
     
    4043 * A:127.0.0.1       # Allow local loopback connections
    4144 * D:*               # Deny from other IP connections
     45 * E404:/path/e404.html # /path/e404.html is the 404 (not found) error page
    4246 * /cgi-bin:foo:bar  # Require user foo, pwd bar on urls starting with /cgi-bin/
    4347 * /adm:admin:setup  # Require user admin, pwd setup on urls starting with /adm/
     
    8286 * result, the subdir settings only have a lifetime of a single request.
    8387 *
     88 * Custom error pages can contain an absolute path or be relative to
     89 * 'home_httpd'. Error pages are to be static files (no CGI or script). Error
     90 * page can only be defined in the root configuration file and are not taken
     91 * into account in local (directories) config files.
    8492 *
    8593 * If -c is not set, an attempt will be made to open the default
     
    8795 * server exits with an error.
    8896 *
    89 */
    90 
    91 
    92 #include <stdio.h>
    93 #include <ctype.h>         /* for isspace           */
    94 #include <string.h>
    95 #include <stdlib.h>        /* for malloc            */
    96 #include <time.h>
    97 #include <unistd.h>        /* for close             */
    98 #include <signal.h>
    99 #include <sys/types.h>
    100 #include <sys/socket.h>    /* for connect and socket*/
    101 #include <netinet/in.h>    /* for sockaddr_in       */
    102 #include <sys/stat.h>
    103 #include <sys/wait.h>
    104 #include <fcntl.h>         /* for open modes        */
    105 #include "busybox.h"
    106 
    107 
    108 static const char httpdVersion[] = "busybox httpd/1.35 6-Oct-2004";
    109 static const char default_path_httpd_conf[] = "/etc";
    110 static const char httpd_conf[] = "httpd.conf";
    111 static const char home[] = "./";
    112 
    113 #ifdef CONFIG_LFS
    114 # define cont_l_fmt "%lld"
    115 # define cont_l_type (long long)
    116 #else
    117 # define cont_l_fmt "%ld"
    118 # define cont_l_type (long)
    119 #endif
    120 
    121 #define TIMEOUT 60
    122 
    123 // Note: busybox xfuncs are not used because we want the server to keep running
    124 //       if something bad happens due to a malformed user request.
    125 //       As a result, all memory allocation after daemonize
    126 //       is checked rigorously
     97 */
     98
     99#include "libbb.h"
     100#if ENABLE_FEATURE_HTTPD_USE_SENDFILE
     101#include <sys/sendfile.h>
     102#endif
    127103
    128104//#define DEBUG 1
    129 
    130 #ifndef DEBUG
    131 # define DEBUG 0
    132 #endif
    133 
    134 #define MAX_MEMORY_BUFF 8192    /* IO buffer */
    135 
    136 typedef struct HT_ACCESS {
     105#define DEBUG 0
     106
     107/* amount of buffering in a pipe */
     108#ifndef PIPE_BUF
     109# define PIPE_BUF 4096
     110#endif
     111
     112#define IOBUF_SIZE 8192    /* IO buffer */
     113
     114#define HEADER_READ_TIMEOUT 60
     115
     116static const char default_path_httpd_conf[] ALIGN1 = "/etc";
     117static const char httpd_conf[] ALIGN1 = "httpd.conf";
     118static const char HTTP_200[] ALIGN1 = "HTTP/1.0 200 OK\r\n";
     119
     120typedef struct has_next_ptr {
     121    struct has_next_ptr *next;
     122} has_next_ptr;
     123
     124/* Must have "next" as a first member */
     125typedef struct Htaccess {
     126    struct Htaccess *next;
    137127    char *after_colon;
    138     struct HT_ACCESS *next;
    139     char before_colon[1];         /* really bigger, must last */
     128    char before_colon[1];  /* really bigger, must be last */
    140129} Htaccess;
    141130
    142 typedef struct HT_ACCESS_IP {
    143     unsigned int ip;
    144     unsigned int mask;
     131/* Must have "next" as a first member */
     132typedef struct Htaccess_IP {
     133    struct Htaccess_IP *next;
     134    unsigned ip;
     135    unsigned mask;
    145136    int allow_deny;
    146     struct HT_ACCESS_IP *next;
    147137} Htaccess_IP;
    148138
    149 typedef struct
    150 {
    151   char buf[MAX_MEMORY_BUFF];
    152 
    153   USE_FEATURE_HTTPD_BASIC_AUTH(const char *realm;)
    154   USE_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;)
    155 
    156   const char *query;
    157 
    158   USE_FEATURE_HTTPD_CGI(char *referer;)
    159 
    160   const char *configFile;
    161 
    162   unsigned int rmt_ip;
    163 #if defined(CONFIG_FEATURE_HTTPD_CGI) || DEBUG
    164   char rmt_ip_str[16];     /* for set env REMOTE_ADDR */
    165 #endif
    166   unsigned port;           /* server initial port and for
    167                   set env REMOTE_PORT */
    168   union HTTPD_FOUND {
     139enum {
     140    HTTP_OK = 200,
     141    HTTP_MOVED_TEMPORARILY = 302,
     142    HTTP_BAD_REQUEST = 400,       /* malformed syntax */
     143    HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */
     144    HTTP_NOT_FOUND = 404,
     145    HTTP_FORBIDDEN = 403,
     146    HTTP_REQUEST_TIMEOUT = 408,
     147    HTTP_NOT_IMPLEMENTED = 501,   /* used for unrecognized requests */
     148    HTTP_INTERNAL_SERVER_ERROR = 500,
     149    HTTP_CONTINUE = 100,
     150#if 0   /* future use */
     151    HTTP_SWITCHING_PROTOCOLS = 101,
     152    HTTP_CREATED = 201,
     153    HTTP_ACCEPTED = 202,
     154    HTTP_NON_AUTHORITATIVE_INFO = 203,
     155    HTTP_NO_CONTENT = 204,
     156    HTTP_MULTIPLE_CHOICES = 300,
     157    HTTP_MOVED_PERMANENTLY = 301,
     158    HTTP_NOT_MODIFIED = 304,
     159    HTTP_PAYMENT_REQUIRED = 402,
     160    HTTP_BAD_GATEWAY = 502,
     161    HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */
     162    HTTP_RESPONSE_SETSIZE = 0xffffffff
     163#endif
     164};
     165
     166static const uint16_t http_response_type[] ALIGN2 = {
     167    HTTP_OK,
     168    HTTP_MOVED_TEMPORARILY,
     169    HTTP_REQUEST_TIMEOUT,
     170    HTTP_NOT_IMPLEMENTED,
     171#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
     172    HTTP_UNAUTHORIZED,
     173#endif
     174    HTTP_NOT_FOUND,
     175    HTTP_BAD_REQUEST,
     176    HTTP_FORBIDDEN,
     177    HTTP_INTERNAL_SERVER_ERROR,
     178#if 0   /* not implemented */
     179    HTTP_CREATED,
     180    HTTP_ACCEPTED,
     181    HTTP_NO_CONTENT,
     182    HTTP_MULTIPLE_CHOICES,
     183    HTTP_MOVED_PERMANENTLY,
     184    HTTP_NOT_MODIFIED,
     185    HTTP_BAD_GATEWAY,
     186    HTTP_SERVICE_UNAVAILABLE,
     187#endif
     188};
     189
     190static const struct {
     191    const char *name;
     192    const char *info;
     193} http_response[ARRAY_SIZE(http_response_type)] = {
     194    { "OK", NULL },
     195    { "Found", "Directories must end with a slash" }, /* ?? */
     196    { "Request Timeout", "No request appeared within 60 seconds" },
     197    { "Not Implemented", "The requested method is not recognized" },
     198#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
     199    { "Unauthorized", "" },
     200#endif
     201    { "Not Found", "The requested URL was not found" },
     202    { "Bad Request", "Unsupported method" },
     203    { "Forbidden", ""  },
     204    { "Internal Server Error", "Internal Server Error" },
     205#if 0   /* not implemented */
     206    { "Created" },
     207    { "Accepted" },
     208    { "No Content" },
     209    { "Multiple Choices" },
     210    { "Moved Permanently" },
     211    { "Not Modified" },
     212    { "Bad Gateway", "" },
     213    { "Service Unavailable", "" },
     214#endif
     215};
     216
     217struct globals {
     218    int verbose;            /* must be int (used by getopt32) */
     219    smallint flg_deny_all;
     220
     221    unsigned rmt_ip;    /* used for IP-based allow/deny rules */
     222    time_t last_mod;
     223    off_t ContentLength;    /* -1 - unknown */
     224    char *rmt_ip_str;       /* for $REMOTE_ADDR and $REMOTE_PORT */
     225    const char *bind_addr_or_port;
     226
     227    const char *g_query;
     228    const char *configFile;
     229    const char *home_httpd;
     230
    169231    const char *found_mime_type;
    170232    const char *found_moved_temporarily;
    171   } httpd_found;
    172 
    173   off_t ContentLength;          /* -1 - unknown */
    174   time_t last_mod;
    175 
    176   Htaccess_IP *ip_a_d;          /* config allow/deny lines */
    177   int flg_deny_all;
    178 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
    179   Htaccess *auth;               /* config user:password lines */
    180 #endif
    181 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
    182   Htaccess *mime_a;             /* config mime types */
    183 #endif
    184 
    185 #ifdef CONFIG_FEATURE_HTTPD_WITHOUT_INETD
    186   int accepted_socket;
    187 # define a_c_r config->accepted_socket
    188 # define a_c_w config->accepted_socket
    189 #else
    190 # define a_c_r 0
    191 # define a_c_w 1
    192 #endif
    193   volatile int alarm_signaled;
    194 
    195 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
    196   Htaccess *script_i;           /* config script interpreters */
    197 #endif
    198 } HttpdConfig;
    199 
    200 static HttpdConfig *config;
    201 
    202 static const char request_GET[] = "GET";    /* size algorithmic optimize */
    203 
    204 static const char* const suffixTable [] = {
    205 /* Warning: shorted equivalent suffix in one line must be first */
    206   ".htm.html", "text/html",
    207   ".jpg.jpeg", "image/jpeg",
    208   ".gif", "image/gif",
    209   ".png", "image/png",
    210   ".txt.h.c.cc.cpp", "text/plain",
    211   ".css", "text/css",
    212   ".wav", "audio/wav",
    213   ".avi", "video/x-msvideo",
    214   ".qt.mov", "video/quicktime",
    215   ".mpe.mpeg", "video/mpeg",
    216   ".mid.midi", "audio/midi",
    217   ".mp3", "audio/mpeg",
    218 #if 0                        /* unpopular */
    219   ".au", "audio/basic",
    220   ".pac", "application/x-ns-proxy-autoconfig",
    221   ".vrml.wrl", "model/vrml",
    222 #endif
    223   0, "application/octet-stream" /* default */
    224   };
    225 
    226 typedef enum
    227 {
    228   HTTP_OK = 200,
    229   HTTP_MOVED_TEMPORARILY = 302,
    230   HTTP_BAD_REQUEST = 400,       /* malformed syntax */
    231   HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */
    232   HTTP_NOT_FOUND = 404,
    233   HTTP_FORBIDDEN = 403,
    234   HTTP_REQUEST_TIMEOUT = 408,
    235   HTTP_NOT_IMPLEMENTED = 501,   /* used for unrecognized requests */
    236   HTTP_INTERNAL_SERVER_ERROR = 500,
    237 #if 0 /* future use */
    238   HTTP_CONTINUE = 100,
    239   HTTP_SWITCHING_PROTOCOLS = 101,
    240   HTTP_CREATED = 201,
    241   HTTP_ACCEPTED = 202,
    242   HTTP_NON_AUTHORITATIVE_INFO = 203,
    243   HTTP_NO_CONTENT = 204,
    244   HTTP_MULTIPLE_CHOICES = 300,
    245   HTTP_MOVED_PERMANENTLY = 301,
    246   HTTP_NOT_MODIFIED = 304,
    247   HTTP_PAYMENT_REQUIRED = 402,
    248   HTTP_BAD_GATEWAY = 502,
    249   HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */
    250   HTTP_RESPONSE_SETSIZE=0xffffffff
    251 #endif
    252 } HttpResponseNum;
    253 
    254 typedef struct
    255 {
    256   HttpResponseNum type;
    257   const char *name;
    258   const char *info;
    259 } HttpEnumString;
    260 
    261 static const HttpEnumString httpResponseNames[] = {
    262   { HTTP_OK, "OK", NULL },
    263   { HTTP_MOVED_TEMPORARILY, "Found", "Directories must end with a slash." },
    264   { HTTP_REQUEST_TIMEOUT, "Request Timeout",
    265     "No request appeared within a reasonable time period." },
    266   { HTTP_NOT_IMPLEMENTED, "Not Implemented",
    267     "The requested method is not recognized by this server." },
    268 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
    269   { HTTP_UNAUTHORIZED, "Unauthorized", "" },
    270 #endif
    271   { HTTP_NOT_FOUND, "Not Found",
    272     "The requested URL was not found on this server." },
    273   { HTTP_BAD_REQUEST, "Bad Request", "Unsupported method." },
    274   { HTTP_FORBIDDEN, "Forbidden", "" },
    275   { HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error",
    276     "Internal Server Error" },
    277 #if 0                               /* not implemented */
    278   { HTTP_CREATED, "Created" },
    279   { HTTP_ACCEPTED, "Accepted" },
    280   { HTTP_NO_CONTENT, "No Content" },
    281   { HTTP_MULTIPLE_CHOICES, "Multiple Choices" },
    282   { HTTP_MOVED_PERMANENTLY, "Moved Permanently" },
    283   { HTTP_NOT_MODIFIED, "Not Modified" },
    284   { HTTP_BAD_GATEWAY, "Bad Gateway", "" },
    285   { HTTP_SERVICE_UNAVAILABLE, "Service Unavailable", "" },
     233    Htaccess_IP *ip_a_d;    /* config allow/deny lines */
     234
     235    USE_FEATURE_HTTPD_BASIC_AUTH(const char *g_realm;)
     236    USE_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;)
     237    USE_FEATURE_HTTPD_CGI(char *referer;)
     238    USE_FEATURE_HTTPD_CGI(char *user_agent;)
     239
     240#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
     241    Htaccess *g_auth;       /* config user:password lines */
     242#endif
     243#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
     244    Htaccess *mime_a;       /* config mime types */
     245#endif
     246#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
     247    Htaccess *script_i;     /* config script interpreters */
     248#endif
     249    char *iobuf;            /* [IOBUF_SIZE] */
     250#define hdr_buf bb_common_bufsiz1
     251    char *hdr_ptr;
     252    int hdr_cnt;
     253#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
     254    const char *http_error_page[ARRAY_SIZE(http_response_type)];
    286255#endif
    287256};
    288 
    289 
    290 static const char RFC1123FMT[] = "%a, %d %b %Y %H:%M:%S GMT";
    291 static const char Content_length[] = "Content-length:";
    292 
    293 
    294 static int
    295 scan_ip (const char **ep, unsigned int *ip, unsigned char endc)
    296 {
    297   const char *p = *ep;
    298   int auto_mask = 8;
    299   int j;
    300 
    301   *ip = 0;
    302   for (j = 0; j < 4; j++) {
    303     unsigned int octet;
    304 
    305     if ((*p < '0' || *p > '9') && (*p != '/' || j == 0) && *p != 0)
    306       return -auto_mask;
    307     octet = 0;
    308     while (*p >= '0' && *p <= '9') {
    309       octet *= 10;
    310       octet += *p - '0';
    311       if (octet > 255)
     257#define G (*ptr_to_globals)
     258#define verbose           (G.verbose          )
     259#define flg_deny_all      (G.flg_deny_all     )
     260#define rmt_ip            (G.rmt_ip           )
     261#define bind_addr_or_port (G.bind_addr_or_port)
     262#define g_query           (G.g_query          )
     263#define configFile        (G.configFile       )
     264#define home_httpd        (G.home_httpd       )
     265#define found_mime_type   (G.found_mime_type  )
     266#define found_moved_temporarily (G.found_moved_temporarily)
     267#define ContentLength     (G.ContentLength    )
     268#define last_mod          (G.last_mod         )
     269#define ip_a_d            (G.ip_a_d           )
     270#define g_realm           (G.g_realm          )
     271#define remoteuser        (G.remoteuser       )
     272#define referer           (G.referer          )
     273#define user_agent        (G.user_agent       )
     274#define rmt_ip_str        (G.rmt_ip_str       )
     275#define g_auth            (G.g_auth           )
     276#define mime_a            (G.mime_a           )
     277#define script_i          (G.script_i         )
     278#define iobuf             (G.iobuf            )
     279#define hdr_ptr           (G.hdr_ptr          )
     280#define hdr_cnt           (G.hdr_cnt          )
     281#define http_error_page   (G.http_error_page  )
     282#define INIT_G() do { \
     283    PTR_TO_GLOBALS = xzalloc(sizeof(G)); \
     284    USE_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \
     285    bind_addr_or_port = "80"; \
     286    ContentLength = -1; \
     287} while (0)
     288
     289
     290#define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1)
     291
     292/* Prototypes */
     293static void send_file_and_exit(const char *url, int headers) ATTRIBUTE_NORETURN;
     294
     295static void free_llist(has_next_ptr **pptr)
     296{
     297    has_next_ptr *cur = *pptr;
     298    while (cur) {
     299        has_next_ptr *t = cur;
     300        cur = cur->next;
     301        free(t);
     302    }
     303    *pptr = NULL;
     304}
     305
     306#if ENABLE_FEATURE_HTTPD_BASIC_AUTH \
     307 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \
     308 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
     309static ALWAYS_INLINE void free_Htaccess_list(Htaccess **pptr)
     310{
     311    free_llist((has_next_ptr**)pptr);
     312}
     313#endif
     314
     315static ALWAYS_INLINE void free_Htaccess_IP_list(Htaccess_IP **pptr)
     316{
     317    free_llist((has_next_ptr**)pptr);
     318}
     319
     320/* Returns presumed mask width in bits or < 0 on error.
     321 * Updates strp, stores IP at provided pointer */
     322static int scan_ip(const char **strp, unsigned *ipp, unsigned char endc)
     323{
     324    const char *p = *strp;
     325    int auto_mask = 8;
     326    unsigned ip = 0;
     327    int j;
     328
     329    if (*p == '/')
    312330        return -auto_mask;
    313       p++;
    314     }
    315     if (*p == '.')
    316       p++;
    317     if (*p != '/' && *p != 0)
    318       auto_mask += 8;
    319     *ip = ((*ip) << 8) | octet;
    320   }
    321   if (*p != 0) {
    322     if (*p != endc)
    323         return -auto_mask;
    324     p++;
    325     if(*p == 0)
    326         return -auto_mask;
    327   }
    328   *ep = p;
    329   return auto_mask;
    330 }
    331 
    332 static int
    333 scan_ip_mask (const char *ipm, unsigned int *ip, unsigned int *mask)
    334 {
    335   int i;
    336   unsigned int msk;
    337 
    338   i = scan_ip(&ipm, ip, '/');
    339   if(i < 0)
    340     return i;
    341   if(*ipm) {
    342     const char *p = ipm;
    343 
    344     i = 0;
    345     while (*p) {
    346         if (*p < '0' || *p > '9') {
    347             if (*p == '.') {
    348                 i = scan_ip (&ipm, mask, 0);
    349                 return i != 32;
    350             }
     331
     332    for (j = 0; j < 4; j++) {
     333        unsigned octet;
     334
     335        if ((*p < '0' || *p > '9') && *p != '/' && *p)
     336            return -auto_mask;
     337        octet = 0;
     338        while (*p >= '0' && *p <= '9') {
     339            octet *= 10;
     340            octet += *p - '0';
     341            if (octet > 255)
     342                return -auto_mask;
     343            p++;
     344        }
     345        if (*p == '.')
     346            p++;
     347        if (*p != '/' && *p)
     348            auto_mask += 8;
     349        ip = (ip << 8) | octet;
     350    }
     351    if (*p) {
     352        if (*p != endc)
     353            return -auto_mask;
     354        p++;
     355        if (*p == '\0')
     356            return -auto_mask;
     357    }
     358    *ipp = ip;
     359    *strp = p;
     360    return auto_mask;
     361}
     362
     363/* Returns 0 on success. Stores IP and mask at provided pointers */
     364static int scan_ip_mask(const char *str, unsigned *ipp, unsigned *maskp)
     365{
     366    int i;
     367    unsigned mask;
     368    char *p;
     369
     370    i = scan_ip(&str, ipp, '/');
     371    if (i < 0)
     372        return i;
     373
     374    if (*str) {
     375        /* there is /xxx after dotted-IP address */
     376        i = bb_strtou(str, &p, 10);
     377        if (*p == '.') {
     378            /* 'xxx' itself is dotted-IP mask, parse it */
     379            /* (return 0 (success) only if it has N.N.N.N form) */
     380            return scan_ip(&str, maskp, '\0') - 32;
     381        }
     382        if (*p)
    351383            return -1;
    352         }
    353         i *= 10;
    354         i += *p - '0';
    355         p++;
    356     }
    357   }
    358   if (i > 32 || i < 0)
    359       return -1;
    360   msk = 0x80000000;
    361   *mask = 0;
    362   while (i > 0) {
    363     *mask |= msk;
    364     msk >>= 1;
    365     i--;
    366   }
    367   return 0;
    368 }
    369 
    370 #if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES)
    371 static void free_config_lines(Htaccess **pprev)
    372 {
    373     Htaccess *prev = *pprev;
    374 
    375     while( prev ) {
    376     Htaccess *cur = prev;
    377 
    378     prev = cur->next;
    379     free(cur);
    380     }
    381     *pprev = NULL;
    382 }
    383 #endif
    384 
     384    }
     385
     386    if (i > 32)
     387        return -1;
     388
     389    if (sizeof(unsigned) == 4 && i == 32) {
     390        /* mask >>= 32 below may not work */
     391        mask = 0;
     392    } else {
     393        mask = 0xffffffff;
     394        mask >>= i;
     395    }
     396    /* i == 0 -> *maskp = 0x00000000
     397     * i == 1 -> *maskp = 0x80000000
     398     * i == 4 -> *maskp = 0xf0000000
     399     * i == 31 -> *maskp = 0xfffffffe
     400     * i == 32 -> *maskp = 0xffffffff */
     401    *maskp = (uint32_t)(~mask);
     402    return 0;
     403}
     404
     405/*
     406 * Parse configuration file into in-memory linked list.
     407 *
     408 * The first non-white character is examined to determine if the config line
     409 * is one of the following:
     410 *    .ext:mime/type   # new mime type not compiled into httpd
     411 *    [adAD]:from      # ip address allow/deny, * for wildcard
     412 *    /path:user:pass  # username/password
     413 *    Ennn:error.html  # error page for status nnn
     414 *
     415 * Any previous IP rules are discarded.
     416 * If the flag argument is not SUBDIR_PARSE then all /path and mime rules
     417 * are also discarded.  That is, previous settings are retained if flag is
     418 * SUBDIR_PARSE.
     419 * Error pages are only parsed on the main config file.
     420 *
     421 * path   Path where to look for httpd.conf (without filename).
     422 * flag   Type of the parse request.
     423 */
    385424/* flag */
    386425#define FIRST_PARSE          0
     
    388427#define SIGNALED_PARSE       2
    389428#define FIND_FROM_HTTPD_ROOT 3
    390 /****************************************************************************
    391  *
    392  > $Function: parse_conf()
    393  *
    394  * $Description: parse configuration file into in-memory linked list.
    395  *
    396  * The first non-white character is examined to determine if the config line
    397  * is one of the following:
    398  *    .ext:mime/type   # new mime type not compiled into httpd
    399  *    [adAD]:from      # ip address allow/deny, * for wildcard
    400  *    /path:user:pass  # username/password
    401  *
    402  * Any previous IP rules are discarded.
    403  * If the flag argument is not SUBDIR_PARSE then all /path and mime rules
    404  * are also discarded.  That is, previous settings are retained if flag is
    405  * SUBDIR_PARSE.
    406  *
    407  * $Parameters:
    408  *      (const char *) path . . null for ip address checks, path for password
    409  *                              checks.
    410  *      (int) flag  . . . . . . the source of the parse request.
    411  *
    412  * $Return: (None)
    413  *
    414  ****************************************************************************/
    415429static void parse_conf(const char *path, int flag)
    416430{
    417     FILE *f;
    418 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
    419     Htaccess *prev, *cur;
    420 #elif CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
    421     Htaccess *cur;
    422 #endif
    423 
    424     const char *cf = config->configFile;
    425     char buf[160];
    426     char *p0 = NULL;
    427     char *c, *p;
    428 
    429     /* free previous ip setup if present */
    430     Htaccess_IP *pip = config->ip_a_d;
    431 
    432     while( pip ) {
    433     Htaccess_IP *cur_ipl = pip;
    434 
    435     pip = cur_ipl->next;
    436     free(cur_ipl);
    437     }
    438     config->ip_a_d = NULL;
    439 
    440     config->flg_deny_all = 0;
    441 
    442 #if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR)
    443     /* retain previous auth and mime config only for subdir parse */
    444     if(flag != SUBDIR_PARSE) {
    445 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
    446     free_config_lines(&config->auth);
    447 #endif
    448 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
    449     free_config_lines(&config->mime_a);
    450 #endif
    451 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
    452     free_config_lines(&config->script_i);
    453 #endif
    454     }
    455 #endif
    456 
    457     if(flag == SUBDIR_PARSE || cf == NULL) {
    458     cf = alloca(strlen(path) + sizeof(httpd_conf) + 2);
    459     if(cf == NULL) {
    460         if(flag == FIRST_PARSE)
    461         bb_error_msg_and_die(bb_msg_memory_exhausted);
    462         return;
    463     }
    464     sprintf((char *)cf, "%s/%s", path, httpd_conf);
    465     }
    466 
    467     while((f = fopen(cf, "r")) == NULL) {
    468     if(flag == SUBDIR_PARSE || flag == FIND_FROM_HTTPD_ROOT) {
    469         /* config file not found, no changes to config */
    470         return;
    471     }
    472     if(config->configFile && flag == FIRST_PARSE) /* if -c option given */
    473         bb_perror_msg_and_die("%s", cf);
    474     flag = FIND_FROM_HTTPD_ROOT;
    475     cf = httpd_conf;
    476     }
    477 
    478 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
    479     prev = config->auth;
    480 #endif
    481     /* This could stand some work */
    482     while ( (p0 = fgets(buf, sizeof(buf), f)) != NULL) {
    483     c = NULL;
    484     for(p = p0; *p0 != 0 && *p0 != '#'; p0++) {
    485         if(!isspace(*p0)) {
    486             *p++ = *p0;
    487             if(*p0 == ':' && c == NULL)
    488             c = p;
    489         }
    490     }
    491     *p = 0;
    492 
    493     /* test for empty or strange line */
    494     if (c == NULL || *c == 0)
    495         continue;
    496     p0 = buf;
    497     if(*p0 == 'd')
    498         *p0 = 'D';
    499     if(*c == '*') {
    500         if(*p0 == 'D') {
    501         /* memorize deny all */
    502         config->flg_deny_all++;
    503         }
    504         /* skip default other "word:*" config lines */
    505         continue;
    506     }
    507 
    508     if(*p0 == 'a')
    509         *p0 = 'A';
    510     else if(*p0 != 'D' && *p0 != 'A'
    511 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
    512        && *p0 != '/'
    513 #endif
    514 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
    515        && *p0 != '.'
    516 #endif
    517 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
    518        && *p0 != '*'
    519 #endif
    520       )
    521            continue;
    522     if(*p0 == 'A' || *p0 == 'D') {
    523         /* storing current config IP line */
    524         pip = calloc(1, sizeof(Htaccess_IP));
    525         if(pip) {
    526             if(scan_ip_mask (c, &(pip->ip), &(pip->mask))) {
    527             /* syntax IP{/mask} error detected, protect all */
     431    FILE *f;
     432#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
     433    Htaccess *prev;
     434#endif
     435#if ENABLE_FEATURE_HTTPD_BASIC_AUTH \
     436 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \
     437 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
     438    Htaccess *cur;
     439#endif
     440    const char *cf = configFile;
     441    char buf[160];
     442    char *p0 = NULL;
     443    char *c, *p;
     444    Htaccess_IP *pip;
     445
     446    /* discard old rules */
     447    free_Htaccess_IP_list(&ip_a_d);
     448    flg_deny_all = 0;
     449#if ENABLE_FEATURE_HTTPD_BASIC_AUTH \
     450 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \
     451 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
     452    /* retain previous auth and mime config only for subdir parse */
     453    if (flag != SUBDIR_PARSE) {
     454#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
     455        free_Htaccess_list(&g_auth);
     456#endif
     457#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
     458        free_Htaccess_list(&mime_a);
     459#endif
     460#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
     461        free_Htaccess_list(&script_i);
     462#endif
     463    }
     464#endif
     465
     466    if (flag == SUBDIR_PARSE || cf == NULL) {
     467        cf = alloca(strlen(path) + sizeof(httpd_conf) + 2);
     468        sprintf((char *)cf, "%s/%s", path, httpd_conf);
     469    }
     470
     471    while ((f = fopen(cf, "r")) == NULL) {
     472        if (flag == SUBDIR_PARSE || flag == FIND_FROM_HTTPD_ROOT) {
     473            /* config file not found, no changes to config */
     474            return;
     475        }
     476        if (configFile && flag == FIRST_PARSE) /* if -c option given */
     477            bb_perror_msg_and_die("%s", cf);
     478        flag = FIND_FROM_HTTPD_ROOT;
     479        cf = httpd_conf;
     480    }
     481
     482#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
     483    prev = g_auth;
     484#endif
     485    /* This could stand some work */
     486    while ((p0 = fgets(buf, sizeof(buf), f)) != NULL) {
     487        c = NULL;
     488        for (p = p0; *p0 != '\0' && *p0 != '#'; p0++) {
     489            if (!isspace(*p0)) {
     490                *p++ = *p0;
     491                if (*p0 == ':' && c == NULL)
     492                    c = p;
     493            }
     494        }
     495        *p = '\0';
     496
     497        /* test for empty or strange line */
     498        if (c == NULL || *c == '\0')
     499            continue;
     500        p0 = buf;
     501        if (*p0 == 'd')
    528502            *p0 = 'D';
    529             pip->mask = 0;
    530             }
    531             pip->allow_deny = *p0;
    532             if(*p0 == 'D') {
    533             /* Deny:form_IP move top */
    534             pip->next = config->ip_a_d;
    535             config->ip_a_d = pip;
    536             } else {
    537             /* add to bottom A:form_IP config line */
    538             Htaccess_IP *prev_IP = config->ip_a_d;
    539 
    540             if(prev_IP == NULL) {
    541                 config->ip_a_d = pip;
    542             } else {
    543                 while(prev_IP->next)
    544                     prev_IP = prev_IP->next;
    545                 prev_IP->next = pip;
    546             }
    547             }
    548         }
    549         continue;
    550     }
    551 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
    552     if(*p0 == '/') {
    553         /* make full path from httpd root / curent_path / config_line_path */
    554         cf = flag == SUBDIR_PARSE ? path : "";
    555         p0 = malloc(strlen(cf) + (c - buf) + 2 + strlen(c));
    556         if(p0 == NULL)
    557         continue;
    558         c[-1] = 0;
    559         sprintf(p0, "/%s%s", cf, buf);
    560 
    561         /* another call bb_simplify_path */
    562         cf = p = p0;
    563 
    564         do {
    565             if (*p == '/') {
    566             if (*cf == '/') {    /* skip duplicate (or initial) slash */
    567                 continue;
    568             } else if (*cf == '.') {
    569                 if (cf[1] == '/' || cf[1] == 0) { /* remove extra '.' */
     503        if (*c == '*') {
     504            if (*p0 == 'D') {
     505                /* memorize deny all */
     506                flg_deny_all = 1;
     507            }
     508            /* skip default other "word:*" config lines */
     509            continue;
     510        }
     511
     512        if (*p0 == 'a')
     513            *p0 = 'A';
     514        if (*p0 == 'A' || *p0 == 'D') {
     515            /* storing current config IP line */
     516            pip = xzalloc(sizeof(Htaccess_IP));
     517            if (pip) {
     518                if (scan_ip_mask(c, &(pip->ip), &(pip->mask))) {
     519                    /* syntax IP{/mask} error detected, protect all */
     520                    *p0 = 'D';
     521                    pip->mask = 0;
     522                }
     523                pip->allow_deny = *p0;
     524                if (*p0 == 'D') {
     525                    /* Deny:from_IP move top */
     526                    pip->next = ip_a_d;
     527                    ip_a_d = pip;
     528                } else {
     529                    /* add to bottom A:form_IP config line */
     530                    Htaccess_IP *prev_IP = ip_a_d;
     531
     532                    if (prev_IP == NULL) {
     533                        ip_a_d = pip;
     534                    } else {
     535                        while (prev_IP->next)
     536                            prev_IP = prev_IP->next;
     537                        prev_IP->next = pip;
     538                    }
     539                }
     540            }
     541            continue;
     542        }
     543
     544#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
     545        if (flag == FIRST_PARSE && *p0 == 'E') {
     546            int i;
     547            /* error status code */
     548            int status = atoi(++p0);
     549            /* c already points at the character following ':' in parse loop */
     550            /* c = strchr(p0, ':'); c++; */
     551            if (status < HTTP_CONTINUE) {
     552                bb_error_msg("config error '%s' in '%s'", buf, cf);
    570553                continue;
    571                 } else if ((cf[1] == '.') && (cf[2] == '/' || cf[2] == 0)) {
    572                 ++cf;
    573                 if (p > p0) {
    574                     while (*--p != '/');    /* omit previous dir */
    575                 }
    576                 continue;
    577                 }
    578             }
    579             }
    580             *++p = *cf;
    581         } while (*++cf);
    582 
    583         if ((p == p0) || (*p != '/')) {      /* not a trailing slash */
    584         ++p;                             /* so keep last character */
    585         }
    586         *p = 0;
    587         sprintf(p0, "%s:%s", p0, c);
    588     }
    589 #endif
    590 
    591 #if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR)
    592     /* storing current config line */
    593     cur = calloc(1, sizeof(Htaccess) + strlen(p0));
    594     if(cur) {
    595         cf = strcpy(cur->before_colon, p0);
    596         c = strchr(cf, ':');
    597         *c++ = 0;
    598         cur->after_colon = c;
    599 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
    600         if(*cf == '.') {
    601         /* config .mime line move top for overwrite previous */
    602         cur->next = config->mime_a;
    603         config->mime_a = cur;
    604         continue;
    605         }
    606 #endif
    607 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
    608         if(*cf == '*' && cf[1] == '.') {
    609         /* config script interpreter line move top for overwrite previous */
    610         cur->next = config->script_i;
    611         config->script_i = cur;
    612         continue;
    613         }
    614 #endif
    615 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
    616         free(p0);
    617         if(prev == NULL) {
    618         /* first line */
    619         config->auth = prev = cur;
    620         } else {
    621         /* sort path, if current lenght eq or bigger then move up */
    622         Htaccess *prev_hti = config->auth;
    623         size_t l = strlen(cf);
    624         Htaccess *hti;
    625 
    626         for(hti = prev_hti; hti; hti = hti->next) {
    627             if(l >= strlen(hti->before_colon)) {
    628             /* insert before hti */
    629             cur->next = hti;
    630             if(prev_hti != hti) {
    631                 prev_hti->next = cur;
    632             } else {
    633                 /* insert as top */
    634                 config->auth = cur;
    635             }
    636             break;
    637             }
    638             if(prev_hti != hti)
    639                 prev_hti = prev_hti->next;
    640         }
    641         if(!hti)  {       /* not inserted, add to bottom */
    642             prev->next = cur;
    643             prev = cur;
    644         }
    645         }
    646 #endif
    647     }
    648 #endif
    649    }
    650    fclose(f);
    651 }
    652 
    653 #ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
    654 /****************************************************************************
    655  *
    656  > $Function: encodeString()
    657  *
    658  * $Description: Given a string, html encode special characters.
    659  *   This is used for the -e command line option to provide an easy way
    660  *   for scripts to encode result data without confusing browsers.  The
    661  *   returned string pointer is memory allocated by malloc().
    662  *
    663  * $Parameters:
    664  *      (const char *) string . . The first string to encode.
    665  *
    666  * $Return: (char *) . . . .. . . A pointer to the encoded string.
    667  *
    668  * $Errors: Returns a null string ("") if memory is not available.
    669  *
    670  ****************************************************************************/
    671 static char *encodeString(const char *string)
    672 {
    673   /* take the simple route and encode everything */
    674   /* could possibly scan once to get length.     */
    675   int len = strlen(string);
    676   char *out = malloc(len * 6 + 1);
    677   char *p=out;
    678   char ch;
    679 
    680   if (!out) return "";
    681   while ((ch = *string++)) {
    682     // very simple check for what to encode
    683     if (isalnum(ch)) *p++ = ch;
    684     else p += sprintf(p, "&#%d;", (unsigned char) ch);
    685   }
    686   *p=0;
    687   return out;
    688 }
    689 #endif          /* CONFIG_FEATURE_HTTPD_ENCODE_URL_STR */
    690 
    691 /****************************************************************************
    692  *
    693  > $Function: decodeString()
    694  *
    695  * $Description: Given a URL encoded string, convert it to plain ascii.
    696  *   Since decoding always makes strings smaller, the decode is done in-place.
    697  *   Thus, callers should strdup() the argument if they do not want the
    698  *   argument modified.  The return is the original pointer, allowing this
    699  *   function to be easily used as arguments to other functions.
    700  *
    701  * $Parameters:
    702  *      (char *) string . . . The first string to decode.
    703  *      (int)    flag   . . . 1 if require decode '+' as ' ' for CGI
    704  *
    705  * $Return: (char *)  . . . . A pointer to the decoded string (same as input).
    706  *
    707  * $Errors: None
    708  *
    709  ****************************************************************************/
    710 static char *decodeString(char *orig, int flag_plus_to_space)
    711 {
    712   /* note that decoded string is always shorter than original */
    713   char *string = orig;
    714   char *ptr = string;
    715 
    716   while (*ptr)
    717   {
    718     if (*ptr == '+' && flag_plus_to_space)    { *string++ = ' '; ptr++; }
    719     else if (*ptr != '%') *string++ = *ptr++;
    720     else  {
    721       unsigned int value1, value2;
    722 
    723       ptr++;
    724       if(sscanf(ptr, "%1X", &value1) != 1 ||
    725                 sscanf(ptr+1, "%1X", &value2) != 1) {
    726     if(!flag_plus_to_space)
    727         return NULL;
    728     *string++ = '%';
    729       } else {
    730     value1 = value1 * 16 + value2;
    731     if(value1 == '/' || value1 == 0)
    732         return orig+1;
    733     *string++ = value1;
    734     ptr += 2;
    735       }
    736     }
    737   }
    738   *string = '\0';
    739   return orig;
    740 }
    741 
    742 
    743 #ifdef CONFIG_FEATURE_HTTPD_CGI
    744 /****************************************************************************
    745  *
    746  > $Function: addEnv()
    747  *
    748  * $Description: Add an environment variable setting to the global list.
    749  *    A NAME=VALUE string is allocated, filled, and added to the list of
    750  *    environment settings passed to the cgi execution script.
    751  *
    752  * $Parameters:
    753  *  (char *) name_before_underline - The first part environment variable name.
    754  *  (char *) name_after_underline  - The second part environment variable name.
    755  *  (char *) value  . . The value to which the env variable is set.
    756  *
    757  * $Return: (void)
    758  *
    759  * $Errors: Silently returns if the env runs out of space to hold the new item
    760  *
    761  ****************************************************************************/
    762 static void addEnv(const char *name_before_underline,
    763             const char *name_after_underline, const char *value)
    764 {
    765   char *s = NULL;
    766   const char *underline;
    767 
    768   if (!value)
    769     value = "";
    770   underline = *name_after_underline ? "_" : "";
    771   asprintf(&s, "%s%s%s=%s", name_before_underline, underline,
    772                     name_after_underline, value);
    773   if(s) {
    774     putenv(s);
    775   }
    776 }
    777 
    778 #if defined(CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV) || defined(CONFIG_FEATURE_HTTPD_WITHOUT_INETD)
    779 /* set environs SERVER_PORT and REMOTE_PORT */
    780 static void addEnvPort(const char *port_name)
    781 {
    782       char buf[16];
    783 
    784       sprintf(buf, "%u", config->port);
    785       addEnv(port_name, "PORT", buf);
    786 }
    787 #endif
    788 #endif          /* CONFIG_FEATURE_HTTPD_CGI */
    789 
    790 
    791 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
    792 /****************************************************************************
    793  *
    794  > $Function: decodeBase64()
    795  *
    796  > $Description: Decode a base 64 data stream as per rfc1521.
    797  *    Note that the rfc states that none base64 chars are to be ignored.
    798  *    Since the decode always results in a shorter size than the input, it is
    799  *    OK to pass the input arg as an output arg.
    800  *
    801  * $Parameter:
    802  *      (char *) Data . . . . A pointer to a base64 encoded string.
    803  *                            Where to place the decoded data.
    804  *
    805  * $Return: void
    806  *
    807  * $Errors: None
    808  *
    809  ****************************************************************************/
    810 static void decodeBase64(char *Data)
    811 {
    812 
    813   const unsigned char *in = (const unsigned char *)Data;
    814   // The decoded size will be at most 3/4 the size of the encoded
    815   unsigned long ch = 0;
    816   int i = 0;
    817 
    818   while (*in) {
    819     int t = *in++;
    820 
    821     if(t >= '0' && t <= '9')
    822     t = t - '0' + 52;
    823     else if(t >= 'A' && t <= 'Z')
    824     t = t - 'A';
    825     else if(t >= 'a' && t <= 'z')
    826     t = t - 'a' + 26;
    827     else if(t == '+')
    828     t = 62;
    829     else if(t == '/')
    830     t = 63;
    831     else if(t == '=')
    832     t = 0;
    833     else
    834     continue;
    835 
    836     ch = (ch << 6) | t;
    837     i++;
    838     if (i == 4) {
    839     *Data++ = (char) (ch >> 16);
    840     *Data++ = (char) (ch >> 8);
    841     *Data++ = (char) ch;
    842     i = 0;
    843     }
    844   }
    845   *Data = 0;
    846 }
    847 #endif
    848 
    849 
    850 #ifdef CONFIG_FEATURE_HTTPD_WITHOUT_INETD
    851 /****************************************************************************
    852  *
    853  > $Function: openServer()
    854  *
    855  * $Description: create a listen server socket on the designated port.
    856  *
    857  * $Return: (int)  . . . A connection socket. -1 for errors.
    858  *
    859  * $Errors: None
    860  *
    861  ****************************************************************************/
    862 static int openServer(void)
    863 {
    864   struct sockaddr_in lsocket;
    865   int fd;
    866   int on = 1;
    867 
    868   /* create the socket right now */
    869   /* inet_addr() returns a value that is already in network order */
    870   memset(&lsocket, 0, sizeof(lsocket));
    871   lsocket.sin_family = AF_INET;
    872   lsocket.sin_addr.s_addr = INADDR_ANY;
    873   lsocket.sin_port = htons(config->port);
    874   fd = bb_xsocket(AF_INET, SOCK_STREAM, 0);
    875   /* tell the OS it's OK to reuse a previous address even though */
    876   /* it may still be in a close down state.  Allows bind to succeed. */
    877 #ifdef SO_REUSEPORT
    878   setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (void *)&on, sizeof(on)) ;
    879 #else
    880   setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)) ;
    881 #endif
    882   bb_xbind(fd, (struct sockaddr *)&lsocket, sizeof(lsocket));
    883   listen(fd, 9); /* bb_xlisten? */
    884   signal(SIGCHLD, SIG_IGN);   /* prevent zombie (defunct) processes */
    885   return fd;
    886 }
    887 #endif  /* CONFIG_FEATURE_HTTPD_WITHOUT_INETD */
    888 
    889 /****************************************************************************
    890  *
    891  > $Function: sendHeaders()
    892  *
    893  * $Description: Create and send HTTP response headers.
    894  *   The arguments are combined and sent as one write operation.  Note that
    895  *   IE will puke big-time if the headers are not sent in one packet and the
    896  *   second packet is delayed for any reason.
    897  *
    898  * $Parameter:
    899  *      (HttpResponseNum) responseNum . . . The result code to send.
    900  *
    901  * $Return: (int)  . . . . writing errors
    902  *
    903  ****************************************************************************/
    904 static int sendHeaders(HttpResponseNum responseNum)
    905 {
    906   char *buf = config->buf;
    907   const char *responseString = "";
    908   const char *infoString = 0;
    909   const char *mime_type;
    910   unsigned int i;
    911   time_t timer = time(0);
    912   char timeStr[80];
    913   int len;
    914 
    915   for (i = 0;
    916     i < (sizeof(httpResponseNames)/sizeof(httpResponseNames[0])); i++) {
    917         if (httpResponseNames[i].type == responseNum) {
    918             responseString = httpResponseNames[i].name;
    919             infoString = httpResponseNames[i].info;
    920             break;
    921         }
    922   }
    923   /* error message is HTML */
    924   mime_type = responseNum == HTTP_OK ?
    925         config->httpd_found.found_mime_type : "text/html";
    926 
    927   /* emit the current date */
    928   strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&timer));
    929   len = sprintf(buf,
    930     "HTTP/1.0 %d %s\r\nContent-type: %s\r\n"
    931     "Date: %s\r\nConnection: close\r\n",
    932       responseNum, responseString, mime_type, timeStr);
    933 
    934 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
    935   if (responseNum == HTTP_UNAUTHORIZED) {
    936     len += sprintf(buf+len, "WWW-Authenticate: Basic realm=\"%s\"\r\n",
    937                                 config->realm);
    938   }
    939 #endif
    940   if(responseNum == HTTP_MOVED_TEMPORARILY) {
    941     len += sprintf(buf+len, "Location: %s/%s%s\r\n",
    942         config->httpd_found.found_moved_temporarily,
    943         (config->query ? "?" : ""),
    944         (config->query ? config->query : ""));
    945   }
    946 
    947   if (config->ContentLength != -1) {    /* file */
    948     strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&config->last_mod));
    949     len += sprintf(buf+len, "Last-Modified: %s\r\n%s " cont_l_fmt "\r\n",
    950                   timeStr, Content_length, cont_l_type config->ContentLength);
    951   }
    952   strcat(buf, "\r\n");
    953   len += 2;
    954   if (infoString) {
    955     len += sprintf(buf+len,
    956         "<HEAD><TITLE>%d %s</TITLE></HEAD>\n"
    957         "<BODY><H1>%d %s</H1>\n%s\n</BODY>\n",
    958         responseNum, responseString,
    959         responseNum, responseString, infoString);
    960   }
    961 #if DEBUG
    962   fprintf(stderr, "Headers: '%s'", buf);
    963 #endif
    964   return bb_full_write(a_c_w, buf, len);
    965 }
    966 
    967 /****************************************************************************
    968  *
    969  > $Function: getLine()
    970  *
    971  * $Description: Read from the socket until an end of line char found.
    972  *
    973  *   Characters are read one at a time until an eol sequence is found.
    974  *
    975  * $Return: (int) . . . . number of characters read.  -1 if error.
    976  *
    977  ****************************************************************************/
    978 static int getLine(void)
    979 {
    980   int  count = 0;
    981   char *buf = config->buf;
    982 
    983   while (read(a_c_r, buf + count, 1) == 1) {
    984     if (buf[count] == '\r') continue;
    985     if (buf[count] == '\n') {
    986       buf[count] = 0;
    987       return count;
    988     }
    989     if(count < (MAX_MEMORY_BUFF-1))      /* check owerflow */
    990     count++;
    991   }
    992   if (count) return count;
    993   else return -1;
    994 }
    995 
    996 #ifdef CONFIG_FEATURE_HTTPD_CGI
    997 /****************************************************************************
    998  *
    999  > $Function: sendCgi()
    1000  *
    1001  * $Description: Execute a CGI script and send it's stdout back
    1002  *
    1003  *   Environment variables are set up and the script is invoked with pipes
    1004  *   for stdin/stdout.  If a post is being done the script is fed the POST
    1005  *   data in addition to setting the QUERY_STRING variable (for GETs or POSTs).
    1006  *
    1007  * $Parameters:
    1008  *      (const char *) url . . . . . . The requested URL (with leading /).
    1009  *      (int bodyLen)  . . . . . . . . Length of the post body.
    1010  *      (const char *cookie) . . . . . For set HTTP_COOKIE.
    1011  *      (const char *content_type) . . For set CONTENT_TYPE.
    1012 
    1013  *
    1014  * $Return: (char *)  . . . . A pointer to the decoded string (same as input).
    1015  *
    1016  * $Errors: None
    1017  *
    1018  ****************************************************************************/
    1019 static int sendCgi(const char *url,
    1020            const char *request, int bodyLen, const char *cookie,
    1021            const char *content_type)
    1022 {
    1023   int fromCgi[2];  /* pipe for reading data from CGI */
    1024   int toCgi[2];    /* pipe for sending data to CGI */
    1025 
    1026   static char * argp[] = { 0, 0 };
    1027   int pid = 0;
    1028   int inFd;
    1029   int outFd;
    1030   int firstLine = 1;
    1031 
    1032   do {
    1033     if (pipe(fromCgi) != 0) {
    1034       break;
    1035     }
    1036     if (pipe(toCgi) != 0) {
    1037       break;
    1038     }
    1039 
    1040     pid = fork();
    1041     if (pid < 0) {
    1042     pid = 0;
    1043     break;
    1044     }
    1045 
    1046     if (!pid) {
    1047       /* child process */
    1048       char *script;
    1049       char *purl = strdup( url );
    1050       char realpath_buff[MAXPATHLEN];
    1051 
    1052       if(purl == NULL)
    1053     _exit(242);
    1054 
    1055       inFd  = toCgi[0];
    1056       outFd = fromCgi[1];
    1057 
    1058       dup2(inFd, 0);  // replace stdin with the pipe
    1059       dup2(outFd, 1);  // replace stdout with the pipe
    1060       if(!DEBUG)
    1061     dup2(outFd, 2);  // replace stderr with the pipe
    1062 
    1063       close(toCgi[0]);
    1064       close(toCgi[1]);
    1065       close(fromCgi[0]);
    1066       close(fromCgi[1]);
    1067 
    1068       /*
    1069        * Find PATH_INFO.
    1070        */
    1071       script = purl;
    1072       while((script = strchr( script + 1, '/' )) != NULL) {
    1073     /* have script.cgi/PATH_INFO or dirs/script.cgi[/PATH_INFO] */
    1074     struct stat sb;
    1075 
    1076     *script = '\0';
    1077     if(is_directory(purl + 1, 1, &sb) == 0) {
    1078         /* not directory, found script.cgi/PATH_INFO */
    1079         *script = '/';
    1080         break;
    1081     }
    1082     *script = '/';          /* is directory, find next '/' */
    1083       }
    1084       addEnv("PATH", "INFO", script);   /* set /PATH_INFO or NULL */
    1085       addEnv("PATH",           "",         getenv("PATH"));
    1086       addEnv("REQUEST",        "METHOD",   request);
    1087       if(config->query) {
    1088     char *uri = alloca(strlen(purl) + 2 + strlen(config->query));
    1089     if(uri)
    1090         sprintf(uri, "%s?%s", purl, config->query);
    1091     addEnv("REQUEST",        "URI",   uri);
    1092       } else {
    1093     addEnv("REQUEST",        "URI",   purl);
    1094       }
    1095       if(script != NULL)
    1096     *script = '\0';         /* reduce /PATH_INFO */
    1097        /* SCRIPT_FILENAME required by PHP in CGI mode */
    1098        if(realpath(purl + 1, realpath_buff))
    1099      addEnv("SCRIPT", "FILENAME", realpath_buff);
    1100        else
    1101      *realpath_buff = 0;
    1102       /* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */
    1103       addEnv("SCRIPT_NAME",    "",         purl);
    1104       addEnv("QUERY_STRING",   "",         config->query);
    1105       addEnv("SERVER",         "SOFTWARE", httpdVersion);
    1106       addEnv("SERVER",         "PROTOCOL", "HTTP/1.0");
    1107       addEnv("GATEWAY_INTERFACE", "",      "CGI/1.1");
    1108       addEnv("REMOTE",         "ADDR",     config->rmt_ip_str);
    1109 #ifdef CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
    1110       addEnvPort("REMOTE");
    1111 #endif
    1112       if(bodyLen) {
    1113     char sbl[32];
    1114 
    1115     sprintf(sbl, "%d", bodyLen);
    1116     addEnv("CONTENT", "LENGTH", sbl);
    1117       }
    1118       if(cookie)
    1119     addEnv("HTTP", "COOKIE", cookie);
    1120       if(content_type)
    1121     addEnv("CONTENT", "TYPE", content_type);
    1122 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
    1123       if(config->remoteuser) {
    1124     addEnv("REMOTE", "USER", config->remoteuser);
    1125     addEnv("AUTH_TYPE", "", "Basic");
    1126       }
    1127 #endif
    1128       if(config->referer)
    1129     addEnv("HTTP", "REFERER", config->referer);
    1130 
    1131     /* set execve argp[0] without path */
    1132       argp[0] = strrchr( purl, '/' ) + 1;
    1133     /* but script argp[0] must have absolute path and chdiring to this */
    1134       if(*realpath_buff) {
    1135         script = strrchr(realpath_buff, '/');
    1136         if(script) {
    1137         *script = '\0';
    1138         if(chdir(realpath_buff) == 0) {
    1139           // now run the program.  If it fails,
    1140           // use _exit() so no destructors
    1141           // get called and make a mess.
    1142 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
    1143           char *interpr = NULL;
    1144           char *suffix = strrchr(purl, '.');
    1145 
    1146           if(suffix) {
    1147             Htaccess * cur;
    1148             for (cur = config->script_i; cur; cur = cur->next)
    1149                 if(strcmp(cur->before_colon + 1, suffix) == 0) {
    1150                     interpr = cur->after_colon;
     554            }
     555
     556            /* then error page; find matching status */
     557            for (i = 0; i < ARRAY_SIZE(http_response_type); i++) {
     558                if (http_response_type[i] == status) {
     559                    http_error_page[i] = concat_path_file((*c == '/') ? NULL : home_httpd, c);
    1151560                    break;
    1152561                }
    1153           }
    1154 #endif
    1155           *script = '/';
    1156 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
    1157           if (interpr)
    1158             execv(interpr, argp);
    1159           else
    1160 #endif
    1161             execv(realpath_buff, argp);
    1162         }
    1163         }
    1164       }
    1165 #ifdef CONFIG_FEATURE_HTTPD_WITHOUT_INETD
    1166       config->accepted_socket = 1;      /* send to stdout */
    1167 #endif
    1168       sendHeaders(HTTP_NOT_FOUND);
    1169       _exit(242);
    1170     } /* end child */
    1171 
    1172   } while (0);
    1173 
    1174   if (pid) {
    1175     /* parent process */
    1176     int status;
    1177     size_t post_readed_size = 0, post_readed_idx = 0;
    1178 
    1179     inFd  = fromCgi[0];
    1180     outFd = toCgi[1];
    1181     close(fromCgi[1]);
    1182     close(toCgi[0]);
    1183     signal(SIGPIPE, SIG_IGN);
    1184 
    1185     while (1) {
    1186       fd_set readSet;
    1187       fd_set writeSet;
    1188       char wbuf[128];
    1189       int nfound;
    1190       int count;
    1191 
    1192       FD_ZERO(&readSet);
    1193       FD_ZERO(&writeSet);
    1194       FD_SET(inFd, &readSet);
    1195       if(bodyLen > 0 || post_readed_size > 0) {
    1196     FD_SET(outFd, &writeSet);
    1197     nfound = outFd > inFd ? outFd : inFd;
    1198     if(post_readed_size == 0) {
    1199         FD_SET(a_c_r, &readSet);
    1200         if(nfound < a_c_r)
    1201             nfound = a_c_r;
    1202     }
    1203       /* Now wait on the set of sockets! */
    1204     nfound = select(nfound + 1, &readSet, &writeSet, 0, NULL);
    1205       } else {
    1206     if(!bodyLen) {
    1207         close(outFd);
    1208         bodyLen = -1;
    1209     }
    1210     nfound = select(inFd + 1, &readSet, 0, 0, NULL);
    1211       }
    1212 
    1213       if (nfound <= 0) {
    1214     if (waitpid(pid, &status, WNOHANG) > 0) {
    1215       close(inFd);
     562            }
     563            continue;
     564        }
     565#endif
     566
     567#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
     568        if (*p0 == '/') {
     569            /* make full path from httpd root / current_path / config_line_path */
     570            cf = (flag == SUBDIR_PARSE ? path : "");
     571            p0 = xmalloc(strlen(cf) + (c - buf) + 2 + strlen(c));
     572            c[-1] = '\0';
     573            sprintf(p0, "/%s%s", cf, buf);
     574
     575            /* another call bb_simplify_path */
     576            cf = p = p0;
     577
     578            do {
     579                if (*p == '/') {
     580                    if (*cf == '/') {    /* skip duplicate (or initial) slash */
     581                        continue;
     582                    } else if (*cf == '.') {
     583                        if (cf[1] == '/' || cf[1] == '\0') { /* remove extra '.' */
     584                            continue;
     585                        } else if ((cf[1] == '.') && (cf[2] == '/' || cf[2] == '\0')) {
     586                            ++cf;
     587                            if (p > p0) {
     588                                while (*--p != '/') /* omit previous dir */;
     589                            }
     590                            continue;
     591                        }
     592                    }
     593                }
     594                *++p = *cf;
     595            } while (*++cf);
     596
     597            if ((p == p0) || (*p != '/')) {      /* not a trailing slash */
     598                ++p;                             /* so keep last character */
     599            }
     600            *p = '\0';
     601            sprintf(p0 + strlen(p0), ":%s", c);
     602        }
     603#endif
     604
     605#if ENABLE_FEATURE_HTTPD_BASIC_AUTH \
     606 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \
     607 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
     608        /* storing current config line */
     609        cur = xzalloc(sizeof(Htaccess) + strlen(p0));
     610        if (cur) {
     611            cf = strcpy(cur->before_colon, p0);
     612            c = strchr(cf, ':');
     613            *c++ = 0;
     614            cur->after_colon = c;
     615#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
     616            if (*cf == '.') {
     617                /* config .mime line move top for overwrite previous */
     618                cur->next = mime_a;
     619                mime_a = cur;
     620                continue;
     621            }
     622#endif
     623#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
     624            if (*cf == '*' && cf[1] == '.') {
     625                /* config script interpreter line move top for overwrite previous */
     626                cur->next = script_i;
     627                script_i = cur;
     628                continue;
     629            }
     630#endif
     631#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
     632            free(p0);
     633            if (prev == NULL) {
     634                /* first line */
     635                g_auth = prev = cur;
     636            } else {
     637                /* sort path, if current lenght eq or bigger then move up */
     638                Htaccess *prev_hti = g_auth;
     639                size_t l = strlen(cf);
     640                Htaccess *hti;
     641
     642                for (hti = prev_hti; hti; hti = hti->next) {
     643                    if (l >= strlen(hti->before_colon)) {
     644                        /* insert before hti */
     645                        cur->next = hti;
     646                        if (prev_hti != hti) {
     647                            prev_hti->next = cur;
     648                        } else {
     649                            /* insert as top */
     650                            g_auth = cur;
     651                        }
     652                        break;
     653                    }
     654                    if (prev_hti != hti)
     655                        prev_hti = prev_hti->next;
     656                }
     657                if (!hti) {       /* not inserted, add to bottom */
     658                    prev->next = cur;
     659                    prev = cur;
     660                }
     661            }
     662#endif
     663        }
     664#endif
     665     }
     666     fclose(f);
     667}
     668
     669#if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
     670/*
     671 * Given a string, html-encode special characters.
     672 * This is used for the -e command line option to provide an easy way
     673 * for scripts to encode result data without confusing browsers.  The
     674 * returned string pointer is memory allocated by malloc().
     675 *
     676 * Returns a pointer to the encoded string (malloced).
     677 */
     678static char *encodeString(const char *string)
     679{
     680    /* take the simple route and encode everything */
     681    /* could possibly scan once to get length.     */
     682    int len = strlen(string);
     683    char *out = xmalloc(len * 6 + 1);
     684    char *p = out;
     685    char ch;
     686
     687    while ((ch = *string++)) {
     688        /* very simple check for what to encode */
     689        if (isalnum(ch))
     690            *p++ = ch;
     691        else
     692            p += sprintf(p, "&#%d;", (unsigned char) ch);
     693    }
     694    *p = '\0';
     695    return out;
     696}
     697#endif          /* FEATURE_HTTPD_ENCODE_URL_STR */
     698
     699/*
     700 * Given a URL encoded string, convert it to plain ascii.
     701 * Since decoding always makes strings smaller, the decode is done in-place.
     702 * Thus, callers should strdup() the argument if they do not want the
     703 * argument modified.  The return is the original pointer, allowing this
     704 * function to be easily used as arguments to other functions.
     705 *
     706 * string    The first string to decode.
     707 * option_d  1 if called for httpd -d
     708 *
     709 * Returns a pointer to the decoded string (same as input).
     710 */
     711static unsigned hex_to_bin(unsigned char c)
     712{
     713    unsigned v;
     714
     715    v = c - '0';
     716    if (v <= 9)
     717        return v;
     718    /* c | 0x20: letters to lower case, non-letters
     719     * to (potentially different) non-letters */
     720    v = (unsigned)(c | 0x20) - 'a';
     721    if (v <= 5)
     722        return v + 10;
     723    return ~0;
     724}
     725/* For testing:
     726void t(char c) { printf("'%c'(%u) %u\n", c, c, hex_to_bin(c)); }
     727int main() { t(0x10); t(0x20); t('0'); t('9'); t('A'); t('F'); t('a'); t('f');
     728t('0'-1); t('9'+1); t('A'-1); t('F'+1); t('a'-1); t('f'+1); return 0; }
     729*/
     730static char *decodeString(char *orig, int option_d)
     731{
     732    /* note that decoded string is always shorter than original */
     733    char *string = orig;
     734    char *ptr = string;
     735    char c;
     736
     737    while ((c = *ptr++) != '\0') {
     738        unsigned v;
     739
     740        if (option_d && c == '+') {
     741            *string++ = ' ';
     742            continue;
     743        }
     744        if (c != '%') {
     745            *string++ = c;
     746            continue;
     747        }
     748        v = hex_to_bin(ptr[0]);
     749        if (v > 15) {
     750 bad_hex:
     751            if (!option_d)
     752                return NULL;
     753            *string++ = '%';
     754            continue;
     755        }
     756        v = (v * 16) | hex_to_bin(ptr[1]);
     757        if (v > 255)
     758            goto bad_hex;
     759        if (!option_d && (v == '/' || v == '\0')) {
     760            /* caller takes it as indication of invalid
     761             * (dangerous wrt exploits) chars */
     762            return orig + 1;
     763        }
     764        *string++ = v;
     765        ptr += 2;
     766    }
     767    *string = '\0';
     768    return orig;
     769}
     770
     771#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
     772/*
     773 * Decode a base64 data stream as per rfc1521.
     774 * Note that the rfc states that non base64 chars are to be ignored.
     775 * Since the decode always results in a shorter size than the input,
     776 * it is OK to pass the input arg as an output arg.
     777 * Parameter: a pointer to a base64 encoded string.
     778 * Decoded data is stored in-place.
     779 */
     780static void decodeBase64(char *Data)
     781{
     782    const unsigned char *in = (const unsigned char *)Data;
     783    /* The decoded size will be at most 3/4 the size of the encoded */
     784    unsigned ch = 0;
     785    int i = 0;
     786
     787    while (*in) {
     788        int t = *in++;
     789
     790        if (t >= '0' && t <= '9')
     791            t = t - '0' + 52;
     792        else if (t >= 'A' && t <= 'Z')
     793            t = t - 'A';
     794        else if (t >= 'a' && t <= 'z')
     795            t = t - 'a' + 26;
     796        else if (t == '+')
     797            t = 62;
     798        else if (t == '/')
     799            t = 63;
     800        else if (t == '=')
     801            t = 0;
     802        else
     803            continue;
     804
     805        ch = (ch << 6) | t;
     806        i++;
     807        if (i == 4) {
     808            *Data++ = (char) (ch >> 16);
     809            *Data++ = (char) (ch >> 8);
     810            *Data++ = (char) ch;
     811            i = 0;
     812        }
     813    }
     814    *Data = '\0';
     815}
     816#endif
     817
     818/*
     819 * Create a listen server socket on the designated port.
     820 */
     821static int openServer(void)
     822{
     823    int n = bb_strtou(bind_addr_or_port, NULL, 10);
     824    if (!errno && n && n <= 0xffff)
     825        n = create_and_bind_stream_or_die(NULL, n);
     826    else
     827        n = create_and_bind_stream_or_die(bind_addr_or_port, 80);
     828    xlisten(n, 9);
     829    return n;
     830}
     831
     832/*
     833 * Log the connection closure and exit.
     834 */
     835static void log_and_exit(void) ATTRIBUTE_NORETURN;
     836static void log_and_exit(void)
     837{
     838    /* Paranoia. IE said to be buggy. It may send some extra data
     839     * or be confused by us just exiting without SHUT_WR. Oh well. */
     840    shutdown(1, SHUT_WR);
     841    ndelay_on(0);
     842    while (read(0, iobuf, IOBUF_SIZE) > 0)
     843        continue;
     844
     845    if (verbose > 2)
     846        bb_error_msg("closed");
     847    _exit(xfunc_error_retval);
     848}
     849
     850/*
     851 * Create and send HTTP response headers.
     852 * The arguments are combined and sent as one write operation.  Note that
     853 * IE will puke big-time if the headers are not sent in one packet and the
     854 * second packet is delayed for any reason.
     855 * responseNum - the result code to send.
     856 */
     857static void send_headers(int responseNum)
     858{
     859    static const char RFC1123FMT[] ALIGN1 = "%a, %d %b %Y %H:%M:%S GMT";
     860
     861    const char *responseString = "";
     862    const char *infoString = NULL;
     863    const char *mime_type;
     864#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
     865    const char *error_page = 0;
     866#endif
     867    unsigned i;
     868    time_t timer = time(0);
     869    char tmp_str[80];
     870    int len;
     871
     872    for (i = 0; i < ARRAY_SIZE(http_response_type); i++) {
     873        if (http_response_type[i] == responseNum) {
     874            responseString = http_response[i].name;
     875            infoString = http_response[i].info;
     876#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
     877            error_page = http_error_page[i];
     878#endif
     879            break;
     880        }
     881    }
     882    /* error message is HTML */
     883    mime_type = responseNum == HTTP_OK ?
     884                found_mime_type : "text/html";
     885
     886    if (verbose)
     887        bb_error_msg("response:%u", responseNum);
     888
     889    /* emit the current date */
     890    strftime(tmp_str, sizeof(tmp_str), RFC1123FMT, gmtime(&timer));
     891    len = sprintf(iobuf,
     892            "HTTP/1.0 %d %s\r\nContent-type: %s\r\n"
     893            "Date: %s\r\nConnection: close\r\n",
     894            responseNum, responseString, mime_type, tmp_str);
     895
     896#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
     897    if (responseNum == HTTP_UNAUTHORIZED) {
     898        len += sprintf(iobuf + len,
     899                "WWW-Authenticate: Basic realm=\"%s\"\r\n",
     900                g_realm);
     901    }
     902#endif
     903    if (responseNum == HTTP_MOVED_TEMPORARILY) {
     904        len += sprintf(iobuf + len, "Location: %s/%s%s\r\n",
     905                found_moved_temporarily,
     906                (g_query ? "?" : ""),
     907                (g_query ? g_query : ""));
     908    }
     909
     910#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
     911    if (error_page && !access(error_page, R_OK)) {
     912        strcat(iobuf, "\r\n");
     913        len += 2;
     914
     915        if (DEBUG)
     916            fprintf(stderr, "headers: '%s'\n", iobuf);
     917        full_write(1, iobuf, len);
     918        if (DEBUG)
     919            fprintf(stderr, "writing error page: '%s'\n", error_page);
     920        return send_file_and_exit(error_page, FALSE);
     921    }
     922#endif
     923
     924    if (ContentLength != -1) {    /* file */
     925        strftime(tmp_str, sizeof(tmp_str), RFC1123FMT, gmtime(&last_mod));
     926        len += sprintf(iobuf + len, "Last-Modified: %s\r\n%s %"OFF_FMT"d\r\n",
     927            tmp_str, "Content-length:", ContentLength);
     928    }
     929    iobuf[len++] = '\r';
     930    iobuf[len++] = '\n';
     931    if (infoString) {
     932        len += sprintf(iobuf + len,
     933                "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>\n"
     934                "<BODY><H1>%d %s</H1>\n%s\n</BODY></HTML>\n",
     935                responseNum, responseString,
     936                responseNum, responseString, infoString);
     937    }
     938    if (DEBUG)
     939        fprintf(stderr, "headers: '%s'\n", iobuf);
     940    if (full_write(1, iobuf, len) != len) {
     941        if (verbose > 1)
     942            bb_perror_msg("error");
     943        log_and_exit();
     944    }
     945}
     946
     947static void send_headers_and_exit(int responseNum) ATTRIBUTE_NORETURN;
     948static void send_headers_and_exit(int responseNum)
     949{
     950    send_headers(responseNum);
     951    log_and_exit();
     952}
     953
     954/*
     955 * Read from the socket until '\n' or EOF. '\r' chars are removed.
     956 * '\n' is replaced with NUL.
     957 * Return number of characters read or 0 if nothing is read
     958 * ('\r' and '\n' are not counted).
     959 * Data is returned in iobuf.
     960 */
     961static int get_line(void)
     962{
     963    int count = 0;
     964    char c;
     965
     966    while (1) {
     967        if (hdr_cnt <= 0) {
     968            hdr_cnt = safe_read(0, hdr_buf, sizeof(hdr_buf));
     969            if (hdr_cnt <= 0)
     970                break;
     971            hdr_ptr = hdr_buf;
     972        }
     973        iobuf[count] = c = *hdr_ptr++;
     974        hdr_cnt--;
     975
     976        if (c == '\r')
     977            continue;
     978        if (c == '\n') {
     979            iobuf[count] = '\0';
     980            return count;
     981        }
     982        if (count < (IOBUF_SIZE - 1))      /* check overflow */
     983            count++;
     984    }
     985    return count;
     986}
     987
     988#if ENABLE_FEATURE_HTTPD_CGI
     989static void setenv1(const char *name, const char *value)
     990{
     991    setenv(name, value ? value : "", 1);
     992}
     993
     994/*
     995 * Spawn CGI script, forward CGI's stdin/out <=> network
     996 *
     997 * Environment variables are set up and the script is invoked with pipes
     998 * for stdin/stdout.  If a post is being done the script is fed the POST
     999 * data in addition to setting the QUERY_STRING variable (for GETs or POSTs).
     1000 *
     1001 * Parameters:
     1002 * const char *url              The requested URL (with leading /).
     1003 * int bodyLen                  Length of the post body.
     1004 * const char *cookie           For set HTTP_COOKIE.
     1005 * const char *content_type     For set CONTENT_TYPE.
     1006 */
     1007static void send_cgi_and_exit(
     1008        const char *url,
     1009        const char *request,
     1010        int bodyLen,
     1011        const char *cookie,
     1012        const char *content_type) ATTRIBUTE_NORETURN;
     1013static void send_cgi_and_exit(
     1014        const char *url,
     1015        const char *request,
     1016        int bodyLen,
     1017        const char *cookie,
     1018        const char *content_type)
     1019{
     1020    struct { int rd; int wr; } fromCgi;  /* CGI -> httpd pipe */
     1021    struct { int rd; int wr; } toCgi;    /* httpd -> CGI pipe */
     1022    char *fullpath;
     1023    char *script;
     1024    char *purl;
     1025    int buf_count;
     1026    int status;
     1027    int pid = 0;
     1028
     1029    /*
     1030     * We are mucking with environment _first_ and then vfork/exec,
     1031     * this allows us to use vfork safely. Parent don't care about
     1032     * these environment changes anyway.
     1033     */
     1034
     1035    /*
     1036     * Find PATH_INFO.
     1037     */
     1038    purl = xstrdup(url);
     1039    script = purl;
     1040    while ((script = strchr(script + 1, '/')) != NULL) {
     1041        /* have script.cgi/PATH_INFO or dirs/script.cgi[/PATH_INFO] */
     1042        struct stat sb;
     1043
     1044        *script = '\0';
     1045        if (!is_directory(purl + 1, 1, &sb)) {
     1046            /* not directory, found script.cgi/PATH_INFO */
     1047            *script = '/';
     1048            break;
     1049        }
     1050        *script = '/';          /* is directory, find next '/' */
     1051    }
     1052    setenv1("PATH_INFO", script);   /* set /PATH_INFO or "" */
     1053    setenv1("REQUEST_METHOD", request);
     1054    if (g_query) {
     1055        putenv(xasprintf("%s=%s?%s", "REQUEST_URI", purl, g_query));
     1056    } else {
     1057        setenv1("REQUEST_URI", purl);
     1058    }
     1059    if (script != NULL)
     1060        *script = '\0';         /* cut off /PATH_INFO */
     1061
     1062    /* SCRIPT_FILENAME required by PHP in CGI mode */
     1063    fullpath = concat_path_file(home_httpd, purl);
     1064    setenv1("SCRIPT_FILENAME", fullpath);
     1065    /* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */
     1066    setenv1("SCRIPT_NAME", purl);
     1067    /* http://hoohoo.ncsa.uiuc.edu/cgi/env.html:
     1068     * QUERY_STRING: The information which follows the ? in the URL
     1069     * which referenced this script. This is the query information.
     1070     * It should not be decoded in any fashion. This variable
     1071     * should always be set when there is query information,
     1072     * regardless of command line decoding. */
     1073    /* (Older versions of bbox seem to do some decoding) */
     1074    setenv1("QUERY_STRING", g_query);
     1075    putenv((char*)"SERVER_SOFTWARE=busybox httpd/"BB_VER);
     1076    putenv((char*)"SERVER_PROTOCOL=HTTP/1.0");
     1077    putenv((char*)"GATEWAY_INTERFACE=CGI/1.1");
     1078    /* Having _separate_ variables for IP and port defeats
     1079     * the purpose of having socket abstraction. Which "port"
     1080     * are you using on Unix domain socket?
     1081     * IOW - REMOTE_PEER="1.2.3.4:56" makes much more sense.
     1082     * Oh well... */
     1083    {
     1084        char *p = rmt_ip_str ? rmt_ip_str : (char*)"";
     1085        char *cp = strrchr(p, ':');
     1086        if (ENABLE_FEATURE_IPV6 && cp && strchr(cp, ']'))
     1087            cp = NULL;
     1088        if (cp) *cp = '\0'; /* delete :PORT */
     1089        setenv1("REMOTE_ADDR", p);
     1090        if (cp) {
     1091            *cp = ':';
     1092#if ENABLE_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
     1093            setenv1("REMOTE_PORT", cp + 1);
     1094#endif
     1095        }
     1096    }
     1097    setenv1("HTTP_USER_AGENT", user_agent);
     1098    if (bodyLen)
     1099        putenv(xasprintf("CONTENT_LENGTH=%d", bodyLen));
     1100    if (cookie)
     1101        setenv1("HTTP_COOKIE", cookie);
     1102    if (content_type)
     1103        setenv1("CONTENT_TYPE", content_type);
     1104#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
     1105    if (remoteuser) {
     1106        setenv1("REMOTE_USER", remoteuser);
     1107        putenv((char*)"AUTH_TYPE=Basic");
     1108    }
     1109#endif
     1110    if (referer)
     1111        setenv1("HTTP_REFERER", referer);
     1112
     1113    xpipe(&fromCgi.rd);
     1114    xpipe(&toCgi.rd);
     1115
     1116    pid = vfork();
     1117    if (pid < 0) {
     1118        /* TODO: log perror? */
     1119        log_and_exit();
     1120    }
     1121
     1122    if (!pid) {
     1123        /* Child process */
     1124        xfunc_error_retval = 242;
     1125
     1126        xmove_fd(toCgi.rd, 0);  /* replace stdin with the pipe */
     1127        xmove_fd(fromCgi.wr, 1);  /* replace stdout with the pipe */
     1128        close(fromCgi.rd);
     1129        close(toCgi.wr);
     1130        /* User seeing stderr output can be a security problem.
     1131         * If CGI really wants that, it can always do dup itself. */
     1132        /* dup2(1, 2); */
     1133
     1134        /* script must have absolute path */
     1135        script = strrchr(fullpath, '/');
     1136        if (!script)
     1137            goto error_execing_cgi;
     1138        *script = '\0';
     1139        /* chdiring to script's dir */
     1140        if (chdir(fullpath) == 0) {
     1141            char *argv[2];
     1142#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
     1143            char *interpr = NULL;
     1144            char *suffix = strrchr(purl, '.');
     1145
     1146            if (suffix) {
     1147                Htaccess *cur;
     1148                for (cur = script_i; cur; cur = cur->next) {
     1149                    if (strcmp(cur->before_colon + 1, suffix) == 0) {
     1150                        interpr = cur->after_colon;
     1151                        break;
     1152                    }
     1153                }
     1154            }
     1155#endif
     1156            *script = '/';
     1157            /* set argv[0] to name without path */
     1158            argv[0] = (char*)bb_basename(purl);
     1159            argv[1] = NULL;
     1160#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
     1161            if (interpr)
     1162                execv(interpr, argv);
     1163            else
     1164#endif
     1165                execv(fullpath, argv);
     1166        }
     1167 error_execing_cgi:
     1168        /* send to stdout
     1169         * (we are CGI here, our stdout is pumped to the net) */
     1170        send_headers_and_exit(HTTP_NOT_FOUND);
     1171    } /* end child */
     1172
     1173    /* Parent process */
     1174
     1175    /* First, restore variables possibly changed by child */
     1176    xfunc_error_retval = 0;
     1177
     1178    /* Prepare for pumping data.
     1179     * iobuf is used for CGI -> network data,
     1180     * hdr_buf is for network -> CGI data (POSTDATA) */
     1181    buf_count = 0;
     1182    close(fromCgi.wr);
     1183    close(toCgi.rd);
     1184
     1185    /* If CGI dies, we still want to correctly finish reading its output
     1186     * and send it to the peer. So please no SIGPIPEs! */
     1187    signal(SIGPIPE, SIG_IGN);
     1188
     1189    /* This loop still looks messy. What is an exit criteria?
     1190     * "CGI's output closed"? Or "CGI has exited"?
     1191     * What to do if CGI has closed both input and output, but
     1192     * didn't exit? etc... */
     1193
     1194    /* NB: breaking out of this loop jumps to log_and_exit() */
     1195    while (1) {
     1196        fd_set readSet;
     1197        fd_set writeSet;
     1198        int nfound;
     1199        int count;
     1200
     1201        FD_ZERO(&readSet);
     1202        FD_ZERO(&writeSet);
     1203        FD_SET(fromCgi.rd, &readSet);
     1204        if (bodyLen > 0 || hdr_cnt > 0) {
     1205            FD_SET(toCgi.wr, &writeSet);
     1206            nfound = toCgi.wr > fromCgi.rd ? toCgi.wr : fromCgi.rd;
     1207            if (hdr_cnt <= 0)
     1208                FD_SET(0, &readSet);
     1209            /* Now wait on the set of sockets! */
     1210            nfound = select(nfound + 1, &readSet, &writeSet, NULL, NULL);
     1211        } else {
     1212            if (!bodyLen) {
     1213                close(toCgi.wr); /* no more POST data to CGI */
     1214                bodyLen = -1;
     1215            }
     1216            nfound = select(fromCgi.rd + 1, &readSet, NULL, NULL, NULL);
     1217        }
     1218
     1219        if (nfound <= 0) {
     1220            if (waitpid(pid, &status, WNOHANG) <= 0) {
     1221                /* Weird. CGI didn't exit and no fd's
     1222                 * are ready, yet select returned?! */
     1223                continue;
     1224            }
     1225            close(fromCgi.rd);
     1226            if (DEBUG && WIFEXITED(status))
     1227                bb_error_msg("CGI exited, status=%d", WEXITSTATUS(status));
     1228            if (DEBUG && WIFSIGNALED(status))
     1229                bb_error_msg("CGI killed, signal=%d", WTERMSIG(status));
     1230            break;
     1231        }
     1232
     1233        if (hdr_cnt > 0 && FD_ISSET(toCgi.wr, &writeSet)) {
     1234            /* Have data from peer and can write to CGI */
     1235            count = safe_write(toCgi.wr, hdr_ptr, hdr_cnt);
     1236            /* Doesn't happen, we dont use nonblocking IO here
     1237             *if (count < 0 && errno == EAGAIN) {
     1238             *  ...
     1239             *} else */
     1240            if (count > 0) {
     1241                hdr_ptr += count;
     1242                hdr_cnt -= count;
     1243            } else {
     1244                hdr_cnt = bodyLen = 0; /* EOF/broken pipe to CGI */
     1245            }
     1246        } else if (bodyLen > 0 && hdr_cnt == 0
     1247         && FD_ISSET(0, &readSet)
     1248        ) {
     1249            /* We expect data, prev data portion is eaten by CGI
     1250             * and there *is* data to read from the peer
     1251             * (POSTDATA?) */
     1252            count = bodyLen > (int)sizeof(hdr_buf) ? (int)sizeof(hdr_buf) : bodyLen;
     1253            count = safe_read(0, hdr_buf, count);
     1254            if (count > 0) {
     1255                hdr_cnt = count;
     1256                hdr_ptr = hdr_buf;
     1257                bodyLen -= count;
     1258            } else {
     1259                bodyLen = 0; /* closed */
     1260            }
     1261        }
     1262
     1263#define PIPESIZE PIPE_BUF
     1264#if PIPESIZE >= IOBUF_SIZE
     1265# error "PIPESIZE >= IOBUF_SIZE"
     1266#endif
     1267        if (FD_ISSET(fromCgi.rd, &readSet)) {
     1268            /* There is something to read from CGI */
     1269            char *rbuf = iobuf;
     1270
     1271            /* Are we still buffering CGI output? */
     1272            if (buf_count >= 0) {
     1273                /* HTTP_200[] has single "\r\n" at the end.
     1274                 * According to http://hoohoo.ncsa.uiuc.edu/cgi/out.html,
     1275                 * CGI scripts MUST send their own header terminated by
     1276                 * empty line, then data. That's why we have only one
     1277                 * <cr><lf> pair here. We will output "200 OK" line
     1278                 * if needed, but CGI still has to provide blank line
     1279                 * between header and body */
     1280
     1281                /* Must use safe_read, not full_read, because
     1282                 * CGI may output a few first bytes and then wait
     1283                 * for POSTDATA without closing stdout.
     1284                 * With full_read we may wait here forever. */
     1285                count = safe_read(fromCgi.rd, rbuf + buf_count, PIPESIZE - 8);
     1286                if (count <= 0) {
     1287                    /* eof (or error) and there was no "HTTP",
     1288                     * so write it, then write received data */
     1289                    if (buf_count) {
     1290                        full_write(1, HTTP_200, sizeof(HTTP_200)-1);
     1291                        full_write(1, rbuf, buf_count);
     1292                    }
     1293                    break; /* CGI stdout is closed, exiting */
     1294                }
     1295                buf_count += count;
     1296                count = 0;
     1297                /* "Status" header format is: "Status: 302 Redirected\r\n" */
     1298                if (buf_count >= 8 && memcmp(rbuf, "Status: ", 8) == 0) {
     1299                    /* send "HTTP/1.0 " */
     1300                    if (full_write(1, HTTP_200, 9) != 9)
     1301                        break;
     1302                    rbuf += 8; /* skip "Status: " */
     1303                    count = buf_count - 8;
     1304                    buf_count = -1; /* buffering off */
     1305                } else if (buf_count >= 4) {
     1306                    /* Did CGI add "HTTP"? */
     1307                    if (memcmp(rbuf, HTTP_200, 4) != 0) {
     1308                        /* there is no "HTTP", do it ourself */
     1309                        if (full_write(1, HTTP_200, sizeof(HTTP_200)-1) != sizeof(HTTP_200)-1)
     1310                            break;
     1311                    }
     1312                    /* Commented out:
     1313                    if (!strstr(rbuf, "ontent-")) {
     1314                        full_write(s, "Content-type: text/plain\r\n\r\n", 28);
     1315                    }
     1316                     * Counter-example of valid CGI without Content-type:
     1317                     * echo -en "HTTP/1.0 302 Found\r\n"
     1318                     * echo -en "Location: http://www.busybox.net\r\n"
     1319                     * echo -en "\r\n"
     1320                     */
     1321                    count = buf_count;
     1322                    buf_count = -1; /* buffering off */
     1323                }
     1324            } else {
     1325                count = safe_read(fromCgi.rd, rbuf, PIPESIZE);
     1326                if (count <= 0)
     1327                    break;  /* eof (or error) */
     1328            }
     1329            if (full_write(1, rbuf, count) != count)
     1330                break;
     1331            if (DEBUG)
     1332                fprintf(stderr, "cgi read %d bytes: '%.*s'\n", count, count, rbuf);
     1333        } /* if (FD_ISSET(fromCgi.rd)) */
     1334    } /* while (1) */
     1335    log_and_exit();
     1336}
     1337#endif          /* FEATURE_HTTPD_CGI */
     1338
     1339/*
     1340 * Send a file response to a HTTP request, and exit
     1341 *
     1342 * Parameters:
     1343 * const char *url    The requested URL (with leading /).
     1344 * headers            Don't send headers before if FALSE.
     1345 */
     1346static void send_file_and_exit(const char *url, int headers)
     1347{
     1348    static const char *const suffixTable[] = {
     1349    /* Warning: shorter equivalent suffix in one line must be first */
     1350        ".htm.html", "text/html",
     1351        ".jpg.jpeg", "image/jpeg",
     1352        ".gif",      "image/gif",
     1353        ".png",      "image/png",
     1354        ".txt.h.c.cc.cpp", "text/plain",
     1355        ".css",      "text/css",
     1356        ".wav",      "audio/wav",
     1357        ".avi",      "video/x-msvideo",
     1358        ".qt.mov",   "video/quicktime",
     1359        ".mpe.mpeg", "video/mpeg",
     1360        ".mid.midi", "audio/midi",
     1361        ".mp3",      "audio/mpeg",
     1362#if 0                        /* unpopular */
     1363        ".au",       "audio/basic",
     1364        ".pac",      "application/x-ns-proxy-autoconfig",
     1365        ".vrml.wrl", "model/vrml",
     1366#endif
     1367        NULL
     1368    };
     1369
     1370    char *suffix;
     1371    int f;
     1372    const char *const *table;
     1373    const char *try_suffix;
     1374    ssize_t count;
     1375#if ENABLE_FEATURE_HTTPD_USE_SENDFILE
     1376    off_t offset = 0;
     1377#endif
     1378
     1379    suffix = strrchr(url, '.');
     1380
     1381    /* If not found, set default as "application/octet-stream";  */
     1382    found_mime_type = "application/octet-stream";
     1383    if (suffix) {
     1384#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
     1385        Htaccess *cur;
     1386#endif
     1387        for (table = suffixTable; *table; table += 2) {
     1388            try_suffix = strstr(table[0], suffix);
     1389            if (try_suffix) {
     1390                try_suffix += strlen(suffix);
     1391                if (*try_suffix == '\0' || *try_suffix == '.') {
     1392                    found_mime_type = table[1];
     1393                    break;
     1394                }
     1395            }
     1396        }
     1397#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
     1398        for (cur = mime_a; cur; cur = cur->next) {
     1399            if (strcmp(cur->before_colon, suffix) == 0) {
     1400                found_mime_type = cur->after_colon;
     1401                break;
     1402            }
     1403        }
     1404#endif
     1405    }
     1406
     1407    if (DEBUG)
     1408        bb_error_msg("sending file '%s' content-type: %s",
     1409            url, found_mime_type);
     1410
     1411    f = open(url, O_RDONLY);
     1412    if (f < 0) {
     1413        if (DEBUG)
     1414            bb_perror_msg("cannot open '%s'", url);
     1415        if (headers)
     1416            send_headers_and_exit(HTTP_NOT_FOUND);
     1417    }
     1418
     1419    if (headers)
     1420        send_headers(HTTP_OK);
     1421
     1422    /* If you want to know about EPIPE below
     1423     * (happens if you abort downloads from local httpd): */
     1424    signal(SIGPIPE, SIG_IGN);
     1425
     1426#if ENABLE_FEATURE_HTTPD_USE_SENDFILE
     1427    do {
     1428        /* byte count (3rd arg) is rounded down to 64k */
     1429        count = sendfile(1, f, &offset, MAXINT(ssize_t) - 0xffff);
     1430        if (count < 0) {
     1431            if (offset == 0)
     1432                goto fallback;
     1433            goto fin;
     1434        }
     1435    } while (count > 0);
     1436    log_and_exit();
     1437
     1438 fallback:
     1439#endif
     1440    while ((count = safe_read(f, iobuf, IOBUF_SIZE)) > 0) {
     1441        ssize_t n = count;
     1442        count = full_write(1, iobuf, count);
     1443        if (count != n)
     1444            break;
     1445    }
     1446#if ENABLE_FEATURE_HTTPD_USE_SENDFILE
     1447 fin:
     1448#endif
     1449    if (count < 0 && verbose > 1)
     1450        bb_perror_msg("error");
     1451    log_and_exit();
     1452}
     1453
     1454static int checkPermIP(void)
     1455{
     1456    Htaccess_IP *cur;
     1457
     1458    /* This could stand some work */
     1459    for (cur = ip_a_d; cur; cur = cur->next) {
    12161460#if DEBUG
    1217       if (WIFEXITED(status))
    1218           bb_error_msg("piped has exited with status=%d", WEXITSTATUS(status));
    1219       if (WIFSIGNALED(status))
    1220           bb_error_msg("piped has exited with signal=%d", WTERMSIG(status));
    1221 #endif
    1222       break;
    1223     }
    1224       } else if(post_readed_size > 0 && FD_ISSET(outFd, &writeSet)) {
    1225         count = bb_full_write(outFd, wbuf + post_readed_idx, post_readed_size);
    1226         if(count > 0) {
    1227             post_readed_size -= count;
    1228             post_readed_idx += count;
    1229             if(post_readed_size == 0)
    1230                 post_readed_idx = 0;
    1231         } else {
    1232             post_readed_size = post_readed_idx = bodyLen = 0; /* broken pipe to CGI */
    1233         }
    1234       } else if(bodyLen > 0 && post_readed_size == 0 && FD_ISSET(a_c_r, &readSet)) {
    1235         count = bodyLen > (int)sizeof(wbuf) ? (int)sizeof(wbuf) : bodyLen;
    1236         count = safe_read(a_c_r, wbuf, count);
    1237         if(count > 0) {
    1238             post_readed_size += count;
    1239             bodyLen -= count;
    1240         } else {
    1241             bodyLen = 0;    /* closed */
    1242         }
    1243       }
    1244       if(FD_ISSET(inFd, &readSet)) {
    1245     int s = a_c_w;
    1246     char *rbuf = config->buf;
    1247 
    1248 #ifndef PIPE_BUF
    1249 # define PIPESIZE 4096          /* amount of buffering in a pipe */
     1461        fprintf(stderr,
     1462            "checkPermIP: '%s' ? '%u.%u.%u.%u/%u.%u.%u.%u'\n",
     1463            rmt_ip_str,
     1464            (unsigned char)(cur->ip >> 24),
     1465            (unsigned char)(cur->ip >> 16),
     1466            (unsigned char)(cur->ip >> 8),
     1467            (unsigned char)(cur->ip),
     1468            (unsigned char)(cur->mask >> 24),
     1469            (unsigned char)(cur->mask >> 16),
     1470            (unsigned char)(cur->mask >> 8),
     1471            (unsigned char)(cur->mask)
     1472        );
     1473#endif
     1474        if ((rmt_ip & cur->mask) == cur->ip)
     1475            return cur->allow_deny == 'A';   /* Allow/Deny */
     1476    }
     1477
     1478    /* if unconfigured, return 1 - access from all */
     1479    return !flg_deny_all;
     1480}
     1481
     1482#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
     1483/*
     1484 * Check the permission file for access password protected.
     1485 *
     1486 * If config file isn't present, everything is allowed.
     1487 * Entries are of the form you can see example from header source
     1488 *
     1489 * path      The file path.
     1490 * request   User information to validate.
     1491 *
     1492 * Returns 1 if request is OK.
     1493 */
     1494static int checkPerm(const char *path, const char *request)
     1495{
     1496    Htaccess *cur;
     1497    const char *p;
     1498    const char *p0;
     1499
     1500    const char *prev = NULL;
     1501
     1502    /* This could stand some work */
     1503    for (cur = g_auth; cur; cur = cur->next) {
     1504        size_t l;
     1505
     1506        p0 = cur->before_colon;
     1507        if (prev != NULL && strcmp(prev, p0) != 0)
     1508            continue;       /* find next identical */
     1509        p = cur->after_colon;
     1510        if (DEBUG)
     1511            fprintf(stderr, "checkPerm: '%s' ? '%s'\n", p0, request);
     1512
     1513        l = strlen(p0);
     1514        if (strncmp(p0, path, l) == 0
     1515         && (l == 1 || path[l] == '/' || path[l] == '\0')
     1516        ) {
     1517            char *u;
     1518            /* path match found.  Check request */
     1519            /* for check next /path:user:password */
     1520            prev = p0;
     1521            u = strchr(request, ':');
     1522            if (u == NULL) {
     1523                /* bad request, ':' required */
     1524                break;
     1525            }
     1526
     1527            if (ENABLE_FEATURE_HTTPD_AUTH_MD5) {
     1528                char *cipher;
     1529                char *pp;
     1530
     1531                if (strncmp(p, request, u - request) != 0) {
     1532                    /* user doesn't match */
     1533                    continue;
     1534                }
     1535                pp = strchr(p, ':');
     1536                if (pp && pp[1] == '$' && pp[2] == '1'
     1537                 && pp[3] == '$' && pp[4]
     1538                ) {
     1539                    pp++;
     1540                    cipher = pw_encrypt(u+1, pp);
     1541                    if (strcmp(cipher, pp) == 0)
     1542                        goto set_remoteuser_var;   /* Ok */
     1543                    /* unauthorized */
     1544                    continue;
     1545                }
     1546            }
     1547
     1548            if (strcmp(p, request) == 0) {
     1549 set_remoteuser_var:
     1550                remoteuser = strdup(request);
     1551                if (remoteuser)
     1552                    remoteuser[u - request] = '\0';
     1553                return 1;   /* Ok */
     1554            }
     1555            /* unauthorized */
     1556        }
     1557    } /* for */
     1558
     1559    return prev == NULL;
     1560}
     1561#endif  /* FEATURE_HTTPD_BASIC_AUTH */
     1562
     1563/*
     1564 * Handle timeouts
     1565 */
     1566static void exit_on_signal(int sig) ATTRIBUTE_NORETURN;
     1567static void exit_on_signal(int sig)
     1568{
     1569    send_headers_and_exit(HTTP_REQUEST_TIMEOUT);
     1570}
     1571
     1572/*
     1573 * Handle an incoming http request and exit.
     1574 */
     1575static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) ATTRIBUTE_NORETURN;
     1576static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
     1577{
     1578    static const char request_GET[] ALIGN1 = "GET";
     1579
     1580    struct stat sb;
     1581    char *urlcopy;
     1582    char *urlp;
     1583    char *tptr;
     1584    int http_major_version;
     1585    int ip_allowed;
     1586#if ENABLE_FEATURE_HTTPD_CGI
     1587    const char *prequest;
     1588    unsigned long length = 0;
     1589    char *cookie = 0;
     1590    char *content_type = 0;
     1591#endif
     1592    struct sigaction sa;
     1593#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
     1594    int credentials = -1;  /* if not required this is Ok */
     1595#endif
     1596
     1597    /* Allocation of iobuf is postponed until now
     1598     * (IOW, server process doesn't need to waste 8k) */
     1599    iobuf = xmalloc(IOBUF_SIZE);
     1600
     1601    rmt_ip = 0;
     1602    if (fromAddr->sa.sa_family == AF_INET) {
     1603        rmt_ip = ntohl(fromAddr->sin.sin_addr.s_addr);
     1604    }
     1605#if ENABLE_FEATURE_IPV6
     1606    if (fromAddr->sa.sa_family == AF_INET6
     1607     && fromAddr->sin6.sin6_addr.s6_addr32[0] == 0
     1608     && fromAddr->sin6.sin6_addr.s6_addr32[1] == 0
     1609     && ntohl(fromAddr->sin6.sin6_addr.s6_addr32[2]) == 0xffff)
     1610        rmt_ip = ntohl(fromAddr->sin6.sin6_addr.s6_addr32[3]);
     1611#endif
     1612    if (ENABLE_FEATURE_HTTPD_CGI || DEBUG || verbose) {
     1613        rmt_ip_str = xmalloc_sockaddr2dotted(&fromAddr->sa);
     1614    }
     1615    if (verbose) {
     1616        /* this trick makes -v logging much simpler */
     1617        applet_name = rmt_ip_str;
     1618        if (verbose > 2)
     1619            bb_error_msg("connected");
     1620    }
     1621
     1622    /* Install timeout handler */
     1623    memset(&sa, 0, sizeof(sa));
     1624    sa.sa_handler = exit_on_signal;
     1625    /* sigemptyset(&sa.sa_mask); - memset should be enough */
     1626    /*sa.sa_flags = 0; - no SA_RESTART */
     1627    sigaction(SIGALRM, &sa, NULL);
     1628    alarm(HEADER_READ_TIMEOUT);
     1629
     1630    if (!get_line()) /* EOF or error or empty line */
     1631        send_headers_and_exit(HTTP_BAD_REQUEST);
     1632
     1633    /* Determine type of request (GET/POST) */
     1634    urlp = strpbrk(iobuf, " \t");
     1635    if (urlp == NULL)
     1636        send_headers_and_exit(HTTP_BAD_REQUEST);
     1637    *urlp++ = '\0';
     1638#if ENABLE_FEATURE_HTTPD_CGI
     1639    prequest = request_GET;
     1640    if (strcasecmp(iobuf, prequest) != 0) {
     1641        prequest = "POST";
     1642        if (strcasecmp(iobuf, prequest) != 0)
     1643            send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
     1644    }
    12501645#else
    1251 # define PIPESIZE PIPE_BUF
    1252 #endif
    1253 #if PIPESIZE >= MAX_MEMORY_BUFF
    1254 # error "PIPESIZE >= MAX_MEMORY_BUFF"
    1255 #endif
    1256 
    1257     // There is something to read
    1258     count = safe_read(inFd, rbuf, PIPESIZE);
    1259     if (count == 0)
    1260         break;  /* closed */
    1261     if (count > 0) {
    1262       if (firstLine) {
    1263         rbuf[count] = 0;
    1264         /* check to see if the user script added headers */
    1265         if(strncmp(rbuf, "HTTP/1.0 200 OK\r\n", 4) != 0) {
    1266           bb_full_write(s, "HTTP/1.0 200 OK\r\n", 17);
    1267         }
    1268         if (strstr(rbuf, "ontent-") == 0) {
    1269           bb_full_write(s, "Content-type: text/plain\r\n\r\n", 28);
    1270         }
    1271         firstLine = 0;
    1272       }
    1273       if (bb_full_write(s, rbuf, count) != count)
    1274           break;
    1275 
    1276 #if DEBUG
    1277       fprintf(stderr, "cgi read %d bytes\n", count);
    1278 #endif
    1279     }
    1280       }
    1281     }
    1282   }
    1283   return 0;
    1284 }
    1285 #endif          /* CONFIG_FEATURE_HTTPD_CGI */
    1286 
    1287 /****************************************************************************
    1288  *
    1289  > $Function: sendFile()
    1290  *
    1291  * $Description: Send a file response to an HTTP request
    1292  *
    1293  * $Parameter:
    1294  *      (const char *) url . . The URL requested.
    1295  *
    1296  * $Return: (int)  . . . . . . Always 0.
    1297  *
    1298  ****************************************************************************/
    1299 static int sendFile(const char *url)
    1300 {
    1301   char * suffix;
    1302   int  f;
    1303   const char * const * table;
    1304   const char * try_suffix;
    1305 
    1306   suffix = strrchr(url, '.');
    1307 
    1308   for (table = suffixTable; *table; table += 2)
    1309     if(suffix != NULL && (try_suffix = strstr(*table, suffix)) != 0) {
    1310         try_suffix += strlen(suffix);
    1311         if(*try_suffix == 0 || *try_suffix == '.')
    1312             break;
    1313     }
    1314   /* also, if not found, set default as "application/octet-stream";  */
    1315   config->httpd_found.found_mime_type = *(table+1);
    1316 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
    1317   if (suffix) {
    1318     Htaccess * cur;
    1319 
    1320     for (cur = config->mime_a; cur; cur = cur->next) {
    1321     if(strcmp(cur->before_colon, suffix) == 0) {
    1322         config->httpd_found.found_mime_type = cur->after_colon;
    1323         break;
    1324     }
    1325     }
    1326   }
    1327 #endif  /* CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES */
    1328 
    1329 #if DEBUG
    1330   fprintf(stderr, "Sending file '%s' Content-type: %s\n",
    1331             url, config->httpd_found.found_mime_type);
    1332 #endif
    1333 
    1334   f = open(url, O_RDONLY);
    1335   if (f >= 0) {
    1336     int count;
    1337     char *buf = config->buf;
    1338 
    1339     sendHeaders(HTTP_OK);
    1340     while ((count = bb_full_read(f, buf, MAX_MEMORY_BUFF)) > 0) {
    1341         if (bb_full_write(a_c_w, buf, count) != count)
    1342             break;
    1343     }
    1344     close(f);
    1345   } else {
    1346 #if DEBUG
    1347     bb_perror_msg("Unable to open '%s'", url);
    1348 #endif
    1349     sendHeaders(HTTP_NOT_FOUND);
    1350   }
    1351 
    1352   return 0;
    1353 }
    1354 
    1355 static int checkPermIP(void)
    1356 {
    1357     Htaccess_IP * cur;
    1358 
    1359     /* This could stand some work */
    1360     for (cur = config->ip_a_d; cur; cur = cur->next) {
    1361 #if DEBUG
    1362     fprintf(stderr, "checkPermIP: '%s' ? ", config->rmt_ip_str);
    1363     fprintf(stderr, "'%u.%u.%u.%u/%u.%u.%u.%u'\n",
    1364         (unsigned char)(cur->ip >> 24),
    1365         (unsigned char)(cur->ip >> 16),
    1366         (unsigned char)(cur->ip >> 8),
    1367                 cur->ip & 0xff,
    1368         (unsigned char)(cur->mask >> 24),
    1369         (unsigned char)(cur->mask >> 16),
    1370         (unsigned char)(cur->mask >> 8),
    1371                 cur->mask & 0xff);
    1372 #endif
    1373     if((config->rmt_ip & cur->mask) == cur->ip)
    1374         return cur->allow_deny == 'A';   /* Allow/Deny */
    1375     }
    1376 
    1377     /* if unconfigured, return 1 - access from all */
    1378     return !config->flg_deny_all;
    1379 }
    1380 
    1381 /****************************************************************************
    1382  *
    1383  > $Function: checkPerm()
    1384  *
    1385  * $Description: Check the permission file for access password protected.
    1386  *
    1387  *   If config file isn't present, everything is allowed.
    1388  *   Entries are of the form you can see example from header source
    1389  *
    1390  * $Parameters:
    1391  *      (const char *) path  . . . . The file path.
    1392  *      (const char *) request . . . User information to validate.
    1393  *
    1394  * $Return: (int)  . . . . . . . . . 1 if request OK, 0 otherwise.
    1395  *
    1396  ****************************************************************************/
    1397 
    1398 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
    1399 static int checkPerm(const char *path, const char *request)
    1400 {
    1401     Htaccess * cur;
    1402     const char *p;
    1403     const char *p0;
    1404 
    1405     const char *prev = NULL;
    1406 
    1407     /* This could stand some work */
    1408     for (cur = config->auth; cur; cur = cur->next) {
    1409     p0 = cur->before_colon;
    1410     if(prev != NULL && strcmp(prev, p0) != 0)
    1411         continue;       /* find next identical */
    1412     p = cur->after_colon;
    1413 #if DEBUG
    1414     fprintf(stderr,"checkPerm: '%s' ? '%s'\n", p0, request);
    1415 #endif
     1646    if (strcasecmp(iobuf, request_GET) != 0)
     1647        send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
     1648#endif
     1649    urlp = skip_whitespace(urlp);
     1650    if (urlp[0] != '/')
     1651        send_headers_and_exit(HTTP_BAD_REQUEST);
     1652
     1653    /* Find end of URL and parse HTTP version, if any */
     1654    http_major_version = -1;
     1655    tptr = strchrnul(urlp, ' ');
     1656    /* Is it " HTTP/"? */
     1657    if (tptr[0] && strncmp(tptr + 1, HTTP_200, 5) == 0)
     1658        http_major_version = (tptr[6] - '0');
     1659    *tptr = '\0';
     1660
     1661    /* Copy URL from after "GET "/"POST " to stack-allocated char[] */
     1662    urlcopy = alloca((tptr - urlp) + sizeof("/index.html"));
     1663    /*if (urlcopy == NULL)
     1664     *  send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);*/
     1665    strcpy(urlcopy, urlp);
     1666    /* NB: urlcopy ptr is never changed after this */
     1667
     1668    /* Extract url args if present */
     1669    tptr = strchr(urlcopy, '?');
     1670    g_query = NULL;
     1671    if (tptr) {
     1672        *tptr++ = '\0';
     1673        g_query = tptr;
     1674    }
     1675
     1676    /* Decode URL escape sequences */
     1677    tptr = decodeString(urlcopy, 0);
     1678    if (tptr == NULL)
     1679        send_headers_and_exit(HTTP_BAD_REQUEST);
     1680    if (tptr == urlcopy + 1) {
     1681        /* '/' or NUL is encoded */
     1682        send_headers_and_exit(HTTP_NOT_FOUND);
     1683    }
     1684
     1685    /* Canonicalize path */
     1686    /* Algorithm stolen from libbb bb_simplify_path(),
     1687     * but don't strdup and reducing trailing slash and protect out root */
     1688    urlp = tptr = urlcopy;
     1689    do {
     1690        if (*urlp == '/') {
     1691            /* skip duplicate (or initial) slash */
     1692            if (*tptr == '/') {
     1693                continue;
     1694            }
     1695            if (*tptr == '.') {
     1696                /* skip extra '.' */
     1697                if (tptr[1] == '/' || !tptr[1]) {
     1698                    continue;
     1699                }
     1700                /* '..': be careful */
     1701                if (tptr[1] == '.' && (tptr[2] == '/' || !tptr[2])) {
     1702                    ++tptr;
     1703                    if (urlp == urlcopy) /* protect root */
     1704                        send_headers_and_exit(HTTP_BAD_REQUEST);
     1705                    while (*--urlp != '/') /* omit previous dir */;
     1706                        continue;
     1707                }
     1708            }
     1709        }
     1710        *++urlp = *tptr;
     1711    } while (*++tptr);
     1712    *++urlp = '\0';       /* so keep last character */
     1713    tptr = urlp;          /* end ptr */
     1714
     1715    /* If URL is a directory, add '/' */
     1716    if (tptr[-1] != '/') {
     1717        if (is_directory(urlcopy + 1, 1, &sb)) {
     1718            found_moved_temporarily = urlcopy;
     1719        }
     1720    }
     1721
     1722    /* Log it */
     1723    if (verbose > 1)
     1724        bb_error_msg("url:%s", urlcopy);
     1725
     1726    tptr = urlcopy;
     1727    ip_allowed = checkPermIP();
     1728    while (ip_allowed && (tptr = strchr(tptr + 1, '/')) != NULL) {
     1729        /* have path1/path2 */
     1730        *tptr = '\0';
     1731        if (is_directory(urlcopy + 1, 1, &sb)) {
     1732            /* may be having subdir config */
     1733            parse_conf(urlcopy + 1, SUBDIR_PARSE);
     1734            ip_allowed = checkPermIP();
     1735        }
     1736        *tptr = '/';
     1737    }
     1738    if (http_major_version >= 0) {
     1739        /* Request was with "... HTTP/nXXX", and n >= 0 */
     1740
     1741        /* Read until blank line for HTTP version specified, else parse immediate */
     1742        while (1) {
     1743            alarm(HEADER_READ_TIMEOUT);
     1744            if (!get_line())
     1745                break; /* EOF or error or empty line */
     1746            if (DEBUG)
     1747                bb_error_msg("header: '%s'", iobuf);
     1748
     1749#if ENABLE_FEATURE_HTTPD_CGI
     1750            /* try and do our best to parse more lines */
     1751            if ((STRNCASECMP(iobuf, "Content-length:") == 0)) {
     1752                /* extra read only for POST */
     1753                if (prequest != request_GET) {
     1754                    tptr = iobuf + sizeof("Content-length:") - 1;
     1755                    if (!tptr[0])
     1756                        send_headers_and_exit(HTTP_BAD_REQUEST);
     1757                    errno = 0;
     1758                    /* not using strtoul: it ignores leading minus! */
     1759                    length = strtol(tptr, &tptr, 10);
     1760                    /* length is "ulong", but we need to pass it to int later */
     1761                    /* so we check for negative or too large values in one go: */
     1762                    /* (long -> ulong conv caused negatives to be seen as > INT_MAX) */
     1763                    if (tptr[0] || errno || length > INT_MAX)
     1764                        send_headers_and_exit(HTTP_BAD_REQUEST);
     1765                }
     1766            } else if (STRNCASECMP(iobuf, "Cookie:") == 0) {
     1767                cookie = strdup(skip_whitespace(iobuf + sizeof("Cookie:")-1));
     1768            } else if (STRNCASECMP(iobuf, "Content-Type:") == 0) {
     1769                content_type = strdup(skip_whitespace(iobuf + sizeof("Content-Type:")-1));
     1770            } else if (STRNCASECMP(iobuf, "Referer:") == 0) {
     1771                referer = strdup(skip_whitespace(iobuf + sizeof("Referer:")-1));
     1772            } else if (STRNCASECMP(iobuf, "User-Agent:") == 0) {
     1773                user_agent = strdup(skip_whitespace(iobuf + sizeof("User-Agent:")-1));
     1774            }
     1775#endif
     1776#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
     1777            if (STRNCASECMP(iobuf, "Authorization:") == 0) {
     1778                /* We only allow Basic credentials.
     1779                 * It shows up as "Authorization: Basic <userid:password>" where
     1780                 * the userid:password is base64 encoded.
     1781                 */
     1782                tptr = skip_whitespace(iobuf + sizeof("Authorization:")-1);
     1783                if (STRNCASECMP(tptr, "Basic") != 0)
     1784                    continue;
     1785                tptr += sizeof("Basic")-1;
     1786                /* decodeBase64() skips whitespace itself */
     1787                decodeBase64(tptr);
     1788                credentials = checkPerm(urlcopy, tptr);
     1789            }
     1790#endif          /* FEATURE_HTTPD_BASIC_AUTH */
     1791        } /* while extra header reading */
     1792    }
     1793
     1794    /* We read headers, disable peer timeout */
     1795    alarm(0);
     1796
     1797    if (strcmp(bb_basename(urlcopy), httpd_conf) == 0 || ip_allowed == 0) {
     1798        /* protect listing [/path]/httpd_conf or IP deny */
     1799        send_headers_and_exit(HTTP_FORBIDDEN);
     1800    }
     1801
     1802#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
     1803    if (credentials <= 0 && checkPerm(urlcopy, ":") == 0) {
     1804        send_headers_and_exit(HTTP_UNAUTHORIZED);
     1805    }
     1806#endif
     1807
     1808    if (found_moved_temporarily) {
     1809        send_headers_and_exit(HTTP_MOVED_TEMPORARILY);
     1810    }
     1811
     1812    tptr = urlcopy + 1;      /* skip first '/' */
     1813
     1814#if ENABLE_FEATURE_HTTPD_CGI
     1815    if (strncmp(tptr, "cgi-bin/", 8) == 0) {
     1816        if (tptr[8] == '\0') {
     1817            /* protect listing "cgi-bin/" */
     1818            send_headers_and_exit(HTTP_FORBIDDEN);
     1819        }
     1820        send_cgi_and_exit(urlcopy, prequest, length, cookie, content_type);
     1821    }
     1822#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
    14161823    {
    1417         size_t l = strlen(p0);
    1418 
    1419         if(strncmp(p0, path, l) == 0 &&
    1420                 (l == 1 || path[l] == '/' || path[l] == 0)) {
    1421         char *u;
    1422         /* path match found.  Check request */
    1423         /* for check next /path:user:password */
    1424         prev = p0;
    1425         u = strchr(request, ':');
    1426         if(u == NULL) {
    1427             /* bad request, ':' required */
    1428             break;
    1429             }
    1430 
    1431 #ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
    1432         {
    1433             char *cipher;
    1434             char *pp;
    1435 
    1436             if(strncmp(p, request, u-request) != 0) {
    1437                 /* user uncompared */
    1438                 continue;
    1439             }
    1440             pp = strchr(p, ':');
    1441             if(pp && pp[1] == '$' && pp[2] == '1' &&
    1442                          pp[3] == '$' && pp[4]) {
    1443                 pp++;
    1444                 cipher = pw_encrypt(u+1, pp);
    1445                 if (strcmp(cipher, pp) == 0)
    1446                     goto set_remoteuser_var;   /* Ok */
    1447                 /* unauthorized */
    1448                 continue;
    1449             }
    1450         }
    1451 #endif
    1452         if (strcmp(p, request) == 0) {
    1453 #ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
    1454 set_remoteuser_var:
    1455 #endif
    1456             config->remoteuser = strdup(request);
    1457             if(config->remoteuser)
    1458             config->remoteuser[(u - request)] = 0;
    1459             return 1;   /* Ok */
    1460         }
    1461         /* unauthorized */
    1462         }
    1463     }
    1464     }   /* for */
    1465 
    1466     return prev == NULL;
    1467 }
    1468 
    1469 #endif  /* CONFIG_FEATURE_HTTPD_BASIC_AUTH */
    1470 
    1471 /****************************************************************************
    1472  *
    1473  > $Function: handle_sigalrm()
    1474  *
    1475  * $Description: Handle timeouts
    1476  *
    1477  ****************************************************************************/
    1478 
    1479 static void
    1480 handle_sigalrm( int sig )
    1481 {
    1482     sendHeaders(HTTP_REQUEST_TIMEOUT);
    1483     config->alarm_signaled = sig;
    1484 }
    1485 
    1486 /****************************************************************************
    1487  *
    1488  > $Function: handleIncoming()
    1489  *
    1490  * $Description: Handle an incoming http request.
    1491  *
    1492  ****************************************************************************/
    1493 static void handleIncoming(void)
    1494 {
    1495   char *buf = config->buf;
    1496   char *url;
    1497   char *purl;
    1498   int  blank = -1;
    1499   char *test;
    1500   struct stat sb;
    1501   int ip_allowed;
    1502 #ifdef CONFIG_FEATURE_HTTPD_CGI
    1503   const char *prequest = request_GET;
    1504   long length=0;
    1505   char *cookie = 0;
    1506   char *content_type = 0;
    1507 #endif
    1508   fd_set s_fd;
    1509   struct timeval tv;
    1510   int retval;
    1511   struct sigaction sa;
    1512 
    1513 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
    1514   int credentials = -1;  /* if not requred this is Ok */
    1515 #endif
    1516 
    1517   sa.sa_handler = handle_sigalrm;
    1518   sigemptyset(&sa.sa_mask);
    1519   sa.sa_flags = 0; /* no SA_RESTART */
    1520   sigaction(SIGALRM, &sa, NULL);
    1521 
    1522   do {
    1523     int  count;
    1524 
    1525     (void) alarm( TIMEOUT );
    1526     if (getLine() <= 0)
    1527     break;  /* closed */
    1528 
    1529     purl = strpbrk(buf, " \t");
    1530     if(purl == NULL) {
    1531 BAD_REQUEST:
    1532       sendHeaders(HTTP_BAD_REQUEST);
    1533       break;
    1534     }
    1535     *purl = 0;
    1536 #ifdef CONFIG_FEATURE_HTTPD_CGI
    1537     if(strcasecmp(buf, prequest) != 0) {
    1538     prequest = "POST";
    1539     if(strcasecmp(buf, prequest) != 0) {
    1540         sendHeaders(HTTP_NOT_IMPLEMENTED);
    1541         break;
    1542     }
    1543     }
     1824        char *suffix = strrchr(tptr, '.');
     1825        if (suffix) {
     1826            Htaccess *cur;
     1827            for (cur = script_i; cur; cur = cur->next) {
     1828                if (strcmp(cur->before_colon + 1, suffix) == 0) {
     1829                    send_cgi_and_exit(urlcopy, prequest, length, cookie, content_type);
     1830                }
     1831            }
     1832        }
     1833    }
     1834#endif
     1835    if (prequest != request_GET) {
     1836        send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
     1837    }
     1838#endif  /* FEATURE_HTTPD_CGI */
     1839
     1840    if (urlp[-1] == '/')
     1841        strcpy(urlp, "index.html");
     1842    if (stat(tptr, &sb) == 0) {
     1843        /* It's a dir URL and there is index.html */
     1844        ContentLength = sb.st_size;
     1845        last_mod = sb.st_mtime;
     1846    }
     1847#if ENABLE_FEATURE_HTTPD_CGI
     1848    else if (urlp[-1] == '/') {
     1849        /* It's a dir URL and there is no index.html
     1850         * Try cgi-bin/index.cgi */
     1851        if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) {
     1852            urlp[0] = '\0';
     1853            g_query = urlcopy;
     1854            send_cgi_and_exit("/cgi-bin/index.cgi", prequest, length, cookie, content_type);
     1855        }
     1856    }
     1857#endif
     1858    /* else {
     1859     *  fall through to send_file, it errors out if open fails
     1860     * }
     1861     */
     1862
     1863    send_file_and_exit(tptr, TRUE);
     1864}
     1865
     1866/*
     1867 * The main http server function.
     1868 * Given a socket, listen for new connections and farm out
     1869 * the processing as a [v]forked process.
     1870 * Never returns.
     1871 */
     1872#if BB_MMU
     1873static void mini_httpd(int server_socket) ATTRIBUTE_NORETURN;
     1874static void mini_httpd(int server_socket)
     1875{
     1876    /* NB: it's best to not use xfuncs in this loop before fork().
     1877     * Otherwise server may die on transient errors (temporary
     1878     * out-of-memory condition, etc), which is Bad(tm).
     1879     * Try to do any dangerous calls after fork.
     1880     */
     1881    while (1) {
     1882        int n;
     1883        len_and_sockaddr fromAddr;
     1884       
     1885        /* Wait for connections... */
     1886        fromAddr.len = LSA_SIZEOF_SA;
     1887        n = accept(server_socket, &fromAddr.sa, &fromAddr.len);
     1888
     1889        if (n < 0)
     1890            continue;
     1891        /* set the KEEPALIVE option to cull dead connections */
     1892        setsockopt(n, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
     1893
     1894        if (fork() == 0) {
     1895            /* child */
     1896#if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
     1897            /* Do not reload config on HUP */
     1898            signal(SIGHUP, SIG_IGN);
     1899#endif
     1900            close(server_socket);
     1901            xmove_fd(n, 0);
     1902            xdup2(0, 1);
     1903
     1904            handle_incoming_and_exit(&fromAddr);
     1905        }
     1906        /* parent, or fork failed */
     1907        close(n);
     1908    } /* while (1) */
     1909    /* never reached */
     1910}
    15441911#else
    1545     if(strcasecmp(buf, request_GET) != 0) {
    1546     sendHeaders(HTTP_NOT_IMPLEMENTED);
    1547     break;
    1548     }
    1549 #endif
    1550     *purl = ' ';
    1551     count = sscanf(purl, " %[^ ] HTTP/%d.%*d", buf, &blank);
    1552 
    1553     if (count < 1 || buf[0] != '/') {
    1554       /* Garbled request/URL */
    1555       goto BAD_REQUEST;
    1556     }
    1557     url = alloca(strlen(buf) + 12);      /* + sizeof("/index.html\0") */
    1558     if(url == NULL) {
    1559     sendHeaders(HTTP_INTERNAL_SERVER_ERROR);
    1560     break;
    1561     }
    1562     strcpy(url, buf);
    1563     /* extract url args if present */
    1564     test = strchr(url, '?');
    1565     if (test) {
    1566       *test++ = 0;
    1567       config->query = test;
    1568     }
    1569 
    1570     test = decodeString(url, 0);
    1571     if(test == NULL)
    1572     goto BAD_REQUEST;
    1573     if(test == (buf+1)) {
    1574     sendHeaders(HTTP_NOT_FOUND);
    1575     break;
    1576     }
    1577     /* algorithm stolen from libbb bb_simplify_path(),
    1578        but don`t strdup and reducing trailing slash and protect out root */
    1579     purl = test = url;
    1580 
    1581     do {
    1582     if (*purl == '/') {
    1583         if (*test == '/') {        /* skip duplicate (or initial) slash */
    1584         continue;
    1585         } else if (*test == '.') {
    1586         if (test[1] == '/' || test[1] == 0) { /* skip extra '.' */
    1587             continue;
    1588         } else if ((test[1] == '.') && (test[2] == '/' || test[2] == 0)) {
    1589             ++test;
    1590             if (purl == url) {
    1591             /* protect out root */
    1592             goto BAD_REQUEST;
    1593             }
    1594             while (*--purl != '/');    /* omit previous dir */
    1595             continue;
    1596         }
    1597         }
    1598     }
    1599     *++purl = *test;
    1600     } while (*++test);
    1601 
    1602     *++purl = 0;        /* so keep last character */
    1603     test = purl;        /* end ptr */
    1604 
    1605     /* If URL is directory, adding '/' */
    1606     if(test[-1] != '/') {
    1607         if ( is_directory(url + 1, 1, &sb) ) {
    1608             config->httpd_found.found_moved_temporarily = url;
    1609         }
    1610     }
    1611 #if DEBUG
    1612     fprintf(stderr, "url='%s', args=%s\n", url, config->query);
    1613 #endif
    1614 
    1615     test = url;
    1616     ip_allowed = checkPermIP();
    1617     while(ip_allowed && (test = strchr( test + 1, '/' )) != NULL) {
    1618     /* have path1/path2 */
    1619     *test = '\0';
    1620     if( is_directory(url + 1, 1, &sb) ) {
    1621         /* may be having subdir config */
    1622         parse_conf(url + 1, SUBDIR_PARSE);
    1623         ip_allowed = checkPermIP();
    1624     }
    1625     *test = '/';
    1626     }
    1627     if(blank >= 0) {
    1628       // read until blank line for HTTP version specified, else parse immediate
    1629       while(1) {
    1630     alarm(TIMEOUT);
    1631     count = getLine();
    1632     if(count <= 0)
    1633         break;
    1634 
    1635 #if DEBUG
    1636     fprintf(stderr, "Header: '%s'\n", buf);
    1637 #endif
    1638 
    1639 #ifdef CONFIG_FEATURE_HTTPD_CGI
    1640     /* try and do our best to parse more lines */
    1641     if ((strncasecmp(buf, Content_length, 15) == 0)) {
    1642       if(prequest != request_GET)
    1643           length = strtol(buf + 15, 0, 0); // extra read only for POST
    1644     } else if ((strncasecmp(buf, "Cookie:", 7) == 0)) {
    1645           for(test = buf + 7; isspace(*test); test++)
    1646               ;
    1647           cookie = strdup(test);
    1648     } else if ((strncasecmp(buf, "Content-Type:", 13) == 0)) {
    1649           for(test = buf + 13; isspace(*test); test++)
    1650               ;
    1651           content_type = strdup(test);
    1652     } else if ((strncasecmp(buf, "Referer:", 8) == 0)) {
    1653           for(test = buf + 8; isspace(*test); test++)
    1654               ;
    1655           config->referer = strdup(test);
    1656     }
    1657 #endif
    1658 
    1659 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
    1660     if (strncasecmp(buf, "Authorization:", 14) == 0) {
    1661       /* We only allow Basic credentials.
    1662        * It shows up as "Authorization: Basic <userid:password>" where
    1663        * the userid:password is base64 encoded.
    1664        */
    1665       for(test = buf + 14; isspace(*test); test++)
    1666           ;
    1667       if (strncasecmp(test, "Basic", 5) != 0)
    1668           continue;
    1669 
    1670       test += 5;  /* decodeBase64() skiping space self */
    1671       decodeBase64(test);
    1672       credentials = checkPerm(url, test);
    1673     }
    1674 #endif          /* CONFIG_FEATURE_HTTPD_BASIC_AUTH */
    1675 
    1676       }   /* while extra header reading */
    1677     }
    1678     (void) alarm( 0 );
    1679     if(config->alarm_signaled)
    1680     break;
    1681 
    1682     if (strcmp(strrchr(url, '/') + 1, httpd_conf) == 0 || ip_allowed == 0) {
    1683         /* protect listing [/path]/httpd_conf or IP deny */
    1684 #ifdef CONFIG_FEATURE_HTTPD_CGI
    1685 FORBIDDEN:      /* protect listing /cgi-bin */
    1686 #endif
    1687         sendHeaders(HTTP_FORBIDDEN);
    1688         break;
    1689     }
    1690 
    1691 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
    1692     if (credentials <= 0 && checkPerm(url, ":") == 0) {
    1693       sendHeaders(HTTP_UNAUTHORIZED);
    1694       break;
    1695     }
    1696 #endif
    1697 
    1698     if(config->httpd_found.found_moved_temporarily) {
    1699     sendHeaders(HTTP_MOVED_TEMPORARILY);
    1700 #if DEBUG
    1701     /* clear unforked memory flag */
    1702     config->httpd_found.found_moved_temporarily = NULL;
    1703 #endif
    1704     break;
    1705     }
    1706 
    1707     test = url + 1;      /* skip first '/' */
    1708 
    1709 #ifdef CONFIG_FEATURE_HTTPD_CGI
    1710     /* if strange Content-Length */
    1711     if (length < 0)
    1712     break;
    1713 
    1714     if (strncmp(test, "cgi-bin", 7) == 0) {
    1715         if(test[7] == '/' && test[8] == 0)
    1716             goto FORBIDDEN;     // protect listing cgi-bin/
    1717         sendCgi(url, prequest, length, cookie, content_type);
    1718     } else {
    1719     if (prequest != request_GET)
    1720         sendHeaders(HTTP_NOT_IMPLEMENTED);
    1721     else {
    1722 #endif  /* CONFIG_FEATURE_HTTPD_CGI */
    1723         if(purl[-1] == '/')
    1724             strcpy(purl, "index.html");
    1725         if ( stat(test, &sb ) == 0 ) {
    1726             config->ContentLength = sb.st_size;
    1727             config->last_mod = sb.st_mtime;
    1728         }
    1729         sendFile(test);
    1730 #ifdef CONFIG_FEATURE_HTTPD_WITHOUT_INETD
    1731         /* unset if non inetd looped */
    1732         config->ContentLength = -1;
    1733 #endif
    1734 
    1735 #ifdef CONFIG_FEATURE_HTTPD_CGI
    1736     }
    1737     }
    1738 #endif
    1739 
    1740   } while (0);
    1741 
    1742 
    1743 #ifdef CONFIG_FEATURE_HTTPD_WITHOUT_INETD
    1744 /* from inetd don`t looping: freeing, closing automatic from exit always */
    1745 # if DEBUG
    1746   fprintf(stderr, "closing socket\n");
    1747 # endif
    1748 # ifdef CONFIG_FEATURE_HTTPD_CGI
    1749   free(cookie);
    1750   free(content_type);
    1751   free(config->referer);
    1752 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
    1753   free(config->remoteuser);
    1754 #endif
    1755 # endif
    1756 #endif  /* CONFIG_FEATURE_HTTPD_WITHOUT_INETD */
    1757   shutdown(a_c_w, SHUT_WR);
    1758 
    1759   /* Properly wait for remote to closed */
    1760   FD_ZERO (&s_fd) ;
    1761   FD_SET (a_c_r, &s_fd) ;
    1762 
    1763   do {
    1764     tv.tv_sec = 2 ;
    1765     tv.tv_usec = 0 ;
    1766     retval = select (a_c_r + 1, &s_fd, NULL, NULL, &tv);
    1767   } while (retval > 0 && (read (a_c_r, buf, sizeof (config->buf)) > 0));
    1768 
    1769   shutdown(a_c_r, SHUT_RD);
    1770 #ifdef CONFIG_FEATURE_HTTPD_WITHOUT_INETD
    1771   close(config->accepted_socket);
    1772 #endif  /* CONFIG_FEATURE_HTTPD_WITHOUT_INETD */
    1773 }
    1774 
    1775 /****************************************************************************
    1776  *
    1777  > $Function: miniHttpd()
    1778  *
    1779  * $Description: The main http server function.
    1780  *
    1781  *   Given an open socket fildes, listen for new connections and farm out
    1782  *   the processing as a forked process.
    1783  *
    1784  * $Parameters:
    1785  *      (int) server. . . The server socket fildes.
    1786  *
    1787  * $Return: (int) . . . . Always 0.
    1788  *
    1789  ****************************************************************************/
    1790 #ifdef CONFIG_FEATURE_HTTPD_WITHOUT_INETD
    1791 static int miniHttpd(int server)
    1792 {
    1793   fd_set readfd, portfd;
    1794 
    1795   FD_ZERO(&portfd);
    1796   FD_SET(server, &portfd);
    1797 
    1798   /* copy the ports we are watching to the readfd set */
    1799   while (1) {
    1800     readfd = portfd;
    1801 
    1802     /* Now wait INDEFINITELY on the set of sockets! */
    1803     if (select(server + 1, &readfd, 0, 0, 0) > 0) {
    1804       if (FD_ISSET(server, &readfd)) {
    1805     int on;
    1806     struct sockaddr_in fromAddr;
    1807 
    1808     socklen_t fromAddrLen = sizeof(fromAddr);
    1809     int s = accept(server,
    1810                (struct sockaddr *)&fromAddr, &fromAddrLen);
    1811 
    1812     if (s < 0) {
    1813         continue;
    1814     }
    1815     config->accepted_socket = s;
    1816     config->rmt_ip = ntohl(fromAddr.sin_addr.s_addr);
    1817 #if defined(CONFIG_FEATURE_HTTPD_CGI) || DEBUG
    1818     sprintf(config->rmt_ip_str, "%u.%u.%u.%u",
    1819         (unsigned char)(config->rmt_ip >> 24),
    1820         (unsigned char)(config->rmt_ip >> 16),
    1821         (unsigned char)(config->rmt_ip >> 8),
    1822                 config->rmt_ip & 0xff);
    1823     config->port = ntohs(fromAddr.sin_port);
    1824 #if DEBUG
    1825     bb_error_msg("connection from IP=%s, port %u\n",
    1826                     config->rmt_ip_str, config->port);
    1827 #endif
    1828 #endif /* CONFIG_FEATURE_HTTPD_CGI */
    1829 
    1830     /*  set the KEEPALIVE option to cull dead connections */
    1831     on = 1;
    1832     setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof (on));
    1833 
    1834 #if !DEBUG
    1835     if (fork() == 0)
    1836 #endif
    1837     {
    1838         /* This is the spawned thread */
    1839 #ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
    1840         /* protect reload config, may be confuse checking */
    1841         signal(SIGHUP, SIG_IGN);
    1842 #endif
    1843         handleIncoming();
    1844 #if !DEBUG
    1845         exit(0);
    1846 #endif
    1847     }
    1848     close(s);
    1849       }
    1850     }
    1851   } // while (1)
    1852   return 0;
    1853 }
    1854 
    1855 #else
    1856     /* from inetd */
    1857 
    1858 static int miniHttpd(void)
    1859 {
    1860   struct sockaddr_in fromAddrLen;
    1861   socklen_t sinlen = sizeof (struct sockaddr_in);
    1862 
    1863   getpeername (0, (struct sockaddr *)&fromAddrLen, &sinlen);
    1864   config->rmt_ip = ntohl(fromAddrLen.sin_addr.s_addr);
    1865 #ifdef CONFIG_FEATURE_HTTPD_CGI
    1866   sprintf(config->rmt_ip_str, "%u.%u.%u.%u",
    1867         (unsigned char)(config->rmt_ip >> 24),
    1868         (unsigned char)(config->rmt_ip >> 16),
    1869         (unsigned char)(config->rmt_ip >> 8),
    1870                 config->rmt_ip & 0xff);
    1871 #endif
    1872   config->port = ntohs(fromAddrLen.sin_port);
    1873   handleIncoming();
    1874   return 0;
    1875 }
    1876 #endif  /* CONFIG_FEATURE_HTTPD_WITHOUT_INETD */
    1877 
    1878 #ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
     1912static void mini_httpd_nommu(int server_socket, int argc, char **argv) ATTRIBUTE_NORETURN;
     1913static void mini_httpd_nommu(int server_socket, int argc, char **argv)
     1914{
     1915    char *argv_copy[argc + 2];
     1916
     1917    argv_copy[0] = argv[0];
     1918    argv_copy[1] = (char*)"-i";
     1919    memcpy(&argv_copy[2], &argv[1], argc * sizeof(argv[0]));
     1920
     1921    /* NB: it's best to not use xfuncs in this loop before vfork().
     1922     * Otherwise server may die on transient errors (temporary
     1923     * out-of-memory condition, etc), which is Bad(tm).
     1924     * Try to do any dangerous calls after fork.
     1925     */
     1926    while (1) {
     1927        int n;
     1928        len_and_sockaddr fromAddr;
     1929       
     1930        /* Wait for connections... */
     1931        fromAddr.len = LSA_SIZEOF_SA;
     1932        n = accept(server_socket, &fromAddr.sa, &fromAddr.len);
     1933
     1934        if (n < 0)
     1935            continue;
     1936        /* set the KEEPALIVE option to cull dead connections */
     1937        setsockopt(n, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
     1938
     1939        if (vfork() == 0) {
     1940            /* child */
     1941#if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
     1942            /* Do not reload config on HUP */
     1943            signal(SIGHUP, SIG_IGN);
     1944#endif
     1945            close(server_socket);
     1946            xmove_fd(n, 0);
     1947            xdup2(0, 1);
     1948
     1949            /* Run a copy of ourself in inetd mode */
     1950            re_exec(argv_copy);
     1951        }
     1952        /* parent, or vfork failed */
     1953        close(n);
     1954    } /* while (1) */
     1955    /* never reached */
     1956}
     1957#endif
     1958
     1959/*
     1960 * Process a HTTP connection on stdin/out.
     1961 * Never returns.
     1962 */
     1963static void mini_httpd_inetd(void) ATTRIBUTE_NORETURN;
     1964static void mini_httpd_inetd(void)
     1965{
     1966    len_and_sockaddr fromAddr;
     1967
     1968    fromAddr.len = LSA_SIZEOF_SA;
     1969    getpeername(0, &fromAddr.sa, &fromAddr.len);
     1970    handle_incoming_and_exit(&fromAddr);
     1971}
     1972
     1973#if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
    18791974static void sighup_handler(int sig)
    18801975{
    1881     /* set and reset */
    18821976    struct sigaction sa;
    18831977
    1884     parse_conf(default_path_httpd_conf,
    1885             sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE);
     1978    parse_conf(default_path_httpd_conf, sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE);
     1979
     1980    memset(&sa, 0, sizeof(sa));
    18861981    sa.sa_handler = sighup_handler;
    1887     sigemptyset(&sa.sa_mask);
     1982    /*sigemptyset(&sa.sa_mask); - memset should be enough */
    18881983    sa.sa_flags = SA_RESTART;
    18891984    sigaction(SIGHUP, &sa, NULL);
     
    18911986#endif
    18921987
    1893 enum httpd_opts_nums {
     1988enum {
    18941989    c_opt_config_file = 0,
    18951990    d_opt_decode_url,
    18961991    h_opt_home_httpd,
    18971992    USE_FEATURE_HTTPD_ENCODE_URL_STR(e_opt_encode_url,)
    1898     USE_FEATURE_HTTPD_BASIC_AUTH(r_opt_realm,)
    1899     USE_FEATURE_HTTPD_AUTH_MD5(m_opt_md5,)
    1900     USE_FEATURE_HTTPD_SETUID(u_opt_setuid,)
    1901     USE_FEATURE_HTTPD_WITHOUT_INETD(p_opt_port,)
     1993    USE_FEATURE_HTTPD_BASIC_AUTH(    r_opt_realm     ,)
     1994    USE_FEATURE_HTTPD_AUTH_MD5(      m_opt_md5       ,)
     1995    USE_FEATURE_HTTPD_SETUID(        u_opt_setuid    ,)
     1996    p_opt_port      ,
     1997    p_opt_inetd     ,
     1998    p_opt_foreground,
     1999    p_opt_verbose   ,
     2000    OPT_CONFIG_FILE = 1 << c_opt_config_file,
     2001    OPT_DECODE_URL  = 1 << d_opt_decode_url,
     2002    OPT_HOME_HTTPD  = 1 << h_opt_home_httpd,
     2003    OPT_ENCODE_URL  = USE_FEATURE_HTTPD_ENCODE_URL_STR((1 << e_opt_encode_url)) + 0,
     2004    OPT_REALM       = USE_FEATURE_HTTPD_BASIC_AUTH(    (1 << r_opt_realm     )) + 0,
     2005    OPT_MD5         = USE_FEATURE_HTTPD_AUTH_MD5(      (1 << m_opt_md5       )) + 0,
     2006    OPT_SETUID      = USE_FEATURE_HTTPD_SETUID(        (1 << u_opt_setuid    )) + 0,
     2007    OPT_PORT        = 1 << p_opt_port,
     2008    OPT_INETD       = 1 << p_opt_inetd,
     2009    OPT_FOREGROUND  = 1 << p_opt_foreground,
     2010    OPT_VERBOSE     = 1 << p_opt_verbose,
    19022011};
    19032012
    1904 static const char httpd_opts[]="c:d:h:"
    1905     USE_FEATURE_HTTPD_ENCODE_URL_STR("e:")
    1906     USE_FEATURE_HTTPD_BASIC_AUTH("r:")
    1907     USE_FEATURE_HTTPD_AUTH_MD5("m:")
    1908     USE_FEATURE_HTTPD_SETUID("u:")
    1909     USE_FEATURE_HTTPD_WITHOUT_INETD("p:");
    1910 
    1911 #define OPT_CONFIG_FILE (1<<c_opt_config_file)
    1912 #define OPT_DECODE_URL  (1<<d_opt_decode_url)
    1913 #define OPT_HOME_HTTPD  (1<<h_opt_home_httpd)
    1914 
    1915 #define OPT_ENCODE_URL  USE_FEATURE_HTTPD_ENCODE_URL_STR((1<<e_opt_encode_url)) \
    1916             SKIP_FEATURE_HTTPD_ENCODE_URL_STR(0)
    1917 
    1918 #define OPT_REALM       USE_FEATURE_HTTPD_BASIC_AUTH((1<<r_opt_realm)) \
    1919             SKIP_FEATURE_HTTPD_BASIC_AUTH(0)
    1920 
    1921 #define OPT_MD5         USE_FEATURE_HTTPD_AUTH_MD5((1<<m_opt_md5)) \
    1922             SKIP_FEATURE_HTTPD_AUTH_MD5(0)
    1923 
    1924 #define OPT_SETUID      USE_FEATURE_HTTPD_SETUID((1<<u_opt_setuid)) \
    1925             SKIP_FEATURE_HTTPD_SETUID(0)
    1926 
    1927 #define OPT_PORT        USE_FEATURE_HTTPD_WITHOUT_INETD((1<<p_opt_port)) \
    1928             SKIP_FEATURE_HTTPD_WITHOUT_INETD(0)
    1929 
    1930 
    1931 int httpd_main(int argc, char *argv[])
    1932 {
    1933   unsigned long opt;
    1934   const char *home_httpd = home;
    1935   char *url_for_decode;
    1936   USE_FEATURE_HTTPD_ENCODE_URL_STR(const char *url_for_encode;)
    1937   USE_FEATURE_HTTPD_WITHOUT_INETD(const char *s_port;)
    1938   USE_FEATURE_HTTPD_WITHOUT_INETD(int server;)
    1939 
    1940   USE_FEATURE_HTTPD_SETUID(const char *s_uid;)
    1941   USE_FEATURE_HTTPD_SETUID(long uid = -1;)
    1942 
    1943   USE_FEATURE_HTTPD_AUTH_MD5(const char *pass;)
    1944 
    1945   config = xcalloc(1, sizeof(*config));
    1946 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
    1947   config->realm = "Web Server Authentication";
    1948 #endif
    1949 
    1950 #ifdef CONFIG_FEATURE_HTTPD_WITHOUT_INETD
    1951   config->port = 80;
    1952 #endif
    1953 
    1954   config->ContentLength = -1;
    1955 
    1956   opt = bb_getopt_ulflags(argc, argv, httpd_opts,
    1957             &(config->configFile), &url_for_decode, &home_httpd
     2013
     2014int httpd_main(int argc, char **argv);
     2015int httpd_main(int argc, char **argv)
     2016{
     2017    int server_socket = server_socket; /* for gcc */
     2018    unsigned opt;
     2019    char *url_for_decode;
     2020    USE_FEATURE_HTTPD_ENCODE_URL_STR(const char *url_for_encode;)
     2021    USE_FEATURE_HTTPD_SETUID(const char *s_ugid = NULL;)
     2022    USE_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;)
     2023    USE_FEATURE_HTTPD_AUTH_MD5(const char *pass;)
     2024
     2025    INIT_G();
     2026
     2027#if ENABLE_LOCALE_SUPPORT
     2028    /* Undo busybox.c: we want to speak English in http (dates etc) */
     2029    setlocale(LC_TIME, "C");
     2030#endif
     2031
     2032    home_httpd = xrealloc_getcwd_or_warn(NULL);
     2033    /* -v counts, -i implies -f */
     2034    opt_complementary = "vv:if";
     2035    /* We do not "absolutize" path given by -h (home) opt.
     2036     * If user gives relative path in -h, $SCRIPT_FILENAME can end up
     2037     * relative too. */
     2038    opt = getopt32(argv, "c:d:h:"
     2039            USE_FEATURE_HTTPD_ENCODE_URL_STR("e:")
     2040            USE_FEATURE_HTTPD_BASIC_AUTH("r:")
     2041            USE_FEATURE_HTTPD_AUTH_MD5("m:")
     2042            USE_FEATURE_HTTPD_SETUID("u:")
     2043            "p:ifv",
     2044            &configFile, &url_for_decode, &home_httpd
    19582045            USE_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode)
    1959             USE_FEATURE_HTTPD_BASIC_AUTH(, &(config->realm))
     2046            USE_FEATURE_HTTPD_BASIC_AUTH(, &g_realm)
    19602047            USE_FEATURE_HTTPD_AUTH_MD5(, &pass)
    1961             USE_FEATURE_HTTPD_SETUID(, &s_uid)
    1962             USE_FEATURE_HTTPD_WITHOUT_INETD(, &s_port)
    1963     );
    1964 
    1965   if(opt & OPT_DECODE_URL) {
    1966       printf("%s", decodeString(url_for_decode, 1));
    1967       return 0;
    1968   }
    1969 #ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
    1970   if(opt & OPT_ENCODE_URL) {
    1971       printf("%s", encodeString(url_for_encode));
    1972       return 0;
    1973   }
    1974 #endif
    1975 #ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
    1976   if(opt & OPT_MD5) {
    1977       printf("%s\n", pw_encrypt(pass, "$1$"));
    1978       return 0;
    1979   }
    1980 #endif
    1981 #ifdef CONFIG_FEATURE_HTTPD_WITHOUT_INETD
    1982     if(opt & OPT_PORT)
    1983     config->port = bb_xgetlarg(s_port, 10, 1, 0xffff);
    1984 #ifdef CONFIG_FEATURE_HTTPD_SETUID
    1985     if(opt & OPT_SETUID) {
    1986     char *e;
    1987 
    1988     uid = strtol(s_uid, &e, 0);
    1989     if(*e != '\0') {
    1990         /* not integer */
    1991         uid = bb_xgetpwnam(s_uid);
    1992     }
    1993       }
    1994 #endif
    1995 #endif
    1996 
    1997   bb_xchdir(home_httpd);
    1998 #ifdef CONFIG_FEATURE_HTTPD_WITHOUT_INETD
    1999   server = openServer();
    2000 # ifdef CONFIG_FEATURE_HTTPD_SETUID
    2001   /* drop privileges */
    2002   if(uid > 0)
    2003     setuid(uid);
    2004 # endif
    2005 #endif
    2006 
    2007 #ifdef CONFIG_FEATURE_HTTPD_CGI
    2008    {
    2009     char *p = getenv("PATH");
    2010     if(p) {
    2011         p = bb_xstrdup(p);
    2012     }
    2013     clearenv();
    2014     if(p)
    2015         setenv("PATH", p, 1);
    2016 # ifdef CONFIG_FEATURE_HTTPD_WITHOUT_INETD
    2017     addEnvPort("SERVER");
    2018 # endif
    2019    }
    2020 #endif
    2021 
    2022 #ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
    2023   sighup_handler(0);
     2048            USE_FEATURE_HTTPD_SETUID(, &s_ugid)
     2049            , &bind_addr_or_port
     2050            , &verbose
     2051        );
     2052    if (opt & OPT_DECODE_URL) {
     2053        fputs(decodeString(url_for_decode, 1), stdout);
     2054        return 0;
     2055    }
     2056#if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
     2057    if (opt & OPT_ENCODE_URL) {
     2058        fputs(encodeString(url_for_encode), stdout);
     2059        return 0;
     2060    }
     2061#endif
     2062#if ENABLE_FEATURE_HTTPD_AUTH_MD5
     2063    if (opt & OPT_MD5) {
     2064        puts(pw_encrypt(pass, "$1$"));
     2065        return 0;
     2066    }
     2067#endif
     2068#if ENABLE_FEATURE_HTTPD_SETUID
     2069    if (opt & OPT_SETUID) {
     2070        if (!get_uidgid(&ugid, s_ugid, 1))
     2071            bb_error_msg_and_die("unrecognized user[:group] "
     2072                        "name '%s'", s_ugid);
     2073    }
     2074#endif
     2075
     2076#if !BB_MMU
     2077    if (!(opt & OPT_FOREGROUND)) {
     2078        bb_daemonize_or_rexec(0, argv); /* don't change current directory */
     2079    }
     2080#endif
     2081
     2082    xchdir(home_httpd);
     2083    if (!(opt & OPT_INETD)) {
     2084        signal(SIGCHLD, SIG_IGN);
     2085        server_socket = openServer();
     2086#if ENABLE_FEATURE_HTTPD_SETUID
     2087        /* drop privileges */
     2088        if (opt & OPT_SETUID) {
     2089            if (ugid.gid != (gid_t)-1) {
     2090                if (setgroups(1, &ugid.gid) == -1)
     2091                    bb_perror_msg_and_die("setgroups");
     2092                xsetgid(ugid.gid);
     2093            }
     2094            xsetuid(ugid.uid);
     2095        }
     2096#endif
     2097    }
     2098
     2099#if ENABLE_FEATURE_HTTPD_CGI
     2100    {
     2101        char *p = getenv("PATH");
     2102        /* env strings themself are not freed, no need to strdup(p): */
     2103        clearenv();
     2104        if (p)
     2105            putenv(p - 5);
     2106//      if (!(opt & OPT_INETD))
     2107//          setenv_long("SERVER_PORT", ???);
     2108    }
     2109#endif
     2110
     2111#if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
     2112    if (!(opt & OPT_INETD))
     2113        sighup_handler(0);
     2114    else /* do not install HUP handler in inetd mode */
     2115#endif
     2116        parse_conf(default_path_httpd_conf, FIRST_PARSE);
     2117
     2118    xfunc_error_retval = 0;
     2119    if (opt & OPT_INETD)
     2120        mini_httpd_inetd();
     2121#if BB_MMU
     2122    if (!(opt & OPT_FOREGROUND))
     2123        bb_daemonize(0); /* don't change current directory */
     2124    mini_httpd(server_socket); /* never returns */
    20242125#else
    2025   parse_conf(default_path_httpd_conf, FIRST_PARSE);
    2026 #endif
    2027 
    2028 #ifdef CONFIG_FEATURE_HTTPD_WITHOUT_INETD
    2029 # if !DEBUG
    2030   bb_xdaemon(1, 0);     /* don`t change curent directory */
    2031 # endif
    2032   return miniHttpd(server);
    2033 #else
    2034   return miniHttpd();
    2035 #endif
    2036 }
     2126    mini_httpd_nommu(server_socket, argc, argv); /* never returns */
     2127#endif
     2128    /* return 0; */
     2129}
Note: See TracChangeset for help on using the changeset viewer.