예제 #1
0
    def __init__(self, host, port, proxy=None):
        """
        :param proxy: (dict) Default: None; Set this one up if you wish to use
                a proxy to hit your target host. E.g.:
                {
                    "server":"someproxy.com",
                    "port":6000,
                    "user":"******",
                    "pass":"******"
                }
        """
        self.host = host
        self.port = port
        self.policies = {
            "ssl2.0": {
                "allowed": False,
                "command": Sslv20ScanCommand()
            },
            "ssl3.0": {
                "allowed": False,
                "command": Sslv30ScanCommand()
            },
            "tls1.0": {
                "allowed": False,
                "command": Tlsv10ScanCommand()
            },
            "tls1.1": {
                "allowed": False,
                "command": Tlsv11ScanCommand()
            },
            "tls1.2": {
                "allowed": True,
                "command": Tlsv12ScanCommand()
            },
            "tls1.3": {
                "allowed": True,
                "command": Tlsv13ScanCommand()
            },
        }

        if not isinstance(host, str) or not isinstance(port, int):
            raise TypeError('EncryptionCheck class not properly initialized.')

        # Proxy setup
        if proxy is not None:
            if not ("server" in proxy and "port" in proxy):
                raise ValueError('Invalid proxy settings detected.')
            proxy_server = proxy['server']
            proxy_port = proxy['port']
            proxy_user = proxy.get('user', None)
            proxy_pass = proxy.get('pass', None)
            tunnel_settings = HttpConnectTunnelingSettings(
                proxy_server,
                proxy_port,
                basic_auth_user=proxy_user,
                basic_auth_password=proxy_pass)
        else:
            tunnel_settings = None
        self.tunnel = tunnel_settings
        self.proxy = proxy
예제 #2
0
    def test_https_tunneling(self):
        # Start a local proxy
        proxy_port = 8000
        p = multiprocessing.Process(target=proxy_worker, args=(proxy_port, ))
        p.start()

        try:
            # Run a scan through the proxy
            tunnel_settings = HttpConnectTunnelingSettings(
                'localhost', proxy_port)
            server_info = ServerConnectivityInfo(
                hostname='www.google.com',
                http_tunneling_settings=tunnel_settings)

            # Try to connect to the proxy - retry if the proxy subprocess wasn't ready
            proxy_connection_attempts = 0
            while True:
                try:
                    server_info.test_connectivity_to_server()
                    break
                except ServerConnectivityError:
                    if proxy_connection_attempts > 3:
                        raise
                    proxy_connection_attempts += 1

            plugin = CertificateInfoPlugin()
            plugin_result = plugin.process_task(server_info, 'certinfo_basic')

            self.assertTrue(plugin_result.certificate_chain)

            self.assertTrue(plugin_result.as_text())
            self.assertTrue(plugin_result.as_xml())
        finally:
            # Kill the local proxy - unclean
            p.terminate()
예제 #3
0
 def test_https_tunneling_bad_arguments(self):
     # Ensure that an IP address cannot be specified when using an HTTP proxy for scans
     tunnel_settings = HttpConnectTunnelingSettings('fakedomain', 443)
     with self.assertRaisesRegexp(
             ValueError,
             'Cannot specify both ip_address and http_tunneling_settings'):
         ServerConnectivityInfo(hostname='www.google.com',
                                ip_address='1.2.3.4',
                                http_tunneling_settings=tunnel_settings)
예제 #4
0
 def test_https_tunneling_bad_arguments(self):
     # Ensure that an IP address cannot be specified when using an HTTP proxy for scans
     tunnel_settings = HttpConnectTunnelingSettings('fakedomain', 443)
     with pytest.raises(ValueError):
         ServerConnectivityTester(
             hostname='www.google.com',
             ip_address='1.2.3.4',
             http_tunneling_settings=tunnel_settings
         )
