[comp.sources.misc] v06i054: PSERV sample programs

allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (03/07/89)

Posting-number: Volume 6, Issue 54
Submitted-by: lars@ACC-SB-UNIX.ARPA (Lars J Poulsen)
Archive-name: pserv

PSERV - a sample piece of functional TCP/IP Berkeley socket programming.

Every so often, programmers new to socket programming ask for working
examples. I will give you this small example to play with.

This program is a minimal remote spooling package, intended to solve
a personal problem: I use every day two VMS systems and a Unix system.
In my work area we have a LaserWriter connected to the Unix system,
but I have to ride a sloooow elevator two floors to get to a VMS printer.

I really wanted to write a small "lpd" client, but found that this
would require the cooperation of system managers on both machines, since
(1) LPD will only accept commands from known hosts
(2) LPD will only accept connections from privileged ports (<1024)
    and the Wollongong VMS package enforces this also; you need
    system privileges to get a low-numbered port on WIN/TCP.

So I decided to write my own mini protocol. This program illustrates
the basic mechanism used by any server/client pair, and is small enough
to dink around with fairly safely. [After all, if the system lets an
unprivileged user do it, it must be safe :-) ?]

The client runs un 4.3BSD or VMS/WIN/TCP; the server runs on 4.3BSD.

Enjoy.

	/ Lars Poulsen
	  ACC Customer Service
--------------------------------- Cut Here --------------------------------
# This is a shell archive.  Remove anything before this line
# then unpack it by saving it in a file and typing "sh file"
# (Files unpacked will be owned by you and have default permissions).
# This archive contains the following files:
#	./copytoheap.c
#	./filename.c
#	./listen.c
#	./rmtprint.c
#	./strpak.c
#	./Makefile
#
if `test ! -s ./copytoheap.c`
then
echo "writing ./copytoheap.c"
cat > ./copytoheap.c << '\Rogue\Monster\'
static char rcsid[] = "$Header: copytoheap.c,v 3.5 87/05/26 14:16:25 lars Production $";

#include <stdio.h>

extern char *malloc();
extern int   strlen();

char *
copytoheap(string)
    char *string;
{
    char *temp;
    int   size;
    if (string == NULL) return NULL;
    size = strlen(string)+1;
    if (size > 64*1024)
    {
	fprintf(stderr,"copytoheap: object too large (%d bytes)\n",size);
	exit(-1);
    }
    temp = malloc(size);
    strcpy(temp,string);
    return temp;
}
\Rogue\Monster\
else
  echo "will not over write ./copytoheap.c"
fi
if `test ! -s ./filename.c`
then
echo "writing ./filename.c"
cat > ./filename.c << '\Rogue\Monster\'
static char rcsid[] = "$Header: filename.c,v 3.3 88/08/07 19:00:37 lars Exp $";

/* filename.c - extract the FILENAME part from a pathname string
 *              written by Lars Poulsen <lars@acc.arpa>
 *              for the ACC PSR system
 *
 * $Header: filename.c,v 3.3 88/08/07 19:00:37 lars Exp $
 *
 * $Log:	filename.c,v $
 * Revision 3.3  88/08/07  19:00:37  lars
 * handle vms-filenames also:
 * if name contains a slash, it's a unix name.
 * else, if it has "]" or ":", cut off what's before those.
 * 
 * Revision 3.2  87/05/18  13:36:19  lars
 * First stable version of PSR after pf_get/pf_write went in
 * 
 * Revision 3.1  87/05/16  21:16:55  lars
 * At end of splitting up (First stable version)
 * 
 */
extern char *rindex();

char   *
filename(path)
    char   *path;
{
    char   *ptr;

/* first deal with unix filenames */
    ptr = rindex(path, '/');
    if (ptr)
	return ++ptr;
    /* now deal with vmsisms */

    ptr = rindex(path, ']');
    if (ptr)
	return ++ptr;
    ptr = rindex(path, ':');
    if (ptr)
	return ++ptr;
    return path;
}
\Rogue\Monster\
else
  echo "will not over write ./filename.c"
fi
if `test ! -s ./listen.c`
then
echo "writing ./listen.c"
cat > ./listen.c << '\Rogue\Monster\'
/* listen.c - a server daemon to match "rmtprint"
 *
 * Written by Lars Poulsen <lars@acc-sb-unix.arpa>
 *	August 1988
 *
 *	This is a simple example of a TCP based network server.
 */
#include <ctype.h>
#include <stdio.h>
#include <sys/types.h>
#include <errno.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <pwd.h>

