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

Hi everyone, I've recently started experimenting with PERL in the area of sockets and networks. I've gotten the two programs listed below working on my own machine (through localhost). However, I want to prompt the server to ask the client for a password, printing a message if the correct password is entered or not. The problem I'm having is how do I send the values I read in in the client program to the server program? I know how to check for these values but I'm stumped as to how to send the string that's entered client-side to the server-side. Or am I completely on the wrong track? Server Program
#!/usr/bin/perl -w # server2way.pl - a server that reads from # and writes to a client use strict; use IO::Socket; use Sys::Hostname; my $sock = new IO::Socket::INET( LocalHost => 'localhost', LocalPort => 7890, Proto => 'tcp', Listen => SOMAXCONN, Reuse => 1); $sock or die "no socket :$!"; STDOUT->autoflush(1); my($new_sock, $buf); while ($new_sock = $sock->accept()) { while (defined($buf = <$new_sock>)) { foreach ($buf) { /^HELLO$/ and print($new_sock "Enter Name: \n"), l +ast; /^NAME$/ and print($new_sock "Enter Password: \n +"), last; /^DATE$/ and print($new_sock scalar(localtime), "\ +n"),last; print $new_sock "DEFAULT\n"; } } close $new_sock; }
Client Code
#!/usr/bin/perl -w # client2way.pl - a client that writes to # and reads from a server use strict; use IO::Socket; my $host = shift || 'localhost'; my $port = shift || 7890; my $sock = new IO::Socket::INET( PeerAddr => $host, PeerPort => $port, Proto => 'tcp'); $sock or die "no socket :$!"; # send message to server print $sock "HELLO\n"; print scalar <$sock>; my $name =<STDIN>; print $sock "NAME\n"; print scalar <$sock>; my $guess=<STDIN>; print $sock "DATE\n"; print scalar <$sock>; print $sock "NONE\n"; print scalar <$sock>; close $sock;

Replies are listed 'Best First'.
Re: Client/Server Question
by almut (Canon) on Mar 06, 2010 at 21:22 UTC
    The problem I'm having is how do I send the values I read in in the client program to the server program?

    Like any other data you're already sending, e.g.

    ... my $name =<STDIN>; print $sock $name; # $name still holds the newline you got from STDI +N

    Or maybe simply append it to the next message you're sending (i.e. "NAME"):

    print $sock "NAME: $name";

    That way, you could stick to your processing scheme server side, and just extract the sent part in question via regex capture:

    /^NAME: (\w*)/ and ... # do something with $1

    (Or have I misunderstood something?)

      Thanks for your reply almut. I think I'm the one that's missed something as I'm completely new to Perl and a bit lost! So I do all of the processing server-side? Can you give me a quick example of how I might, for example, read in a name that someone's entered on the clint side and then check that name against a default name on the server side? I find Perl's syntax very fiddly and all of my attempts to do it so far have resulted in errors and infinite loops galore. Thanks so much for your help.

        The general problem with these things is that server and client have to agree on some protocol, i.e. that each party knows how to react in response to what the other side has done.  Here's your example modified to handle name and password checking with dynamic branching on the client side, in case re-entry of name or password is required.

        Server:

        #!/usr/bin/perl -w use strict; use IO::Socket; my $sock = new IO::Socket::INET( LocalHost => 'localhost', LocalPort => 7890, Proto => 'tcp', Listen => SOMAXCONN, Reuse => 1); $sock or die "no socket :$!"; STDOUT->autoflush(1); my $req_name = "Enter Name: \n"; my $req_pass = "Enter Password: \n"; my $req_none = "Enter \n"; sub verify_passw { my ($name, $pass) = @_; # print STDERR "name=$name, pass=$pass\n"; # debug # ... return 1; } while (my $new_sock = $sock->accept()) { my $name = ""; # lifetime is connection my $auth = 0; # ditto (storing state of session) while (<$new_sock>) { /^HELLO$/ and print($new_sock $req_name), next; /^NAME: (\w*)/ and do { $name = $1; if ($name && length($name)<=8) { print $new_sock "Ok, name: $name\n"; print $new_sock $req_pass; } else { print $new_sock "Invalid name: '$name'\n"; print $new_sock $req_name; } next }; /^PASS: (\w*)/ and do { my $pass = $1; if ($auth = verify_passw($name, $pass)) { # =, not == print $new_sock "Login ok\n"; print $new_sock $req_none; } else { print $new_sock "Invalid password!\n"; print $new_sock $req_pass; } next }; /^DATE$/ and print($new_sock scalar(localtime), "\n"), next; print $new_sock "DEFAULT\n"; } close $new_sock; }

        Client:

        #!/usr/bin/perl -w use strict; use IO::Socket; my $host = shift || 'localhost'; my $port = shift || 7890; my $sock = new IO::Socket::INET( PeerAddr => $host, PeerPort => $port, Proto => 'tcp'); $sock or die "no socket :$!"; my %handler = ( Name => \&name, Password => \&pass, ); sub show_reply_and_next { do { $_=<$sock>; print unless /Enter $/; } until /^Enter (\w*)/; # $1 holds what to do next if ($handler{$1}) { $handler{$1}->(); } # else just return } sub hello { print $sock "HELLO\n"; show_reply_and_next(); } sub name { my $name = <STDIN>; print $sock "NAME: $name"; show_reply_and_next(); } sub pass { my $pass = <STDIN>; print $sock "PASS: $pass"; show_reply_and_next(); } ### hello(); # initiates login print $sock "DATE\n"; print scalar <$sock>; print $sock "NONE\n"; print scalar <$sock>; close $sock;

        Sample session:

        $ ./827156_client.pl Enter Name: toooooooo_long Invalid name: 'toooooooo_long' Enter Name: Invalid name: '' Enter Name: almut Ok, name: almut Enter Password: foobar Login ok Sun Mar 7 01:10:53 2010 DEFAULT

        It's maybe worth noting that in real life the server should not allow access to any "protected" resources unless the login has successfully been completed. The idea being that a client program with possibly manipulated logic of flow will have no chance to bypass security checks.  This is not implemented so far — the code just sets $auth (which should then be used for subsequent checks).

        If you didn't do the checking on the server side, you would have a big security hole. An attacker could just eliminate any checks in your client program or write his own client without any checking of a password.

        This would be similar to a security guard asking someone who wants to enter: "Do you know the password? Is it the correct one?" and when he gets the answer "Yes and yes" to let him in.