X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;ds=sidebyside;f=networking%2Fhttpd.c;h=ba65e13611c5521451e3859d6347f55c7ec03307;hb=0d6d88a2058d191c34d25a8709aca40311bb0c2e;hp=eb03f34c0d8d27f5c5b2df1e8d7156404822b6b8;hpb=b65422cf652c3f04cf6edd4c6050186df25e844c;p=oweals%2Fbusybox.git diff --git a/networking/httpd.c b/networking/httpd.c index eb03f34c0..ba65e1361 100644 --- a/networking/httpd.c +++ b/networking/httpd.c @@ -33,11 +33,7 @@ * * When a url contains "cgi-bin" it is assumed to be a cgi script. The * server changes directory to the location of the script and executes it - * after setting QUERY_STRING and other environment variables. If url args - * are included in the url or as a post, the args are placed into decoded - * environment variables. e.g. /cgi-bin/setup?foo=Hello%20World will set - * the $CGI_foo environment variable to "Hello World" while - * CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV enabled. + * after setting QUERY_STRING and other environment variables. * * The server can also be invoked as a url arg decoder and html text encoder * as follows: @@ -120,7 +116,7 @@ #include "busybox.h" -static const char httpdVersion[] = "busybox httpd/1.30 7-Sep-2003"; +static const char httpdVersion[] = "busybox httpd/1.34 2-Oct-2003"; static const char default_path_httpd_conf[] = "/etc"; static const char httpd_conf[] = "httpd.conf"; static const char home[] = "./"; @@ -131,7 +127,6 @@ static const char home[] = "./"; # define cont_l_fmt "%ld" #endif - // Note: bussybox xfuncs are not used because we want the server to keep running // if something bad happens due to a malformed user request. // As a result, all memory allocation after daemonize @@ -142,7 +137,6 @@ static const char home[] = "./"; /* Configure options, disabled by default as custom httpd feature */ /* disabled as optional features */ -//#define CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV //#define CONFIG_FEATURE_HTTPD_ENCODE_URL_STR //#define CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV //#define CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES @@ -166,7 +160,6 @@ static const char home[] = "./"; /* unset config option for remove warning as redefined */ #undef CONFIG_FEATURE_HTTPD_BASIC_AUTH #undef CONFIG_FEATURE_HTTPD_AUTH_MD5 -#undef CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV #undef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR #undef CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV #undef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES @@ -176,7 +169,6 @@ static const char home[] = "./"; /* enable all features now */ #define CONFIG_FEATURE_HTTPD_BASIC_AUTH #define CONFIG_FEATURE_HTTPD_AUTH_MD5 -#define CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV #define CONFIG_FEATURE_HTTPD_ENCODE_URL_STR #define CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV #define CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES @@ -202,15 +194,6 @@ void bb_show_usage(void) #undef DEBUG #endif -/* CGI environ size */ -#ifdef CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV -#define ENVSIZE 70 /* set max CGI variable */ -#else -#define ENVSIZE 15 /* minimal requires */ -#endif - -#define MAX_POST_SIZE (64*1024) /* 64k. Its Small? May be ;) */ - #define MAX_MEMORY_BUFF 8192 /* IO buffer */ typedef struct HT_ACCESS { @@ -228,15 +211,17 @@ typedef struct HT_ACCESS_IP { typedef struct { -#ifdef CONFIG_FEATURE_HTTPD_CGI - char *envp[ENVSIZE+1]; - int envCount; -#endif char buf[MAX_MEMORY_BUFF]; #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH const char *realm; + char *remoteuser; +#endif + +#ifdef CONFIG_FEATURE_HTTPD_CGI + char *referer; #endif + const char *configFile; unsigned int rmt_ip; @@ -485,9 +470,10 @@ static void free_config_lines(Htaccess **pprev) static void parse_conf(const char *path, int flag) { FILE *f; - Htaccess *cur; #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH - Htaccess *prev; + Htaccess *prev, *cur; +#elif CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES + Htaccess *cur; #endif const char *cf = config->configFile; @@ -659,20 +645,17 @@ static void parse_conf(const char *path, int flag) c = strchr(cf, ':'); *c++ = 0; cur->after_colon = c; -#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH - if(*cf == '/') - free(p0); -#endif #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES - else if(*cf == '.') { + if(*cf == '.') { /* config .mime line move top for overwrite previous */ cur->next = config->mime_a; config->mime_a = cur; + continue; } #endif - #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH - else if(prev == NULL) { + free(p0); + if(prev == NULL) { /* first line */ config->auth = prev = cur; } else { @@ -701,9 +684,9 @@ static void parse_conf(const char *path, int flag) prev = cur; } } -#endif #endif } +#endif } fclose(f); } @@ -809,19 +792,16 @@ static char *decodeString(char *orig, int flag_plus_to_space) static void addEnv(const char *name_before_underline, const char *name_after_underline, const char *value) { - char *s; + char *s = NULL; const char *underline; - if (config->envCount >= ENVSIZE) - return; if (!value) value = ""; underline = *name_after_underline ? "_" : ""; asprintf(&s, "%s%s%s=%s", name_before_underline, underline, name_after_underline, value); if(s) { - config->envp[config->envCount++] = s; - config->envp[config->envCount] = 0; + putenv(s); } } @@ -837,58 +817,6 @@ static void addEnvPort(const char *port_name) #endif #endif /* CONFIG_FEATURE_HTTPD_CGI */ -#ifdef CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV -/**************************************************************************** - * - > $Function: addEnvCgi - * - * $Description: Create environment variables given a URL encoded arg list. - * For each variable setting the URL encoded arg list, create a corresponding - * environment variable. URL encoded arguments have the form - * name1=value1&name2=value2&name3=&ignores - * from this example, name3 set empty value, tail without '=' skiping - * - * $Parameters: - * (char *) pargs . . . . A pointer to the URL encoded arguments. - * - * $Return: None - * - * $Errors: None - * - ****************************************************************************/ -static void addEnvCgi(const char *pargs) -{ - char *args; - char *memargs; - char *namelist; /* space separated list of arg names */ - if (pargs==0) return; - - /* args are a list of name=value&name2=value2 sequences */ - namelist = (char *) malloc(strlen(pargs)); - if (namelist) namelist[0]=0; - memargs = args = strdup(pargs); - while (args && *args) { - const char *name = args; - char *value = strchr(args, '='); - - if (!value) /* &XXX without '=' */ - break; - *value++ = 0; - args = strchr(value, '&'); - if (args) - *args++ = 0; - addEnv("CGI", name, decodeString(value, 1)); - if (*namelist) strcat(namelist, " "); - strcat(namelist, name); - } - free(memargs); - if (namelist) { - addEnv("CGI", "ARGLIST_", namelist); - free(namelist); - } -} -#endif /* CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV */ - #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH /**************************************************************************** @@ -1073,15 +1001,13 @@ static int sendHeaders(HttpResponseNum responseNum) * * Characters are read one at a time until an eol sequence is found. * - * $Parameters: - * (char *) buf . . Where to place the read result. - * * $Return: (int) . . . . number of characters read. -1 if error. * ****************************************************************************/ -static int getLine(char *buf) +static int getLine(void) { int count = 0; + char *buf = config->buf; while (read(a_c_r, buf + count, 1) == 1) { if (buf[count] == '\r') continue; @@ -1108,11 +1034,11 @@ static int getLine(char *buf) * data in addition to setting the QUERY_STRING variable (for GETs or POSTs). * * $Parameters: - * (const char *) url . . . The requested URL (with leading /). - * (const char *urlArgs). . Any URL arguments. - * (const char *body) . . . POST body contents. - * (int bodyLen) . . . . . Length of the post body. - * (const char *cookie) . . For set HTTP_COOKIE. + * (const char *) url . . . . . . The requested URL (with leading /). + * (const char *urlArgs). . . . . Any URL arguments. + * (int bodyLen) . . . . . . . . Length of the post body. + * (const char *cookie) . . . . . For set HTTP_COOKIE. + * (const char *content_type) . . For set CONTENT_TYPE. * * $Return: (char *) . . . . A pointer to the decoded string (same as input). @@ -1122,7 +1048,8 @@ static int getLine(char *buf) ****************************************************************************/ static int sendCgi(const char *url, const char *request, const char *urlArgs, - const char *body, int bodyLen, const char *cookie) + int bodyLen, const char *cookie, + const char *content_type) { int fromCgi[2]; /* pipe for reading data from CGI */ int toCgi[2]; /* pipe for sending data to CGI */ @@ -1207,28 +1134,26 @@ static int sendCgi(const char *url, addEnv("SERVER", "SOFTWARE", httpdVersion); addEnv("SERVER", "PROTOCOL", "HTTP/1.0"); addEnv("GATEWAY_INTERFACE", "", "CGI/1.1"); -#ifdef CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV addEnv("REMOTE", "ADDR", config->rmt_ip_str); +#ifdef CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV addEnvPort("REMOTE"); -#else - addEnv("REMOTE_ADDR", "", config->rmt_ip_str); #endif if(bodyLen) { char sbl[32]; sprintf(sbl, "%d", bodyLen); - addEnv("CONTENT_LENGTH", "", sbl); + addEnv("CONTENT", "LENGTH", sbl); } if(cookie) - addEnv("HTTP_COOKIE", "", cookie); - -#ifdef CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV - if (request != request_GET) { - addEnvCgi(body); - } else { - addEnvCgi(urlArgs); + addEnv("HTTP", "COOKIE", cookie); + if(content_type) + addEnv("CONTENT", "TYPE", content_type); + if(config->remoteuser) { + addEnv("REMOTE", "USER", config->remoteuser); + addEnv("AUTH_TYPE", "", "Basic"); } -#endif + if(config->referer) + addEnv("HTTP", "REFERER", config->referer); /* set execve argp[0] without path */ argp[0] = strrchr( purl, '/' ) + 1; @@ -1242,7 +1167,7 @@ static int sendCgi(const char *url, // now run the program. If it fails, // use _exit() so no destructors // get called and make a mess. - execve(realpath_buff, argp, config->envp); + execv(realpath_buff, argp); } } } @@ -1258,28 +1183,41 @@ static int sendCgi(const char *url, if (pid) { /* parent process */ int status; + size_t post_readed_size = 0, post_readed_idx = 0; inFd = fromCgi[0]; outFd = toCgi[1]; close(fromCgi[1]); close(toCgi[0]); - if (body) bb_full_write(outFd, body, bodyLen); - close(outFd); + signal(SIGPIPE, SIG_IGN); while (1) { - struct timeval timeout; fd_set readSet; - char buf[160]; + fd_set writeSet; + char wbuf[128]; int nfound; int count; FD_ZERO(&readSet); + FD_ZERO(&writeSet); FD_SET(inFd, &readSet); - + if(bodyLen > 0 || post_readed_size > 0) { + FD_SET(outFd, &writeSet); + nfound = outFd > inFd ? outFd : inFd; + if(post_readed_size == 0) { + FD_SET(a_c_r, &readSet); + if(nfound < a_c_r) + nfound = a_c_r; + } /* Now wait on the set of sockets! */ - timeout.tv_sec = 0; - timeout.tv_usec = 10000; - nfound = select(inFd + 1, &readSet, 0, 0, &timeout); + nfound = select(nfound + 1, &readSet, &writeSet, 0, NULL); + } else { + if(!bodyLen) { + close(outFd); + bodyLen = -1; + } + nfound = select(inFd + 1, &readSet, 0, 0, NULL); + } if (nfound <= 0) { if (waitpid(pid, &status, WNOHANG) > 0) { @@ -1292,29 +1230,46 @@ static int sendCgi(const char *url, bb_error_msg("piped has exited with signal=%d", WTERMSIG(status)); } #endif - pid = -1; break; } + } else if(post_readed_size > 0 && FD_ISSET(outFd, &writeSet)) { + count = bb_full_write(outFd, wbuf + post_readed_idx, post_readed_size); + if(count > 0) { + post_readed_size -= count; + post_readed_idx += count; + if(post_readed_size == 0) + post_readed_idx = 0; + } + } else if(bodyLen > 0 && post_readed_size == 0 && FD_ISSET(a_c_r, &readSet)) { + count = bodyLen > sizeof(wbuf) ? sizeof(wbuf) : bodyLen; + count = bb_full_read(a_c_r, wbuf, count); + if(count > 0) { + post_readed_size += count; + bodyLen -= count; } else { + bodyLen = 0; /* closed */ + } + } else if(FD_ISSET(inFd, &readSet)) { int s = a_c_w; + char *rbuf = config->buf; // There is something to read - count = bb_full_read(inFd, buf, sizeof(buf)-1); - // If a read returns 0 at this point then some type of error has - // occurred. Bail now. - if (count == 0) break; + count = bb_full_read(inFd, rbuf, MAX_MEMORY_BUFF-1); + if (count == 0) + break; /* closed */ if (count > 0) { if (firstLine) { + rbuf[count] = 0; /* check to see if the user script added headers */ - if (strncmp(buf, "HTTP/1.0 200 OK\n", 4) != 0) { + if(strncmp(rbuf, "HTTP/1.0 200 OK\n", 4) != 0) { bb_full_write(s, "HTTP/1.0 200 OK\n", 16); } - if (strstr(buf, "ontent-") == 0) { + if (strstr(rbuf, "ontent-") == 0) { bb_full_write(s, "Content-type: text/plain\n\n", 26); } - firstLine=0; + firstLine = 0; } - bb_full_write(s, buf, count); + bb_full_write(s, rbuf, count); #ifdef DEBUG if (config->debugHttpd) fprintf(stderr, "cgi read %d bytes\n", count); @@ -1335,12 +1290,11 @@ static int sendCgi(const char *url, * * $Parameter: * (const char *) url . . The URL requested. - * (char *) buf . . . . . The stack buffer. * * $Return: (int) . . . . . . Always 0. * ****************************************************************************/ -static int sendFile(const char *url, char *buf) +static int sendFile(const char *url) { char * suffix; int f; @@ -1379,6 +1333,7 @@ static int sendFile(const char *url, char *buf) f = open(url, O_RDONLY); if (f >= 0) { int count; + char *buf = config->buf; sendHeaders(HTTP_OK); while ((count = bb_full_read(f, buf, MAX_MEMORY_BUFF)) > 0) { @@ -1465,20 +1420,21 @@ static int checkPerm(const char *path, const char *request) if(strncmp(p0, path, l) == 0 && (l == 1 || path[l] == '/' || path[l] == 0)) { + char *u; /* path match found. Check request */ - /* for check next /path:user:password */ prev = p0; + u = strchr(request, ':'); + if(u == NULL) { + /* bad request, ':' required */ + break; + } + #ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5 { char *cipher; char *pp; - char *u = strchr(request, ':'); - if(u == NULL) { - /* bad request, ':' required */ - continue; - } if(strncmp(p, request, u-request) != 0) { /* user uncompared */ continue; @@ -1489,14 +1445,21 @@ static int checkPerm(const char *path, const char *request) pp++; cipher = pw_encrypt(u+1, pp); if (strcmp(cipher, pp) == 0) - return 1; /* Ok */ + goto set_remoteuser_var; /* Ok */ /* unauthorized */ continue; } } #endif - if (strcmp(p, request) == 0) + if (strcmp(p, request) == 0) { +#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5 +set_remoteuser_var: +#endif + config->remoteuser = strdup(request); + if(config->remoteuser) + config->remoteuser[(u - request)] = 0; return 1; /* Ok */ + } /* unauthorized */ } } @@ -1524,9 +1487,9 @@ static void handleIncoming(void) char *urlArgs; #ifdef CONFIG_FEATURE_HTTPD_CGI const char *prequest = request_GET; - char *body = 0; long length=0; char *cookie = 0; + char *content_type = 0; #endif char *test; struct stat sb; @@ -1539,7 +1502,7 @@ static void handleIncoming(void) do { int count; - if (getLine(buf) <= 0) + if (getLine() <= 0) break; /* closed */ purl = strpbrk(buf, " \t"); @@ -1637,7 +1600,7 @@ BAD_REQUEST: } // read until blank line for HTTP version specified, else parse immediate - while (blank >= 0 && (count = getLine(buf)) > 0) { + while (blank >= 0 && (count = getLine()) > 0) { #ifdef DEBUG if (config->debugHttpd) fprintf(stderr, "Header: '%s'\n", buf); @@ -1652,6 +1615,14 @@ BAD_REQUEST: for(test = buf + 7; isspace(*test); test++) ; cookie = strdup(test); + } else if ((strncasecmp(buf, "Content-Type:", 13) == 0)) { + for(test = buf + 13; isspace(*test); test++) + ; + content_type = strdup(test); + } else if ((strncasecmp(buf, "Referer:", 8) == 0)) { + for(test = buf + 8; isspace(*test); test++) + ; + config->referer = strdup(test); } #endif @@ -1695,23 +1666,13 @@ FORBIDDEN: /* protect listing /cgi-bin */ #ifdef CONFIG_FEATURE_HTTPD_CGI /* if strange Content-Length */ - if (length < 0 || length > MAX_POST_SIZE) + if (length < 0) break; - if (length > 0) { - body = malloc(length + 1); - if (body) { - length = bb_full_read(a_c_r, body, length); - if(length < 0) // closed - length = 0; - body[length] = 0; // always null terminate for safety - } - } - if (strncmp(test, "cgi-bin", 7) == 0) { if(test[7] == '/' && test[8] == 0) goto FORBIDDEN; // protect listing cgi-bin/ - sendCgi(url, prequest, urlArgs, body, length, cookie); + sendCgi(url, prequest, urlArgs, length, cookie, content_type); } else { if (prequest != request_GET) sendHeaders(HTTP_NOT_IMPLEMENTED); @@ -1723,7 +1684,7 @@ FORBIDDEN: /* protect listing /cgi-bin */ config->ContentLength = sb.st_size; config->last_mod = sb.st_mtime; } - sendFile(test, buf); + sendFile(test); #ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY /* unset if non inetd looped */ config->ContentLength = -1; @@ -1743,8 +1704,10 @@ FORBIDDEN: /* protect listing /cgi-bin */ if (config->debugHttpd) fprintf(stderr, "closing socket\n"); # endif # ifdef CONFIG_FEATURE_HTTPD_CGI - free(body); free(cookie); + free(content_type); + free(config->remoteuser); + free(config->referer); # endif shutdown(a_c_w, SHUT_WR); shutdown(a_c_r, SHUT_RD); @@ -2008,9 +1971,21 @@ int httpd_main(int argc, char *argv[]) if(uid > 0) setuid(uid); # endif -# ifdef CONFIG_FEATURE_HTTPD_CGI - addEnvPort("SERVER"); +#endif + +#ifdef CONFIG_FEATURE_HTTPD_CGI + { + char *p = getenv("PATH"); + if(p) { + p = bb_xstrdup(p); + } + clearenv(); + if(p) + setenv("PATH", p, 1); +# ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY + addEnvPort("SERVER"); # endif + } #endif #ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP