Example #1
0
def makeService(options):
    """Construct a TCPServer from a LabRAD node."""
    name = options['name']
    host = options['host']
    port = int(options['port'])
    tls = C.check_tls_mode(options['tls'])
    return Node(name, host, port, tls)
Example #2
0
 def _connect(self, password, timeout, tls_mode):
     tls_mode = C.check_tls_mode(tls_mode)
     if tls_mode == 'on':
         raise Exception('TLS is not currently supported with the asyncore '
                         'backend')
     self.connected = False
     self.serverCache = {}
     self.settingCache = {}
     if self.port is None:
         port = C.MANAGER_PORT_TLS if tls_mode == 'on' else C.MANAGER_PORT
     else:
         port = self.port
     try:
         sock = socket.create_connection((self.host, port),
                                         timeout or 5)
         socketMap = {}
         self.cxn = AsyncoreProtocol(sock, map=socketMap)
         self.loop = threading.Thread(target=asyncore.loop,
             kwargs={'timeout':0.01, 'map': socketMap})
         self.loop.daemon = True
         self.loop.start()
         try:
             return self.login(password, self.name)
         except Exception, e:
             self.disconnect()
             raise
     except LoginFailedError:
         raise
     except Exception, e:
         raise LoginFailedError(e)
Example #3
0
def makeService(options):
    """Construct a TCPServer from a LabRAD node."""
    name = options['name']
    host = options['host']
    port = int(options['port'])
    tls = C.check_tls_mode(options['tls'])
    return Node(name, host, port, tls)
Example #4
0
def syncRunServer(srv, host=C.MANAGER_HOST, port=None, username=None,
                  password=None, tls_mode=C.MANAGER_TLS):
    """Run a labrad server of the specified class in a synchronous context.

    Returns a context manager to be used with python's with statement that
    will yield when the server has started and then shut the server down after
    the context is exited.
    """
    from labrad import protocol

    tls_mode = C.check_tls_mode(tls_mode)

    if port is None:
        port = C.MANAGER_PORT_TLS if tls_mode == 'on' else C.MANAGER_PORT

    @inlineCallbacks
    def start_server():
        p = yield protocol.connect(host, port, tls_mode, username, password)
        yield srv.startup(p)

    @inlineCallbacks
    def stop_server():
        srv.disconnect()
        yield srv.onShutdown()

    thread.startReactor()
    blockingCallFromThread(reactor, start_server)
    try:
        yield
    finally:
        try:
            blockingCallFromThread(reactor, stop_server)
        except Exception:
            pass # don't care about exceptions here
Example #5
0
def makeService(options):
    """Construct a TCPServer from a LabRAD node."""
    name = options['name']
    host = options['host']
    port = int(options['port'])
    password = labrad.support.get_password(host, port)
    tls_mode = C.check_tls_mode(options['tls'])
    return Node(name, host, port, password, tls_mode)
Example #6
0
def makeService(options):
    """Construct a TCPServer from a LabRAD node."""
    name = options['name']
    host = options['host']
    port = int(options['port'])
    username = options['username']
    password = options['password']
    tls_mode = C.check_tls_mode(options['tls'])
    return Node(name, host, port, username, password, tls_mode)
Example #7
0
def makeService(options):
    """Construct a TCPServer from a LabRAD node."""
    name = options['name']
    host = options['host']
    port = int(options['port'])
    username = options['username']
    password = options['password']
    tls_mode = C.check_tls_mode(options['tls'])
    return Node(name, host, port, username, password, tls_mode)
