def connect(self, loop, metadata, domain: str, host, port, negotiation_timeout, base_logger=None): """ .. seealso:: :meth:`BaseConnector.connect` For general information on the :meth:`connect` method. Connect to `host` at TCP port number `port`. The :class:`aioxmpp.security_layer.SecurityLayer` object `metadata` is used to determine the parameters of the TLS connection. First, a normal TCP connection is opened and the stream header is sent. The stream features are waited for, and then STARTTLS is negotiated if possible. :attr:`~.security_layer.SecurityLayer.tls_required` is honoured: if it is true and TLS negotiation fails, :class:`~.errors.TLSUnavailable` is raised. TLS negotiation is always attempted if :attr:`~.security_layer.SecurityLayer.tls_required` is true, even if the server does not advertise a STARTTLS stream feature. This might help to prevent trivial downgrade attacks, and we don’t have anything to lose at this point anymore anyways. :attr:`~.security_layer.SecurityLayer.ssl_context_factory` and :attr:`~.security_layer.SecurityLayer.certificate_verifier_factory` are used to configure the TLS connection. .. versionchanged:: 0.10 The `negotiation_timeout` is set as :attr:`~.XMLStream.deadtime_hard_limit` on the returned XML stream. """ features_future = asyncio.Future(loop=loop) stream = protocol.XMLStream( to=domain, features_future=features_future, base_logger=base_logger, ) if base_logger is not None: logger = base_logger.getChild(type(self).__name__) else: logger = logging.getLogger(".".join([ __name__, type(self).__qualname__, ])) try: transport, _ = yield from ssl_transport.create_starttls_connection( loop, lambda: stream, host=host, port=port, peer_hostname=host, server_hostname=domain, use_starttls=True, ) except: # NOQA stream.abort() raise stream.deadtime_hard_limit = timedelta(seconds=negotiation_timeout) features = yield from features_future try: features[nonza.StartTLSFeature] except KeyError: if not metadata.tls_required: return transport, stream, (yield from features_future) logger.debug( "attempting STARTTLS despite not announced since it is" " required") try: response = yield from protocol.send_and_wait_for( stream, [ nonza.StartTLS(), ], [ nonza.StartTLSFailure, nonza.StartTLSProceed, ]) except errors.StreamError as exc: raise errors.TLSUnavailable( "STARTTLS not supported by server, but required by client") if not isinstance(response, nonza.StartTLSProceed): if metadata.tls_required: message = ("server failed to STARTTLS") protocol.send_stream_error_and_close( stream, condition=errors.StreamErrorCondition.POLICY_VIOLATION, text=message, ) raise errors.TLSUnavailable(message) return transport, stream, (yield from features_future) verifier = metadata.certificate_verifier_factory() yield from verifier.pre_handshake( domain, host, port, metadata, ) ssl_context = metadata.ssl_context_factory() verifier.setup_context(ssl_context, transport) yield from stream.starttls( ssl_context=ssl_context, post_handshake_callback=verifier.post_handshake, ) features_future = yield from protocol.reset_stream_and_get_features( stream, timeout=negotiation_timeout, ) return transport, stream, features_future
def connect(self, loop, metadata, domain, host, port, negotiation_timeout): """ .. seealso:: :meth:`BaseConnector.connect` For general information on the :meth:`connect` method. Connect to `host` at TCP port number `port`. The :class:`aioxmpp.security_layer.SecurityLayer` object `metadata` is used to determine the parameters of the TLS connection. First, a normal TCP connection is opened and the stream header is sent. The stream features are waited for, and then STARTTLS is negotiated if possible. :attr:`~.security_layer.SecurityLayer.tls_required` is honoured: if it is true and the server does not offer TLS or TLS negotiation fails, :class:`~.errors.TLSUnavailable` is raised. :attr:`~.security_layer.SecurityLayer.ssl_context_factory` and :attr:`~.security_layer.SecurityLayer.certificate_verifier_factory` are used to configure the TLS connection. """ features_future = asyncio.Future(loop=loop) stream = protocol.XMLStream( to=domain, features_future=features_future, ) transport, _ = yield from ssl_transport.create_starttls_connection( loop, lambda: stream, host=host, port=port, peer_hostname=host, server_hostname=domain, use_starttls=True, ) features = yield from features_future try: features[nonza.StartTLSFeature] except KeyError: if metadata.tls_required: message = ( "STARTTLS not supported by server, but required by client") protocol.send_stream_error_and_close( stream, condition=(namespaces.streams, "policy-violation"), text=message, ) raise errors.TLSUnavailable(message) else: return transport, stream, (yield from features_future) response = yield from protocol.send_and_wait_for( stream, [ nonza.StartTLS(), ], [ nonza.StartTLSFailure, nonza.StartTLSProceed, ]) if not isinstance(response, nonza.StartTLSProceed): if metadata.tls_required: message = ("server failed to STARTTLS") protocol.send_stream_error_and_close( stream, condition=(namespaces.streams, "policy-violation"), text=message, ) raise errors.TLSUnavailable(message) return transport, stream, (yield from features_future) verifier = metadata.certificate_verifier_factory() yield from verifier.pre_handshake( domain, host, port, metadata, ) ssl_context = metadata.ssl_context_factory() verifier.setup_context(ssl_context, transport) yield from stream.starttls( ssl_context=ssl_context, post_handshake_callback=verifier.post_handshake, ) features_future = yield from protocol.reset_stream_and_get_features( stream, timeout=negotiation_timeout, ) return transport, stream, features_future