#define MY_PORT 12345
#define ANY_PORT 0
#define DEFPRT "mac7"
#define FORKING	1

extern int errno;

extern char *asctime();
extern struct tm *localtime();
extern char *mktemp();
extern long time();

extern char *copytoheap();
extern char *filename();

char   *what_alarm = NULL;
char    temp_name[40];
int     forking = 0;
extern char *home_directory();
extern exit_server();
extern  timeout();
extern  wait_child();

char   *
tstamp()
{
    long    time_now;
    char   *temp;

    time_now = time(0);
    temp = asctime(localtime(&time_now)) + 4;
    temp[12] = 0;
    return temp;
}

main(argc, argv)
    int     argc;
    char   *argv[];

{
    int     s,
            cs;
    struct sockaddr_in my_name;
    struct sockaddr_in any_name;
    struct sockaddr_in his_name;
    int     idsw;
    int     his_addr_len;

    forking = FORKING;		/* done this way in case it is an expression */

    signal(SIGALRM, timeout);
    signal(SIGCHLD, wait_child);
    signal(SIGTERM, exit_server);
    printf("%s Print server starting - defprt = %s\n", tstamp(), DEFPRT);
    fflush(stdout);

    s = socket(AF_INET, SOCK_STREAM, 0);	/* protocol #0 is IP */
    if (0)
	printf("tcp: got socket fd=%d (Hex: %x)\n", s, s);

    bzero((char *) &my_name, sizeof(my_name));
    my_name.sin_family = AF_INET;
    my_name.sin_port = htons(MY_PORT);
    my_name.sin_addr.s_addr = INADDR_ANY;

    idsw = bind(s, &my_name, sizeof(my_name));
    if (idsw != 0)
	printf("tcp: bind returned %d - errno = %d\n", idsw, errno);

    bzero((char *) &any_name, sizeof(any_name));
    any_name.sin_family = AF_INET;
    any_name.sin_port = htons(ANY_PORT);
    any_name.sin_addr.s_addr = INADDR_ANY;
    idsw = listen(s, 0);
    if (idsw != 0)
	printf("listen: listen returned %d - errno = %d\n", idsw, errno);
next_in_file:
    alarm(0);
    bzero((char *) &his_name, sizeof(his_name));
    his_addr_len = sizeof(his_name);
    cs = accept(s, &his_name, &his_addr_len);
    if (cs < 0)
    {
	printf("listen: accept failed,  errno = %d his_addr_len = %d\n",
	       errno, his_addr_len);
	fflush(stdout);
	exit(errno);
    }
    process_a_file(cs);
    goto next_in_file;
}

process_a_file(cs)
{
    int     bcnt;
    char    buffer[512];
    int     ofd;
    char    printer[20],
            rmtuser[40],
            rmtfile[80];
    int     file_size = 0;
    int     idsw;

    if (forking)
    {
	idsw = fork();
	if (idsw < 0)
	{
	    printf("%s listen: Fatal error - fork() failed with error = %d\n",
		   tstamp(), -idsw);
	    exit(-idsw);
	}
	if (idsw != 0)		/* parent listens for next connection */
	{
	    close(cs);
	    return;
	}
    }
    what_alarm = "Reading Command Line";
    alarm(30);

    if (get_command(cs, printer, rmtuser, rmtfile) != 0)
    {
	printf("%s Bad incoming commnd %s %s %s\n",
	       tstamp(), printer, rmtuser, rmtfile);
	fflush(stdout);
	close(cs);
	return;
    }
    printf("%s Received connection %s %s %s\n",
	   tstamp(), printer, rmtuser, rmtfile);
    fflush(stdout);

    ofd = open_out_file(printer, rmtuser, rmtfile);

    what_alarm = "Receiving File";
    alarm(30);
    while (bcnt = recv(cs, buffer, sizeof(buffer), 0))
    {
	write(ofd, buffer, bcnt);
	alarm(30);
	file_size += bcnt;
	if (buffer[bcnt - 1] == 0)
	    break;
    }
    close(cs);
    printf("%s Received %d bytes for %s %s %s\r\n",
	   tstamp(), file_size, printer, rmtuser, rmtfile);
    fflush(stdout);

    what_alarm = "Printing File";
    alarm(5);
    if (strcmp(printer, "save") != 0)
    {
	check_printer(printer, DEFPRT);
	sprintf(buffer, "lpr -P %s -r -s %s", printer, temp_name);
	idsw = system(buffer);
	if (idsw)
	{
	    printf("listen: lpr command returned %d\n", idsw);
	    fflush(stdout);
	}
    }
    if (forking)
	exit(0);
    else
        return;
}

