Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

IO::Socket:SSL and Net::Server STDOUT redirection

by Zucan (Beadle)
on Nov 17, 2014 at 15:45 UTC ( [id://1107431]=perlquestion: print w/replies, xml ) Need Help??

Zucan has asked for the wisdom of the Perl Monks concerning the following question:

I have been tinkering around with a forking server in Perl. Since I wanted the ability to shell out and execute commands and have their output sent back over the socket, I took advantage of redirecting STDIN/STDOUT to the socket. This works perfectly if I am not doing SSL. With SSL, it does not work. Here is an example of me using IO::Socket::INET, which works:
#!/usr/bin/perl -w use strict; use IO::Socket::INET; my $port = 12345; my $server = IO::Socket::INET->new( LocalAddr => '127.0.0.1', LocalPort => $port, Listen => 10, Reuse => 1, ) or die "failed to listen: $!"; my $client = $server->accept; my $cf = $client->fileno; open(STDIN, "<&$cf") || die "Couldn't dup client to stdin"; open(STDOUT, ">&$cf") || die "Couldn't dup client to stdout"; print "The client should see this message.\n"; close($client);
Here is an example of me using IO::Socket::SSL, which does not work:
#!/usr/bin/perl -w use strict; use IO::Socket::SSL; my $port = 12345; my $server = IO::Socket::SSL->new( LocalAddr => '127.0.0.1', LocalPort => $port, Listen => 10, Reuse => 1, SSL_cert_file => 'cert.pem', SSL_key_file => 'key.pem', ) or die "failed to listen: $!"; my $client = $server->accept; my $cf = $client->fileno; open(STDIN, "<&$cf") || die "Couldn't dup client to stdin"; open(STDOUT, ">&$cf") || die "Couldn't dup client to stdout"; print "The client should see this message.\n"; close($client);
Here is the output of each script when ran:
$ nc localhost 12345 The client should see this message. $ nc --ssl localhost 12345

There are plenty of examples about how to do a forking server with the IO::Socket module with STDIN/STDOUT redirection. As soon as the IO::Socket module is flipped to SSL, the redirection no longer works. The above examples do not do what I really wanted, which is to shell out and execute a command. If I can't get a basic print statement to work with IO redirection, then shelling out won't work either.

I looked at other modules too. Net::Server was more useful to me and seemed to work better. Here is an example without using SSL:
#!/usr/bin/perl -w package NetServer; use strict; use base qw(Net::Server::Fork); sub process_request { my $self = shift; ### COMMAND PIPE print "Executing Command Pipe\n"; open DATA, "/usr/bin/echo ==== Command Pipe Successful|" or die "Couldn't open command: $!"; while (defined(my $line = <DATA>)) { chomp($line); print "$line\n"; } close DATA; ### SYSTEM COMMAND print "Executing System Command\n"; system("/usr/bin/echo ==== System Command Successful"); ### EXEC COMMAND print "Executing Exec Command\n"; exec "/usr/bin/echo ==== Exec Command Successful" or die "Couldn't exec command: $!"; } NetServer->run(port=>12345);
The above works perfectly. All three echo commands go over the socket. Here is the SSL example:
#!/usr/bin/perl -w package NetServer; use strict; use base qw(Net::Server::Fork); sub SSL_key_file { "key.pem" } sub SSL_cert_file { "cert.pem" } sub process_request { my $self = shift; ### COMMAND PIPE print "Executing Command Pipe\n"; open DATA, "/usr/bin/echo ==== Command Pipe Successful|" or die "Couldn't open command: $!"; while (defined(my $line = <DATA>)) { chomp($line); print "$line\n"; } close DATA; ### SYSTEM COMMAND print "Executing System Command\n"; system("/usr/bin/echo ==== System Command Successful"); ### EXEC COMMAND print "Executing Exec Command\n"; exec "/usr/bin/echo ==== Exec Command Successful" or die "Couldn't exec command: $!"; } #NetServer->run(proto => 'ssl', port=>12345); NetServer->run(proto => 'ssleay', port=>12345);
I tried it with both "ssl" and "ssleay" and the results were identical. Surprisingly, I get the output from the Command Pipe, but I don't get output from the other two. The following is the output from the non-ssl and the ssl example scripts from above:
$ nc localhost 12345 Executing Command Pipe ==== Command Pipe Successful Executing System Command ==== System Command Successful Executing Exec Command ==== Exec Command Successful $ nc --ssl localhost 12345 Executing Command Pipe ==== Command Pipe Successful Executing System Command Executing Exec Command

Just to be sure the Command Pipe section wasn't interfering with the other two sections, I did play around with commenting all but one section at a time and the results were the same... the Command Pipe works but the System Command and Exec Command sections do not.

I spent the last several days researching this on the Internet, reading up in my books and playing around extensively in code. I now ask for Wisdom from the Perl Monks community. I would like to know why redirection doesn't work at all with IO::Socket::SSL and what am I missing with regards to the system() and exec() calls that prevents redirection working with the Net::Server module.

Thank you,
Zucan

Replies are listed 'Best First'.
Re: IO::Socket:SSL and Net::Server STDOUT redirection
by McA (Priest) on Nov 17, 2014 at 16:45 UTC

    Hi Zucan,

    just a hint to be sure that the problem is Per related and not SSL related: have you tried the openssl s_client command to interact with your server snippet. This tool can also be interactively used and logs many informations concerning the SSL protocol.

    UPDATE: Yes, openssl helped me to get an idea. Change the following line

    open(STDOUT, ">&$cf") || die "Couldn't dup client to stdout";

    into

    open(STDOUT, ">&=", $client) || die "Couldn't dup client to stdout";

    I got it to work this way.

    Hope it helps
    McA

      This didn't work for me:
      my $client = $server->accept; open(STDIN, "<&=", $client) || die "Couldn't dup client to stdin"; open(STDOUT, ">&=", $client) || die "Couldn't dup client to stdout"; print "The client should see this STDOUT message.\n"; print $client "The client should see this SOCKET message.\n";
      What I see is this:
      $ nc --ssl localhost 12345 The client should see this SOCKET message

      Before I was using "nc --ssl host port", I was using "openssl s_client -connect host:port" for my testing. I got all the certificate info at the beginning of the output and I didn't see anything that helped. I would love to know what you saw in the openssl output that clued you in on the file descriptor. In any case, changing my script to your suggestion doesn't change anything for me.

      On a similar note: I was reading up on IO redirection in Perl and it doesn't seem surprising that system() and exec() continues to use the standard STDIN/STDIO/STDERR file descriptors. The redirection in Perl stays within perl and doesn't carry forth externally. It may be possible to use FileHandle or Tie::Handle to get it to work, but since using an command pipe with open() seems to work, it doesn't seem worth the extra effort. It works with POE as well.

      Mostly, I am bummed by the fact that IO::Socket::SSL acts differently from IO::Socket::INET. I can't get any redirection working at the moment. I at least have something working with Net::Server and with POE.

        Hi Zucan,

        sorry, but I have to admit I'm an idiot. While testing several options being curious, I did exactly the same as you:

        print $client "The client should see this message.\n";

        I left this line unchanged while changing others and was happy to "having found the solution".

        Sorry again.

        But with using openssl s_client I found out that sending the message via STDOUT seemed to circumvent the encryption layer. This lead me to the conclusion that you have to redirect some "upper" layer.

        UPDATE: Being aschamed about my error I gave it another try. I now did a:

        *STDOUT = $client;

        I really hope this also works for you now.

        Regards
        McA

Re: IO::Socket:SSL and Net::Server STDOUT redirection
by Loops (Curate) on Nov 17, 2014 at 19:21 UTC

    McA got to the heart of the problem already, although I'm not sure if the same solution can be applied with Net::Server. It uses Tie::Handle to capture STDOUT, which obviously wont be respected by any forked process. However, you could use IPC::Run to capture the output instead. The following works:

    package NetServer; use base qw(Net::Server::Fork); use IPC::Run; sub SSL_key_file { "key.pem" } sub SSL_cert_file { "cert.pem" } sub process_request { my $command = [ 'echo', '==== System Command Successful' ]; IPC::Run::run($command, '>', sub { print shift }); } NetServer->run(proto => 'ssleay', port=>12345);

    And actually in the simple case, IPC::Run doesn't buy you much over just:

    print qx( /usr/bin/echo ==== System Command Successful );

    But to be clear, the IPC::Run version is better when executing a long running child, since it will asynchronously capture and forward output. The "qx" version waits until the child completes before forwarding the output.

      This worked pretty good too. Thank you!

      The distinction between qx() and the IPC::Run version was also nicely explained. I appreciate it!

      Zucan

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1107431]
Approved by rnewsham
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others admiring the Monastery: (2)
As of 2024-04-20 04:11 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found