Go Premium for a chance to win a PS4. Enter to Win

  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 597
  • Last Modified:

Perl Socket Problem

I want to be able to change my passwd on the server "ebs2.eecs" from my machine "ebs1.eecs" using sockets.
ebs2.eecs has an entry for the program under /etc/services and /etc/inetd.conf. On calling the program from ebs1.eecs I do get Testing.. as the output but nothing further happens.

1) What do I need to do to get this to work?
2) How come I don't get an output for $name when I call the
   client prog. with an output?

The client program that runs on my machine "ebs1.eecs"

# passwd.client

$AF_INET = 2;
$sockaddr = 'S n a4 x8';

chop ($user = `whoami`) unless $user;

$port = 1997;
$host = 'ebs2.eecs';

($n, $a, $proto) = getprotobyname('tcp');
($n, $a, $t, $l, @thisaddr) = gethostbyname('localhost');
($n, $a, $t, $l, @thataddr) = gethostbyname($host);

$this = pack($sockaddr, $AF_INET, 0, $thisaddr);
$that = pack($sockaddr, $AF_INET, $port, @thataddr);

socket(S, $AF_INET, $SOCK_STREAM, $proto) || die "Can't open socket!\n";
bind(S, $this) || die "Couldn't bind a new socket!\n";
connect(S, $that) || die "Couldn't connect to host $host, port $port!\n";

select(S); $| = 1; select(STDOUT);

print S "$user\n";  ##Pass the user to the server                                  passwd.answerd prog

