From 783b182c8fa92674fa609b584c0b187469893ca4 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 6 May 2014 15:25:01 +0200 Subject: Remove dummy PRIszX macros for formatting Size specifiers are no longer needed now that we use typesafe tinyformat for string formatting, instead of the system's sprintf. No functional changes. This continues the work in #3735. --- src/rpcprotocol.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/rpcprotocol.cpp') diff --git a/src/rpcprotocol.cpp b/src/rpcprotocol.cpp index 652b14d18..5cbaa535a 100644 --- a/src/rpcprotocol.cpp +++ b/src/rpcprotocol.cpp @@ -92,7 +92,7 @@ string HTTPReply(int nStatus, const string& strMsg, bool keepalive) "HTTP/1.1 %d %s\r\n" "Date: %s\r\n" "Connection: %s\r\n" - "Content-Length: %"PRIszu"\r\n" + "Content-Length: %u\r\n" "Content-Type: application/json\r\n" "Server: bitcoin-json-rpc/%s\r\n" "\r\n" -- cgit v1.2.3 From 3e8ac6af9a993e262d1160fb2e6e1e1f1d5d19f2 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 8 May 2014 18:01:10 +0200 Subject: Replace non-threadsafe gmtime and setlocale Make DateTimeStrFormat use boost::posix_time. Also re-enable the util_DateTimeStrFormat tests, as they are no longer platform specific. --- src/rpcprotocol.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'src/rpcprotocol.cpp') diff --git a/src/rpcprotocol.cpp b/src/rpcprotocol.cpp index 5cbaa535a..2718f8178 100644 --- a/src/rpcprotocol.cpp +++ b/src/rpcprotocol.cpp @@ -51,15 +51,7 @@ string HTTPPost(const string& strMsg, const map& mapRequestHeader static string rfc1123Time() { - char buffer[64]; - time_t now; - time(&now); - struct tm* now_gmt = gmtime(&now); - string locale(setlocale(LC_TIME, NULL)); - setlocale(LC_TIME, "C"); // we want POSIX (aka "C") weekday/month strings - strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt); - setlocale(LC_TIME, locale.c_str()); - return string(buffer); + return DateTimeStrFormat("%a, %d %b %Y %H:%M:%S +0000", GetTime()); } string HTTPReply(int nStatus, const string& strMsg, bool keepalive) -- cgit v1.2.3 From c912e22db08d0a44ad6fd027c09bbdf79c34dbbc Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Wed, 4 Jun 2014 11:24:43 -0400 Subject: RPC cleanup: Improve HTTP server replies 1) support varying content types 2) support only sending the header 3) properly deliver error message as content, if HTTP error 4) move AcceptedConnection class to header, for wider use --- src/rpcprotocol.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'src/rpcprotocol.cpp') diff --git a/src/rpcprotocol.cpp b/src/rpcprotocol.cpp index 2718f8178..bfa799f84 100644 --- a/src/rpcprotocol.cpp +++ b/src/rpcprotocol.cpp @@ -54,7 +54,8 @@ static string rfc1123Time() return DateTimeStrFormat("%a, %d %b %Y %H:%M:%S +0000", GetTime()); } -string HTTPReply(int nStatus, const string& strMsg, bool keepalive) +string HTTPReply(int nStatus, const string& strMsg, bool keepalive, + bool headersOnly, const char *contentType) { if (nStatus == HTTP_UNAUTHORIZED) return strprintf("HTTP/1.0 401 Authorization Required\r\n" @@ -73,6 +74,7 @@ string HTTPReply(int nStatus, const string& strMsg, bool keepalive) "\r\n" "

401 Unauthorized.

\r\n" "\r\n", rfc1123Time(), FormatFullVersion()); + const char *cStatus; if (nStatus == HTTP_OK) cStatus = "OK"; else if (nStatus == HTTP_BAD_REQUEST) cStatus = "Bad Request"; @@ -80,12 +82,19 @@ string HTTPReply(int nStatus, const string& strMsg, bool keepalive) else if (nStatus == HTTP_NOT_FOUND) cStatus = "Not Found"; else if (nStatus == HTTP_INTERNAL_SERVER_ERROR) cStatus = "Internal Server Error"; else cStatus = ""; + + bool useInternalContent = false; + if (nStatus != HTTP_OK) { + contentType = "text/plain"; + useInternalContent = true; + } + return strprintf( "HTTP/1.1 %d %s\r\n" "Date: %s\r\n" "Connection: %s\r\n" "Content-Length: %u\r\n" - "Content-Type: application/json\r\n" + "Content-Type: %s\r\n" "Server: bitcoin-json-rpc/%s\r\n" "\r\n" "%s", @@ -94,8 +103,10 @@ string HTTPReply(int nStatus, const string& strMsg, bool keepalive) rfc1123Time(), keepalive ? "keep-alive" : "close", strMsg.size(), + contentType, FormatFullVersion(), - strMsg); + headersOnly ? "" : + useInternalContent ? cStatus : strMsg.c_str()); } bool ReadHTTPRequestLine(std::basic_istream& stream, int &proto, -- cgit v1.2.3 From ed5769f536f663a8deb8e8b7a68681cebaa52bdd Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Fri, 27 Jun 2014 00:10:53 -0400 Subject: Move AcceptedConnection class to rpcserver.h. Also, add parens to HTTPReply() to assist readability. --- src/rpcprotocol.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/rpcprotocol.cpp') diff --git a/src/rpcprotocol.cpp b/src/rpcprotocol.cpp index bfa799f84..2cb4a35c4 100644 --- a/src/rpcprotocol.cpp +++ b/src/rpcprotocol.cpp @@ -105,8 +105,8 @@ string HTTPReply(int nStatus, const string& strMsg, bool keepalive, strMsg.size(), contentType, FormatFullVersion(), - headersOnly ? "" : - useInternalContent ? cStatus : strMsg.c_str()); + (headersOnly ? "" : + (useInternalContent ? cStatus : strMsg.c_str()))); } bool ReadHTTPRequestLine(std::basic_istream& stream, int &proto, -- cgit v1.2.3 From 16f33f163d4e2c10320a96a22bbab71c9a0df195 Mon Sep 17 00:00:00 2001 From: kazcw Date: Sat, 28 Jun 2014 18:14:36 -0700 Subject: fix RPC error replies After pull #4288, RPC messages indicating errors have a Content-Length unrelated to their actual contents, rendering bitcoin-cli and curl unable to decode the reply. This patch sets the Content-Length field based on the actual content returned. Additionally, pull #4288 clobbered the error descriptions provided in ErrorReply, which bitcoin-cli relies upon; this patch moves #4288 http-error descriptions to an HTTPError method, allowing HTTPReply to pass content on unchanged. --- src/rpcprotocol.cpp | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) (limited to 'src/rpcprotocol.cpp') diff --git a/src/rpcprotocol.cpp b/src/rpcprotocol.cpp index 2cb4a35c4..dd8692e80 100644 --- a/src/rpcprotocol.cpp +++ b/src/rpcprotocol.cpp @@ -54,8 +54,19 @@ static string rfc1123Time() return DateTimeStrFormat("%a, %d %b %Y %H:%M:%S +0000", GetTime()); } -string HTTPReply(int nStatus, const string& strMsg, bool keepalive, - bool headersOnly, const char *contentType) +static const char *httpStatusDescription(int nStatus) +{ + switch (nStatus) { + case HTTP_OK: return "OK"; + case HTTP_BAD_REQUEST: return "Bad Request"; + case HTTP_FORBIDDEN: return "Forbidden"; + case HTTP_NOT_FOUND: return "Not Found"; + case HTTP_INTERNAL_SERVER_ERROR: return "Internal Server Error"; + default: return ""; + } +} + +string HTTPError(int nStatus, bool keepalive, bool headersOnly) { if (nStatus == HTTP_UNAUTHORIZED) return strprintf("HTTP/1.0 401 Authorization Required\r\n" @@ -75,20 +86,13 @@ string HTTPReply(int nStatus, const string& strMsg, bool keepalive, "

401 Unauthorized.

\r\n" "\r\n", rfc1123Time(), FormatFullVersion()); - const char *cStatus; - if (nStatus == HTTP_OK) cStatus = "OK"; - else if (nStatus == HTTP_BAD_REQUEST) cStatus = "Bad Request"; - else if (nStatus == HTTP_FORBIDDEN) cStatus = "Forbidden"; - else if (nStatus == HTTP_NOT_FOUND) cStatus = "Not Found"; - else if (nStatus == HTTP_INTERNAL_SERVER_ERROR) cStatus = "Internal Server Error"; - else cStatus = ""; - - bool useInternalContent = false; - if (nStatus != HTTP_OK) { - contentType = "text/plain"; - useInternalContent = true; - } + return HTTPReply(nStatus, httpStatusDescription(nStatus), keepalive, + headersOnly, "text/plain"); +} +string HTTPReply(int nStatus, const string& strMsg, bool keepalive, + bool headersOnly, const char *contentType) +{ return strprintf( "HTTP/1.1 %d %s\r\n" "Date: %s\r\n" @@ -99,14 +103,14 @@ string HTTPReply(int nStatus, const string& strMsg, bool keepalive, "\r\n" "%s", nStatus, - cStatus, + httpStatusDescription(nStatus), rfc1123Time(), keepalive ? "keep-alive" : "close", - strMsg.size(), + (headersOnly ? 0 : strMsg.size()), contentType, FormatFullVersion(), - (headersOnly ? "" : - (useInternalContent ? cStatus : strMsg.c_str()))); + (headersOnly ? "" : strMsg.c_str()) + ); } bool ReadHTTPRequestLine(std::basic_istream& stream, int &proto, -- cgit v1.2.3 From 2ec5a3d212ac4b09e6c32d495f34ee3cdedc8c66 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 20 Jun 2014 15:21:30 +0200 Subject: rpc: Prevent easy memory exhaustion attack Allocate memory for POST message data only as bytes come in, instead of all at once at the beginning. Fixes #4343. --- src/rpcprotocol.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'src/rpcprotocol.cpp') diff --git a/src/rpcprotocol.cpp b/src/rpcprotocol.cpp index dd8692e80..9e18ca847 100644 --- a/src/rpcprotocol.cpp +++ b/src/rpcprotocol.cpp @@ -25,6 +25,9 @@ using namespace boost; using namespace boost::asio; using namespace json_spirit; +// Number of bytes to allocate and read at most at once in post data +const size_t POST_READ_SIZE = 256 * 1024; + // // HTTP protocol // @@ -204,8 +207,17 @@ int ReadHTTPMessage(std::basic_istream& stream, map 0) { - vector vch(nLen); - stream.read(&vch[0], nLen); + vector vch; + size_t ptr = 0; + while (ptr < (size_t)nLen) + { + size_t bytes_to_read = std::min((size_t)nLen - ptr, POST_READ_SIZE); + vch.resize(ptr + bytes_to_read); + stream.read(&vch[ptr], bytes_to_read); + if (!stream) // Connection lost while reading + return HTTP_INTERNAL_SERVER_ERROR; + ptr += bytes_to_read; + } strMessageRet = string(vch.begin(), vch.end()); } -- cgit v1.2.3