Example #8
0
def parseServerOptions(name, exit_on_failure=True, options=None):
    """Parse standard command line options for a server.

    Args:
        name (string): The default server name to use if no name is specified
            on the command line.
        exit_on_failure (boolean): If True, we call sys.exit when we fail
            to parse the command line options. Otherwise, we raise UsageError.
        options (list(string)): If given, parse options from the given strings.
            Otherwise, will parse options from the command line in sys.argv.

    Returns:
        A ServerOptions instance initialized from the command line arguments.
        This is a dict-like object containing these string-valued keys:
            'name', 'node', 'host', 'port', 'password', 'tls'
    """
    from twisted.python import usage

    class ServerOptions(usage.Options):
        optParameters = [
            ['name', 'n', name, 'Server name.'],
            ['node', 'd', getNodeName(), 'Node name.'],
            ['host', 'h', C.MANAGER_HOST, 'Manager location.'],
            ['port', 'p', None, 'Manager port.', int],
            ['username', 'u', None, 'Username.'],
            ['password', 'w', None, 'Login password.'],
            [
                'tls', 's', C.MANAGER_TLS,
                'TLS mode for connecting to manager (on/starttls/off)'
            ]
        ]

    config = ServerOptions()
    config['tls'] = C.check_tls_mode(config['tls'])
    try:
        config.parseOptions(options=options)
        if config['port'] is None:
            tls_on = config['tls'] == 'on'
            config['port'] = C.MANAGER_PORT_TLS if tls_on else C.MANAGER_PORT
    except usage.UsageError as errortext:
        print('%s: %s' % (sys.argv[0], errortext))
        print('%s: Try --help for usage details.' % (sys.argv[0]))
        if exit_on_failure:
            sys.exit(1)
        else:
            raise
    return config
Example #9
0
def syncRunServer(srv,
                  host=C.MANAGER_HOST,
                  port=None,
                  password=None,
                  tls=C.MANAGER_TLS):
    """Run a labrad server of the specified class in a synchronous context.

    Returns a context manager to be used with python's with statement that
    will yield when the server has started and then shut the server down after
    the context is exited.
    """

    tls = C.check_tls_mode(tls)

    if port is None:
        port = C.MANAGER_PORT_TLS if tls == 'on' else C.MANAGER_PORT

    if password is None:
        password = C.PASSWORD

    srv.password = password

    @inlineCallbacks
    def start_server():
        srv.configure_tls(host, tls)
        if tls == 'on':
            tls_options = crypto.tls_options(host)
            reactor.connectSSL(host, port, srv, tls_options)
        else:
            reactor.connectTCP(host, port, srv)
        yield srv.onStartup()

    @inlineCallbacks
    def stop_server():
        srv.disconnect()
        yield srv.onShutdown()

    thread.startReactor()
    blockingCallFromThread(reactor, start_server)
    try:
        yield
    finally:
        try:
            blockingCallFromThread(reactor, stop_server)
        except Exception:
            pass  # don't care about exceptions here
Example #10
0
def parseServerOptions(name, exit_on_failure=True, options=None):
    """Parse standard command line options for a server.

    Args:
        name (string): The default server name to use if no name is specified
            on the command line.
        exit_on_failure (boolean): If True, we call sys.exit when we fail
            to parse the command line options. Otherwise, we raise UsageError.
        options (list(string)): If given, parse options from the given strings.
            Otherwise, will parse options from the command line in sys.argv.

    Returns:
        A ServerOptions instance initialized from the command line arguments.
        This is a dict-like object containing these string-valued keys:
            'name', 'node', 'host', 'port', 'password', 'tls'
    """
    from twisted.python import usage

    class ServerOptions(usage.Options):
        optParameters = [
            ['name', 'n', name, 'Server name.'],
            ['node', 'd', getNodeName(), 'Node name.'],
            ['host', 'h', C.MANAGER_HOST, 'Manager location.'],
            ['port', 'p', None, 'Manager port.', int],
            ['password', 'w', None, 'Login password.'],
            ['tls', 's', C.MANAGER_TLS,
             'TLS mode for connecting to manager (on/starttls/off)']]

    config = ServerOptions()
    config['tls'] = C.check_tls_mode(config['tls'])
    try:
        config.parseOptions(options=options)
        if config['password'] is None:
            config['password'] = support.get_password(host=config['host'],
                                                      port=config['port'],
                                                      prompt=False)
        if config['port'] is None:
            tls_on = config['tls'] == 'on'
            config['port'] = C.MANAGER_PORT_TLS if tls_on else C.MANAGER_PORT
    except usage.UsageError, errortext:
        print '%s: %s' % (sys.argv[0], errortext)
        print '%s: Try --help for usage details.' % (sys.argv[0])
        if exit_on_failure:
            sys.exit(1)
        else:
            raise
