brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (06/26/91)
This is clientserver 0.50, a set of three client-server suites supporting different protocols with the same well-defined interface. Two of the suites support the TCP and UNDOM protocols for BSD systems; one supports the NPIPE protocol for System V. (I'd love to hear from anyone with a System V more modern than a 3B2: Does NPIPE run on your machine?) All obey the UCSPI (UNIX Client-Server Program Interface) standard, so you can write programs that work exactly the same way on top of any of the protocols. You can even use these communications facilities inside shell scripts. Why do you want to use UCSPI-compliant communications tools? One advantage is instant portability. By working with the client-server model given here, you instantly make your programs usable over the Internet as well as on System V machines. Not only that, but your code will work with any future communications tools designed to fit the standard. Imagine supporting DECnet, AT&T's TLI, even Kerberos without lifting a finger! Another advantage is modularity: you can mix 'n' match protocols and applications without worrying which program supports which protocols. Finally, you can skip all those nasty steps of getting connection information from the operating system. No matter what the protocol is, UCSPI insists that you be given environment variables saying who you're talking to. UCSPI also mandates an often ignored aspect of client-server communications: a standard way to kill the server. You don't have to go searching through process lists or checking /etc/syslog.pid or sending HUPS left and right. You stop a server the same way you started it. You might set up a telnet daemon, for example, with this command: tcpserver 0 telnet sh -c '/usr/etc/in.telnetd <&6 >&7 6<&- 7>&-' & Here 0 refers to the local machine, and telnet is a port. To kill the server you just have to know the address: tcpkiller 0 telnet That's it. Everything here except the UCSPI-91 standard is in the public domain. Feel free to copy UCSPI as long as you don't modify it. Of course, UCSPI is just a document laying out some ideas; it's not a real standard until enough people decide it's worth supporting. Please let me know what you think! ---Dan Bernstein, brnstnd@nyu.edu #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 1 (of 2)." # Contents: BLURB DESIGN.npipe FILES FORMLETTER Makefile NPIPE README # TCP UNDOM authuser.c authuser.h dupdup.c dupdup.h env.c env.h # errno.h getopt.h hostname.c hostname.h malloc.h npipekiller.c # numeric.c numeric.h numfiles.h portname.c portname.h tcpkiller.c # testmain testresult testucspi undomclient.c undomkiller.c # username.c username.h # Wrapped by brnstnd@kramden on Tue Jun 25 18:41:38 1991 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'BLURB' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'BLURB'\" else echo shar: Extracting \"'BLURB'\" \(2255 characters\) sed "s/^X//" >'BLURB' <<'END_OF_FILE' XThis is clientserver 0.50, a set of three client-server suites Xsupporting different protocols with the same well-defined interface. Two Xof the suites support the TCP and UNDOM protocols for BSD systems; one Xsupports the NPIPE protocol for System V. (I'd love to hear from anyone Xwith a System V more modern than a 3B2: Does NPIPE run on your machine?) XAll obey the UCSPI (UNIX Client-Server Program Interface) standard, so Xyou can write programs that work exactly the same way on top of any of Xthe protocols. You can even use these communications facilities inside Xshell scripts. X XWhy do you want to use UCSPI-compliant communications tools? One Xadvantage is instant portability. By working with the client-server Xmodel given here, you instantly make your programs usable over the XInternet as well as on System V machines. Not only that, but your code Xwill work with any future communications tools designed to fit the Xstandard. Imagine supporting DECnet, AT&T's TLI, even Kerberos without Xlifting a finger! Another advantage is modularity: you can mix 'n' match Xprotocols and applications without worrying which program supports which Xprotocols. Finally, you can skip all those nasty steps of getting Xconnection information from the operating system. No matter what the Xprotocol is, UCSPI insists that you be given environment variables Xsaying who you're talking to. X XUCSPI also mandates an often ignored aspect of client-server Xcommunications: a standard way to kill the server. You don't have to go Xsearching through process lists or checking /etc/syslog.pid or sending XHUPS left and right. You stop a server the same way you started it. You Xmight set up a telnet daemon, for example, with this command: X X tcpserver 0 telnet sh -c '/usr/etc/in.telnetd <&6 >&7 6<&- 7>&-' & X XHere 0 refers to the local machine, and telnet is a port. To kill the Xserver you just have to know the address: X X tcpkiller 0 telnet X XThat's it. X XEverything here except the UCSPI-91 standard is in the public domain. XFeel free to copy UCSPI as long as you don't modify it. Of course, UCSPI Xis just a document laying out some ideas; it's not a real standard until Xenough people decide it's worth supporting. Please let me know what you Xthink! X X---Dan Bernstein, brnstnd@nyu.edu END_OF_FILE if test 2255 -ne `wc -c <'BLURB'`; then echo shar: \"'BLURB'\" unpacked with wrong size! fi # end of 'BLURB' fi if test -f 'DESIGN.npipe' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'DESIGN.npipe'\" else echo shar: Extracting \"'DESIGN.npipe'\" \(950 characters\) sed "s/^X//" >'DESIGN.npipe' <<'END_OF_FILE' XHere's the idea: We create DIR/l.username/service, a mode-000 file. We Xcreate s.username, a mode-600 named pipe. We then change l.username to Xmode 600 and open s.username for reading. This blocks. Note that a Xclient may start writing before we start reading, or vice versa. X XMain loop: The client locks l.username. When it receives the lock, it Xopens s.username for writing, then creates DIR/s2c.pid and DIR/c2s.pid, Xthen writes pid and its username, then closes s.username, then opens Xs2c.pid for reading (this blocks) and c2s.pid for writing (this blocks). XWe read pid and the username, then close and unlink s.username. We then Xfork. In the parent, we then create s.username, a mode-600 named pipe, Xthen open s.username for reading; this is back to the original state. In Xthe child, we open s2c.pid for writing and c2s.pid for reading, then Xwrite a byte to s2c.pid. The client reads that byte, unlinks s2c.pid and Xc2s.pid, and unlocks l.username. END_OF_FILE if test 950 -ne `wc -c <'DESIGN.npipe'`; then echo shar: \"'DESIGN.npipe'\" unpacked with wrong size! fi # end of 'DESIGN.npipe' fi if test -f 'FILES' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'FILES'\" else echo shar: Extracting \"'FILES'\" \(399 characters\) sed "s/^X//" >'FILES' <<'END_OF_FILE' XBLURB XREADME XFILES XFORMLETTER XUCSPI XTCP XUNDOM XNPIPE XDESIGN.npipe Xtestmain Xtestresult Xtestucspi XMakefile Xauthuser.h Xdupdup.h Xenv.h Xerrno.h Xgetopt.h Xhostname.h Xmalloc.h Xnumeric.h Xnumfiles.h Xportname.h Xusername.h Xauthuser.c Xdupdup.c Xenv.c Xhostname.c Xnpipeclient.c Xnpipeserver.c Xnpipekiller.c Xnumeric.c Xportname.c Xundomclient.c Xundomserver.c Xundomkiller.c Xusername.c Xtcpclient.c Xtcpserver.c Xtcpkiller.c END_OF_FILE if test 399 -ne `wc -c <'FILES'`; then echo shar: \"'FILES'\" unpacked with wrong size! fi # end of 'FILES' fi if test -f 'FORMLETTER' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'FORMLETTER'\" else echo shar: Extracting \"'FORMLETTER'\" \(612 characters\) sed "s/^X//" >'FORMLETTER' <<'END_OF_FILE' XTo: brnstnd@nyu.edu XFrom: XDate: X XPackage: clientserver 0.50 XObtained from (e.g., uunet.uu.net): XObtained by (e.g., ftp): X X1. Machine architecture (e.g., Sun 4/280): X2. Operating system (e.g., SunOS 4.1): X3. OS vendor (e.g., Sun): X4. Does tcp work on your machine so far (yes/no/didn't try)? X5. Does undom work on your machine so far (yes/no/didn't try)? X6. Does npipe work on your machine so far (yes/no/didn't try)? X7. What are CC and CCOPTS in your Makefile? X CC= X CCOPTS= X X8. Describe any problems you've had with clientserver. X X X9. Any further questions, comments, or suggestions? X X XYour name: END_OF_FILE if test 612 -ne `wc -c <'FORMLETTER'`; then echo shar: \"'FORMLETTER'\" unpacked with wrong size! fi # end of 'FORMLETTER' fi if test -f 'Makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Makefile'\" else echo shar: Extracting \"'Makefile'\" \(3109 characters\) sed "s/^X//" >'Makefile' <<'END_OF_FILE' XCC=cc XCCOPTS=-g -DUNDOMDIR=\"/usr/local/etc/undom\" -DNPIPEDIR=\"/usr/local/etc/npipe\" -DSIGRET=int XMACHOPTS= XLIBS=-lresolv # XXX: only necessary for tcp* X# X Xdefault: undom tcp npipe X Xundom: undomserver undomclient undomkiller X Xtcp: tcpserver tcpclient tcpkiller X Xnpipe: npipeserver npipeclient npipekiller X Xnpipekiller: npipekiller.o dupdup.o username.o numeric.o env.o X $(CC) $(CCOPTS) $(MACHOPTS) -o npipekiller npipekiller.o dupdup.o username.o numeric.o env.o $(LIBS) X Xnpipeserver: npipeserver.o dupdup.o username.o numeric.o env.o X $(CC) $(CCOPTS) $(MACHOPTS) -o npipeserver npipeserver.o dupdup.o username.o numeric.o env.o $(LIBS) X Xnpipeclient: npipeclient.o dupdup.o username.o numeric.o env.o X $(CC) $(CCOPTS) $(MACHOPTS) -o npipeclient npipeclient.o dupdup.o username.o numeric.o env.o $(LIBS) X Xundomkiller: undomkiller.o dupdup.o username.o numeric.o env.o X $(CC) $(CCOPTS) $(MACHOPTS) -o undomkiller undomkiller.o dupdup.o username.o numeric.o env.o $(LIBS) X Xundomserver: undomserver.o dupdup.o username.o numeric.o env.o X $(CC) $(CCOPTS) $(MACHOPTS) -o undomserver undomserver.o dupdup.o username.o numeric.o env.o $(LIBS) X Xundomclient: undomclient.o dupdup.o username.o numeric.o env.o X $(CC) $(CCOPTS) $(MACHOPTS) -o undomclient undomclient.o dupdup.o username.o numeric.o env.o $(LIBS) X Xtcpkiller: tcpkiller.o username.o numeric.o portname.o hostname.o env.o X $(CC) $(CCOPTS) $(MACHOPTS) -o tcpkiller tcpkiller.o username.o numeric.o portname.o hostname.o env.o $(LIBS) X Xtcpserver: tcpserver.o dupdup.o username.o numeric.o authuser.o portname.o hostname.o env.o X $(CC) $(CCOPTS) $(MACHOPTS) -o tcpserver tcpserver.o dupdup.o username.o numeric.o authuser.o portname.o hostname.o env.o $(LIBS) X Xtcpclient: tcpclient.o dupdup.o username.o numeric.o authuser.o portname.o hostname.o env.o X $(CC) $(CCOPTS) $(MACHOPTS) -o tcpclient tcpclient.o dupdup.o username.o numeric.o authuser.o portname.o hostname.o env.o $(LIBS) X Xnpipekiller.o: npipekiller.c X $(CC) $(CCOPTS) $(MACHOPTS) -c npipekiller.c X Xnpipeserver.o: npipeserver.c X $(CC) $(CCOPTS) $(MACHOPTS) -c npipeserver.c X Xnpipeclient.o: npipeclient.c X $(CC) $(CCOPTS) $(MACHOPTS) -c npipeclient.c X Xundomkiller.o: undomkiller.c X $(CC) $(CCOPTS) $(MACHOPTS) -c undomkiller.c X Xundomserver.o: undomserver.c X $(CC) $(CCOPTS) $(MACHOPTS) -c undomserver.c X Xundomclient.o: undomclient.c X $(CC) $(CCOPTS) $(MACHOPTS) -c undomclient.c X Xtcpclient.o: tcpclient.c X $(CC) $(CCOPTS) $(MACHOPTS) -c tcpclient.c X Xtcpkiller.o: tcpkiller.c X $(CC) $(CCOPTS) $(MACHOPTS) -c tcpkiller.c X Xtcpserver.o: tcpserver.c X $(CC) $(CCOPTS) $(MACHOPTS) -c tcpserver.c X Xdupdup.o: dupdup.c X $(CC) $(CCOPTS) $(MACHOPTS) -c dupdup.c X Xusername.o: username.c X $(CC) $(CCOPTS) $(MACHOPTS) -c username.c X Xportname.o: portname.c X $(CC) $(CCOPTS) $(MACHOPTS) -c portname.c X Xhostname.o: hostname.c X $(CC) $(CCOPTS) $(MACHOPTS) -c hostname.c X Xnumeric.o: numeric.c X $(CC) $(CCOPTS) $(MACHOPTS) -c numeric.c X Xenv.o: env.c X $(CC) $(CCOPTS) $(MACHOPTS) -c env.c X Xauthuser.o: authuser.c X $(CC) $(CCOPTS) $(MACHOPTS) -c authuser.c X Xshar: X shar `cat FILES` > ucspi.shar X chmod 400 ucspi.shar END_OF_FILE if test 3109 -ne `wc -c <'Makefile'`; then echo shar: \"'Makefile'\" unpacked with wrong size! fi # end of 'Makefile' fi if test -f 'NPIPE' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'NPIPE'\" else echo shar: Extracting \"'NPIPE'\" \(1377 characters\) sed "s/^X//" >'NPIPE' <<'END_OF_FILE' XNPIPE X6/23/91 X X XThis document defines the NPIPE protocol for UCSPI programs. A NPIPE Xclient communicates with a NPIPE server on the same machine through a Xpair of named pipes. X X<address> consists of two arguments: <username> <service>. Neither Xargument can contain slashes, newlines, or periods. <username> must not Xcontain colons. Any other character understood by the local machine may Xappear. <username> is a username on the machine, and <service> is any Xtext chosen by <username> to represent a service. X X<clientaddress> is the username of the client user. <serveraddress> is X<username>:<service>. Both of these addresses are limited to 100 Xcharacters. X XThe descriptors passed to <userprogram> refer to two independent Xunlinked named pipes (fifos). Descriptor 6 supports only reading and Xdescriptor 7 supports only writing. The semantics of these pipes are Xdefined by the local UNIX variant; no operations other than those Xapplicable to normal pipes may be assumed. X XIt must not be possible for one user to set up a service with a Xdifferent <username>. It must not be possible for a user to conceal or Xdisguise the username of a client. X XAll NPIPE programs have undefined behavior if any username involved Xshares a uid with a different username. X XPreferred executable names: npipeclient, npipeserver, npipekiller. X X XUCSPI Administrator XDaniel J. Bernstein, brnstnd@nyu.edu END_OF_FILE if test 1377 -ne `wc -c <'NPIPE'`; then echo shar: \"'NPIPE'\" unpacked with wrong size! fi # end of 'NPIPE' fi if test -f 'README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'README'\" else echo shar: Extracting \"'README'\" \(2539 characters\) sed "s/^X//" >'README' <<'END_OF_FILE' Xclientserver 0.50, 6/25/91, Daniel J. Bernstein, brnstnd@nyu.edu. XWatch alt.sources.d for patches, comp.sources.unix for the next version. XLatest version inside stealth.acf.nyu.edu:pub/hier/clientserver/. X XThis has been extensively tested under SunOS 4.0.3, Ultrix 4.1, BSD 4.3, Xand the latest pre-BSD 4.4, and it appears to work on several other Xsystems. Under gcc you MUST define -fwritable-strings; sorry, old habits Xdie hard. X XModify UNDOMDIR and NPIPEDIR in Makefile for your system. Make some uid, Xsay undom, with access to UNDOMDIR; make UNDOMDIR mode 700 ownder undom. XMake some uid, say npipe, with access to NPIPEDIR; make NPIPEDIR mode X700 owner npipe. Make sure LIBS makes sense for you: most BSD systems Xneed -lresolv to use the resolver, and if you don't have getopt you'll Xhave to pick it up and put it into LIBS. That's it for configuration. X XMake. On BSD systems without named pipes, you should make only tcp and Xundom. On System V systems without sockets, you should make npipe. (I Xhave absolutely no idea whether npipe actually works on any modern XSystem V machines.) Under SunOS and Ultrix, make everything. X XTo test the programs, run testmain in this directory with appropriate Xarguments. Examples: X X ./testmain NPIPE npipe <yourusername> blah X ./testmain UNDOM undom <yourusername> foop X ./testmain TCP tcp 0 <randomportnumber> X XThe 0 in the last line is an abbreviation for the local host. testmain Xshould say ``Everything compares okay''; if not, don't panic, because Xthe test code can report false alarms. (It uses sleep(1) for Xsynchronization.) Just run it again. To see what's going on, run Xtestucspi in place of testmain. X XInstall tcp{client,server,killer} somewhere, NOT setuid. Install Xundom{client,server,killer} somewhere, owner undom, mode 4755. Install Xnpipe{client,server,killer} somewhere, owner npipe, mode 4755. That's Xit. You might want to have your rc.local remove anything in UNDOMDIR or XNPIPEDIR. X X*PLEASE* fill out FORMLETTER and send it to me if you had problems. X*PLEASE* fill out FORMLETTER and send it to me if everything worked! XThanks for being a good sport. X XThe best documentation available at the moment is UCSPI and its Xassociated documents (TCP, UNDOM, NPIPE). Look at testucspi for an Xexample of how to communicate between independent processes with UCSPI. X X XXXX: handle EINTR better in undomserver.c and npipe*.c XXXX: restore signals properly? XXXX: fix all the exit codes XXXX: write man pages! XXXX: write a real README! and BLURB XXXX: provide semi-automatic reboot cleanup stuff? END_OF_FILE if test 2539 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test -f 'TCP' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'TCP'\" else echo shar: Extracting \"'TCP'\" \(2835 characters\) sed "s/^X//" >'TCP' <<'END_OF_FILE' XTCP X6/19/91 X X XThis document defines the TCP protocol for UCSPI programs. A TCP client Xcommunicates with a TCP server, on the same machine or on a different Xmachine, via the TCP/IP protocol through an Internet-domain socket. X X<address> consists of two arguments: <hostname> <port>. <hostname> may Xeither be a dotted IP address (such as 128.122.128.2) or a name which, Xon the local machine, somehow represents such an address. (For example, Xdomain names understood by DNS or by a local NIC-style host table might Xbe valid <hostname>s.) <hostname> may also be the single character 0, Xreferring to the local machine. <port> may either be a numeric TCP port Xnumber (such as 25, the standard SMTP port) or a name which, on the Xlocal machine, somehow represents a port number. Any character may Xappear in <hostname> or <port>. X X<clientaddress> and <serveraddress> share a single format: X<username>@<hostnumber>(<hostname>):<port>. <username> is a username on Xthe machine in question: inside TCPLOCAL, <username> is the real Xusername of the process, and inside TCPREMOTE, <username> is the Xusername returned by the other machine via RFC 931, or blank if the Xother machine does not support RFC 931. The client and server must take Xa -R option to turn off RFC 931 checking, and -r to turn it back on. XUnder -R, <username> will always be blank inside TCPREMOTE. <hostnumber> Xis the IP address of the communicating host. <hostname> is either that Xaddress or, if available, a name representing that address. <port> is Xthe relevant TCP port. X XNote that this address format is ambiguous, except in <port>. <hostname> Xdepends on local name lookup conventions, <username> may be blank, and X<hostnumber> might refer to the same machine through special numbers Xsuch as 127.0.0.1 and 0.0.0.0. Application programs which use X<clientaddress> and <serveraddress> should keep these caveats in mind. X XThe descriptors passed to <userprogram> refer to an INET-domain stream Xsocket, dup()ed from a single connect() or accept(). The semantics of Xthis socket are defined by the local UNIX variant, but it is expected Xthat it will support out-of-band data as in BSD systems past 4.2, Xsubject to the restrictions of RFC 1122 and RFC 1123. X XIf <port> is 0, the server must choose an available port number or Xreport an error. The client must take a -p <locport> option to set a Xdesired TCP port on the local side of the connection; if it cannot Xsatisfy this request, it must report an error. X XThe killer must not work upon a server running on a different machine. XThe killer and the server may communicate through files stored in the Xhome directory of the invoking user, as specified by environment Xvariable TCPHOME, or HOME if TCPHOME is not set. X XPreferred executable names: tcpclient, tcpserver, tcpkiller. X X XUCSPI Administrator XDaniel J. Bernstein, brnstnd@nyu.edu END_OF_FILE if test 2835 -ne `wc -c <'TCP'`; then echo shar: \"'TCP'\" unpacked with wrong size! fi # end of 'TCP' fi if test -f 'UNDOM' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'UNDOM'\" else echo shar: Extracting \"'UNDOM'\" \(1409 characters\) sed "s/^X//" >'UNDOM' <<'END_OF_FILE' XUNDOM X6/16/91 X X XThis document defines the UNDOM protocol for UCSPI programs. A UNDOM Xclient communicates with a UNDOM server on the same machine through a XUNIX-domain socket. X X<address> consists of two arguments: <username> <service>. Neither Xargument can contain slashes, newlines, or periods. <username> must not Xcontain colons. Any other character understood by the local machine may Xappear. <username> is a username on the machine, and <service> is any Xtext chosen by <username> to represent a service. X X<clientaddress> is the username of the client user. <serveraddress> is X<username>:<service>. Both of these addresses are limited to 100 Xcharacters. X XThe descriptors passed to <userprogram> refer to a UNIX-domain stream Xsocket whose name is a function of <username> and <service>; they are Xdup()ed from a single connect() or accept(). The semantics of this Xsocket are defined by the local UNIX variant, but it is expected that it Xwill support file descriptor passing as in BSD systems past 4.2. X XIt must not be possible for one user to set up a service with a Xdifferent <username>. It must not be possible for a user to conceal or Xdisguise the username of a client. X XAll UNDOM programs have undefined behavior if any username involved Xshares a uid with a different username. X XPreferred executable names: undomclient, undomserver, undomkiller. X X XUCSPI Administrator XDaniel J. Bernstein, brnstnd@nyu.edu END_OF_FILE if test 1409 -ne `wc -c <'UNDOM'`; then echo shar: \"'UNDOM'\" unpacked with wrong size! fi # end of 'UNDOM' fi if test -f 'authuser.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'authuser.c'\" else echo shar: Extracting \"'authuser.c'\" \(3622 characters\) sed "s/^X//" >'authuser.c' <<'END_OF_FILE' X/* X5/6/91 DJB baseline authuser 3.1. Public domain. X*/ X X#include <stdio.h> X#include <sys/types.h> X#include <sys/socket.h> X#include <netinet/in.h> X#include <arpa/inet.h> X#include <netdb.h> X#include <errno.h> X#include <ctype.h> Xextern int errno; X#include "authuser.h" X Xunsigned short auth_tcpport = 113; X X#define SIZ 500 /* various buffers */ X Xstatic int usercmp(u,v) Xregister char *u; Xregister char *v; X{ X /* is it correct to consider Foo and fOo the same user? yes */ X /* but the function of this routine may change later */ X while (*u && *v) X if (tolower(*u) != tolower(*v)) X return tolower(*u) - tolower(*v); X else X ++u,++v; X return *u || *v; X} X Xstatic char authline[SIZ]; X Xchar *auth_xline(user,fd,in) Xregister char *user; /* the supposed name of the user, NULL if unknown */ Xregister int fd; /* the file descriptor of the connection */ Xregister unsigned long *in; X{ X unsigned short local; X unsigned short remote; X register char *ruser; X X if (auth_fd(fd,in,&local,&remote) == -1) X return 0; X ruser = auth_tcpuser(*in,local,remote); X if (!ruser) X return 0; X if (!user) X user = ruser; /* forces X-Auth-User */ X (void) sprintf(authline, X (usercmp(ruser,user) ? "X-Forgery-By: %s" : "X-Auth-User: %s"), X ruser); X return authline; X} X Xint auth_fd(fd,in,local,remote) Xregister int fd; Xregister unsigned long *in; Xregister unsigned short *local; Xregister unsigned short *remote; X{ X struct sockaddr_in sa; X int dummy; X X dummy = sizeof(sa); X if (getsockname(fd,&sa,&dummy) == -1) X return -1; X if (sa.sin_family != AF_INET) X { X errno = EAFNOSUPPORT; X return -1; X } X *local = ntohs(sa.sin_port); X dummy = sizeof(sa); X if (getpeername(fd,&sa,&dummy) == -1) X return -1; X *remote = ntohs(sa.sin_port); X *in = sa.sin_addr.s_addr; X return 0; X} X Xstatic char ruser[SIZ]; Xstatic char realbuf[SIZ]; Xstatic char *buf; X Xchar *auth_tcpuser(in,local,remote) Xregister unsigned long in; Xregister unsigned short local; Xregister unsigned short remote; X{ X struct sockaddr_in sa; X register int s; X register int buflen; X register int w; X register int saveerrno; X char ch; X unsigned short rlocal; X unsigned short rremote; X X if ((s = socket(AF_INET,SOCK_STREAM,0)) == -1) X return 0; X sa.sin_family = AF_INET; X sa.sin_port = htons(auth_tcpport); X sa.sin_addr.s_addr = in; X if (connect(s,&sa,sizeof(sa)) == -1) X { X saveerrno = errno; X (void) close(s); X errno = saveerrno; X return 0; X } X X buf = realbuf; X (void) sprintf(buf,"%u , %u\r\n",(unsigned int) remote,(unsigned int) local); X /* note the reversed order---the example in the RFC is misleading */ X buflen = strlen(buf); X while ((w = write(s,buf,buflen)) < buflen) X if (w == -1) /* should we worry about 0 as well? */ X { X saveerrno = errno; X (void) close(s); X errno = saveerrno; X return 0; X } X else X { X buf += w; X buflen -= w; X } X buf = realbuf; X while ((w = read(s,&ch,1)) == 1) X { X *buf = ch; X if ((ch != ' ') && (ch != '\t') && (ch != '\r')) X ++buf; X if ((buf - realbuf == sizeof(realbuf) - 1) || (ch == '\n')) X break; X } X if (w == -1) X { X saveerrno = errno; X (void) close(s); X errno = saveerrno; X return 0; X } X *buf = '\0'; X X if (sscanf(realbuf,"%hd,%hd: USERID :%*[^:]:%s",&rremote,&rlocal,ruser) < 3) X { X (void) close(s); X errno = EIO; X /* makes sense, right? well, not when USERID failed to match ERROR */ X /* but there's no good error to return in that case */ X return 0; X } X if ((remote != rremote) || (local != rlocal)) X { X (void) close(s); X errno = EIO; X return 0; X } X /* XXX: we're not going to do any backslash processing */ X (void) close(s); X return ruser; X} END_OF_FILE if test 3622 -ne `wc -c <'authuser.c'`; then echo shar: \"'authuser.c'\" unpacked with wrong size! fi # end of 'authuser.c' fi if test -f 'authuser.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'authuser.h'\" else echo shar: Extracting \"'authuser.h'\" \(161 characters\) sed "s/^X//" >'authuser.h' <<'END_OF_FILE' X#ifndef AUTHUSER_H X#define AUTHUSER_H X Xextern unsigned short auth_tcpport; Xextern char *auth_xline(); Xextern int auth_fd(); Xextern char *auth_tcpuser(); X X#endif END_OF_FILE if test 161 -ne `wc -c <'authuser.h'`; then echo shar: \"'authuser.h'\" unpacked with wrong size! fi # end of 'authuser.h' fi if test -f 'dupdup.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dupdup.c'\" else echo shar: Extracting \"'dupdup.c'\" \(2174 characters\) sed "s/^X//" >'dupdup.c' <<'END_OF_FILE' X/* Public domain. */ X X#include <fcntl.h> X#include "dupdup.h" X Xstatic int dupit(old,new) Xint old; Xint new; X{ X/* What follows is roughly equivalent to the following: X if (dup2(old,new) == -1) return -1; close(old); return 0; X Differences: errno can be different; this works under System V; X this is about twice as slow. X*/ X if (old == new) /* shouldn't happen */ X return 0; X if (fcntl(old,F_GETFL,0) == -1) /* old is bad */ X return -1; X close(new); /* XXX: check error codes? */ X if (fcntl(old,F_DUPFD,new) == -1) /* can't dup */ X return -1; X close(old); /*XXX*/ X return 0; X} X Xint dupdup(d,n) Xint *d; Xint n; X{ X int i; X int j; X int k; X int l; X X /* Descriptor i will become descriptor d[i], if d[i] != -1. */ X /* The original descriptor d[i] will disappear. */ X /* The array d[i] may be mangled freely. */ X /* n is the size of d[]. */ X /* No guarantees if i and j both become k. */ X /* errno is set properly on a -1 return. */ X X for (i = 0;i < n;++i) X if (d[i] != -1) X { X for (j = d[i];(j != -1) && (j != i) && (j != d[j]);j = d[j]) X ; X if ((j == -1) || (j == d[j])) X if (j == i) X ; /* self-loop */ X else X { /* no cycle */ X if (j != -1) X d[j] = -1; X if (d[d[i]] == -1) /*XXX*/ X { X if (dupit(i,d[i]) == -1) X return -1; X d[i] = -1; X } X else X { X k = i; j = d[i]; X do X { X l = d[j]; d[j] = k; k = j; j = l; X } X while (j != -1); X for (j = k;j != i;j = k) X { X if (dupit(d[j],j) == -1) X return -1; X k = d[j]; X d[j] = -1; X } X d[i] = -1; X } X } X else X { /* cycle */ X if (dupit(i,n) == -1) X return -1; X if (d[d[i]] == i) /*XXX*/ X { X if (dupit(d[i],i) == -1) X return -1; X if (dupit(n,d[i]) == -1) X return -1; X d[d[i]] = -1; X d[i] = -1; X } X else X { X k = i; j = d[i]; X do X { X l = d[j]; d[j] = k; k = j; j = l; X } X while (j != i); X for (j = k;j != i;j = k) X { X if (dupit(d[j],j) == -1) X return -1; X k = d[j]; X d[j] = -1; X } X d[i] = -1; X if (dupit(n,k) == -1) X return -1; X } X } X } X return 0; X} END_OF_FILE if test 2174 -ne `wc -c <'dupdup.c'`; then echo shar: \"'dupdup.c'\" unpacked with wrong size! fi # end of 'dupdup.c' fi if test -f 'dupdup.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dupdup.h'\" else echo shar: Extracting \"'dupdup.h'\" \(64 characters\) sed "s/^X//" >'dupdup.h' <<'END_OF_FILE' X#ifndef DUPDUP_H X#define DUPDUP_H X Xextern int dupdup(); X X#endif END_OF_FILE if test 64 -ne `wc -c <'dupdup.h'`; then echo shar: \"'dupdup.h'\" unpacked with wrong size! fi # end of 'dupdup.h' fi if test -f 'env.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'env.c'\" else echo shar: Extracting \"'env.c'\" \(2157 characters\) sed "s/^X//" >'env.c' <<'END_OF_FILE' X/* Public domain. */ X X/* XThese are portable versions of putenv(). env_put("FOO=BAR") is like XFOO=BAR inside sh; env_unset("FOO") unsets any variable FOO. env_init() Xis optional initialization. env_get is just like getenv. Of course, all Xchanges are tracked through environ, so execvp() and friends will pick Xup the new variables. X*/ X X/* uses environ, malloc, free, strlen, strncmp */ X X#include "env.h" X#include "malloc.h" X Xextern char **environ; Xstatic int init = 0; Xstatic int numenv; Xstatic int allocenv; X Xextern char *env_get(s) Xchar *s; X{ X int i; X int slen; X X if (!init) X if (env_init()) X return 0; X X slen = strlen(s); X for (i = 0;i < numenv;++i) X if ((!strncmp(s,environ[i],slen)) && (environ[i][slen] == '=')) X return environ[i] + slen + 1; X return 0; X} X Xextern int env_unset(s) Xchar *s; X{ X int i; X int slen; X X if (!init) X if (env_init()) X return -1; X slen = strlen(s); X for (i = 0;i < numenv;++i) X if ((!strncmp(s,environ[i],slen)) && (environ[i][slen] == '=')) X { X if (i < --numenv) X environ[i] = environ[numenv]; X environ[numenv] = 0; X } X return 0; X} X Xstatic int env_realloc() X{ X char **envp; X X allocenv = numenv + 30; X envp = environ; X environ = (char **) malloc(allocenv * (int) sizeof(char *)); X if (!environ) X { X environ = envp; X allocenv = numenv; X return -1; X } X numenv = 0; X while (*envp) X { X environ[numenv] = *envp; X ++numenv; X ++envp; X } X environ[numenv] = 0; X free((char *) (envp - numenv)); X return 0; X} X Xstatic int env_add(s) Xchar *s; X{ X char *t; X for (t = s;*t;++t) X if (*t == '=') X break; X if (*t) X { X *t = 0; X if (env_unset(s)) X return -1; X *t = '='; X } X if (numenv == allocenv) X if (env_realloc()) X return -1; X environ[numenv] = s; X ++numenv; X environ[numenv] = 0; X return 0; X} X Xint env_init() X{ X char **envp; X X numenv = 0; X allocenv = 0; X envp = environ; X environ = (char **) malloc(sizeof(char *)); X if (!environ) X { X environ = envp; X return -1; X } X init = 1; X while (*envp) X { X if (env_add(*envp)) X return -1; X ++envp; X } X return 0; X} X Xint env_put(s) Xchar *s; X{ X if (!init) X if (env_init()) X return -1; X return env_add(s); X} END_OF_FILE if test 2157 -ne `wc -c <'env.c'`; then echo shar: \"'env.c'\" unpacked with wrong size! fi # end of 'env.c' fi if test -f 'env.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'env.h'\" else echo shar: Extracting \"'env.h'\" \(153 characters\) sed "s/^X//" >'env.h' <<'END_OF_FILE' X#ifndef ENV_H X#define ENV_H X Xextern int env_init(); Xextern int env_put(); Xextern int env_unset(); Xextern char *env_get(); /* for completeness */ X X#endif END_OF_FILE if test 153 -ne `wc -c <'env.h'`; then echo shar: \"'env.h'\" unpacked with wrong size! fi # end of 'env.h' fi if test -f 'errno.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'errno.h'\" else echo shar: Extracting \"'errno.h'\" \(78 characters\) sed "s/^X//" >'errno.h' <<'END_OF_FILE' X#ifndef ERRNO_H X#define ERRNO_H X X#include <errno.h> Xextern int errno; X X#endif END_OF_FILE if test 78 -ne `wc -c <'errno.h'`; then echo shar: \"'errno.h'\" unpacked with wrong size! fi # end of 'errno.h' fi if test -f 'getopt.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'getopt.h'\" else echo shar: Extracting \"'getopt.h'\" \(298 characters\) sed "s/^X//" >'getopt.h' <<'END_OF_FILE' X#ifndef GETOPT_H X#define GETOPT_H X X/* I'm trusting the user to acquire a getopt library, rather than Xproviding one myself. This is probably a mistake. */ X X/* The following should be in /usr/include/getopt.h but often aren't. */ X Xextern int getopt(); Xextern char *optarg; Xextern int optind; X X#endif END_OF_FILE if test 298 -ne `wc -c <'getopt.h'`; then echo shar: \"'getopt.h'\" unpacked with wrong size! fi # end of 'getopt.h' fi if test -f 'hostname.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'hostname.c'\" else echo shar: Extracting \"'hostname.c'\" \(1191 characters\) sed "s/^X//" >'hostname.c' <<'END_OF_FILE' X/* History: X5/1/91 DJB baseline public domain. todo: cache hosts. X*/ X X#include <sys/types.h> X#include <sys/socket.h> X#include <netdb.h> X#include <netinet/in.h> X#include "hostname.h" X#include "numeric.h" X Xint inaddr2hostname(inaddr,hostname) Xstruct in_addr inaddr; Xchar **hostname; X{ X struct hostent *he; X static char hn[30]; X unsigned char *x; X X if (he = gethostbyaddr(&inaddr,sizeof(inaddr),AF_INET)) X { X *hostname = he->h_name; X return 0; X } X x = (unsigned char *) &inaddr; X sprintf(hn,"%d.%d.%d.%d" X ,(int) (unsigned int) x[0],(int) (unsigned int) x[1] X ,(int) (unsigned int) x[2],(int) (unsigned int) x[3] X ); X *hostname = hn; X return 1; X} X Xint hostname2inaddr(hostname,inaddr) Xchar *hostname; Xstruct in_addr *inaddr; X{ X struct hostent *he; X char *x; X int h1; int h2; int h3; int h4; X X x = (char *) inaddr; X if (numericdots(hostname) == 3) X if (sscanf(hostname,"%d.%d.%d.%d",&h1,&h2,&h3,&h4) == 4) X { X x[0] = h1; x[1] = h2; x[2] = h3; x[3] = h4; X return 1; X } X /* could use inet_ntoa here */ X he = gethostbyname(hostname); X if (!he) X return -1; /*XXX*/ X x[0] = he->h_addr[0]; X x[1] = he->h_addr[1]; X x[2] = he->h_addr[2]; X x[3] = he->h_addr[3]; X return 0; X} END_OF_FILE if test 1191 -ne `wc -c <'hostname.c'`; then echo shar: \"'hostname.c'\" unpacked with wrong size! fi # end of 'hostname.c' fi if test -f 'hostname.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'hostname.h'\" else echo shar: Extracting \"'hostname.h'\" \(107 characters\) sed "s/^X//" >'hostname.h' <<'END_OF_FILE' X#ifndef HOSTNAME_H X#define HOSTNAME_H X Xextern int inaddr2hostname(); Xextern int hostname2inaddr(); X X#endif END_OF_FILE if test 107 -ne `wc -c <'hostname.h'`; then echo shar: \"'hostname.h'\" unpacked with wrong size! fi # end of 'hostname.h' fi if test -f 'malloc.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'malloc.h'\" else echo shar: Extracting \"'malloc.h'\" \(66 characters\) sed "s/^X//" >'malloc.h' <<'END_OF_FILE' X#ifndef MALLOC_H X#define MALLOC_H X Xextern char *malloc(); X X#endif END_OF_FILE if test 66 -ne `wc -c <'malloc.h'`; then echo shar: \"'malloc.h'\" unpacked with wrong size! fi # end of 'malloc.h' fi if test -f 'npipekiller.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'npipekiller.c'\" else echo shar: Extracting \"'npipekiller.c'\" \(3207 characters\) sed "s/^X//" >'npipekiller.c' <<'END_OF_FILE' X/* Public domain. */ X X#include <stdio.h> X#include <sys/types.h> X#include <sys/file.h> X#include <signal.h> X#include <fcntl.h> X#include "dupdup.h" X#include "malloc.h" X#include "errno.h" X#include "username.h" X#include "env.h" X#include "getopt.h" X#include "numfiles.h" X X#ifndef F_LOCK X#define F_LOCK 1 X#endif X#ifndef O_RDONLY X#define O_RDONLY 0 X#endif X#ifndef O_WRONLY X#define O_WRONLY 1 X#endif X#ifndef O_RDWR X#define O_RDWR 2 X#endif X Xstatic int flagverbose = 1; X Xstatic char errbuf[500]; Xvoid die(n,s) int n; char *s; X{ if (flagverbose) fprintf(stderr,"npipekiller: fatal: %s\n",s); exit(n); } Xvoid dies(n,s,t) int n; char *s; char *t; X{ if (flagverbose) fprintf(stderr,"npipekiller: fatal: %s%s\n",s,t); exit(n); } Xvoid diep(n,s) int n; char *s; X{ if (flagverbose) sprintf(errbuf,"npipekiller: fatal: %s",s); perror(errbuf); exit(n); } Xvoid warnp(s) char *s; X{ if (flagverbose) sprintf(errbuf,"npipekiller: warning: %s",s); perror(errbuf); } Xvoid warnsp(s,t) char *s; char *t; X{ if (flagverbose) sprintf(errbuf,"npipekiller: warning: %s%s",s,t); perror(errbuf); } X Xmain(argc,argv,envp) Xint argc; Xchar *argv[]; Xchar *envp[]; X{ X int arguid; X int uid; X int i; X int fdserv; X int fdlock; X char fnlock[200]; X char fnserv[200]; X int m; X int n; X int r; X char buf[100]; X char *uname; X int opt; X X while ((opt = getopt(argc,argv,"qQ")) != EOF) X switch(opt) X { X case 'q': X flagverbose = 0; break; X case 'Q': X flagverbose = 1; break; X case '?': X break; /*XXX*/ X default: X break; /*XXX*/ X } X argc -= optind; argv += optind; X X if (argc < 2) X die(1,"need at least two arguments"); X X uid = getuid(); X if (username2uid(argv[0],&arguid) == -1) X dies(2,"cannot figure out username ",argv[0]); X X if (arguid != uid) X dies(1,"permission denied by ",argv[0]); X X for (i = 0;argv[0][i];++i) X if ((argv[0][i] == '/') || (argv[0][i] == ':') || (argv[0][i] == '.') || (argv[0][i] == '\n')) X dies(4,"illegal characters in username ",argv[0]); X X for (i = 0;argv[1][i];++i) X if ((argv[1][i] == '/') || (argv[1][i] == '.') || (argv[0][i] == '\n')) X dies(5,"illegal characters in service ",argv[1]); X X signal(SIGPIPE,SIG_IGN); X X if (uid2username(uid,&uname) == -1) /* will never happen */ X die(2,"cannot figure out my own username"); X X if (strlen(NPIPEDIR) + strlen(argv[0]) + strlen(argv[1]) + 1 > 90) /*XXX*/ X die(8,"server filenames too long"); X sprintf(fnserv,"%s/%ds%s",NPIPEDIR,arguid,argv[1]); X sprintf(fnlock,"%s/%dl%s",NPIPEDIR,arguid,argv[1]); X sprintf(buf,"%d",0); X n = strlen(buf); X if (n + strlen(uname) + 10 > sizeof(buf)) X die(8,"username too long"); X strcpy(buf + n + 1,uname); X n += strlen(uname) + 2; X X fdlock = open(fnlock,O_RDWR,0); X if (fdlock == -1) X diep(1,"cannot open lock file"); X /* Here's the tricky bit: file locking. :-( */ X /* XXX: We assume that anyone with named pipes also has lockf(). */ X if (lockf(fdlock,F_LOCK,1) == -1) X diep(1,"cannot wait for lock on lock file"); X X fdserv = open(fnserv,O_WRONLY,0); X if (fdserv == -1) X diep(1,"cannot open server's named pipe"); X X m = 0; X r = 0; X while ((m < n) && ((r = write(fdserv,buf + m,n - m)) > 0)) X m += r; X if (r <= 0) X die(1,"cannot tell server to kill itself"); X X exit(0); X} END_OF_FILE if test 3207 -ne `wc -c <'npipekiller.c'`; then echo shar: \"'npipekiller.c'\" unpacked with wrong size! fi # end of 'npipekiller.c' fi if test -f 'numeric.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'numeric.c'\" else echo shar: Extracting \"'numeric.c'\" \(782 characters\) sed "s/^X//" >'numeric.c' <<'END_OF_FILE' X/* History: X6/19/91 DJB added numericdots() X5/1/91 DJB baseline public domain X*/ X X/* X Xint numeric(s) char *s; returns 1 if s is entirely composed of the Xdigits 0 through 9, 0 otherwise. X X*/ X X#include "numeric.h" X Xint numeric(s) Xchar *s; X{ X while (*s) X { X if ((*s != '0') && (*s != '1') && (*s != '2') && (*s != '3') && (*s != '4') X && (*s != '5') && (*s != '6') && (*s != '7') && (*s != '8') && (*s != '9')) X return 0; X ++s; X } X return 1; X} X Xint numericdots(s) Xchar *s; X{ X int dots; X X dots = 0; X while (*s) X { X if ((*s != '0') && (*s != '1') && (*s != '2') && (*s != '3') && (*s != '4') X && (*s != '5') && (*s != '6') && (*s != '7') && (*s != '8') && (*s != '9') X && (*s != '.') X ) X return -1; X if (*s == '.') X ++dots; X ++s; X } X return dots; X} END_OF_FILE if test 782 -ne `wc -c <'numeric.c'`; then echo shar: \"'numeric.c'\" unpacked with wrong size! fi # end of 'numeric.c' fi if test -f 'numeric.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'numeric.h'\" else echo shar: Extracting \"'numeric.h'\" \(93 characters\) sed "s/^X//" >'numeric.h' <<'END_OF_FILE' X#ifndef NUMERIC_H X#define NUMERIC_H X Xextern int numeric(); Xextern int numericdots(); X X#endif END_OF_FILE if test 93 -ne `wc -c <'numeric.h'`; then echo shar: \"'numeric.h'\" unpacked with wrong size! fi # end of 'numeric.h' fi if test -f 'numfiles.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'numfiles.h'\" else echo shar: Extracting \"'numfiles.h'\" \(82 characters\) sed "s/^X//" >'numfiles.h' <<'END_OF_FILE' X#ifndef NUMFILES_H X#define NUMFILES_H X X#define NUMFILES (getdtablesize()) X X#endif END_OF_FILE if test 82 -ne `wc -c <'numfiles.h'`; then echo shar: \"'numfiles.h'\" unpacked with wrong size! fi # end of 'numfiles.h' fi if test -f 'portname.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'portname.c'\" else echo shar: Extracting \"'portname.c'\" \(764 characters\) sed "s/^X//" >'portname.c' <<'END_OF_FILE' X/* History: X6/20/91 DJB added proto arg X5/9/91 DJB fixed for backwards architectures, tnx VA X5/1/91 DJB baseline public domain X*/ X X#include "portname.h" X#include "numeric.h" X#include <netdb.h> X X/* XXX: cache */ X Xint port2portname(port,pop,proto) Xint port; Xchar **pop; Xchar *proto; X{ X struct servent *se; X static char po[20]; X X if (se = getservbyport(port,proto)) X { X *pop = se->s_name; X return 0; X } X sprintf(po,"%d",port); X *pop = po; X return 1; X} X Xint portname2port(po,port,proto) Xchar *po; Xint *port; Xchar *proto; X{ X struct servent *se; X X if (numeric(po)) X { X *port = atoi(po); X return 1; X } X se = getservbyname(po,proto); X if (!se) X return -1; /*XXX*/ X *port = ntohs(se->s_port); X /* XXX: stupidity alert: se->s_port is an int! */ X return 0; X} END_OF_FILE if test 764 -ne `wc -c <'portname.c'`; then echo shar: \"'portname.c'\" unpacked with wrong size! fi # end of 'portname.c' fi if test -f 'portname.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'portname.h'\" else echo shar: Extracting \"'portname.h'\" \(103 characters\) sed "s/^X//" >'portname.h' <<'END_OF_FILE' X#ifndef PORTNAME_H X#define PORTNAME_H X Xextern int port2portname(); Xextern int portname2port(); X X#endif END_OF_FILE if test 103 -ne `wc -c <'portname.h'`; then echo shar: \"'portname.h'\" unpacked with wrong size! fi # end of 'portname.h' fi if test -f 'tcpkiller.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'tcpkiller.c'\" else echo shar: Extracting \"'tcpkiller.c'\" \(2818 characters\) sed "s/^X//" >'tcpkiller.c' <<'END_OF_FILE' X/* Public domain. */ X X#include <stdio.h> X#include <sys/types.h> X#include <sys/param.h> X#include <sys/file.h> X#include <sys/socket.h> X#include <signal.h> X#include <netinet/in.h> X#include "errno.h" X#include "username.h" X#include "hostname.h" X#include "portname.h" X#include "getopt.h" X#include "malloc.h" X#include "env.h" X X#ifndef MAXHOSTNAMELEN X#define MAXHOSTNAMELEN 64 X#endif X Xstatic int flagverbose = 1; X Xstatic char errbuf[500]; Xvoid die(n,s) int n; char *s; X{ if (flagverbose) fprintf(stderr,"tcpkiller: fatal: %s\n",s); exit(n); } Xvoid dies(n,s,t) int n; char *s; char *t; X{ if (flagverbose) fprintf(stderr,"tcpkiller: fatal: %s%s\n",s,t); exit(n); } Xvoid diep(n,s) int n; char *s; X{ if (flagverbose) sprintf(errbuf,"tcpkiller: fatal: %s",s); perror(errbuf); exit(n); } Xvoid warnp(s) char *s; X{ if (flagverbose) sprintf(errbuf,"tcpkiller: warning: %s",s); perror(errbuf); } Xvoid warnsp(s,t) char *s; char *t; X{ if (flagverbose) sprintf(errbuf,"tcpkiller: warning: %s%s",s,t); perror(errbuf); } X Xmain(argc,argv,envp) Xint argc; Xchar *argv[]; Xchar *envp[]; X{ X int uid; X int opt; X char lochost[MAXHOSTNAMELEN]; X int locport; X char *home; X char *fnrecord; X char pidstr[50]; X int pid; X int fd; X X while ((opt = getopt(argc,argv,"qQ")) != EOF) X switch(opt) X { X case 'q': X flagverbose = 0; break; X case 'Q': X flagverbose = 1; break; X case '?': X break; /*XXX*/ X default: X break; /*XXX*/ X } X argc -= optind; argv += optind; X X if (argc < 2) X die(1,"need at least two arguments"); X X uid = getuid(); X X gethostname(lochost,sizeof(lochost)); X X if ((argv[0][0] == '0') && (argv[0][1] == 0)) X ; /* local host, fine */ X else X { X struct in_addr inaddr1; X struct in_addr inaddr2; X if (hostname2inaddr(argv[0],&inaddr1) == -1) X dies(2,"cannot figure out hostname ",argv[0]); X if (hostname2inaddr(lochost,&inaddr2) == -1) X die(2,"cannot figure out my own hostname"); X /* XXX: this doesn't allow 127.1 */ X if (inaddr1.s_addr != inaddr2.s_addr) X dies(2,"host does not match local host ",argv[0]); X } X X if (portname2port(argv[1],&locport,"tcp") == -1) X dies(2,"cannot figure out portname ",argv[1]); X locport = locport & 65535; X X home = env_get("TCPHOME"); X if (!home) X { X home = env_get("HOME"); X if (!home) X die(1,"cannot find TCPHOME or HOME in environment"); X } X fnrecord = malloc(strlen(home) + strlen(lochost) + 100); X if (!fnrecord) X die(1,"out of memory"); X sprintf(fnrecord,"%s/.tcpserver.%s.%d.%d",home,lochost,uid,locport); X X fd = open(fnrecord,O_RDONLY,0400); X if (fd == -1) X dies(1,"cannot open ",fnrecord); X X read(fd,pidstr,sizeof(pidstr)); /* XXX: check for partial reads */ X if (sscanf(pidstr,"%d",&pid) != 1) X die(1,"cannot figure out pid of tcpserver"); X X close(fd); X X kill(pid,SIGTERM); /*XXX: report errors? */ X X exit(0); X} END_OF_FILE if test 2818 -ne `wc -c <'tcpkiller.c'`; then echo shar: \"'tcpkiller.c'\" unpacked with wrong size! fi # end of 'tcpkiller.c' fi if test -f 'testmain' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'testmain'\" else echo shar: Extracting \"'testmain'\" \(269 characters\) sed "s/^X//" >'testmain' <<'END_OF_FILE' X#!/bin/sh X# testmain args X./testucspi ${1+"$@"} | sed 's/=.*//' > .ucspi.$$ X./testresult ${1+"$@"} | sed 's/=.*//' > .result.$$ Xif cmp .ucspi.$$ .result.$$ >/dev/null 2>&1 Xthen echo 'Everything compares okay.' Xelse echo 'Aaack! Test failed.' Xfi Xrm .ucspi.$$ .result.$$ END_OF_FILE if test 269 -ne `wc -c <'testmain'`; then echo shar: \"'testmain'\" unpacked with wrong size! fi chmod +x 'testmain' # end of 'testmain' fi if test -f 'testresult' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'testresult'\" else echo shar: Extracting \"'testresult'\" \(915 characters\) sed "s/^X//" >'testresult' <<'END_OF_FILE' X#!/bin/sh X# testresult proto prog address X# XXX: test args Xproto="$1"; shift; export proto Xprog="$1"; shift Xclient=./"$prog"client; export client Xserver=./"$prog"server; export server Xkiller=./"$prog"killer; export killer Xecho "$server started, ${proto}LOCAL=(depends on the protocol)" Xecho "$client started" Xecho "$client connected to ${proto}REMOTE=(depends on the protocol)" Xecho "$client reading line from server..." Xecho "$server accepted connection from ${proto}REMOTE=(depends on the protocol)" Xecho "$server writing line to client..." Xecho "$client received from server: is anybody out there..." Xecho "$client writing line to server..." Xecho "$server reading line from client..." Xecho "$server received from client: is anybody in there..." Xecho "$server done with connection" Xecho "$client done with connection" Xecho "$client finished" Xecho "$killer started" Xecho "$server finished" Xecho "$killer finished" END_OF_FILE if test 915 -ne `wc -c <'testresult'`; then echo shar: \"'testresult'\" unpacked with wrong size! fi chmod +x 'testresult' # end of 'testresult' fi if test -f 'testucspi' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'testucspi'\" else echo shar: Extracting \"'testucspi'\" \(1220 characters\) sed "s/^X//" >'testucspi' <<'END_OF_FILE' X#!/bin/sh X# testucspi proto prog address X# XXX: test args Xproto="$1"; shift; export proto Xprog="$1"; shift Xclient=./"$prog"client; export client Xserver=./"$prog"server; export server Xkiller=./"$prog"killer; export killer X X( Xecho "$server started, ${proto}LOCAL=" | tr -d '\012' X# blessed be the shells that allow "$server" here X$server -4 "$@" sh -c ' X exec <&6 X sleep 5 X echo "$server accepted connection from ${proto}REMOTE=" | tr -d "\012" X printenv ${proto}REMOTE X echo "$server writing line to client..." X echo "is anybody out there..." >&7 X sleep 5 X echo "$server reading line from client..." X read i X echo "$server received from client: $i" X echo "$server done with connection" X' 4>&1 Xecho "$server finished" X) & X Xsleep 5 X Xecho "$client started" X$client "$@" sh -c ' X exec <&6 X sleep 1 X echo "$client connected to ${proto}REMOTE=" | tr -d "\012" X printenv ${proto}REMOTE X echo "$client reading line from server..." X read i X echo "$client received from server: $i" X echo "$client writing line to server..." X echo "is anybody in there..." >&7 X sleep 5 X echo "$client done with connection" X' Xecho "$client finished" X Xecho "$killer started" Xeval "$killer" "$@" Xsleep 2 Xecho "$killer finished" END_OF_FILE if test 1220 -ne `wc -c <'testucspi'`; then echo shar: \"'testucspi'\" unpacked with wrong size! fi chmod +x 'testucspi' # end of 'testucspi' fi if test -f 'undomclient.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'undomclient.c'\" else echo shar: Extracting \"'undomclient.c'\" \(3980 characters\) sed "s/^X//" >'undomclient.c' <<'END_OF_FILE' X/* Public domain. */ X X#include <stdio.h> X#include <sys/types.h> X#include <sys/socket.h> X#include <sys/un.h> X#include <signal.h> X#include "dupdup.h" X#include "malloc.h" X#include "errno.h" X#include "username.h" X#include "env.h" X#include "getopt.h" X#include "numfiles.h" X Xstatic int flagverbose = 1; Xstatic struct sockaddr_un sa; X Xstatic char errbuf[500]; Xvoid die(n,s) int n; char *s; X{ if (flagverbose) fprintf(stderr,"undomclient: fatal: %s\n",s); exit(n); } Xvoid dies(n,s,t) int n; char *s; char *t; X{ if (flagverbose) fprintf(stderr,"undomclient: fatal: %s%s\n",s,t); exit(n); } Xvoid diep(n,s) int n; char *s; X{ if (flagverbose) sprintf(errbuf,"undomclient: fatal: %s",s); perror(errbuf); exit(n); } Xvoid warnp(s) char *s; X{ if (flagverbose) sprintf(errbuf,"undomclient: warning: %s",s); perror(errbuf); } Xvoid warnsp(s,t) char *s; char *t; X{ if (flagverbose) sprintf(errbuf,"undomclient: warning: %s%s",s,t); perror(errbuf); } X Xmain(argc,argv,envp) Xint argc; Xchar *argv[]; Xchar *envp[]; X{ X int t; X int *d; X int tdup; X int numfiles; X int arguid; X int uid; X int i; X static char undomlocal[200]; X static char undomremote[300]; X unsigned char urlen; X char *uname; X int opt; X X while ((opt = getopt(argc,argv,"qQ")) != EOF) X switch(opt) X { X case 'q': X flagverbose = 0; break; X case 'Q': X flagverbose = 1; break; X case '?': X break; /*XXX*/ X default: X break; /*XXX*/ X } X argc -= optind; argv += optind; X X if (argc < 3) X die(1,"need at least three arguments"); X X uid = getuid(); X if (username2uid(argv[0],&arguid) == -1) X dies(2,"cannot figure out username ",argv[0]); X /* XXX: any permission checks? */ X X for (i = 0;argv[0][i];++i) X if ((argv[0][i] == '/') || (argv[0][i] == ':') || (argv[0][i] == '.') || (argv[0][i] == '\n')) X dies(4,"illegal characters in username ",argv[0]); X X for (i = 0;argv[1][i];++i) X if ((argv[1][i] == '/') || (argv[1][i] == '.') || (argv[0][i] == '\n')) X dies(5,"illegal characters in service ",argv[1]); X X close(6); X close(7); /* XXX: these have implications that should be documented */ X X signal(SIGPIPE,SIG_IGN); X X if (uid2username(uid,&uname) == -1) /* will never happen */ X die(2,"cannot figure out my own username"); X urlen = strlen(uname) + 1; X X numfiles = NUMFILES; X d = (int *) malloc(numfiles * sizeof(int)); X if (!d) X die(6,"cannot malloc"); X for (i = 0;i < numfiles;++i) X d[i] = -1; X X if (strlen(UNDOMDIR) + strlen(argv[0]) + strlen(argv[1]) + 1 > 100) /*XXX*/ X die(8,"server socket name too long"); X sprintf(sa.sun_path,"%s/%s:%s",UNDOMDIR,argv[0],argv[1]); X X if (env_init() == -1) X die(11,"cannot init environment"); X if (env_put("PROTO=UNDOM") == -1) X die(11,"out of memory putting PROTO into environment"); X sprintf(undomlocal,"UNDOMLOCAL=%s",uname); X if (env_put(undomlocal) == -1) X die(11,"out of memory putting UNDOMLOCAL into environment"); X sprintf(undomremote,"UNDOMREMOTE=%s:%s",argv[0],argv[1]); X if (env_put(undomremote) == -1) X die(11,"out of memory putting UNDOMREMOTE into environment"); X X t = socket(AF_UNIX,SOCK_STREAM,0); X if (t == -1) X diep(7,"cannot create client socket"); X sa.sun_family = AF_UNIX; X if (connect(t,&sa,sizeof(sa)) == -1) X diep(9,"cannot connect to server"); X X /* XXX: fork? not necessary here */ X X if ((i = write(t,&urlen,1)) == -1) X diep(1,"cannot write length of local username"); X if (!i) /* ever? */ X die(1,"cannot write length of local username"); X if (write(t,uname,urlen) < urlen) X die(1,"cannot write local username"); X X tdup = dup(t); X if (tdup == -1) X diep(10,"cannot dup connection to server"); X X /* Now put t into descriptor 6 and tdup into descriptor 7. */ X /* XXX: should check that t and tdup are small enough */ X d[t] = 6; X d[tdup] = 7; X if (dupdup(d,numfiles) == -1) X diep(10,"cannot dup connection to server"); X X if (setreuid(uid,uid) == -1) X diep(1,"cannot setreuid"); /* will never happen */ X signal(SIGPIPE,SIG_DFL); X execvp(argv[2],argv + 2); X warnsp("cannot exec ",argv[2]); X exit(1); X} END_OF_FILE if test 3980 -ne `wc -c <'undomclient.c'`; then echo shar: \"'undomclient.c'\" unpacked with wrong size! fi # end of 'undomclient.c' fi if test -f 'undomkiller.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'undomkiller.c'\" else echo shar: Extracting \"'undomkiller.c'\" \(2703 characters\) sed "s/^X//" >'undomkiller.c' <<'END_OF_FILE' X/* Public domain. */ X X#include <stdio.h> X#include <sys/types.h> X#include <sys/socket.h> X#include <sys/un.h> X#include <signal.h> X#include "errno.h" X#include "username.h" X#include "getopt.h" X Xstatic int flagverbose = 1; Xstatic struct sockaddr_un sa; X Xstatic char errbuf[500]; Xvoid die(n,s) int n; char *s; X{ if (flagverbose) fprintf(stderr,"undomkiller: fatal: %s\n",s); exit(n); } Xvoid dies(n,s,t) int n; char *s; char *t; X{ if (flagverbose) fprintf(stderr,"undomkiller: fatal: %s%s\n",s,t); exit(n); } Xvoid diep(n,s) int n; char *s; X{ if (flagverbose) sprintf(errbuf,"undomkiller: fatal: %s",s); perror(errbuf); exit(n); } Xvoid warnp(s) char *s; X{ if (flagverbose) sprintf(errbuf,"undomkiller: warning: %s",s); perror(errbuf); } Xvoid warnsp(s,t) char *s; char *t; X{ if (flagverbose) sprintf(errbuf,"undomkiller: warning: %s%s",s,t); perror(errbuf); } X Xmain(argc,argv,envp) Xint argc; Xchar *argv[]; Xchar *envp[]; X{ X int t; X int arguid; X int uid; X int i; X unsigned char urlen; X char *uname; X int opt; X X while ((opt = getopt(argc,argv,"qQ")) != EOF) X switch(opt) X { X case 'q': X flagverbose = 0; break; X case 'Q': X flagverbose = 1; break; X case '?': X break; /*XXX*/ X default: X break; /*XXX*/ X } X argc -= optind; argv += optind; X X if (argc < 2) X die(1,"need at least two arguments"); X X uid = getuid(); X if (username2uid(argv[0],&arguid) == -1) X dies(2,"cannot figure out username ",argv[0]); X X if (arguid != uid) X dies(15,"permission denied by ",argv[0]); X /* XXX: special-case root? more checks? */ X /* This is enough to prevent one user from wasting another's CPU time. */ X X for (i = 0;argv[0][i];++i) X if ((argv[0][i] == '/') || (argv[0][i] == ':') || (argv[0][i] == '.') || (argv[0][i] == '\n')) X dies(4,"illegal characters in username ",argv[0]); X X for (i = 0;argv[1][i];++i) X if ((argv[1][i] == '/') || (argv[1][i] == '.') || (argv[0][i] == '\n')) X dies(5,"illegal characters in service ",argv[1]); X X if (uid2username(uid,&uname) == -1) /* will never happen */ X die(2,"cannot figure out my own username"); X urlen = strlen(uname) + 1; X X if (strlen(UNDOMDIR) + strlen(argv[0]) + strlen(argv[1]) + 1 > 100) /*XXX*/ X die(8,"server socket name too long"); X sprintf(sa.sun_path,"%s/%s:%s",UNDOMDIR,argv[0],argv[1]); X X t = socket(AF_UNIX,SOCK_STREAM,0); X if (t == -1) X diep(7,"cannot create killer socket"); X sa.sun_family = AF_UNIX; X if (connect(t,&sa,sizeof(sa)) == -1) X diep(9,"cannot connect to server"); X X signal(SIGPIPE,SIG_IGN); X X urlen = 0; /* XXX: this signals server to clean up and die */ X if ((i = write(t,&urlen,1)) == -1) X diep(1,"cannot kill server"); X if (!i) /* ever? */ X die(1,"cannot kill server"); X X exit(0); X} END_OF_FILE if test 2703 -ne `wc -c <'undomkiller.c'`; then echo shar: \"'undomkiller.c'\" unpacked with wrong size! fi # end of 'undomkiller.c' fi if test -f 'username.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'username.c'\" else echo shar: Extracting \"'username.c'\" \(549 characters\) sed "s/^X//" >'username.c' <<'END_OF_FILE' X/* History: X5/1/91 DJB baseline public domain X*/ X X#include <pwd.h> X#include "username.h" X#include "numeric.h" X X/*XXX: cache */ X Xint uid2username(uid,unp) Xint uid; Xchar **unp; X{ X struct passwd *pw; X static char un[20]; X X if (pw = getpwuid(uid)) X { X *unp = pw->pw_name; X return 0; X } X sprintf(un,"%d",uid); X *unp = un; X return 1; X} X Xint username2uid(un,uid) Xchar *un; Xint *uid; X{ X struct passwd *pw; X X if (numeric(un)) X { X *uid = atoi(un); X return 1; X } X pw = getpwnam(un); X if (!pw) X return -1; /*XXX*/ X *uid = pw->pw_uid; X return 0; X} END_OF_FILE if test 549 -ne `wc -c <'username.c'`; then echo shar: \"'username.c'\" unpacked with wrong size! fi # end of 'username.c' fi if test -f 'username.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'username.h'\" else echo shar: Extracting \"'username.h'\" \(101 characters\) sed "s/^X//" >'username.h' <<'END_OF_FILE' X#ifndef USERNAME_H X#define USERNAME_H X Xextern int uid2username(); Xextern int username2uid(); X X#endif END_OF_FILE if test 101 -ne `wc -c <'username.h'`; then echo shar: \"'username.h'\" unpacked with wrong size! fi # end of 'username.h' fi echo shar: End of archive 1 \(of 2\). cp /dev/null ark1isdone MISSING="" for I in 1 2 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked both archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0
brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (06/26/91)
#! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 2 (of 2)." # Contents: UCSPI npipeclient.c npipeserver.c tcpclient.c tcpserver.c # undomserver.c # Wrapped by brnstnd@kramden on Tue Jun 25 18:41:40 1991 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'UCSPI' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'UCSPI'\" else echo shar: Extracting \"'UCSPI'\" \(9408 characters\) sed "s/^X//" >'UCSPI' <<'END_OF_FILE' XUNIX Client-Server Program Interface, UCSPI-1991 XCopyright 1991, Daniel J. Bernstein X X X1. Introduction X XThis document describes the UNIX Client-Server Program Interface, UCSPI X(ooks-pie), a standard program-based interface to client-server Xcommunications tools under UNIX and its variants. (UNIX is a trademark Xof AT&T.) X XUCSPI is designed to solve several problems. First, although most Xnetworked applications today are essentially independent of the Xunderlying communications protocol, each program must provide direct Xsupport for each protocol. To run a TCP/IP program over DECnet or Xthrough other IPC mechanisms requires rewriting and recompiling the Xprogram. In contrast, an application written for UCSPI will work without Xchange over any communications medium. X XSecond, no standard tools are provided for shell scripts to take Xadvantage of networking. A UCSPI-compliant communications tool is Xautomatically usable within a shell script or from other languages. X XThird, current applications have no mechanism for passing information Xabout a communications link. A typical TCP/IP connection to a BSD Xmachine may go through inetd, an access controller, and one or two Xapplications. Each level may do several system calls, disk reads, and Xnetwork requests for a name lookup and possibly authentication, instead Xof getting the information once and passing it down to the next level. XIn the case of logins, a user shell cannot even determine reliably where Xthe connection is from. In contrast, a UCSPI-compliant tool will collect Xall necessary information and pass it through environment variables to Xthe lower layers. X XA program is not compliant with UCSPI-1991 if it does not satisfy any Xrule marked MUST. It is compliant if it satisfies all the rules marked XMUST. In that case, it is unconditionally compliant if it satisfies all Xthe rules marked SHOULD, and conditionally compliant otherwise. X X X2. The general UCSPI interface X XUCSPI clients and servers are executable programs. They MUST accept a Xcommand line in the following general format: X X <program> <options> <address> <userprogram> <arg...> X XHere <program> is the name of the client or server, <options> are zero Xor more option arguments, <address> is a protocol-specific address, X<userprogram> is a user-specified program to run upon any connection, Xand <arg...> are zero or more arguments to pass through to <userprogram> Xwithout interpretation. X XUCSPI killers are executable programs. They MUST accept a command line Xin the following general format: X X <program> <options> <address> X XEach client, server, and killer MUST support some protocol, as defined Xbelow. <address> SHOULD take up a constant number of arguments, as Xdefined by the protocol. <options> SHOULD be processed by the getopt Xstandard; in particular, an argument of -- SHOULD terminate <options>. X XIf <program> is a client or server, it MUST change descriptors 6 and 7 Xand certain environment variables as described below before executing X<userprogram>. <program> MUST NOT interpret, quote, or unquote <arg...> Xin any way. <program> may or may not fork before executing X<userprogram>. <program> SHOULD NOT change its process state in any Xother way before executing <userprogram>. <program> SHOULD close Xdescriptors 6 and 7 immediately upon entry. X X<program> MUST NOT assume that any other particular descriptors are open Xor closed. <program> may assume that a certain number of descriptors are Xavailable. <program> SHOULD NOT assume anything else about its process Xstate; in particular, <program> SHOULD NOT attempt to find its Xcontrolling terminal device, and <program> SHOULD NOT use getlogin(). X X X3. Servers X XIf <program> is a UCSPI server, it MUST wait for a client to connect to X<address>. It SHOULD NOT accept connections from clients that do not Xsupport the same protocol. Upon accepting a connection, it MUST spawn X<userprogram> with the given <arg...>, descriptor 6 reading from the Xconnection, and descriptor 7 writing to the connection. It MUST then Xcontinue accepting connections to the same <address>. X X<program> MUST set the following environment variables for X<userprogram>: PROTO=<PROTO>, where <PROTO> is the communications Xprotocol used by the connection; <PROTO>LOCAL=<serveraddress>, where X<serveraddress> is <address> in a server format defined by the protocol; X<PROTO>REMOTE=<clientaddress>, where <clientaddress> is some information Xabout the client in a client format defined by the protocol. <PROTO> XMUST consist of characters selected from ABCDEFGHIJKLMNOPQRSTUVWXYZ, Xabcdefghijklmnopqrstuvwxyz, and 0123456789; it MUST NOT be longer than X100 characters. <serveraddress> and <clientaddress> SHOULD use solely Xprintable ASCII characters, SHOULD have a predefined maximum length, Xand MUST NOT contain newline or null. X XA server SHOULD accept the following options: -q to turn off all error Xmessages; -Q to turn on error messages, negating any previous -q; -4 to Ximmediately write the value of <PROTO>LOCAL, followed by a newline, to Xdescriptor 4, and then close descriptor 4. X XIf the server receives signal SIGTERM, it MUST exit with exit code 0; Xthis SHOULD NOT terminate any current connections. If the server cannot Xperform its functions, it MUST exit with a nonzero exit code. The server XSHOULD interpret SIGINT the same way as SIGTERM. X X X4. Clients X XIf <program> is a UCSPI client, it MUST connect to a server at X<address>. It SHOULD NOT connect to a server that does not support the Xsame protocol. Upon connecting, it MUST spawn <userprogram> with the Xgiven <arg...>, descriptor 6 reading from the connection, and descriptor X7 writing to the connection. It SHOULD NOT make further connections. X X<program> MUST set the following environment variables for X<userprogram>: PROTO=<PROTO>, <PROTO>LOCAL=<clientaddress>, and X<PROTO>REMOTE=<serveraddress>. Here <PROTO>, <clientaddress>, and X<remoteaddress> are under the same rules as for servers, but note that Xthe roles of <PROTO>LOCAL and <PROTO>REMOTE are reversed. The protocol Xmay allow <clientaddress> and <serveraddress> to be only partially Xdefined for a given connection, so the client's <PROTO>LOCAL may not Xagree with the server's <PROTO>REMOTE, and vice versa. X XA client SHOULD accept the following options: -q to turn off all error Xmessages; -Q to turn on error messages, negating any previous -q. X XIf the client cannot perform its functions, it MUST exit with a nonzero Xexit code. X X X5. Killers X XIf <program> is a UCSPI killer, it MUST tell a server at <address> to Ximmediately stop accepting connections, just as if the server process Xhad received SIGTERM. It SHOULD NOT have this effect upon a server that Xdoes not support the same protocol. The server may or may not accept the Xkill request. X XA killer SHOULD accept the following options: -q to turn off all error Xmessages; -Q to turn on error messages, negating any previous -q. X XIf the killer cannot perform its functions, it MUST exit with a nonzero Xexit code. X X X6. Protocols X XA protocol MUST define at least the following information: Its name, X<PROTO>. The format and meaning of <address>. The number of arguments X<address> takes, or a notice that it does not take a constant number of Xarguments. Client and server address format. The maximum lengths of X<serveraddress> and <clientaddress>, or a notice that they may grow to Xany length. The address character set, if it is not merely alphanumeric. X XA protocol may define the following information, and more: Supported Xoptions. The forking behavior of clients and servers. Any changes to the Xprocess state not required by UCSPI. The implementation of the Xunderlying protocol, and how it interacts with other protocols. The Xnature of the descriptors passed to <userprogram>. The preferred names Xof the client, server, and killer programs. The circumstances under Xwhich a server will ignore a killer. X XIt is expected that all protocols will provide reliable two-way Xfull-duplex strictly ordered not necessarily timed stream communication, Xthough some protocols may also provide other types of communication X(e.g., expedited [``out-of-band''] transmission, in which the stream is Xno longer ordered). X XAll clients and servers must obey any rules imposed by a protocol in Xorder to support that protocol. If a program supporting a protocol Xcannot be unconditionally compliant with UCSPI-1991, the protocol is Xat most conditionally compliant. X X X7. Protocol management X XPublic protocol definitions may be registered with the UCSPI XAdministrator. The UCSPI Administrator will normally refuse a Xregistration request only on the grounds of a namespace problem, and in Xthat case he will suggest an acceptable name. X XAll protocol names beginning with X are reserved for experimental or Xnonstandard use. All protocol names beginning with x are reserved for Xextended namespaces whose management is allocated to other authorities. X XFurther requests for interpretation or revision of UCSPI should be sent Xto the UCSPI Administrator. The UCSPI Administrator may designate one or Xmore reference implementations of a registered protocol, and where the Xreference implementation establishes an interpretation not required by Xthis document but not in conflict with this document, that Xinterpretation should be considered binding until the UCSPI standard is Xrevised. X X XThe following protocol names have been registered as of 6/16/91: X X TCP X UNDOM X X XUCSPI Administrator XDaniel J. Bernstein, brnstnd@nyu.edu END_OF_FILE if test 9408 -ne `wc -c <'UCSPI'`; then echo shar: \"'UCSPI'\" unpacked with wrong size! fi # end of 'UCSPI' fi if test -f 'npipeclient.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'npipeclient.c'\" else echo shar: Extracting \"'npipeclient.c'\" \(5236 characters\) sed "s/^X//" >'npipeclient.c' <<'END_OF_FILE' X/* Public domain. */ X X#include <stdio.h> X#include <sys/types.h> X#include <sys/file.h> X#include <signal.h> X#include <fcntl.h> X#include "dupdup.h" X#include "malloc.h" X#include "errno.h" X#include "username.h" X#include "env.h" X#include "getopt.h" X#include "numfiles.h" X X#ifndef F_LOCK X#define F_LOCK 1 X#endif X#ifndef O_RDONLY X#define O_RDONLY 0 X#endif X#ifndef O_WRONLY X#define O_WRONLY 1 X#endif X#ifndef O_RDWR X#define O_RDWR 2 X#endif X Xstatic int flagverbose = 1; X Xstatic char errbuf[500]; Xvoid die(n,s) int n; char *s; X{ if (flagverbose) fprintf(stderr,"npipeclient: fatal: %s\n",s); exit(n); } Xvoid dies(n,s,t) int n; char *s; char *t; X{ if (flagverbose) fprintf(stderr,"npipeclient: fatal: %s%s\n",s,t); exit(n); } Xvoid diep(n,s) int n; char *s; X{ if (flagverbose) sprintf(errbuf,"npipeclient: fatal: %s",s); perror(errbuf); exit(n); } Xvoid warnp(s) char *s; X{ if (flagverbose) sprintf(errbuf,"npipeclient: warning: %s",s); perror(errbuf); } Xvoid warnsp(s,t) char *s; char *t; X{ if (flagverbose) sprintf(errbuf,"npipeclient: warning: %s%s",s,t); perror(errbuf); } X Xmain(argc,argv,envp) Xint argc; Xchar *argv[]; Xchar *envp[]; X{ X int *d; X int numfiles; X int arguid; X int uid; X int i; X static char npipelocal[200]; X static char npiperemote[300]; X int fdserv; X int fdlock; X int fds2c; X int fdc2s; X char fnlock[200]; X char fns2c[200]; X char fnc2s[200]; X char fnserv[200]; X int m; X int n; X int r; X char buf[100]; X char *uname; X int pid; X int opt; X X pid = getpid(); X X while ((opt = getopt(argc,argv,"qQ")) != EOF) X switch(opt) X { X case 'q': X flagverbose = 0; break; X case 'Q': X flagverbose = 1; break; X case '?': X break; /*XXX*/ X default: X break; /*XXX*/ X } X argc -= optind; argv += optind; X X if (argc < 3) X die(1,"need at least three arguments"); X X uid = getuid(); X if (username2uid(argv[0],&arguid) == -1) X dies(2,"cannot figure out username ",argv[0]); X /* XXX: any permission checks? */ X X for (i = 0;argv[0][i];++i) X if ((argv[0][i] == '/') || (argv[0][i] == ':') || (argv[0][i] == '.') || (argv[0][i] == '\n')) X dies(4,"illegal characters in username ",argv[0]); X X for (i = 0;argv[1][i];++i) X if ((argv[1][i] == '/') || (argv[1][i] == '.') || (argv[0][i] == '\n')) X dies(5,"illegal characters in service ",argv[1]); X X close(6); X close(7); /* XXX: these have implications that should be documented */ X X signal(SIGPIPE,SIG_IGN); X X if (uid2username(uid,&uname) == -1) /* will never happen */ X die(2,"cannot figure out my own username"); X X numfiles = NUMFILES; X d = (int *) malloc(numfiles * sizeof(int)); X if (!d) X die(6,"cannot malloc"); X for (i = 0;i < numfiles;++i) X d[i] = -1; X X if (strlen(NPIPEDIR) + strlen(argv[0]) + strlen(argv[1]) + 1 > 90) /*XXX*/ X die(8,"server filenames too long"); X sprintf(fnserv,"%s/%ds%s",NPIPEDIR,arguid,argv[1]); X sprintf(fnlock,"%s/%dl%s",NPIPEDIR,arguid,argv[1]); X sprintf(fns2c,"%s/s2c.%d",NPIPEDIR,pid); X sprintf(fnc2s,"%s/c2s.%d",NPIPEDIR,pid); X sprintf(buf,"%d",pid); X n = strlen(buf); X if (n + strlen(uname) + 10 > sizeof(buf)) X die(8,"username too long"); X strcpy(buf + n + 1,uname); X n += strlen(uname) + 2; X X if (env_init() == -1) X die(11,"cannot init environment"); X if (env_put("PROTO=NPIPE") == -1) X die(11,"out of memory putting PROTO into environment"); X sprintf(npipelocal,"NPIPELOCAL=%s",uname); X if (env_put(npipelocal) == -1) X die(11,"out of memory putting NPIPELOCAL into environment"); X sprintf(npiperemote,"NPIPEREMOTE=%s:%s",argv[0],argv[1]); X if (env_put(npiperemote) == -1) X die(11,"out of memory putting NPIPEREMOTE into environment"); X X /* XXX: fork? not necessary here */ X X /* Action! */ X X fdlock = open(fnlock,O_RDWR,0); X if (fdlock == -1) X diep(1,"cannot open lock file"); X /* Here's the tricky bit: file locking. :-( */ X /* XXX: We assume that anyone with named pipes also has lockf(). */ X if (lockf(fdlock,F_LOCK,1) == -1) X diep(1,"cannot wait for lock on lock file"); X X fdserv = open(fnserv,O_WRONLY,0); X if (fdserv == -1) X diep(1,"cannot open server's named pipe"); X X if (mknod(fns2c,010600,0) == -1) X diep(1,"cannot create server-to-client pipe"); X if (mknod(fnc2s,010600,0) == -1) X { X unlink(fns2c); X diep(1,"cannot create client-to-server pipe"); X } X X m = 0; X r = 0; X while ((m < n) && ((r = write(fdserv,buf + m,n - m)) > 0)) X m += r; X if (r <= 0) X { X unlink(fnc2s); X unlink(fns2c); X die(1,"cannot write pid and username to server"); X } X close(fdserv); /*XXX*/ X X fds2c = open(fns2c,O_RDONLY,0); /* blocks */ X unlink(fns2c); X if (fds2c == -1) X { X unlink(fnc2s); X die(1,"cannot open server-to-client pipe"); X } X fdc2s = open(fnc2s,O_WRONLY,0); /* blocks */ X unlink(fnc2s); X if (fdc2s == -1) X die(1,"cannot open client-to-server pipe"); X X if (read(fds2c,buf,1) < 1) X diep(1,"cannot read from server-to-client pipe"); X X close(fdlock); X X /* Now put fds2c into descriptor 6 and fdc2s into descriptor 7. */ X /* XXX: should check that fdc2s and fds2c are small enough */ X d[fds2c] = 6; X d[fdc2s] = 7; X if (dupdup(d,numfiles) == -1) X diep(10,"cannot dup connection to server"); X X if (setreuid(uid,uid) == -1) X diep(1,"cannot setreuid"); /* will never happen */ X signal(SIGPIPE,SIG_DFL); X execvp(argv[2],argv + 2); X warnsp("cannot exec ",argv[2]); X exit(1); X} END_OF_FILE if test 5236 -ne `wc -c <'npipeclient.c'`; then echo shar: \"'npipeclient.c'\" unpacked with wrong size! fi # end of 'npipeclient.c' fi if test -f 'npipeserver.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'npipeserver.c'\" else echo shar: Extracting \"'npipeserver.c'\" \(7393 characters\) sed "s/^X//" >'npipeserver.c' <<'END_OF_FILE' X/* Public domain. */ X X#include <stdio.h> X#include <signal.h> X#include <sys/types.h> X#include <sys/stat.h> X#include <sys/file.h> X#include <sys/wait.h> X#include <sys/time.h> X#include "dupdup.h" X#include "malloc.h" X#include "errno.h" X#include "username.h" X#include "env.h" X#include "getopt.h" X#include "numfiles.h" X X#ifndef O_RDONLY X#define O_RDONLY 0 X#endif X#ifndef O_WRONLY X#define O_WRONLY 1 X#endif X Xstatic int flagprintlocal = 0; Xstatic int flagverbose = 1; Xstatic char fnserv[200]; Xstatic char fnlock[200]; Xstatic char fnexcl[200]; Xstatic int fdserv; Xstatic int fdlock; Xstatic int fdexcl; Xstatic int flagbindok = 0; X Xunlinkem() X{ X if (flagbindok) X { X unlink(fnserv); X unlink(fnlock); X unlink(fnexcl); X } X} X XSIGRET sigterm() X{ X unlinkem(); X exit(0); X} X XSIGRET sigchld() X{ X wait((int *) 0); /*XXX*/ X} X Xstatic char errbuf[500]; Xvoid die(n,s) int n; char *s; X{ if (flagverbose) fprintf(stderr,"npipeserver: fatal: %s\n",s); exit(n); } Xvoid dies(n,s,t) int n; char *s; char *t; X{ if (flagverbose) fprintf(stderr,"npipeserver: fatal: %s%s\n",s,t); exit(n); } Xvoid diep(n,s) int n; char *s; X{ if (flagverbose) sprintf(errbuf,"npipeserver: fatal: %s",s); perror(errbuf); exit(n); } Xvoid warnp(s) char *s; X{ if (flagverbose) sprintf(errbuf,"npipeserver: warning: %s",s); perror(errbuf); } Xvoid warnsp(s,t) char *s; char *t; X{ if (flagverbose) sprintf(errbuf,"npipeserver: warning: %s%s",s,t); perror(errbuf); } X Xmain(argc,argv,envp) Xint argc; Xchar *argv[]; Xchar *envp[]; X{ X int *d; X int numfiles; X int uid; X int arguid; X int i; X int r; X int n; X char fns2c[200]; X char fnc2s[200]; X char buf[100]; X int pid; X int fds2c; X int fdc2s; X char npipelocal[200]; /*XXX*/ X char npiperemote[300]; /*XXX*/ X int opt; X X while ((opt = getopt(argc,argv,"qQ4")) != EOF) X switch(opt) X { X case 'q': X flagverbose = 0; X break; X case 'Q': X flagverbose = 1; X break; X case '4': X flagprintlocal = 1; X break; X case '?': X break; /*XXX*/ X default: X break; /*XXX*/ X } X argc -= optind; argv += optind; X X if (argc < 3) X die(1,"need at least three arguments"); X X uid = getuid(); X if (username2uid(argv[0],&arguid) == -1) X dies(2,"cannot figure out username ",argv[0]); X if (uid && (uid != arguid)) X dies(3,"permission denied for username ",argv[0]); X X for (i = 0;argv[0][i];++i) X if ((argv[0][i] == '/') || (argv[0][i] == ':') || (argv[0][i] == '.') || (argv[0][i] == '\n')) X dies(4,"illegal characters in username ",argv[0]); X X for (i = 0;argv[1][i];++i) X if ((argv[1][i] == '/') || (argv[1][i] == '.') || (argv[0][i] == '\n')) X dies(5,"illegal characters in service ",argv[1]); X X close(6); X close(7); /* XXX: these have implications that should be documented */ X X numfiles = NUMFILES; X d = (int *) malloc(numfiles * sizeof(int)); X if (!d) X die(6,"cannot malloc"); X for (i = 0;i < numfiles;++i) X d[i] = -1; X X if (strlen(NPIPEDIR) + strlen(argv[0]) + strlen(argv[1]) + 1 > 90) /*XXX*/ X die(8,"server filenames too long"); X sprintf(fnserv,"%s/%ds%s",NPIPEDIR,arguid,argv[1]); X sprintf(fnlock,"%s/%dl%s",NPIPEDIR,arguid,argv[1]); X sprintf(fnexcl,"%s/%de%s",NPIPEDIR,arguid,argv[1]); X /* XXX: Even if arguid has 5 digits, this leaves 8 characters for X the service in System V filesystems. It might be worth switching X to a fixed-length or hex format... I dunno. */ X X signal(SIGTERM,sigterm); X signal(SIGINT,sigterm); X signal(SIGCHLD,sigchld); X signal(SIGPIPE,SIG_IGN); X X if (env_init() == -1) X die(11,"cannot init environment"); X if (env_put("PROTO=NPIPE") == -1) X die(11,"out of memory putting PROTO into environment"); X sprintf(npipelocal,"NPIPELOCAL=%s:%s",argv[0],argv[1]); X if (env_put(npipelocal) == -1) X die(11,"out of memory putting NPIPELOCAL into environment"); X X if (flagprintlocal) X { X FILE *fd4; X fd4 = fdopen(4,"w"); X if (!fd4) X die(1,"cannot print NPIPELOCAL on fd 4"); X fprintf(fd4,"%s:%s\n",argv[0],argv[1]); X fclose(fd4); X close(4); /* just in case */ X } X X /* Action! */ X X fdexcl = creat(fnexcl,0); X if (fdexcl == -1) /* somebody else got here before us */ X die(1,"service already in use"); X /* XXX: there's a race right here */ X flagbindok = 1; X fdlock = creat(fnlock,0); /* refuse connections until we're ready */ X if (fdlock == -1) X diep(1,"cannot create lock file"); X if (write(fdlock,buf,1) < 1) X diep(1,"cannot write to lock file"); X if (mknod(fnserv,010600,0) == -1) X diep(1,"cannot create server's named pipe"); X if (chmod(fnlock,0600) == -1) /* gaargh. fchmod(fdlock,0600)! */ X diep(1,"cannot set modes on lock file"); X X /* I hate programming like this. In a real OS I'd just create the named X pipe---outside the directory hierarchy!---then try to link it in. X None of this stupid testing and locking and so on. Gaargh. */ X X for (;;) X { X /* XXX: should we really die on simple I/O errors? */ X for (;;) X { X fdserv = open(fnserv,O_RDONLY,0); /* this blocks */ X /* XXX: don't accept too many requests at once? */ X if (fdserv == -1) /* down the drain */ X { X if (errno == EINTR) X continue; X unlinkem(); X diep(1,"cannot open server's named pipe"); X } X break; X } X n = 0; X r = 0; X while ((n < sizeof(buf)) && ((r = read(fdserv,buf + n,sizeof(buf) - n)) > 0)) X n += r; X if (r > 0) X ; /* overflowed buffer---shouldn't happen, we'll just truncate it */ X if (r == -1) X { X unlinkem(); X diep(1,"cannot read client's pid and username"); X } X /* XXX: do something for killer? */ X close(fdserv); X unlink(fnserv); /*XXX: check for errors?*/ X for (i = 0;i < n;++i) X if (!buf[i]) X break; X if (i == n) X { X unlinkem(); X die(1,"cannot read remote username"); X } X /* XXX: if i is n - 1, we have a null username */ X ++i; /* now buf + i is the username */ X if (sscanf(buf,"%d",&pid) < 1) X { X unlinkem(); X die(1,"cannot read remote pid"); X } X if (!pid) /* npipekiller */ X sigterm(); /*XXX*/ X X switch(fork()) X { X case -1: X warnp("cannot fork"); X /* XXX: say anything to other side? */ X break; X X case 0: X X close(fdlock); X close(fdexcl); X X sprintf(fns2c,"%s/s2c.%d",NPIPEDIR,pid); X sprintf(fnc2s,"%s/c2s.%d",NPIPEDIR,pid); X X fds2c = open(fns2c,O_WRONLY,0); /* blocks */ X if (fds2c == -1) X diep(1,"cannot open server-to-client pipe"); X fdc2s = open(fnc2s,O_RDONLY,0); /* blocks */ X if (fdc2s == -1) X diep(1,"cannot open client-to-server pipe"); X X if (write(fds2c,buf,1) < 1) X diep(1,"cannot write on server-to-client pipe"); X X sprintf(npiperemote,"NPIPEREMOTE=%s",buf + i); X if (env_put(npiperemote) == -1) X die(1,"out of memory putting NPIPEREMOTE into environment"); X X /* Now put fdc2s into descriptor 6 and fds2c into descriptor 7. */ X /* XXX: should check that fdc2s and fds2c are small enough */ X d[fdc2s] = 6; X d[fds2c] = 7; X if (dupdup(d,numfiles) == -1) X diep(1,"cannot dup connection to client"); X X if (setreuid(uid,uid) == -1) X diep(1,"cannot setreuid"); /* will never happen */ X signal(SIGPIPE,SIG_DFL); X execvp(argv[2],argv + 2); X warnsp("cannot exec ",argv[2]); X exit(1); X X default: X ; X } X /* the cycle begins anew */ X if (mknod(fnserv,010600,0) == -1) X { X unlinkem(); X diep(1,"cannot create server's named pipe"); X } X } X} END_OF_FILE if test 7393 -ne `wc -c <'npipeserver.c'`; then echo shar: \"'npipeserver.c'\" unpacked with wrong size! fi # end of 'npipeserver.c' fi if test -f 'tcpclient.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'tcpclient.c'\" else echo shar: Extracting \"'tcpclient.c'\" \(5066 characters\) sed "s/^X//" >'tcpclient.c' <<'END_OF_FILE' X/* Public domain. */ X X#include <stdio.h> X#include <sys/types.h> X#include <sys/socket.h> X#include <sys/param.h> X#include <netinet/in.h> X#include <arpa/inet.h> X#include <signal.h> X#include "dupdup.h" X#include "malloc.h" X#include "errno.h" X#include "username.h" X#include "hostname.h" X#include "portname.h" X#include "env.h" X#include "getopt.h" X#include "authuser.h" X#include "numfiles.h" X X#ifndef MAXHOSTNAMELEN X#define MAXHOSTNAMELEN 64 X#endif X Xstatic int flagdo931 = 1; Xstatic int flagverbose = 1; Xstatic struct sockaddr_in sa; Xstatic struct sockaddr_in saloc; X Xstatic char errbuf[500]; Xvoid die(n,s) int n; char *s; X{ if (flagverbose) fprintf(stderr,"tcpclient: fatal: %s\n",s); exit(n); } Xvoid dies(n,s,t) int n; char *s; char *t; X{ if (flagverbose) fprintf(stderr,"tcpclient: fatal: %s%s\n",s,t); exit(n); } Xvoid diep(n,s) int n; char *s; X{ if (flagverbose) sprintf(errbuf,"tcpclient: fatal: %s",s); perror(errbuf); exit(n); } Xvoid warnp(s) char *s; X{ if (flagverbose) sprintf(errbuf,"tcpclient: warning: %s",s); perror(errbuf); } Xvoid warnsp(s,t) char *s; char *t; X{ if (flagverbose) sprintf(errbuf,"tcpclient: warning: %s%s",s,t); perror(errbuf); } X Xmain(argc,argv,envp) Xint argc; Xchar *argv[]; Xchar *envp[]; X{ X int t; X int *d; X int tdup; X int numfiles; X int i; X static char tcplocal[200]; X static char tcpremote[300]; X int opt; X int uid; X char *uname; X char lochost[MAXHOSTNAMELEN]; X struct in_addr locinaddr; X int port; X int locport; X int dummy; X char *ruser; X X locport = 0; X X while ((opt = getopt(argc,argv,"qQp:rR")) != EOF) X switch(opt) X { X case 'q': X flagverbose = 0; break; X case 'Q': X flagverbose = 1; break; X case 'p': X /* XXX: check that optarg is numeric? */ X locport = atoi(optarg); X break; X case 'r': X flagdo931 = 1; X break; X case 'R': X flagdo931 = 0; X break; X case '?': X break; /*XXX*/ X default: X break; /*XXX*/ X } X argc -= optind; argv += optind; X X if (argc < 3) X die(1,"need at least three arguments"); X X uid = getuid(); X if (uid2username(uid,&uname) == -1) /* will never happen */ X die(2,"cannot figure out my own username"); X X gethostname(lochost,sizeof(lochost)); X if (hostname2inaddr(lochost,&locinaddr) == -1) X die(2,"cannot figure out my own hostname"); X X if ((argv[0][0] == '0') && (argv[0][1] == 0)) X sa.sin_addr.s_addr = INADDR_ANY; /* special case "0" */ X else X { X if (hostname2inaddr(argv[0],&sa.sin_addr) == -1) X dies(2,"cannot figure out hostname ",argv[0]); X } X X if (portname2port(argv[1],&port,"tcp") == -1) X dies(2,"cannot figure out portname ",argv[1]); X port = port & 65535; X sa.sin_port = htons((short) port); X X close(6); X close(7); /* XXX: these have implications that should be documented */ X X signal(SIGPIPE,SIG_IGN); X X numfiles = NUMFILES; X d = (int *) malloc(numfiles * sizeof(int)); X if (!d) X die(6,"cannot malloc"); X for (i = 0;i < numfiles;++i) X d[i] = -1; X X if (env_init() == -1) X die(11,"cannot init environment"); X if (env_put("PROTO=TCP") == -1) X die(11,"out of memory putting PROTO into environment"); X X t = socket(AF_INET,SOCK_STREAM,0); X if (t == -1) X diep(7,"cannot create client socket"); X sa.sin_family = AF_INET; X if (locport) X { X /* attempt to bind to given local port */ X /* could do this in advance in all cases if we were desperate to X get TCPLOCAL figured out as fast as possible */ X saloc.sin_family = AF_INET; X saloc.sin_addr.s_addr = INADDR_ANY; X saloc.sin_port = htons((short) locport); X if (bind(t,&saloc,sizeof(saloc)) == -1) X diep(9,"cannot bind to local port"); X } X if (connect(t,&sa,sizeof(sa)) == -1) X /* XXX: HRRFC says we should try alternate addresses */ X diep(9,"cannot connect to server"); X dummy = sizeof(saloc); X if (getsockname(t,&saloc,&dummy) == -1) X diep(9,"cannot figure out local connection address"); X X locport = ntohs(saloc.sin_port); X X ruser = ""; X if (flagdo931) X { X ruser = auth_tcpuser((unsigned long) sa.sin_addr.s_addr X ,(unsigned short) locport X ,(unsigned short) port); X if (!ruser) X ruser = ""; /* XXX: any other suggestions? */ X } X X sprintf(tcplocal,"TCPLOCAL=%s@%s(%s):%d" X ,uname X ,inet_ntoa(locinaddr) /* could use saloc.sin_addr */ X ,lochost /* could use hostname of saloc.sin_addr */ X ,locport X ); X if (env_put(tcplocal) == -1) X die(11,"out of memory putting TCPLOCAL into environment"); X sprintf(tcpremote,"TCPREMOTE=%s@%s(%s):%d" X ,ruser X ,inet_ntoa(sa.sin_addr) X ,argv[0] /* could do lookup instead, but why bother? */ X ,port X ); X if (env_put(tcpremote) == -1) X die(11,"out of memory putting TCPREMOTE into environment"); X X /* XXX: fork? not necessary here */ X X tdup = dup(t); X if (tdup == -1) X diep(10,"cannot dup connection to server"); X X /* Now put t into descriptor 6 and tdup into descriptor 7. */ X /* XXX: should check that t and tdup are small enough */ X d[t] = 6; X d[tdup] = 7; X if (dupdup(d,numfiles) == -1) X diep(10,"cannot dup connection to server"); X X signal(SIGPIPE,SIG_DFL); X execvp(argv[2],argv + 2); X warnsp("cannot exec ",argv[2]); X exit(1); X} END_OF_FILE if test 5066 -ne `wc -c <'tcpclient.c'`; then echo shar: \"'tcpclient.c'\" unpacked with wrong size! fi # end of 'tcpclient.c' fi if test -f 'tcpserver.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'tcpserver.c'\" else echo shar: Extracting \"'tcpserver.c'\" \(7346 characters\) sed "s/^X//" >'tcpserver.c' <<'END_OF_FILE' X/* Public domain. */ X X#include <stdio.h> X#include <signal.h> X#include <sys/types.h> X#include <sys/socket.h> X#include <sys/stat.h> X#include <sys/wait.h> X#include <sys/file.h> X#include <netinet/in.h> X#include <netdb.h> X#include <arpa/inet.h> X#include <sys/time.h> X#include <sys/param.h> X#include "dupdup.h" X#include "malloc.h" X#include "errno.h" X#include "username.h" X#include "hostname.h" X#include "portname.h" X#include "env.h" X#include "getopt.h" X#include "authuser.h" X#include "numfiles.h" X X#ifndef MAXHOSTNAMELEN X#define MAXHOSTNAMELEN 64 X#endif X Xstatic int flagdo931 = 1; Xstatic int flagprintlocal = 0; Xstatic int flagverbose = 1; Xstatic struct sockaddr_in sabind; X Xstatic char *fnrecord; X Xint record(uid,host,port,pid) Xint uid; Xchar *host; Xint port; Xint pid; X{ X char *home; X char pidstr[50]; X int fd; X /* The idea is to record our pid in $HOME/.tcpserver.host.uid.port. */ X /* Then tcpkiller can read the pid from there, and send us a signal. */ X /* TCPHOME overrides HOME if it is set. */ X /* XXX: should we choose a signal that won't hurt ordinary processes? */ X /* XXX: anything good to do if $HOME is undefined or unwritable? */ X X home = env_get("TCPHOME"); X if (!home) X { X home = env_get("HOME"); X if (!home) X return -1; X } X fnrecord = malloc(strlen(home) + strlen(host) + 100); X if (!fnrecord) X return -1; X sprintf(fnrecord,"%s/.tcpserver.%s.%d.%d",home,host,uid,port); X X unlink(fnrecord); /* XXX */ X X fd = open(fnrecord,O_WRONLY | O_CREAT | O_EXCL,0400); X if (fd == -1) X { X /* XXX */ X return -1; X } X X sprintf(pidstr,"%d",pid); X X write(fd,pidstr,strlen(pidstr) + 1); /* XXX: check for partial writes */ X X close(fd); X return 0; X} X XSIGRET sigterm() X{ X if (fnrecord) X unlink(fnrecord); /*XXX*/ X exit(0); X} X XSIGRET sigchld() X{ X wait((union wait *) 0); /*XXX*/ X} X Xstatic char errbuf[500]; Xvoid die(n,s) int n; char *s; X{ if (flagverbose) fprintf(stderr,"tcpserver: fatal: %s\n",s); exit(n); } Xvoid dies(n,s,t) int n; char *s; char *t; X{ if (flagverbose) fprintf(stderr,"tcpserver: fatal: %s%s\n",s,t); exit(n); } Xvoid diep(n,s) int n; char *s; X{ if (flagverbose) sprintf(errbuf,"tcpserver: fatal: %s",s); perror(errbuf); exit(n); } Xvoid warnp(s) char *s; X{ if (flagverbose) sprintf(errbuf,"tcpserver: warning: %s",s); perror(errbuf); } Xvoid warnsp(s,t) char *s; char *t; X{ if (flagverbose) sprintf(errbuf,"tcpserver: warning: %s%s",s,t); perror(errbuf); } X Xmain(argc,argv,envp) Xint argc; Xchar *argv[]; Xchar *envp[]; X{ X int s; X int dummy; X int t; X int *d; X int tdup; X int numfiles; X static struct sockaddr_in sa; X int uid; X int i; X char lochost[MAXHOSTNAMELEN]; X struct in_addr locinaddr; X char tcplocal[300]; /*XXX*/ X char tcpremote[300]; /*XXX*/ X int locport; X int remport; X char *remhost; X char *ruser; X char *uname; X int opt; X X while ((opt = getopt(argc,argv,"qQ4rR")) != EOF) X switch(opt) X { X case 'q': X flagverbose = 0; X break; X case 'Q': X flagverbose = 1; X break; X case '4': X flagprintlocal = 1; X break; X case 'r': X flagdo931 = 1; X break; X case 'R': X flagdo931 = 0; X break; X case '?': X break; /*XXX*/ X default: X break; /*XXX*/ X } X argc -= optind; argv += optind; X X if (argc < 3) X die(1,"need at least three arguments"); X X uid = getuid(); X if (uid2username(uid,&uname) == -1) /* will never happen */ X die(2,"cannot figure out my own username"); X X gethostname(lochost,sizeof(lochost)); X if (hostname2inaddr(lochost,&locinaddr) == -1) X die(2,"cannot figure out my own hostname"); X X if ((argv[0][0] == '0') && (argv[0][1] == 0)) X sabind.sin_addr.s_addr = INADDR_ANY; /* special case "0" */ X else X { X if (hostname2inaddr(argv[0],&sabind.sin_addr) == -1) X dies(2,"cannot figure out hostname ",argv[0]); X /* XXX: check whether sabind.sin_addr matches locinaddr? */ X } X X if (portname2port(argv[1],&locport,"tcp") == -1) X dies(2,"cannot figure out portname ",argv[1]); X locport = locport & 65535; X sabind.sin_port = htons((short) locport); X X close(6); X close(7); /* XXX: these have implications that should be documented */ X signal(SIGTERM,sigterm); X signal(SIGINT,sigterm); X signal(SIGCHLD,sigchld); X signal(SIGPIPE,SIG_IGN); X X numfiles = NUMFILES; X d = (int *) malloc(numfiles * sizeof(int)); X if (!d) X die(6,"cannot malloc"); X for (i = 0;i < numfiles;++i) X d[i] = -1; X X s = socket(AF_INET,SOCK_STREAM,0); X if (s == -1) X diep(7,"cannot create server socket"); X sabind.sin_family = AF_INET; X if (bind(s,(struct sockaddr *) &sabind,sizeof(sabind)) == -1) X diep(9,"cannot bind server socket"); X if (listen(s,5) == -1) X diep(10,"cannot listen on server socket"); X dummy = sizeof(sabind); X if (getsockname(s,&sabind,&dummy) == -1) X diep(9,"cannot figure out local connection address"); X locport = ntohs(sabind.sin_port); X X if (env_init() == -1) X die(11,"cannot init environment"); X if (env_put("PROTO=TCP") == -1) X die(11,"out of memory putting PROTO into environment"); X sprintf(tcplocal,"TCPLOCAL=%s@%s(%s):%d" X ,uname X ,inet_ntoa(locinaddr) /* could use sabind.sin_addr */ X ,lochost X ,locport X ); X if (env_put(tcplocal) == -1) X die(11,"out of memory putting TCPLOCAL into environment"); X X if (flagprintlocal) X { X FILE *fd4; X fd4 = fdopen(4,"w"); X if (!fd4) X die(1,"cannot print TCPLOCAL on fd 4"); X fprintf(fd4,"%s@%s(%s):%d\n" X ,uname X ,inet_ntoa(locinaddr) /* could use sabind.sin_addr */ X ,lochost X ,locport X ); X fclose(fd4); X close(4); /* just in case */ X } X X if (record(uid,lochost,locport,getpid()) == -1) X die(1,"cannot record process id for tcpkiller"); X /* XXX: could use argv[0] instead of lochost here... hmmm */ X X for (;;) X { X /* XXX: don't accept too many requests at once? */ X dummy = sizeof(sa); X t = accept(s,(struct sockaddr *) &sa,&dummy); X if (t == -1) /* so when can this happen? */ X { X switch(errno) X { X case SIGCHLD: break; /*XXX*/ X case EINTR: break; /*XXX*/ X case EMFILE: sleep(1); break; /*XXX*/ X case ENFILE: sleep(1); break; /*XXX*/ X default: ; /*XXX*/ X } X continue; X } X X switch(fork()) X { X case -1: X warnp("cannot fork"); X break; X X case 0: X X remport = ntohs(sa.sin_port); X ruser = ""; X if (flagdo931) X { X ruser = auth_tcpuser((unsigned long) sa.sin_addr.s_addr X ,(unsigned short) locport X ,(unsigned short) remport); X if (!ruser) X ruser = ""; /* XXX: any other suggestions? */ X } X if (inaddr2hostname(sa.sin_addr,&remhost) == -1) /* will never happen */ X die(1,"cannot find hostname"); X sprintf(tcpremote,"TCPREMOTE=%s@%s(%s):%d" X ,ruser X ,inet_ntoa(sa.sin_addr) X ,remhost X ,remport X ); X if (env_put(tcpremote) == -1) X die(1,"out of memory putting TCPREMOTE into environment"); X X tdup = dup(t); X if (tdup == -1) X diep(1,"cannot dup connection to client"); X X /* Now put t into descriptor 6 and tdup into descriptor 7. */ X /* XXX: should check that t and tdup are small enough */ X d[t] = 6; X d[tdup] = 7; X if (dupdup(d,numfiles) == -1) X diep(1,"cannot dup connection to client"); X X signal(SIGPIPE,SIG_DFL); X execvp(argv[2],argv + 2); X warnsp("cannot exec ",argv[2]); X exit(1); X X default: X close(t); X } X } X} END_OF_FILE if test 7346 -ne `wc -c <'tcpserver.c'`; then echo shar: \"'tcpserver.c'\" unpacked with wrong size! fi # end of 'tcpserver.c' fi if test -f 'undomserver.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'undomserver.c'\" else echo shar: Extracting \"'undomserver.c'\" \(5582 characters\) sed "s/^X//" >'undomserver.c' <<'END_OF_FILE' X/* Public domain. */ X X#include <stdio.h> X#include <signal.h> X#include <sys/types.h> X#include <sys/socket.h> X#include <sys/stat.h> X#include <sys/wait.h> X#include <sys/un.h> X#include <sys/time.h> X#include "dupdup.h" X#include "malloc.h" X#include "errno.h" X#include "username.h" X#include "env.h" X#include "getopt.h" X#include "numfiles.h" X Xstatic int flagprintlocal = 0; Xstatic int flagverbose = 1; Xstatic struct sockaddr_un sabind; Xstatic int flagbindok = 0; X XSIGRET sigterm() X{ X if (flagbindok) X unlink(sabind.sun_path); X exit(0); X} X XSIGRET sigchld() X{ X wait((union wait *) 0); /*XXX*/ X} X Xstatic char errbuf[500]; Xvoid die(n,s) int n; char *s; X{ if (flagverbose) fprintf(stderr,"undomserver: fatal: %s\n",s); exit(n); } Xvoid dies(n,s,t) int n; char *s; char *t; X{ if (flagverbose) fprintf(stderr,"undomserver: fatal: %s%s\n",s,t); exit(n); } Xvoid diep(n,s) int n; char *s; X{ if (flagverbose) sprintf(errbuf,"undomserver: fatal: %s",s); perror(errbuf); exit(n); } Xvoid warnp(s) char *s; X{ if (flagverbose) sprintf(errbuf,"undomserver: warning: %s",s); perror(errbuf); } Xvoid warnsp(s,t) char *s; char *t; X{ if (flagverbose) sprintf(errbuf,"undomserver: warning: %s%s",s,t); perror(errbuf); } X Xmain(argc,argv,envp) Xint argc; Xchar *argv[]; Xchar *envp[]; X{ X int s; X int dummy; X int t; X int *d; X int tdup; X int numfiles; X static struct sockaddr_un sa; X int uid; X int arguid; X int i; X char undomlocal[200]; /*XXX*/ X char undomremote[300]; /*XXX*/ X unsigned char urlen; X int opt; X X while ((opt = getopt(argc,argv,"qQ4")) != EOF) X switch(opt) X { X case 'q': X flagverbose = 0; X break; X case 'Q': X flagverbose = 1; X break; X case '4': X flagprintlocal = 1; X break; X case '?': X break; /*XXX*/ X default: X break; /*XXX*/ X } X argc -= optind; argv += optind; X X if (argc < 3) X die(1,"need at least three arguments"); X X uid = getuid(); X if (username2uid(argv[0],&arguid) == -1) X dies(2,"cannot figure out username ",argv[0]); X if (uid && (uid != arguid)) X dies(3,"permission denied for username ",argv[0]); X X for (i = 0;argv[0][i];++i) X if ((argv[0][i] == '/') || (argv[0][i] == ':') || (argv[0][i] == '.') || (argv[0][i] == '\n')) X dies(4,"illegal characters in username ",argv[0]); X X for (i = 0;argv[1][i];++i) X if ((argv[1][i] == '/') || (argv[1][i] == '.') || (argv[0][i] == '\n')) X dies(5,"illegal characters in service ",argv[1]); X X close(6); X close(7); /* XXX: these have implications that should be documented */ X X numfiles = NUMFILES; X d = (int *) malloc(numfiles * sizeof(int)); X if (!d) X die(6,"cannot malloc"); X for (i = 0;i < numfiles;++i) X d[i] = -1; X X if (strlen(UNDOMDIR) + strlen(argv[0]) + strlen(argv[1]) + 1 > 100) /*XXX*/ X die(8,"server socket name too long"); X sprintf(sabind.sun_path,"%s/%s:%s",UNDOMDIR,argv[0],argv[1]); X X signal(SIGTERM,sigterm); X signal(SIGINT,sigterm); X signal(SIGCHLD,sigchld); X X if (env_init() == -1) X die(11,"cannot init environment"); X if (env_put("PROTO=UNDOM") == -1) X die(11,"out of memory putting PROTO into environment"); X sprintf(undomlocal,"UNDOMLOCAL=%s:%s",argv[0],argv[1]); X if (env_put(undomlocal) == -1) X die(11,"out of memory putting UNDOMLOCAL into environment"); X sprintf(undomremote,"UNDOMREMOTE="); X X if (flagprintlocal) X { X FILE *fd4; X fd4 = fdopen(4,"w"); X if (!fd4) X die(1,"cannot print UNDOMLOCAL on fd 4"); X fprintf(fd4,"%s:%s\n",argv[0],argv[1]); X fclose(fd4); X close(4); /* just in case */ X } X X s = socket(AF_UNIX,SOCK_STREAM,0); X if (s == -1) X diep(7,"cannot create server socket"); X sabind.sun_family = AF_UNIX; X if (bind(s,(struct sockaddr *) &sabind,strlen(sabind.sun_path) + 2) == -1) X dies(9,"cannot bind server socket ",sabind.sun_path); X /* XXX: there's a race right here */ X flagbindok = 1; X if (listen(s,5) == -1) X { X unlink(sabind.sun_path); X diep(10,"cannot listen on server socket"); X } X X for (;;) X { X /* XXX: don't accept too many requests at once? */ X dummy = sizeof(sa); X t = accept(s,(struct sockaddr *) &sa,&dummy); X if (t == -1) /* so when can this happen? */ X { X switch(errno) X { X case SIGCHLD: break; /*XXX*/ X case EINTR: break; /*XXX*/ X case EMFILE: sleep(1); break; /*XXX*/ X case ENFILE: sleep(1); break; /*XXX*/ X default: ; /*XXX*/ X } X continue; X } X X if (((i = read(t,&urlen,1)) == -1) || (!i)) X continue; /* XXX: error message? */ X if (urlen == 0) /* XXX: undomkiller */ X sigterm(); X X switch(fork()) X { X case -1: X warnp("cannot fork"); X break; X X case 0: X X if (urlen > sizeof(undomremote) - strlen(undomremote) - 5) X die(1,"remote username too long"); X if (read(t,undomremote + strlen(undomremote),urlen) < urlen) X die(1,"cannot read remote username"); X undomremote[sizeof(undomremote) - 1] = 0; /* just in case */ X /* XXX: warn if it's not already just the right length? */ X X if (env_put(undomremote) == -1) X die(1,"out of memory putting UNDOMREMOTE into environment"); X X tdup = dup(t); X if (tdup == -1) X diep(1,"cannot dup connection to client"); X X /* Now put t into descriptor 6 and tdup into descriptor 7. */ X /* XXX: should check that t and tdup are small enough */ X d[t] = 6; X d[tdup] = 7; X if (dupdup(d,numfiles) == -1) X diep(1,"cannot dup connection to client"); X X if (setreuid(uid,uid) == -1) X diep(1,"cannot setreuid"); /* will never happen */ X execvp(argv[2],argv + 2); X warnsp("cannot exec ",argv[2]); X exit(1); X X default: X close(t); X } X } X} END_OF_FILE if test 5582 -ne `wc -c <'undomserver.c'`; then echo shar: \"'undomserver.c'\" unpacked with wrong size! fi # end of 'undomserver.c' fi echo shar: End of archive 2 \(of 2\). cp /dev/null ark2isdone MISSING="" for I in 1 2 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked both archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0