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.

396 lines
12 KiB

  1. /*--
  2. Copyright (c) 1995-1998 Microsoft Corporation
  3. Module Name: BUFFIO.CPP
  4. Author: Arul Menezes
  5. Abstract: Buffer handling class & socket IO helpers
  6. --*/
  7. #include "pch.h"
  8. #pragma hdrstop
  9. #include "httpd.h"
  10. // Wait for input on socket with timeout
  11. int MySelect(SOCKET sock, DWORD dwMillisecs)
  12. {
  13. fd_set set;
  14. struct timeval t;
  15. if (dwMillisecs != INFINITE)
  16. {
  17. t.tv_sec = (dwMillisecs / 1000);
  18. t.tv_usec = (dwMillisecs % 1000)*1000;
  19. }
  20. FD_ZERO(&set);
  21. FD_SET(sock, &set);
  22. TraceTag(ttidWebServer, "Calling select(%x). Timeout=%d", sock, dwMillisecs);
  23. int iRet = select(0, &set, NULL, NULL, ((dwMillisecs==INFINITE) ? NULL : (&t)));
  24. TraceTag(ttidWebServer, "Select(%x) got %d", sock, iRet);
  25. return iRet;
  26. }
  27. // Semi-blocking wait for input on a socket. This function will exit either
  28. // when input is available or when the shutdown event has been set
  29. //
  30. int MySelect2(SOCKET sock, DWORD dwMillisecs)
  31. {
  32. HANDLE hEvent;
  33. int iRet = 0;
  34. HANDLE rgHandles[2];
  35. hEvent = WSACreateEvent();
  36. if (hEvent != WSA_INVALID_EVENT)
  37. {
  38. rgHandles[0] = hEvent;
  39. rgHandles[1] = g_pVars->m_hEventShutdown;
  40. iRet = WSAEventSelect(sock, hEvent, FD_READ);
  41. if (!iRet)
  42. {
  43. DWORD dwRet;
  44. dwRet = WaitForMultipleObjects(2, rgHandles, FALSE, dwMillisecs);
  45. if (WAIT_OBJECT_0 == dwRet)
  46. {
  47. // iRet should be 1 if input is available
  48. iRet = 1;
  49. }
  50. }
  51. CloseHandle(hEvent);
  52. }
  53. return iRet;
  54. }
  55. // need space for iLen more data
  56. BOOL CBuffer::AllocMem(DWORD dwLen)
  57. {
  58. // figure out buffer size
  59. DWORD dwAlloc = max(MINBUFSIZE, dwLen);
  60. // allocate or reallocate buffer
  61. if (!m_pszBuf)
  62. {
  63. m_pszBuf = MyRgAllocZ(char, dwAlloc);
  64. TraceTag(ttidWebServer, "New buffer (data=%d size=%d buf=0x%08x)", dwLen, dwAlloc, m_pszBuf);
  65. m_iSize = dwAlloc;
  66. }
  67. else if ((m_iSize-m_iNextIn) <= (int)dwLen)
  68. {
  69. m_pszBuf = MyRgReAlloc(char, m_pszBuf, m_iSize, dwAlloc+m_iSize);
  70. TraceTag(ttidWebServer, "Realloc buffer (datasize=%d oldsize=%d size=%d buf=0x%08x)", dwLen, m_iSize, dwAlloc+m_iSize, m_pszBuf);
  71. m_iSize += dwAlloc;
  72. }
  73. if (!m_pszBuf)
  74. {
  75. TraceTag(ttidWebServer, "CBuffer:AllocMem(%d) failed. GLE=%d", dwLen, GetLastError());
  76. m_iNextInFollow = m_iSize = m_iNextOut = m_iNextIn = 0;
  77. m_chSaved = 0;
  78. return FALSE;
  79. }
  80. return TRUE;
  81. }
  82. // Pull in all white space before a request. Note: We techinally should let
  83. // the filter get this too, but too much work. Also note that we could read
  84. // past a double CRLF if there was only white space before it, again this
  85. // is a strange enough condition that we don't care about it.
  86. BOOL CBuffer::TrimWhiteSpace()
  87. {
  88. int i = 0, j = 0;
  89. while ( isspace(m_pszBuf[i]) && i < m_iNextIn)
  90. {
  91. i++;
  92. }
  93. if (i == 0)
  94. return TRUE;
  95. if (i == m_iNextIn)
  96. return FALSE; // need to read more data, all white spaces so far.
  97. for (j = 0; j < m_iNextIn - i; j++)
  98. m_pszBuf[j] = m_pszBuf[j+i];
  99. m_iNextIn -= i;
  100. TraceTag(ttidWebServer, "HTTPD: TrimWhiteSpace removing first %d bytes from steam",i);
  101. return TRUE;
  102. }
  103. // This function reads eitehr request-headers from the socket
  104. // terminated by a double CRLF, OR reads a post-body from the socket
  105. // terminated by having read the right number of bytes
  106. //
  107. // We are keeping the really simple--we read the entire header
  108. // into one contigous buffer before we do anything.
  109. //
  110. // dwLength is -1 for reading headers, or Content-Length for reading body
  111. // or 0 is content-length is unknown, in which case it reads until EOF
  112. HRINPUT CBuffer::RecvToBuf(SOCKET sock, DWORD dwLength, DWORD dwTimeout, BOOL fFromFilter)
  113. {
  114. DEBUG_CODE_INIT;
  115. int iScan = 0;
  116. HRINPUT ret = INPUT_ERROR;
  117. DWORD dwBytesRemainingToBeRead;
  118. // Both IE and Netscape tack on a trailing \r\n to POST data but don't
  119. // count it as part of the Content-length. IIS doesn't pass the \r\n
  120. // to the script engine, so we don't either. To do this, we set
  121. // the \r to \0. Also we reset m_iNextIn. This \r\n code is only
  122. // relevant when RecvToBuf is called from HandleRequest, otherwise
  123. // we assume it's a filter calling us and don't interfere.
  124. if (dwLength != -1)
  125. {
  126. if (!fFromFilter && ((m_iNextIn-m_iNextOut) >= (int) dwLength))
  127. {
  128. if (((m_iNextIn-m_iNextOut) == (int) dwLength) ||
  129. ((m_iNextIn-m_iNextOut) == (int) dwLength+2))
  130. {
  131. m_iNextIn = m_iNextOut + dwLength;
  132. // This is reachable from HandleRequest, and
  133. myretleave(INPUT_NOCHANGE,0);
  134. }
  135. else
  136. {
  137. myretleave(INPUT_ERROR, 111);
  138. }
  139. }
  140. if (!fFromFilter)
  141. {
  142. dwLength = dwLength - (m_iNextIn - m_iNextOut); // account for amount of POST data already in
  143. }
  144. m_iNextInFollow = m_iNextIn;
  145. // allocate or reallocate buffer. Since we already know size we want, do it here rather than later.
  146. if (!AllocMem(dwLength+1))
  147. myretleave(INPUT_ERROR, 103);
  148. }
  149. dwBytesRemainingToBeRead = dwLength;
  150. for (;;)
  151. {
  152. // see if we got the double CRLF for HTTP Headers.
  153. if (dwLength == (DWORD)-1)
  154. {
  155. BOOL fScan = TRUE;
  156. if (iScan == 0 && m_iNextIn)
  157. {
  158. fScan = TrimWhiteSpace();
  159. }
  160. if (fScan)
  161. {
  162. while (iScan+3 < m_iNextIn)
  163. {
  164. if (m_pszBuf[iScan]=='\r' && m_pszBuf[iScan+1]=='\n' && m_pszBuf[iScan+2]=='\r' && m_pszBuf[iScan+3]=='\n')
  165. {
  166. myretleave(INPUT_OK,0);
  167. }
  168. iScan++;
  169. }
  170. }
  171. }
  172. // else see if we have the number of bytes we want.
  173. // Browsers sometimes tack an extra \r\n to very end of POST data, even
  174. // though they don't include it in the Content-Length field. IIS
  175. // never passes this extra \r\n to ISAPI extensions, neither do we.
  176. else if ((m_iNextIn-m_iNextInFollow) >= (int)dwLength)
  177. {
  178. DEBUGCHK((int)dwLength + 2 == (m_iNextIn-m_iNextInFollow) ||
  179. (int)dwLength == (m_iNextIn-m_iNextInFollow));
  180. DEBUGCHK(dwBytesRemainingToBeRead == 0);
  181. m_iNextIn = m_iNextInFollow+(int)dwLength; // don't copy trailing \r\n
  182. myretleave(INPUT_OK,0);
  183. }
  184. // check if we have input. If we are waiting for subsequent input (i.e. not the start of a request)
  185. // then drop the timeout value lower, and if we timeout return ERROR, not TIMEOUT
  186. switch (MySelect2(sock, ((m_iNextIn ? RECVTIMEOUT : dwTimeout))))
  187. {
  188. case 0: myretleave((m_iNextIn ? INPUT_ERROR : INPUT_TIMEOUT),100);
  189. case SOCKET_ERROR: myretleave(INPUT_ERROR, 101);
  190. }
  191. // check how much input is waiting
  192. DWORD dwAvailable;
  193. if (ioctlsocket(sock, FIONREAD, &dwAvailable))
  194. myretleave(INPUT_ERROR, 102);
  195. DWORD dwBytesToRecv;
  196. if (dwLength == -1) // Read in as much http header as we have.
  197. {
  198. dwBytesToRecv = dwAvailable;
  199. // allocate or reallocate buffer. For headers, have to do it each pass.
  200. if (!AllocMem(dwAvailable+1))
  201. myretleave(INPUT_ERROR, 103);
  202. }
  203. else // Read in only requested amount of POST
  204. dwBytesToRecv = (dwAvailable < dwBytesRemainingToBeRead) ? dwAvailable : dwBytesRemainingToBeRead;
  205. DEBUGCHK((m_iSize-m_iNextIn) >= (int)dwBytesToRecv);
  206. DEBUGCHK(m_iNextIn >= m_iNextOut);
  207. DEBUGCHK(m_iNextIn >= m_iNextInFollow);
  208. // safe to call recv, because we know we have something. It will return immediately
  209. int iRecv = recv(sock, m_pszBuf+m_iNextIn, dwBytesToRecv, 0);
  210. TraceTag(ttidWebServer, "recv(%x) got %d", sock, iRecv);
  211. if (iRecv == 0)
  212. {
  213. myretleave((m_iNextIn ? INPUT_OK : INPUT_TIMEOUT), 0);
  214. } // got EOF. If we have any data return OK, else return TIMEOUT
  215. else if (iRecv == SOCKET_ERROR)
  216. {
  217. myretleave(((GetLastError()==WSAECONNRESET) ? INPUT_TIMEOUT : INPUT_ERROR), 104);
  218. }
  219. m_iNextIn += iRecv;
  220. dwBytesRemainingToBeRead -= iRecv;
  221. DEBUGCHK(m_iSize >= m_iNextIn);
  222. }
  223. DebugBreak(); // no fall through
  224. done:
  225. // Always make this buffer into a null terminated string
  226. if (m_pszBuf)
  227. m_pszBuf[m_iNextIn] = 0;
  228. TraceTag(ttidWebServer, "end RecvToBuf (ret=%d err=%d iGLE=%d)", ret, err, GLE(err));
  229. return ret;
  230. }
  231. // tokenize the input stream: We always skip leading white-space
  232. // once we're in the token, we stop on whitespace or EOL, depending
  233. // on the fWS param
  234. BOOL CBuffer::NextToken(PSTR* ppszTok, int* piLen, BOOL fWS, BOOL fColon /*=FALSE*/)
  235. {
  236. int i, j;
  237. // restore saved char, if any
  238. if (m_chSaved)
  239. {
  240. DEBUGCHK(m_pszBuf[m_iNextOut]==0);
  241. m_pszBuf[m_iNextOut] = m_chSaved;
  242. m_chSaved = 0;
  243. }
  244. for (i=m_iNextOut; i<m_iNextIn; i++)
  245. {
  246. // if not whitespace break
  247. if (! (m_pszBuf[i]==' ' || m_pszBuf[i]=='\t') )
  248. break;
  249. }
  250. for (j=i; j<m_iNextIn; j++)
  251. {
  252. // if we get an EOL, it's always end of token
  253. if (m_pszBuf[j]=='\r' || m_pszBuf[j]=='\n')
  254. break;
  255. // if fWS==TRUE and we got white-space, then end of token
  256. if (fWS && (m_pszBuf[j]==' ' || m_pszBuf[j]=='\t'))
  257. break;
  258. if (fColon && m_pszBuf[j]==':')
  259. {
  260. j++; // we want to return the colon
  261. break;
  262. }
  263. }
  264. m_iNextOut = j;
  265. *piLen = (int)(INT_PTR)((j-i));
  266. *ppszTok = &(m_pszBuf[i]);
  267. if (i==j)
  268. {
  269. TraceTag(ttidWebServer, "Got NULL token");
  270. return FALSE;
  271. }
  272. else
  273. {
  274. // save a char so we can null-terminate the current token
  275. m_chSaved = m_pszBuf[m_iNextOut];
  276. m_pszBuf[m_iNextOut] = 0;
  277. TraceTag(ttidWebServer, "Got token (%s) Len %d", *ppszTok, (*piLen));
  278. return TRUE;
  279. }
  280. }
  281. // skip rest of current line and CRLF
  282. BOOL CBuffer::NextLine()
  283. {
  284. int i, j;
  285. // restore saved char, if any
  286. if (m_chSaved)
  287. {
  288. DEBUGCHK(m_pszBuf[m_iNextOut]==0);
  289. m_pszBuf[m_iNextOut] = m_chSaved;
  290. m_chSaved = 0;
  291. }
  292. for (i=m_iNextOut, j=i+1; j<m_iNextIn; i++, j++)
  293. {
  294. if (m_pszBuf[i]=='\r' && m_pszBuf[j]=='\n')
  295. {
  296. m_iNextOut = j+1;
  297. TraceTag(ttidWebServer, "NextLine: OK");
  298. return TRUE;
  299. }
  300. }
  301. TraceTag(ttidWebServer, "NextLine: error");
  302. return FALSE;
  303. }
  304. // used only on output buffers by ASP
  305. BOOL CBuffer::AppendData(PSTR pszData, int iLen)
  306. {
  307. // make sure we have enough memory
  308. if (!AllocMem(iLen+1))
  309. return FALSE;
  310. DEBUGCHK((m_iSize-m_iNextIn) >= iLen);
  311. memcpy(m_pszBuf+m_iNextIn, pszData, iLen);
  312. m_iNextIn += iLen;
  313. return TRUE;
  314. }
  315. BOOL CBuffer::SendBuffer(SOCKET sock, CHttpRequest *pRequest)
  316. {
  317. PSTR pszSendBuf = m_pszBuf; // use temp ptrs in case filter changes them
  318. int cbSendBuf = m_iNextIn;
  319. DWORD fRet = FALSE;
  320. DEBUGCHK(m_iNextOut==0);
  321. DEBUGCHK(m_chSaved==0);
  322. if (!m_iNextIn)
  323. {
  324. TraceTag(ttidWebServer, "SendBuffer: empty");
  325. return TRUE;
  326. }
  327. if (g_pVars->m_fFilters &&
  328. ! pRequest->CallFilter(SF_NOTIFY_SEND_RAW_DATA, &pszSendBuf, &cbSendBuf))
  329. goto done;
  330. if (cbSendBuf != send(sock, pszSendBuf, cbSendBuf, 0))
  331. {
  332. TraceTag(ttidWebServer, "SendBuffer FAILED. GLE=%d", GetLastError());
  333. goto done;
  334. }
  335. fRet = TRUE;
  336. done:
  337. m_iNextIn = 0;
  338. return fRet;
  339. }