Example #1
0
    def test_eof_received(self, tls_controller, client):
        # I don't like this. It's too intimately involved with the innards of the SMTP
        # class. But for the life of me, I can't figure out why coverage there fail
        # intermittently.
        #
        # I suspect it's a race condition, but with what, and how to prevent that from
        # happening, that's ... a mystery.

        # Entering portion of code where hang is possible (upon assertion fail), so
        # we must wrap with "try..finally".
        try:
            code, mesg = client.ehlo("example.com")
            assert code == 250
            resp = client.starttls()
            assert resp == S.S220_READY_TLS
            # Need this to make SMTP update its internal session variable
            code, mesg = client.ehlo("example.com")
            assert code == 250
            sess: Sess_ = tls_controller.smtpd.session
            assert sess.ssl is not None
            client.noop()
            catchup_delay()
            handler: EOFingHandler = tls_controller.handler
            assert handler.ssl_existed is True
            assert handler.result is False
        finally:
            tls_controller.stop()
Example #2
0
 def test_inet_loopstop(self, autostop_loop, runner):
     """
     Verify behavior when the loop is stopped before controller is stopped
     """
     autostop_loop.set_debug(True)
     cont = UnthreadedController(Sink(), loop=autostop_loop)
     cont.begin()
     # Make sure event loop is not running (will be started in thread)
     assert autostop_loop.is_running() is False
     runner(autostop_loop)
     # Make sure event loop is up and running (started within thread)
     assert autostop_loop.is_running() is True
     # Check we can connect
     with SMTPClient(cont.hostname, cont.port,
                     timeout=AUTOSTOP_DELAY) as client:
         code, _ = client.helo("example.org")
         assert code == 250
     # Wait until thread ends, which it will be when the loop autostops
     runner.join(timeout=AUTOSTOP_DELAY)
     assert runner.is_alive() is False
     catchup_delay()
     assert autostop_loop.is_running() is False
     # At this point, the loop _has_ stopped, but the task is still listening,
     # so rather than socket.timeout, we'll get a refusal instead, thus causing
     # SMTPServerDisconnected
     with pytest.raises(SMTPServerDisconnected):
         SMTPClient(cont.hostname, cont.port, timeout=0.1)
     cont.end()
     catchup_delay()
     cont.ended.wait()
     # Now the listener has gone away, and thus we will end up with socket.timeout
     # or ConnectionError (depending on OS)
     # noinspection PyTypeChecker
     with pytest.raises((socket.timeout, ConnectionError)):
         SMTPClient(cont.hostname, cont.port, timeout=0.1)
Example #3
0
 def test_unixsocket(self, safe_socket_dir, autostop_loop, runner):
     sockfile = safe_socket_dir / "smtp"
     cont = UnixSocketUnthreadedController(Sink(),
                                           unix_socket=sockfile,
                                           loop=autostop_loop)
     cont.begin()
     # Make sure event loop is not running (will be started in thread)
     assert autostop_loop.is_running() is False
     runner(autostop_loop)
     # Make sure event loop is up and running (started within thread)
     assert autostop_loop.is_running() is True
     # Check we can connect
     assert_smtp_socket(cont)
     # Wait until thread ends, which it will be when the loop autostops
     runner.join(timeout=AUTOSTOP_DELAY)
     assert runner.is_alive() is False
     catchup_delay()
     assert autostop_loop.is_running() is False
     # At this point, the loop _has_ stopped, but the task is still listening
     assert assert_smtp_socket(cont) is False
     # Stop the task
     cont.end()
     catchup_delay()
     # Now the listener has gone away
     # noinspection PyTypeChecker
     with pytest.raises((socket.timeout, ConnectionError)):
         assert_smtp_socket(cont)
Example #4
0
 def test_smtps(self, temp_event_loop):
     with watcher_process(watch_for_smtps) as retq:
         temp_event_loop.call_later(AUTOSTOP_DELAY, temp_event_loop.stop)
         main_n("--smtpscert", str(SERVER_CRT), "--smtpskey",
                str(SERVER_KEY))
         catchup_delay()
     has_smtps = retq.get()
     assert has_smtps is True
Example #5
0
 def test_normal_situation(self):
     cont = Controller(Sink())
     try:
         cont.start()
         catchup_delay()
         assert cont.smtpd is not None
         assert cont._thread_exception is None
     finally:
         cont.stop()
