def test_tls_over_1023_available(self, mock_getuid): cfile = create_config(('tls_port=1024',)) conf = Config(cfile).load() with mock.patch('socket.socket.bind', return_value=True): conf.test_tls_port() assert mock_getuid.called is True assert mock_getuid.call_count is 1
def test_ipv6_disabled(self): cfile = create_config(("tls_listen=:::465", )) conf = Config(cfile).load() conf._tls_listen = [("::", 465, socket.AF_UNSPEC, {})] with pytest.raises(ConfigException), mock.patch( "socket.has_ipv6", False): conf.test_tls_ipv6_support()
def test_pidfile_with_permission(self): cfile = create_config(("pidfile=/tmp/path.pid", )) conf = Config(cfile).load() with mock.patch("os.path.exists", return_value=True), mock.patch("os.access", return_value=True): conf.test_pidfile()
def test_pidfile_no_permission(self): cfile = create_config(("pidfile=/fake/path.pid",)) conf = Config(cfile).load() with mock.patch("os.access", return_value=False), pytest.raises( ConfigException ): conf.test_pidfile()
async def test_mode_and_delay_directive(event_loop, unused_tcp_port): cfile = create_config( ("listen=:{} delay=5 mode=bounce".format(unused_tcp_port),) ) conf = Config(cfile).load() sock = _socket("127.0.0.1", unused_tcp_port, socket.AF_INET) controller = Controller(sock) controller.start() conf.flags_from_listener("127.0.0.1", unused_tcp_port) host, port = sock.getsockname() with SMTP(host, port) as client: msg = [ "From: [email protected]", "To: [email protected]", "Subject: Test", "X-Blackhole-Mode: accept", "X-Blackhole-Delay: 30", "", "Testing 1, 2, 3", ] msg = "\n".join(msg) start = time.time() code, resp = client.data(msg.encode("utf-8")) stop = time.time() assert code in [450, 451, 452, 458, 521, 550, 551, 552, 553, 571] assert round(stop - start) in (4, 5, 6) controller.stop()
def test_pidfile_with_permission(self): cfile = create_config(("pidfile=/tmp/path.pid",)) conf = Config(cfile).load() with mock.patch("os.path.exists", return_value=True), mock.patch( "os.access", return_value=True ): conf.test_pidfile()
def test_no_access(mock_os_access): conf = Config() conf.config_file = '/fake/file.conf' with pytest.raises(ConfigException): conf.load() assert mock_os_access.called is True assert mock_os_access.call_count is 1
def test_same_port_tls_port(self): cfile = create_config( ("listen=127.0.0.1:25", "tls_listen=127.0.0.1:25") ) conf = Config(cfile).load() with pytest.raises(ConfigException): conf.test_tls_port()
def test_ipv6_disabled(self): cfile = create_config(('tls_listen=:::465', )) conf = Config(cfile).load() conf._tls_listen = [('::', 465, socket.AF_UNSPEC, {})] with pytest.raises(ConfigException), \ mock.patch('socket.has_ipv6', False): conf.test_tls_ipv6_support()
def test_tls_under_1024_no_perms(self, mock_getuid): cfile = create_config(('tls_port=1023',)) conf = Config(cfile).load() with pytest.raises(ConfigException): conf.test_tls_port() assert mock_getuid.called is True assert mock_getuid.call_count is 1
def test_ipv6_disabled(self): cfile = create_config(("listen=:::25",)) conf = Config(cfile).load() conf._listen = [("::", 25, socket.AF_UNSPEC, {})] with pytest.raises(ConfigException), mock.patch( "socket.has_ipv6", False ): conf.test_ipv6_support()
def test_initiation(): cfile = create_config(('', )) with mock.patch('os.access', return_value=False), \ mock.patch('socket.getfqdn', return_value='a.blackhole.io'): conf = Config(cfile) conf.load() smtp = Smtp([]) assert smtp.fqdn == 'a.blackhole.io'
def test_tls_over_1023_available(self): cfile = create_config(('tls_listen=127.0.0.1:1024', )) conf = Config(cfile).load() with mock.patch('os.getuid', return_value=9000) as mock_getuid, \ mock.patch('socket.socket.bind', return_value=True): conf.test_tls_port() assert mock_getuid.called is True assert mock_getuid.call_count is 1
def test_tls_under_1024_no_perms(self): cfile = create_config(('tls_listen=127.0.0.1:1023', )) conf = Config(cfile).load() with mock.patch('os.getuid', return_value=9000) as mock_getuid, \ pytest.raises(ConfigException): conf.test_tls_port() assert mock_getuid.called is True assert mock_getuid.call_count is 1
def test_no_access(): conf = Config() conf.config_file = '/fake/file.conf' with mock.patch('os.access', return_value=False) as mock_os_access, \ pytest.raises(ConfigException): conf.load() assert mock_os_access.called is True assert mock_os_access.call_count is 1
def test_tls_over_1023_available(self): cfile = create_config(("tls_listen=127.0.0.1:1024", )) conf = Config(cfile).load() with mock.patch("os.getuid", return_value=9000) as mock_getuid, mock.patch( "socket.socket.bind", return_value=True): conf.test_tls_port() assert mock_getuid.called is True assert mock_getuid.call_count is 1
def test_initiation(): cfile = create_config(("",)) with mock.patch("os.access", return_value=False), mock.patch( "socket.getfqdn", return_value="a.blackhole.io" ): conf = Config(cfile) conf.load() smtp = Smtp([]) assert smtp.fqdn == "a.blackhole.io"
def test_tls_under_1024_no_perms(self): cfile = create_config(("tls_listen=127.0.0.1:1023", )) conf = Config(cfile).load() with mock.patch("os.getuid", return_value=9000) as mock_getuid, pytest.raises( ConfigException): conf.test_tls_port() assert mock_getuid.called is True assert mock_getuid.call_count is 1
def test_tls_under_1024_with_perms_available(self): cfile = create_config(("tls_listen=127.0.0.1:1024",)) conf = Config(cfile).load() with mock.patch( "os.getuid", return_value=0 ) as mock_getuid, mock.patch("socket.socket.bind", return_value=True): conf.test_tls_port() assert mock_getuid.called is True assert mock_getuid.call_count is 1
def test_dynamic_switch_invalid(self): cfile = create_config(("dynamic_switch=abc",)) with pytest.raises(ConfigException): conf = Config(cfile).load() cfile = create_config(("",)) conf = Config(cfile).load() conf._dynamic_switch = "abc" with pytest.raises(ConfigException): conf.test_dynamic_switch()
def test_port_no_certkey(self): settings = ("tls_listen=127.0.0.1:123",) cfile = create_config(settings) conf = Config(cfile).load() with pytest.raises(ConfigException): conf.test_tls_settings() assert conf.tls_listen == [("127.0.0.1", 123, socket.AF_INET, {})] assert conf.tls_cert is None assert conf.tls_key is None
def test_tls_over_1023_unavailable(self, mock_getuid, mock_socket): cfile = create_config(('tls_port=1024',)) conf = Config(cfile).load() with pytest.raises(ConfigException): conf.test_tls_port() assert mock_getuid.called is True assert mock_getuid.call_count is 1 assert mock_socket.called is True assert mock_socket.call_count is 1
def test_no_access(): conf = Config() conf.config_file = "/fake/file.conf" with mock.patch("os.access", return_value=False) as mock_os_access, pytest.raises( ConfigException): conf.load() assert mock_os_access.called is True assert mock_os_access.call_count is 1
def test_port_no_certkey(self): settings = ('tls_port=123',) cfile = create_config(settings) conf = Config(cfile).load() with pytest.raises(ConfigException): conf.test_tls_settings() assert conf.tls_port == 123 assert conf.tls_cert is None assert conf.tls_key is None
def test_port_no_certkey(self): settings = ("tls_listen=127.0.0.1:123", ) cfile = create_config(settings) conf = Config(cfile).load() with pytest.raises(ConfigException): conf.test_tls_settings() assert conf.tls_listen == [("127.0.0.1", 123, socket.AF_INET, {})] assert conf.tls_cert is None assert conf.tls_key is None
def test_cert_no_port_key(self): cert = create_file("crt.crt") settings = ("tls_cert={}".format(cert), ) cfile = create_config(settings) conf = Config(cfile).load() with pytest.raises(ConfigException): conf.test_tls_settings() assert conf.tls_listen == [] assert conf.tls_cert == cert assert conf.tls_key is None
def test_port_key_no_cert(self): key = create_file("key.key") settings = ("tls_listen=127.0.0.1:123", "tls_key={}".format(key)) cfile = create_config(settings) conf = Config(cfile).load() with pytest.raises(ConfigException): conf.test_tls_settings() assert conf.tls_listen == [("127.0.0.1", 123, socket.AF_INET, {})] assert conf.tls_cert is None assert conf.tls_key == key
def test_port_key_no_cert(self): key = self.create_file('key.key') settings = ('tls_port=123', 'tls_key={}'.format(key)) cfile = create_config(settings) conf = Config(cfile).load() with pytest.raises(ConfigException): conf.test_tls_settings() assert conf.tls_port == 123 assert conf.tls_cert is None assert conf.tls_key == key
def test_port_cert_no_key(self): cert = create_file("crt.crt") settings = ("tls_listen=127.0.0.1:123", "tls_cert={}".format(cert)) cfile = create_config(settings) conf = Config(cfile).load() with pytest.raises(ConfigException): conf.test_tls_settings() assert conf.tls_listen == [("127.0.0.1", 123, socket.AF_INET, {})] assert conf.tls_cert == cert assert conf.tls_key is None
def test_port_cert_no_key(self): cert = self.create_file('crt.crt') settings = ('tls_port=123', 'tls_cert={}'.format(cert),) cfile = create_config(settings) conf = Config(cfile).load() with pytest.raises(ConfigException): conf.test_tls_settings() assert conf.tls_port == 123 assert conf.tls_cert == cert assert conf.tls_key is None
def test_key_no_port_cert(self): key = create_file("key.key") settings = ("tls_key={}".format(key), ) cfile = create_config(settings) conf = Config(cfile).load() with pytest.raises(ConfigException): conf.test_tls_settings() assert conf.tls_listen == [] assert conf.tls_cert is None assert conf.tls_key == key
def test_cert_no_port_key(self): cert = create_file("crt.crt") settings = ("tls_cert={}".format(cert),) cfile = create_config(settings) conf = Config(cfile).load() with pytest.raises(ConfigException): conf.test_tls_settings() assert conf.tls_listen == [] assert conf.tls_cert == cert assert conf.tls_key is None
def test_key_no_port_cert(self): key = create_file("key.key") settings = ("tls_key={}".format(key),) cfile = create_config(settings) conf = Config(cfile).load() with pytest.raises(ConfigException): conf.test_tls_settings() assert conf.tls_listen == [] assert conf.tls_cert is None assert conf.tls_key == key
def test_tls_context_less_secure(): tls_cert = create_file('cert.cert') tls_key = create_file('key.key') cfile = create_config(('listen=127.0.0.1:25', 'tls_listen=127.0.0.1:9000', 'tls_cert={}'.format(tls_cert), 'tls_key={}'.format(tls_key), )) conf = Config(cfile).load() conf.args = Args((('less_secure', True), )) with mock.patch('ssl.SSLContext.load_cert_chain'): _context(use_tls=True)
def test_port_cert_key(self): key = self.create_file('key.key') cert = self.create_file('crt.crt') settings = ('tls_port=123', 'tls_cert={}'.format(cert), 'tls_key={}'.format(key)) cfile = create_config(settings) conf = Config(cfile).load() conf.test_tls_settings() assert conf.tls_port == 123 assert conf.tls_cert == cert assert conf.tls_key == key
def test_port_cert_key(self): key = create_file('key.key') cert = create_file('crt.crt') settings = ('tls_listen=127.0.0.1:123', 'tls_cert={}'.format(cert), 'tls_key={}'.format(key)) cfile = create_config(settings) conf = Config(cfile).load() conf.test_tls_settings() assert conf.tls_listen == [('127.0.0.1', 123, socket.AF_INET, {})] assert conf.tls_cert == cert assert conf.tls_key == key
def _run(self, ready_event): asyncio.set_event_loop(self.loop) conf = Config(None) conf.mailname = "blackhole.io" _server = self.loop.create_server(lambda: Smtp([]), sock=self.sock) self.server = self.loop.run_until_complete(_server) self.loop.call_soon(ready_event.set) self.loop.run_forever() self.server.close() self.loop.run_until_complete(self.server.wait_closed()) self.loop.close() self.server = None
def test_create_server_tls_ipv6_bind_works(): cfile = create_config(('listen=:::25', 'tls_listen=:::9000', )) conf = Config(cfile).load() conf.args = Args((('less_secure', False), )) with mock.patch('socket.socket.bind') as mock_sock, \ mock.patch('ssl.create_default_context') as mock_ssl: server('::', 9000, socket.AF_INET6, {}, use_tls=True) assert mock_sock.called is True assert mock_sock.call_count is 1 assert mock_ssl.called is True assert mock_ssl.call_count is 1
def test_create_server_tls_ipv6_bind_works(): cfile = create_config(("listen=:::25", "tls_listen=:::9000")) conf = Config(cfile).load() conf.args = Args((("less_secure", False),)) with mock.patch("socket.socket.bind") as mock_sock, mock.patch( "ssl.create_default_context" ) as mock_ssl: _server = server("::", 9000, socket.AF_INET6, use_tls=True) _server["sock"].close() assert mock_sock.called is True assert mock_sock.call_count is 1 assert mock_ssl.called is True assert mock_ssl.call_count is 1
def test_tls_over_1023_unavailable(self): cfile = create_config(("tls_listen=127.0.0.1:1024", )) conf = Config(cfile).load() with mock.patch("os.getuid", return_value=9000) as mock_getuid, mock.patch( "socket.socket.bind", side_effect=OSError( 1, "none")) as mock_socket, pytest.raises( ConfigException): conf.test_tls_port() assert mock_getuid.called is True assert mock_getuid.call_count is 1 assert mock_socket.called is True assert mock_socket.call_count is 1
def test_port_cert_key(self): key = create_file("key.key") cert = create_file("crt.crt") settings = ( "tls_listen=127.0.0.1:123", "tls_cert={}".format(cert), "tls_key={}".format(key), ) cfile = create_config(settings) conf = Config(cfile).load() conf.test_tls_settings() assert conf.tls_listen == [("127.0.0.1", 123, socket.AF_INET, {})] assert conf.tls_cert == cert assert conf.tls_key == key
def test_tls_context_less_secure(): tls_cert = create_file("cert.cert") tls_key = create_file("key.key") cfile = create_config( ( "listen=127.0.0.1:25", "tls_listen=127.0.0.1:9000", "tls_cert={}".format(tls_cert), "tls_key={}".format(tls_key), ) ) conf = Config(cfile).load() conf.args = Args((("less_secure", True),)) with mock.patch("ssl.SSLContext.load_cert_chain"): _context(use_tls=True)
def test_ipv4_and_ipv6_diff_port(self): cfile = create_config(("tls_listen=127.0.0.1:9000,:::9001", )) conf = Config(cfile).load() assert conf.tls_listen == [ ("127.0.0.1", 9000, socket.AF_INET, {}), ("::", 9001, socket.AF_INET6, {}), ]
def test_create_server_ipv4_bind_works(mock_sock): cfile = create_config(("listen=127.0.0.1:9000",)) Config(cfile).load() _server = server("127.0.0.1", 9000, socket.AF_INET) assert mock_sock.called is True assert mock_sock.call_count is 1 _server["sock"].close()
def setUp(self): cfile = create_config(("timeout=5", "max_message_size=1024")) Config(cfile).load() controller = Controller() controller.start() self.host, self.port = controller.sock.getsockname() self.addCleanup(controller.stop)
def test_mode_and_delay_range_flag(self): cfile = create_config(("listen=:25 delay=15-20 mode=bounce", )) conf = Config(cfile).load() assert conf.listen == [("", 25, socket.AF_INET, { "delay": ("15", "20"), "mode": "bounce" })]
def test_start_servers(mock_bind): cfile = create_config(('port=9000', )) Config(cfile).load() start_servers() assert len(blackhole.control._servers) is 1 assert mock_bind.called is True assert mock_bind.call_count is 1
def start_servers(): """Create each server listener and bind to the socket.""" config = Config() logger.debug('Starting...') create_server() if config.tls_port and config.tls_cert and config.tls_key: create_server(use_tls=True)
def run(): """Create the asyncio loop and start the server.""" args = parse_cmd_args(sys.argv[1:]) configure_logs(args) logger = logging.getLogger('blackhole') if args.test: config_test(args) try: config = Config(args.config_file).load().test() except ConfigException as err: logger.fatal(err) raise SystemExit(os.EX_USAGE) if args.background and not config.pidfile: logger.fatal('Cannot run in the background without a pidfile.') raise SystemExit(os.EX_USAGE) loop = asyncio.get_event_loop() loop.add_signal_handler(signal.SIGINT, loop.stop) start_servers() setgid() setuid() if args.background: try: Daemon(config.pidfile).daemonize() except DaemonException as err: stop_servers() logger.fatal(err) raise SystemExit(os.EX_USAGE) try: loop.run_forever() except KeyboardInterrupt: pass stop_servers() raise SystemExit(os.EX_OK)
def test_setuid_no_perms(): cfile = create_config(("user=testuser",)) with mock.patch( "pwd.getpwnam", side_effect=PermissionError ), pytest.raises(SystemExit) as err: Config(cfile).load() setuid() assert err.value.code == 77
def test_setuid_invalid_user(): cfile = create_config(("user=testuser",)) with mock.patch("pwd.getpwnam", side_effect=KeyError), pytest.raises( SystemExit ) as exc: Config(cfile).load() setuid() assert exc.value.code == 64
def test_setgid_no_perms(): cfile = create_config(("group=testgroup",)) with mock.patch( "grp.getgrnam", side_effect=PermissionError ), pytest.raises(SystemExit) as exc: Config(cfile).load() setgid() assert exc.value.code == 77
def test_create_server_ipv6_bind_works(): cfile = create_config(("listen=:::9000",)) Config(cfile).load() with mock.patch("socket.socket.bind") as mock_sock: _server = server("::", 9000, socket.AF_INET6) _server["sock"].close() assert mock_sock.called is True assert mock_sock.call_count is 1
def test_create_server_bind_works(mock_sock): assert len(blackhole.control._servers) is 0 cfile = create_config(('port=9000', )) Config(cfile).load() create_server() assert len(blackhole.control._servers) is 1 assert mock_sock.called is True assert mock_sock.call_count is 1
def test_load(): cfile = create_config(( "#not=thisline", "listen=10.0.0.1:1025", "mode=bounce #default accept", )) conf = Config(cfile).load() assert conf.listen == [("10.0.0.1", 1025, socket.AF_INET, {})] assert conf.tls_listen == [] cfile = create_config(( "#not=thisline", "listen=10.0.0.1:1025", """this won't be added""", "mode=bounce #default accept", )) with pytest.raises(ConfigException): Config(cfile).load()