while (<S>) {

The server prog on "ebs2.eecs"

# passwd.server

$ENV{'PATH'} = '/bin:/etc:/etc/bin:/usr/bin:/usr/ucb:/usr/local/bin';
print "Testing.. \n" ;
local($name) = @_ ;

$AF_INET = 2;
$sockaddr = 'S n a4 x8';

$port = 1997;

($n, $a, $t, $l, @thisaddr) = gethostbyname($host);
($n, $a, $proto) = getprotobyname('tcp');
$thisport = pack($sockaddr,$AF_INET,$port,$thisaddr);
socket(S, $AF_INET, $SOCK_STREAM, $proto) || die "Can't open socket!";
bind(S,$thisport) || die "Cannot bind socket \n" ;
listen(S,5) || die "Cannot listen socket \n" ;
  accept(NS,S) || die "Cannot accept socket\n" ;
  system "/bin/passwd" ;
  close NS;

  • 2
1 Solution
The first thing I would recommend doing is changing the
socket handling techniques you are using to use the Socket
module provided with perl5.002 and higher.

# Sample code to get the server operating (assumes
# perl5.002 is installed at /usr/local/bin/perl):


require 5.002;
use Socket;
use Fcntl;
use POSIX;

$port = 1997;
$proto = getprotobyname('tcp');

# Open the socket, set socket options for reuse

socket(Server, PF_INET, SOCK_STREAM, $proto) or die "socket: $!";
setsockopt(Server, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) or die "setsockopt: $!";
setsockopt(Server, SOL_SOCKET, SO_LINGER, 0);

# bind socket, allowing 1 connection at a time, set
# non-blocking io

bind(Server, sockaddr_in($port, INADDR_ANY)) or die "bind: $!";
fcntl(Server, F_SETFL, O_NONBLOCK);
listen(Server, 1) or die "listen: $!";

for (; $paddr = accept(Client,Server); close Client){

   # Ok, the server has a connection which can now
   # be referenced as the filehandle Client.
   # Expect input in the form: user tab pwd
   # Warning: You will likely need to strip some
   # extra characters found at the end of the string.
   # You can do this by checking for non-null characters.

   my ($input) = <Client>;
   my ($user, $pwd) = (split("\t", $input));

   # You will likely need to reorganize the way you
   # interact wth the passwd command, see below.  
   # after executing pwd command the for loop closes
   # the connection.

# Sample Code for Interacting with passwd command

open(PWD, "|/bin/passwd $user");

   # You know what the password command prompts for
   # and you need to print to it in the order that
   # it requires.

   print PWD "${pwd}\n"; # Password to change to
   print PWD "${pwd}\n"; # Verification of new password

# Sample Client Code. Assumes same perl call and
# same same modules used

$remote  = "hostname.ext";
$port    = 1997
$iaddr   = inet_aton($remote) || return 0;
$paddr   = sockaddr_in($port, $iaddr);

# Open the scket
socket(SOCK, PF_INET, SOCK_STREAM, $proto);  
# Call up the server
connect(SOCK, $paddr);

   # Print the user name and password, tab-delim
   # The server should expect to read a string, tab, then
   # string.

   print "${user}\t${pwd}\n";

   # The server will close the connection when it is
   # done, this should end the while loop.

This should give you a basis for converting to the
Socket module, it is much better than using the old
socket handling techniques, which do not work on all
platforms. This example also shows you how to interact
with the password command through a named pipe. This is
also a good way of handling this...

Hope this helps!
monishAuthor Commented:
I tried your example. I see the server process start up at
the server "ebs2" and get some error checking output on "ebs1".  
The following is the output I get back on ebs1 when I run the program :

bind: Address already in useCHECK C4

So it never goes pass the following line on the server :
bind(Server, $port)                     or die "bind: $!" ;

Any clues on how I can get the client and Server to talk and fix this ? Thanks.

The client code on ebs1 :

require 5.002;
use strict;
use Socket;
use Fcntl;
use POSIX;

my $user = `whoami` ;
my ($remote,$port,$iaddr,$paddr,$proto,$line);

$remote = "ebs2.eecs.berkeley.edu";
$port = 1995 ;

$iaddr = inet_aton($remote) or print "ERROR1\n" ;
$paddr = sockaddr_in($port, $iaddr);

# Open the socket
$proto = getprotobyname('tcp') ;
socket(SOCK, PF_INET, SOCK_STREAM, $proto) or die "Could'nt create socket";

# Call up the server
connect(SOCK, $paddr) or die "Couldn't connect socket";

print "CHECK C1\n" ;
##select(SOCK); $| = 1; select(STDOUT);
print "CHECK C2\n" ;

    print "CHECK C3\n" ;
    print ;
print "CHECK C4\n" ;
close (SOCK) || die "Couldn't close socket\n";
exit ;

The server code on ebs2 :

require 5.002;
use strict;
use Socket;
use Carp;
use Fcntl;
use POSIX;

print "CHECK S1\n" ;
my $port = 1995;
my $proto = getprotobyname('tcp');

socket(Server, PF_INET, SOCK_STREAM, $proto) or die "socket: $!" ;
print "CHECK S2\n" ;
setsockopt(Server, SOL_SOCKET, SO_REUSEADDR, pack("1",1))       or die "setsockopt: $!";
print "CHECK S3\n" ;
#bind(Server,sockaddr_in($port,INADDR_ANY)) or print "bind: $!" ;
bind(Server, $port)                     or die "bind: $!" ;
print "CHECK S4\n" ;
listen(Server,SOMAXCONN)                or die "listen: $!" ;

my $paddr;
for ( ; $paddr = accept(Client,Server); close Client)
  my($port, $iaddr) = sockaddr_in($paddr);
  my $name = gethostbyaddr($iaddr,AF_INET);
  print Client "Hello there, $name \n";


Ok, the bind: Address already in use indicates that either
the port you are using is already bound by another program,
or it is bound by the current program but has not been.

For example, if you run the program and then kill it, the
port will remain bound for a few minutes. I have this problem
with a proxy server I use, it takes about 5 full minutes
before I can run it again after the server dies. Give it
some time and then try again. I know this sucks and is quite
a hassle when debugging but it happens... *sigh*


Featured Post


Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now