Example #11
0
def syncRunServer(srv, host=C.MANAGER_HOST, port=None, password=None,
                  tls=C.MANAGER_TLS):
    """Run a labrad server of the specified class in a synchronous context.

    Returns a context manager to be used with python's with statement that
    will yield when the server has started and then shut the server down after
    the context is exited.
    """

    tls = C.check_tls_mode(tls)

    if port is None:
        port = C.MANAGER_PORT_TLS if tls == 'on' else C.MANAGER_PORT

    if password is None:
        password = C.PASSWORD

    srv.password = password

    @inlineCallbacks
    def start_server():
        srv.configure_tls(host, tls)
        if tls == 'on':
            tls_options = crypto.tls_options(host)
            reactor.connectSSL(host, port, srv, tls_options)
        else:
            reactor.connectTCP(host, port, srv)
        yield srv.onStartup()

    @inlineCallbacks
    def stop_server():
        srv.disconnect()
        yield srv.onShutdown()

    thread.startReactor()
    blockingCallFromThread(reactor, start_server)
    try:
        yield
    finally:
        try:
            blockingCallFromThread(reactor, stop_server)
        except Exception:
            pass # don't care about exceptions here
Example #12
0
def parseServerOptions(name, exit_on_failure=True, options=None):
    """Parse standard command line options for a server.

    Args:
        name (string): The default server name to use if no name is specified
            on the command line.
        exit_on_failure (boolean): If True, we call sys.exit when we fail
            to parse the command line options. Otherwise, we raise UsageError.
        options (list(string)): If given, parse options from the given strings.
            Otherwise, will parse options from the command line in sys.argv.

    Returns:
        A ServerOptions instance initialized from the command line arguments.
        This is a dict-like object containing these string-valued keys:
            'name', 'node', 'host', 'port', 'password', 'tls'
    """
    from twisted.python import usage

    class ServerOptions(usage.Options):
        optParameters = [
            ["name", "n", name, "Server name."],
            ["node", "d", getNodeName(), "Node name."],
            ["host", "h", C.MANAGER_HOST, "Manager location."],
            ["port", "p", None, "Manager port.", int],
            ["password", "w", C.PASSWORD, "Login password."],
            ["tls", "s", C.MANAGER_TLS, "TLS mode for connecting to manager (on/starttls/off)"],
        ]

    config = ServerOptions()
    config["tls"] = C.check_tls_mode(config["tls"])
    try:
        config.parseOptions(options=options)
        if config["port"] is None:
            tls_on = config["tls"] == "on"
            config["port"] = C.MANAGER_PORT_TLS if tls_on else C.MANAGER_PORT
    except usage.UsageError, errortext:
        print "%s: %s" % (sys.argv[0], errortext)
        print "%s: Try --help for usage details." % (sys.argv[0])
        if exit_on_failure:
            sys.exit(1)
        else:
            raise
Example #13
0
def syncRunServer(srv,
                  host=C.MANAGER_HOST,
                  port=None,
                  username=None,
                  password=None,
                  tls_mode=C.MANAGER_TLS):
    """Run a labrad server of the specified class in a synchronous context.

    Returns a context manager to be used with python's with statement that
    will yield when the server has started and then shut the server down after
    the context is exited.
    """
    from labrad import protocol

    tls_mode = C.check_tls_mode(tls_mode)

    if port is None:
        port = C.MANAGER_PORT_TLS if tls_mode == 'on' else C.MANAGER_PORT

    @inlineCallbacks
    def start_server():
        p = yield protocol.connect(host, port, tls_mode, username, password)
        yield srv.startup(p)

    @inlineCallbacks
    def stop_server():
        srv.disconnect()
        yield srv.onShutdown()

    thread.startReactor()
    blockingCallFromThread(reactor, start_server)
    try:
        yield
    finally:
        try:
            blockingCallFromThread(reactor, stop_server)
        except Exception:
            pass  # don't care about exceptions here
