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