noxxi / p5-io-socket-ssl

IO::Socket::SSL Perl Module
36 stars 59 forks source link

IO::Socket::SSL + fork #19

Closed utopicdev closed 9 years ago

utopicdev commented 9 years ago

Hello, I've asked a question on perlmonks.org today ( http://perlmonks.org/?node_id=1107935 ), and it seems this issue needs an implementation. IO::Socket::SSL is a very versatile module, but the lack of fork support is a really sad fact.

Since IO::Socket::SSL is a descendent of IO::Socket::INET, I think it should support fork as the second one does.

Would it be possible to implement a such 'fix' for not using third-party event-driven frameworks?

noxxi commented 9 years ago

I cannot reproduce your problem with my own tests. Could you please provide some minimal code which can be actually used to reproduce the problem (the code at perlmonks needs some unknown server as peer) and reproduces the problem with the latest versions of IO::Socket::SSL and Net::SSLeay?.

utopicdev commented 9 years ago

I thought it will be much to post my bitcoin event handler server-side to reproduce the problem, though the functionality is similar to an IRC client, the fastest way was to write an IRC connection. Here is a working code: https://paste.debian.net/132821/

It connects to irc.freenode.org and joins channel #test12345 You should connect there also and write "tick qwdf" in the channel. First time you do this, the program will respond 'hi' to the channel, the second time you type "tick qwdf", the program will suddenly quit due to the main problem.

It's gonna look like this:

* test12345 (~w@rayleask.demon.co.uk) has joined #test12345
<popl> tick qwdf
<test12345> hi
<popl> tick qwdf
* test12345 has quit (Remote host closed the connection)
noxxi commented 9 years ago

Your code in effect does the following:

  1. establish the SSL connection
  2. do some I/O in parent process
  3. fork, do some I/O in child process
  4. do again I/O in parent

This will not work because TLS is a layer 5 protocol which has its own state inside the user space. Thus if you fork you will duplicate this state information too, that is each process maintains its own state. If you then do I/O in the child the TLS state of the child will change, while the TLS state of the parent will remain the same. If you then do I/O in the parent again it will be based on an old state. The TLS peer will notice that and drop the connection because the peer sends data which do not match the current TLS state.

With plain TCP this works because there is no user space state but both parent and client share the same in-kernel TCP state.

With Windows this works because the fork() emulation does not do a real fork and instead only duplicates the Perl-related structures. Since the TLS state is handled by OpenSSL library and not by Perl it will not be duplicated, so it will be shared between the emulated processes (which are threads in reality).

In summary: this is not a problem of IO::Socket::SSL and it will happen too if you write your code in C, C++, Java etc. The problem is that the state is not shared but duplicated between processes and thus gets invalid in one process if the other does I/O.