Twisted always immediately reports the connectionMade to the application-level 
protocol, so a TLS protocol's connectionMade means the same thing as a TCP 
protocol's connectionMade.

If you call self.transport.write on a TLS transport which has not yet succeeded 
at verifying its peer, the bytes will be buffered and delivered only when the 
handshake has succeeded.  If the handshake fails the outgoing bytes will be 
discarded.

The application can tell that the connection failed due to TLS negotiation 
failure as opposed to a TCP transport problem by inspecting the exception 
within the Failure passed as the "reason" to the connectionLost method.  The 
full horrific menagerie of OpenSSL error codes are sadly exposed there, but the 
easiest rule of thumb is that it's a handshake problem if (A) it's 
OpenSSL.SSL.Error rather than twisted.internet.error.ConnectionLost, and (B) 
you haven't gotten any application-level data yet.

Presently, there is no general hook for "TLS handshake succeeded".  It's 
possible to do using OpenSSL APIs, although it's unpleasant.  This is clearly 
an oversight, and we're working on it 
<https://twistedmatrix.com/trac/ticket/6024> (thanks for reminding me, I should 
probably contribute to that ticket) but it has rarely been a practical problem. 
 From an application's perspective the only problem it can see is that 
getPeerCertificate returns None in connectionMade, but the only time you care 
about that is if you have peer-certificate-dependent data that you need to emit 
in a greeting (i.e. before they speak) to your peer, which is fairly unusual 
except in applications that need to do elaborate client-certificate 
verification.  I've written exactly one application that needed that in the 
past decade, and I managed to hack around it using the aforementioned pyOpenSSL 
APIs with a minimum of queasiness.  If you have control over the protocol, or 
if you're speaking something like HTTP where the client speaks first, you can 
always do certificate-dependent calculations in dataReceived, and dataReceived 
implicitly happens after the handshake is completed.

So if you're going to skip either the "handshake succeeded" notification or the 
"connection established" notification though, "handshake succeeded" is 
definitely the one to skip.  There are very, very few things that you actually 
want to do upon handshake completion and almost all of them can be accomplished 
in a different callback, whereas there are a bunch of resource-management 
things you want to do in connectionMade.

Although our implementation isn't complete, I'm pretty confident that the 
"right" way to do this is simply to have a separate (optional) callback which 
is only invoked on protocols with a TLS transport.  Probably rather than having 
the transport promise to store its peer certificate forever, you could just 
pass the certificate to this callback.

-glyph

P.S.: I am definitely going to put the quote below on a bumper sticker, with 
attribution ;-)

> On Jan 28, 2015, at 6:56 PM, Guido van Rossum <[email protected]> wrote:
> 
> What would Twisted do? (WWTD)
> 
> On Wed, Jan 28, 2015 at 6:20 PM, Victor Stinner <[email protected] 
> <mailto:[email protected]>> wrote:
> 2015-01-29 2:47 GMT+01:00 Guido van Rossum <[email protected] 
> <mailto:[email protected]>>:
> > Doesn't the exception bubble up to the caller of create_connection()?
> 
> Yes, it does.
> 
> >  So what's the problem?
> 
> The problem is in server which accepts incoming connections:
> BaseSelectorEventLoop._accept_conncetion(). Extract of the code:
> ---
>     conn, addr = sock.accept()
>     (...)
>     protocol = protocol_factory()
>     if sslcontext:
>         self._make_ssl_transport(
>             conn, protocol, sslcontext,
>             server_side=True, extra={'peername': addr}, server=server)
>     else:
>         self._make_socket_transport(
>             conn, protocol , extra={'peername': addr},
>             server=server)
>     # It's now up to the protocol to handle the connection.
> ---
> 
> If the transport fails before calling connection_made() (ex: SSL
> handshake failure): the transport and the protocol are destroyed, no
> message is logged, the server is not aware of the connection failure.
> 
> I would like to notify the server through the protocol (call
> connection_failed) that an incoming connection failed. Random use
> case: blacklist temporary the client IP.
> 
> Victor
> 
> 
> 
> -- 
> --Guido van Rossum (python.org/~guido <http://python.org/~guido>)

Reply via email to