예제 #5
0
    def test_https_tunneling(self):
        # Start a local proxy
        proxy_port = 8000
        p = multiprocessing.Process(target=proxy_worker, args=(proxy_port, ))
        p.start()

        # On Travis CI, the server sometimes is still not ready to accept connections when we get here
        # Wait a bit more to make the test suite less flaky
        time.sleep(0.5)

        try:
            # Run a scan through the proxy
            tunnel_settings = HttpConnectTunnelingSettings(
                'localhost',
                proxy_port,
                basic_auth_user='******',
                basic_auth_password='******')
            server_info = ServerConnectivityInfo(
                hostname='www.google.com',
                http_tunneling_settings=tunnel_settings)

            # Try to connect to the proxy - retry if the proxy subprocess wasn't ready
            proxy_connection_attempts = 0
            while True:
                try:
                    server_info.test_connectivity_to_server()
                    break
                except ServerConnectivityError:
                    if proxy_connection_attempts > 3:
                        raise
                    proxy_connection_attempts += 1

            plugin = CertificateInfoPlugin()
            plugin_result = plugin.process_task(server_info,
                                                CertificateInfoScanCommand())

            self.assertGreaterEqual(len(plugin_result.certificate_chain), 1)

            self.assertTrue(plugin_result.as_text())
            self.assertTrue(plugin_result.as_xml())
        finally:
            # Kill the local proxy - unclean
            p.terminate()
예제 #6
0
    def test_with_http_tunneling(self):
        output_file = StringIO()
        generator = XmlOutputGenerator(output_file)

        # When scanning through a proxy, we do not know the final server's IP address
        # This makes sure the XML output properly handles that
        tunneling_settings = HttpConnectTunnelingSettings('prôxyé.com', 3128)
        server_info = MockServerConnectivityInfo(http_tunneling_settings=tunneling_settings)

        # noinspection PyTypeChecker
        server_scan = CompletedServerScan(server_info, [])
        generator.server_scan_completed(server_scan)
        generator.scans_completed(1.3)

        received_output = output_file.getvalue()
        output_file.close()

        # Ensure the output displayed the tunneling settings
        self.assertIn('httpsTunnelHostname="{}"'.format(tunneling_settings.hostname), received_output)
        self.assertIn('httpsTunnelPort="{}"'.format(tunneling_settings.port), received_output)
예제 #7
0
    def test_server_connectivity_test_succeeded_with_http_tunneling(self):
        output_file = StringIO()
        generator = ConsoleOutputGenerator(output_file)

        # When scanning through a proxy, we do not know the final server's IP address
        # This makes sure the console output properly handles that
        tunneling_settings = HttpConnectTunnelingSettings('ûnicôdé.com', 3128)
        server_info = MockServerConnectivityInfo(http_tunneling_settings=tunneling_settings)

        generator.server_connectivity_test_succeeded(server_info)

        received_output = output_file.getvalue()
        output_file.close()

        # Ensure the console output properly listed the online domain and that it was going through a proxy
        assert server_info.hostname in received_output
        assert str(server_info.port) in received_output
        assert 'Proxy' in received_output
        assert tunneling_settings.hostname in received_output
        assert str(tunneling_settings.port) in received_output
    def test_server_scan_completed_with_http_tunneling(self):
        output_file = StringIO()
        generator = ConsoleOutputGenerator(output_file)

        # When scanning through a proxy, we do not know the final server's IP address
        # This makes sure the console output properly handles that
        tunneling_settings = HttpConnectTunnelingSettings(u'ûnicôdé.com', 3128)
        server_info = MockServerConnectivityInfo(
            http_tunneling_settings=tunneling_settings)

        server_scan = CompletedServerScan(server_info, [])
        generator.server_scan_completed(server_scan)

        received_output = output_file.getvalue()
        output_file.close()

        # Ensure the console output properly listed the online domain and that it was going through a proxy
        self.assertIn(server_info.hostname, received_output.lower())
        self.assertIn(str(server_info.port), received_output.lower())
        self.assertIn('proxy', received_output.lower())
        self.assertIn(tunneling_settings.hostname, received_output.lower())
        self.assertIn(str(tunneling_settings.port), received_output.lower())
