32
32
33
33
#include " winsock2.h"
34
34
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
+
35
42
class REQUEST_STORED_CONTEXT : public IHttpStoredContext
36
43
{
37
44
public:
@@ -82,8 +89,11 @@ class REQUEST_STORED_CONTEXT : public IHttpStoredContext
82
89
char *m_pResponseBuffer;
83
90
ULONGLONG m_pResponseLength;
84
91
ULONGLONG m_pResponsePosition;
92
+
93
+ preAllocBodyChunk* requestBodyBufferHead;
85
94
};
86
95
96
+
87
97
// ----------------------------------------------------------------------------
88
98
89
99
char *GetIpAddr (apr_pool_t *pool, PSOCKADDR pAddr)
@@ -286,6 +296,44 @@ REQUEST_STORED_CONTEXT *RetrieveIISContext(request_rec *r)
286
296
return NULL ;
287
297
}
288
298
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
+
289
337
HRESULT CMyHttpModule::ReadFileChunk (HTTP_DATA_CHUNK *chunk, char *buf)
290
338
{
291
339
OVERLAPPED ovl;
@@ -752,6 +800,24 @@ CMyHttpModule::OnBeginRequest(
752
800
goto Finished;
753
801
}
754
802
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
+
755
821
// every 3 seconds we check for changes in config file
756
822
//
757
823
DWORD ctime = GetTickCount ();
@@ -841,6 +907,8 @@ CMyHttpModule::OnBeginRequest(
841
907
rsc->m_pRequestRec = r;
842
908
rsc->m_pHttpContext = pHttpContext;
843
909
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
844
912
845
913
pHttpContext->GetModuleContextContainer ()->SetModuleContext (rsc, g_pModuleContext);
846
914
@@ -1086,6 +1154,16 @@ CMyHttpModule::OnBeginRequest(
1086
1154
Finished:
1087
1155
LeaveCriticalSection (&m_csLock);
1088
1156
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
+
1089
1167
if ( FAILED ( hr ) )
1090
1168
{
1091
1169
return RQ_NOTIFICATION_FINISH_REQUEST;
@@ -1095,40 +1173,29 @@ CMyHttpModule::OnBeginRequest(
1095
1173
1096
1174
apr_status_t ReadBodyCallback (request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos)
1097
1175
{
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);
1107
1177
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
+ }
1110
1183
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);
1116
1187
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);
1118
1192
1119
- if (FAILED (hr) )
1193
+ if (rsc-> requestBodyBufferHead == NULL )
1120
1194
{
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 ;
1129
1196
}
1130
1197
1131
- return APR_SUCCESS;
1198
+ return APR_SUCCESS;
1132
1199
}
1133
1200
1134
1201
apr_status_t WriteBodyCallback (request_rec *r, char *buf, unsigned int length)
0 commit comments