diff --git a/httpd/Config.in b/httpd/Config.in
index ce42a59a7..f938d39d3 100644
--- a/httpd/Config.in
+++ b/httpd/Config.in
@@ -53,20 +53,34 @@ config CONFIG_HTTP_TIMEOUT
help
Set the timeout of a connection in seconds.
+menu "CGI"
+
config CONFIG_HTTP_HAS_CGI
bool "Enable CGI"
default n
+ depends on !CONFIG_PLATFORM_WIN32
help
- Enable the CGI capability.
+ Enable the CGI capability. Not available on Win32 platforms.
config CONFIG_HTTP_CGI_EXTENSIONS
string "CGI File Extension(s)"
- default ".php,.sh"
+ default ".lua,.lp"
depends on CONFIG_HTTP_HAS_CGI
help
Tell axhhtpd what file extension(s) are used for CGI.
- This is a comma separated list.
+ This is a comma separated list - e.g. ".php,.pl" etc
+
+config CONFIG_HTTP_LUA_LAUNCHER
+ string "Lua's CGI Launcher"
+ default "/usr/local/bin/cgi" if CONFIG_PLATFORM_LINUX
+ default "/usr/local/bin/cgi.exe" if CONFIG_PLATFORM_CYGWIN
+ depends on CONFIG_HTTP_HAS_CGI
+
+ help
+ Lua has a special launcher application to run kepler/lua scripts.
+ ".lua and .lp" extensions must be defined in CONFIG_HTTP_CGI_EXTENSIONS.
+endmenu
config CONFIG_HTTP_DIRECTORIES
bool "Enable Directory Listing"
diff --git a/httpd/axhttp.h b/httpd/axhttp.h
index a2b14e687..1cd07ffef 100644
--- a/httpd/axhttp.h
+++ b/httpd/axhttp.h
@@ -26,17 +26,16 @@
#endif
#define MAXREQUESTLENGTH 256
-#define MAXCGIARGS 100
#define BLOCKSIZE 4096
#define INITIAL_CONNECTION_SLOTS 10
#define CONFIG_HTTP_DEFAULT_SSL_OPTIONS 0
-#define STATE_WANT_TO_READ_HEAD 1
-#define STATE_WANT_TO_SEND_HEAD 2
-#define STATE_WANT_TO_READ_FILE 3
-#define STATE_WANT_TO_SEND_FILE 4
-#define STATE_DOING_DIR 5
+#define STATE_WANT_TO_READ_HEAD 1
+#define STATE_WANT_TO_SEND_HEAD 2
+#define STATE_WANT_TO_READ_FILE 3
+#define STATE_WANT_TO_SEND_FILE 4
+#define STATE_DOING_DIR 5
enum
{
@@ -67,7 +66,7 @@ struct connstruct
char actualfile[MAXREQUESTLENGTH];
char filereq[MAXREQUESTLENGTH];
char dirname[MAXREQUESTLENGTH];
- char virtualhostreq[MAXREQUESTLENGTH];
+ char server_name[MAXREQUESTLENGTH];
int numbytes;
char databuf[BLOCKSIZE];
uint8_t is_ssl;
@@ -75,9 +74,13 @@ struct connstruct
time_t if_modified_since;
#if defined(CONFIG_HTTP_HAS_CGI)
- char cgiargs[MAXREQUESTLENGTH];
- char cgiscriptinfo[MAXREQUESTLENGTH];
- char cgipathinfo[MAXREQUESTLENGTH];
+ uint8_t is_cgi;
+ uint8_t is_lua;
+ int content_length;
+ char remote_addr[MAXREQUESTLENGTH];
+ char uri_request[MAXREQUESTLENGTH];
+ char uri_path_info[MAXREQUESTLENGTH];
+ char uri_query[MAXREQUESTLENGTH];
#endif
#if defined(CONFIG_HTTP_HAS_AUTHORIZATION)
char authorization[MAXREQUESTLENGTH];
@@ -104,6 +107,8 @@ struct cgiextstruct
extern struct serverstruct *servers;
extern struct connstruct *usedconns;
extern struct connstruct *freeconns;
+extern const char * const server_version;
+
#if defined(CONFIG_HTTP_HAS_CGI)
extern struct cgiextstruct *cgiexts;
#endif
diff --git a/httpd/axhttpd.c b/httpd/axhttpd.c
index 5bdf423d3..c67c08eca 100644
--- a/httpd/axhttpd.c
+++ b/httpd/axhttpd.c
@@ -27,6 +27,7 @@
struct serverstruct *servers;
struct connstruct *usedconns;
struct connstruct *freeconns;
+const char * const server_version = "axhttpd/"AXTLS_VERSION;
static void addtoservers(int sd);
static int openlistener(int port);
@@ -162,8 +163,8 @@ int main(int argc, char *argv[])
addcgiext(CONFIG_HTTP_CGI_EXTENSIONS);
#endif
#if defined(CONFIG_HTTP_VERBOSE)
- printf("axhttpd (%s): listening on ports %d (http) and %d (https)\n",
- ssl_version(), CONFIG_HTTP_PORT, CONFIG_HTTP_HTTPS_PORT);
+ printf("%s: listening on ports %d (http) and %d (https)\n",
+ server_version, CONFIG_HTTP_PORT, CONFIG_HTTP_HTTPS_PORT);
TTY_FLUSH();
#endif
@@ -475,15 +476,15 @@ static void addconnection(int sd, char *ip, int is_ssl)
#if defined(CONFIG_HTTP_HAS_DIRECTORIES)
tp->dirp = NULL;
#endif
- *(tp->actualfile) = '\0';
- *(tp->filereq) = '\0';
-#if defined(CONFIG_HTTP_HAS_CGI)
- *(tp->cgiargs) = '\0';
-#endif
+ *tp->actualfile = '\0';
+ *tp->filereq = '\0';
tp->state = STATE_WANT_TO_READ_HEAD;
tp->reqtype = TYPE_GET;
tp->close_when_done = 0;
tp->timeout = time(NULL) + CONFIG_HTTP_TIMEOUT;
+#if defined(CONFIG_HTTP_HAS_CGI)
+ strcpy(tp->remote_addr, ip);
+#endif
}
void removeconnection(struct connstruct *cn)
@@ -564,3 +565,4 @@ static void ax_chdir(void)
exit(1);
}
}
+
diff --git a/httpd/proc.c b/httpd/proc.c
index 86bf163bd..c65d0998e 100644
--- a/httpd/proc.c
+++ b/httpd/proc.c
@@ -38,17 +38,15 @@ static void buildactualfile(struct connstruct *cn);
static int sanitizefile(const char *buf);
static int sanitizehost(char *buf);
static int htaccess_check(struct connstruct *cn);
+static const char *getmimetype(const char *name);
#if defined(CONFIG_HTTP_DIRECTORIES)
static void urlencode(const uint8_t *s, char *t);
static void procdirlisting(struct connstruct *cn);
-static const char *getmimetype(const char *name);
#endif
#if defined(CONFIG_HTTP_HAS_CGI)
-static void proccgi(struct connstruct *cn, int has_pathinfo);
-static int trycgi_withpathinfo(struct connstruct *cn);
-static void split(char *tp, char *sp[], int maxwords, char sc);
-static int iscgi(const char *fn);
+static void proccgi(struct connstruct *cn);
+static void decode_path_info(struct connstruct *cn, char *path_info);
#endif
#ifdef CONFIG_HTTP_HAS_AUTHORIZATION
static int auth_check(struct connstruct *cn);
@@ -58,9 +56,6 @@ static int auth_check(struct connstruct *cn);
static int procheadelem(struct connstruct *cn, char *buf)
{
char *delim, *value;
-#if defined(CONFIG_HTTP_HAS_CGI)
- char *cgi_delim;
-#endif
if ((delim = strchr(buf, ' ')) == NULL)
return 0;
@@ -89,13 +84,10 @@ static int procheadelem(struct connstruct *cn, char *buf)
}
#if defined(CONFIG_HTTP_HAS_CGI)
- if ((cgi_delim = strchr(value, '?')))
- {
- *cgi_delim = 0;
- my_strncpy(cn->cgiargs, cgi_delim+1, MAXREQUESTLENGTH);
- }
-#endif
+ decode_path_info(cn, value);
+#else
my_strncpy(cn->filereq, value, MAXREQUESTLENGTH);
+#endif
cn->if_modified_since = -1;
}
else if (strcmp(buf, "Host:") == 0)
@@ -106,7 +98,7 @@ static int procheadelem(struct connstruct *cn, char *buf)
return 0;
}
- my_strncpy(cn->virtualhostreq, value, MAXREQUESTLENGTH);
+ my_strncpy(cn->server_name, value, MAXREQUESTLENGTH);
}
else if (strcmp(buf, "Connection:") == 0 && strcmp(value, "close") == 0)
{
@@ -128,6 +120,12 @@ static int procheadelem(struct connstruct *cn, char *buf)
cn->authorization[size] = 0;
}
#endif
+#if defined(CONFIG_HTTP_HAS_CGI)
+ else if (strcmp(buf, "Content-Length:") == 0)
+ {
+ sscanf(value, "%d", &cn->content_length);
+ }
+#endif
return 1;
}
@@ -169,7 +167,7 @@ static void procdirlisting(struct connstruct *cn)
snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\nContent-Type: text/html\n\n"
"
\nDirectory Listing\n"
"Directory listing of %s://%s%s
\n",
- cn->is_ssl ? "https" : "http", cn->virtualhostreq, cn->filereq);
+ cn->is_ssl ? "https" : "http", cn->server_name, cn->filereq);
special_write(cn, buf, strlen(buf));
cn->state = STATE_DOING_DIR;
}
@@ -320,6 +318,7 @@ void procsendhead(struct connstruct *cn)
struct stat stbuf;
time_t now = cn->timeout - CONFIG_HTTP_TIMEOUT;
char date[32];
+ int file_exists;
/* are we trying to access a file over the HTTP connection instead of a
* HTTPS connection? Or is this directory disabled? */
@@ -340,31 +339,19 @@ void procsendhead(struct connstruct *cn)
}
#endif
- if (stat(cn->actualfile, &stbuf) == -1)
- {
-#if defined(CONFIG_HTTP_HAS_CGI)
- if (stat(cn->actualfile, &stbuf) == -1 && trycgi_withpathinfo(cn) == 0)
- {
- /* We Try To Find A CGI */
- proccgi(cn, 1);
- return;
- }
-#endif
- }
+ file_exists = stat(cn->actualfile, &stbuf);
#if defined(CONFIG_HTTP_HAS_CGI)
- if (iscgi(cn->actualfile))
+ if (file_exists != -1 && cn->is_cgi)
{
-#ifndef WIN32
- /* An executable file? */
- if ((stbuf.st_mode & S_IEXEC) == 0 || isdir(cn->actualfile))
+ if ((stbuf.st_mode & S_IEXEC) == 0 || isdir(cn->actualfile))
{
+ /* A non-executable file, or directory? */
send_error(cn, 404);
- return;
}
-#endif
+ else
+ proccgi(cn);
- proccgi(cn, 0);
return;
}
#endif
@@ -373,10 +360,10 @@ void procsendhead(struct connstruct *cn)
if (isdir(cn->actualfile))
{
char tbuf[MAXREQUESTLENGTH];
- sprintf(tbuf, "%s%s", cn->actualfile, index_file);
+ snprintf(tbuf, MAXREQUESTLENGTH, "%s%s", cn->actualfile, index_file);
- if (stat(tbuf, &stbuf) != -1)
- strcat(cn->actualfile, index_file);
+ if ((file_exists = stat(tbuf, &stbuf)) != -1)
+ my_strncpy(cn->actualfile, tbuf, MAXREQUESTLENGTH);
else
{
#if defined(CONFIG_HTTP_DIRECTORIES)
@@ -387,22 +374,12 @@ void procsendhead(struct connstruct *cn)
#endif
return;
}
+ }
-#if defined(CONFIG_HTTP_HAS_CGI)
- /* If the index is a CGI file, handle it like any other CGI */
- if (iscgi(cn->actualfile))
- {
- /* Set up CGI script */
- if ((stbuf.st_mode & S_IEXEC) == 0 || isdir(cn->actualfile))
- {
- send_error(cn, 404);
- return;
- }
-
- proccgi(cn, 0);
- return;
- }
-#endif
+ if (file_exists == -1)
+ {
+ send_error(cn, 404);
+ return;
}
strcpy(date, ctime(&now));
@@ -412,7 +389,7 @@ void procsendhead(struct connstruct *cn)
cn->if_modified_since >= stbuf.st_mtime))
{
snprintf(buf, sizeof(buf), "HTTP/1.1 304 Not Modified\nServer: "
- "axhttpd V%s\nDate: %s\n", ssl_version(), date);
+ "%s\nDate: %s\n", server_version, date);
special_write(cn, buf, strlen(buf));
cn->state = STATE_WANT_TO_READ_HEAD;
return;
@@ -437,9 +414,9 @@ void procsendhead(struct connstruct *cn)
return;
}
- snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\nServer: axhttpd V%s\n"
+ snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\nServer: %s\n"
"Content-Type: %s\nContent-Length: %ld\n"
- "Date: %sLast-Modified: %s\n", ssl_version(),
+ "Date: %sLast-Modified: %s\n", server_version,
getmimetype(cn->actualfile), (long) stbuf.st_size,
date, ctime(&stbuf.st_mtime)); /* ctime() has a \n on the end */
@@ -514,18 +491,24 @@ void procsendfile(struct connstruct *cn)
}
#if defined(CONFIG_HTTP_HAS_CGI)
-static void proccgi(struct connstruct *cn, int has_pathinfo)
+#define CGI_ARG_SIZE 14
+
+static void proccgi(struct connstruct *cn)
{
int tpipe[2];
- char *myargs[5];
- char buf[MAXREQUESTLENGTH];
+ char *myargs[2];
+ char cgienv[CGI_ARG_SIZE][MAXREQUESTLENGTH];
+ char * cgiptr[CGI_ARG_SIZE+1];
+ const char *type = "HEAD";
+ int cgi_index = 0, i;
#ifdef WIN32
int tmp_stdout;
#endif
- snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\nServer: axhttpd V%s\n%s",
- ssl_version(), (cn->reqtype == TYPE_HEAD) ? "\n" : "");
- special_write(cn, buf, strlen(buf));
+ snprintf(cgienv[0], MAXREQUESTLENGTH,
+ "HTTP/1.1 200 OK\nServer: %s\n%s",
+ server_version, (cn->reqtype == TYPE_HEAD) ? "\n" : "");
+ special_write(cn, cgienv[0], strlen(cgienv[0]));
if (cn->reqtype == TYPE_HEAD)
{
@@ -533,6 +516,12 @@ static void proccgi(struct connstruct *cn, int has_pathinfo)
return;
}
+#ifdef CONFIG_HTTP_VERBOSE
+ printf("[CGI]: %s:/%s\n", cn->is_ssl ? "https" : "http", cn->filereq);
+ TTY_FLUSH();
+#endif
+
+ /* win32 cgi is a bit too painful */
#ifndef WIN32
pipe(tpipe);
@@ -560,144 +549,139 @@ static void proccgi(struct connstruct *cn, int has_pathinfo)
close(tpipe[0]);
close(tpipe[1]);
- myargs[0] = cn->actualfile;
- myargs[1] = cn->cgiargs;
- myargs[2] = NULL;
- if (!has_pathinfo)
+ myargs[0] = cn->actualfile;
+ myargs[1] = NULL;
+
+ /*
+ * set the cgi args. A url is defined by:
+ * http://$SERVER_NAME:$SERVER_PORT$SCRIPT_NAME$PATH_INFO?$QUERY_STRING
+ * TODO: other CGI parameters?
+ */
+ sprintf(cgienv[cgi_index++], "SERVER_SOFTWARE=%s", server_version);
+ strcpy(cgienv[cgi_index++], "DOCUMENT_ROOT=" CONFIG_HTTP_WEBROOT);
+ snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH,
+ "SERVER_NAME=%s", cn->server_name);
+ sprintf(cgienv[cgi_index++], "SERVER_PORT=%d",
+ cn->is_ssl ? CONFIG_HTTP_HTTPS_PORT : CONFIG_HTTP_PORT);
+ snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH,
+ "REQUEST_URI=%s", cn->uri_request);
+ snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH,
+ "SCRIPT_NAME=%s", cn->filereq);
+ snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH,
+ "PATH_INFO=%s", cn->uri_path_info);
+ snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH,
+ "QUERY_STRING=%s", cn->uri_query);
+ snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH,
+ "REMOTE_ADDR=%s", cn->remote_addr);
+
+ switch (cn->reqtype)
{
- my_strncpy(cn->cgipathinfo, "/", MAXREQUESTLENGTH);
- my_strncpy(cn->cgiscriptinfo, cn->filereq, MAXREQUESTLENGTH);
+ case TYPE_GET:
+ type = "GET";
+ break;
+
+ case TYPE_POST:
+ type = "POST";
+ sprintf(cgienv[cgi_index++],
+ "CONTENT_LENGTH=%d", cn->content_length);
+ strcpy(cgienv[cgi_index++],
+ "CONTENT_TYPE=application/x-www-form-urlencoded");
+ break;
}
- execv(cn->actualfile, myargs);
-#else /* WIN32 */
- _pipe(tpipe, 8192, O_BINARY| O_NOINHERIT);
+ sprintf(cgienv[cgi_index++], "REQUEST_METHOD=%s", type);
- myargs[0] = "sh";
- myargs[1] = "-c";
- myargs[2] = &cn->filereq[1]; /* ignore the inital "/" */
- myargs[3] = cn->cgiargs;
- myargs[4] = NULL;
+ if (cn->is_ssl)
+ strcpy(cgienv[cgi_index++], "HTTPS=on");
- tmp_stdout = _dup(_fileno(stdout));
- _dup2(tpipe[1], _fileno(stdout));
- close(tpipe[1]);
+#ifdef CONFIG_PLATFORM_CYGWIN
+ /* TODO: find out why Lua needs this */
+ strcpy(cgienv[cgi_index++], "PATH=/usr/bin");
+#endif
- /* change to suit execution method */
- if (spawnl(P_NOWAIT, "c:\\Program Files\\cygwin\\bin\\sh.exe",
- myargs[0], myargs[1], myargs[2], myargs[3], myargs[4]) == -1)
+ if (cgi_index >= CGI_ARG_SIZE)
{
- removeconnection(cn);
+ printf("Content-type: text/plain\n\nToo many CGI args\n");
return;
}
- _dup2(tmp_stdout, _fileno(stdout));
- close(tmp_stdout);
- cn->filedesc = tpipe[0];
- cn->state = STATE_WANT_TO_READ_FILE;
- cn->close_when_done = 1;
+ /* copy across the pointer indexes */
+ for (i = 0; i < cgi_index; i++)
+ cgiptr[i] = cgienv[i];
- for (;;)
- {
- procreadfile(cn);
-
- if (cn->filedesc == -1)
- break;
-
- procsendfile(cn);
- usleep(200000); /* don't know why this delay makes it work (yet) */
- }
+ cgiptr[i] = NULL;
+ execve(myargs[0], myargs, cgiptr);
+ printf("Content-type: text/plain\n\nshouldn't get here\n");
#endif
}
-static int trycgi_withpathinfo(struct connstruct *cn)
-{
- char tpfile[MAXREQUESTLENGTH];
- char fr_str[MAXREQUESTLENGTH];
- char *fr_rs[MAXCGIARGS]; /* filereq splitted */
- int i = 0, offset;
-
- my_strncpy(fr_str, cn->filereq, MAXREQUESTLENGTH);
- split(fr_str, fr_rs, MAXCGIARGS, '/');
-
- while (fr_rs[i] != NULL)
- {
- snprintf(tpfile, sizeof(tpfile), "/%s%s",
- cn->virtualhostreq, fr_str);
-
- if (iscgi(tpfile) && isdir(tpfile) == 0)
- {
- /* We've found our CGI file! */
- my_strncpy(cn->actualfile, tpfile, MAXREQUESTLENGTH);
- my_strncpy(cn->cgiscriptinfo, fr_str, MAXREQUESTLENGTH);
- offset = (fr_rs[i] + strlen(fr_rs[i])) - fr_str;
- my_strncpy(cn->cgipathinfo, cn->filereq+offset, MAXREQUESTLENGTH);
- return 0;
- }
-
- *(fr_rs[i]+strlen(fr_rs[i])) = '/';
- i++;
- }
-
- /* Couldn't find any CGIs :( */
- *(cn->cgiscriptinfo) = '\0';
- *(cn->cgipathinfo) = '\0';
- return -1;
-}
-
-static int iscgi(const char *fn)
+static char * cgi_filetype_match(struct connstruct *cn, const char *fn)
{
struct cgiextstruct *tp = cgiexts;
- int fnlen, extlen;
-
- fnlen = strlen(fn);
while (tp != NULL)
{
- extlen = strlen(tp->ext);
+ char *t;
- if (strcasecmp(fn+(fnlen-extlen), tp->ext) == 0)
- return 1;
+ if ((t = strstr(fn, tp->ext)) != NULL)
+ {
+
+ t += strlen(tp->ext);
+
+ if (*t == '/' || *t == '\0')
+ {
+ if (strcmp(tp->ext, ".lua") == 0 || strcmp(tp->ext, ".lp") == 0)
+ cn->is_lua = 1;
+
+ return t;
+ }
+ else
+ return NULL;
+
+ }
tp = tp->next;
}
- return 0;
+ return NULL;
}
-static void split(char *tp, char *sp[], int maxwords, char sc)
+static void decode_path_info(struct connstruct *cn, char *path_info)
{
- int i = 0;
+ char *cgi_delim;
- while(1)
+ cn->is_cgi = 0;
+ cn->is_lua = 0;
+ *cn->uri_request = '\0';
+ *cn->uri_path_info = '\0';
+ *cn->uri_query = '\0';
+
+ my_strncpy(cn->uri_request, path_info, MAXREQUESTLENGTH);
+
+ /* query info? */
+ if ((cgi_delim = strchr(path_info, '?')))
{
- /* Skip leading whitespace */
- while (*tp == sc) tp++;
-
- if (*tp == '\0')
- {
- sp[i] = NULL;
- break;
- }
-
- if (i==maxwords-2)
- {
- sp[maxwords-2] = NULL;
- break;
- }
-
- sp[i] = tp;
-
- while(*tp != sc && *tp != '\0')
- tp++;
-
- if (*tp == sc)
- *tp++ = '\0';
-
- i++;
+ *cgi_delim = '\0';
+ my_strncpy(cn->uri_query, cgi_delim+1, MAXREQUESTLENGTH);
}
+
+ if ((cgi_delim = cgi_filetype_match(cn, path_info)) != NULL)
+ {
+ cn->is_cgi = 1; /* definitely a CGI script */
+
+ /* path info? */
+ if (*cgi_delim != '\0')
+ {
+ my_strncpy(cn->uri_path_info, cgi_delim, MAXREQUESTLENGTH);
+ *cgi_delim = '\0';
+ }
+ }
+
+ /* the bit at the start must be the script name */
+ my_strncpy(cn->filereq, path_info, MAXREQUESTLENGTH);
}
+
#endif /* CONFIG_HTTP_HAS_CGI */
/* Decode string %xx -> char (in place) */
@@ -755,6 +739,15 @@ static void buildactualfile(struct connstruct *cn)
{
char *cp;
+#if defined(CONFIG_HTTP_HAS_CGI)
+ /* use the lua launcher if this file has a lua extension */
+ if (cn->is_lua)
+ {
+ strcpy(cn->actualfile, CONFIG_HTTP_LUA_LAUNCHER);
+ return;
+ }
+#endif
+
#ifdef CONFIG_HTTP_USE_CHROOT
snprintf(cn->actualfile, MAXREQUESTLENGTH, "%s", cn->filereq);
#else
@@ -937,7 +930,7 @@ static int auth_check(struct connstruct *cn)
error:
fclose(fp);
- send_authenticate(cn, cn->virtualhostreq);
+ send_authenticate(cn, cn->server_name);
return -1;
}
#endif