예제 #9
0
    def parse_command_line(self):
        """Parses the command line used to launch SSLyze.
        """

        (args_command_list, args_target_list) = self._parser.parse_args()

        # Handle the --targets_in command line and fill args_target_list
        if args_command_list.targets_in:
            if args_target_list:
                raise CommandLineParsingError("Cannot use --targets_list and specify targets within the command line.")

            try:  # Read targets from a file
                with open(args_command_list.targets_in) as f:
                    for target in f.readlines():
                        if target.strip():  # Ignore empty lines
                            if not target.startswith('#'):  # Ignore comment lines
                                args_target_list.append(target.strip())
            except IOError:
                raise CommandLineParsingError("Can't read targets from input file '{}.".format(
                        args_command_list.targets_in))

        if not args_target_list:
            raise CommandLineParsingError('No targets to scan.')


        # Handle the --regular command line parameter as a shortcut
        if self._parser.has_option('--regular'):
            if getattr(args_command_list, 'regular'):
                setattr(args_command_list, 'regular', False)
                for cmd in self.REGULAR_CMD:
                    setattr(args_command_list, cmd, True)


        # Sanity checks on the command line options
        # Prevent --quiet and --xml_out -
        if args_command_list.xml_file and args_command_list.xml_file == '-' and args_command_list.quiet:
                raise CommandLineParsingError('Cannot use --quiet with --xml_out -.')

        # Prevent --quiet and --json_out -
        if args_command_list.json_file and args_command_list.json_file == '-' and args_command_list.quiet:
                raise CommandLineParsingError('Cannot use --quiet with --json_out -.')

        # Prevent --xml_out - and --json_out -
        if args_command_list.json_file and args_command_list.json_file == '-'\
                and args_command_list.xml_file and args_command_list.xml_file == '-'.quiet:
                raise CommandLineParsingError('Cannot use --xml_out - with --json_out -.')


        # Sanity checks on the client cert options
        client_auth_creds = None
        if bool(args_command_list.cert) ^ bool(args_command_list.key):
            raise CommandLineParsingError('No private key or certificate file were given. See --cert and --key.')

        elif args_command_list.cert:
            # Private key formats
            if args_command_list.keyform == 'DER':
                key_type = SSL_FILETYPE_ASN1
            elif args_command_list.keyform == 'PEM':
                key_type = SSL_FILETYPE_PEM
            else:
                raise CommandLineParsingError('--keyform should be DER or PEM.')

            # Let's try to open the cert and key files
            try:
                client_auth_creds = ClientAuthenticationCredentials(args_command_list.cert,
                                                                    args_command_list.key,
                                                                    key_type,
                                                                    args_command_list.keypass)
            except ValueError as e:
                raise CommandLineParsingError('Invalid client authentication settings: {}.'.format(e[0]))


        # HTTP CONNECT proxy
        http_tunneling_settings = None
        if args_command_list.https_tunnel:
            try:
                http_tunneling_settings = HttpConnectTunnelingSettings.from_url(args_command_list.https_tunnel)
            except ValueError as e:
                raise CommandLineParsingError('Invalid proxy URL for --https_tunnel: {}.'.format(e[0]))


        # STARTTLS
        tls_wrapped_protocol = TlsWrappedProtocolEnum.PLAIN_TLS
        if args_command_list.starttls:
            if args_command_list.starttls not in self.START_TLS_PROTOCOLS:
                raise CommandLineParsingError(self.START_TLS_USAGE)
            else:
                # StartTLS was specified
                if args_command_list.starttls in self.STARTTLS_PROTOCOL_DICT.keys():
                    # Protocol was given in the command line
                    tls_wrapped_protocol = self.STARTTLS_PROTOCOL_DICT[args_command_list.starttls]


        # Number of connection retries
        if args_command_list.nb_retries < 1:
            raise CommandLineParsingError('Cannot have a number smaller than 1 for --nb_retries.')


        # Create the server connectivity info for each specifed servers
        # A limitation when using the command line is that only one client_auth_credentials and http_tunneling_settings
        # can be specified, for all the servers to scan
        good_server_list = []
        bad_server_list = []
        for server_string in args_target_list:
            try:
                good_server_list.append(ServerConnectivityInfo.from_command_line(
                    server_string=server_string,
                    tls_wrapped_protocol=tls_wrapped_protocol,
                    tls_server_name_indication=args_command_list.sni,
                    xmpp_to_hostname=args_command_list.xmpp_to,
                    client_auth_credentials=client_auth_creds,
                    http_tunneling_settings=http_tunneling_settings)
                )
            except ServerConnectivityError as e:
                # Will happen for example if the DNS lookup failed or the server string is malformed
                bad_server_list.append((server_string, e))
            except ValueError as e:
                # Will happen for example if xmpp_to is specified for a non-XMPP connection
                raise CommandLineParsingError(e[0])


        # Command line hacks
        # Handle --starttls=auto now that we parsed the server strings
        if args_command_list.starttls == 'auto':
            for server_info in good_server_list:
                # We use the port number to deduce the protocol
                if server_info.port in self.STARTTLS_PROTOCOL_DICT.keys():
                    server_info.tls_wrapped_protocol = self.STARTTLS_PROTOCOL_DICT[server_info.port]

        # Handle --http_get now that we parsed the server strings
        # Doing it here is hacky as the option is defined within PluginOpenSSLCipherSuites
        if args_command_list.http_get:
            for server_info in good_server_list:
                if server_info.port == 443:
                    server_info.tls_wrapped_protocol = TlsWrappedProtocolEnum.HTTPS

        return good_server_list, bad_server_list, args_command_list