get_command(nfd, printer, rmtuser, rmtfile)
    int     nfd;
    char   *printer,
           *rmtuser,
           *rmtfile;
{
    char    c,
           *p;
    extern char readch();

    p = printer;
    while (isalnum(c = readch(nfd)))
	*(p++) = c;
    *p = 0;
    if (c != ' ')
	goto cmd_error;

    p = rmtuser;
    while (isalnum(c = readch(nfd)))
	*(p++) = c;
    *p = 0;
    if (c != ' ')
	goto cmd_error;

    p = rmtfile;
    while ((c = readch(nfd)) && (c > ' '))
	*(p++) = c;
    if (!isspace(c))
	goto cmd_error;		/* space or \n */
    *p = 0;
    return 0;
cmd_error:
    printf("syntax error in print command *p = %d\n", *p);
    printf("printer = %s user = %s filename = %s\n",
	   printer, rmtuser, rmtfile);
    return -1;
}
char
readch(fd)
{
    char    buf;

begin:
    if (recv(fd, &buf, 1, 0) != 1)
    {
	printf("readch(%d) got error %d\n", fd, errno);
	return 0;
    }
    if (buf == 0)
	goto begin;
    return buf;
}

check_printer(printer, defprt)
    char   *printer,
           *defprt;
{
    char    fname[80];
    int     fd;
    int     err;

/* The following check is bad: If the device exists and is busy,
	we will wait forever for the daemon to exit... */
    if (0)
    {
	sprintf(fname, "/dev/%s", printer);
	errno = 0;
	fd = open(fname, 0, 0);
	err = errno;
	close(fd);
	if (errno == ENOENT)
	    goto failed;
    }
    sprintf(fname, "/usr/spool/%s", printer);
    errno = 0;
    fd = open(fname, 0, 0);
    err = errno;
    close(fd);
    if (errno)
	goto failed;

    return;
failed:
    printf("%s Failed to open %s - errno = %d\n",
	   tstamp, fname, err);
    printf("%s %s is not a valid printer, %s substituted\n",
	   tstamp, printer, defprt);
    fflush(stdout);
    strcpy(printer, defprt);
    return;
}

/* alarm clock is used to debug hang's */
timeout()
{
    printf("%s Fatal Timeout While %s\n", tstamp(), what_alarm);
    exit(2);
}

/* dummy child handler - just get rid of <defunct> child */
wait_child()
{
	wait(0);
}

/* clean exit on a simple "kill" */
exit_server()
{
    printf("%s Received SIGTERM - exiting\n", tstamp());
    exit(0);
}

open_out_file(printer, rmtuser, rmtfile)
    char   *printer,
           *rmtuser,
           *rmtfile;
{
    int     ofd;
    char   *directory;

    rmtfile = filename(rmtfile);
    if ((directory = home_directory(rmtuser)) == NULL)
	directory = "/tmp";
    sprintf(temp_name, "%s/%s", directory, rmtfile);
    if ((ofd = open(temp_name, 0) < 0) && errno == ENOENT)
	/* no such file exists */
	ofd = creat(temp_name, 0644);
    if (ofd >= 0)
	return ofd;

    close(ofd);

    strcpy(temp_name, "/tmp/spoolXXXXXX");
    (void) mktemp(temp_name);
    ofd = creat(temp_name, 0644);
    return ofd;
}
char   *
home_directory(name)
    char   *name;
{
    struct passwd *pw;
    char   *hd;

    setpwent();
    for (pw = getpwent(); pw; pw = getpwent())
    {
	if (strcmp_nocase(pw->pw_name, name) == 0)
	    break;
    }
    if (pw)
	hd = copytoheap(pw->pw_dir);
    else
	hd = NULL;
    if (forking && !geteuid())
	setuid(pw->pw_uid);	/* try to set this child to the right userid */
    endpwent();
    return hd;
}
\Rogue\Monster\
else
  echo "will not over write ./listen.c"
fi
if `test ! -s ./rmtprint.c`
then
echo "writing ./rmtprint.c"
cat > ./rmtprint.c << '\Rogue\Monster\'
#include <stdio.h>
#include <signal.h>
#ifdef VAXC
#include "twg$tcp:[netdist.include.sys]types.h"
#include "twg$tcp:[netdist.include]errno.h"
#include "twg$tcp:[netdist.include.sys]socket.h"
#include "twg$tcp:[netdist.include.netinet]in.h"
#include "twg$tcp:[netdist.include]netdb.h"
#else
#include <sys/types.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define uerrno errno
#endif

