Rob Crittenden wrote:
> 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.

>> 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.

OK, well, I would not have (and obviously did not) guess that from the code
you initially presented.  That changes my answer, and makes me glad I asked
all those questions.

You're trying to do fairly typical non-blocking client behavior, with the
added wrinkle that you want to do the handshake before doing the first
write, for some reason I can't yet fathom.  Since the socket is non-blocking,
the client already must know how to handle PR_WOULD_BLOCK_ERROR.
As long as the client does that correctly, it shouldn't need to force the
handshake before the first write.  The initial write attempts will simply
return PR_WOULD_BLOCK_ERROR until the handshake is completed.

Still, that's all very easy to do.  There are 3 steps that need to be done,
in order, each step completed before the next one is begun.  They are
a) initiate and complete the TCP connection
b) do the handshake
c) write the initial http request

Notice that there's no need to do ANY recv calls in there, anywhere.
The receive calls are completely unnecessary and actually complicate
matters because of the timeouts.  Just don't do 'em.

NSS's test client program, tstclnt, already does everything you want to do
except forcing the handshake before the first write.  I'm going to point
you to tstclnt and let you follow that example.  It will show you how to
initiate a non-blocking connection, and wait for it to complete and KNOW
when it is completed.  Look at lines 751-797 of
http://landfill.mozilla.org/mxr-test/security/source/security/nss/cmd/tstclnt/tstclnt.c#751

The only additional piece you need to know is how to force the handshake,
AFTER the TCP connection is completely established.

> 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?)

>> 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.

Now I understand that you're using PR_Recv as a substitute for PR_Poll.
Use PR_Poll instead.  As far as "noticeable pause", you have to wait for
the handshake to complete, however long that takes.  You don't want to
introduce any extra wait time, so don't use PR_Recv.  Use PR_Poll.

>> 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).

To do that, you need SSL_ForceHandshake and PR_Poll.  PR_Recv is actually
the problem in this code.

Given that you're the client doing the first handshake on a client-speaks-
first application protocol, you don't need to worry about receiving any
application data from the server before the handshake completes.  That
simplifies things a lot,  AFTER you're sure the connection is completed
(that is, fully established), you can simply do a loop, calling
SSL_ForceHandshake, and if it returns PR_WOULD_BLOCK_ERROR, then calling
PR_Poll on the socket until it is readable again.  Repeat this until
you get any other error or the handshake is completed, or too much time
has elapsed.

Don't make timeouts occur.  You have to wait until the handshake is done
anyway.  You should only use timeout to detect that too much total time
has elapsed.  Set the socket's timeout to the upper bound of that time limit.

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

Reply via email to