close connection after several bad commands

This commit is contained in:
Chuck 2013-07-05 14:42:41 +07:00
parent 821265bb6d
commit 2795a4ca8d

View File

@ -31,6 +31,7 @@ class bitmessageSMTPChannel(asynchat.async_chat):
self.__data = '' self.__data = ''
self.__fqdn = socket.getfqdn() self.__fqdn = socket.getfqdn()
self.__version = 'Python SMTP proxy version 0.2a' self.__version = 'Python SMTP proxy version 0.2a'
self.__invalid_command_count = 0
self.logged_in = False self.logged_in = False
try: try:
self.__peer = conn.getpeername() self.__peer = conn.getpeername()
@ -53,6 +54,15 @@ class bitmessageSMTPChannel(asynchat.async_chat):
def collect_incoming_data(self, data): def collect_incoming_data(self, data):
self.__line.append(data) self.__line.append(data)
# Close the connection after enough errors
def invalid_command(self, msg):
self.__invalid_command_count += 1
if self.__invalid_command_count >= 10:
self.push(msg + " (closing connection)")
self.close_when_done()
else:
self.push(msg)
# Implementation of base class abstract method # Implementation of base class abstract method
def found_terminator(self): def found_terminator(self):
line = ''.join(self.__line) line = ''.join(self.__line)
@ -60,7 +70,7 @@ class bitmessageSMTPChannel(asynchat.async_chat):
self.__line = [] self.__line = []
if self.__state == self.COMMAND: if self.__state == self.COMMAND:
if not line: if not line:
self.push('500 Error: bad syntax') self.invalid_command('500 Error: bad syntax')
return return
method = None method = None
i = line.find(' ') i = line.find(' ')
@ -72,13 +82,13 @@ class bitmessageSMTPChannel(asynchat.async_chat):
arg = line[i+1:].strip() arg = line[i+1:].strip()
method = getattr(self, 'smtp_' + command, None) method = getattr(self, 'smtp_' + command, None)
if not method: if not method:
self.push('502 Error: command "%s" not implemented' % command) self.invalid_command('502 Error: command "%s" not implemented' % command)
return return
method(arg) method(arg)
return return
else: else:
if self.__state != self.DATA: if self.__state != self.DATA:
self.push('451 Internal confusion') self.invalid_command('451 Internal confusion')
return return
# Remove extraneous carriage returns and de-transparency according # Remove extraneous carriage returns and de-transparency according
# to RFC 821, Section 4.5.2. # to RFC 821, Section 4.5.2.
@ -110,10 +120,11 @@ class bitmessageSMTPChannel(asynchat.async_chat):
def smtp_EHLO(self, arg): def smtp_EHLO(self, arg):
if not arg: if not arg:
self.push('501 Syntax: EHLO hostname') self.invalid_command('501 Syntax: EHLO hostname')
return return
if self.__greeting: if self.__greeting:
self.push('503 Duplicate HELO/EHLO') self.invalid_command('503 Duplicate HELO/EHLO')
return
else: else:
self.__greeting = arg self.__greeting = arg
self.push('250-%s offers:' % self.__fqdn) self.push('250-%s offers:' % self.__fqdn)
@ -121,10 +132,10 @@ class bitmessageSMTPChannel(asynchat.async_chat):
def smtp_HELO(self, arg): def smtp_HELO(self, arg):
if not arg: if not arg:
self.push('501 Syntax: HELO hostname') self.invalid_command('501 Syntax: HELO hostname')
return return
if self.__greeting: if self.__greeting:
self.push('503 Duplicate HELO/EHLO') self.invalid_command('503 Duplicate HELO/EHLO')
else: else:
self.__greeting = arg self.__greeting = arg
self.push('250 %s' % self.__fqdn) self.push('250 %s' % self.__fqdn)
@ -132,7 +143,7 @@ class bitmessageSMTPChannel(asynchat.async_chat):
def smtp_AUTH(self, arg): def smtp_AUTH(self, arg):
encoding, pw = arg.split(' ') encoding, pw = arg.split(' ')
if encoding != 'PLAIN': if encoding != 'PLAIN':
self.push('501 method not understood') self.invalid_command('501 method not understood')
return return
try: try:
@ -141,11 +152,11 @@ class bitmessageSMTPChannel(asynchat.async_chat):
z = 'error' z = 'error'
if z != '': if z != '':
self.push('501 method not understood') self.invalid_command('501 method not understood')
return return
if '@' not in username: if '@' not in username:
self.push('530 Access denied.') self.invalid_command('530 Access denied.')
return return
capitalization, address = username.split('@', 1) capitalization, address = username.split('@', 1)
@ -168,7 +179,7 @@ class bitmessageSMTPChannel(asynchat.async_chat):
# Must match full email address with capitalization # Must match full email address with capitalization
if username != self.fullUsername: if username != self.fullUsername:
self.push('530 Access denied.') self.invalid_command('530 Access denied.')
return return
# Each identity must be enabled independly by setting the smtppop3password for the identity # Each identity must be enabled independly by setting the smtppop3password for the identity
@ -183,11 +194,11 @@ class bitmessageSMTPChannel(asynchat.async_chat):
except: except:
pass pass
self.push('530 Access denied.') self.invalid_command('530 Access denied.')
def smtp_NOOP(self, arg): def smtp_NOOP(self, arg):
if arg: if arg:
self.push('501 Syntax: NOOP') self.invalid_command('501 Syntax: NOOP')
else: else:
self.push('250 Ok') self.push('250 Ok')
@ -215,19 +226,19 @@ class bitmessageSMTPChannel(asynchat.async_chat):
address = self.__getaddr('FROM:', arg) if arg else None address = self.__getaddr('FROM:', arg) if arg else None
if not address: if not address:
self.push('501 Syntax: MAIL FROM:<address>') self.invalid_command('501 Syntax: MAIL FROM: <address>')
return return
if self.__mailfrom: if self.__mailfrom:
self.push('503 Error: nested MAIL command') self.invalid_command('503 Error: nested MAIL command')
return return
if not self.logged_in: if not self.logged_in:
self.push('503 Not authenticated.') self.invalid_command('503 Not authenticated.')
return return
if address != self.fullUsername: if address != self.fullUsername:
self.push('530 Access denied: address domain must match Bitmessage identity') self.invalid_command('530 Access denied: address domain must match Bitmessage identity')
return return
self.__mailfrom = address self.__mailfrom = address
@ -237,15 +248,15 @@ class bitmessageSMTPChannel(asynchat.async_chat):
def smtp_RCPT(self, arg): def smtp_RCPT(self, arg):
print >> smtpd.DEBUGSTREAM, '===> RCPT', arg print >> smtpd.DEBUGSTREAM, '===> RCPT', arg
if not self.__mailfrom: if not self.__mailfrom:
self.push('503 Error: need MAIL command') self.invalid_command('503 Error: need MAIL command')
return return
if not self.logged_in: if not self.logged_in:
# This will never happen. :) # This will never happen. :)
self.push('503 Not authenticated.') self.invalid_command('503 Not authenticated.')
return return
address = self.__getaddr('TO:', arg) if arg else None address = self.__getaddr('TO:', arg) if arg else None
if not address: if not address:
self.push('501 Syntax: RCPT TO: <address>') self.invalid_command('501 Syntax: RCPT TO: <address>')
return return
capitalization, address = address.split('@', 1) capitalization, address = address.split('@', 1)
realAddress = applyBase58Capitalization(address, int(capitalization)) realAddress = applyBase58Capitalization(address, int(capitalization))
@ -255,7 +266,7 @@ class bitmessageSMTPChannel(asynchat.async_chat):
def smtp_RSET(self, arg): def smtp_RSET(self, arg):
if arg: if arg:
self.push('501 Syntax: RSET') self.invalid_command('501 Syntax: RSET')
return return
# Resets the sender, recipients, and data, but not the greeting # Resets the sender, recipients, and data, but not the greeting
self.__mailfrom = None self.__mailfrom = None
@ -266,13 +277,13 @@ class bitmessageSMTPChannel(asynchat.async_chat):
def smtp_DATA(self, arg): def smtp_DATA(self, arg):
if not self.logged_in: if not self.logged_in:
self.push('503 Not authenticated.') self.invalid_command('503 Not authenticated.')
return return
if not self.__rcpttos: if not self.__rcpttos:
self.push('503 Error: need RCPT command') self.invalid_command('503 Error: need RCPT command')
return return
if arg: if arg:
self.push('501 Syntax: DATA') self.invalid_command('501 Syntax: DATA')
return return
self.__state = self.DATA self.__state = self.DATA
self.set_terminator('\r\n.\r\n') self.set_terminator('\r\n.\r\n')