windows-nt/Source/XPSP1/NT/net/upnp/host/upnphost/udhhttp/isapi.cpp

344 lines
9.8 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*--
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;
}