For those of you who already played with Webmin, you probably noticed that connecting without SSL to the webmin interface (typically on port 10000) displays a message telling you how to access the SSL secured interface… on the same port.

Ever wondered how to accept both SSL and non-SSL connections on the same port?

Basically, to display such a message, we need to know if the client talking to us is speaking using SSL, or not. This is easily done by reading a few bytes from the stream, but if you do this, starting the crypto using for example stream_socket_enable_crypto() will fail, since OpenSSL won’t find the full client SSL handshake anymore.

PHP offers us a nice solution to fix this, using stream_socket_recvfrom(). By passing option STREAM_PEEK to this function, we can take a peek at the data pending in the socket, and try to determine if that’s SSL or not.

There, we can either try to parse a SSL packet, or instead try to find data we know there should be if the stream is not encrypted.

Doing this for the HTTP protocol is easy. The protocol is text based, and the first word we will get from the client will be something like “GET”, “POST” or “HEAD”. We just check if we got any of those. If we did, we got plain text connection. If we don’t, it means we are probably facing a real openssl client, and we can try to start negociating the link.

I wrote a little example you can download via SVN at http://ookoo.org/svn/snip/https_multi_serv/. Just run “gen_key.sh” in the ssl directory to get a SSL private key, then run the server with PHP. By default it will listen on port 8000, so direct your browser to localhost:8000 with or without SSL (both will work, this is the point of this server).

Feel free to use the code there, I commented it a bit so it should be somewhat helpful, and I officially release it under public domain (or BSD if “public domain” does not legally exists in your country).

By the way it’s also a nice example of async server using stream_select().

Of course it’s not possible to auto-magically determine if the client is talking SSL when he’s not talking first. You could wait for one or two seconds to see if something comes (ie. an SSL handshake) but it’s not really something that could be called good practice… So let’s just keep this for cases where the client talks first.

Tags: , , ,