diff --git a/Changelog b/Changelog index 5972726..5c3c4b9 100644 --- a/Changelog +++ b/Changelog @@ -1,5 +1,9 @@ Version 0.4: ============= +2026-06-09: +- WebRequestBuffer::RequestComplete did not check the content lenght and the + data size correctly. + 2026-04-19: - JSONElement: adding SetArray function to create an valid empty array diff --git a/Makefile b/Makefile index 99f434f..bb0f50a 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ noconfig: help endif -all: dep $(TARGET) test-udp test-tcpserver test-tcpclient test-ssl test-json test-webserver test-getrandom test-base64 +all: dep $(TARGET) test-udp test-tcpserver test-tcpclient test-ssl test-json test-webserver test-getrandom test-base64 test-webbuffer -include .depend @@ -58,6 +58,9 @@ test-udp: $(TARGET) test-udp.o test-json: $(TARGET) test-json.o $(CXX) test-json.o -o $@ -lUDPTCPNetwork -L./ -I./ $(LDFLAGS) +test-webbuffer: $(TARGET) test-webbuffer.o + $(CXX) test-webbuffer.o -o $@ -lUDPTCPNetwork -L./ -I./ $(LDFLAGS) + keygen: # openssl req -nodes -new -newkey rsa:2048 -sha256 -out csr.pem -keyout privkey.pem openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout privkey.pem -out cert.pem @@ -89,6 +92,7 @@ clean: rm test-tcpclient -rf rm test-webserver -rf rm test-getrandom -rf + rm test-webbuffer -rf rm test-base64 -rf rm test-json -rf rm test-udp -rf diff --git a/UDPTCPNetwork-webutils.h b/UDPTCPNetwork-webutils.h index 0b8d1ab..623580c 100644 --- a/UDPTCPNetwork-webutils.h +++ b/UDPTCPNetwork-webutils.h @@ -13,6 +13,10 @@ enum { // request types WEB_REQUEST_UNSUPPORTED, WEB_REQUEST_GET, WEB_REQUEST_POST, + WEB_REQUEST_PUT, + WEB_REQUEST_PATCH, + WEB_REQUEST_DELETE, + WEB_REQUEST_HEAD, WEB_REQUEST_MAX // not set }; @@ -45,13 +49,14 @@ class WebRequestBuffer { std::string protocol_version; std::list headerlines; int buffer_pos_data; - + public: WebRequestBuffer(); ~WebRequestBuffer(); int AddBuffer (char *b, int len); - int GetBuffer (char **dstptr, int *max); + int GetBuffer (char **dstptr, int *max); // get the current buffer (incl. Headers), resize if needed + int GetData (char **dstptr, int *max); // get the data part of the buffer, resize if needed bool RequestComplete(); void Clear() { buffer_len = 0; }; int GetType() { return request_type; }; diff --git a/test-webbuffer.cc b/test-webbuffer.cc new file mode 100644 index 0000000..a7ef233 --- /dev/null +++ b/test-webbuffer.cc @@ -0,0 +1,66 @@ + +#include +#include "UDPTCPNetwork.h" + + +int test_fragmentedbuffer() { + std::string buf1; + WebRequestBuffer buffer; + + buffer.Clear(); + buf1 = "POST /get/newvalues HTTP/1.1\r\n"; + buf1 += "Host: localhost:11001\r\n"; + buf1 += "Connection: keep-alive\r\n"; + buf1 += "Content-Length: 20\r\n"; + buf1 += "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36\r\n"; + buf1 += "Accept: *//*\r\n"; + buf1 += "Origin: http://localhost:11001\r\n"; + buf1 += "Sec-Fetch-Site: same-origin\r\n"; + buf1 += "Referer: http://localhost:11001/index.html\r\n"; + buf1 += "Accept-Encoding: gzip, deflate, br, zstd\r\n"; + buf1 += "Accept-Language: de-DE,de;q=0.9,en-DE;q=0.8,en;q=0.7,en-US;q=0.6\r\n"; + buf1 += "\r\n"; + + buffer.AddBuffer((char*)buf1.c_str(), strlen(buf1.c_str())); + if (buffer.RequestComplete()) { + debug ("header added: request is marked as complete but it isn't"); + return 0; + }; + + buf1 = "0123456789"; + buffer.AddBuffer((char*)buf1.c_str(), strlen(buf1.c_str())); + if (buffer.RequestComplete()) { + char *ptr = NULL; + int max = 0; + debug ("first 10 bytes: request is marked as complete but it isn't"); + buffer.GetBuffer(&ptr, &max); + debug ("buffer:'%s'", ptr); + + return 0; + }; + + buf1 = "0123456789"; + buffer.AddBuffer((char*)buf1.c_str(), strlen(buf1.c_str())); + if (!buffer.RequestComplete()) { + char *ptr = NULL; + int max = 0; + debug ("second 10 bytes: request is not marked as complete but it is"); + buffer.GetBuffer(&ptr, &max); + debug ("buffer:'%s'", ptr); + + return 0; + }; + + return 1; +}; + + + +int main(int argc, char** argv) { + debug ("test: buffer with fragmented header and content"); + if (!test_fragmentedbuffer()) exit(1); + + debug ("all tests passed"); + + return 0; +}; diff --git a/webutils.cc b/webutils.cc index 4650fb9..df37713 100644 --- a/webutils.cc +++ b/webutils.cc @@ -34,7 +34,7 @@ WebRequestBuffer::~WebRequestBuffer () { /// @return 0 on error, 1 on success int WebRequestBuffer::AddBuffer (char *buffer, int len) { // - // if needed allocate enought buffer, to the next kB + // if needed allocate enought buffer, to the next kB, add 1 for the null termination. if (buffer_ptr == NULL || buffer_size <= buffer_len+len) { buffer_size = 1024 * (1 + ((buffer_len + len) / 1024)); if (buffer_ptr == NULL) buffer_ptr = (char*) malloc (buffer_size); @@ -49,15 +49,14 @@ int WebRequestBuffer::AddBuffer (char *buffer, int len) { memcpy (buffer_ptr + buffer_len, buffer, len); buffer_len += len; - buffer_ptr[buffer_len] = 0; - - // debug("buffer:'%s'\n", buffer_ptr); + if (buffer_len >= buffer_size) errorexit ("buffer_size < buffer_len"); return 1; }; /// @brief copy the current buffer to the destination, resize destination if needed +/// @brief the buffer includes the header and the data part, use GetData to get only the data part /// @param dstptr pointer to the destination pointer, in case of a reallocation /// @param max pointer to current allocated size of the pointer /// @return > 0 - len in bytes of the buffer, = -1 error occured @@ -70,10 +69,10 @@ int WebRequestBuffer::GetBuffer (char **dstptr, int *max) { } // check: size and reallocate - if (*max < buffer_len || *max < 0) { - char *tb = (char*) realloc (*dstptr, buffer_len); + if (*max < buffer_len+1 || *max < 0) { + *max = (1024*(1+((buffer_len+1)/1024))); + char *tb = (char*) realloc (*dstptr, (*max)); *dstptr = tb; - *max = buffer_size; } // check: could alloc memory @@ -88,8 +87,41 @@ int WebRequestBuffer::GetBuffer (char **dstptr, int *max) { }; +/// @brief copy the data part of the buffer to the destination, resize destination if needed +/// @param dstptr pointer to the destination pointer, in case of a reallocation +/// @param max pointer to current allocated size of the pointer +/// @return > 0 - len in bytes of the buffer, = -1 error occured +int WebRequestBuffer::GetData (char **dstptr, int *max) { + // check: valid pointers + if (dstptr == NULL || max == NULL) { + fprintf (stderr, "%s:%d no pointer to buffer or no pointer to a lenght set\n", + __FILE__, __LINE__); + return -1; + } + + // check: size and reallocate + if (*max < buffer_len+1 || *max < 0) { + *max = (1024*(1+((buffer_len+1)/1024))); + char *tb = (char*) realloc (*dstptr, (*max)); + *dstptr = tb; + } + + // check: could alloc memory + if (*dstptr == NULL) { + fprintf (stderr, "%s:%d no destination pointer set\n", + __FILE__, __LINE__); + return -1; + } + + memcpy (*dstptr, buffer_ptr + buffer_pos_data, buffer_len - buffer_pos_data); + return buffer_len - buffer_pos_data; +}; + + + /// @brief Check if the Request is Complete. In this case the request_type and the request can /// @brief be read with GetRequest and GetType(). For a new request Clear() must be called. +/// @brief The request is complete if the header is complete and if the content length is reached (if content length is set). /// @return 0 . not complete, 1 . request complete bool WebRequestBuffer::RequestComplete() { int i = 0; @@ -111,7 +143,7 @@ bool WebRequestBuffer::RequestComplete() { if (buffer_ptr[i] == '\n') { if (hl.value.length() == 0 && hl.parameter.length() == 0) { buffer_pos_data = i+1; // save position of first data byte - return true; + break; } // the first line holds the request type and the location/path. @@ -119,6 +151,10 @@ bool WebRequestBuffer::RequestComplete() { hl.parameter = string_to_upper(hl.parameter); if (hl.parameter.compare("GET") == 0) request_type = WEB_REQUEST_GET; if (hl.parameter.compare("POST") == 0) request_type = WEB_REQUEST_POST; + if (hl.parameter.compare("PUT") == 0) request_type = WEB_REQUEST_PUT; + if (hl.parameter.compare("PATCH") == 0) request_type = WEB_REQUEST_PATCH; + if (hl.parameter.compare("DELETE") == 0) request_type = WEB_REQUEST_DELETE; + if (hl.parameter.compare("HEAD") == 0) request_type = WEB_REQUEST_HEAD; request = string_to_lower(hl.value); } @@ -143,7 +179,21 @@ bool WebRequestBuffer::RequestComplete() { } } - return false; + // abort if no header end was found, or unsuppoerted request type + if (buffer_pos_data == -1) return false; + if (request_type == WEB_REQUEST_UNSUPPORTED) return false; + + // check if content length is set and if the content is complete + if (request_type == WEB_REQUEST_POST || request_type == WEB_REQUEST_PUT || request_type == WEB_REQUEST_PATCH) { + std::string cl = GetHeader("Content-Length"); + if (cl.length() == 0) return false; // content length is missing, request is not complete + i = atoi(cl.c_str()); + if (i == 0) return true; // no content, request is complete + if (buffer_len - buffer_pos_data == i) return true; // content length is reached, request is complete + return false; + } + + return true; // for GET, DELETE, HEAD there is no content, so the request is complete }