Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

500 lines
16 KiB

  1. /*--
  2. Copyright (c) 1995-1998 Microsoft Corporation
  3. Module Name: filter.cpp
  4. Author: John Spaith
  5. Abstract: ISAPI Filter call back functions
  6. --*/
  7. // BUGBUG - don't support advanced header management on SF_NOTIFY_SEND_RESPONSE.
  8. // IIS allows response headers to be deleted or set to different values after
  9. // they are set with a call to SetHeader or AddHeader.
  10. // Fix: None. This takes +300 lines of code, doesn't add much. Instead we
  11. // dump all header data in a buffer.
  12. #include "pch.h"
  13. #pragma hdrstop
  14. #include "httpd.h"
  15. // Allocates and returns a buffer size cbSize. This will be deleted at the
  16. // end of the session.
  17. // The MSDN docs on this claim this returns a BOOL. Looking in httpfilt.h
  18. // shows that the actual implementation is a VOID *.
  19. VOID* WINAPI AllocMem(PHTTP_FILTER_CONTEXT pfc, DWORD cbSize, DWORD dwReserved)
  20. {
  21. CHECKPFC(pfc);
  22. // NOTE: alloc mem isn't directly related to connection, don't check m_pFINfo->fAccept like
  23. // the other filters do
  24. return((CHttpRequest*)pfc->ServerContext)->m_pFInfo->AllocMem(cbSize,dwReserved);
  25. }
  26. VOID* CFilterInfo::AllocMem(DWORD cbSize, DWORD dwReserved)
  27. {
  28. if (cbSize == 0)
  29. return NULL;
  30. if (NULL == m_pAllocMem)
  31. {
  32. m_pAllocMem = MyRgAllocZ(PVOID,VALUE_GROW_SIZE);
  33. if (! (m_pAllocMem))
  34. {
  35. TraceTag(ttidWebServer, "CFilterInfo::AllocMem failed on inital alloc, GLE=%d",GetLastError());
  36. return NULL;
  37. }
  38. }
  39. else if (m_nAllocBlocks % VALUE_GROW_SIZE == 0)
  40. {
  41. m_pAllocMem = MyRgReAlloc(PVOID,m_pAllocMem,m_nAllocBlocks,m_nAllocBlocks + VALUE_GROW_SIZE);
  42. if ( !m_pAllocMem)
  43. {
  44. TraceTag(ttidWebServer, "CFilterInfo::AllocMem failed on re-allocing for block %d, GLE=%d",m_nAllocBlocks+1,GetLastError());
  45. return NULL;
  46. }
  47. }
  48. if (! (m_pAllocMem[m_nAllocBlocks] = MyRgAllocNZ(PVOID,cbSize)))
  49. {
  50. TraceTag(ttidWebServer, "CFilterInfo::AllocMem failed on allocating block %d, GLE=%d",m_nAllocBlocks+1,GetLastError());
  51. return NULL;
  52. }
  53. m_nAllocBlocks++;
  54. return m_pAllocMem[m_nAllocBlocks-1];
  55. }
  56. void CFilterInfo::FreeAllocMem()
  57. {
  58. DWORD dwI;
  59. if (0 == m_nAllocBlocks || ! (m_pAllocMem))
  60. return;
  61. for (dwI = 0; dwI < m_nAllocBlocks; dwI++)
  62. {
  63. MyFree(m_pAllocMem[dwI]);
  64. }
  65. MyFree(m_pAllocMem);
  66. }
  67. // Adds httpd headers.
  68. // In effect this is identical to a call to SetHeader or AddHeader except that
  69. // the user formats the whole string (name and value) before caalling
  70. // AddResponseHeaders, in Add/Set Header they come in 2 seperate fields.
  71. BOOL WINAPI AddResponseHeaders(PHTTP_FILTER_CONTEXT pfc,LPSTR lpszHeaders,DWORD dwReserved)
  72. {
  73. CHECKPFC(pfc);
  74. CHECKPTR(lpszHeaders);
  75. CHECKFILTER(pfc);
  76. if (dwReserved != 0)
  77. {
  78. SetLastError(ERROR_INVALID_PARAMETER);
  79. return FALSE;
  80. }
  81. return((CHttpRequest*)pfc->ServerContext)->AddResponseHeaders(lpszHeaders,dwReserved);
  82. }
  83. BOOL CHttpRequest::AddResponseHeaders(LPSTR lpszHeaders,DWORD dwReserved)
  84. {
  85. // Can't set response headers on 0.9 version. Err code is like IIS.
  86. if (m_dwVersion <= MAKELONG(9, 0))
  87. {
  88. SetLastError(ERROR_NOT_SUPPORTED);
  89. return FALSE;
  90. }
  91. // According to MSDN, it's invalid to use this fcn during or after
  92. // SEND_RESPONSE event. However, IIS doesn't report an error if
  93. // this is called when it shouldn't be, so neither do we.
  94. if ( m_pFInfo->m_dwSFEvent & (SF_NOTIFY_END_OF_NET_SESSION | SF_NOTIFY_END_OF_REQUEST |
  95. SF_NOTIFY_LOG | SF_NOTIFY_SEND_RAW_DATA |
  96. SF_NOTIFY_SEND_RESPONSE))
  97. return TRUE;
  98. // Note: We don't use CBuffer::AddHeaders because it does formatting, we assume
  99. // here filter alreadf formatted everything correctly.
  100. return m_bufRespHeaders.AppendData(lpszHeaders, strlen(lpszHeaders));
  101. }
  102. BOOL WINAPI GetServerVariable(PHTTP_FILTER_CONTEXT pfc, PSTR psz, PVOID pv, PDWORD pdw)
  103. {
  104. CHECKPFC(pfc);
  105. CHECKPTRS3(psz, pv, pdw);
  106. // We don't check m_fFAccept here because this function is read only,
  107. // doesn't try and do anything across the network. Like IIS.
  108. return((CHttpRequest*)pfc->ServerContext)->GetServerVariable(psz, pv, pdw, TRUE);
  109. }
  110. BOOL WINAPI WriteClient(PHTTP_FILTER_CONTEXT pfc, PVOID pv, PDWORD pdw, DWORD dwFlags)
  111. {
  112. CHECKPFC(pfc);
  113. CHECKPTRS2(pv, pdw);
  114. // CHECKFILTER(pfc); // IIS always accepts WriteClient, even if filter has returned an error at some point.
  115. if (dwFlags != 0)
  116. {
  117. SetLastError(ERROR_INVALID_PARAMETER);
  118. return FALSE;
  119. }
  120. return((CHttpRequest*)pfc->ServerContext)->WriteClient(pv,pdw,TRUE);
  121. }
  122. // Misc Server features. See MSDN for full docs.
  123. BOOL WINAPI ServerSupportFunction(PHTTP_FILTER_CONTEXT pfc, enum SF_REQ_TYPE sfReq,
  124. PVOID pData, ULONG_PTR ul1, ULONG_PTR ul2)
  125. {
  126. CHECKPFC(pfc);
  127. CHECKFILTER(pfc);
  128. return((CHttpRequest*)pfc->ServerContext)->ServerSupportFunction(sfReq, pData, ul1, ul2);
  129. }
  130. BOOL CHttpRequest::ServerSupportFunction(enum SF_REQ_TYPE sfReq,PVOID pData,
  131. ULONG_PTR ul1, ULONG_PTR ul2)
  132. {
  133. switch (sfReq)
  134. {
  135. case SF_REQ_ADD_HEADERS_ON_DENIAL:
  136. {
  137. return MyStrCatA(&m_pFInfo->m_pszDenyHeader,(PSTR) pData);
  138. }
  139. case SF_REQ_DISABLE_NOTIFICATIONS:
  140. {
  141. // u11 has data as to which flags to deactivate for this session,
  142. // For example, setting u11 = SF_NOTIFY_SEND_RAW_DATA | SF_NOTIFY_LOG
  143. // would disable notifications to the filter that called this fcn for
  144. // those events on this request only (per request, not per session!).
  145. // These values are reset to 1's at the end of each request in CHttpRequest::ReInit
  146. m_pFInfo->m_pdwEnable[m_pFInfo->m_iFIndex] &= (m_pFInfo->m_pdwEnable[m_pFInfo->m_iFIndex] ^ ul1);
  147. return TRUE;
  148. }
  149. case SF_REQ_SEND_RESPONSE_HEADER:
  150. {
  151. // no Connection header...let ISAPI send one if it wants
  152. CHttpResponse resp(m_socket, STATUS_OK, CONN_CLOSE,this);
  153. m_rs = STATUS_OK;
  154. // no body, default or otherwise (leave that to the ISAPI), but add default headers
  155. resp.SendResponse((PSTR) ul1, (PSTR) pData, TRUE);
  156. // The reasons for doing this are documented in FilterNoResponse()
  157. // Note: We don't check this sent headers here because IIS doesn't,
  158. // so it's possible to make multiple calls with this option even though it
  159. // doesn't make that much sense for a filter to do so.
  160. m_pFInfo->m_fSentHeaders = TRUE;
  161. m_fKeepAlive = FALSE;
  162. return TRUE;
  163. }
  164. case SF_REQ_SET_NEXT_READ_SIZE:
  165. {
  166. m_pFInfo->m_dwNextReadSize = (DWORD)ul1;
  167. return TRUE;
  168. }
  169. // BUGBUG -- unsupported flags.
  170. // case SF_REQ_NORMALIZE_URL: // TBD
  171. // case SF_REQ_GET_CONNID: // IIS doesn't support in versions 4+
  172. // case SF_REQ_GET_PROPERTY: // Relates to meta database IIS uses but we don't
  173. // case SF_REQ_SET_PROXY_INFO: // No proxy stuff in CE
  174. default:
  175. {
  176. TraceTag(ttidWebServer, "Unsupported or invald ServerSupportFcn request = 0x%08x",sfReq);
  177. SetLastError(ERROR_INVALID_PARAMETER);
  178. return FALSE;
  179. }
  180. }
  181. return TRUE;
  182. }
  183. //***************************************************************************
  184. // Extended filter header options
  185. //
  186. // These fcns allow the filter to perform more advanced header fncs automatically,
  187. // On PREPROC_HEADERS event, it's possible for the filter to change CHttpRequest
  188. // state information through the SetHeaders and AddHeaders callback. We support
  189. // changing URL, Version, If-modified-since, and method through the preproc
  190. // headers calls.
  191. // In a SEND_RESPONSE event, it's possible for the filter to modify the response
  192. // headers through SetHEaders or AddHeaders.
  193. // Unlike IIS, we don't allow the filter to delete a header once it's set, or
  194. // to overwrite a header's information with new info. We only allow the header
  195. // data to be appended to.
  196. //***************************************************************************
  197. BOOL WINAPI GetHeader(PHTTP_FILTER_CONTEXT pfc, LPSTR lpszName, LPVOID lpvBuffer, LPDWORD lpdwSize)
  198. {
  199. CHECKPFC(pfc);
  200. CHECKPTRS3(lpszName, lpvBuffer, lpdwSize);
  201. CHECKFILTER(pfc);
  202. return((CHttpRequest*)pfc->ServerContext)->GetHeader(lpszName, lpvBuffer, lpdwSize);
  203. }
  204. BOOL WINAPI SetHeader(PHTTP_FILTER_CONTEXT pfc, LPSTR lpszName, LPSTR lpszValue)
  205. {
  206. CHECKPFC(pfc);
  207. CHECKPTRS2(lpszName, lpszValue);
  208. CHECKFILTER(pfc);
  209. return((CHttpRequest*)pfc->ServerContext)->SetHeader(lpszName, lpszValue);
  210. }
  211. // Retrieves raw header value for specified name.
  212. BOOL CHttpRequest::GetHeader(LPSTR lpszName, LPVOID lpvBuffer, LPDWORD lpdwSize)
  213. {
  214. DEBUG_CODE_INIT;
  215. BOOL ret = FALSE;
  216. DWORD cbSizeNeeded;
  217. char szBuf[MAXHEADERS];
  218. PSTR pszRet = (PSTR)-1;
  219. PSTR pszTrav = NULL;
  220. PSTR pszEndOfHeaders = NULL;
  221. DWORD cbName;
  222. // this is the only event we allow this for. This is like IIS, but not docced in MSDN.
  223. if (m_pFInfo->m_dwSFEvent != SF_NOTIFY_PREPROC_HEADERS)
  224. {
  225. SetLastError(ERROR_INVALID_INDEX);
  226. myleave(1405);
  227. }
  228. // For method, url, and version (from simple request line) we get the data
  229. // from CHttpRequest
  230. if (0==_stricmp(lpszName, "version"))
  231. {
  232. // There's no sprintf in older versions of CE.
  233. // sprintf(szBuf, "HTTP/%d.%d", HIWORD(m_dwVersion), LOWORD(m_dwVersion));
  234. WriteHTTPVersion(szBuf,m_dwVersion);
  235. pszRet = szBuf;
  236. }
  237. else if (0 == _stricmp(lpszName, "url"))
  238. pszRet = m_pszURL;
  239. else if (0 == _stricmp(lpszName, "method"))
  240. pszRet = m_pszMethod;
  241. else
  242. {
  243. // if it's not one of the 3 special values, we search through the raw
  244. // buffer for the header name.
  245. pszTrav = (PSTR) m_bufRequest.Headers();
  246. pszTrav = strstr(pszTrav,cszCRLF); // skip past simple http header
  247. pszEndOfHeaders = pszTrav + m_bufRequest.GetINextOut();
  248. cbName = strlen(lpszName);
  249. for (; pszTrav; pszTrav = strstr(pszTrav,cszCRLF))
  250. {
  251. pszTrav += sizeof("\r\n") - 1;
  252. // reached end of headers, double CRLF
  253. if (*pszTrav == '\r')
  254. break;
  255. // Make sure we don't walk off the end of the buffer.
  256. if ((int) cbName > (pszEndOfHeaders - pszTrav))
  257. break;
  258. if (0 == _memicmp(pszTrav,lpszName,cbName))
  259. {
  260. pszTrav += cbName;
  261. if (' ' == *pszTrav) // must be a space next for a match
  262. {
  263. pszRet = pszTrav + 1;
  264. pszTrav = strstr(pszTrav,cszCRLF);
  265. DEBUGCHK(pszTrav != NULL); // should catch improperly formatted headers in parser
  266. *pszTrav = 0; // make this the end of string temporarily
  267. break;
  268. }
  269. }
  270. }
  271. }
  272. if ((PSTR)(-1) == pszRet)
  273. {
  274. // unknown var
  275. SetLastError(ERROR_INVALID_INDEX);
  276. myleave(1400);
  277. }
  278. if ((cbSizeNeeded = strlen(pszRet)+1) > *lpdwSize)
  279. {
  280. *lpdwSize = cbSizeNeeded;
  281. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  282. myleave(1401);
  283. }
  284. memcpy(lpvBuffer, pszRet, cbSizeNeeded);
  285. ret = TRUE;
  286. done:
  287. TraceTag(ttidWebServer, "HTTPD:GetHeader failed with variable name<<%s>>,err = %d ",lpszName,err);
  288. if (pszTrav)
  289. *pszTrav = '\r'; // reset the value
  290. return ret;
  291. }
  292. // BUGBUG: SetHeader and AddHeader both use SetHeader fcn, breaks IIS spec.
  293. // Fix: None. On IIS for a PREPROC_HEADER event, filter can call (for instance)
  294. // an AddHeader("URL","url.htm"), which will append to the end of the existing URL.
  295. // In this case, if the requested URL was "foo.htm", IIS would lookup foo.htm,url.htm
  296. // This isn't worth the hassle (the code to do it is below, though).
  297. BOOL CHttpRequest::SetHeader(LPSTR lpszName, LPSTR lpszValue)
  298. {
  299. DEBUG_CODE_INIT;
  300. PSTR pszNew = NULL;
  301. BOOL ret = FALSE;
  302. TraceTag(ttidWebServer, "Servicing SetHeader request with name<<%s>>,value<<%s>> ",lpszName,lpszValue);
  303. if (0==_stricmp(lpszName, "version"))
  304. {
  305. SetHTTPVersion(lpszValue,&m_dwVersion);
  306. ret = TRUE;
  307. }
  308. else if (0 == _stricmp(lpszName, "url"))
  309. {
  310. pszNew = MySzDupA(lpszValue);
  311. if (!pszNew)
  312. myleave(256);
  313. MyFree(m_pszURL);
  314. m_pszURL = pszNew;
  315. ret = TRUE;
  316. }
  317. else if (0 == _stricmp(lpszName, "method"))
  318. {
  319. pszNew = MySzDupA(lpszValue);
  320. if (!pszNew)
  321. myleave(257);
  322. MyFree(m_pszMethod);
  323. m_pszMethod = pszNew;
  324. ret = TRUE;
  325. }
  326. // BUGBUG - changing headers on the preproc event would require advanced
  327. // header management.
  328. // Fix: None. If the filter writer wants to change the state that headers
  329. // set they'll have plenty of oppurtunity to do it in later events.
  330. else if (m_pFInfo->m_dwSFEvent == SF_NOTIFY_PREPROC_HEADERS)
  331. {
  332. myretleave(TRUE,0);
  333. }
  334. else // custom response header, like "Content-length:" or whatever else
  335. {
  336. // Can't set response headers on 0.9 version. Err code is like IIS.
  337. if (m_dwVersion <= MAKELONG(9, 0))
  338. {
  339. SetLastError(ERROR_NOT_SUPPORTED);
  340. myleave(258);
  341. }
  342. ret = m_bufRespHeaders.AddHeader(lpszName,lpszValue);
  343. }
  344. done:
  345. TraceTag(ttidWebServer, "SetHeader failed with request with name<<%s>>,value<<%s>>, GLE=%d ",lpszName,lpszValue,GetLastError());
  346. return ret;
  347. }
  348. // This handles a special case for Filters / extensions. IF a call is made
  349. // to ISAPI Extension ServerSupportFunction with HSE_REQ_MAP_URL_TO_PATH, then
  350. // a filter call to SF_NOTIFY_URL_MAP is performed.
  351. // We can't call the filter directly because SF_NOTIFY_URL_MAP usually gets
  352. // path info from the CHttpRequest class, but in this case it's getting it's
  353. // data from and writing out to a user buffer.
  354. // pszBuf is the original string passed into ServerSupportFunction by the ISAPI.
  355. // When the function begins it has a virtual path, on successful termination it has a physical path
  356. // pdwSize is it's size
  357. // wszPath is it's mapped virtual root path
  358. // dwBufNeeded is the size of the buffer required to the physical path.
  359. BOOL CHttpRequest::FilterMapURL(PSTR pszBuf, WCHAR *wszPath, DWORD *pdwSize, DWORD dwBufNeeded, PSTR pszURLEx)
  360. {
  361. DEBUG_CODE_INIT;
  362. PSTR pszPhysicalOrg;
  363. PSTR pszPhysicalNew;
  364. PSTR pszVirtual = pszURLEx ? pszURLEx : pszBuf;
  365. DWORD cbBufNew = dwBufNeeded;
  366. BOOL ret = FALSE;
  367. // Regardless of if buffer was big enough to hold the original data, we always
  368. // allocate a buf for the filter. The filter may end up changing the data
  369. // so it's small enough to fit in the buffer.
  370. // Don't use MySzDupWtoA here because we alread know the length needed, MySzDupWtoA
  371. // would needlessly recompute it.
  372. if (NULL == (pszPhysicalOrg = MyRgAllocNZ(CHAR,dwBufNeeded)))
  373. myleave(710);
  374. MyW2A(wszPath, pszPhysicalOrg, dwBufNeeded);
  375. pszPhysicalNew = pszPhysicalOrg; // Keep a copy of pointer for freeing, CallFilter may modify it
  376. if ( !CallFilter(SF_NOTIFY_URL_MAP,&pszVirtual,(int*) &cbBufNew,&pszPhysicalNew))
  377. myleave(711);
  378. // Buffer isn't big enough
  379. if (*pdwSize < cbBufNew)
  380. {
  381. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  382. myleave(712);
  383. }
  384. // Copy changes over. pszPhysicalNew will be filter set or will be
  385. // the original wszPath converted to ASCII without filter.
  386. if (NULL != pszPhysicalNew)
  387. {
  388. memcpy(pszBuf,pszPhysicalNew,cbBufNew);
  389. }
  390. ret = TRUE;
  391. done:
  392. TraceTag(ttidWebServer, "FilterMapURL failed, err = %d, GLE=%X",err,GetLastError());
  393. *pdwSize = cbBufNew;
  394. MyFree(pszPhysicalOrg);
  395. return ret;
  396. }