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

Hi, I have two scripts: server.pl and client.pl. This is source code: server.pl:
#!/usr/bin/perl # serverfork.pl - a server that forks a child # process to handle client connections # use strict; use IO::Socket; use Sys::Hostname; use POSIX qw(:sys_wait_h); use Crypt::CBC; sub REAP { 1 until (-1 == waitpid(-1, WNOHANG)); $SIG{CHLD} = \&REAP; } $SIG{CHLD} = \&REAP; my $sock = new IO::Socket::INET( LocalHost => 'localhost', LocalPort => 9000, Proto => 'tcp', Listen => SOMAXCONN, Reuse => 1); $sock or die "no socket :$!"; # STDOUT->autoflush(1); my $cipher = Crypt::CBC->new( -key => '1234', -cipher => 'Blowfish' ); my($new_sock, $buf, $kid); while ($new_sock = $sock->accept()) { # execute a fork, if this is # the parent, its work is done, # go straight to continue next if $kid = fork; die "fork: $!" unless defined $kid; # child now... # close the server - not needed close $sock; while (defined($buf = <$new_sock>)) { chop $buf; server(); } exit; } continue { # parent closes the client since # it is not needed close $new_sock; } sub server { my $plaintext = $cipher->decrypt($buf); my $in = "SERVER: Text: ".$plaintext; my $ciphertext = $cipher->encrypt($in); print($new_sock $ciphertext); }
and client.pl:
#!/usr/bin/perl # use IO::Socket; use Crypt::CBC; my $sock = new IO::Socket::INET( PeerAddr => 'localhost', PeerPort => 9000, Proto => 'tcp'); $sock or die "no socket :$!"; my $cipher = Crypt::CBC->new( -key => '1234', -cipher => 'Blowfish' ); # clientfork.pl - a client that forks to # read from STDIN and write to a server use strict; use IO::Socket; my $host = shift || 'localhost'; my $port = shift || 9000; my $sock = new IO::Socket::INET( PeerAddr => $host, PeerPort => $port, Proto => 'tcp'); $sock or die "no socket :$!"; $sock->autoflush(1); my($in, $buf, $kid); die "fork fail: $!" unless defined($kid = fork); if ($kid) { # parent reads from STDIN, prints to socket while (defined($in = <STDIN>)) { my $ciphertext = $cipher->encrypt($in); print $ciphertext; print $sock $ciphertext; } # kill the child process kill(TERM => $kid); } else { # child reads from socket, prints to STDOUT while (defined($buf = <$sock>)) { my $plaintext = $cipher->decrypt($buf); print $plaintext; } close $sock; }
Ok, when I run client.pl and I write anything, i have this output to SDOUT: http://wklej.org/id/124382/?zawin=0 In output from server.pl I have:
wmp@innocence:~/ISS/iss/daemon$ ./server.pl Ciphertext does not begin with a valid header for 'salt' header mode a +t ./server.pl line 48 wmp@innocence:~/ISS/iss/daemon$
What I cat do to fix this error? And, what I can do to send text in client only one enter click, no many enter click.

Replies are listed 'Best First'.
Re: Crypt::CBC and IO:Socket
by ikegami (Patriarch) on Jul 21, 2009 at 23:41 UTC

    You read from the socket until a newline is found, and you assume you have a full (encrypted) message when you do. There's two problems with that:

    • The server doesn't end messages with newlines.
    • Newlines can be found in the middle of the ciphertext.

    Start by encrypting the stream rather than each message.

    # Sender $cipher->start('encrypting'); print $sock $cipher->crypt($_) while <STDIN>; print $sock $cipher->finish();
    # Receiver $cipher->start('decrypting'); local $/ = \4096; process_bytes($cipher->crypt($_)) while <$sock>; process_bytes($cipher->finish());
    # Interactive receiver $cipher->start('decrypting'); for (;;) { my $rv = sysread($sock, my $buf='', 4096); die $! if !defined $rv last if !$rv; process_bytes($cipher->crypt($buf)); } process_bytes($cipher->finish());

    I think $/=4096;<> will wait for 4096 bytes. sysread will return as soon as any bytes are available.

    If you want to read a line at a time, you'll need to accumulate and find lines in the plaintext yourself.

    Upgrade: Doh! Using sysread is not enough to be truly interactive. You'll need to pad your lines to a multiple of $cipher->blocksize() bytes.

    Interactive or not, why not just use IO::Socket::SSL?

      I writed this code: server.pl:
      sub server { # my $plaintext = $cipher->decrypt($buf); # my $in = "SERVER: Text: ".$plaintext; # my $ciphertext = $cipher->encrypt($in); # print($new_sock $ciphertext); my $plaintext = decrypt($buf); my $in = "SERVER: Text: ".$plaintext; my $ciphertext = crypt_($in); } sub crypt_ { $cipher->start('encrypting'); print $sock $cipher->crypt($_) while <STDIN>; print $sock $cipher->finish(); } sub decrypt { $cipher->start('decrypting'); local $/ = \4096; process_bytes($cipher->crypt($_)) while <$sock>; process_bytes($cipher->finish()); }
      client.pl:
      if ($kid) { # parent reads from STDIN, prints to socket while (defined($in = <STDIN>)) { # my $ciphertext = $cipher->encrypt($in); # print $ciphertext; # print $sock $ciphertext; my $ciphertext = crypt_($in); } # kill the child process kill(TERM => $kid); } else { # child reads from socket, prints to STDOUT while (defined($buf = <$sock>)) { # my $plaintext = $cipher->decrypt($buf); # print $plaintext; my $plaintext = decrypt($buf); print $plaintext; } close $sock; } sub crypt_ { $cipher->start('encrypting'); print $sock $cipher->crypt($_) while <STDIN>; print $sock $cipher->finish(); } sub decrypt { $cipher->start('decrypting'); local $/ = \4096; process_bytes($cipher->crypt($_)) while <$sock>; process_bytes($cipher->finish()); }
      In output from server.pl i have error:
      wmp@innocence:~/ISS/iss/daemon$ ./server.pl Undefined subroutine &main::process_bytes called at ./server.pl line 6 +6. wmp@innocence:~/ISS/iss/daemon$
      In client.pl again I must clicks enter. How to i can use IO::Socket::SSL ?

        In output from server.pl i have error:

        You need to provide the sub. Incoming bytes are fed to that sub. What you do in it is up to you.

        In client.pl again I must clicks enter.

        Well yeah, that's what <STDIN> means. You'll need Term::ReadKey to read a key at a time from the keyboard.

Re: Crypt::CBC and IO:Socket
by jethro (Monsignor) on Jul 21, 2009 at 23:41 UTC
    One problem I see with your code is that you are sending binary data but you try to read lines (i.e. strings ending in \n).

    You might simply use encrypt_hex() and decrypt_hex() to avoid binary strings. Don't forget to add \n on the sending side and remove it again (with chomp) on the receiving side to have correct line endings.

    Or you could keep the binary strings and read fixed length strings with read() or sysread()