예제 #10
0
    def parse_command_line(
        self
    ) -> Tuple[List[ServerConnectivityTester], List[ServerStringParsingError],
               Any]:
        """Parses the command line used to launch SSLyze.
        """
        (args_command_list, args_target_list) = self._parser.parse_args()

        if args_command_list.update_trust_stores:
            # Just update the trust stores and do nothing
            TrustStoresRepository.update_default()
            raise TrustStoresUpdateCompleted()

        # Handle the --targets_in command line and fill args_target_list
        if args_command_list.targets_in:
            if args_target_list:
                raise CommandLineParsingError(
                    "Cannot use --targets_list and specify targets within the command line."
                )

            try:  # Read targets from a file
                with open(args_command_list.targets_in) as f:
                    for target in f.readlines():
                        if target.strip():  # Ignore empty lines
                            if not target.startswith(
                                    "#"):  # Ignore comment lines
                                args_target_list.append(target.strip())
            except IOError:
                raise CommandLineParsingError(
                    "Can't read targets from input file '{}.".format(
                        args_command_list.targets_in))

        if not args_target_list:
            raise CommandLineParsingError("No targets to scan.")

        # Handle the --regular command line parameter as a shortcut
        if self._parser.has_option("--regular"):
            if getattr(args_command_list, "regular"):
                setattr(args_command_list, "regular", False)
                for cmd in self.REGULAR_CMD:
                    setattr(args_command_list, cmd, True)

        # Sanity checks on the command line options
        # Prevent --quiet and --xml_out -
        if args_command_list.xml_file and args_command_list.xml_file == "-" and args_command_list.quiet:
            raise CommandLineParsingError(
                "Cannot use --quiet with --xml_out -.")

        # Prevent --quiet and --json_out -
        if args_command_list.json_file and args_command_list.json_file == "-" and args_command_list.quiet:
            raise CommandLineParsingError(
                "Cannot use --quiet with --json_out -.")

        # Prevent --xml_out - and --json_out -
        if (args_command_list.json_file and args_command_list.json_file == "-"
                and args_command_list.xml_file
                and args_command_list.xml_file == "-"):
            raise CommandLineParsingError(
                "Cannot use --xml_out - with --json_out -.")

        # Sanity checks on the client cert options
        client_auth_creds = None
        if bool(args_command_list.cert) ^ bool(args_command_list.key):
            raise CommandLineParsingError(
                "No private key or certificate file were given. See --cert and --key."
            )

        elif args_command_list.cert:
            # Private key formats
            if args_command_list.keyform == "DER":
                key_type = OpenSslFileTypeEnum.ASN1
            elif args_command_list.keyform == "PEM":
                key_type = OpenSslFileTypeEnum.PEM
            else:
                raise CommandLineParsingError(
                    "--keyform should be DER or PEM.")

            # Let's try to open the cert and key files
            try:
                client_auth_creds = ClientAuthenticationCredentials(
                    args_command_list.cert, args_command_list.key, key_type,
                    args_command_list.keypass)
            except ValueError as e:
                raise CommandLineParsingError(
                    "Invalid client authentication settings: {}.".format(
                        e.args[0]))

        # HTTP CONNECT proxy
        http_tunneling_settings = None
        if args_command_list.https_tunnel:
            try:
                http_tunneling_settings = HttpConnectTunnelingSettings.from_url(
                    args_command_list.https_tunnel)
            except ValueError as e:
                raise CommandLineParsingError(
                    "Invalid proxy URL for --https_tunnel: {}.".format(
                        e.args[0]))

        # STARTTLS
        tls_wrapped_protocol = TlsWrappedProtocolEnum.PLAIN_TLS
        if args_command_list.starttls:
            if args_command_list.starttls not in self.START_TLS_PROTOCOLS:
                raise CommandLineParsingError(self.START_TLS_USAGE)
            else:
                # StartTLS was specified
                if args_command_list.starttls in self.STARTTLS_PROTOCOL_DICT.keys(
                ):
                    # Protocol was given in the command line
                    tls_wrapped_protocol = self.STARTTLS_PROTOCOL_DICT[
                        args_command_list.starttls]

        # Create the server connectivity tester for each specified servers
        # A limitation when using the command line is that only one client_auth_credentials and http_tunneling_settings
        # can be specified, for all the servers to scan
        good_server_list = []
        bad_server_list = []
        for server_string in args_target_list:
            try:
                hostname, ip_address, port = CommandLineServerStringParser.parse_server_string(
                    server_string)
            except ServerStringParsingError as e:
                # Will happen if the server string is malformed
                bad_server_list.append(e)
                continue

            try:
                # TODO(AD): Unicode hostnames may fail on Python2
                # hostname = hostname.decode('utf-8')
                server_info = ServerConnectivityTester(
                    hostname=hostname,
                    port=port,
                    ip_address=ip_address,
                    tls_wrapped_protocol=tls_wrapped_protocol,
                    tls_server_name_indication=args_command_list.sni,
                    xmpp_to_hostname=args_command_list.xmpp_to,
                    client_auth_credentials=client_auth_creds,
                    http_tunneling_settings=http_tunneling_settings,
                )
                good_server_list.append(server_info)
            except ValueError as e:
                # Will happen for example if xmpp_to is specified for a non-XMPP connection
                raise CommandLineParsingError(e.args[0])

        # Command line hacks
        # Handle --starttls=auto now that we parsed the server strings
        if args_command_list.starttls == "auto":
            for server_info in good_server_list:
                # We use the port number to deduce the protocol
                if server_info.port in self.STARTTLS_PROTOCOL_DICT.keys():
                    server_info.tls_wrapped_protocol = self.STARTTLS_PROTOCOL_DICT[
                        server_info.port]

        # Handle --http_get now that we parsed the server strings
        # Doing it here is hacky as the option is defined within PluginOpenSSLCipherSuites
        if args_command_list.http_get:
            for server_info in good_server_list:
                if server_info.port == 443:
                    server_info.tls_wrapped_protocol = TlsWrappedProtocolEnum.HTTPS

        return good_server_list, bad_server_list, args_command_list
