From 4f44a82e1d7ffd9df87bd49fb28d0fbbb8b23338 Mon Sep 17 00:00:00 2001 From: Kkevsterrr Date: Sat, 5 Sep 2020 23:56:28 -0400 Subject: [PATCH] Committed missing SMTP plugin --- plugins/smtp/client.py | 119 +++++++++++++++++++++++++++++++++++++++++ plugins/smtp/server.py | 116 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 235 insertions(+) create mode 100644 plugins/smtp/client.py create mode 100644 plugins/smtp/server.py diff --git a/plugins/smtp/client.py b/plugins/smtp/client.py new file mode 100644 index 0000000..61e8501 --- /dev/null +++ b/plugins/smtp/client.py @@ -0,0 +1,119 @@ +""" +Client +Run by the evaluator, tests a forbidden string from client to server +""" + +import argparse +import logging +import os +import random +import socket +import sys +import time +import traceback +import urllib.request + +import requests + +socket.setdefaulttimeout(1) + +import external_sites +import actions.utils + +from plugins.plugin_client import ClientPlugin + +BASEPATH = os.path.dirname(os.path.abspath(__file__)) + + +class SMTPClient(ClientPlugin): + """ + Defines the SMTP client. + """ + name = "smtp" + + def __init__(self, args): + """ + Initializes the SMTP client. + """ + ClientPlugin.__init__(self) + self.args = args + + @staticmethod + def get_args(command): + """ + Defines required args for this plugin + """ + super_args = ClientPlugin.get_args(command) + parser = argparse.ArgumentParser(description='SMTP Client') + + parser.add_argument('--server', action='store', help="server to connect to") + parser.add_argument('--smtp-request', action='store_true', help='Send an SMTP byte string that triggers censorship') + + args, _ = parser.parse_known_args(command) + args = vars(args) + + super_args.update(args) + return super_args + + def run(self, args, logger, engine=None): + """ + Try to send a forbidden string + """ + fitness = 0 + port = int(args["port"]) + server = args["server"] + bad_word = args["bad_word"] + msg = bad_word + + if args.get('smtp_request'): + msg = b'MAIL FROM: xiazai@upup.info \r\n' + + if type(msg) == str: + msg = msg.encode() + + try: + client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + client.settimeout(10) + client.connect((server, port)) + + tries = args.get("tries", 1) + for idx in range(0, int(tries)): + client.sendall(msg) + server_data = client.recv(1024) + logger.debug("Data recieved: %s", server_data.decode('utf-8', 'ignore')) + if server_data == b'k': + fitness += 100 + else: + fitness -= 90 + break + + time.sleep(1) + client.close() + # If the fitness is 0, the strategy did something to corrupt/interfere with the socket + # sending/receiving, usually by just artificially closing the connection. This behavior + # should not be rewarded with a higher fitness + if fitness == 0: + fitness -= 100 + except socket.timeout: + logger.debug("Client: Timeout") + fitness -= 100 + except socket.error as exc: + # If the censor we're running against tears down connects via RSTs, we can punish RSTs as + # if the strategy did not harm the underlying connection. However, if the censor only injects + # traffic, not resets, we should punish RSTs harshly, as the strategy likely caused it. + + if exc.errno == 104: + if args.get("injection_censor"): + fitness -= 110 + else: + fitness -= 90 + logger.debug("Client: Connection RST.") + else: + fitness -= 100 + logger.exception("Socket error caught in client smtp test.") + except Exception: + logger.exception("Exception caught in client smtp test.") + fitness = -120 + finally: + logger.debug("Client finished smtp test.") + return fitness * 4 diff --git a/plugins/smtp/server.py b/plugins/smtp/server.py new file mode 100644 index 0000000..ad6458b --- /dev/null +++ b/plugins/smtp/server.py @@ -0,0 +1,116 @@ +import argparse +import os +import socket +import subprocess +import time + +from plugins.plugin_server import ServerPlugin + +BASEPATH = os.path.dirname(os.path.abspath(__file__)) + + +class SMTPServer(ServerPlugin): + """ + Defines the SMTP client. + """ + name = "smtp" + def __init__(self, args): + """ + Initializes the SMTP client. + """ + ServerPlugin.__init__(self) + self.args = args + if args: + self.port = args["port"] + + @staticmethod + def get_args(command): + """ + Defines arguments for this plugin + """ + super_args = ServerPlugin.get_args(command) + + parser = argparse.ArgumentParser(description='SMTP Server') + + parser.add_argument('--smtp-request', action='store_true', help='Send an SMTP byte string that triggers censorship') + + args, _ = parser.parse_known_args(command) + args = vars(args) + super_args.update(args) + return super_args + + def run(self, args, logger): + """ + Initializes the SMTP server. + """ + logger.debug("SMTP server initializing") + try: + port = int(args["port"]) + control_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + # Allow socket re-use + control_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + server_address = ('0.0.0.0', port) + logger.debug("Binding to server address 0.0.0.0:%d" % port) + control_socket.bind(server_address) + control_socket.settimeout(5) + control_socket.listen(1) + except: + logger.exception("Caught exception in smtp run") + return + + try: + connection, client_address = self.get_request(control_socket) + if not connection: + logger.error("Failed to get connection") + return + + bad_word = args["bad_word"] + expected_msg = bad_word + + if args.get('smtp_request'): + expected_msg = b'MAIL FROM: xiazai@upup.info \r\n' + + if type(expected_msg) == str: + expected_msg = expected_msg.encode() + + # SMTP data back and forth + tries = args.get("tries", 1) + for i in range(0, int(tries)): + msg = b'' + while msg != expected_msg: + msg += connection.recv(1024) + time.sleep(1) + connection.sendall(b'k') + + time.sleep(1) + connection.close() + except socket.error as e: + if e.errno == 104: + logger.debug("Server: Connection RST.") + else: + logger.debug("Server: Client quit.") + except socket.ConnectionResetError: + logger.debug("Server: Connection RST.") + except Exception: + logger.exception("Failed during server communication.") + finally: + logger.debug("Server exiting") + + def get_request(self, control_socket): + """ + Get a request from the socket. + """ + while True: + try: + sock, addr = control_socket.accept() + sock.settimeout(5) + return (sock, addr) + except socket.timeout: + pass + return (None, None) + + def stop(self): + """ + Stops this server. + """ + ServerPlugin.stop(self)