Example #6
0
 def test_tls_handshake_failing(self, tls_controller, client):
     handler = tls_controller.handler
     assert isinstance(handler, ExceptionCaptureHandler)
     try:
         client.ehlo("example.com")
         code, response = client.docmd("STARTTLS")
         with pytest.raises(SMTPServerDisconnected):
             client.docmd("SOMEFAILINGHANDSHAKE")
         catchup_delay()
         assert isinstance(handler.error, TLSSetupException)
     finally:
         tls_controller.stop()
Example #7
0
 def test_server_creation_ssl(self, safe_socket_dir, ssl_context_server):
     sockfile = safe_socket_dir / "smtp"
     cont = UnixSocketController(Sink(),
                                 unix_socket=sockfile,
                                 ssl_context=ssl_context_server)
     try:
         cont.start()
         # Allow additional time for SSL to kick in
         catchup_delay()
         assert_smtp_socket(cont)
     finally:
         cont.stop()
Example #8
0
 def test_tls_noreq(self, temp_event_loop):
     with watcher_process(watch_for_tls) as retq:
         temp_event_loop.call_later(AUTOSTOP_DELAY, temp_event_loop.stop)
         main_n(
             "--tlscert",
             str(SERVER_CRT),
             "--tlskey",
             str(SERVER_KEY),
             "--no-requiretls",
         )
         catchup_delay()
     has_starttls = retq.get()
     assert has_starttls is True
     require_tls = retq.get()
     assert require_tls is False
Example #9
0
 def test_inet_contstop(self, temp_event_loop, runner):
     """
     Verify behavior when the controller is stopped before loop is stopped
     """
     cont = UnthreadedController(Sink(), loop=temp_event_loop)
     cont.begin()
     # Make sure event loop is not running (will be started in thread)
     assert temp_event_loop.is_running() is False
     runner(temp_event_loop)
     # Make sure event loop is up and running
     assert temp_event_loop.is_running() is True
     try:
         # Check that we can connect
         with SMTPClient(cont.hostname, cont.port,
                         timeout=AUTOSTOP_DELAY) as client:
             code, _ = client.helo("example.org")
             assert code == 250
             client.quit()
         catchup_delay()
         temp_event_loop.call_soon_threadsafe(cont.end)
         for _ in range(10):  # 10 is arbitrary
             catchup_delay(
             )  # effectively yield to other threads/event loop
             if cont.ended.wait(1.0):
                 break
         assert temp_event_loop.is_running() is True
         # Because we've called .end() there, the server listener should've gone
         # away, so we should end up with a socket.timeout or ConnectionError or
         # SMTPServerDisconnected (depending on lotsa factors)
         expect_errs = (socket.timeout, ConnectionError,
                        SMTPServerDisconnected)
         # noinspection PyTypeChecker
         with pytest.raises(expect_errs):
             SMTPClient(cont.hostname, cont.port, timeout=0.1)
     finally:
         # Wrap up, or else we'll hang
         temp_event_loop.call_soon_threadsafe(cont.cancel_tasks)
         catchup_delay()
         runner.join()
     assert runner.is_alive() is False
     assert temp_event_loop.is_running() is False
     assert temp_event_loop.is_closed() is False
Example #10
0
def assert_smtp_socket(controller: UnixSocketMixin) -> bool:
    assert Path(controller.unix_socket).exists()
    sockfile = controller.unix_socket
    ssl_context = controller.ssl_context
    with ExitStack() as stk:
        sock: socket.socket = stk.enter_context(
            socket.socket(socket.AF_UNIX, socket.SOCK_STREAM))
        sock.settimeout(AUTOSTOP_DELAY)
        sock.connect(str(sockfile))
        if ssl_context:
            sock = stk.enter_context(ssl_context.wrap_socket(sock))
        catchup_delay()
        try:
            resp = sock.recv(1024)
        except socket.timeout:
            return False
        if not resp:
            return False
        assert resp.startswith(b"220 ")
        assert resp.endswith(b"\r\n")
        sock.send(b"EHLO socket.test\r\n")
        # We need to "build" resparr because, especially when socket is wrapped
        # in SSL, the SMTP server takes it sweet time responding with the list
        # of ESMTP features ...
        resparr = bytearray()
        while not resparr.endswith(b"250 HELP\r\n"):
            catchup_delay()
            resp = sock.recv(1024)
            if not resp:
                break
            resparr += resp
        assert resparr.endswith(b"250 HELP\r\n")
        sock.send(b"QUIT\r\n")
        catchup_delay()
        resp = sock.recv(1024)
        assert resp.startswith(b"221")
    return True
Example #11
0
 def starter(loop: asyncio.AbstractEventLoop):
     nonlocal thread
     thread = Thread(target=_runner, args=(loop, ))
     thread.setDaemon(True)
     thread.start()
     catchup_delay()