Article 7068 of comp.lang.perl:
Xref: feenix.metronet.com comp.lang.perl:7068
Path: feenix.metronet.com!news.ecn.bgu.edu!usenet.ins.cwru.edu!agate!spool.mu.edu!sgiblab!idiom.berkeley.ca.us!idiom.berkeley.ca.us!not-for-mail
From: [email protected] (David Muir Sharnoff)
Newsgroups: comp.lang.perl
Subject: Yet another friendly socket library + test code for Larry
Date: 21 Oct 1993 01:38:08 -0700
Organization: Idiom Consulting / Berkeley, CA
Lines: 394
Message-ID: <[email protected]>
NNTP-Posting-Host: idiom.berkeley.ca.us

I wanted to use udp sockets in perl and didn't see any nice
examples, so I made one.  In the process, I decided to make a
library for creating all sorts of sockets.

To test it, I built a Larry-style .t file.  Everything works,
except, I can't get return address from UNIX-domain sockets.

No change that -- it appears that there are anonymous
unix domain sockets.  If you bind() a unix domain socket, and then
connect with it, the address is available to the process you
connected to, but if you don't then it doesn't appear that
a return address is available.   This wouldn't matter except that
it means that when you use datagrams you can't always reply!

Sockets.pl will make udp, tcp, unix-stream, and unix-dgram sockets.

Have fun!

-Dave

