Path: seismo!harvard!talcott!panda!sources-request From: sources-request@panda.UUCP Newsgroups: mod.sources Subject: Generic client and server commands for 4.2BSD Message-ID: <1623@panda.UUCP> Date: 8 Apr 86 14:04:52 GMT Sender: jpn@panda.UUCP Lines: 736 Approved: jpn@panda.UUCP Mod.sources: Volume 4, Issue 54 Submitted by: talcott!ut-sally!im4u!jsq (John Quarterman) : This is a shar archive. Extract with sh, not csh. echo x - README sed -e 's/^X//' > README << '!RoNnIe!RaYgUn!' XThis posting includes source and man pages for two programs called Xclient and server. They form a remote command execution facility Xsimilar to rsh, except that the user of client(1) does not have to have Xan account on the machine server is running on, and the commands Xserver(8) will allow to be executed are limited by a parameter file. XSome associated programs are also included. X X John Quarterman, ut-sally!jsq, jsq@sally.utexas.edu X XAn implementation of finger for use with client is included, Xas are client and server shell scripts for a telephone directory Xlookup facility. Some people have noticed that uuhosts (not included Xhere) has a potential call on client in it. Diffs to the man command Xfollow in a separate article. X XThis implementation is for 4.2BSD with inetd. I may eventually Xadapt it for 4.3BSD, which has better logging facilities than Xserver uses in this implementation. X XWARNING: It is easy to build new server facilities with these Xprograms, but it is even easier to write them in such a way as Xto be very easy to break into and to use for unintended purposes. XI make no claims whatever as to the security, safety, or utility Xof this software. USE AT YOUR OWN RISK! !RoNnIe!RaYgUn! echo x - server.8 sed -e 's/^X//' > server.8 << '!RoNnIe!RaYgUn!' X.TH SERVER 8C 85/10/27 X.UC 4 X.SH NAME Xserver \- remote command server X.SH SYNOPSIS X.B /etc/server X.br X.B /etc/fingerd X.SH DESCRIPTION XThis is a generic server daemon program, to be executed by \fIinetd\fP(8). XIt is ordinarly accessed by client(1), and is useful for implementing Xremote man servers, phone lists, etc. X.PP X\fIServer\fP gathers a line of input, converts it into arguments, Xchecks command name against the file \fB/etc/server.cmds\fP, Xand executes the command. XIt is similar to rshd, except it doesn't require user authorization, Xand it is therefore limited to a small number of commands. X.PP X\fB/etc/server.cmds\fP ordinarly consists of one command name per line. XThere may be other words on the same line, in any order: X.IP "login" Xthe connection is logged in wtmp. X.IP "local" Xthe foreign host must be in the same domain as this host. X.LP XIf \fB/etc/server.cmds\fP can't be found, the default line is X.IP Xman login X.PP XComments may be introduced with # at the beginning of a word. XThis works both in the input line, and in \fB/etc/server.cmds\fP. X.PP XIf the name of the command is not \fBserver\fP, and instead Xends with the letter `d', the command to be called is assumed Xto be the last filename element of that name minus the letter `d'. XE.g., if \fB/etc/server\fP is linked to \fB/etc/fingerd\fP Xand \fIinetd\fP calls it under the latter name, \fIserver\fP Xwill assume the command to be executed is \fIfinger\fP. XThe \fIlogin\fP option will also be assumed, but not \fIlocal\fP. XIt is not necessary for the client to pass the command name Xin the input line, as \fIserver\fP will insert the command name X(in the example, \fBfinger\fP) as the first word of the line, Xtaking the line passed as arguments to the command. X.PP XNote that the environment variable \fBPATH\fP should be set Xappropriately when \fIinetd\fP is started, in order for the Xvarious server commands to be found properly. X.SH DIAGNOSTICS XText messages through the TCP port. XShould be self-explanatory. X.SH FILES X.nf X/etc/server.cmds X/etc/services: Xserver 599/tcp # generic command server X.SH "SEE ALSO" Xclient(1), Xinetd(8), Xrshd(8). !RoNnIe!RaYgUn! echo x - server.c sed -e 's/^X//' > server.c << '!RoNnIe!RaYgUn!' X#ifndef lint Xstatic char sccsid[] = "@(#) server.c 1.4 85/10/27"; X#endif X#include X#include X#include X X/* X * Generic simple TCP server program, to be called by inetd. X * Gathers a line of input, converts it into arguments, X * checks command name against COMMANDS, execs command. X * Like rshd, except doesn't require authorization, X * and is limited to a small number of commands. X * X * COMMANDS ordinarly consists of one command name per line. X * There may be other words on the same line, in any order: X * login the connection is logged in wtmp. X * local the foreign host must be in the same domain as this host. X * If COMMANDS can't be found, the default is man, with no options. X * X * Comments may be introduced with # at the beginning of a word. X * This works both in the input line, and in COMMANDS. X */ X#define COMMANDS "/etc/server.cmds" X Xstatic Xint logit = 0, local = 0; Xstatic Xchar *otherd = NULL; X Xmain(argc, argv) Xint argc; Xchar **argv; X{ X extern char *checkname(); X int pid, wpid, status; X char **args, *what, *who; X extern char *them(); X X if (dup2(0, 1) < 0 || dup2(1, 2) < 0) { X perror("dup2"); X _exit(1); X } X otherd = checkname(*argv); X if (getargs(&args) < 1) { X fprintf(stderr, X "server: No input arguments: don't know what to do.\r\n"); X exit(1); X } X what = *args; X if (otherd == NULL && !checkcommand(what)) X exit(1); X if (!logit) { X doit(what, args); X exit(-1); X } X who = them(argv[1]); X if (local && !checkdomain(who)) { X fprintf (stderr, "%s\r\n", X "server: Sorry, only local access permitted."); X sleep(3); X exit(1); X } X dologin(what, "tcp", who); X status = 0; X switch ((pid = fork())) { X case -1: /* error */ X perror("fork"); X dologout(1); X break; X case 0: /* child */ X doit(what, args); X dologout(-1); X break; X default: /* parent */ X while ((wpid = wait(&status)) != pid) { X if (wpid == -1) { X perror("wait"); X status = -1; X break; X } X } X if (status == 0) X dologout(0); X else X dologout(1); X break; X } X (void)close(1); X (void)close(2); X (void)close(0); X exit(status); X} X Xdoit(what, args) Xchar *what, **args; X{ X (void)setuid(1); /* setuid daemon, in case inetd didn't */ X X execvp (what, args); X perror ("exec"); X (void)fprintf (stderr, "\r\nserver: %s unavailable\r\n", what); X} X Xchar * Xcheckname(name) Xchar *name; X{ X char *cp, *rindex(); X register int length; X X if ((cp = rindex(name, '/')) != NULL) X name = cp; X if ((length = strlen(name)) > 0 && name[--length] == 'd') { X name[length] = '\0'; X logit++; X return(name); X } X return(NULL); X} X X#include Xstatic Xjmp_buf jb; Xstatic Xsigalarm() X{ X longjmp(jb, 1); X} X X/* X * Collect a line of input from stdin, break it into arguments, return it. X * Arguments are put in *pargv, and their number is the returned value. X */ Xgetargs(pargv) Xchar ***pargv; X{ X static char *arglist[32], namebuf[512]; X register int nread; X register char *line; X X arglist[0] = NULL; X *pargv = arglist; X (void) signal (SIGALRM, sigalarm); X if(setjmp(jb)) { X fprintf(stderr, "server: Connection timed out.\r\n"); X return(0); X } X (void) alarm (60); X for (line = namebuf; line < &namebuf[sizeof(namebuf)-1]; line++) { X if ((nread = getc(stdin)) == EOF) X break; X if (nread == '\n' || nread == '\r') X break; X *line = nread; X } X *line = '\0'; X (void)alarm (0); X if (nread < 0) X perror("read error"); X if (nread <= 0) X return(0); X nread = sizeof(arglist) / sizeof(arglist[0]); X if (otherd != NULL) { /* stuff the command name in as first arg */ X **pargv = otherd; X (*pargv)++; X nread--; X } X nread = argcargv(namebuf, *pargv, nread); X if (nread >= 0 && otherd) { X nread++; X *pargv = arglist; X } X return(nread); X} X X/* X * Take a line of input, a pointer (argv) to space for args, and max of same. X * Return actual number of arguments (argc) parsed from line buffer into argv. X * The argument strings themselves are still in the line buffer, X * which is modified to null terminate them. The character # at the beginning X * of an argument makes it and the rest of the line a comment, which is ignored. X */ Xint Xargcargv(line, argv, nargv) Xregister char *line, **argv; Xint nargv; X{ X register char **argtop; X register int argc = 0; X X for (argtop = &argv[nargv - 1]; argv < argtop; argv++) { X while (*line && isspace (*line)) X line++; /* skip leading white space */ X if (*line == '\0') X break; /* end of line */ X if (*line == '#') X break; /* comment to end of line */ X *argv = line; X argc++; X while (*line && !isspace(*line)) X line++; /* save an argument */ X if (*line != '\0') X *line++ = '\0'; /* null terminate it */ X } X *argv = (char *)0; /* NULL terminate argv */ X return(argc); X} X Xcheckcommand(command) Xchar *command; X{ X extern char *gets(); X FILE *fp; X char *argv[16], buffer[128]; X int argc; X X if ((fp = fopen(COMMANDS, "r")) == NULL) { X perror(COMMANDS); X if (strcmp (command, "man") == 0) { X logit = 1; X return(1); X } X fprintf(stderr, "server: No go, mate.\r\n"); X return(0); X } X while (fgets(buffer, sizeof(buffer), fp) != NULL) { X argc = argcargv(buffer, argv, sizeof(argv)/sizeof(argv[0])); X if (argc <= 0) X continue; X if (strcmp(command, argv[0]) == 0) { X int i; X X for (i = 1; i < argc; i++) { X if (strcmp(argv[i], "login") == 0) { X logit = 1; X continue; X } X if (strcmp(argv[i], "local") == 0) { X local = 1; X continue; X } X } X (void)fclose(fp); X return(1); X } X } X fprintf(stderr, "server: Can't do that.\r\n"); X (void)fclose(fp); X return(0); X} X X#include X#include X#include X/* #include */ X#include X X/* X * Find the name of the foreign host. X * SMI inetd passes the address and socket in an argument. X * Berkeley inetd expects program to get it with getpeername. X */ Xchar *them(arg) Xchar *arg; X{ X static char remotehost[128]; X extern char *index(), *inet_ntoa(); X extern u_long inet_addr(); X struct sockaddr_in theirsock; X int theirlen = sizeof theirsock; X#define addr theirsock.sin_addr X struct hostent *hp; X X if (arg != 0) { X if (index(arg, '.') != 0) X *index(arg, '.') = '\0'; X (void)strcpy (remotehost, "0x"); X (void)strncat(remotehost, arg, sizeof(remotehost) - 1); X addr.s_addr = inet_addr(remotehost); X (void)strncpy(remotehost, inet_ntoa(addr), X sizeof(remotehost) - 1); X } else X if (getpeername(0, &theirsock, &theirlen) == -1) X perror ("getpeername"); X if ((hp = gethostbyaddr(&addr, sizeof(addr), AF_INET)) != 0) X (void)strncpy(remotehost, hp -> h_name, sizeof(remotehost) - 1); X return(remotehost); X} X X/* X * Ensure target host is in the same domain as the local host. X */ Xcheckdomain(machine) Xchar *machine; X{ X extern char *index(); X char hostname[256]; X char *mydomain, *theirdomain; X struct hostent *hp; X X if (gethostname(hostname, sizeof(hostname)) != 0) { X perror("gethostname"); X return(0); X } X if ((hp = gethostbyname(hostname)) == NULL) { X perror("gethostbyname"); X return(0); X } X if ((mydomain = index(hp -> h_name, '.')) == NULL) { X fprintf(stderr, "server: No domain in primary name %s of %s.\n", X hp -> h_name, hostname); X return(0); X } X if ((theirdomain = index(machine, '.')) == NULL) { X fprintf(stderr, "server: No domain in %s.\n", machine); X return(0); X } X if (casecmp (mydomain, theirdomain) != 0) X return(0); X return(1); X} X Xcasecmp(one, two) Xregister char *one, *two; X{ X register int first, second; X X while (*one && *two) { X first = *one; X if (islower(first)) X first = toupper(first); X second = *two; X if (islower(second)) X second = toupper(second); X if (first != second) X break; X one++; X two++; X } X if (*one == *two) X return(0); X if (*one > *two) X return(1); X return(-1); X} X X#include X#include X X#define SCPYN(a, b) (void)strncpy(a, b, sizeof (a)) Xstruct utmp utmp; X X/* X * Record login in wtmp file. X */ Xdologin(name, tty, remotehost) X char *name, *tty, *remotehost; X{ X int wtmp; X char line[32]; X X wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); X if (wtmp >= 0) { X /* hack, but must be unique and no tty line */ X (void)sprintf(line, "%s%d", tty, getpid()); X SCPYN(utmp.ut_line, line); X SCPYN(utmp.ut_name, name); X SCPYN(utmp.ut_host, remotehost); X utmp.ut_time = time((char *)0); X (void) write(wtmp, (char *)&utmp, sizeof (utmp)); X (void) close(wtmp); X } X} X X/* X * Record logout in wtmp file. X */ Xdologout(status) X int status; X{ X int wtmp; X X#ifdef notdef X if (!logged_in) X _exit(status); X#endif X wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); X if (wtmp >= 0) { X SCPYN(utmp.ut_name, ""); X SCPYN(utmp.ut_host, ""); X utmp.ut_time = time((char *)0); X (void) write(wtmp, (char *)&utmp, sizeof (utmp)); X (void) close(wtmp); X } X} !RoNnIe!RaYgUn! echo x - server.cmds sed -e 's/^X//' > server.cmds << '!RoNnIe!RaYgUn!' Xecho login local Xman login Xapropos login Xwhatis login Xphone.local login Xuuhosts login local !RoNnIe!RaYgUn! echo x - client.1 sed -e 's/^X//' > client.1 << '!RoNnIe!RaYgUn!' X.TH CLIENT 1L 85/10/14 X.UC 4 X.SH NAME Xclient \- remote command client X.SH SYNOPSIS X.B client Xhost command X.SH DESCRIPTION XThis is a generic network client program, Xwhich is used to connect to \fIserver\fP(8). XIt is useful for remote man servers, phone lists, etc. X.PP XThe arguments are the name of a host to connect to and a command to Xexecute. XThe arguments are passed to \fIserver\fP(8) on the given host Xas a line of input, and any output from \fIserver\fP is copied Xto \fIclient\fP's standard output. XAny verification of the command is done by \fIserver\fP(8), Xnot \fIclient\fP(1). X.SH DIAGNOSTICS XShould be self-explanatory. X.SH FILES X.SH "SEE ALSO" Xserver(8), Xrsh(1). !RoNnIe!RaYgUn! echo x - client.c sed -e 's/^X//' > client.c << '!RoNnIe!RaYgUn!' X/* X */ X#ifndef lint Xchar sccsid[] = "@(#) client.c 1.2 85/10/19"; X#endif X X#include X#include X#include X#include X#include X Xusage(message) Xchar *message; X{ X fprintf (stderr, "client: %s.\n", message); X fprintf (stderr, "Usage: client [ -p port ] host command.\n"); X exit(1); X} X Xmain(argc, argv) Xint argc; Xchar **argv; X{ X int sock, c; X register FILE *sock_r, *sock_w; X char *host, *service = "server"; X struct sockaddr_in sin; X struct hostent *host_p; X struct servent *serv_p; X X if (argc < 3) X usage("too few arguments"); X while (*++argv && **argv == '-') { X switch (argv[0][1]) { X case 'p': X if ((service = *++argv) == NULL) X usage("no value for -p option"); X break; X default: X usage("unknown option"); X break; X } X } X if ((host = *argv) == NULL) X usage("not enough arguments"); X host_p = gethostbyname(host); X if (host_p == NULL) { X fprintf(stderr, "client: %s: host unknown\n", host); X exit(1); X } X host = host_p->h_name; X sock = socket(host_p->h_addrtype, SOCK_STREAM, 0); X if (sock < 0) { X perror("client: socket"); X exit(2); X } X sin.sin_family = host_p->h_addrtype; X if (bind(sock, &sin, sizeof(sin)) < 0) { X perror("client: bind"); X exit(3); X } X bcopy(host_p->h_addr, &sin.sin_addr, host_p->h_length); X if ((sin.sin_port = atoi(service)) == 0) { X serv_p = getservbyname(service, "tcp"); X if (serv_p == NULL) { X fprintf(stderr, X "client: %s/tcp: service unknown\n", service); X exit(4); X } X sin.sin_port = serv_p->s_port; X } X#ifdef DEBUG X fprintf(stderr, "Connecting to %s...\n", host); X#endif X if (connect(sock, &sin, sizeof(sin)) < 0) { X perror("client: connect"); X exit(5); X } X sock_r = fdopen(sock, "r"); X sock_w = fdopen(sock, "w"); X if (sock_r == NULL || sock_w == NULL) { X perror("client: fdopen"); X close(sock); X exit(1); X } X for (fputs(*++argv, sock_w); *++argv != NULL; fputs(*argv, sock_w)) X putc(' ', sock_w); X fputs("\r\n", sock_w); X fflush(sock_w); X if (ferror(sock_w)) { X perror("client: write"); X exit(1); X } X while ((c = getc(sock_r)) != EOF) X putchar(c); X exit(0); X} !RoNnIe!RaYgUn! echo x - finger.c sed -e 's/^X//' > finger.c << '!RoNnIe!RaYgUn!' X#ifndef lint Xstatic char *sccsid = "@(#) finger.c 1.4 85/10/19"; X#endif X X/* X * If there's an @ in the first argument, must be a network finger, X * so use client to do it. Otherwise, just exec /usr/ucb/finger. X */ Xstatic char *FINGER = "/usr/ucb/finger"; Xmain(argc, argv) Xint argc; Xchar **argv; X{ X extern char *index(); X register char *host; X X if (argc == 2 && (host = index(argv[1], '@')) != 0) { X *host++ = '\0'; X execlp("client", "client", "-p", "finger", host, X argv[1], (char *)0); X perror("client"); X exit(1); X } X execv(FINGER, argv); X perror(FINGER); X exit(1); X} !RoNnIe!RaYgUn! echo x - phone.1 sed -e 's/^X//' > phone.1 << '!RoNnIe!RaYgUn!' X.TH PHONE 1L "13 March 1985" X.UC 4 X.SH NAME Xphone \- lookup against telephone list on remote host X.SH SYNOPSIS X.B phone X[ -h X.I host X] X.I name X.SH DESCRIPTION X.I Phone Xis a trivial client program used to retrieve local employee information Xfrom a master file on a remote host by contacting a suitable server. XThe user list residing on X.I host X(default \fBut\-ratliff\fP) Xis searched for all lines containing X.I name Xas a substring (the search is case-insensitive). XThese lines are printed as the output of the X.I phone Xprogram. X.PP X.I Phone Xoperates by opening a TCP connection to the port indicated Xin the service specification for ``phone''. XThe remote X.I phoned Xserver then reads up to 128 characters of X.I name Xand uses this Xstring as the search key, folding to lower case. XRegular expressions of the type allowed by X.I grep(1) Xare allowed, with the caution that special characters should Xbe escaped to avoid interpretation by the shell. X.I Name Xmay be specified as the null string, Xin which case the entire user list is retrieved. X.PP X.SH "FILES" X.br X.ns X.TP 21 X/etc/services Xfor specification of service port X.SH "SEE ALSO" Xgrep(1), phoned(8) X.SH "BUGS" XThe update procedure for the list itself is ill-defined. !RoNnIe!RaYgUn! echo x - phone.sh sed -e 's/^X//' > phone.sh << '!RoNnIe!RaYgUn!' X#!/bin/sh XPATH=/usr/local:/usr/ucb:/bin:/usr/bin Xexport PATH Xexec client phone.CS.UTEXAS.EDU phone.local $* !RoNnIe!RaYgUn! echo x - phone.local.sh sed -e 's/^X//' > phone.local.sh << '!RoNnIe!RaYgUn!' X#! /bin/sh XPHONELIST="/usr/local/lib/phonelist" Xcase $1 in X"") X grep "." $PHONELIST X ;; Xesac Xgrep -i $1 $PHONELIST Xgrep -i $1 /usr/lib/aliases | grep @ | grep -v "," !RoNnIe!RaYgUn! exit