aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/beam/packet_parser.c
diff options
context:
space:
mode:
authorSteve Vinoski <[email protected]>2011-11-24 21:03:33 -0500
committerSverker Eriksson <[email protected]>2011-12-05 18:02:04 +0100
commit5984409d1264871cbe61bfec875de53e51713efb (patch)
tree81b099759be61caa3528dc37c9f4c1f926b6d51c /erts/emulator/beam/packet_parser.c
parent290be471b96879bd9cdcc89d7841eb5557c54e50 (diff)
downloadotp-5984409d1264871cbe61bfec875de53e51713efb.tar.gz
otp-5984409d1264871cbe61bfec875de53e51713efb.tar.bz2
otp-5984409d1264871cbe61bfec875de53e51713efb.zip
honor packet_size for http packet parsing to fix OTP-9389
Allow applications to use a packet_size setting on a socket to control acceptable HTTP header line length. This gives them the ability to accept HTTP headers larger than the default settings allow, but also lets them avoid DOS attacks by accepting header lines only up to whatever length they wish to allow. Without this change, if an HTTP request/response line or header arrives on a socket in http, http_bin, httph, or httph_bin parsing mode, and the request/response line or header is too long to fit into a default inet_drv buffer of 1460 bytes, an unexpected error occurs. These problems were described and discussed on erlang-questions in June 2011 in this thread: http://erlang.org/pipermail/erlang-questions/2011-June/059563.html In the original code, no buffer reallocation occurs to enlarge the buffer, even if packet_size or line_length are set in a way that should allow the HTTP data to be parsed properly. The only available workaround was to collect headers and parse them using erlang:decode_packet, but that approach has drawbacks such as having to collect all HTTP header data before it can be handed to decode_packet for correct parsing, and also requiring each and every Erlang web server developer/maintainer to add the workaround to his or her web server. Change the packet parser to honor the packet_size setting for HTTP parsing. If packet_size is set, and an HTTP request/response or header line exceeds the default 1460 byte TCP buffer limit, return an indication to tcp_remain that it should realloc the buffer to enlarge it to packet_size. Also fix the HTTP parsing code to properly honor line_length by truncating any HTTP request/response or header lines that exceed that setting. For backward compatibility, default behavior is unchanged; if an application wants to be able to accept long HTTP header lines, it must set packet_size to an appropriate value. Buffer reallocation occurs only when needed, so the original default buffer size in the code is still the default. Make the line mode parsing honor packet_size as well, for consistency. Add new regression tests to the emulator decode_packet suite and also to the kernel gen_tcp_misc suite. The documentation for packet_size in inet:setopts/2 is already sufficient. Many thanks to Sverker Eriksson for his guidance on how to best fix this bug and also for reviewing a number of patch attempts prior to this one.
Diffstat (limited to 'erts/emulator/beam/packet_parser.c')
-rw-r--r--erts/emulator/beam/packet_parser.c43
1 files changed, 36 insertions, 7 deletions
diff --git a/erts/emulator/beam/packet_parser.c b/erts/emulator/beam/packet_parser.c
index a66d60aa22..8c1662dc6f 100644
--- a/erts/emulator/beam/packet_parser.c
+++ b/erts/emulator/beam/packet_parser.c
@@ -301,7 +301,11 @@ int packet_get_length(enum PacketParseType htype,
/* TCP_PB_LINE_LF: [Data ... \n] */
const char* ptr2;
if ((ptr2 = memchr(ptr, '\n', n)) == NULL) {
- if (n >= trunc_len && trunc_len!=0) { /* buffer full */
+ if (n > max_plen && max_plen != 0) { /* packet full */
+ DEBUGF((" => packet full (no NL)=%d\r\n", n));
+ goto error;
+ }
+ else if (n >= trunc_len && trunc_len!=0) { /* buffer full */
DEBUGF((" => line buffer full (no NL)=%d\r\n", n));
return trunc_len;
}
@@ -309,6 +313,10 @@ int packet_get_length(enum PacketParseType htype,
}
else {
int len = (ptr2 - ptr) + 1; /* including newline */
+ if (len > max_plen && max_plen!=0) {
+ DEBUGF((" => packet_size %d exceeded\r\n", max_plen));
+ goto error;
+ }
if (len > trunc_len && trunc_len!=0) {
DEBUGF((" => truncated line=%d\r\n", trunc_len));
return trunc_len;
@@ -401,7 +409,11 @@ int packet_get_length(enum PacketParseType htype,
const char* ptr2 = memchr(ptr1, '\n', len);
if (ptr2 == NULL) {
- if (n >= trunc_len && trunc_len!=0) { /* buffer full */
+ if (max_plen != 0) {
+ if (n > max_plen) /* packet full */
+ goto error;
+ }
+ else if (n >= trunc_len && trunc_len!=0) { /* buffer full */
plen = trunc_len;
goto done;
}
@@ -409,21 +421,38 @@ int packet_get_length(enum PacketParseType htype,
}
else {
plen = (ptr2 - ptr) + 1;
-
- if (*statep == 0)
+
+ if (*statep == 0) {
+ if (max_plen != 0 && plen > max_plen)
+ goto error;
+ if (plen >= trunc_len && trunc_len != 0)
+ plen = trunc_len;
goto done;
-
+ }
+
if (plen < n) {
if (SP(ptr2+1) && plen>2) {
/* header field value continue on next line */
ptr1 = ptr2+1;
len = n - plen;
}
- else
+ else {
+ if (max_plen != 0 && plen > max_plen)
+ goto error;
+ if (plen >= trunc_len && trunc_len != 0)
+ plen = trunc_len;
goto done;
+ }
}
- else
+ else {
+ if (max_plen != 0 && plen > max_plen)
+ goto error;
+ if (plen >= trunc_len && trunc_len != 0) {
+ plen = trunc_len;
+ goto done;
+ }
goto more;
+ }
}
}
}