#!/bin/sh
# shar: Shell Archiver  (v1.22)
#
#       Run the following text with /bin/sh to create:
#         sockets.pl
#         sockets.t
#
sed 's/^X//' << 'SHAR_EOF' > sockets.pl &&
X
X#  Copyright (c) 1993 David Muir Sharnoff
X#  License at bottom of file
X
Xpackage sockets;
X
X# hardcoded constants, should work fine for BSD-based systems
X$AF_UNIX = 1;
X$AF_INET = 2;
X$SOCK_STREAM = 1;
X$SOCK_DGRAM  = 2;
X$SOCKADDR_IP = 'S n a4 x8';
X$SOCKADDR_UN = 'S a108';
X
X#
X# &socket is a function that creates binds, and connects
X# sockets.
X#
X# Arguments:
X# $S   - the name of the socket, eg 'SOC'.  Use <SOC> elsewhere.
X# $type        - datagram (dgram) or stream.
X# $them        - the remote address (optional)
X# $us  - the local address (optional)
X#
X# Both $us and $them are in a flexible format.  If they look like a
X# unix path (begins with /) then it is assumed you want a unix-domain
X# socket.  Otherwise an IP socket is assumed.
X#
X# There is no default port number.   If you specify a $them IP address,
X# be sure to specify a port number.
X#
X# IP $us and $them are in the format "$hostname/$port".  A symbolic
X# port name will be looked up.
X#
X
Xsub main'socket
X{
X       local($S,$type,$them,$us) = @_;
X       local($t,$ip);
X
X       if ("\L$type" eq 'stream' || "\L$type" eq 'tcp' || $type == $SOCK_STREAM) {
X               $t = $SOCK_STREAM;
X               $ip = 'tcp';
X       } elsif ("\L$type" eq 'dgram' || "\L$type" eq 'udp' || $type == $SOCK_DGRAM) {
X               $t = $SOCK_DGRAM;
X               $ip = 'udp'
X       } else {
X               die "could not figure out socket type: $type";
X       }
X
X       if (($them =~ m,^/,) || ($us =~ m,^/,)) {
X               &unix_socket($S,$t,$them,$us);
X       } else {
X               &ip_socket($S,$t,$ip,$them,$us);
X       }
X}
X
Xsub unix_socket
X{
X       local($S,$type,$them,$us) = @_;
X       local($us_struct,$them_struct);
X
X       print "unix socket $type, $them, $us\n" if $debug;
X       socket($S, $AF_UNIX, $t, 0)
X           || die "socket: $!";
X
X       if ($us) {
X               $us_struct = pack($SOCKADDR_UN, $AF_UNIX, $us);
X               bind($S, $us_struct) || die "bind unix socket $us: $!";
X       }
X       if ($them) {
X               $them_struct = pack($SOCKADDR_UN, $AF_UNIX, $them);
X               connect($S, $them_struct) || die "connect unix socket $them: $!";
X       }
X       select((select($S),$| = 1)[0]); # don't buffer output
X}
X
Xsub ip_socket
X{
X       local($S,$type,$protocol,$them,$us) = @_;
X
X       local($their_port,$their_host);
X
X       local($our_addr_struct) = &get_IP_addr_struct($protocol,$us);
X
X       socket($S, $AF_INET, $t, &get_proto_number($protocol))
X               || die "socket: $!";
X
X       print "us $protocol,$us,$them: ",&unpack_IP_addr_struct($our_addr_struct),"\n" if $debug;
X       bind($S, $our_addr_struct)
X               || die "bind $hostname,0: $!";
X
X       if ($them) {
X               local($their_addr_struct) = &get_IP_addr_struct($protocol,$them);
X               print "them $protocol,$us,$them: ",&unpack_IP_addr_struct($their_addr_struct),"\n" if $debug;
X               connect($S, $their_addr_struct)
X                       || die "connect $host: $!";
X       }
X       select((select($S),$| = 1)[0]); # don't buffer output
X}
X
X#
X# Create IP address structures.
X#
X# The first argument must be 'tcp', or 'udp'.
X# The second argument is the host (`hostname` if null) to connect to.
X# The third argument is the port to bind to.   Pass 0 if any will do.
X#
X# The return arguments are a protocol value that can use by socket()
X# and a port address that can be used by bind().
X#
Xsub get_IP_addr_struct
X{
X       local($protocol,$host,$port) = @_;
X       local($junk,$host_addr);
X
X       if (! $port && ($host =~ s,([^/]+)/(.+),$1,)) {
X               $port = $2;
X       }
X       $host = &hostname()
X               if ! $host;
X       ($junk,$junk,$junk,$junk,$host_addr) = gethostbyname($host);
X
X       die "gethostbyname($host): $!"
X               unless $host_addr;
X
X       if ($port =~ /[^\d]/) {
X               ($junk,$junk,$port) = getservbyname($port,$protocol);
X               die "getservbyname($port,$protocol): $!"
X                       unless $port;
X       }
X
X       return pack($SOCKADDR_IP, $AF_INET, $port, $host_addr);
X}
X
Xsub get_proto_number
X{
X       local($protocol) = @_;
X       local($junk,$proto);
X
X       ($junk,$junk,$proto) = getprotobyname($protocol);
X
X       die "getprotobyname($protocol): $!"
X               unless $proto;
X
X       return $proto;
X}
X
Xsub hostname
X{
X       if (! $hostname) {
X               chop($hostname = `hostname`);
X               if (! $hostname) {
X                       chop($hostname = `uname -n`);
X                       if (! $hostname) {
X                               die "cannot determine hostname";
X                       }
X               }
X       }
X       return $hostname;
X}
X
X#
X# An extra...
X#
X
Xsub unpack_IP_addr_struct
X{
X       local($addr) = @_;
X       local($af,$port,$host) = unpack($SOCKADDR_IP,$addr);
X       local(@IP) = unpack('C4',$host);
X       return join('.',@IP)."/$port";
X}
X
X#############################################################################
X#
X#  Copyright (c) 1993 David Muir Sharnoff
X#  All rights reserved.
X#
X#  Redistribution and use in source and binary forms, with or without
X#  modification, are permitted provided that the following conditions
X#  are met:
X#  1. Redistributions of source code must retain the above copyright
X#     notice, this list of conditions and the following disclaimer.
X#  2. Redistributions in binary form must reproduce the above copyright
X#     notice, this list of conditions and the following disclaimer in the
X#     documentation and/or other materials provided with the distribution.
X#  3. All advertising materials mentioning features or use of this software
X#     must display the following acknowledgement:
X#       This product includes software developed by the David Muir Sharnoff.
X#  4. The name of David Sharnoff may not be used to endorse or promote products
X#     derived from this software without specific prior written permission.
X#
X#  THIS SOFTWARE IS PROVIDED BY THE DAVID MUIR SHARNOFF ``AS IS'' AND
X#  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X#  ARE DISCLAIMED.  IN NO EVENT SHALL DAVID MUIR SHARNOFF BE LIABLE
X#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X#  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X#  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X#  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X#  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X#  SUCH DAMAGE.
X#
X# This copyright notice derrived from material copyrighted by the Regents
X# of the University of California.
X#
X# Contributions accepted.
X#
X#############################################################################
SHAR_EOF
chmod 0644 sockets.pl || echo "restore of sockets.pl fails"
sed 's/^X//' << 'SHAR_EOF' > sockets.t &&
X#!/usr/local/bin/perl
X
Xpackage sockets;
X
X$debug = 0;
X
Xrequire "sockets.pl";
X
X$random_port = 8223;
X
X$uport = "/tmp/uss$$";
X$sready = "/tmp/sready$$";
X
X$h = &hostname();
X
X$| = 1;
X
X$sig{ALRM} = 'death';
Xsub death
X{
X       print "not ok 100\n";
X       die;
X}
X
Xif (fork()) {
X       alarm(200);
X       &tcp_server();
X       &udp_server();
X       &unix_stream_server();
X       &unix_dgram_server();
X       wait();
X} else {
X       alarm(200);
X       &tcp_client();
X       &udp_client();
X       &unix_stream_client();
X       &unix_dgram_client();
X}
X
Xsub udp_server
X{
X       &main'socket(ST,UDP,"","$h/$random_port");
X       symlink(".",$sready);
X       $their_addr = recv(ST,$x,1024,0);
X       print ($x eq "Client here\n" ? "ok 3\n" : "not ok 3\n");
X       send(ST,"Server here\n",0,$their_addr);
X       close(ST);
X}
X
Xsub udp_client
X{
X       1 while (! -l $sready);
X       unlink($sready);
X
X       &main'socket(CT,UDP,"$h/$random_port","");
X       print CT "Client here\n";
X       $x = <CT>;
X       print ($x eq "Server here\n" ? "ok 4\n" : "not ok 4\n");
X       close(CT);
X}
X
Xsub tcp_server
X{
X       &main'socket(ST,TCP,"","$h/$random_port");
X       listen(ST,5) || die "listen: $!";
X       symlink(".",$sready);
X       ($their_addr = accept(NST,ST)) || die "accept: $!";
X       print &unpack_IP_addr_struct($their_addr),"\n" if $debug;
X       select((select(NST),$| = 1)[0]); # don't buffer output
X       print NST "Server here\n";
X       $x = <NST>;
X       print ($x eq "Client here\n" ? "ok 2\n" : "not ok 2\n");
X       close(NST);
X       close(ST);
X}
X
Xsub tcp_client
X{
X       1 while (! -l $sready);
X       unlink($sready);
X
X       &main'socket(CT,TCP,"$h/$random_port","");
X       $x = <CT>;
X       print ($x eq "Server here\n" ? "ok 1\n" : "not ok 1\n");
X       print CT "Client here\n";
X       close(CT);
X}
X
Xsub unix_stream_server
X{
X       &main'socket(ST,STREAM,"",$uport);
X       listen(ST,5) || die "listen: $!";
X       symlink(".",$sready);
X       ($their_addr = accept(NST,ST)) || die "accept: $!";
X       if ($debug) {
X               ($junk,$their_path) = unpack($SOCKADDR_UN,$their_addr);
X               print "Their address: $their_path\n";
X       }
X       select((select(NST),$| = 1)[0]); # don't buffer output
X       print NST "Server here\n";
X       $x = <NST>;
X       print ($x eq "Client here\n" ? "ok 6\n" : "not ok 6\n");
X       close(NST);
X       close(ST);
X       unlink($uport);
X}
X
Xsub unix_stream_client
X{
X       1 while (! -l $sready);
X       unlink($sready);
X
X       &main'socket(CT,STREAM,$uport,"");
X       $x = <CT>;
X       print ($x eq "Server here\n" ? "ok 5\n" : "not ok 5\n");
X       print CT "Client here\n";
X       close(CT);
X}
X
Xsub unix_dgram_server
X{
X       &main'socket(ST,DGRAM,"",$uport);
X       symlink(".",$sready);
X       $their_addr = recv(ST,$x,1024,0);
X       if ($debug) {
X               ($junk,$their_path) = unpack($SOCKADDR_UN,$their_addr);
X               print "Their address: $their_path\n";
X       }
X       print "their-addr: $their_addr\n" if $debug;
X       print ($x eq "Client here\n" ? "ok 7\n" : "not ok 7\n");
X       send(ST,"Server here\n",0,$their_addr);
X       close(ST);
X       unlink($uport);
X}
X
Xsub unix_dgram_client
X{
X       1 while (! -l $sready);
X       unlink($sready);
X
X       &main'socket(CT,DGRAM,$uport,"/tmp/us2.$$");
X       print CT "Client here\n";
X       $x = <CT>;
X       print ($x eq "Server here\n" ? "ok 8\n" : "not ok 8\n");
X       close(CT);
X       unlink("/tmp/us2.$$");
X}
X
SHAR_EOF
chmod 0755 sockets.t || echo "restore of sockets.t fails"
exit 0