SMTP Protocol
This section provides an overview of the SMTPProtocol
class and related functionality implemented in the smtp_protocol.py
file. It describes how the SMTP protocol is handled, including command parsing, response formatting, and state management.
SMTPProtocol Class
The SMTPProtocol
class manages the state and communication for the SMTP protocol. It is responsible for handling SMTP commands, managing session states, and generating appropriate responses.
smtp_protocol.SMTPProtocol
Bases: LineReceiver
Source code in src/smtp_protocol.py
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105 | class SMTPProtocol(LineReceiver):
def __init__(self, factory, debug=False):
self.factory = factory
self.ip = None
self.debug = debug
self.ai_service = AIService(debug_mode=self.debug)
self.responses = ResponseManager(self.ai_service, debug)
self.state = 'INITIAL'
self.data_buffer = []
self.auth_step = None
self.auth_username = None
self.auth_password = None
def connectionMade(self):
self.ip = self.transport.getPeer().host
if not self.factory.rate_limiter.allow_connection(self.ip):
logger.info(f"Rate limit exceeded for IP: {self.ip}")
self.transport.loseConnection()
return
banner = self.factory.banner.get_banner()
self.sendLine(banner.encode('utf-8'))
log_interaction(self.ip, 'WELCOME', banner)
def lineReceived(self, line):
try:
command = line.decode('utf-8').strip()
if self.state == 'DATA':
if command == ".":
self.state = 'INITIAL'
data_message = "\n".join(self.data_buffer)
self.data_buffer = []
response = self.responses.get_response("250-DATA", "250 OK: Queued")
else:
self.data_buffer.append(command)
return
else:
response = self._get_response(command)
self.sendLine(response.encode('utf-8'))
log_interaction(self.ip, command, response)
except Exception as e:
logger.error(f"Error processing command from {self.ip}: {e}")
self.sendLine(b"500 Command unrecognized")
def _get_response(self, command):
command_upper = command.upper()
if command_upper.startswith("EHLO"):
return self._ehlo_response()
elif command_upper.startswith("HELO"):
return self.responses.get_response("250-HELO", "250 localhost")
# Other SMTP commands here
def _ehlo_response(self):
response = [self.responses.get_response("250-EHLO", f"250-{self.factory.banner.domain_name} Hello [{self.ip}]")]
capabilities = [
"SIZE 37748736",
"PIPELINING",
"DSN",
"ENHANCEDSTATUSCODES",
"STARTTLS",
"AUTH LOGIN PLAIN",
"8BITMIME",
"SMTPUTF8",
]
response.extend([f"250-{cap}" for cap in capabilities[:-1]])
response.append(f"250 {capabilities[-1]}")
return "\n".join(response)
|
SMTPFactory Class
The SMTPFactory
class is a factory for creating instances of the SMTPProtocol
class. It initializes and builds new protocol instances for handling connections.
smtp_protocol.SMTPFactory
Bases: Factory
Source code in src/smtp_protocol.py
109
110
111
112
113
114
115
116
117
118 | class SMTPFactory(protocol.Factory):
def __init__(self, debug=False):
self.config = ConfigManager()
self.debug = debug or self.config.getboolean('server', 'debug')
self.banner = SMTPBanner(self.config.get('server', 'domain', fallback='localhost'),
self.config.get('server', 'technology', fallback='generic'))
self.rate_limiter = RateLimiter(self.config.getint('server', 'rate_limit', fallback=5))
def buildProtocol(self, addr):
return SMTPProtocol(self, debug=self.debug)
|