home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / perl_ste.zip / LWP / SecureSocket.pm < prev    next >
Text File  |  1997-08-05  |  10KB  |  445 lines

  1. # $Id: SecureSocket.pm,v 1.1 1997/08/05 14:09:11 aas Exp $
  2. #
  3. # Derived by Joshua Kronengold from Socket.pm and SSLeay
  4. #
  5.  
  6.  
  7. require Socket;
  8. require Net::SSLeay;
  9.  
  10.  
  11. package LWP::SecureSocket;
  12.  
  13. =head1 NAME
  14.  
  15. LWP::SecureSocket - SSL TCP/IP socket interface
  16.  
  17. =head1 SYNOPSIS
  18.  
  19.  $socket = new LWP::SecureSocket;
  20.  $socket->connect('localhost', 7); # echo
  21.  $quote = 'I dunno, I dream in Perl sometimes...';
  22.  $socket->write("$quote\n");
  23.  $socket->read_until("\n", \$buffer);
  24.  $socket->read(\$buffer);
  25.  $socket = undef;  # close
  26.  
  27. =head1 DESCRIPTION
  28.  
  29. This class implements TCP/IP sockets.  It groups socket generation,
  30. TCP address manipulation and buffered reading. Errors are handled by
  31. dying (throws exceptions).
  32.  
  33. This class should really not be required, something like this should
  34. be part of the standard Perl5 library.
  35.  
  36. Running this module standalone executes a self test which requires
  37. localhost to serve chargen and echo protocols.
  38.  
  39. =head1 METHODS
  40.  
  41. =cut
  42.  
  43.  
  44. $VERSION = sprintf("%d.%02d", q$Revision: 1.1 $ =~ /(\d+)\.(\d+)/);
  45. sub Version { $VERSION; }
  46.  
  47. use Socket qw(pack_sockaddr_in unpack_sockaddr_in
  48.           PF_INET SOCK_STREAM INADDR_ANY
  49.           inet_ntoa inet_aton);
  50. Socket->require_version(1.5);
  51.  
  52. use Carp ();
  53. use Symbol qw(gensym);
  54.  
  55. use LWP::Debug ();
  56. use LWP::IO ();
  57. # use Socket;
  58. use Net::SSLeay;
  59.  
  60.  
  61. my $tcp_proto = (getprotobyname('tcp'))[2];
  62.  
  63.  
  64. =head2 $sock = new LWP::SecureSocket()
  65.  
  66. Constructs a new socket object.
  67.  
  68. =cut
  69.  
  70. sub new
  71. {
  72.     my($class, $socket, $host, $port) = @_;
  73.  
  74.     unless ($socket) {
  75.     $socket = gensym();
  76.     LWP::Debug::debug("SecureSocket $socket");
  77.  
  78.     socket($socket, PF_INET, SOCK_STREAM, $tcp_proto) or
  79.       Carp::croak("socket: $!");
  80.     }
  81.  
  82.     my $self = bless {
  83.     'socket' => $socket,
  84.     'host'   => $host,
  85.     'port'   => $port,
  86.     'buffer' => '',
  87.     'size'   => 4096,
  88.     }, $class;
  89.  
  90.     $self;
  91. }
  92.  
  93. sub DESTROY
  94. {
  95.     my $socket = shift->{'socket'};
  96.     close($socket);
  97. }
  98.  
  99. sub host { shift->{'host'}; }
  100. sub port { shift->{'port'}; }
  101.  
  102.  
  103. =head2 $sock->connect($host, $port)
  104.  
  105. Connect the socket to given host and port.
  106.  
  107. =cut
  108.  
  109. sub connect
  110. {
  111.     my($self, $host, $port) = @_;
  112.     Carp::croak("no host") unless defined $host && length $host;
  113.     Carp::croak("no port") unless defined $port && $port > 0;
  114.  
  115.     LWP::Debug::trace("($host, $port)");
  116.  
  117.     $self->{'host'} = $host;
  118.     $self->{'port'} = $port;
  119.  
  120.     my @addr = $self->_getaddress($host, $port);
  121.     Carp::croak("Can't resolv address for $host")
  122.       unless @addr;
  123.     $socket=$self->{'socket'};
  124.     LWP::Debug::debug("Connecting to host '$host' on port '$port'...");
  125.     for (@addr) {
  126.     if (connect($self->{'socket'}, $_)) 
  127.     {
  128. #        print STDERR "\nCreating context....".
  129. #        "(connected socket($self->{'socket'}) to \n$host, $port, and ".
  130. #            "socket is ".."\n";
  131.         my $ctx= Net::SSLeay::CTX_new() or 
  132.           Carp::croak "Failed to create SSL_CTX $!";
  133.         $self->{'context'}=$ctx;
  134.         $self->{SSL} = Net::SSLeay::new($ctx) or 
  135.           Carp::croak "Failed to create SSL $!";
  136.         ($err=Net::SSLeay::set_fd($self->{SSL}, ($fn=fileno($socket)))) or 
  137.         (Carp::croak "Can't set_fd somehow $!"); 
  138. #        print STDERR "\nConnecting....fileno=($fn)\n";
  139.         my $res= Net::SSLeay::connect($self->{SSL});
  140. #        print STDERR "result of connect: ($res)\n";
  141.         return;
  142.     }
  143.     }
  144.     Carp::croak("Could not connect to $host:$port");
  145. }
  146.  
  147.  
  148. =head2 $sock->shutdown()
  149.  
  150. Shuts down the connection.
  151.  
  152. =cut
  153.  
  154. sub shutdown
  155. {
  156.     my($self, $how) = @_;
  157.     $how = 2 unless defined $how;
  158.     shutdown($self->{'socket'}, $how);
  159.     delete $self->{'host'};
  160.     delete $self->{'port'};
  161. }
  162.  
  163.  
  164. =head2 $sock->bind($host, $port)
  165.  
  166. Binds a name to the socket.
  167.  
  168. =cut
  169.  
  170. sub bind
  171. {
  172.     my($self, $host, $port) = @_;
  173.     my $name = $self->_getaddress($host, $port);
  174.     bind($self->{'socket'}, $name);
  175. }
  176.  
  177.  
  178. =head2 $sock->listen($queuesize)
  179.  
  180. Set up listen queue for socket.
  181.  
  182. =cut
  183.  
  184. sub listen
  185. {
  186.     listen(shift->{'socket'}, @_);
  187. }
  188.  
  189.  
  190. =head2 $sock->accept($timeout)
  191.  
  192. Accepts a new connection.  Returns a new LWP::SecureSocket object if successful.
  193. Timeout not implemented yet.  would require modifying new, and not
  194.     necessary for app, so I\'m not bothering to modify.  Might work
  195. anyways, though.
  196.  
  197. =cut
  198.  
  199. sub accept
  200. {
  201.     my $self = shift;
  202.     my $timeout = shift;
  203.     my $ns = gensym();
  204.     my $addr = accept($ns, $self->{'socket'});
  205.     if ($addr) {
  206.     my($port, $addr) = unpack_sockaddr_in($addr);
  207.     return new LWP::SecureSocket $ns, inet_ntoa($addr), $port;
  208.     } else {
  209.     Carp::croak("Can't accept: $!");
  210.     }
  211. }
  212.  
  213.  
  214. =head2 $sock->getsockname()
  215.  
  216. Returns a 2 element array ($host, $port)
  217.  
  218. =cut
  219.  
  220. sub getsockname
  221. {
  222.     my($port, $addr) = unpack_sockaddr_in(getsockname(shift->{'socket'}));
  223.     (inet_ntoa($addr), $port);
  224. }
  225.  
  226.  
  227. =head2 $sock->read_until($delim, $data_ref, $size, $timeout)
  228.  
  229. Reads data from the socket, up to a delimiter specified by a regular
  230. expression.  If $delim is undefined all data is read.  If $size is
  231. defined, data will be read internally in chunks of $size bytes.  This
  232. does not mean that we will return the data when size bytes are read.
  233.  
  234. Note that $delim is discarded from the data returned.
  235.  
  236. =cut
  237.  
  238. sub read_until
  239. {
  240.     my ($self, $delim, $data_ref, $size, $timeout) = @_;
  241.  
  242.     {
  243.     my $d = $delim;
  244.     $d =~ s/\r/\\r/g;
  245.     $d =~ s/\n/\\n/g;
  246.     LWP::Debug::trace("('$d',...)");
  247.     }
  248.  
  249.     my $socket = $self->{'socket'};
  250.     my $ssl = $self->{SSL};
  251.     $delim = '' unless defined $delim;
  252.     $size ||= $self->{'size'};
  253.  
  254.     my $buf = \$self->{'buffer'};
  255.  
  256.     if (length $delim) {
  257.     while ($$buf !~ /$delim/) {
  258.         my $data=Net::SSLeay::read($ssl) or die "Unexpected EOF 1";     
  259.         $$buf .=$data;
  260.         
  261. #        LWP::IO::read($socket, $$buf, $size, length($$buf), $timeout)
  262. #        or die "Unexpected EOF";
  263.     }
  264.     ($$data_ref, $self->{'buffer'}) = split(/$delim/, $$buf, 2);
  265.     } else {
  266.     $data_ref = $buf;
  267.     $self->{'buffer'} = '';
  268.     }
  269.  
  270.     1;
  271. }
  272.  
  273.  
  274. =head2 $sock->read($bufref, [$size, $timeout])
  275.  
  276. Reads data of the socket.  Not more than $size bytes.  Might return
  277. less if the data is available.  Dies on timeout.
  278.  
  279. =cut
  280.  
  281. sub read
  282. {
  283.     my($self, $data_ref, $size, $timeout) = @_;
  284.     $size ||= $self->{'size'};
  285.  
  286.     LWP::Debug::trace('(...)');
  287.     if (length $self->{'buffer'}) {
  288.     # return data from buffer until it is empty
  289.     #print "Returning data from buffer...$self->{'buffer'}\n";
  290.     $$data_ref = substr($self->{'buffer'}, 0, $size);
  291.     substr($self->{'buffer'}, 0, $size) = '';
  292.     return length $$data_ref;
  293.     }
  294.     
  295.     my $ssl = $self->{SSL};
  296. #    print STDERR "reading...";
  297.     (my $data=Net::SSLeay::read($ssl));
  298. #    print STDERR "read ($data)\n";
  299.     $$data_ref.=$data;
  300.     return(length $data);
  301. }
  302.  
  303.  
  304. =head2 $sock->pushback($data)
  305.  
  306. Put data back into the socket.  Data will returned next time you
  307. read().  Can be used if you find out that you have read too much.
  308.  
  309. =cut
  310.  
  311. sub pushback
  312. {
  313.     LWP::Debug::trace('(' . length($_[1]) . ' bytes)');
  314.     my $self = shift;
  315.     substr($self->{'buffer'}, 0, 0) = shift;
  316. }
  317.  
  318.  
  319. =head2 $sock->write($data, [$timeout])
  320.  
  321. Write data to socket.  The $data argument might be a scalar or code.
  322.  
  323. If data is a reference to a subroutine, then we will call this routine
  324. to obtain the data to be written.  The routine will be called until it
  325. returns undef or empty data.  Data might be returned from the callback
  326. as a scalar or as a reference to a scalar.
  327.  
  328. Write returns the number of bytes written to the socket.
  329.  
  330. =cut
  331.  
  332. sub write
  333. {
  334.     my $self = shift;
  335.     my $ssl = $self->{SSL};
  336.  
  337.     my $timeout = $_[1];  # we don't want to copy data in $_[0]
  338.     LWP::Debug::trace('()');
  339.     my $bytes_written = 0;
  340.     if (!ref $_[0]) {
  341.     my $got = Net::SSLeay::write($ssl,  $_[0]);
  342.     $bytes_written=length $_[0] if $got;
  343. #    $bytes_written = LWP::IO::write($self->{'socket'}, $_[0], $timeout);
  344.     } elsif (ref($_[0]) eq 'CODE') {
  345.     # write data until $callback returns empty data '';
  346.     my $callback = shift;
  347.     while (1) {
  348.         my $data = &$callback;
  349.         last unless defined $data;
  350.         my $dataRef = ref($data) ? $data : \$data;
  351.         my $len = length $$dataRef;
  352.         last unless $len;
  353.         my $n = $self->write($$dataRef, $timeout);
  354.         $bytes_written += $n;
  355.         last if $n != $len;
  356.     }
  357.     } else {
  358.     Carp::croak('Illegal LWP::SecureSocket->write() argument');
  359.     }
  360.     $bytes_written;
  361. }
  362.  
  363.  
  364.  
  365. =head2 _getaddress($h, $p)
  366.  
  367. Given a host and a port, it will return the address (sockaddr_in)
  368. suitable as the C<name> argument for connect() or bind(). Might return
  369. several addresses in array context if the hostname is bound to several
  370. IP addresses.
  371.  
  372. =cut
  373.  
  374.  
  375. sub _getaddress
  376. {
  377.     my($self, $host, $port) = @_;
  378.  
  379.     my(@addr);
  380.     if (!defined $host) {
  381.     # INADDR_ANY
  382.     $addr[0] = pack_sockaddr_in($port, INADDR_ANY);
  383.     }
  384.     elsif ($host =~ /^(\d+\.\d+\.\d+\.\d+)$/) {
  385.     # numeric IP address
  386.     $addr[0] = pack_sockaddr_in($port, inet_aton($1));
  387.     } else {
  388.     # hostname
  389.     LWP::Debug::debug("resolving host '$host'...");
  390.     (undef,undef,undef,undef,@addr) = gethostbyname($host);
  391.     for (@addr) {
  392.         LWP::Debug::debug("   ..." . inet_ntoa($_));
  393.         $_ = pack_sockaddr_in($port, $_);
  394.     }
  395.     }
  396.     wantarray ? @addr : $addr[0];
  397. }
  398.  
  399.  
  400. #####################################################################
  401.  
  402. package main;
  403.  
  404. eval join('',<DATA>) || die $@ unless caller();
  405.  
  406. =head1 SELF TEST
  407.  
  408. This self test is only executed when this file is run standalone. It
  409. tests its functions against some standard TCP services implemented by
  410. inetd. If you do not have them around the tests will fail.
  411.  
  412. =cut
  413.  
  414. 1;
  415.  
  416. __END__
  417.  
  418. LWP::Debug::level('+');
  419.  
  420. &chargen;
  421. &echo;
  422. print "SecureSocket.pm $LWP::SecureSocket::VERSION ok\n";
  423.  
  424. sub chargen
  425. {
  426.     my $socket = new LWP::SecureSocket;
  427.     $socket->connect('localhost', 19); # chargen
  428.     $socket->read_until('A', \$buffer, 8);
  429.  
  430.     die 'Read Error' unless $buffer eq ' !"#$%&\'()*+,-./0123456789:;<=>?@';
  431.     $socket->read_until('Z', \$buffer, 8);
  432.     die 'Read Error' unless $buffer eq 'BCDEFGHIJKLMNOPQRSTUVWXY';
  433. }
  434.  
  435. sub echo
  436. {
  437.     $socket = new LWP::SecureSocket;
  438.     $socket->connect('localhost', 7); # echo
  439.     $quote = 'I dunno, I dream in Perl sometimes...';
  440.          # --Larry Wall in  <8538@jpl-devvax.JPL.NASA.GOV>
  441.     $socket->write("$quote\n");
  442.     $socket->read_until("\n", \$buffer);
  443.     die 'Read Error' unless $buffer eq $quote;
  444. }
  445.