caterpillar/smtp.py

114 lines
3.4 KiB
Python
Raw Normal View History

2024-02-29 15:49:20 +00:00
#!/usr/bin/python3
#
# smtp.py
2024-07-12 01:14:39 +00:00
# SMTP mail sender over HTTP/S
2024-02-29 15:49:20 +00:00
#
2024-06-20 08:21:08 +00:00
# Caterpillar Proxy - The simple web debugging proxy (formerly, php-httpproxy)
2024-02-29 15:49:20 +00:00
# Namyheon Go (Catswords Research) <gnh1201@gmail.com>
# https://github.com/gnh1201/caterpillar
# Created at: 2024-03-01
2024-07-12 01:14:39 +00:00
# Updated at: 2024-07-12
2024-02-29 15:49:20 +00:00
#
2024-07-12 01:14:39 +00:00
import asyncio
from aiosmtpd.controller import Controller
from email.message import EmailMessage
import sys
import requests
2024-07-12 01:14:39 +00:00
from platform import python_version
2024-02-29 15:53:33 +00:00
from decouple import config
2024-02-29 15:54:08 +00:00
from requests.auth import HTTPBasicAuth
2024-07-11 10:02:08 +00:00
from base import (
extract_credentials,
jsonrpc2_encode,
2024-08-31 06:48:35 +00:00
Logger, jsonrpc2_decode,
2024-07-11 10:02:08 +00:00
)
logger = Logger(name="smtp")
2024-02-29 15:53:33 +00:00
try:
2024-07-11 10:02:08 +00:00
smtp_host = config("SMTP_HOST", default="127.0.0.1")
smtp_port = config("SMTP_PORT", default=25, cast=int)
_username, _password, server_url = extract_credentials(
config("SERVER_URL", default="")
)
2024-02-29 15:53:33 +00:00
except KeyboardInterrupt:
logger.warning("[*] User has requested an interrupt")
logger.warning("[*] Application Exiting.....")
2024-02-29 15:53:33 +00:00
sys.exit()
auth = None
if _username:
auth = HTTPBasicAuth(_username, _password)
2024-08-31 05:37:21 +00:00
2024-07-12 01:14:39 +00:00
class CaterpillarSMTPHandler:
def __init__(self):
self.smtpd_hostname = "CaterpillarSMTPServer"
self.smtp_version = "0.1.6"
async def handle_DATA(self, server, session, envelope):
2024-08-31 06:48:35 +00:00
mail_from = envelope.mail_from
rcpt_tos = envelope.rcpt_tos
2024-07-12 01:14:39 +00:00
data = envelope.content
message = EmailMessage()
message.set_content(data)
2024-02-29 19:09:45 +00:00
2024-08-31 05:37:21 +00:00
subject = message.get("Subject", "")
to = message.get("To", "")
2024-02-29 19:36:04 +00:00
proxy_data = {
2024-07-11 10:02:08 +00:00
"headers": {
"User-Agent": "php-httpproxy/0.1.6 (Client; Python "
+ python_version()
+ "; Caterpillar; abuse@catswords.net)",
2024-02-29 19:36:04 +00:00
},
2024-07-11 10:02:08 +00:00
"data": {
2024-02-29 19:36:04 +00:00
"to": to,
2024-08-31 06:48:35 +00:00
"from": mail_from,
2024-02-29 19:36:04 +00:00
"subject": subject,
2024-07-11 10:02:08 +00:00
"message": data.decode("utf-8"),
},
2024-02-29 19:36:04 +00:00
}
2024-07-11 10:02:08 +00:00
_, raw_data = jsonrpc2_encode("relay_sendmail", proxy_data["data"])
try:
2024-07-12 01:14:39 +00:00
response = await asyncio.to_thread(
requests.post,
server_url,
2024-08-31 05:37:21 +00:00
headers=proxy_data["headers"],
2024-07-12 01:14:39 +00:00
data=raw_data,
2024-08-31 05:37:21 +00:00
auth=auth,
2024-07-12 01:14:39 +00:00
)
2024-02-29 15:59:36 +00:00
if response.status_code == 200:
2024-08-31 06:48:35 +00:00
_type, _id, rpc_data = jsonrpc2_decode(response.text)
if rpc_data["success"]:
logger.info("[*] Email sent successfully.")
2024-02-29 15:59:36 +00:00
else:
2024-08-31 06:48:35 +00:00
raise Exception(f"({rpc_data['code']}) {rpc_data['message']}")
2024-02-29 19:39:58 +00:00
else:
2024-07-12 01:14:39 +00:00
raise Exception(f"Status {response.status_code}")
except Exception as e:
logger.error("[*] Failed to send email", exc_info=e)
2024-08-31 05:37:21 +00:00
return "500 Could not process your message. " + str(e)
2024-08-31 05:37:21 +00:00
return "250 OK"
2024-07-12 01:14:39 +00:00
2024-02-29 15:40:54 +00:00
2024-07-12 01:14:39 +00:00
# https://aiosmtpd-pepoluan.readthedocs.io/en/latest/migrating.html
def main():
handler = CaterpillarSMTPHandler()
controller = Controller(handler, hostname=smtp_host, port=smtp_port)
# Run the event loop in a separate thread.
controller.start()
# Wait for the user to press Return.
2024-08-31 05:37:21 +00:00
input("SMTP server running. Press Return to stop server and exit.")
2024-07-12 01:14:39 +00:00
controller.stop()
logger.warning("[*] User has requested an interrupt")
logger.warning("[*] Application Exiting.....")
sys.exit()
2024-02-29 15:40:54 +00:00
2024-08-31 05:37:21 +00:00
2024-07-12 01:14:39 +00:00
if __name__ == "__main__":
main()