#define MY_PORT 0
#define HIS_PORT 12345

extern int uerrno;
extern timeout();
char *what_alarm = "Name Service";

main(argc, argv)
    int     argc;
    char   *argv[];

{
    int     s;
    struct sockaddr_in my_name;
    struct sockaddr_in his_name;
    int     idsw;
    int     namelen;
    struct hostent *gethostbyname(),
           *salt_ent;
    u_long *found_addr;
    struct servent *sp;
    char    buffer[512];
    int     bcnt;
    int     ifd;
    char    cmdbuf[80];
    int null = 0;

    if (argc != 4)
    {
	printf("usage: rmtprint host printer file\n");
	exit(1);
    }
    s = socket(AF_INET, SOCK_STREAM, 0);	/* protocol #0 is IP */
    if (0)
	printf("tcp: got socket fd=%d (Hex: %x)\n", s, s);

    bzero((char *) &my_name, sizeof(my_name));
    my_name.sin_family = AF_INET;
    my_name.sin_port = MY_PORT;
    my_name.sin_addr.s_addr = INADDR_ANY;

    idsw = bind(s, &my_name, sizeof(my_name));
    if (idsw != 0)
	printf("tcp: bind returned %d - uerrno = %d\n", idsw, uerrno);

    signal(SIGALRM, timeout);
    alarm(30);
    salt_ent = gethostbyname(argv[1]);
    if (salt_ent) found_addr = (u_long *)salt_ent->h_addr_list[0];
    if ((salt_ent == NULL) || (found_addr == 0))
    {
	printf("rmtprint: %s is not a known host\n", argv[1]);
	exit(2);
    }
    bzero((char *) &his_name, sizeof(his_name));
    his_name.sin_family = AF_INET;
    sp = getservbyname("rmtprint", "tcp");
    if (sp)
	his_name.sin_port = sp->s_port;
    else
	his_name.sin_port = htons(HIS_PORT);
    bcopy(salt_ent->h_addr_list[0], &his_name.sin_addr, salt_ent->h_length);

    what_alarm = "Connection Request";
    alarm(30);
    idsw = connect(s, &his_name, sizeof(my_name));
    if (idsw != 0)
    {
	idsw = uerrno;
	printf("rmtprint: Connect to %s port %d returned Unix error %d\n",
	       argv[1], ntohs(his_name.sin_port), idsw);
	if (idsw == 61)
	    printf("rmtprint: Connection refused by %s - server not running ?\n",
		   argv[1]);
	exit(2);
    }
    what_alarm = "Sending Command Line";
    alarm(30);
    sprintf(cmdbuf, "%s %s %s\n", argv[2], getenv("USER"), argv[3]);
    send(s, cmdbuf, strlen(cmdbuf), 0);
    what_alarm = "Sending File";
    ifd = open(argv[3], 0, 0);
    if (ifd < 0)
    {idsw = errno;
    printf("rmtprint: Failed to open %s errno = %d\n", argv[3], idsw);
    exit(idsw);
    }
    bcnt = read(ifd, buffer, sizeof(buffer));
    while (bcnt > 0)
    {
        alarm(30);
	send(s, buffer, bcnt, 0);
	bcnt = read(ifd, buffer, sizeof(buffer));
    }
    if (errno) printf("rmtprint: read ended with errno = %d\n",errno);
    close(ifd);
    sleep(1);
    send(s, &null, 1, 0);
    recv(s, buffer, sizeof(buffer), 0);
    close(s);
    exit(1);
}
timeout()
{
    printf("rmtprint: Fatal timeout during %s\n", what_alarm);
    exit(4);
}
\Rogue\Monster\
else
  echo "will not over write ./rmtprint.c"
fi
if `test ! -s ./strpak.c`
then
echo "writing ./strpak.c"
cat > ./strpak.c << '\Rogue\Monster\'
static char rcsid[] = "$Header: strpak.c,v 1.10 88/08/07 20:18:49 lars Exp $";
#include <stdio.h>
#include <ctype.h>
extern char *index();
extern char *malloc();

char *
str_firstline(multi_field)
    char *multi_field;
{
    char *end_of_line;
    static int i = 0;
    static char buf[4][80];
    i = (i+1)&3;
    strncpy(buf[i], multi_field, 80);
    if (end_of_line = index(buf[i],'\n')) *end_of_line = 0;
    return buf[i];
}

char *
str_firstword(multi_field)
    char *multi_field;
{
    char *end_of_line;
    static int i = 0;
    static char buf[4][80];
    i = (i+1)&3;
    strncpy(buf[i], multi_field, 80);
    for (end_of_line = buf[i]; isalpha(*end_of_line); end_of_line++);
    *end_of_line = 0;
    return buf[i];
}

