📄 request.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
/*--
Module Name: request.cpp
Abstract: per-Connection thread
--*/
#include "httpd.h"
#define AUTH_FILTER_DONE 0x1000 // no more filter calls to SF_AUTH after the 1st one in a session
BOOL IsLocalFile(PWSTR wszFile);
void CHttpRequest::HandleRequest() {
int err = 1;
RESPONSESTATUS ret = STATUS_BADREQ;
DWORD dwLength = 0;
HANDLE hFile = INVALID_HANDLE_VALUE;
HRINPUT hi;
BOOL fSendDirectoryList = FALSE;
WCHAR *wszISAPIPath;
BOOL fASP = FALSE;
BOOL fRenegotiateSSL = FALSE;
BOOL fDelayAuth = FALSE;
m_rs = STATUS_OK;
DEBUGMSG(ZONE_REQUEST,(L"HTTPD: HandleRequest: entry. Current Authorization Level = %d\r\n",m_AuthLevelGranted));
if (m_fHandleSSL) {
if (! HandleSSLHandShake()) {
m_fKeepAlive = FALSE;
return; // HandleSSLHandshake handles all error codes, logging, et al.
}
}
hi = m_bufRequest.RecvHeaders(m_socket,this);
// Even if we get an error, continue processing, because we may have read in
// binary data and have a filter installed that can convert it for us.
if (hi == INPUT_TIMEOUT || hi == INPUT_ERROR) {
// our socket has been closed as part of web server shutdown. Since we haven't done
// any real work at this stage terminate request, don't log.
if (! IsActive())
return;
// Either we have no data, or there's no filter to read in data, return
if (!g_pVars->m_fFilters || !g_pVars->m_fReadFilters || m_bufRequest.Count() == 0) {
m_fKeepAlive = FALSE;
if (hi == INPUT_ERROR && !m_fIsSecurePort) {
if (m_rs == STATUS_OK) { // if no called into fcn set the error code, set = bad req.
myretleave(m_rs = STATUS_BADREQ,61);
}
else {
myretleave(m_rs,62);
}
}
else
return; // don't send any data across socket on timeout.
}
}
if (g_pVars->m_fFilters &&
! CallFilter(SF_NOTIFY_READ_RAW_DATA))
myleave(231);
// parse the request headers
if(!ParseHeaders())
myleave(50);
// There were numerous filter calls in ParseHeaders, make sure none requested end of connection
if (m_pFInfo && m_pFInfo->m_fFAccept==FALSE)
myleave(63); // don't change m_rs, filter set it as appropriate
// If we received a request for 'OPTIONS *' or 'OPTIONS /', this requires special case
// handeling to avoid authentication and other virtual root and redirection checks.
if (m_fOptionsAsterisk || (m_pVRoot && (VERB_OPTIONS==m_idMethod) && IsRootVDir())) {
if (! m_pWebsite->m_fWebDav)
myretleave(m_rs = STATUS_NOTIMPLEM,83);
if (HandleWebDav()) {
err = 0;
}
else {
DEBUGCHK(!IS_STATUS_2XX(m_rs));
err = 64; // force error to be sent
}
goto done;
}
// If VROOT was configured to auto-redirect, skip all other checks.
if (m_pVRoot && m_pVRoot->fRedirect) {
DEBUGMSG(ZONE_REQUEST,(L"HTTPD: VROOT maps to redirect, sending client to URL %a\r\n",m_pVRoot->pszRedirectURL));
CHttpResponse resp(this,STATUS_MOVED,CONN_CLOSE);
resp.SendRedirect(m_pVRoot->pszRedirectURL);
err = 0;
goto done;
}
// check if we successfully mapped the VRoot
if (!m_wszPath)
myretleave(m_rs = STATUS_NOTFOUND, 59);
//
// Authentication checks come first
//
// Check if we're requiring SSL on this VRoot.
if ((!m_fIsSecurePort && ((GetPerms() & HSE_URL_FLAGS_SSL) || (GetPerms() & HSE_URL_FLAGS_SSL128))) ||
(m_fIsSecurePort && (m_SSLInfo.m_dwSessionKeySize < 128) && (GetPerms() & HSE_URL_FLAGS_SSL128)))
{
if (g_pVars->m_fFilters)
CallFilter(SF_NOTIFY_ACCESS_DENIED);
myretleave(m_rs = STATUS_FORBIDDEN, 55);
}
// If we want or require a client cert and we don't have one, we must renegotiate with the client.
// Only call SSLRenegotiateRequest() once per session, in event that only HSE_URL_FLAGS_NEGO_CERT
// is set and client doesn't provide a cert.
if (!m_SSLInfo.m_pClientCertContext && (GetPerms() & (HSE_URL_FLAGS_REQUIRE_CERT | HSE_URL_FLAGS_NEGO_CERT)) && !m_fPerformedSSLRenegotiateRequest) {
if (SSLRenegotiateRequest())
fRenegotiateSSL = TRUE;
else if (GetPerms() & HSE_URL_FLAGS_REQUIRE_CERT) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: SSL renegotiate failed and we require a certificate, fail request\r\n"));
myleave(236);
}
// If we're using client certificates and we're renegotiating, we haven't read in enough
// information to determine whether to grant or deny access at this point.
fDelayAuth = TRUE;
}
if (!fDelayAuth) {
if (AllowNTLM() || AllowBasic() || AllowNegotiate() || g_pVars->m_fSSL)
HandleAuthentication();
if ( !CheckAuth())
myretleave(m_rs = STATUS_UNAUTHORIZED, 52);
}
// ISAPI filters gets only 1 call per session to notify them of SF_NOTIFY_AUTHENTICATION event.
if (! CallAuthFilterIfNeeded())
myleave(294);
if (!ReadPostData(g_pVars->m_dwPostReadSize,TRUE,fRenegotiateSSL))
myleave(233); // ReadPostData sets m_rs
// If content-length is less than # of bytes we've read in, then we have
// a garbage request (possibly multiplexing). CE web server supports
// one specific case of multiplexing (allows multiple POSTS in one packet)
// to work around a known broken HTTP client, however does not handle
// general case so fail out if this happens.
// (-2 because sometimes browsers tack on CRLF to end of POST data but
// don't count it in body-length).
if ((int)m_dwContentLength < (int)(m_bufRequest.Count()-2)) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: content-length is greater than muber of bytes received, probably HTTP 1.1 pipelined request. HTTPD does not support this.\r\n"));
m_rs = STATUS_BADREQ;
myleave(241);
}
// For SSL case we should have client cert by now, so do authentication.
if (fDelayAuth) {
DEBUGCHK(IsSecure());
if (!m_SSLInfo.m_pClientCertContext && (GetPerms() & HSE_URL_FLAGS_REQUIRE_CERT)) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: Virtual root set HSE_URL_FLAGS_REQUIRE_CERT but no cert from client, ending request\r\n"));
m_rs = STATUS_FORBIDDEN;
myleave(239);
}
HandleSSLClientCertCheck();
if (!CheckAuth())
myretleave(m_rs = STATUS_UNAUTHORIZED, 153);
}
// if we're in middle of authenticating, jump past other stuff to end
if (m_AuthState.m_Stage == SEC_STATE_PROCESSING)
myretleave(m_rs = STATUS_UNAUTHORIZED, 65);
m_dwFileAttributes = GetFileAttributes(m_wszPath);
if (! (m_wszExt && g_pVars->m_fExtensions && MapScriptExtension(m_wszExt,&wszISAPIPath,&fASP)) && VerbSupportsDirBrowsing() ) {
if ((-1) == m_dwFileAttributes)
myretleave(m_rs = GLEtoStatus(), 60);
if (IsDirectory(m_dwFileAttributes)) {
if (SendRedirectIfNeeded()) {
err = 0;
goto done;
}
// If there's no default page then we send dir list (later, after some
// extra checking). If there is a default page match m_wszPath is
// updated appropriatly. This must be done before script processing.
fSendDirectoryList = !MapDirToDefaultPage();
}
}
// HandleScript returns true if page maps to ASP or ISAPI DLL, regardless of whether
// we have correct permissions, component was included, there was an error, etc.
// HandleScript sets its own errors.
if ( !fSendDirectoryList && HandleScript()) {
err = ! (IS_STATUS_2XX(m_rs) || (m_rs == STATUS_MOVED)); // Only send message on internal error.
goto done;
}
if (m_idMethod != VERB_GET && m_idMethod != VERB_HEAD) {
// if it's not an ISAPI or ASP or there's no DAV, we can't handle anything but GET and HEAD
if (m_idMethod == VERB_UNKNOWN || !m_pWebsite->m_fWebDav) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: Request fails, received unknown verb. Non ISAPI/ASP pages can only handle GET+HEAD\r\n"));
myretleave(m_rs = STATUS_NOTIMPLEM, 54);
}
// Let DAV handle all other flavors of verbs.
// If HandleWebDav() fails and wants HandleRequest() to send error, it will
// set m_rs appropriatly. Otherwise HandleWebDav succeeded.
// In either event no need to do rest of the code in this function, so goto-done.
if (HandleWebDav()) {
err = 0;
}
else {
DEBUGCHK(!IS_STATUS_2XX(m_rs));
err = 64; // force error to be sent
}
goto done;
}
// check permissions
if (!(GetPerms() & HSE_URL_FLAGS_READ)) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: Request fails, vroot does not have HSE_URL_FLAGS_READ permissions\r\n"));
if (g_pVars->m_fFilters)
CallFilter(SF_NOTIFY_ACCESS_DENIED);
myretleave(m_rs = STATUS_FORBIDDEN, 55);
}
if (fSendDirectoryList) {
// In this case there's no default page but directory browsing is turned
// off. Return same error code IIS does.
if (FALSE == AllowDirBrowse()) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: Request fails, directory browsing is disabled\r\n"));
if (g_pVars->m_fFilters)
CallFilter(SF_NOTIFY_ACCESS_DENIED);
myretleave(m_rs = STATUS_FORBIDDEN,78);
}
if(!EmitDirListing())
myretleave(m_rs = STATUS_INTERNALERR, 53);
err=0;
goto done;
}
// If we get to here then we're just sending a plain old static file.
// try to open the file & get the length
if (INVALID_HANDLE_VALUE == (hFile = MyOpenReadFile(m_wszPath))) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: Request fails, unable to open file %s, GLE=0x%08x\r\n",m_wszPath,GetLastError()));
myretleave(m_rs = GLEtoStatus(), 56);
}
// get the size
if (((DWORD)-1) == (dwLength = GetFileSize(hFile, 0)))
myretleave(m_rs = GLEtoStatus(), 57);
// if it's a GET check if-modified-since
if ((m_idMethod==VERB_GET) && IsNotModified(hFile, dwLength))
myretleave(m_rs = STATUS_NOTMODIFIED, 58);
// if it's a HTTP/0.9 request, just send back the body. NO headers
if (m_dwVersion <= MAKELONG(9, 0)) {
DEBUGMSG(ZONE_RESPONSE, (L"HTTPD: Sending HTTP/0.9 response with NO headers\r\n"));
SendFile(m_socket, hFile, this);
}
else {
// create a response object & send response
// if it's a head request, skip the actual body
CHttpResponse resp(this);
resp.SetBody(hFile, m_wszExt, dwLength);
resp.SendResponse();
}
DEBUGMSG(ZONE_REQUEST, (L"HTTPD: HTTP Request SUCCEEDED\r\n"));
err = 0;
ret = m_rs = STATUS_OK;
done:
MyCloseHandle(hFile);
if(err) {
// end this session ASAP if we've encountered an error.
if (m_rs == STATUS_INTERNALERR || m_rs == STATUS_BADREQ)
m_fKeepAlive = FALSE;
// if there's been an error but we're doing keep-alives, it's possible
// there's POST data we haven't read in. We need to read this
// before sending response, or next time we recv() HTTP headers we'll
// start in middle of POST rather than in the new request.
if (m_fKeepAlive) {
DEBUGCHK(m_dwContentLength - m_bufRequest.Count() >= 0);
// If the amount of POST data we'd have to read is greater
// than the max we're willing to, then we end the request now.
if (m_dwContentLength < g_pVars->m_dwPostReadSize) {
if ((int)(m_dwContentLength - m_bufRequest.Count()) > 0) {
DEBUGMSG(ZONE_REQUEST,(L"HTTP: HandleRequest: Error occured on keepalive, reading %d POST bytes now\r\n",m_dwContentLength - m_bufRequest.Count()));
ReadPostData(m_dwContentLength - m_bufRequest.Count(),FALSE,FALSE);
}
}
else {
DEBUGMSG(ZONE_REQUEST,(L"HTTP: HandleRequest: client requested keep-alive but has content-length set = %d bytes, more than what we will read. End session\r\n",m_dwContentLength));
m_fKeepAlive = FALSE;
}
}
CHttpResponse resp(this, m_rs);
resp.SetDefaultBody();
resp.SendResponse();
DEBUGMSG(ZONE_REQUEST, (L"HTTPD: HTTP Request FAILED: GLE=%d err=%d status=%d (%d, %a)\r\n",
GetLastError(), err, ret, rgStatus[ret].dwStatusNumber, rgStatus[ret].pszStatusText));
}
// if in middle of NTLM request, don't do this stuff
if (m_AuthState.m_Stage != SEC_STATE_PROCESSING) {
if (g_pVars->m_fFilters) {
CallFilter(SF_NOTIFY_END_OF_REQUEST);
CallFilter(SF_NOTIFY_LOG);
}
g_pVars->m_pLog->WriteLog(this);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -