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: muir@idiom.berkeley.ca.us (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: <2a5hpg$v7@idiom.berkeley.ca.us> 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 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 = ; 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 = ; 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 = ; 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 = ; 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 = ; 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 = ; 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 .