* server changes directory to the location of the script and executes it
* after setting QUERY_STRING and other environment variables.
*
+ * Doc:
+ * "CGI Environment Variables": http://hoohoo.ncsa.uiuc.edu/cgi/env.html
+ *
* The server can also be invoked as a url arg decoder and html text encoder
* as follows:
* foo=`httpd -d $foo` # decode "Hello%20World" as "Hello World"
*
*/
+#include "libbb.h"
-#include "busybox.h"
-
+/* amount of buffering in a pipe */
+#ifndef PIPE_BUF
+# define PIPE_BUF 4096
+#endif
-static const char httpdVersion[] = "busybox httpd/1.35 6-Oct-2004";
static const char default_path_httpd_conf[] = "/etc";
static const char httpd_conf[] = "httpd.conf";
-static const char home[] = "./";
#define TIMEOUT 60
// is checked rigorously
//#define DEBUG 1
-
-#ifndef DEBUG
-# define DEBUG 0
-#endif
+#define DEBUG 0
#define MAX_MEMORY_BUFF 8192 /* IO buffer */
} Htaccess;
typedef struct HT_ACCESS_IP {
- unsigned int ip;
- unsigned int mask;
+ unsigned ip;
+ unsigned mask;
int allow_deny;
struct HT_ACCESS_IP *next;
} Htaccess_IP;
-typedef struct {
- char buf[MAX_MEMORY_BUFF];
-
- USE_FEATURE_HTTPD_BASIC_AUTH(const char *realm;)
- USE_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;)
-
- const char *query;
-
- USE_FEATURE_HTTPD_CGI(char *referer;)
-
+struct globals {
+ int server_socket;
+ int accepted_socket;
+ volatile smallint alarm_signaled;
+ smallint flg_deny_all;
+ const char *g_query;
const char *configFile;
+ const char *home_httpd;
+ unsigned rmt_ip;
- unsigned int rmt_ip;
-#if ENABLE_FEATURE_HTTPD_CGI || DEBUG
- char rmt_ip_str[16]; /* for set env REMOTE_ADDR */
-#endif
- unsigned port; /* server initial port and for
- set env REMOTE_PORT */
const char *found_mime_type;
const char *found_moved_temporarily;
-
off_t ContentLength; /* -1 - unknown */
time_t last_mod;
-
Htaccess_IP *ip_a_d; /* config allow/deny lines */
- int flg_deny_all;
+
+ USE_FEATURE_HTTPD_BASIC_AUTH(const char *g_realm;)
+ USE_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;)
+ USE_FEATURE_HTTPD_CGI(char *referer;)
+
+#if ENABLE_FEATURE_HTTPD_CGI || DEBUG
+ char *rmt_ip_str; /* for set env REMOTE_ADDR */
+#endif
+ unsigned tcp_port; /* server initial port and for
+ set env REMOTE_PORT */
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
- Htaccess *auth; /* config user:password lines */
+ Htaccess *g_auth; /* config user:password lines */
#endif
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
Htaccess *mime_a; /* config mime types */
#endif
-
- int server_socket;
- int accepted_socket;
- volatile int alarm_signaled;
-
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
Htaccess *script_i; /* config script interpreters */
#endif
-} HttpdConfig;
-
-static HttpdConfig *config;
+ char iobuf[MAX_MEMORY_BUFF];
+};
+#define G (*ptr_to_globals)
+#define server_socket (G.server_socket )
+#define accepted_socket (G.accepted_socket)
+#define alarm_signaled (G.alarm_signaled )
+#define flg_deny_all (G.flg_deny_all )
+#define g_query (G.g_query )
+#define configFile (G.configFile )
+#define home_httpd (G.home_httpd )
+#define rmt_ip (G.rmt_ip )
+#define found_mime_type (G.found_mime_type)
+#define found_moved_temporarily (G.found_moved_temporarily)
+#define ContentLength (G.ContentLength )
+#define last_mod (G.last_mod )
+#define ip_a_d (G.ip_a_d )
+#define g_realm (G.g_realm )
+#define remoteuser (G.remoteuser )
+#define referer (G.referer )
+#if ENABLE_FEATURE_HTTPD_CGI || DEBUG
+#define rmt_ip_str (G.rmt_ip_str )
+#endif
+#define tcp_port (G.tcp_port )
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+#define g_auth (G.g_auth )
+#endif
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+#define mime_a (G.mime_a )
+#endif
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+#define script_i (G.script_i )
+#endif
+#define iobuf (G.iobuf )
+#define INIT_G() do { \
+ PTR_TO_GLOBALS = xzalloc(sizeof(G)); \
+ USE_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \
+ tcp_port = 80; \
+ ContentLength = -1; \
+} while (0)
static const char request_GET[] = "GET"; /* size algorithmic optimize */
#define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1)
-static int scan_ip(const char **ep, unsigned int *ip, unsigned char endc)
+static int scan_ip(const char **ep, unsigned *ip, unsigned char endc)
{
const char *p = *ep;
int auto_mask = 8;
*ip = 0;
for (j = 0; j < 4; j++) {
- unsigned int octet;
+ unsigned octet;
if ((*p < '0' || *p > '9') && (*p != '/' || j == 0) && *p != 0)
return -auto_mask;
return auto_mask;
}
-static int scan_ip_mask(const char *ipm, unsigned int *ip, unsigned int *mask)
+static int scan_ip_mask(const char *ipm, unsigned *ip, unsigned *mask)
{
int i;
- unsigned int msk;
+ unsigned msk;
i = scan_ip(&ipm, ip, '/');
if (i < 0)
return 0;
}
-#if ENABLE_FEATURE_HTTPD_BASIC_AUTH || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH \
+ || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \
+ || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
static void free_config_lines(Htaccess **pprev)
{
Htaccess *prev = *pprev;
{
FILE *f;
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
- Htaccess *prev, *cur;
-#elif ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+ Htaccess *prev;
+#endif
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH \
+ || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \
+ || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
Htaccess *cur;
#endif
- const char *cf = config->configFile;
+ const char *cf = configFile;
char buf[160];
char *p0 = NULL;
char *c, *p;
/* free previous ip setup if present */
- Htaccess_IP *pip = config->ip_a_d;
+ Htaccess_IP *pip = ip_a_d;
while (pip) {
Htaccess_IP *cur_ipl = pip;
pip = cur_ipl->next;
free(cur_ipl);
}
- config->ip_a_d = NULL;
+ ip_a_d = NULL;
- config->flg_deny_all = 0;
+ flg_deny_all = 0;
-#if ENABLE_FEATURE_HTTPD_BASIC_AUTH || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH \
+ || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \
+ || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
/* retain previous auth and mime config only for subdir parse */
if (flag != SUBDIR_PARSE) {
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
- free_config_lines(&config->auth);
+ free_config_lines(&g_auth);
#endif
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
- free_config_lines(&config->mime_a);
+ free_config_lines(&mime_a);
#endif
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
- free_config_lines(&config->script_i);
+ free_config_lines(&script_i);
#endif
}
#endif
/* config file not found, no changes to config */
return;
}
- if (config->configFile && flag == FIRST_PARSE) /* if -c option given */
+ if (configFile && flag == FIRST_PARSE) /* if -c option given */
bb_perror_msg_and_die("%s", cf);
flag = FIND_FROM_HTTPD_ROOT;
cf = httpd_conf;
}
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
- prev = config->auth;
+ prev = g_auth;
#endif
- /* This could stand some work */
+ /* This could stand some work */
while ((p0 = fgets(buf, sizeof(buf), f)) != NULL) {
c = NULL;
for (p = p0; *p0 != 0 && *p0 != '#'; p0++) {
if (*c == '*') {
if (*p0 == 'D') {
/* memorize deny all */
- config->flg_deny_all++;
+ flg_deny_all = 1;
}
/* skip default other "word:*" config lines */
continue;
continue;
if (*p0 == 'A' || *p0 == 'D') {
/* storing current config IP line */
- pip = calloc(1, sizeof(Htaccess_IP));
+ pip = xzalloc(sizeof(Htaccess_IP));
if (pip) {
if (scan_ip_mask(c, &(pip->ip), &(pip->mask))) {
/* syntax IP{/mask} error detected, protect all */
pip->allow_deny = *p0;
if (*p0 == 'D') {
/* Deny:form_IP move top */
- pip->next = config->ip_a_d;
- config->ip_a_d = pip;
+ pip->next = ip_a_d;
+ ip_a_d = pip;
} else {
/* add to bottom A:form_IP config line */
- Htaccess_IP *prev_IP = config->ip_a_d;
+ Htaccess_IP *prev_IP = ip_a_d;
if (prev_IP == NULL) {
- config->ip_a_d = pip;
+ ip_a_d = pip;
} else {
while (prev_IP->next)
prev_IP = prev_IP->next;
}
#endif
-#if ENABLE_FEATURE_HTTPD_BASIC_AUTH || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH \
+ || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \
+ || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
/* storing current config line */
- cur = calloc(1, sizeof(Htaccess) + strlen(p0));
+ cur = xzalloc(sizeof(Htaccess) + strlen(p0));
if (cur) {
cf = strcpy(cur->before_colon, p0);
c = strchr(cf, ':');
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
if (*cf == '.') {
/* config .mime line move top for overwrite previous */
- cur->next = config->mime_a;
- config->mime_a = cur;
+ cur->next = mime_a;
+ mime_a = cur;
continue;
}
#endif
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
if (*cf == '*' && cf[1] == '.') {
/* config script interpreter line move top for overwrite previous */
- cur->next = config->script_i;
- config->script_i = cur;
+ cur->next = script_i;
+ script_i = cur;
continue;
}
#endif
free(p0);
if (prev == NULL) {
/* first line */
- config->auth = prev = cur;
+ g_auth = prev = cur;
} else {
/* sort path, if current lenght eq or bigger then move up */
- Htaccess *prev_hti = config->auth;
+ Htaccess *prev_hti = g_auth;
size_t l = strlen(cf);
Htaccess *hti;
prev_hti->next = cur;
} else {
/* insert as top */
- config->auth = cur;
+ g_auth = cur;
}
break;
}
/* take the simple route and encode everything */
/* could possibly scan once to get length. */
int len = strlen(string);
- char *out = malloc(len * 6 + 1);
+ char *out = xmalloc(len * 6 + 1);
char *p = out;
char ch;
- if (!out) return "";
while ((ch = *string++)) {
// very simple check for what to encode
if (isalnum(ch)) *p++ = ch;
else p += sprintf(p, "&#%d;", (unsigned char) ch);
}
- *p = 0;
+ *p = '\0';
return out;
}
#endif /* FEATURE_HTTPD_ENCODE_URL_STR */
*
* $Parameters:
* (char *) string . . . The first string to decode.
- * (int) flag . . . 1 if require decode '+' as ' ' for CGI
+ * (int) option_d . . 1 if called for httpd -d
*
* $Return: (char *) . . . . A pointer to the decoded string (same as input).
*
* $Errors: None
*
****************************************************************************/
-static char *decodeString(char *orig, int flag_plus_to_space)
+static char *decodeString(char *orig, int option_d)
{
/* note that decoded string is always shorter than original */
char *string = orig;
char *ptr = string;
+ char c;
- while (*ptr) {
- if (*ptr == '+' && flag_plus_to_space) { *string++ = ' '; ptr++; }
- else if (*ptr != '%') *string++ = *ptr++;
- else {
- unsigned int value1, value2;
-
- ptr++;
- if (sscanf(ptr, "%1X", &value1) != 1 ||
- sscanf(ptr+1, "%1X", &value2) != 1) {
- if (!flag_plus_to_space)
- return NULL;
- *string++ = '%';
- } else {
- value1 = value1 * 16 + value2;
- if (value1 == '/' || value1 == 0)
- return orig+1;
- *string++ = value1;
- ptr += 2;
- }
+ while ((c = *ptr++) != '\0') {
+ unsigned value1, value2;
+
+ if (option_d && c == '+') {
+ *string++ = ' ';
+ continue;
+ }
+ if (c != '%') {
+ *string++ = c;
+ continue;
+ }
+ if (sscanf(ptr, "%1X", &value1) != 1
+ || sscanf(ptr+1, "%1X", &value2) != 1
+ ) {
+ if (!option_d)
+ return NULL;
+ *string++ = '%';
+ continue;
+ }
+ value1 = value1 * 16 + value2;
+ if (!option_d && (value1 == '/' || value1 == '\0')) {
+ /* caller takes it as indication of invalid
+ * (dangerous wrt exploits) chars */
+ return orig + 1;
}
+ *string++ = value1;
+ ptr += 2;
}
*string = '\0';
return orig;
****************************************************************************/
static void decodeBase64(char *Data)
{
-
const unsigned char *in = (const unsigned char *)Data;
// The decoded size will be at most 3/4 the size of the encoded
- unsigned long ch = 0;
+ unsigned ch = 0;
int i = 0;
while (*in) {
i = 0;
}
}
- *Data = 0;
+ *Data = '\0';
}
#endif
****************************************************************************/
static int openServer(void)
{
- struct sockaddr_in lsocket;
int fd;
- int on = 1;
/* create the socket right now */
- /* inet_addr() returns a value that is already in network order */
- memset(&lsocket, 0, sizeof(lsocket));
- lsocket.sin_family = AF_INET;
- lsocket.sin_addr.s_addr = INADDR_ANY;
- lsocket.sin_port = htons(config->port);
- fd = xsocket(AF_INET, SOCK_STREAM, 0);
- /* tell the OS it's OK to reuse a previous address even though */
- /* it may still be in a close down state. Allows bind to succeed. */
-#ifdef SO_REUSEPORT
- setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (void *)&on, sizeof(on));
-#else
- setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
-#endif
- xbind(fd, (struct sockaddr *)&lsocket, sizeof(lsocket));
+ fd = create_and_bind_stream_or_die(NULL, tcp_port);
xlisten(fd, 9);
- signal(SIGCHLD, SIG_IGN); /* prevent zombie (defunct) processes */
return fd;
}
****************************************************************************/
static int sendHeaders(HttpResponseNum responseNum)
{
- char *buf = config->buf;
+ char *buf = iobuf;
const char *responseString = "";
const char *infoString = 0;
const char *mime_type;
- unsigned int i;
+ unsigned i;
time_t timer = time(0);
char timeStr[80];
int len;
}
/* error message is HTML */
mime_type = responseNum == HTTP_OK ?
- config->found_mime_type : "text/html";
+ found_mime_type : "text/html";
/* emit the current date */
strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&timer));
len = sprintf(buf,
- "HTTP/1.0 %d %s\r\nContent-type: %s\r\n"
- "Date: %s\r\nConnection: close\r\n",
+ "HTTP/1.0 %d %s\r\nContent-type: %s\r\n"
+ "Date: %s\r\nConnection: close\r\n",
responseNum, responseString, mime_type, timeStr);
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
if (responseNum == HTTP_UNAUTHORIZED) {
- len += sprintf(buf+len, "WWW-Authenticate: Basic realm=\"%s\"\r\n",
- config->realm);
+ len += sprintf(buf+len,
+ "WWW-Authenticate: Basic realm=\"%s\"\r\n",
+ g_realm);
}
#endif
if (responseNum == HTTP_MOVED_TEMPORARILY) {
len += sprintf(buf+len, "Location: %s/%s%s\r\n",
- config->found_moved_temporarily,
- (config->query ? "?" : ""),
- (config->query ? config->query : ""));
+ found_moved_temporarily,
+ (g_query ? "?" : ""),
+ (g_query ? g_query : ""));
}
- if (config->ContentLength != -1) { /* file */
- strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&config->last_mod));
- len += sprintf(buf+len, "Last-Modified: %s\r\n%s %"OFF_FMT"\r\n",
- timeStr, "Content-length:", (off_t) config->ContentLength);
+ if (ContentLength != -1) { /* file */
+ strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&last_mod));
+ len += sprintf(buf+len, "Last-Modified: %s\r\n%s %"OFF_FMT"d\r\n",
+ timeStr, "Content-length:", ContentLength);
}
strcat(buf, "\r\n");
len += 2;
}
if (DEBUG)
fprintf(stderr, "headers: '%s'\n", buf);
- return full_write(config->accepted_socket, buf, len);
+ i = accepted_socket;
+ if (i == 0) i++; /* write to fd# 1 in inetd mode */
+ return full_write(i, buf, len);
}
/****************************************************************************
static int getLine(void)
{
int count = 0;
- char *buf = config->buf;
+ char *buf = iobuf;
- while (read(config->accepted_socket, buf + count, 1) == 1) {
+ while (read(accepted_socket, buf + count, 1) == 1) {
if (buf[count] == '\r') continue;
if (buf[count] == '\n') {
buf[count] = 0;
* (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).
*
{
int fromCgi[2]; /* pipe for reading data from CGI */
int toCgi[2]; /* pipe for sending data to CGI */
-
- static char * argp[] = { 0, 0 };
+ char *fullpath;
+ char *argp[] = { NULL, NULL };
int pid = 0;
int inFd;
int outFd;
- int firstLine = 1;
+ int buf_count;
int status;
- size_t post_readed_size, post_readed_idx;
+ size_t post_read_size, post_read_idx;
if (pipe(fromCgi) != 0)
return 0;
if (pipe(toCgi) != 0)
return 0;
+/*
+ * Note: We can use vfork() here in the no-mmu case, although
+ * the child modifies the parent's variables, due to:
+ * 1) The parent does not use the child-modified variables.
+ * 2) The allocated memory (in the child) is freed when the process
+ * exits. This happens instantly after the child finishes,
+ * since httpd is run from inetd (and it can't run standalone
+ * in uClinux).
+ */
+
+// FIXME: setenv leaks memory! (old values of env vars are leaked)
+// Thus we have a bug on NOMMU.
+// Need to use this instead:
+// [malloc +] putenv + (in child: exec) + (in parent: unsetenv [+ free])
+
+#if !BB_MMU
+ fullpath = NULL;
+ pid = vfork();
+#else
pid = fork();
+#endif
if (pid < 0)
return 0;
-
+
if (!pid) {
/* child process */
char *script;
- char *purl = strdup(url);
- char realpath_buff[MAXPATHLEN];
-
- if (purl == NULL)
- _exit(242);
+ char *purl;
- inFd = toCgi[0];
- outFd = fromCgi[1];
+ if (accepted_socket > 1)
+ close(accepted_socket);
+ if (server_socket > 1)
+ close(server_socket);
- dup2(inFd, 0); // replace stdin with the pipe
- dup2(outFd, 1); // replace stdout with the pipe
- if (!DEBUG)
- dup2(outFd, 2); // replace stderr with the pipe
-
- close(toCgi[0]);
- close(toCgi[1]);
+ xmove_fd(toCgi[0], 0); /* replace stdin with the pipe */
+ xmove_fd(fromCgi[1], 1); /* replace stdout with the pipe */
close(fromCgi[0]);
close(fromCgi[1]);
-
- close(config->accepted_socket);
- close(config->server_socket);
+ /* Huh? User seeing stderr can be a security problem...
+ * and if CGI really wants that, it can always dup2(1,2)...
+ * dup2(fromCgi[1], 2); */
/*
* Find PATH_INFO.
*/
+ xfunc_error_retval = 242;
+ purl = xstrdup(url);
script = purl;
while ((script = strchr(script + 1, '/')) != NULL) {
/* have script.cgi/PATH_INFO or dirs/script.cgi[/PATH_INFO] */
*script = '/'; /* is directory, find next '/' */
}
setenv1("PATH_INFO", script); /* set /PATH_INFO or "" */
- /* setenv1("PATH", getenv("PATH")); redundant */
setenv1("REQUEST_METHOD", request);
- if (config->query) {
- char *uri = alloca(strlen(purl) + 2 + strlen(config->query));
+ if (g_query) {
+ char *uri = alloca(strlen(purl) + 2 + strlen(g_query));
if (uri)
- sprintf(uri, "%s?%s", purl, config->query);
+ sprintf(uri, "%s?%s", purl, g_query);
setenv1("REQUEST_URI", uri);
} else {
setenv1("REQUEST_URI", purl);
}
if (script != NULL)
*script = '\0'; /* cut off /PATH_INFO */
- /* SCRIPT_FILENAME required by PHP in CGI mode */
- if (!realpath(purl + 1, realpath_buff))
- goto error_execing_cgi;
- setenv1("SCRIPT_FILENAME", realpath_buff);
+
+ /* SCRIPT_FILENAME required by PHP in CGI mode */
+ fullpath = concat_path_file(home_httpd, purl);
+ setenv1("SCRIPT_FILENAME", fullpath);
/* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */
setenv1("SCRIPT_NAME", purl);
- setenv1("QUERY_STRING", config->query);
- setenv1("SERVER_SOFTWARE", httpdVersion);
- putenv("SERVER_PROTOCOL=HTTP/1.0");
- putenv("GATEWAY_INTERFACE=CGI/1.1");
- setenv1("REMOTE_ADDR", config->rmt_ip_str);
+ /* http://hoohoo.ncsa.uiuc.edu/cgi/env.html:
+ * QUERY_STRING: The information which follows the ? in the URL
+ * which referenced this script. This is the query information.
+ * It should not be decoded in any fashion. This variable
+ * should always be set when there is query information,
+ * regardless of command line decoding. */
+ /* (Older versions of bbox seem to do some decoding) */
+ setenv1("QUERY_STRING", g_query);
+ putenv((char*)"SERVER_SOFTWARE=busybox httpd/"BB_VER);
+ putenv((char*)"SERVER_PROTOCOL=HTTP/1.0");
+ putenv((char*)"GATEWAY_INTERFACE=CGI/1.1");
+ /* Having _separate_ variables for IP and port defeats
+ * the purpose of having socket abstraction. Which "port"
+ * are you using on Unix domain socket?
+ * IOW - REMOTE_PEER="1.2.3.4:56" makes much more sense.
+ * Oh well... */
+ {
+ char *p = rmt_ip_str ? : (char*)"";
+ char *cp = strrchr(p, ':');
+ if (ENABLE_FEATURE_IPV6 && cp && strchr(cp, ']'))
+ cp = NULL;
+ if (cp) *cp = '\0'; /* delete :PORT */
+ setenv1("REMOTE_ADDR", p);
+ if (cp) *cp = ':';
+ }
#if ENABLE_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
- setenv_long("REMOTE_PORT", config->port);
+ setenv_long("REMOTE_PORT", tcp_port);
#endif
if (bodyLen)
setenv_long("CONTENT_LENGTH", bodyLen);
if (content_type)
setenv1("CONTENT_TYPE", content_type);
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
- if (config->remoteuser) {
- setenv1("REMOTE_USER", config->remoteuser);
- putenv("AUTH_TYPE=Basic");
+ if (remoteuser) {
+ setenv1("REMOTE_USER", remoteuser);
+ putenv((char*)"AUTH_TYPE=Basic");
}
#endif
- if (config->referer)
- setenv1("HTTP_REFERER", config->referer);
+ if (referer)
+ setenv1("HTTP_REFERER", referer);
/* set execve argp[0] without path */
argp[0] = strrchr(purl, '/') + 1;
/* but script argp[0] must have absolute path and chdiring to this */
- script = strrchr(realpath_buff, '/');
+ script = strrchr(fullpath, '/');
if (!script)
goto error_execing_cgi;
*script = '\0';
- if (chdir(realpath_buff) == 0) {
- // now run the program. If it fails,
+ if (chdir(fullpath) == 0) {
+ // Now run the program. If it fails,
// use _exit() so no destructors
// get called and make a mess.
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
if (suffix) {
Htaccess *cur;
- for (cur = config->script_i; cur; cur = cur->next) {
+ for (cur = script_i; cur; cur = cur->next) {
if (strcmp(cur->before_colon + 1, suffix) == 0) {
interpr = cur->after_colon;
break;
execv(interpr, argp);
else
#endif
- execv(realpath_buff, argp);
+ execv(fullpath, argp);
}
error_execing_cgi:
/* send to stdout (even if we are not from inetd) */
- config->accepted_socket = 1;
+ accepted_socket = 1;
sendHeaders(HTTP_NOT_FOUND);
_exit(242);
} /* end child */
/* parent process */
+#if !BB_MMU
+ free(fullpath);
+#endif
- post_readed_size = 0;
- post_readed_idx = 0;
+ buf_count = 0;
+ post_read_size = 0;
+ post_read_idx = 0; /* for gcc */
inFd = fromCgi[0];
outFd = toCgi[1];
close(fromCgi[1]);
FD_ZERO(&readSet);
FD_ZERO(&writeSet);
FD_SET(inFd, &readSet);
- if (bodyLen > 0 || post_readed_size > 0) {
+ if (bodyLen > 0 || post_read_size > 0) {
FD_SET(outFd, &writeSet);
nfound = outFd > inFd ? outFd : inFd;
- if (post_readed_size == 0) {
- FD_SET(config->accepted_socket, &readSet);
- if (nfound < config->accepted_socket)
- nfound = config->accepted_socket;
+ if (post_read_size == 0) {
+ FD_SET(accepted_socket, &readSet);
+ if (nfound < accepted_socket)
+ nfound = accepted_socket;
}
/* Now wait on the set of sockets! */
- nfound = select(nfound + 1, &readSet, &writeSet, 0, NULL);
+ nfound = select(nfound + 1, &readSet, &writeSet, NULL, NULL);
} else {
if (!bodyLen) {
- close(outFd);
+ close(outFd); /* no more POST data to CGI */
bodyLen = -1;
}
- nfound = select(inFd + 1, &readSet, 0, 0, NULL);
+ nfound = select(inFd + 1, &readSet, NULL, NULL, NULL);
}
if (nfound <= 0) {
- if (waitpid(pid, &status, WNOHANG) > 0) {
- close(inFd);
- if (DEBUG && WIFEXITED(status))
- bb_error_msg("piped has exited with status=%d", WEXITSTATUS(status));
- if (DEBUG && WIFSIGNALED(status))
- bb_error_msg("piped has exited with signal=%d", WTERMSIG(status));
- break;
+ if (waitpid(pid, &status, WNOHANG) <= 0) {
+ /* Weird. CGI didn't exit and no fd's
+ * are ready, yet select returned?! */
+ continue;
}
- } else if (post_readed_size > 0 && FD_ISSET(outFd, &writeSet)) {
- count = full_write(outFd, wbuf + post_readed_idx, post_readed_size);
+ close(inFd);
+ if (DEBUG && WIFEXITED(status))
+ bb_error_msg("piped has exited with status=%d", WEXITSTATUS(status));
+ if (DEBUG && WIFSIGNALED(status))
+ bb_error_msg("piped has exited with signal=%d", WTERMSIG(status));
+ break;
+ }
+
+ if (post_read_size > 0 && FD_ISSET(outFd, &writeSet)) {
+ /* Have data from peer and can write to CGI */
+ // huh? why full_write? what if we will block?
+ // (imagine that CGI does not read its stdin...)
+ count = full_write(outFd, wbuf + post_read_idx, post_read_size);
if (count > 0) {
- post_readed_size -= count;
- post_readed_idx += count;
- if (post_readed_size == 0)
- post_readed_idx = 0;
+ post_read_idx += count;
+ post_read_size -= count;
} else {
- post_readed_size = post_readed_idx = bodyLen = 0; /* broken pipe to CGI */
+ post_read_size = bodyLen = 0; /* broken pipe to CGI */
}
- } else if (bodyLen > 0 && post_readed_size == 0 && FD_ISSET(config->accepted_socket, &readSet)) {
+ } else if (bodyLen > 0 && post_read_size == 0
+ && FD_ISSET(accepted_socket, &readSet)
+ ) {
+ /* We expect data, prev data portion is eaten by CGI
+ * and there *is* data to read from the peer
+ * (POSTDATA?) */
count = bodyLen > (int)sizeof(wbuf) ? (int)sizeof(wbuf) : bodyLen;
- count = safe_read(config->accepted_socket, wbuf, count);
+ count = safe_read(accepted_socket, wbuf, count);
if (count > 0) {
- post_readed_size += count;
+ post_read_size = count;
+ post_read_idx = 0;
bodyLen -= count;
} else {
bodyLen = 0; /* closed */
}
}
- if (FD_ISSET(inFd, &readSet)) {
- int s = config->accepted_socket;
- char *rbuf = config->buf;
-#ifndef PIPE_BUF
-# define PIPESIZE 4096 /* amount of buffering in a pipe */
-#else
-# define PIPESIZE PIPE_BUF
-#endif
+#define PIPESIZE PIPE_BUF
#if PIPESIZE >= MAX_MEMORY_BUFF
# error "PIPESIZE >= MAX_MEMORY_BUFF"
#endif
-
- /* There is something to read */
- count = safe_read(inFd, rbuf, PIPESIZE);
- if (count == 0)
- break; /* closed */
- if (count > 0) {
- if (firstLine) {
- rbuf[count] = 0;
- /* check to see if the user script added headers */
- if (strncmp(rbuf, "HTTP/1.0 200 OK\r\n", 4) != 0) {
- full_write(s, "HTTP/1.0 200 OK\r\n", 17);
+ if (FD_ISSET(inFd, &readSet)) {
+ /* There is something to read from CGI */
+ int s = accepted_socket;
+ char *rbuf = iobuf;
+
+ /* Are we still buffering CGI output? */
+ if (buf_count >= 0) {
+ static const char HTTP_200[] = "HTTP/1.0 200 OK\r\n";
+ /* Must use safe_read, not full_read, because
+ * CGI may output a few first bytes and then wait
+ * for POSTDATA without closing stdout.
+ * With full_read we may wait here forever. */
+ count = safe_read(inFd, rbuf + buf_count, PIPESIZE - 4);
+ if (count <= 0) {
+ /* eof (or error) and there was no "HTTP",
+ * so add one and write out the received data */
+ if (buf_count) {
+ full_write(s, HTTP_200, sizeof(HTTP_200)-1);
+ full_write(s, rbuf, buf_count);
+ }
+ break; /* closed */
+ }
+ buf_count += count;
+ count = 0;
+ if (buf_count >= 4) {
+ /* check to see if CGI added "HTTP" */
+ if (memcmp(rbuf, HTTP_200, 4) != 0) {
+ /* there is no "HTTP", do it ourself */
+ if (full_write(s, HTTP_200, sizeof(HTTP_200)-1) != sizeof(HTTP_200)-1)
+ break;
}
- /* Sometimes CGI is writing to pipe in small chunks
- * and we don't see Content-type (because the read
- * is too short) and we emit bogus "text/plain"!
- * Is it a bug or CGI *has to* write it in one piece? */
- if (strstr(rbuf, "ontent-") == 0) {
+ /* example of valid CGI without "Content-type:"
+ * echo -en "HTTP/1.0 302 Found\r\n"
+ * echo -en "Location: http://www.busybox.net\r\n"
+ * echo -en "\r\n"
+ if (!strstr(rbuf, "ontent-")) {
full_write(s, "Content-type: text/plain\r\n\r\n", 28);
}
- firstLine = 0;
+ */
+ count = buf_count;
+ buf_count = -1; /* buffering off */
}
- if (full_write(s, rbuf, count) != count)
- break;
-
- if (DEBUG)
- fprintf(stderr, "cgi read %d bytes: '%.*s'\n", count, count, rbuf);
+ } else {
+ count = safe_read(inFd, rbuf, PIPESIZE);
+ if (count <= 0)
+ break; /* eof (or error) */
}
- }
- }
+ if (full_write(s, rbuf, count) != count)
+ break;
+ if (DEBUG)
+ fprintf(stderr, "cgi read %d bytes: '%.*s'\n", count, count, rbuf);
+ } /* if (FD_ISSET(inFd)) */
+ } /* while (1) */
return 0;
}
#endif /* FEATURE_HTTPD_CGI */
break;
}
/* also, if not found, set default as "application/octet-stream"; */
- config->found_mime_type = table[1];
+ found_mime_type = table[1];
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
if (suffix) {
Htaccess * cur;
- for (cur = config->mime_a; cur; cur = cur->next) {
+ for (cur = mime_a; cur; cur = cur->next) {
if (strcmp(cur->before_colon, suffix) == 0) {
- config->found_mime_type = cur->after_colon;
+ found_mime_type = cur->after_colon;
break;
}
}
if (DEBUG)
fprintf(stderr, "sending file '%s' content-type: %s\n",
- url, config->found_mime_type);
+ url, found_mime_type);
f = open(url, O_RDONLY);
if (f >= 0) {
int count;
- char *buf = config->buf;
+ char *buf = iobuf;
sendHeaders(HTTP_OK);
/* TODO: sendfile() */
while ((count = full_read(f, buf, MAX_MEMORY_BUFF)) > 0) {
- if (full_write(config->accepted_socket, buf, count) != count)
+ int fd = accepted_socket;
+ if (fd == 0) fd++; /* write to fd# 1 in inetd mode */
+ if (full_write(fd, buf, count) != count)
break;
}
close(f);
Htaccess_IP * cur;
/* This could stand some work */
- for (cur = config->ip_a_d; cur; cur = cur->next) {
- if (DEBUG)
- fprintf(stderr, "checkPermIP: '%s' ? ", config->rmt_ip_str);
- if (DEBUG)
- fprintf(stderr, "'%u.%u.%u.%u/%u.%u.%u.%u'\n",
- (unsigned char)(cur->ip >> 24),
- (unsigned char)(cur->ip >> 16),
- (unsigned char)(cur->ip >> 8),
- cur->ip & 0xff,
- (unsigned char)(cur->mask >> 24),
- (unsigned char)(cur->mask >> 16),
- (unsigned char)(cur->mask >> 8),
- cur->mask & 0xff);
- if ((config->rmt_ip & cur->mask) == cur->ip)
+ for (cur = ip_a_d; cur; cur = cur->next) {
+#if ENABLE_FEATURE_HTTPD_CGI && DEBUG
+ fprintf(stderr, "checkPermIP: '%s' ? ", rmt_ip_str);
+#endif
+#if DEBUG
+ fprintf(stderr, "'%u.%u.%u.%u/%u.%u.%u.%u'\n",
+ (unsigned char)(cur->ip >> 24),
+ (unsigned char)(cur->ip >> 16),
+ (unsigned char)(cur->ip >> 8),
+ (unsigned char)(cur->ip),
+ (unsigned char)(cur->mask >> 24),
+ (unsigned char)(cur->mask >> 16),
+ (unsigned char)(cur->mask >> 8),
+ (unsigned char)(cur->mask)
+ );
+#endif
+ if ((rmt_ip & cur->mask) == cur->ip)
return cur->allow_deny == 'A'; /* Allow/Deny */
}
/* if unconfigured, return 1 - access from all */
- return !config->flg_deny_all;
+ return !flg_deny_all;
}
/****************************************************************************
const char *prev = NULL;
/* This could stand some work */
- for (cur = config->auth; cur; cur = cur->next) {
+ for (cur = g_auth; cur; cur = cur->next) {
size_t l;
p0 = cur->before_colon;
if (strcmp(p, request) == 0) {
set_remoteuser_var:
- config->remoteuser = strdup(request);
- if (config->remoteuser)
- config->remoteuser[(u - request)] = 0;
+ remoteuser = strdup(request);
+ if (remoteuser)
+ remoteuser[(u - request)] = 0;
return 1; /* Ok */
}
/* unauthorized */
static void handle_sigalrm(int sig)
{
sendHeaders(HTTP_REQUEST_TIMEOUT);
- config->alarm_signaled = sig;
+ alarm_signaled = 1;
}
/****************************************************************************
****************************************************************************/
static void handleIncoming(void)
{
- char *buf = config->buf;
+ char *buf = iobuf;
char *url;
char *purl;
int blank = -1;
strcpy(url, buf);
/* extract url args if present */
test = strchr(url, '?');
- config->query = NULL;
+ g_query = NULL;
if (test) {
*test++ = '\0';
- config->query = test;
+ g_query = test;
}
test = decodeString(url, 0);
if (test == NULL)
goto BAD_REQUEST;
- /* FIXME: bug? should be "url+1"? */
- if (test == (buf+1)) {
+ if (test == url+1) {
+ /* '/' or NUL is encoded */
sendHeaders(HTTP_NOT_FOUND);
break;
}
/* algorithm stolen from libbb bb_simplify_path(),
- but don't strdup and reducing trailing slash and protect out root */
+ * but don't strdup and reducing trailing slash and protect out root */
purl = test = url;
do {
if (*purl == '/') {
}
if (*test == '.') {
/* skip extra '.' */
- if (test[1] == '/' || test[1] == 0) {
+ if (test[1] == '/' || !test[1]) {
continue;
- } else
+ }
/* '..': be careful */
- if (test[1] == '.' && (test[2] == '/' || test[2] == 0)) {
+ if (test[1] == '.' && (test[2] == '/' || !test[2])) {
++test;
if (purl == url) {
/* protect out root */
goto BAD_REQUEST;
}
while (*--purl != '/') /* omit previous dir */;
- continue;
+ continue;
}
}
}
/* If URL is directory, adding '/' */
if (test[-1] != '/') {
if (is_directory(url + 1, 1, &sb)) {
- config->found_moved_temporarily = url;
+ found_moved_temporarily = url;
}
}
if (DEBUG)
- fprintf(stderr, "url='%s', args=%s\n", url, config->query);
+ fprintf(stderr, "url='%s', args=%s\n", url, g_query);
test = url;
ip_allowed = checkPermIP();
/* extra read only for POST */
if (prequest != request_GET) {
test = buf + sizeof("Content-length:")-1;
- if (!test[0]) goto bail_out;
+ if (!test[0])
+ goto bail_out;
errno = 0;
/* not using strtoul: it ignores leading munis! */
length = strtol(test, &test, 10);
} else if ((STRNCASECMP(buf, "Content-Type:") == 0)) {
content_type = strdup(skip_whitespace(buf + sizeof("Content-Type:")-1));
} else if ((STRNCASECMP(buf, "Referer:") == 0)) {
- config->referer = strdup(skip_whitespace(buf + sizeof("Referer:")-1));
+ referer = strdup(skip_whitespace(buf + sizeof("Referer:")-1));
}
#endif
} /* while extra header reading */
}
alarm(0);
- if (config->alarm_signaled)
+ if (alarm_signaled)
break;
if (strcmp(strrchr(url, '/') + 1, httpd_conf) == 0 || ip_allowed == 0) {
}
#endif
- if (config->found_moved_temporarily) {
+ if (found_moved_temporarily) {
sendHeaders(HTTP_MOVED_TEMPORARILY);
/* clear unforked memory flag */
- config->found_moved_temporarily = NULL;
+ found_moved_temporarily = NULL;
break;
}
sendCgi(url, prequest, length, cookie, content_type);
break;
}
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ {
+ char *suffix = strrchr(test, '.');
+ if (suffix) {
+ Htaccess *cur;
+ for (cur = script_i; cur; cur = cur->next) {
+ if (strcmp(cur->before_colon + 1, suffix) == 0) {
+ sendCgi(url, prequest, length, cookie, content_type);
+ goto bail_out;
+ }
+ }
+ }
+ }
+#endif
if (prequest != request_GET) {
sendHeaders(HTTP_NOT_IMPLEMENTED);
break;
strcpy(purl, "index.html");
if (stat(test, &sb) == 0) {
/* It's a dir URL and there is index.html */
- config->ContentLength = sb.st_size;
- config->last_mod = sb.st_mtime;
+ ContentLength = sb.st_size;
+ last_mod = sb.st_mtime;
}
#if ENABLE_FEATURE_HTTPD_CGI
else if (purl[-1] == '/') {
* Try cgi-bin/index.cgi */
if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) {
purl[0] = '\0';
- config->query = url;
+ g_query = url;
sendCgi("/cgi-bin/index.cgi", prequest, length, cookie, content_type);
break;
}
}
#endif /* FEATURE_HTTPD_CGI */
sendFile(test);
- config->ContentLength = -1;
+ ContentLength = -1;
} while (0);
+#if ENABLE_FEATURE_HTTPD_CGI
bail_out:
+#endif
if (DEBUG)
fprintf(stderr, "closing socket\n\n");
#if ENABLE_FEATURE_HTTPD_CGI
free(cookie);
free(content_type);
- free(config->referer);
- config->referer = NULL;
+ free(referer);
+ referer = NULL;
# if ENABLE_FEATURE_HTTPD_BASIC_AUTH
- free(config->remoteuser);
- config->remoteuser = NULL;
+ free(remoteuser);
+ remoteuser = NULL;
# endif
#endif
- shutdown(config->accepted_socket, SHUT_WR);
+ shutdown(accepted_socket, SHUT_WR);
/* Properly wait for remote to closed */
FD_ZERO(&s_fd);
- FD_SET(config->accepted_socket, &s_fd);
+ FD_SET(accepted_socket, &s_fd);
do {
tv.tv_sec = 2;
tv.tv_usec = 0;
- retval = select(config->accepted_socket + 1, &s_fd, NULL, NULL, &tv);
- } while (retval > 0 && read(config->accepted_socket, buf, sizeof(config->buf) > 0));
+ retval = select(accepted_socket + 1, &s_fd, NULL, NULL, &tv);
+ } while (retval > 0 && read(accepted_socket, buf, sizeof(iobuf) > 0));
- shutdown(config->accepted_socket, SHUT_RD);
+ shutdown(accepted_socket, SHUT_RD);
/* In inetd case, we close fd 1 (stdout) here. We will exit soon anyway */
- close(config->accepted_socket);
+ close(accepted_socket);
}
/****************************************************************************
/* copy the ports we are watching to the readfd set */
while (1) {
- int on, s;
- socklen_t fromAddrLen;
- struct sockaddr_in fromAddr;
+ int s;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ USE_FEATURE_IPV6(struct sockaddr_in6 sin6;)
+ } fromAddr;
+ socklen_t fromAddrLen = sizeof(fromAddr);
/* Now wait INDEFINITELY on the set of sockets! */
readfd = portfd;
continue;
if (!FD_ISSET(server, &readfd))
continue;
- fromAddrLen = sizeof(fromAddr);
- s = accept(server, (struct sockaddr *)&fromAddr, &fromAddrLen);
+ s = accept(server, &fromAddr.sa, &fromAddrLen);
if (s < 0)
continue;
- config->accepted_socket = s;
- config->rmt_ip = ntohl(fromAddr.sin_addr.s_addr);
+ accepted_socket = s;
+ rmt_ip = 0;
+ tcp_port = 0;
#if ENABLE_FEATURE_HTTPD_CGI || DEBUG
- sprintf(config->rmt_ip_str, "%u.%u.%u.%u",
- (unsigned char)(config->rmt_ip >> 24),
- (unsigned char)(config->rmt_ip >> 16),
- (unsigned char)(config->rmt_ip >> 8),
- config->rmt_ip & 0xff);
- config->port = ntohs(fromAddr.sin_port);
+ free(rmt_ip_str);
+ rmt_ip_str = xmalloc_sockaddr2dotted(&fromAddr.sa, fromAddrLen);
#if DEBUG
- bb_error_msg("connection from IP=%s, port %u",
- config->rmt_ip_str, config->port);
+ bb_error_msg("connection from '%s'", rmt_ip_str);
#endif
#endif /* FEATURE_HTTPD_CGI */
+ if (fromAddr.sa.sa_family == AF_INET) {
+ rmt_ip = ntohl(fromAddr.sin.sin_addr.s_addr);
+ tcp_port = ntohs(fromAddr.sin.sin_port);
+ }
+#if ENABLE_FEATURE_IPV6
+ if (fromAddr.sa.sa_family == AF_INET6) {
+ //rmt_ip = ntohl(fromAddr.sin.sin_addr.s_addr);
+ tcp_port = ntohs(fromAddr.sin6.sin6_port);
+ }
+#endif
/* set the KEEPALIVE option to cull dead connections */
- on = 1;
- setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on));
-#if !DEBUG
- if (fork() == 0)
-#endif
- {
- /* This is the spawned thread */
+ setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
+
+ if (DEBUG || fork() == 0) {
+ /* child */
#if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
/* protect reload config, may be confuse checking */
signal(SIGHUP, SIG_IGN);
#endif
handleIncoming();
-#if !DEBUG
- exit(0);
-#endif
+ if (!DEBUG)
+ exit(0);
}
close(s);
- } // while (1)
+ } /* while (1) */
return 0;
}
/* from inetd */
static int miniHttpd_inetd(void)
{
- struct sockaddr_in fromAddrLen;
- socklen_t sinlen = sizeof(struct sockaddr_in);
-
- getpeername(0, (struct sockaddr *)&fromAddrLen, &sinlen);
- config->rmt_ip = ntohl(fromAddrLen.sin_addr.s_addr);
-#if ENABLE_FEATURE_HTTPD_CGI
- sprintf(config->rmt_ip_str, "%u.%u.%u.%u",
- (unsigned char)(config->rmt_ip >> 24),
- (unsigned char)(config->rmt_ip >> 16),
- (unsigned char)(config->rmt_ip >> 8),
- config->rmt_ip & 0xff);
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ USE_FEATURE_IPV6(struct sockaddr_in6 sin6;)
+ } fromAddr;
+ socklen_t fromAddrLen = sizeof(fromAddr);
+
+ getpeername(0, &fromAddr.sa, &fromAddrLen);
+ rmt_ip = 0;
+ tcp_port = 0;
+#if ENABLE_FEATURE_HTTPD_CGI || DEBUG
+ free(rmt_ip_str);
+ rmt_ip_str = xmalloc_sockaddr2dotted(&fromAddr.sa, fromAddrLen);
+#endif
+ if (fromAddr.sa.sa_family == AF_INET) {
+ rmt_ip = ntohl(fromAddr.sin.sin_addr.s_addr);
+ tcp_port = ntohs(fromAddr.sin.sin_port);
+ }
+#if ENABLE_FEATURE_IPV6
+ if (fromAddr.sa.sa_family == AF_INET6) {
+ //rmt_ip = ntohl(fromAddr.sin.sin_addr.s_addr);
+ tcp_port = ntohs(fromAddr.sin6.sin6_port);
+ }
#endif
- config->port = ntohs(fromAddrLen.sin_port);
handleIncoming();
return 0;
}
OPT_FOREGROUND = 1 << p_opt_foreground,
};
-static const char httpd_opts[] = "c:d:h:"
- USE_FEATURE_HTTPD_ENCODE_URL_STR("e:")
- USE_FEATURE_HTTPD_BASIC_AUTH("r:")
- USE_FEATURE_HTTPD_AUTH_MD5("m:")
- USE_FEATURE_HTTPD_SETUID("u:")
- "p:if";
-
-int httpd_main(int argc, char *argv[])
+int httpd_main(int argc, char **argv);
+int httpd_main(int argc, char **argv)
{
unsigned opt;
- const char *home_httpd = home;
char *url_for_decode;
USE_FEATURE_HTTPD_ENCODE_URL_STR(const char *url_for_encode;)
const char *s_port;
setlocale(LC_TIME, "C");
#endif
- config = xzalloc(sizeof(*config));
-#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
- config->realm = "Web Server Authentication";
-#endif
- config->port = 80;
- config->ContentLength = -1;
-
- opt = getopt32(argc, argv, httpd_opts,
- &(config->configFile), &url_for_decode, &home_httpd
+ INIT_G();
+ home_httpd = xrealloc_getcwd_or_warn(NULL);
+ /* We do not "absolutize" path given by -h (home) opt.
+ * If user gives relative path in -h, $SCRIPT_FILENAME can end up
+ * relative too. */
+ opt = getopt32(argc, argv, "c:d:h:"
+ USE_FEATURE_HTTPD_ENCODE_URL_STR("e:")
+ USE_FEATURE_HTTPD_BASIC_AUTH("r:")
+ USE_FEATURE_HTTPD_AUTH_MD5("m:")
+ USE_FEATURE_HTTPD_SETUID("u:")
+ "p:if",
+ &(configFile), &url_for_decode, &home_httpd
USE_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode)
- USE_FEATURE_HTTPD_BASIC_AUTH(, &(config->realm))
+ USE_FEATURE_HTTPD_BASIC_AUTH(, &g_realm)
USE_FEATURE_HTTPD_AUTH_MD5(, &pass)
USE_FEATURE_HTTPD_SETUID(, &s_ugid)
, &s_port
}
#endif
if (opt & OPT_PORT)
- config->port = xatou16(s_port);
+ tcp_port = xatou16(s_port);
#if ENABLE_FEATURE_HTTPD_SETUID
if (opt & OPT_SETUID) {
- char *e;
- // FIXME: what the default group should be?
- ugid.gid = -1;
- ugid.uid = strtoul(s_ugid, &e, 0);
- if (*e == ':') {
- e++;
- ugid.gid = strtoul(e, &e, 0);
- }
- if (*e != '\0') {
- /* not integer */
- if (!uidgid_get(&ugid, s_ugid))
- bb_error_msg_and_die("unrecognized user[:group] "
+ if (!get_uidgid(&ugid, s_ugid, 1))
+ bb_error_msg_and_die("unrecognized user[:group] "
"name '%s'", s_ugid);
- }
}
#endif
xchdir(home_httpd);
if (!(opt & OPT_INETD)) {
- config->server_socket = openServer();
+ signal(SIGCHLD, SIG_IGN);
+ server_socket = openServer();
#if ENABLE_FEATURE_HTTPD_SETUID
/* drop privileges */
if (opt & OPT_SETUID) {
if (ugid.gid != (gid_t)-1) {
if (setgroups(1, &ugid.gid) == -1)
- bb_perror_msg_and_die("setgroups");
+ bb_perror_msg_and_die("setgroups");
xsetgid(ugid.gid);
}
xsetuid(ugid.uid);
#if ENABLE_FEATURE_HTTPD_CGI
{
char *p = getenv("PATH");
- if (p) {
- p = xstrdup(p);
- }
+ /* env strings themself are not freed, no need to strdup(p): */
clearenv();
if (p)
- setenv1("PATH", p);
+ putenv(p - 5);
if (!(opt & OPT_INETD))
- setenv_long("SERVER_PORT", config->port);
+ setenv_long("SERVER_PORT", tcp_port);
}
#endif
return miniHttpd_inetd();
if (!(opt & OPT_FOREGROUND))
- xdaemon(1, 0); /* don't change current directory */
- return miniHttpd(config->server_socket);
+ bb_daemonize(0); /* don't change current directory */
+ return miniHttpd(server_socket);
}