v01i003: supersrv - offer any program as a network service, Part02/02
Steven Grimm
Steven.Grimm at Eng.Sun.COM
Sat Jun 1 13:39:09 AEST 1991
Submitted-by: Steven Grimm <Steven.Grimm at Eng.Sun.COM>
Posting-number: Volume 1, Issue 3
Archive-name: supersrv/part02
#! /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: supersrv.c
# Wrapped by csr at calvin.doc.ca on Fri May 31 23:28:09 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'supersrv.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'supersrv.c'\"
else
echo shar: Extracting \"'supersrv.c'\" \(8732 characters\)
sed "s/^X//" >'supersrv.c' <<'END_OF_FILE'
X#include "common.h"
X#include <netinet/in.h>
X#include <arpa/inet.h>
X
Xextern int errno;
X
X/*
X * SuperServer.
X *
X * 1.2: REGISTER handler sends QUIT to all old servers running with the same
X * username.
X * 1.3: Fix infinite loop bug
X * Pass client IP address to server
X * 1.4: Services can have descriptions
X */
X
X#define WELCOME "SuperServer -- enter service desired.\n"
X#define NOSERV "Service not offered.\n"
X
Xint thirty;
X
X/*
X * This structure is used to keep the database of available services.
X */
Xstruct service {
X char name[20]; /* Service name */
X char desc[50]; /* Service description */
X int fd; /* File descriptor that offers it */
X struct service *next; /* Next service in list */
X} *list = (struct service *)0;
X
Xchar users[NOFILE][12]; /* user connected to each fd */
X
X/*
X * The peer of the most recent connection.
X */
Xstruct sockaddr_in rcon;
X
Xmain()
X{
X int fd_so, /* Socket() file descriptor */
X fd_co; /* Connected file descriptor */
X short portno; /* Port number to listen on */
X char request[80];
X extern int sigchld();
X
X/*
X * First things first: put ourselves in the background.
X */
X/* if (fork())
X exit(0);*/
X
X portno = SUPERPORT;
X thirty = 30;
X
X/*
X * Set up the server socket on the appropriate port number and listen on it.
X * If we get an address-already-in-use error, silently exit, since we're
X * presumably waiting for the TCP port to be cleared up, and will be executed
X * again by server.
X */
X fd_so = serversock(portno);
X if (fd_so < 0)
X {
X if (errno != EADDRINUSE)
X perror("serversock");
X exit(errno);
X }
X
X (void)listen(fd_so, 5);
X setsockopt(fd_so, SOL_SOCKET, SO_LINGER, &thirty, sizeof(thirty));
X fcntl(fd_so, F_SETOWN, getpid());
X
X/*
X * And we'll need to accomodate child processes.
X */
X signal(SIGCHLD, sigchld);
X/*
X * We will catch broken pipes in our main loop, so don't fuss with signals.
X */
X signal(SIGPIPE, SIG_IGN);
X
X/*
X * Now keep accepting connections and interpreting them.
X */
X while (1)
X {
X fd_co = getcon(fd_so);
X
X if (fd_co < 0)
X {
X perror("accept");
X exit(0);
X }
X
X fcntl(fd_co, F_SETOWN, getpid());
X setsockopt(fd_co,SOL_SOCKET,SO_LINGER,&thirty, sizeof(thirty));
X
X write(fd_co, WELCOME, sizeof(WELCOME)-1);
X if (getline(fd_co, request, sizeof(request)-1))
X {
X if (handle(fd_co, request))
X close(fd_co);
X }
X else
X close(fd_co);
X }
X}
X
X/*
X * Get a connection, or handle a disconnected server.
X */
Xgetcon(old)
Xint old;
X{
X struct service *cur;
X fd_set reed, tread, other;
X int fd, size;
X
X FD_ZERO(&reed);
X FD_ZERO(&other);
X
X for (cur = list; cur; cur = cur->next)
X FD_SET(cur->fd, &reed);
X FD_SET(old, &reed);
X
X while (1)
X {
X tread = reed;
X select(NOFILE, &tread, NULL, NULL, NULL);
X if (FD_ISSET(old, &tread))
X break;
X for (fd = 0; fd < NOFILE; fd++)
X if (FD_ISSET(fd, &tread))
X {
X killfd(fd);
X close(fd);
X FD_CLR(fd, &reed);
X }
X }
X size = sizeof(rcon);
X return( accept(old, &rcon, &size) );
X}
X
X
X/*
X * Get an input line from a file descriptor. This is probably very slow.
X * Since it's only called once, though...
X */
Xgetline(fd, buf, len)
Xint fd, len;
Xchar *buf;
X{
X int index = 0;
X char c;
X
X while (read(fd, &c, 1) == 1)
X {
X if (c == '\n')
X break;
X
X if (c == '\r')
X continue;
X
X if (index < len)
X buf[index++] = c;
X }
X
X buf[index] = 0;
X return index;
X}
X
X
X/*
X * Handle a user request. This will either be "REGISTER" or some
X * user-defined function.
X */
Xhandle(fd, string)
Xint fd;
Xchar *string;
X{
X struct service *cur;
X char user[16], hisip[16];
X
X if (string[0] == '\0')
X return 1;
X/*
X * If a subserver wants to register itself, grab service
X * names from it until it outputs an empty line.
X */
X if (!strcmp(string, "REGISTER"))
X {
X char name[70];
X int i;
X
X if (! getline(fd, users[fd], 11))
X return 1;
X
X/*
X * If this user already has servers connected, kill them off.
X */
X for (i = 0; i < NOFILE; i++)
X if (i != fd && ! strcmp(users[i], users[fd]))
X {
X killfd(i);
X write(i, "QUIT\0xxxxxxxxxxxxxxx", 20);
X close(i);
X users[i][0] = '\0';
X }
X
X/*
X * Get service names. If there are descriptions, use those as well.
X */
X while (getline(fd, name, 69))
X {
X char *desc, *index();
X
X#ifdef DEBUG
X printf("supersrv got input '%s'\n", name);
X#endif
X desc = index(name, '\t');
X if (desc != NULL)
X *desc++ = '\0';
X
X cur = (struct service *)malloc(sizeof(*cur));
X strcpy(cur->name, name);
X if (desc != NULL)
X strcpy(cur->desc, desc);
X else
X cur->desc[0] = '\0';
X
X cur->fd = fd;
X cur->next = list;
X list = cur;
X }
X return 0; /* Keep file descriptor open */
X }
X
X getline(fd, user, 15);
X
X/*
X * Print a nicely-formatted list of services. If there aren't any services,
X * print a message to that effect.
X */
X if (!strcmp(string, "LIST"))
X {
X char buf[100], format[20];
X int maxlen = 10;
X#define LISTHEAD "Username Service\n-------- -------\n"
X#define NOSERVS "No services available.\n"
X
X if (list)
X {
X write(fd, LISTHEAD, sizeof(LISTHEAD)-1);
X
X for (cur = list; cur; cur = cur->next)
X if (maxlen < strlen(cur->name))
X maxlen = strlen(cur->name);
X sprintf(format, "%%-12s %%-%ds%%s\n", maxlen + 2);
X
X for (cur = list; cur; cur = cur->next)
X {
X if (user[0] && strcmp(user, users[cur->fd]))
X continue;
X sprintf(buf, format, users[cur->fd], cur->name,
X cur->desc);
X write(fd, buf, strlen(buf));
X }
X }
X else
X write(fd, NOSERVS, sizeof(NOSERVS)-1);
X return 1;
X
X }
X
X for (cur = list; cur; cur=cur->next)
X if (! strcmp(string, cur->name))
X if ((! user[0]) || (! strcmp(user, users[cur->fd])))
X break;
X
X if (! cur)
X {
X write(fd, NOSERV, sizeof(NOSERV));
X return 1;
X }
X
X write(cur->fd, string, 20);
X/*
X * 1.3: Write out the client's IP address (which we know will be in rcon.)
X */
X sprintf(hisip, "%s\n", inet_ntoa(rcon.sin_addr));
X write(cur->fd, hisip, strlen(hisip));
X
X shuffle(cur->fd, fd);
X
X return 1;
X}
X
Xsigchld()
X{
X wait(0);
X}
X
X/*
X * Kill all entries in the linked list with a certain file
X * descriptor.
X */
Xkillfd(fd)
Xint fd;
X{
X struct service *cur, *temp;
X
X while (list && list->fd == fd)
X {
X temp = list->next;
X free(list);
X list = temp;
X }
X
X if (list)
X for (cur = list; cur; cur = cur->next)
X while (cur->next && cur->next->fd == fd)
X {
X temp = cur->next;
X cur->next = cur->next->next;
X free(temp);
X }
X}
X
X
X/*
X * This is the kludgy part. We want to effectively connect the
X * client and the appropriate subserver. Since there's no way to
X * connect two sockets together, we have to fork off a child and
X * sit there shuffling bytes back and forth between the two file
X * descriptors. When one of them shuts down, we shut the other one
X * down and die.
X *
X * For now, since only one client can be talking to each subserver
X * at a given time, we erase all the subserver's services from the
X * service list. It will reconnect when it's done forking off the
X * service.
X */
X#ifndef MIN
X#define MIN(x,y) (((x)>(y))?(y):(x))
X#endif
X
Xshuffle(subsrv, client)
Xint subsrv, client;
X{
X int fd;
X fd_set reed, rite, except;
X extern void quit();
X
X killfd(subsrv);
X
X if (fork())
X {
X close(subsrv);
X return;
X }
X
X for (fd = 0; fd < NOFILE; fd++)
X if (fd != client && fd != subsrv)
X close(fd);
X
X FD_ZERO(&reed);
X FD_SET(client, &reed);
X FD_SET(subsrv, &reed);
X FD_ZERO(&rite);
X except = reed;
X
X fcntl(client, F_SETOWN, getpid());
X fcntl(subsrv, F_SETOWN, getpid());
X/* fcntl(client, F_SETFL, FNDELAY);
X fcntl(subsrv, F_SETFL, FNDELAY);
X*/
X
X signal(SIGURG, quit);
X signal(SIGPIPE, quit);
X
X while (1)
X {
X fd_set tread, twrite, texcept;
X int numbytes, bsize, numread;
X char buf[4096];
X
X tread = reed;
X twrite = rite;
X texcept = except;
X
X select(NOFILE, &tread, &twrite, &texcept, (void *)0);
X
X if (FD_ISSET(subsrv, &tread))
X {
X ioctl(subsrv, FIONREAD, &numbytes);
X bsize = MIN(numbytes, sizeof(buf));
X numread = read(subsrv, buf, bsize);
X if (numread < 0 && errno != EWOULDBLOCK)
X {
X perror("subsrv");
X exit(0);
X }
X if (! numread)
X {
X shutdown(client, 1);
X shutdown(subsrv, 0);
X FD_CLR(subsrv, &reed);
X }
X else
X write(client, buf, numread);
X }
X
X if (FD_ISSET(client, &tread))
X {
X ioctl(client, FIONREAD, &numbytes);
X bsize = MIN(numbytes, sizeof(buf));
X numread = read(client, buf, bsize);
X if (numread < 0 && errno != EWOULDBLOCK)
X {
X perror("client");
X exit(0);
X }
X if (! numread)
X {
X shutdown(client, 0);
X shutdown(subsrv, 1);
X FD_CLR(client, &reed);
X }
X else
X write(subsrv, buf, numread);
X }
X
X/* If both sides were shut down, leave. */
X if (! (FD_ISSET(client, &reed) || FD_ISSET(subsrv, &reed)))
X {
X close(client);
X close(subsrv);
X exit(0);
X }
X
X if (FD_ISSET(client, &texcept) || FD_ISSET(subsrv, &texcept))
X {
X close(client);
X close(subsrv);
X exit(0);
X }
X }
X}
X
Xvoid quit()
X{
X exit(0);
X}
X
END_OF_FILE
if test 8732 -ne `wc -c <'supersrv.c'`; then
echo shar: \"'supersrv.c'\" unpacked with wrong size!
fi
# end of 'supersrv.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
exit 0 # Just in case...
--
Andrew Patrick acting as Comp.Sources.Reviewed Moderator
Department of Communications, Ottawa, CANADA
csr at calvin.doc.CA
More information about the Comp.sources.reviewed
mailing list