char *str_join3(a,b,c)
    char *a, *b, *c;
{
    int size = strlen(a) + strlen(b) + strlen(c) +1;
    char *new;
    char *pp;

    pp = new = malloc(size);
    if (new == NULL) return NULL;
    if (a != NULL) while (*a) *(pp++) = *(a++);
    if (b != NULL) while (*b) *(pp++) = *(b++);
    if (c != NULL) while (*c) *(pp++) = *(c++);
    *pp = 0;
    return new;
}

int str_lines(string)
    char *string;
{
    char *pp = string;
    char c;
    int count = 1;

    if (string == NULL) return 0;
    if (strlen(string) == 0) return 0;

    while(c = *pp++)
	if (c == '\n')
	    count ++;
    return count;
}

int str_longest(string)
    char *string;
{
    int lines, longest = 0, total;
    int thislen;
    char *thisline, *nextline;

    if (string == NULL) return 0;
    if ((total = strlen(string)) == 0) return 0;
    lines = str_lines(string);
    thisline = string;
    while (--lines)
    {
      nextline = index(thisline,'\n') + 1;
      if (nextline == NULL)
      {
	  fprintf(stderr,"str_longest: Program bug !!\n");
          exit(-1);
      }
      thislen = nextline - thisline - 1;
      if (thislen > longest) longest = thislen;
      thisline = nextline;
    }
    thislen = strlen(thisline);
    if (thislen > longest) longest = thislen;
    return longest;
}

char *
str_trim(string)
    char *string;
{
    char *pp;
    int length;
    static int i = 0;
    static char buf[4][80];

    if (string == NULL) return NULL;
    i = (i+1)&3;

    pp = string + strlen(string) - 1;
    while ((*pp == 0) || (*pp == ' ') || (*pp == '\n')
	|| (*pp == '\t') || (*pp == '_'))
	pp--;
    length = pp - string + 1;
    if (length <= 0) return NULL;
    strncpy(buf[i],string, length);
    buf[i][length] = 0;
    return buf[i];
}

#define MAXINIT 3
char   *
str_initials(name)
    char   *name;
{
    static char init[4][MAXINIT + 1];
    static int i = 0;
    int     j;
    char   *p;
    char    c;

    if (name == NULL)
	return "";
    if (strlen(name) == 0)
	return "";

    i = (++i) & 3;
    j = 0;
    p = name;

    while ((c = *(p++)) && (j < MAXINIT))
	if (isupper(c))
	    init[i][j++] = c;
    init[i][j] = 0;
    return init[i];
}

/*
 * Compare strings (at most n bytes):  s1>s2: >0  s1==s2: 0  s1<s2: <0
 */

strncmp_nocase(s1, s2, n)
register char *s1, *s2;
register n;
{
    register char c1, c2;

	while (--n >= 0)
	{
	   c1 = *s1++;
	   if (c1 == 0) return 0;
	   if (isupper(c1)) c1 = tolower(c1);

	   c2 = *s2++;
	   if (c2 == 0) return 0;
	   if (isupper(c2)) c2 = tolower(c2);

	   if (c1 != c2) break;
	}
	return(n<0 ? 0 : c1 - c2);
}
strcmp_nocase(s1, s2)
register char *s1, *s2;
{
    register char c1, c2;

	while (1)
	{
	   c1 = *s1++;
	   if (isupper(c1)) c1 = tolower(c1);
	   c2 = *s2++;
	   if (isupper(c2)) c2 = tolower(c2);

	   if (c1 == 0) break;
	   if (c2 == 0) break;
	   if (c1 != c2) break;
	}
	return(c1 - c2);
}
\Rogue\Monster\
else
  echo "will not over write ./strpak.c"
fi
if `test ! -s ./Makefile`
then
echo "writing ./Makefile"
cat > ./Makefile << '\Rogue\Monster\'
all: listen rmtprint

listen: listen.o copytoheap.o filename.o strpak.o
	cc -o listen -g listen.o copytoheap.o filename.o strpak.o

rmtprint: rmtprint.o
	cc -o rmtprint -g rmtprint.o

listen.o: listen.c

rmtprint.o: rmtprint.c

copytoheap.o: copytoheap.c

filename.o: filename.c

strpak.o: strpak.c
\Rogue\Monster\
else
  echo "will not over write ./Makefile"
fi
echo "Finished archive 1 of 1"
# if you want to concatenate archives, remove anything after this line
exit