344 lines
9.8 KiB
C++
344 lines
9.8 KiB
C++
/*--
|
|
Copyright (c) 1999-2000 Microsoft Corporation. All rights reserved.
|
|
Module Name: ISAPI.CPP
|
|
Abstract: ISAPI handling code
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
#pragma hdrstop
|
|
|
|
#include "httpd.h"
|
|
|
|
|
|
// Prefix for GetServerVariable for generic HTTP header retrieval, so "HTTPD_FOOBAR" gets HTTP header FOOBAR.
|
|
static const char cszHTTP[] = "HTTP_";
|
|
static const DWORD dwHTTP = CONSTSIZEOF(cszHTTP);
|
|
|
|
|
|
// This function is used when looking through HTTP headers for HTTP_VARIABLE. We
|
|
// can't use strcmp because headers (for most part) have words separated by "-",
|
|
// the pszVar format has vars separated by "_". IE pszVar=HTTP_ACCEPT_LANGUAGE
|
|
// should return for us HTTP header "ACCEPT-LANGUAGE"
|
|
BOOL GetVariableStrCmp(PSTR pszHeader, PSTR pszVar, DWORD_PTR dwLen)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
for (DWORD i = 0; i < dwLen; i++)
|
|
{
|
|
if ( (tolower(*pszHeader) != tolower(*pszVar)) &&
|
|
(*pszVar != '_' && *pszHeader != '-'))
|
|
{
|
|
goto done;
|
|
}
|
|
pszHeader++;
|
|
pszVar++;
|
|
}
|
|
fRet = (*pszHeader == ':');
|
|
|
|
done:
|
|
return fRet;
|
|
}
|
|
|
|
BOOL CHttpRequest::GetServerVariable(PSTR pszVar, PVOID pvOutBuf, PDWORD pdwOutSize, BOOL fFromFilter)
|
|
{
|
|
DWORD dwLen;
|
|
char szBuf[MAXHEADERS];
|
|
PSTR pszRet = (PSTR)-1;
|
|
PSTR pszTrav = NULL;
|
|
CHAR chSave;
|
|
|
|
if (0==_stricmp(pszVar, "APPL_MD_PATH"))
|
|
pszRet = "/LM/W3SVC/1/ROOT/";
|
|
else if (0==_stricmp(pszVar, "APPL_PHYSICAL_PATH"))
|
|
{
|
|
strcpy (szBuf, "/");
|
|
dwLen = sizeof(szBuf);
|
|
|
|
if (MapURLToPath (szBuf, &dwLen))
|
|
pszRet = szBuf;
|
|
}
|
|
else if (0==_stricmp(pszVar, "SERVER_PORT"))
|
|
{
|
|
_itoa (g_pVars->m_dwListenPort, szBuf, 10);
|
|
pszRet = szBuf;
|
|
}
|
|
else if (0==_stricmp(pszVar, "AUTH_TYPE"))
|
|
pszRet = m_pszAuthType;
|
|
else if (0 == _stricmp(pszVar, "AUTH_USER"))
|
|
pszRet = m_pszRemoteUser;
|
|
else if (0 == _stricmp(pszVar, "AUTH_PASSWORD"))
|
|
pszRet = m_pszPassword;
|
|
else if (0==_stricmp(pszVar, "CONTENT_LENGTH"))
|
|
{
|
|
sprintf(szBuf, "%d", m_dwContentLength);
|
|
pszRet = szBuf;
|
|
}
|
|
else if (0==_stricmp(pszVar, "CONTENT_TYPE"))
|
|
pszRet = m_pszContentType;
|
|
else if (0==_stricmp(pszVar, "PATH_INFO"))
|
|
pszRet = m_pszPathInfo;
|
|
else if (0==_stricmp(pszVar, "PATH_TRANSLATED"))
|
|
pszRet = m_pszPathTranslated;
|
|
else if (0==_stricmp(pszVar, "QUERY_STRING"))
|
|
pszRet = m_pszQueryString;
|
|
else if (0==_stricmp(pszVar, "REMOTE_ADDR") || 0==_stricmp(pszVar, "REMOTE_HOST"))
|
|
{
|
|
GetRemoteAddress(m_socket, szBuf);
|
|
pszRet = szBuf;
|
|
}
|
|
// Note: The following is a non-standard ISAPI variable
|
|
//
|
|
else if (0==_stricmp(pszVar, "LOCAL_ADDR"))
|
|
{
|
|
GetLocalAddress(m_socket, szBuf);
|
|
pszRet = szBuf;
|
|
}
|
|
// ----End note
|
|
else if (0==_stricmp(pszVar, "REMOTE_USER"))
|
|
pszRet = m_pszRemoteUser;
|
|
else if (0==_stricmp(pszVar, "UNMAPPED_REMOTE_USER"))
|
|
pszRet = m_pszRemoteUser; /*m_pszRawRemoteUser; BUBUG: what is rawremoteuser?*/
|
|
else if (0==_stricmp(pszVar, "REQUEST_METHOD"))
|
|
pszRet = m_pszMethod;
|
|
else if (0==_stricmp(pszVar, "URL"))
|
|
{
|
|
pszRet = m_pszURL;
|
|
}
|
|
else if (0==_stricmp(pszVar, "SCRIPT_NAME"))
|
|
{
|
|
if (fFromFilter)
|
|
pszRet = NULL;
|
|
else
|
|
pszRet = m_pszURL;
|
|
}
|
|
else if (0==_stricmp(pszVar, "SERVER_NAME"))
|
|
{
|
|
if (0 != gethostname(szBuf, sizeof(szBuf)))
|
|
szBuf[0] = '\0';
|
|
|
|
pszRet = szBuf;
|
|
}
|
|
|
|
// HTTP_VERSION is version info as received from the client
|
|
else if (0==_stricmp(pszVar, "HTTP_VERSION"))
|
|
{
|
|
sprintf(szBuf, cszHTTPVER, HIWORD(m_dwVersion), LOWORD(m_dwVersion));
|
|
pszRet = szBuf;
|
|
}
|
|
|
|
// SERVER_PROTOCOL is the version of http server supports, currently 1.0
|
|
else if (0==_stricmp(pszVar, "SERVER_PROTOCOL"))
|
|
{
|
|
strcpy(szBuf,"HTTP/1.1");
|
|
pszRet = szBuf;
|
|
}
|
|
else if (0==_stricmp(pszVar, "SERVER_SOFTWARE"))
|
|
pszRet = (PSTR)g_pVars->m_pszServerID;
|
|
else if (0==_stricmp(pszVar, "ALL_HTTP"))
|
|
pszRet = 0;
|
|
|
|
// ALL_RAW return http headers, other than the simple request line. (fixes BUG 11991)
|
|
// The way our buffer is set up, we can have POST data immediatly following
|
|
// header data. So the client doesn't get confused, we have to set a \0 to it.
|
|
else if (0 == _stricmp(pszVar, "ALL_RAW"))
|
|
{
|
|
pszRet = m_bufRequest.Headers();
|
|
// skip past simple request line.
|
|
pszRet = strstr(pszRet,"\r\n");
|
|
pszRet += 2;
|
|
|
|
// If there's unaccessed data, buffer has POST data in it
|
|
if (m_bufRequest.HasPostData())
|
|
{
|
|
pszTrav = strstr(pszRet,("\r\n\r\n")) + 4;
|
|
chSave = *pszTrav; *pszTrav = 0;
|
|
}
|
|
}
|
|
else if (0==_stricmp(pszVar, "HTTP_ACCEPT"))
|
|
pszRet = m_pszAccept;
|
|
else if (0==_strnicmp(pszVar,cszHTTP,dwHTTP))
|
|
{
|
|
PSTR pszStart = pszVar + dwHTTP;
|
|
PSTR pszEnd = strchr(pszStart,'\0');
|
|
DWORD_PTR dwLen = pszEnd - pszStart;
|
|
if (dwLen > 1)
|
|
{
|
|
DWORD dwOutLen = 0;
|
|
PSTR pszHeader = m_bufRequest.Headers();
|
|
do
|
|
{
|
|
if (GetVariableStrCmp(pszHeader,pszStart,dwLen))
|
|
{
|
|
pszHeader += dwLen+1; // skip past header + ':'
|
|
while ( (pszHeader)[0] != '\0' && ((pszHeader[0] == ' ') ||
|
|
(pszHeader[0] == '\t')))
|
|
{
|
|
++(pszHeader);
|
|
}
|
|
|
|
pszTrav = strstr(pszHeader,"\r\n");
|
|
if (pszTrav)
|
|
{
|
|
chSave = '\r';
|
|
*pszTrav = 0;
|
|
pszRet = pszHeader;
|
|
}
|
|
break;
|
|
}
|
|
|
|
pszHeader = strstr(pszHeader,"\r\n")+2;
|
|
if (*pszHeader == '\r')
|
|
{
|
|
DEBUGCHK(*(pszHeader+1) == '\n');
|
|
break;
|
|
}
|
|
} while (1);
|
|
}
|
|
}
|
|
|
|
// end of pseudo-case stmnt
|
|
if ((PSTR)(-1) == pszRet)
|
|
{
|
|
// unknown var
|
|
SetLastError(ERROR_INVALID_INDEX);
|
|
return FALSE;
|
|
}
|
|
// no such header/value. return empty (not NULL!) string
|
|
if (!pszRet)
|
|
pszRet = (PSTR)cszEmpty;
|
|
|
|
if ((dwLen = strlen(pszRet)+1) > *pdwOutSize)
|
|
{
|
|
*pdwOutSize = dwLen;
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
if (pszTrav)
|
|
*pszTrav = chSave;
|
|
return FALSE;
|
|
}
|
|
// Change: Check is done here, not in ::GetServerVariable. Lets us get size
|
|
// with out buf = NULL
|
|
if (NULL == pvOutBuf)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
memcpy(pvOutBuf, pszRet, dwLen);
|
|
if (pszTrav)
|
|
*pszTrav = chSave;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CHttpRequest::WriteClientAsync(PVOID pvBuf, PDWORD pdwSize, BOOL fFromFilter)
|
|
{
|
|
BOOL ret = WriteClient(pvBuf, pdwSize, fFromFilter);
|
|
|
|
if (m_pfnCompletion != NULL)
|
|
{
|
|
DWORD dwStatus;
|
|
|
|
if (ret)
|
|
dwStatus = ERROR_SUCCESS;
|
|
else
|
|
dwStatus = GetLastError();
|
|
|
|
__try
|
|
{
|
|
(*m_pfnCompletion)(m_pECB, m_pvContext, *pdwSize, dwStatus);
|
|
}
|
|
__except(1) // catch all errors
|
|
{
|
|
TraceTag(ttidWebServer, "ISAPI I/O completion callback caused exception 0x%08x and was terminated", GetExceptionCode());
|
|
g_pVars->m_pLog->WriteEvent(IDS_HTTPD_EXT_EXCEPTION,m_wszPath,GetExceptionCode(),L"IOCompletionProc",GetLastError());
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
BOOL CHttpRequest::WriteClient(PVOID pvBuf, PDWORD pdwSize, BOOL fFromFilter)
|
|
{
|
|
int cbSendBuf = *pdwSize;
|
|
PSTR pszSendBuf = (PSTR) pvBuf;
|
|
BOOL ret = FALSE;
|
|
|
|
// On a HEAD request to an ASP page or ISAPI extension results in no data
|
|
// being sent back, however for filters we do send data back when they
|
|
// tell us to with WriteClient call.
|
|
if (m_idMethod == TOK_HEAD && !fFromFilter)
|
|
{
|
|
ret = TRUE;
|
|
goto done;
|
|
}
|
|
|
|
// are we buffering? Note: Only ASP can set this
|
|
if (m_fBufferedResponse)
|
|
{
|
|
return m_bufRespBody.AppendData((PSTR) pvBuf, (int) *pdwSize);
|
|
}
|
|
|
|
if (g_pVars->m_fFilters &&
|
|
! CallFilter(SF_NOTIFY_SEND_RAW_DATA, &pszSendBuf, &cbSendBuf))
|
|
goto done;
|
|
|
|
if (cbSendBuf != send(m_socket, pszSendBuf, cbSendBuf, 0))
|
|
{
|
|
TraceTag(ttidWebServer, "HTTPD: SendBuffer FAILED. GLE=%d", GetLastError());
|
|
goto done;
|
|
}
|
|
|
|
ret = TRUE;
|
|
done:
|
|
return ret;
|
|
}
|
|
|
|
|
|
// Acts as the custom header class (for Filters call to AddHeader, SetHeader
|
|
// and for ASP Call to AddHeader and for ASP Cookie handler.
|
|
|
|
// We made this function part of the class because there's no reason to memcpy
|
|
// data into a temp buffer before memcpy'ing it into the real buffer.
|
|
|
|
BOOL CBuffer::AddHeader(PSTR pszName, PSTR pszValue, BOOL fAddColon)
|
|
{
|
|
DEBUG_CODE_INIT;
|
|
BOOL ret = FALSE;
|
|
PSTR pszTrav;
|
|
|
|
if (!pszName || !pszValue)
|
|
{
|
|
DEBUGCHK(0);
|
|
return FALSE;
|
|
}
|
|
|
|
int cbName = strlen(pszName);
|
|
int cbValue = strlen(pszValue);
|
|
|
|
// we need a buffer size of pszName + pszValue, a space, a trailing \r\n, and \0
|
|
int cbTotal = cbName + cbValue + sizeof("\r\n") + (fAddColon ? 1 : 0);
|
|
|
|
if ( ! AllocMem( cbTotal ))
|
|
myleave(900);
|
|
|
|
pszTrav = m_pszBuf + m_iNextIn;
|
|
memcpy(pszTrav, pszName, cbName);
|
|
pszTrav += cbName;
|
|
|
|
// put space between name and value and colon if needed.
|
|
if (fAddColon)
|
|
*pszTrav++ = ':';
|
|
|
|
*pszTrav++ = ' ';
|
|
|
|
memcpy(pszTrav, pszValue, cbValue);
|
|
memcpy(pszTrav + cbValue,"\r\n", sizeof("\r\n"));
|
|
|
|
m_iNextIn += cbTotal;
|
|
ret = TRUE;
|
|
done:
|
|
TraceTag(ttidWebServer, "HTTPD: CBuffer::AddHeader failed, err = %d",err);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|