in netutils/thttpd/libhttpd.c [3207:3536]
int httpd_start_request(httpd_conn *hc, struct timeval *nowp)
{
static char *indexname;
static size_t maxindexname = 0;
#ifdef CONFIG_THTTPD_AUTH_FILE
static char *dirname;
static size_t maxdirname = 0;
#endif /* CONFIG_THTTPD_AUTH_FILE */
size_t expnlen;
size_t indxlen;
char *cp;
char *pi;
int i;
ninfo("File: \"%s\"\n", hc->expnfilename);
expnlen = strlen(hc->expnfilename);
if (hc->method != METHOD_GET && hc->method != METHOD_HEAD &&
hc->method != METHOD_POST)
{
NOTIMPLEMENTED("start");
httpd_send_err(hc, 501, err501title, "", err501form,
httpd_method_str(hc->method));
return -1;
}
/* Stat the file. */
if (stat(hc->expnfilename, &hc->sb) < 0)
{
INTERNALERROR(hc->expnfilename);
httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl);
return -1;
}
/* Is it world-readable or world-executable? We check explicitly instead
* of just trying to open it, so that no one ever gets surprised by a file
* that's not set world-readable and yet somehow is readable by the HTTP
* server and therefore the *whole* world.
*/
if (!(hc->sb.st_mode & (S_IROTH | S_IXOTH)))
{
nwarn("WARNING: %s URL \"%s\" resolves to a non world-readable file\n",
httpd_ntoa(&hc->client_addr), hc->encodedurl);
httpd_send_err(hc, 403, err403title, "",
ERROR_FORM(err403form,
"The requested URL '%s' resolves to a file "
"that is not world-readable.\n"),
hc->encodedurl);
return -1;
}
/* Is it a directory? */
if (S_ISDIR(hc->sb.st_mode))
{
/* If there's pathinfo, it's just a non-existent file. */
if (hc->pathinfo[0] != '\0')
{
httpd_send_err(hc, 404, err404title, "",
err404form, hc->encodedurl);
return -1;
}
/* Special handling for directory URLs that don't end in a slash. We
* send back an explicit redirect with the slash, because otherwise
* many clients can't build relative URLs properly.
*/
if (strcmp(hc->origfilename, "") != 0 &&
strcmp(hc->origfilename, ".") != 0 &&
hc->origfilename[strlen(hc->origfilename) - 1] != '/')
{
send_dirredirect(hc);
return -1;
}
/* Check for an index file. */
for (i = 0; i < nitems(index_names); ++i)
{
httpd_realloc_str(&indexname, &maxindexname,
expnlen + 1 + strlen(index_names[i]));
strlcpy(indexname, hc->expnfilename, maxindexname + 1);
indxlen = strlen(indexname);
if (indxlen == 0 || indexname[indxlen - 1] != '/')
{
strlcat(indexname, "/", maxindexname + 1);
}
if (strcmp(indexname, "./") == 0)
{
indexname[0] = '\0';
}
strlcat(indexname, index_names[i], maxindexname + 1);
if (stat(indexname, &hc->sb) >= 0)
{
goto got_one;
}
}
/* Nope, no index file, so it's an actual directory request. */
#ifdef CONFIG_THTTPD_GENERATE_INDICES
/* Directories must be readable for indexing. */
if (!(hc->sb.st_mode & S_IROTH))
{
nwarn("WARNING: %s URL \"%s\" tried to index a non-readable "
"directory\n", httpd_ntoa(&hc->client_addr), hc->encodedurl);
httpd_send_err(hc, 403, err403title, "",
ERROR_FORM(err403form,
"The requested URL '%s' resolves to a "
"directory that has indexing disabled.\n"),
hc->encodedurl);
return -1;
}
# ifdef CONFIG_THTTPD_AUTH_FILE
/* Check authorization for this directory. */
if (auth_check(hc, hc->expnfilename) == -1)
{
return -1;
}
# endif /* CONFIG_THTTPD_AUTH_FILE */
/* Referer check. */
if (!check_referer(hc))
{
return -1;
}
/* Ok, generate an index. */
return ls(hc);
#else /* CONFIG_THTTPD_GENERATE_INDICES */
/* Indexing is disabled */
nwarn("WARNING: %s URL \"%s\" tried to index a directory with "
"indexing disabled\n",
httpd_ntoa(&hc->client_addr), hc->encodedurl);
httpd_send_err(hc, 403, err403title, "",
ERROR_FORM(err403form,
"The requested URL '%s' is a directory, and "
"directory indexing is disabled on this "
"server.\n"),
hc->encodedurl);
return -1;
#endif /* CONFIG_THTTPD_GENERATE_INDICES */
got_one:
/* Got an index file. Expand again. More pathinfo means
* something went wrong.
*/
cp = expand_filename(indexname, &pi, hc->tildemapped);
if (cp == NULL || pi[0] != '\0')
{
INTERNALERROR(indexname);
httpd_send_err(hc, 500, err500title, "",
err500form, hc->encodedurl);
return -1;
}
expnlen = strlen(cp);
httpd_realloc_str(&hc->expnfilename, &hc->maxexpnfilename, expnlen);
strlcpy(hc->expnfilename, cp, hc->maxexpnfilename + 1);
/* Now, is the index version world-readable or world-executable? */
if (!(hc->sb.st_mode & (S_IROTH | S_IXOTH)))
{
nwarn("WARNING: %s URL \"%s\" resolves to a non-world-readable "
"index file\n",
httpd_ntoa(&hc->client_addr), hc->encodedurl);
httpd_send_err(hc, 403, err403title, "",
ERROR_FORM(err403form,
"The requested URL '%s' resolves to an "
"index file that is not world-readable.\n"),
hc->encodedurl);
return -1;
}
}
/* Check authorization for this directory. */
#ifdef CONFIG_THTTPD_AUTH_FILE
httpd_realloc_str(&dirname, &maxdirname, expnlen);
strlcpy(dirname, hc->expnfilename, maxdirname + 1);
cp = strrchr(dirname, '/');
if (!cp)
{
strlcpy(dirname, httpd_root, maxdirname + 1);
}
else
{
*cp = '\0';
}
if (auth_check(hc, dirname) == -1)
{
return -1;
}
/* Check if the filename is the CONFIG_THTTPD_AUTH_FILE itself -
* that's verboten.
*/
if (expnlen == sizeof(CONFIG_THTTPD_AUTH_FILE) - 1)
{
if (strcmp(hc->expnfilename, CONFIG_THTTPD_AUTH_FILE) == 0)
{
nwarn("WARNING: %s URL \"%s\" tried to retrieve an auth file\n",
httpd_ntoa(&hc->client_addr), hc->encodedurl);
httpd_send_err(hc, 403, err403title, "",
ERROR_FORM(err403form,
"The requested URL '%s' is an "
"authorization file, retrieving it is "
"not permitted.\n"),
hc->encodedurl);
return -1;
}
}
else if (expnlen >= sizeof(CONFIG_THTTPD_AUTH_FILE) &&
strcmp(&hc->expnfilename[expnlen - sizeof(CONFIG_THTTPD_AUTH_FILE)
+ 1], CONFIG_THTTPD_AUTH_FILE) == 0 &&
hc->expnfilename[expnlen - sizeof(CONFIG_THTTPD_AUTH_FILE)] == '/')
{
nwarn("WARNING: %s URL \"%s\" tried to retrieve an auth file\n",
httpd_ntoa(&hc->client_addr), hc->encodedurl);
httpd_send_err(hc, 403, err403title, "",
ERROR_FORM(err403form,
"The requested URL '%s' is an authorization "
"file, retrieving it is not permitted.\n"),
hc->encodedurl);
return -1;
}
#endif /* CONFIG_THTTPD_AUTH_FILE */
/* Referer check. */
if (!check_referer(hc))
{
return -1;
}
/* Is it in the CGI area? */
#ifdef CONFIG_THTTPD_CGI_PATTERN
if (!fnmatch(CONFIG_THTTPD_CGI_PATTERN, hc->expnfilename, 0))
{
return cgi(hc);
}
#endif
/* It's not CGI. If it's executable or there's pathinfo, someone's trying
* to either serve or run a non-CGI file as CGI. Either case is
* prohibited.
*/
if (hc->sb.st_mode & S_IXOTH)
{
nwarn("WARNING: %s URL \"%s\" is executable but isn't CGI\n",
httpd_ntoa(&hc->client_addr), hc->encodedurl);
httpd_send_err(hc, 403, err403title, "",
ERROR_FORM(err403form,
"The requested URL '%s' resolves to a file "
"which is marked executable but is not a "
"CGI file; retrieving it is forbidden.\n"),
hc->encodedurl);
return -1;
}
if (hc->pathinfo[0] != '\0')
{
nwarn("WARNING: %s URL \"%s\" has pathinfo but isn't CGI\n",
httpd_ntoa(&hc->client_addr), hc->encodedurl);
httpd_send_err(hc, 403, err403title, "",
ERROR_FORM(err403form,
"The requested URL '%s' resolves to a file "
"plus CGI-style pathinfo, but the file is "
"not a valid CGI file.\n"),
hc->encodedurl);
return -1;
}
/* Fill in range_end, if necessary. */
if (hc->got_range &&
(hc->range_end == -1 || hc->range_end >= hc->sb.st_size))
{
hc->range_end = hc->sb.st_size - 1;
}
figure_mime(hc);
if (hc->method == METHOD_HEAD)
{
send_mime(hc, 200, ok200title, hc->encodings, "", hc->type,
hc->sb.st_size, hc->sb.st_mtime);
}
else if (hc->if_modified_since != (time_t) - 1 &&
hc->if_modified_since >= hc->sb.st_mtime)
{
send_mime(hc, 304, err304title, hc->encodings, "",
hc->type, (off_t) - 1, hc->sb.st_mtime);
}
else
{
hc->file_fd = open(hc->expnfilename, O_RDONLY);
if (hc->file_fd < 0)
{
INTERNALERROR(hc->expnfilename);
httpd_send_err(hc, 500, err500title, "", err500form,
hc->encodedurl);
return -1;
}
send_mime(hc, 200, ok200title, hc->encodings, "", hc->type,
hc->sb.st_size, hc->sb.st_mtime);
}
return 0;
}