Example #14
0
def getConnection(host=C.MANAGER_HOST, port=None, name="Python Client",
                  password=None, tls=C.MANAGER_TLS):
    """Connect to LabRAD and return a deferred that fires the protocol object."""
    tls = C.check_tls_mode(tls)
    if port is None:
        port = C.MANAGER_PORT_TLS if tls == 'on' else C.MANAGER_PORT

    if tls == 'on':
        tls_options = crypto.tls_options(host)
        p = yield protocol.factory.connectSSL(host, port, tls_options,
                                              timeout=C.TIMEOUT)
    else:
        def connect():
            return protocol.factory.connectTCP(host, port, timeout=C.TIMEOUT)

        @inlineCallbacks
        def start_tls(p, cert_string=None):
            try:
                resp = yield p.sendRequest(C.MANAGER_ID, [(1L, ('STARTTLS', host))])
            except Exception, e:
                raise Exception(
                    'Failed sending STARTTLS command to server. You should '
                    'update the manager and configure it to support encryption '
                    'or else disable encryption for clients. See '
                    'https://github.com/labrad/pylabrad/blob/master/CONFIG.md')
            cert = resp[0][1]
            p.transport.startTLS(crypto.tls_options(host, cert_string=cert_string))
            returnValue(cert)

        def ping(p):
            return p.sendRequest(C.MANAGER_ID, [(2L, 'PING')])

        p = yield connect()
        is_local_connection = util.is_local_connection(p.transport)
        if ((tls == 'starttls-force') or
            (tls == 'starttls' and not is_local_connection)):
            try:
                cert = yield start_tls(p)
            except Exception:
                # TODO: remove this retry. This is a temporary fix to support
                # compatibility until TLS is fully deployed.
                print ('STARTTLS failed; will retry without encryption in case '
                       'we are connecting to a legacy manager.')
                p = yield getConnection(host, port, name, password, tls='off')
                print 'Connected without encryption.'
                returnValue(p)
            try:
                yield ping(p)
            except Exception:
                print 'STARTTLS failed due to untrusted server certificate:'
                print 'SHA1 Fingerprint={}'.format(crypto.fingerprint(cert))
                print
                while True:
                    ans = raw_input(
                            "Accept server certificate for host '{}'? "
                            "(accept just this [O]nce; [S]ave and always "
                            "accept this cert; [R]eject) ".format(host))
                    if ans.lower() in ['o', 's', 'r']:
                        break
                    else:
                        print 'Invalid input:', ans
                if ans.lower() == 'r':
                    raise
                p = yield connect()
                yield start_tls(p, cert)
                yield ping(p)
                if ans.lower() == 's':
                    # save now that we know TLS succeeded,
                    # including hostname verification.
                    crypto.save_cert(host, cert)
Example #15
0
def connect(host=C.MANAGER_HOST, port=None, tls_mode=C.MANAGER_TLS):
    """Connect to LabRAD and return a deferred that fires the protocol object.

    Args:
        host (str): The hostname of the manager.
        port (int): The tcp port of the manager. If None, use the appropriate
            default value based on the TLS mode.
        tls_mode (str): The tls mode to use for this connection. See:
            `labrad.constants.check_tls_mode`.

    Returns:
        twisted.internet.defer.Deferred(LabradProtocol): A deferred that will
        fire with the protocol once the connection is established.
    """
    tls_mode = C.check_tls_mode(tls_mode)
    if port is None:
        port = C.MANAGER_PORT_TLS if tls_mode == 'on' else C.MANAGER_PORT

    if tls_mode == 'on':
        tls_options = crypto.tls_options(host)
        p = yield _factory.connectSSL(host, port, tls_options, timeout=C.TIMEOUT)
        returnValue(p)

    def do_connect():
        return _factory.connectTCP(host, port, timeout=C.TIMEOUT)

    @inlineCallbacks
    def start_tls(p, cert_string=None):
        try:
            resp = yield p.sendRequest(C.MANAGER_ID, [(1L, ('STARTTLS', host))])
        except Exception:
            raise Exception(
                'Failed sending STARTTLS command to server. You should update '
                'the manager and configure it to support encryption or else '
                'disable encryption for clients. See '
                'https://github.com/labrad/pylabrad/blob/master/CONFIG.md')
        cert = resp[0][1]
        p.transport.startTLS(crypto.tls_options(host, cert_string=cert_string))
        returnValue(cert)

    def ping(p):
        return p.sendRequest(C.MANAGER_ID, [(2L, 'PING')])

    p = yield do_connect()
    is_local_connection = util.is_local_connection(p.transport)
    if ((tls_mode == 'starttls-force') or
        (tls_mode == 'starttls' and not is_local_connection)):
        try:
            cert = yield start_tls(p)
        except Exception:
            # TODO: remove this retry. This is a temporary fix to support
            # compatibility until TLS is fully deployed.
            print ('STARTTLS failed; will retry without encryption in case we '
                   'are connecting to a legacy manager.')
            p = yield connect(host, port, tls_mode='off')
            print 'Connected without encryption.'
            returnValue(p)
        try:
            yield ping(p)
        except Exception:
            print 'STARTTLS failed due to untrusted server certificate:'
            print 'SHA1 Fingerprint={}'.format(crypto.fingerprint(cert))
            print
            while True:
                ans = raw_input(
                        'Accept server certificate for host "{}"? (accept just '
                        'this [O]nce; [S]ave and always accept this cert; '
                        '[R]eject) '.format(host))
                ans = ans.lower()
                if ans in ['o', 's', 'r']:
                    break
                else:
                    print 'Invalid input:', ans
            if ans == 'r':
                raise
            p = yield do_connect()
            yield start_tls(p, cert)
            yield ping(p)
            if ans == 's':
                # save now that we know TLS succeeded,
                # including hostname verification.
                crypto.save_cert(host, cert)
    returnValue(p)
