/*-- 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; }