3232
3333#include " winsock2.h"
3434
35+ // Used to hold each chunk of request body that gets read before the full ModSec engine is invoked
36+ typedef struct preAllocBodyChunk {
37+ preAllocBodyChunk* next;
38+ size_t length;
39+ void * data;
40+ } preAllocBodyChunk;
41+
3542class REQUEST_STORED_CONTEXT : public IHttpStoredContext
3643{
3744 public:
@@ -82,8 +89,11 @@ class REQUEST_STORED_CONTEXT : public IHttpStoredContext
8289 char *m_pResponseBuffer;
8390 ULONGLONG m_pResponseLength;
8491 ULONGLONG m_pResponsePosition;
92+
93+ preAllocBodyChunk* requestBodyBufferHead;
8594};
8695
96+
8797// ----------------------------------------------------------------------------
8898
8999char *GetIpAddr (apr_pool_t *pool, PSOCKADDR pAddr)
@@ -286,6 +296,44 @@ REQUEST_STORED_CONTEXT *RetrieveIISContext(request_rec *r)
286296 return NULL ;
287297}
288298
299+ HRESULT GetRequestBodyFromIIS (IHttpRequest* pRequest, preAllocBodyChunk** head)
300+ {
301+ HRESULT hr = S_OK;
302+ HTTP_REQUEST * pRawRequest = pRequest->GetRawHttpRequest ();
303+ preAllocBodyChunk** cur = head;
304+ while (pRequest->GetRemainingEntityBytes () > 0 )
305+ {
306+ // Allocate memory for the preAllocBodyChunk linked list structure, and also the actual body content
307+ // HUGE_STRING_LEN is hardcoded because this is also hardcoded in apache2_io.c's call to ap_get_brigade
308+ preAllocBodyChunk* chunk = (preAllocBodyChunk*)malloc (sizeof (preAllocBodyChunk) + HUGE_STRING_LEN);
309+ chunk->next = NULL ;
310+
311+ // Pointer to rest of allocated memory, for convenience
312+ chunk->data = chunk + 1 ;
313+
314+ DWORD readcnt = 0 ;
315+ hr = pRequest->ReadEntityBody (chunk->data , HUGE_STRING_LEN, false , &readcnt, NULL );
316+ if (ERROR_HANDLE_EOF == (hr & 0x0000FFFF ))
317+ {
318+ free (chunk);
319+ hr = S_OK;
320+ break ;
321+ }
322+ chunk->length = readcnt;
323+
324+ // Append to linked list
325+ *cur = chunk;
326+ cur = &(chunk->next );
327+
328+ if (hr != S_OK)
329+ {
330+ break ;
331+ }
332+ }
333+
334+ return hr;
335+ }
336+
289337HRESULT CMyHttpModule::ReadFileChunk (HTTP_DATA_CHUNK *chunk, char *buf)
290338{
291339 OVERLAPPED ovl;
@@ -752,6 +800,24 @@ CMyHttpModule::OnBeginRequest(
752800 goto Finished;
753801 }
754802
803+ // Get request body without holding lock, because some clients may be slow at sending
804+ LeaveCriticalSection (&m_csLock);
805+ preAllocBodyChunk* requestBodyBufferHead = NULL ;
806+ hr = GetRequestBodyFromIIS (pRequest, &requestBodyBufferHead);
807+ if (hr != S_OK)
808+ {
809+ goto FinishedWithoutLock;
810+ }
811+ EnterCriticalSection (&m_csLock);
812+
813+ // Get the config again, in case it changed during the time we released the lock
814+ hr = MODSECURITY_STORED_CONTEXT::GetConfig (pHttpContext, &pConfig);
815+ if (FAILED (hr))
816+ {
817+ hr = S_OK;
818+ goto Finished;
819+ }
820+
755821 // every 3 seconds we check for changes in config file
756822 //
757823 DWORD ctime = GetTickCount ();
@@ -841,6 +907,8 @@ CMyHttpModule::OnBeginRequest(
841907 rsc->m_pRequestRec = r;
842908 rsc->m_pHttpContext = pHttpContext;
843909 rsc->m_pProvider = pProvider;
910+ rsc->requestBodyBufferHead = requestBodyBufferHead;
911+ requestBodyBufferHead = NULL ; // This is to indicate to the cleanup process to use rsc->requestBodyBufferHead instead of requestBodyBufferHead now
844912
845913 pHttpContext->GetModuleContextContainer ()->SetModuleContext (rsc, g_pModuleContext);
846914
@@ -1086,6 +1154,16 @@ CMyHttpModule::OnBeginRequest(
10861154Finished:
10871155 LeaveCriticalSection (&m_csLock);
10881156
1157+ FinishedWithoutLock:
1158+ // Free the preallocated body in case there was a failure and it wasn't consumed already
1159+ preAllocBodyChunk* chunkToFree = requestBodyBufferHead ? requestBodyBufferHead : rsc->requestBodyBufferHead ;
1160+ while (chunkToFree != NULL )
1161+ {
1162+ preAllocBodyChunk* next = chunkToFree->next ;
1163+ free (chunkToFree);
1164+ chunkToFree = next;
1165+ }
1166+
10891167 if ( FAILED ( hr ) )
10901168 {
10911169 return RQ_NOTIFICATION_FINISH_REQUEST;
@@ -1095,40 +1173,29 @@ CMyHttpModule::OnBeginRequest(
10951173
10961174apr_status_t ReadBodyCallback (request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos)
10971175{
1098- REQUEST_STORED_CONTEXT *rsc = RetrieveIISContext (r);
1099-
1100- *readcnt = 0 ;
1101-
1102- if (rsc == NULL )
1103- {
1104- *is_eos = 1 ;
1105- return APR_SUCCESS;
1106- }
1176+ REQUEST_STORED_CONTEXT *rsc = RetrieveIISContext (r);
11071177
1108- IHttpContext *pHttpContext = rsc->m_pHttpContext ;
1109- IHttpRequest *pRequest = pHttpContext->GetRequest ();
1178+ if (rsc->requestBodyBufferHead == NULL )
1179+ {
1180+ *is_eos = 1 ;
1181+ return APR_SUCCESS;
1182+ }
11101183
1111- if (pRequest->GetRemainingEntityBytes () == 0 )
1112- {
1113- *is_eos = 1 ;
1114- return APR_SUCCESS;
1115- }
1184+ *readcnt = length < (unsigned int ) rsc->requestBodyBufferHead ->length ? length : (unsigned int ) rsc->requestBodyBufferHead ->length ;
1185+ void * src = (char *)rsc->requestBodyBufferHead ->data ;
1186+ memcpy_s (buf, length, src, *readcnt);
11161187
1117- HRESULT hr = pRequest->ReadEntityBody (buf, length, false , (DWORD *)readcnt, NULL );
1188+ // Remove the front and proceed to next chunk in the linked list
1189+ preAllocBodyChunk* chunkToFree = rsc->requestBodyBufferHead ;
1190+ rsc->requestBodyBufferHead = rsc->requestBodyBufferHead ->next ;
1191+ free (chunkToFree);
11181192
1119- if (FAILED (hr) )
1193+ if (rsc-> requestBodyBufferHead == NULL )
11201194 {
1121- // End of data is okay.
1122- if (ERROR_HANDLE_EOF != (hr & 0x0000FFFF ))
1123- {
1124- // Set the error status.
1125- rsc->m_pProvider ->SetErrorStatus ( hr );
1126- }
1127-
1128- *is_eos = 1 ;
1195+ *is_eos = 1 ;
11291196 }
11301197
1131- return APR_SUCCESS;
1198+ return APR_SUCCESS;
11321199}
11331200
11341201apr_status_t WriteBodyCallback (request_rec *r, char *buf, unsigned int length)
0 commit comments