예제 #11
0
    def parse_command_line(self):
        """Parses the command line used to launch SSLyze.
        """

        (args_command_list, args_target_list) = self._parser.parse_args()

        # Handle the --targets_in command line and fill args_target_list
        if args_command_list.targets_in:
            if args_target_list:
                raise CommandLineParsingError(
                    "Cannot use --targets_list and specify targets within the command line."
                )

            try:  # Read targets from a file
                with open(args_command_list.targets_in) as f:
                    for target in f.readlines():
                        if target.strip():  # Ignore empty lines
                            if not target.startswith(
                                    '#'):  # Ignore comment lines
                                args_target_list.append(target.strip())
            except IOError:
                raise CommandLineParsingError(
                    "Can't read targets from input file '{}.".format(
                        args_command_list.targets_in))

        if not args_target_list:
            raise CommandLineParsingError('No targets to scan.')

        # Handle the --regular command line parameter as a shortcut
        if self._parser.has_option('--regular'):
            if getattr(args_command_list, 'regular'):
                setattr(args_command_list, 'regular', False)
                for cmd in self.REGULAR_CMD:
                    setattr(args_command_list, cmd, True)

        # Sanity checks on the command line options
        # Prevent --quiet and --xml_out -
        if args_command_list.xml_file and args_command_list.xml_file == '-' and args_command_list.quiet:
            raise CommandLineParsingError(
                'Cannot use --quiet with --xml_out -.')

        # Prevent --quiet and --json_out -
        if args_command_list.json_file and args_command_list.json_file == '-' and args_command_list.quiet:
            raise CommandLineParsingError(
                'Cannot use --quiet with --json_out -.')

        # Prevent --xml_out - and --json_out -
        if args_command_list.json_file and args_command_list.json_file == '-' \
                and args_command_list.xml_file and args_command_list.xml_file == '-':
            raise CommandLineParsingError(
                'Cannot use --xml_out - with --json_out -.')

        # Sanity checks on the client cert options
        client_auth_creds = None
        if bool(args_command_list.cert) ^ bool(args_command_list.key):
            raise CommandLineParsingError(
                'No private key or certificate file were given. See --cert and --key.'
            )

        elif args_command_list.cert:
            # Private key formats
            if args_command_list.keyform == 'DER':
                key_type = SSL_FILETYPE_ASN1
            elif args_command_list.keyform == 'PEM':
                key_type = SSL_FILETYPE_PEM
            else:
                raise CommandLineParsingError(
                    '--keyform should be DER or PEM.')

            # Let's try to open the cert and key files
            try:
                client_auth_creds = ClientAuthenticationCredentials(
                    args_command_list.cert, args_command_list.key, key_type,
                    args_command_list.keypass)
            except ValueError as e:
                raise CommandLineParsingError(
                    'Invalid client authentication settings: {}.'.format(e[0]))

        # HTTP CONNECT proxy
        http_tunneling_settings = None
        if args_command_list.https_tunnel:
            try:
                http_tunneling_settings = HttpConnectTunnelingSettings.from_url(
                    args_command_list.https_tunnel)
            except ValueError as e:
                raise CommandLineParsingError(
                    'Invalid proxy URL for --https_tunnel: {}.'.format(e[0]))

        # STARTTLS
        tls_wrapped_protocol = TlsWrappedProtocolEnum.PLAIN_TLS
        if args_command_list.starttls:
            if args_command_list.starttls not in self.START_TLS_PROTOCOLS:
                raise CommandLineParsingError(self.START_TLS_USAGE)
            else:
                # StartTLS was specified
                if args_command_list.starttls in self.STARTTLS_PROTOCOL_DICT.keys(
                ):
                    # Protocol was given in the command line
                    tls_wrapped_protocol = self.STARTTLS_PROTOCOL_DICT[
                        args_command_list.starttls]

        # Number of connection retries
        if args_command_list.nb_retries < 1:
            raise CommandLineParsingError(
                'Cannot have a number smaller than 1 for --nb_retries.')

        # Create the server connectivity info for each specifed servers
        # A limitation when using the command line is that only one client_auth_credentials and http_tunneling_settings
        # can be specified, for all the servers to scan
        good_server_list = []
        bad_server_list = []
        for server_string in args_target_list:
            # Support unicode domains
            server_string = unicode(server_string, 'utf-8')
            try:
                good_server_list.append(
                    ServerConnectivityInfo.from_command_line(
                        server_string=server_string,
                        tls_wrapped_protocol=tls_wrapped_protocol,
                        tls_server_name_indication=args_command_list.sni,
                        xmpp_to_hostname=args_command_list.xmpp_to,
                        client_auth_credentials=client_auth_creds,
                        http_tunneling_settings=http_tunneling_settings))
            except ServerConnectivityError as e:
                # Will happen for example if the DNS lookup failed or the server string is malformed
                bad_server_list.append((server_string, e))
            except ValueError as e:
                # Will happen for example if xmpp_to is specified for a non-XMPP connection
                raise CommandLineParsingError(e[0])

        # Command line hacks
        # Handle --starttls=auto now that we parsed the server strings
        if args_command_list.starttls == 'auto':
            for server_info in good_server_list:
                # We use the port number to deduce the protocol
                if server_info.port in self.STARTTLS_PROTOCOL_DICT.keys():
                    server_info.tls_wrapped_protocol = self.STARTTLS_PROTOCOL_DICT[
                        server_info.port]

        # Handle --http_get now that we parsed the server strings
        # Doing it here is hacky as the option is defined within PluginOpenSSLCipherSuites
        if args_command_list.http_get:
            for server_info in good_server_list:
                if server_info.port == 443:
                    server_info.tls_wrapped_protocol = TlsWrappedProtocolEnum.HTTPS

        return good_server_list, bad_server_list, args_command_list