Example #16
0
def connect(host=C.MANAGER_HOST, port=None, tls_mode=C.MANAGER_TLS,
            username=None, password=None, headless=False):
    """Connect to LabRAD and return a deferred that fires the protocol object.

    Args:
        host (str): The hostname of the manager.
        port (int): The tcp port of the manager. If None, use the appropriate
            default value based on the TLS mode.
        tls_mode (str): The tls mode to use for this connection. See:
            `labrad.constants.check_tls_mode`.
        username (str | None): The username to use when authenticating.
        password (str | None): The password to use when authenticating.
        headless (bool): Whether to use headless OAuth flow if no username or
            password is configured.

    Returns:
        twisted.internet.defer.Deferred(LabradProtocol): A deferred that will
        fire with the protocol once the connection is established.
    """
    spawn_kw = dict(host=host, port=port, tls_mode=tls_mode,
                    username=username, password=password, headless=headless)

    tls_mode = C.check_tls_mode(tls_mode)
    if port is None:
        port = C.MANAGER_PORT_TLS if tls_mode == 'on' else C.MANAGER_PORT

    @inlineCallbacks
    def authenticate(p):
        yield p.authenticate(username, password, headless)

    if tls_mode == 'on':
        tls_options = crypto.tls_options(host)
        p = yield _factory.connectSSL(host, port, tls_options, timeout=C.TIMEOUT)
        p.set_address(host, port)
        p.spawn_kw = spawn_kw
        yield authenticate(p)
        returnValue(p)

    @inlineCallbacks
    def do_connect():
        p = yield _factory.connectTCP(host, port, timeout=C.TIMEOUT)
        p.set_address(host, port)
        p.spawn_kw = spawn_kw
        returnValue(p)

    @inlineCallbacks
    def start_tls(p, cert_string=None):
        try:
            cert = yield p._sendManagerRequest(1, ('STARTTLS', host))
        except Exception:
            raise Exception(
                'Failed sending STARTTLS command to server. You should update '
                'the manager and configure it to support encryption or else '
                'disable encryption for clients. See '
                'https://github.com/labrad/pylabrad/blob/master/CONFIG.md')
        p.transport.startTLS(crypto.tls_options(host, cert_string=cert_string))
        returnValue(cert)

    @inlineCallbacks
    def ping(p):
        resp = yield p._sendManagerRequest(2, 'PING')
        if isinstance(resp, tuple):
            manager_features = set(resp[1])
        else:
            manager_features = set()
        returnValue(manager_features)

    p = yield do_connect()
    is_local_connection = util.is_local_connection(p.transport)
    if ((tls_mode == 'starttls-force') or
        (tls_mode == 'starttls' and not is_local_connection)):
        try:
            cert = yield start_tls(p)
        except Exception:
            # TODO: remove this retry. This is a temporary fix to support
            # compatibility until TLS is fully deployed.
            print('STARTTLS failed; will retry without encryption in case we '
                  'are connecting to a legacy manager.')
            p = yield connect(host, port, tls_mode='off')
            print('Connected without encryption.')
            p.manager_features = set()
            yield authenticate(p)
            returnValue(p)
        try:
            manager_features = yield ping(p)
        except Exception:
            print('STARTTLS failed due to untrusted server certificate:')
            print('SHA1 Fingerprint={}'.format(crypto.fingerprint(cert)))
            print()
            while True:
                ans = input(
                        'Accept server certificate for host "{}"? (accept just '
                        'this [O]nce; [S]ave and always accept this cert; '
                        '[R]eject) '.format(host))
                ans = ans.lower()
                if ans in ['o', 's', 'r']:
                    break
                else:
                    print('Invalid input:', ans)
            if ans == 'r':
                raise
            p = yield do_connect()
            yield start_tls(p, cert)
            manager_features = yield ping(p)
            if ans == 's':
                # save now that we know TLS succeeded,
                # including hostname verification.
                crypto.save_cert(host, cert)
    else:
        manager_features = yield ping(p)
    p.manager_features = manager_features

    yield authenticate(p)
    returnValue(p)
