|
203 | 203 | # maximal amount of data to read at one time in _safe_read |
204 | 204 | MAXAMOUNT = 1048576 |
205 | 205 |
|
| 206 | +# maximal line length when calling readline(). |
| 207 | +_MAXLINE = 65536 |
| 208 | + |
206 | 209 | class HTTPMessage(email.message.Message): |
207 | 210 | # XXX The only usage of this method is in |
208 | 211 | # http.server.CGIHTTPRequestHandler. Maybe move the code there so |
@@ -245,7 +248,9 @@ def parse_headers(fp, _class=HTTPMessage): |
245 | 248 | """ |
246 | 249 | headers = [] |
247 | 250 | while True: |
248 | | - line = fp.readline() |
| 251 | + line = fp.readline(_MAXLINE + 1) |
| 252 | + if len(line) > _MAXLINE: |
| 253 | + raise LineTooLong("header line") |
249 | 254 | headers.append(line) |
250 | 255 | if line in (b'\r\n', b'\n', b''): |
251 | 256 | break |
@@ -349,7 +354,10 @@ def begin(self): |
349 | 354 | break |
350 | 355 | # skip the header from the 100 response |
351 | 356 | while True: |
352 | | - skip = self.fp.readline().strip() |
| 357 | + skip = self.fp.readline(_MAXLINE + 1) |
| 358 | + if len(skip) > _MAXLINE: |
| 359 | + raise LineTooLong("header line") |
| 360 | + skip = skip.strip() |
353 | 361 | if not skip: |
354 | 362 | break |
355 | 363 | if self.debuglevel > 0: |
@@ -525,7 +533,9 @@ def _read_chunked(self, amt): |
525 | 533 | value = [] |
526 | 534 | while True: |
527 | 535 | if chunk_left is None: |
528 | | - line = self.fp.readline() |
| 536 | + line = self.fp.readline(_MAXLINE + 1) |
| 537 | + if len(line) > _MAXLINE: |
| 538 | + raise LineTooLong("chunk size") |
529 | 539 | i = line.find(b";") |
530 | 540 | if i >= 0: |
531 | 541 | line = line[:i] # strip chunk-extensions |
@@ -560,7 +570,9 @@ def _read_chunked(self, amt): |
560 | 570 | # read and discard trailer up to the CRLF terminator |
561 | 571 | ### note: we shouldn't have any trailers! |
562 | 572 | while True: |
563 | | - line = self.fp.readline() |
| 573 | + line = self.fp.readline(_MAXLINE + 1) |
| 574 | + if len(line) > _MAXLINE: |
| 575 | + raise LineTooLong("trailer line") |
564 | 576 | if not line: |
565 | 577 | # a vanishingly small number of sites EOF without |
566 | 578 | # sending the trailer |
@@ -703,7 +715,9 @@ def _tunnel(self): |
703 | 715 | raise socket.error("Tunnel connection failed: %d %s" % (code, |
704 | 716 | message.strip())) |
705 | 717 | while True: |
706 | | - line = response.fp.readline() |
| 718 | + line = response.fp.readline(_MAXLINE + 1) |
| 719 | + if len(line) > _MAXLINE: |
| 720 | + raise LineTooLong("header line") |
707 | 721 | if line == b'\r\n': |
708 | 722 | break |
709 | 723 |
|
@@ -1133,6 +1147,11 @@ def __init__(self, line): |
1133 | 1147 | self.args = line, |
1134 | 1148 | self.line = line |
1135 | 1149 |
|
| 1150 | +class LineTooLong(HTTPException): |
| 1151 | + def __init__(self, line_type): |
| 1152 | + HTTPException.__init__(self, "got more than %d bytes when reading %s" |
| 1153 | + % (_MAXLINE, line_type)) |
| 1154 | + |
1136 | 1155 | # for backwards compatibility |
1137 | 1156 | error = HTTPException |
1138 | 1157 |
|
|
0 commit comments