Guest User

Untitled

a guest
Apr 21st, 2018
279
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.37 KB | None | 0 0
  1. import asyncore
  2. import base64
  3.  
  4. from smtpd import SMTPServer, SMTPChannel, DEBUGSTREAM
  5.  
  6.  
  7. def decode_b64(data):
  8. """Wrapper for b64decode, without having to struggle with bytestrings."""
  9. byte_string = data.encode('utf-8')
  10. decoded = base64.b64decode(byte_string)
  11. return decoded.decode('utf-8')
  12.  
  13.  
  14. def encode_b64(data):
  15. """Wrapper for b64encode, without having to struggle with bytestrings."""
  16. byte_string = data.encode('utf-8')
  17. encoded = base64.b64encode(byte_string)
  18. return encoded.decode('utf-8')
  19.  
  20.  
  21. class FakeCredentialValidator(object):
  22. def __init__(self, username, password, chanel):
  23. self.username = username
  24. self.password = password
  25. self.chanel = chanel
  26.  
  27. def validate(self):
  28. if self.username == 'hasan@test.com' and self.password == 'gholami':
  29. return True
  30. return False
  31.  
  32.  
  33. class MySMTPChanel(SMTPChannel):
  34. credential_validator = FakeCredentialValidator
  35.  
  36. def __init__(self, server, conn, addr, *args, **kwargs):
  37. super().__init__(server, conn, addr, *args, **kwargs)
  38. self.username = None
  39. self.password = None
  40. self.authenticated = False
  41. self.authenticating = False
  42.  
  43. def smtp_AUTH(self, arg):
  44. if 'PLAIN' in arg:
  45. split_args = arg.split(' ')
  46. # second arg is Base64-encoded string of blah\0username\0password
  47. authbits = decode_b64(split_args[1]).split('\0')
  48. self.username = authbits[1]
  49. self.password = authbits[2]
  50. if self.credential_validator(self.username, self.password, self).validate():
  51. self.authenticated = True
  52. self.push('235 Authentication successful.')
  53. else:
  54. self.push('454 Temporary authentication failure.')
  55. self.close_when_done()
  56.  
  57. elif 'LOGIN' in arg:
  58. self.authenticating = True
  59. split_args = arg.split(' ')
  60.  
  61. # Some implmentations of 'LOGIN' seem to provide the username
  62. # along with the 'LOGIN' stanza, hence both situations are
  63. # handled.
  64. if len(split_args) == 2:
  65. self.username = decode_b64(arg.split(' ')[1])
  66. self.push('334 ' + encode_b64('Username'))
  67. else:
  68. self.push('334 ' + encode_b64('Username'))
  69.  
  70. elif not self.username:
  71. self.username = decode_b64(arg)
  72. self.push('334 ' + encode_b64('Password'))
  73. else:
  74. self.authenticating = False
  75. self.password = decode_b64(arg)
  76. if self.credential_validator and self.credential_validator.validate(self.username, self.password):
  77. self.authenticated = True
  78. self.push('235 Authentication successful.')
  79. else:
  80. self.push('454 Temporary authentication failure.')
  81. self.close_when_done()
  82.  
  83. def smtp_EHLO(self, arg):
  84. if not arg:
  85. self.push('501 Syntax: EHLO hostname')
  86. return
  87. # See issue #21783 for a discussion of this behavior.
  88. if self.seen_greeting:
  89. self.push('503 Duplicate HELO/EHLO')
  90. return
  91. self._set_rset_state()
  92. self.seen_greeting = arg
  93. self.extended_smtp = True
  94. self.push('250-%s' % self.fqdn)
  95. self.push('250-AUTH LOGIN PLAIN')
  96. if self.data_size_limit:
  97. self.push('250-SIZE %s' % self.data_size_limit)
  98. self.command_size_limits['MAIL'] += 26
  99. if not self._decode_data:
  100. self.push('250-8BITMIME')
  101. if self.enable_SMTPUTF8:
  102. self.push('250-SMTPUTF8')
  103. self.command_size_limits['MAIL'] += 10
  104. self.push('250 HELP')
  105.  
  106. # SMTP and ESMTP commands
  107. def smtp_HELO(self, arg):
  108. print(arg)
  109. if not arg:
  110. self.push('501 Syntax: HELO hostname')
  111. return
  112. # See issue #21783 for a discussion of this behavior.
  113. if self.seen_greeting:
  114. self.push('503 Duplicate HELO/EHLO')
  115. return
  116. self._set_rset_state()
  117. self.seen_greeting = arg
  118. self.push('250 %s' % self.fqdn)
  119.  
  120. # This code is taken directly from the underlying smtpd.SMTPChannel
  121. # support for AUTH is added.
  122.  
  123. def run_command_with_arg(self, command, arg):
  124. method = getattr(self, 'smtp_' + command, None)
  125. if not method:
  126. self.push('500 Error: command "%s" not recognized' % command)
  127. return
  128.  
  129. # White list of operations that are allowed prior to AUTH.
  130. if command not in ['AUTH', 'EHLO', 'HELO', 'NOOP', 'RSET', 'QUIT']:
  131. if not self.authenticated:
  132. self.push('530 Authentication required')
  133. return
  134.  
  135. method(arg)
  136.  
  137. def found_terminator(self):
  138. line = self._emptystring.join(self.received_lines)
  139. print('Data:', repr(line), file=DEBUGSTREAM)
  140. self.received_lines = []
  141. if self.smtp_state == self.COMMAND:
  142. sz, self.num_bytes = self.num_bytes, 0
  143. if not line:
  144. self.push('500 Error: bad syntax')
  145. return
  146. if not self._decode_data:
  147. line = str(line, 'utf-8')
  148. i = line.find(' ')
  149.  
  150. if self.authenticating:
  151. # If we are in an authenticating state, call the
  152. # method smtp_AUTH.
  153. arg = line.strip()
  154. command = 'AUTH'
  155. elif i < 0:
  156. command = line.upper()
  157. arg = None
  158. else:
  159. command = line[:i].upper()
  160. arg = line[i + 1:].strip()
  161. max_sz = (self.command_size_limits[command]
  162. if self.extended_smtp else self.command_size_limit)
  163. if sz > max_sz:
  164. self.push('500 Error: line too long')
  165. return
  166.  
  167. self.run_command_with_arg(command, arg)
  168. return
  169. else:
  170. if self.smtp_state != self.DATA:
  171. self.push('451 Internal confusion')
  172. self.num_bytes = 0
  173. return
  174. if self.data_size_limit and self.num_bytes > self.data_size_limit:
  175. self.push('552 Error: Too much mail data')
  176. self.num_bytes = 0
  177. return
  178. # Remove extraneous carriage returns and de-transparency according
  179. # to RFC 5321, Section 4.5.2.
  180. data = []
  181. for text in line.split(self._linesep):
  182. if text and text[0] == self._dotsep:
  183. data.append(text[1:])
  184. else:
  185. data.append(text)
  186. self.received_data = self._newline.join(data)
  187. args = (self.peer, self.mailfrom, self.rcpttos, self.received_data)
  188. kwargs = {}
  189. if not self._decode_data:
  190. kwargs = {
  191. 'mail_options': self.mail_options,
  192. 'rcpt_options': self.rcpt_options,
  193. }
  194. status = self.smtp_server.process_message(*args, **kwargs)
  195. self._set_post_data_state()
  196. if not status:
  197. self.push('250 OK')
  198. else:
  199. self.push(status)
  200.  
  201.  
  202. class MySMTPServer(SMTPServer):
  203. channel_class = MySMTPChanel
  204.  
  205. def process_message(self, peer, mailfrom, rcpttos, data, **kwargs):
  206. print(peer, mailfrom, rcpttos, data, kwargs)
  207.  
  208.  
  209. MySMTPServer(
  210. ('0.0.0.0', 4465),
  211. None
  212. )
  213.  
  214. asyncore.loop()
Add Comment
Please, Sign In to add comment