Ignore:
Timestamp:
Nov 6, 2007, 11:01:53 AM (16 years ago)
Author:
Bruno Cornec
Message:
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

File:
1 edited

Legend:

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

    r902 r1770  
    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.