예제 #12
0
    def parse_command_line(self) -> Tuple[List[ServerConnectivityTester], List[ServerStringParsingError], Any]:
        """Parses the command line used to launch SSLyze.
        """
        (args_command_list, args_target_list) = self._parser.parse_args()

        if args_command_list.update_trust_stores:
            # Just update the trust stores and do nothing
            TrustStoresRepository.update_default()
            raise TrustStoresUpdateCompleted()

        # Handle the --targets_in command line and fill args_target_list
        if args_command_list.targets_in:
            if args_target_list:
                raise CommandLineParsingError('Cannot use --targets_list and specify targets within the command line.')

            try:  # Read targets from a file
                with open(args_command_list.targets_in) as f:
                    for target in f.readlines():
                        if target.strip():  # Ignore empty lines
                            if not target.startswith('#'):  # Ignore comment lines
                                args_target_list.append(target.strip())
            except IOError:
                raise CommandLineParsingError('Can\'t read targets from input file \'{}.'.format(
                    args_command_list.targets_in))

        if not args_target_list:
            raise CommandLineParsingError('No targets to scan.')

        # Handle the --regular command line parameter as a shortcut
        if self._parser.has_option('--regular'):
            if getattr(args_command_list, 'regular'):
                setattr(args_command_list, 'regular', False)
                for cmd in self.REGULAR_CMD:
                    setattr(args_command_list, cmd, True)

        # Sanity checks on the command line options
        # Prevent --quiet and --xml_out -
        if args_command_list.xml_file and args_command_list.xml_file == '-' and args_command_list.quiet:
            raise CommandLineParsingError('Cannot use --quiet with --xml_out -.')

        # Prevent --quiet and --json_out -
        if args_command_list.json_file and args_command_list.json_file == '-' and args_command_list.quiet:
            raise CommandLineParsingError('Cannot use --quiet with --json_out -.')

        # Prevent --xml_out - and --json_out -
        if args_command_list.json_file and args_command_list.json_file == '-' \
                and args_command_list.xml_file and args_command_list.xml_file == '-':
            raise CommandLineParsingError('Cannot use --xml_out - with --json_out -.')

        # Sanity checks on the client cert options
        client_auth_creds = None
        if bool(args_command_list.cert) ^ bool(args_command_list.key):
            raise CommandLineParsingError('No private key or certificate file were given. See --cert and --key.')

        elif args_command_list.cert:
            # Private key formats
            if args_command_list.keyform == 'DER':
                key_type = OpenSslFileTypeEnum.ASN1
            elif args_command_list.keyform == 'PEM':
                key_type = OpenSslFileTypeEnum.PEM
            else:
                raise CommandLineParsingError('--keyform should be DER or PEM.')

            # Let's try to open the cert and key files
            try:
                client_auth_creds = ClientAuthenticationCredentials(args_command_list.cert,
                                                                    args_command_list.key,
                                                                    key_type,
                                                                    args_command_list.keypass)
            except ValueError as e:
                raise CommandLineParsingError('Invalid client authentication settings: {}.'.format(e.args[0]))

        # HTTP CONNECT proxy
        http_tunneling_settings = None
        if args_command_list.https_tunnel:
            try:
                http_tunneling_settings = HttpConnectTunnelingSettings.from_url(args_command_list.https_tunnel)
            except ValueError as e:
                raise CommandLineParsingError('Invalid proxy URL for --https_tunnel: {}.'.format(e.args[0]))

        # STARTTLS
        tls_wrapped_protocol = TlsWrappedProtocolEnum.PLAIN_TLS
        if args_command_list.starttls:
            if args_command_list.starttls not in self.START_TLS_PROTOCOLS:
                raise CommandLineParsingError(self.START_TLS_USAGE)
            else:
                # StartTLS was specified
                if args_command_list.starttls in self.STARTTLS_PROTOCOL_DICT.keys():
                    # Protocol was given in the command line
                    tls_wrapped_protocol = self.STARTTLS_PROTOCOL_DICT[args_command_list.starttls]

        # Create the server connectivity tester for each specified servers
        # A limitation when using the command line is that only one client_auth_credentials and http_tunneling_settings
        # can be specified, for all the servers to scan
        good_server_list = []
        bad_server_list = []
        for server_string in args_target_list:
            try:
                hostname, ip_address, port = CommandLineServerStringParser.parse_server_string(server_string)
            except ServerStringParsingError as e:
                # Will happen if the server string is malformed
                bad_server_list.append(e)
                continue

            try:
                # TODO(AD): Unicode hostnames may fail on Python2
                # hostname = hostname.decode('utf-8')
                server_info = ServerConnectivityTester(
                    hostname=hostname,
                    port=port,
                    ip_address=ip_address,
                    tls_wrapped_protocol=tls_wrapped_protocol,
                    tls_server_name_indication=args_command_list.sni,
                    xmpp_to_hostname=args_command_list.xmpp_to,
                    client_auth_credentials=client_auth_creds,
                    http_tunneling_settings=http_tunneling_settings
                )
                good_server_list.append(server_info)
            except ValueError as e:
                # Will happen for example if xmpp_to is specified for a non-XMPP connection
                raise CommandLineParsingError(e.args[0])

        # Command line hacks
        # Handle --starttls=auto now that we parsed the server strings
        if args_command_list.starttls == 'auto':
            for server_info in good_server_list:
                # We use the port number to deduce the protocol
                if server_info.port in self.STARTTLS_PROTOCOL_DICT.keys():
                    server_info.tls_wrapped_protocol = self.STARTTLS_PROTOCOL_DICT[server_info.port]

        # Handle --http_get now that we parsed the server strings
        # Doing it here is hacky as the option is defined within PluginOpenSSLCipherSuites
        if args_command_list.http_get:
            for server_info in good_server_list:
                if server_info.port == 443:
                    server_info.tls_wrapped_protocol = TlsWrappedProtocolEnum.HTTPS

        return good_server_list, bad_server_list, args_command_list