Nelson B wrote:
Rob Crittenden wrote:
In an SSL client I want to force the SSL handshake to take place instead of passively waiting for it to happen during the first write.

Right after I connect to the server I'm currently doing this:

   SSL_ResetHandshake(ssl, /* asServer */ PR_FALSE);
   do {
     SSL_ForceHandshake(ssl);
     PR_Recv (ssl, handshake, 1, PR_MSG_PEEK, 100);
     err = PR_GetError();
   } while (err != PR_END_OF_FILE_ERROR && handshake_done == 0);

If the handshake succeeds the my SSL_HandshakeCallback callback sets handshake_done and I exit the loop. If it fails then sooner or later PR_Recv will set the error to EOF. I don't care if I'm losing the error from the handshake, I do the logging for failures in the SSL_BadCertHook() callback.

Here are a few (?) questions and comments:

1. Is this a blocking socket, or non-blocking?

non-blocking, not my choice.


2. If non-blocking, are you certain that the connection has completed?
   That is, are you certain that the TCP's "three phase connect" is
   completely done?  This is tricky for non-blocking sockets, and trivial
   for blocking sockets.

I'm not sure it has completed, hence the simplistic loop and my plea for assistance. Despite empirical evidence that it "worked" in my 3 test cases this code is obviously bogus.

3. Is this the first handshake on the socket after the connect? or
   Is it a subsequent (e.g. second) handshake on the socket?

First connection only.

4. Is this a "server speaks first" application protocol? or
   is this a "client speaks first" protocol?

Client speaks first, HTTPS.

5. What if some other error occurs besides PR_END_OF_FILE_ERROR?
   This loop continues.  IMO, it should not.  Some errors leave the
   SSL socket in a state where it cannot continue.  This includes
   PR_IO_TIMEOUT_ERROR, if I'm not mistaken.

   If you ignore the error code and call an I/O function on the SSL socket
   again, it will return an error again immediately.  As coded, this will
   be an infinite loop.

Yup, that's what I'm worried about.

I guess at least the loop should be:

do
    ret = PR_Recv (ssl, handshake, 1, PR_MSG_PEEK, 100);
    err = PR_GetError();
while (ret < 0 && err == PR_IO_TIMEOUT_ERROR && handshake_done == 0);

On slow connections I've seen the loop fire as many as 20 times. I can increase the timeout, that is purely a goof. But again, I don't want a noticable pause. On connections to a local machine even with an interval of 1 sometimes the handshake completes in one run through the loop (and who said SSL is slow?)


6. IMO, you need to check every SSL call (and that includes PR_Recv)
   to see if it succeeded or failed, and not continue to use the socket
   on failure.  PR_WOULD_BLOCK_ERROR (a.k.a. EWOULDBLOCK) is obviously
   an exception to this rule, but needs to be handled with PR_Poll.

Yes, you're right, I was being lazy, but in this case it will ALWAYS fail since there should never be any data to peek at since this is the first (and only) connection. But I suppose I should consider the case where the remote closes he connection as well, see above for that fix.

7. Why are you using such a short timeout on PR_Recv?

So it doesn't cause a noticable pause in the connection. There will never be any "data" to peek at so I am guaranteed to wait a certain number of PRIntervals until PR_Recv returns. A larger value will almost certainly negate the need for the icky loop but even if the handshake is done, it'll hang around waiting for the timeout to end.

8. Why are you using SSL_ForceHandshake at all?
   Why not use PR_Recv to do both the handshake and receive the first
   application message?

No reason, it works as well without it.

My questions are:

1. Do I need the loop or will the PR_Recv, even with such a short timeout, do the trick for me?

What trick are you trying to do?

Force a handshake to complete on a non-blocking socket without doing I/O with PR_Send/Write or PR_Read/Recv (in non-peek).

2. Is there a better way to do this?

I'm sure the answer is "yes", but I can't advise you of an optimal solution
without knowing the answers to my questions above.  Also I don't really
understand what you're trying to achieve with this loop.

The usual reason for using SSL_ForceHandshake is when you're in a second
handshake situation, where you're a server, you've received a request,
you expect to receive nothing more until you've sent the response,
you've started a second handshake to request client auth, and you
have nothing to send until that handshake finishes.  In that case, it
is not appropriate to call either PR_Send/Write nor PR_Recv/Read, so
you need SSL_ForceHandshake.  In most other situations, where you're waiting
for the other side to send you something, you can just use PR_Recv/Read
to accomplish the handshake and wait for the message.  On a blocking
socket, no loop is needed.

In this case, you're the client and you're waiting for the server to say
something (apparently a "server speaks first" protocol, such as SMTP).
Assuming your socket is blocking, PR_Recv by itself should do the job for
you without a loop, and with a reasonable timeout.

Ah, I see. I sort of threw that in as a Hail Mary to no effect. This is one of those side projects that you work on despite having a million other, more important things to do. I didn't really spend a lot of time on it and when I was done, saw that it wasn't my best work. But still, I thought this was an interesting problem so thought I would try to fix it.

It would be a whole lot simpler if I didn't want to force the handshake. Indeed, that is how I initially wrote it, but then I changed my mind and wrenched the code until it "worked" the way I wanted.

thanks

rob
_______________________________________________
dev-tech-crypto mailing list
dev-tech-crypto@lists.mozilla.org
https://lists.mozilla.org/listinfo/dev-tech-crypto

Reply via email to