Index: Lib/smtpd.py =================================================================== --- Lib/smtpd.py (revision 86712) +++ Lib/smtpd.py (working copy) @@ -121,6 +121,9 @@ self.rcpttos = [] self.received_data = '' self.fqdn = socket.getfqdn() + self.data_size_limit = 33554432 + self.command_size_limit = 512 + self.num_bytes = 0 try: self.peer = conn.getpeername() except socket.error as err: @@ -262,6 +265,15 @@ # Implementation of base class abstract method def collect_incoming_data(self, data): + limit = None + if self.smtp_state == self.COMMAND: + limit = self.command_size_limit + elif self.smtp_state == self.DATA: + limit = self.data_size_limit + if limit and self.num_bytes > limit: + return + elif limit: + self.num_bytes += len(data) self.received_lines.append(str(data, "utf8")) # Implementation of base class abstract method @@ -270,6 +282,10 @@ print('Data:', repr(line), file=DEBUGSTREAM) self.received_lines = [] if self.smtp_state == self.COMMAND: + if self.num_bytes > self.command_size_limit: + self.push('500 Error: line too long') + self.num_bytes = 0 + return if not line: self.push('500 Error: bad syntax') return @@ -291,6 +307,10 @@ if self.smtp_state != self.DATA: self.push('451 Internal confusion') return + if self.num_bytes > self.data_size_limit: + self.push('552 Error: Too much mail data') + self.num_bytes = 0 + return # Remove extraneous carriage returns and de-transparency according # to RFC 821, Section 4.5.2. data = [] @@ -307,6 +327,7 @@ self.rcpttos = [] self.mailfrom = None self.smtp_state = self.COMMAND + self.num_bytes = 0 self.set_terminator(b'\r\n') if not status: self.push('250 Ok') Index: Lib/test/test_smtpd.py =================================================================== --- Lib/test/test_smtpd.py (revision 86712) +++ Lib/test/test_smtpd.py (working copy) @@ -121,6 +121,24 @@ self.assertEqual(self.channel.socket.last, b'451 Internal confusion\r\n') + def test_command_too_long(self): + self.write_line(b'MAIL from ' + + b'a' * self.channel.command_size_limit + + b'@example') + self.assertEqual(self.channel.socket.last, + b'500 Error: line too long\r\n') + + def test_data_too_long(self): + # Small hack. Setting limit to 2K octets here will save us some time. + self.channel.data_size_limit = 2048 + self.write_line(b'MAIL From:eggs@example') + self.write_line(b'RCPT To:spam@example') + self.write_line(b'DATA') + self.write_line(b'A' * self.channel.data_size_limit + + b'A\r\n.') + self.assertEqual(self.channel.socket.last, + b'552 Error: Too much mail data\r\n') + def test_need_MAIL(self): self.write_line(b'RCPT to:spam@example') self.assertEqual(self.channel.socket.last,