Example #17
0
def connect(host=C.MANAGER_HOST,
            port=None,
            tls_mode=C.MANAGER_TLS,
            username=None,
            password=None,
            headless=False):
    """Connect to LabRAD and return a deferred that fires the protocol object.

    Args:
        host (str): The hostname of the manager.
        port (int): The tcp port of the manager. If None, use the appropriate
            default value based on the TLS mode.
        tls_mode (str): The tls mode to use for this connection. See:
            `labrad.constants.check_tls_mode`.
        username (str | None): The username to use when authenticating.
        password (str | None): The password to use when authenticating.
        headless (bool): Whether to use headless OAuth flow if no username or
            password is configured.

    Returns:
        twisted.internet.defer.Deferred(LabradProtocol): A deferred that will
        fire with the protocol once the connection is established.
    """
    spawn_kw = dict(host=host,
                    port=port,
                    tls_mode=tls_mode,
                    username=username,
                    password=password,
                    headless=headless)

    tls_mode = C.check_tls_mode(tls_mode)
    if port is None:
        port = C.MANAGER_PORT_TLS if tls_mode == 'on' else C.MANAGER_PORT

    @inlineCallbacks
    def authenticate(p):
        yield p.authenticate(username, password, headless)

    if tls_mode == 'on':
        tls_options = crypto.tls_options(host)
        p = yield _factory.connectSSL(host,
                                      port,
                                      tls_options,
                                      timeout=C.TIMEOUT)
        p.set_address(host, port)
        p.spawn_kw = spawn_kw
        yield authenticate(p)
        returnValue(p)

    @inlineCallbacks
    def do_connect():
        p = yield _factory.connectTCP(host, port, timeout=C.TIMEOUT)
        p.set_address(host, port)
        p.spawn_kw = spawn_kw
        returnValue(p)

    @inlineCallbacks
    def start_tls(p, cert_string=None):
        try:
            cert = yield p._sendManagerRequest(1, ('STARTTLS', host))
        except Exception:
            raise Exception(
                'Failed sending STARTTLS command to server. You should update '
                'the manager and configure it to support encryption or else '
                'disable encryption for clients. See '
                'https://github.com/labrad/pylabrad/blob/master/CONFIG.md')
        p.transport.startTLS(crypto.tls_options(host, cert_string=cert_string))
        returnValue(cert)

    @inlineCallbacks
    def ping(p):
        resp = yield p._sendManagerRequest(2, 'PING')
        if isinstance(resp, tuple):
            manager_features = set(resp[1])
        else:
            manager_features = set()
        returnValue(manager_features)

    p = yield do_connect()
    is_local_connection = util.is_local_connection(p.transport)
    if ((tls_mode == 'starttls-force')
            or (tls_mode == 'starttls' and not is_local_connection)):
        try:
            cert = yield start_tls(p)
        except Exception:
            # TODO: remove this retry. This is a temporary fix to support
            # compatibility until TLS is fully deployed.
            print('STARTTLS failed; will retry without encryption in case we '
                  'are connecting to a legacy manager.')
            p = yield connect(host, port, tls_mode='off')
            print('Connected without encryption.')
            p.manager_features = set()
            yield authenticate(p)
            returnValue(p)
        try:
            manager_features = yield ping(p)
        except Exception:
            print('STARTTLS failed due to untrusted server certificate:')
            print('SHA1 Fingerprint={}'.format(crypto.fingerprint(cert)))
            print()
            while True:
                ans = input(
                    'Accept server certificate for host "{}"? (accept just '
                    'this [O]nce; [S]ave and always accept this cert; '
                    '[R]eject) '.format(host))
                ans = ans.lower()
                if ans in ['o', 's', 'r']:
                    break
                else:
                    print('Invalid input:', ans)
            if ans == 'r':
                raise
            p = yield do_connect()
            yield start_tls(p, cert)
            manager_features = yield ping(p)
            if ans == 's':
                # save now that we know TLS succeeded,
                # including hostname verification.
                crypto.save_cert(host, cert)
    else:
        manager_features = yield ping(p)
    p.manager_features = manager_features

    yield authenticate(p)
    returnValue(p)