[comp.sources.amiga] v89i102: dnet - networking code

page%rishathra@Sun.COM (Bob Page) (04/29/89)

Submitted-by: dillon@postgres.berkeley.edu (Matt Dillon)
Posting-number: Volume 89, Issue 102
Archive-name: comm/dnet20-us.1

[The latest and greatest.  BSD4.2/4.3 compatible.  You'll want the
Amiga side as well. ..bob]

# This is a shell archive.
# Remove anything above and including the cut line.
# Then run the rest of the file through 'sh'.
# Unpacked files will be owned by you and have default permissions.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: SHell ARchive
# Run the following text through 'sh' to create:
#	CLEAN
#	MAKE
#	README
#	TODO
#	client/getfiles.c
#	client/putfiles.c
#	client/dsoc.c
#	client/draw.c
#	client/dprint.c
#	client/Makefile
#	dnet/channel.h
#	dnet/control.c
#	dnet/TODO3
#	dnet/subs.c
# This is archive 1 of a 2-part kit.
# This archive created: Thu Apr 27 15:41:47 1989
echo "extracting CLEAN"
sed 's/^X//' << \SHAR_EOF > CLEAN
X#!/bin/sh
Xcd dnet
Xmake clean
Xcd ../client
Xmake clean
Xcd ../server
Xmake clean
Xcd ../test
Xmake clean
Xcd ../lib
Xrm -f *.o make.out
Xcd ..
Xrm -f *.o make.out MAKE.out
X
SHAR_EOF
echo "extracting MAKE"
sed 's/^X//' << \SHAR_EOF > MAKE
X#!/bin/sh
Xcd lib
Xmake
Xcd ../dnet
Xmake
Xcd ../client
Xmake
Xcd ../server
Xmake
X
SHAR_EOF
echo "extracting README"
sed 's/^X//' << \SHAR_EOF > README
X
X
X			   DNET V2.00 UNIX SIDE
X
X			   BSD4.2/4.3 compatible
X			   note: 8 bit connection required to run dnet,
X			    which isn't always true if talking to the unix
X			    box through a port selector or terminal
X			    concentrator.
X
X
X
X	DNET (c)Copyright 1987-1989 Matthew Dillon, All Rights Reserved
X
X	Matthew Dillon
X	891 Regal Rd
X	Berkeley, Ca. 94708
X	USA
X
X	...!ihnp4!ucbvax!dillon 	USENET
X	dillon@ucbvax.Berkeley.edu	ARPANET
X	ucbvax.berkeley.edu pub/amiga	ARPANET-FTP
X
X				UNIX SETUP
X
X	The directory structure must remain intact.  simply say MAKE
X	in the main directory and it ought to compile.	There might be
X	minor problems depending on how compatible your UNIX is with
X	BSD4.3 UNIX ..	I have successfully compiled DNet on the following
X	machines:
X
X		    Dec Vax 7xx
X		    Sun 3
X		    Sequent
X
X	(1) Create a directory.  For example, ~/.DNET   WARNING!  This
X	    directory must be on a local partition.  DNET uses a unix
X	    domain socket and these do not work over network fileservers.
X
X	(2) Modify your .cshrc to add the following line:
X
X	    setenv DNETDIR  ~/.DNET/
X
X	    (or wherever you put it.  NOTE! You MUST HAVE THE TRAILING
X	     SLASH!)
X
X	(3) All binaries are usually kept in dnet.unix/bin, add this
X	    directory to your path (in your .cshrc) so DNet can find
X	    the clients.
X
X	(4) Place the file 'dnet.servers' in $DNETDIR.  Modify the file
X	    according to your home directory and where you have put the
X	    servers (usually in dnet.unix/bin).  USE ABSOLUTE PATHS,
X	    DNET.SERVERS DOES NOT UNDERSTAND ~.
X
X	NOTE: You may want to chmod $DNETDIR 700 to disallow any
X	unauthorized access to the network.
X
X
X			     DIALING UP FROM AN AMIGA
X
X	Follow the installation instructions for the Amiga side.  When
X	you dial up the UNIX system you will eventually get a prompt.
X	NOTE!  DNET normally uses 7 bit - even parity for dialing up,
X	then switches to 8 bit no parity for the protocol.  Sometimes
X	the modem or port selector will switch into 7 bit + parity mode
X	and NOT SWITCH OUT.  For this reason, you might want to use the
X	-8 option for the Amiga side of DNET (read the docs in the Amiga
X	section).
X
X	From the DNET window's CSH prompt, start the protocol with:
X
X	% dnet
X
X	That was easy.	The DNet window should go away and an FTERM window
X	should open.  This does not mean success!  If the FTERM window
X	closes again with the message "unable to connect", it was unable
X	to connect.  There are several possibilities:
X
X	(a) You do not have an 8 bit connection from your amiga to the
X	    UNIX host (DNet must be able to send and receive all 256
X	    character codes).
X
X	(b) You did not setup the DNETDIR enviroment variable properly
X
X	(c) You did not copy dnet.servers into $DNETIDR
X
X	(d) The absolute file path in dnet.servers for server #8192 (that
X	    FTerm tires to connect to) is not correct.
X
X				    KILLING DNET
X
X	In most cases simply turning off the modem will suffice.  You can
X	kill dnet more cleanly with the following sequence:
X
X	(1) From an Amiga CLI, run the QuitDnet command.  This will kill
X	    the packet protocol and cause the remote DNet to exit.
X
X	(2) From an Amiga CLI, BREAK the protocol process.  You should get
X	    the original (small) DNet window and the original login shell.
X	    If you get the DNet window but not the shell (you can't type),
X	    the protocol may still be running on the other end, in which
X	    case you forgot to QuitDnet or it didn't work for some
X	    unknown reason.  Restart the protocol w/ the appropriate
X	    menu option and try again.
X
X	(3) logout normally and close the DNet window.  Turn off your
X	    modem.
X
X			     HANGING SERVER PROCESSES
X
X	The UNIX side servers are normally left running when the protocol
X	dies.  These are very very very small and take 0 CPU (they are
X	simply waiting for connections).  These servers will still be
X	there when you log in again and DNet will use them.
X
X	You can kill UNIX side servers at any time.
X
X	Many of the same clients and servers exist on the UNIX end as
X	on the Amiga end.  The UNIX end is missing several.  The UNIX
X	end implements two commands called dsoc and draw which can be
X	used to connect to (in cooked or raw tty mode) specific servers
X	(by port #) on the Amiga side.  For example, to connect to the
X	printer server on the amiga side:
X
X	% dsoc 8198
X	This is a test
X	^D
X	%
X
X	Dumps to PRT: on the Amiga "This is a test"  Gee Wiz!
X
X				SECURITY W/ UNIX
X
X	DNet will be as secure as your account, assuming you chmod DNETDIR
X	700 (so nobody else has access to the unix domain sockets).  From
X	Home, I usually RUN DNET -X , login, start the protocol, and leave
X	it running all day.
X
X	The -X option turns of security (sets all security levels to 9),
X	thus allowing me to drive to the university and at any time from
X	my UNIX account do a getfiles or putfiles from and to my Amiga.
X
X	Refer to the amiga side documentation for more information.
X
X
X
SHAR_EOF
echo "extracting TODO"
sed 's/^X//' << \SHAR_EOF > TODO
X
X    -current connect is done synchronously.  It should be done asynchronously
X     incase the server is hung, but it really doesn't matter since the
X     servers are so incredibly easy to write and are always accepting new
X     connections (I hope).
X
X    -packet size reduction should also depend on the baud rate
X
SHAR_EOF
if `test ! -d client`
then
  mkdir client
  echo "mkdir client"
fi
echo "extracting client/getfiles.c"
sed 's/^X//' << \SHAR_EOF > client/getfiles.c
X
X/*
X *  GETFILES.C	    V1.30
X *
X *  DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved.
X *
X *  GETFILES [-dlocaldir] [-c] file/dir file/dir file/dir
X *
X *  -dlocaldir	local directory to place files
X *
X *  -c		Continue from where you left off before.  Files that already
X *		exist on the local machine will not be re-transfered.  This
X *		will also re-start in the middle of a file that was
X *		partially transfered previously.
X *
X *		This command assumes the file(s) on both machines have not
X *		been modified since the first attempt.	No other checking
X *		is done at the moment.
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/file.h>
X#include <sys/stat.h>
X#include "../server/servers.h"
X
Xtypedef unsigned char ubyte;
X
Xtypedef struct {
X    char    Cmd;
X    char    Str[64];
X    long    Val;
X} HDR;
X
Xchar *NSpaces();
X
Xchar Buf[1024];
X
Xshort ContMode;
Xchar  *NetId;
Xlong  Chan = -1;
X
Xmain(ac,av)
Xchar *av[];
X{
X    HDR Hdr;
X    short error;
X
X    {
X	char *ldir = ".";
X	struct stat stat;
X	ac = DoOption(ac, av, "N%sd%sc", &NetId, &ldir, &ContMode);
X	if (ac <= 1) {
X	    puts("GETFILES [-Nnetid -dlocaldir -c] remotefile/dir ...");
X	    fail(22);
X	}
X	if (chdir(ldir) < 0) {
X	    mkdir(ldir, 0777);
X	    if (chdir(ldir) < 0) {
X		printf("Unable to CD or make local directory: \"%s\"\n",ldir);
X		fail(21);
X	    }
X	}
X    }
X    Chan = DOpen(NetId, PORT_GFILECOPY, 126, -80);
X    if (Chan < 0) {
X	puts("Unable to connect");
X	fail(20);
X    }
X    error = WriteHeader('H', "Hello, getfiles client V1.30", 0);
X    if (error)
X	fail(LostChannel());
X    switch(ReadHeader(&Hdr)) {
X    case -1:
X	fail(LostChannel());
X    case 'H':
X	printf("%s\n", Hdr.Str);
X	break;
X    }
X    {
X	register short i;
X	long val;
X
X	for (i = 1; i < ac; ++i) {
X	    short error;
X
X	    error = WriteHeader('G', av[i], 0);
X	    if (error)
X		fail(LostChannel());
X	    switch(ReadHeader(&Hdr)) {
X	    case -1:
X		fail(LostChannel());
X	    case 'N':
X		printf("Remote error on %s: %s\n", av[i], Hdr.Str);
X		break;
X	    case 'F':
X		error = CheckNoPath(Hdr.Str);
X		if (!error) {
X		    char svpath[1024];
X		    getwd(svpath);
X		    error = GetFile(&Hdr, 0);
X		    chdir(svpath);
X		}
X		break;
X	    case 'D':
X		error = CheckNoPath(Hdr.Str);
X		if (!error) {
X		    char svpath[1024];
X		    getwd(svpath);
X		    error = GetDir(&Hdr, 0);
X		    chdir(svpath);
X		}
X		break;
X	    case 'S':
X		printf("Access Violation: %s\n", Hdr.Str);
X		break;
X	    default:
X		error = UnknownCmd(&Hdr);
X		break;
X	    }
X	    if (error)
X		fail(error);
X	}
X	if (!error) {
X	    error = WriteHeader('E', "bye", 0);
X	    if (error)
X		fail(LostChannel());
X	}
X    }
X    fail(0);
X}
X
Xfail(code)
X{
X    if (Chan >= 0)
X	close(Chan);
X    exit(code);
X}
X
XCheckNoPath(str)
Xregister char *str;
X{
X    while (*str) {
X	if (*str == '/' || *str == ':') {
X	    puts("SECURITY ALERT: Illegal path spec received");
X	    return(40);
X	}
X	++str;
X    }
X    return(0);
X}
X
XLostChannel()
X{
X    puts("DATA CHANNEL LOST");
X    return(10);
X}
X
XUnknownCmd(hdr)
XHDR *hdr;
X{
X    printf("Unrecognized command code: %02x\n", hdr->Cmd);
X}
X
X/*
X *  retrieve a file.  If ContMode set and file exists, try to append to
X *  it.
X */
X
X
X#define BSSTR "\010\010\010\010\010\010\010\010\010\010\010\010\010"
X
XGetFile(hdr, stab)
XHDR *hdr;
X{
X    int fd = -1;
X    long pos = 0;
X    short error = 0;
X
X    printf("%s%-20s ", NSpaces(stab), hdr->Str);
X    fflush(stdout);
X    if (ContMode) {
X	if ((fd = open(hdr->Str, O_WRONLY)) >= 0) {    /*  already exists  */
X	    long len;
X
X	    len = lseek(fd, 0L, 2);
X	    if (len > hdr->Val) {
X		close(fd);
X		printf("Cont Error, local file is larger than remote!: %s\n",
X		   hdr->Str
X		);
X		puts("(not downloaded)");
X		return(0);
X	    }
X	    if (len == hdr->Val) {
X		close(fd);
X		if (error = WriteHeader('S', NULL, 0))
X		    return(LostChannel());
X		puts("HAVE IT, SKIP");
X		return(0);
X	    }
X	    printf("(HAVE %ld/%ld) ", len, hdr->Val);
X	    hdr->Val -= len;		/*  that much less  */
X	    pos = len;			/*  start at offset */
X	}
X    }
X    if (fd < 0) {
X	fd = open(hdr->Str, O_WRONLY|O_CREAT|O_TRUNC, 0666);
X	if (fd < 0) {
X	    error = WriteHeader('N', "open error", 0);
X	    printf("Unable to open %s for output\n", hdr->Str);
X	    if (error)
X		return(LostChannel());
X	    return(1);
X	}
X    }
X    error = WriteHeader('Y', NULL, pos);    /*  yes, gimme gimme    */
X
X    /*
X     *	Retrieve the data
X     */
X
X    if (!error) {
X	register long left = hdr->Val;
X	register long cnt = pos;
X	long total = pos + left;
X
X	printf("             ");
X	while (left) {
X	    register long n = (left > sizeof(Buf)) ? sizeof(Buf) : left;
X	    printf("%s%6ld/%6ld", BSSTR, cnt, total);
X	    fflush(stdout);
X	    if (ggread(Chan, Buf, n) != n) {
X		error = 5;
X		break;
X	    }
X	    if (write(fd, Buf, n) != n) {
X		puts("Local Write failed!");
X		error = 6;
X		break;
X	    }
X	    left -= n;
X	    cnt += n;
X	}
X	printf("%s%6ld/%6ld  %s", BSSTR, cnt, total, 
X	    ((cnt == total) ? "OK" : "INCOMPLETE")
X	);
X    }
X    puts("");
X    if (error) {
X	fchmod(fd, 0222);
X	close(fd);
X	return(LostChannel());
X    }
X    close(fd);
X    return(error);
X}
X
X/*
X *  Retrieve a directory.  Create it if necessary.
X */
X
XGetDir(hdr, stab)
XHDR *hdr;
X{
X    short error = 0;
X    long dirlock;
X    static HDR Hdr;	    /*	note: static */
X    char svpath[1024];
X
X    printf("%s%-20s(DIR)\n", NSpaces(stab), hdr->Str);
X    getwd(svpath);
X    if (chdir(hdr->Str) < 0) {
X	mkdir(hdr->Str, 0777);
X	if (chdir(hdr->Str) < 0) {
X	    error = WriteHeader('N', "couldn't create", 0);
X	    printf("Unable to create local directory: %s\n", hdr->Str);
X	    if (error)
X	        return(LostChannel());
X	    return(1);
X	}
X    }
X    error = WriteHeader('Y', NULL, 0);  /*  yes, gimme gimme    */
X    while (!error) {
X	switch(ReadHeader(&Hdr)) {
X	case -1:
X	    error = 1;
X	    break;
X	case 'E':                   /*  end of directory    */
X	    chdir(svpath);
X	    return(0);
X	    break;
X	case 'F':
X	    error = CheckNoPath(Hdr.Str);
X	    if (!error) {
X		char svpath2[1024];
X		getwd(svpath2);
X		error = GetFile(&Hdr, stab + 4);
X		chdir(svpath2);
X	    }
X	    break;
X	case 'D':
X	    error = CheckNoPath(Hdr.Str);
X	    if (!error) {
X		char svpath2[1024];
X		getwd(svpath2);
X		error = GetDir(&Hdr, stab + 4);
X		chdir(svpath2);
X	    }
X	    break;
X	case 'S':
X	    printf("Access Violation: %s\n", Hdr.Str);
X	    break;
X	case 'N':
X	    printf("REMOTE ERROR: %s\n", Hdr.Str);
X	    error = 10;
X	    break;
X	default:
X	    error = UnknownCmd(&Hdr);
X	    break;
X	}
X    }
X    chdir(svpath);
X    return(LostChannel());
X}
X
XWriteHeader(c, str, len)
Xchar c;
Xchar *str;
Xlong len;
X{
X    ubyte sl;
X
X    if (str == NULL)
X	str = "";
X    sl = strlen(str);
X
X    if (gwrite(Chan, &c, 1) < 0)
X	return(1);
X    if (gwrite(Chan, &sl,1) < 0)
X	return(1);
X    if (gwrite(Chan, str, sl) != sl)
X	return(1);
X    len = htonl68(len);
X    if (gwrite(Chan, &len, 4) != 4)
X	return(1);
X    return(0);
X}
X
XReadHeader(hdr)
XHDR *hdr;
X{
X    ubyte sl;
X    ubyte cmd;
X
X    hdr->Cmd = -1;
X    if (ggread(Chan, &cmd, 1) != 1)
X	return(-1);
X    if (ggread(Chan, &sl, 1) != 1)
X	return(-1);
X    if (sl >= sizeof(hdr->Str)) {
X	puts("Software error: received file name length too long");
X	return(-1);
X    }
X    if (ggread(Chan, hdr->Str, sl) != sl)
X	return(-1);
X    hdr->Str[sl] = 0;
X    if (ggread(Chan, &hdr->Val, 4) != 4)
X	return(-1);
X    hdr->Val = ntohl68(hdr->Val);
X    hdr->Cmd = cmd;
X    return(hdr->Cmd);
X}
X
Xchar *
XNSpaces(n)
Xshort n;
X{
X    static char Buf[128];
X    static short in = 0;
X    static short last;
X
X    if (in == 0) {
X	register short i;
X	in = 1;
X	for (i = 0; i < sizeof(Buf); ++i)
X	    Buf[i] = ' ';
X    }
X    Buf[last] = ' ';
X    if (n < 127)
X	Buf[n] = 0;
X    last = n;
X    return(Buf);
X}
X
SHAR_EOF
echo "extracting client/putfiles.c"
sed 's/^X//' << \SHAR_EOF > client/putfiles.c
X
X/*
X *  PUTFILES.C
X *
X *	DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved
X *
X *  Download one or more files from the remote computer
X *
X *  PUTFILES file/dir1 file/dir2 ... file/dirN
X *
X *  placed in directory server ran from on remote host.
X */
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/dir.h>
X#include <stdio.h>
X#include <sys/file.h>
X#include "../server/servers.h"
X
Xchar Buf[1024];
X
Xtypedef struct stat STAT;
X
Xmain(ac,av)
Xchar *av[];
X{
X    long chan;
X    long n, len, orig;
X    long fh;
X    short i, j;
X    char fn;
X
X    if (ac == 1) {
X	puts("putfiles V1.00 (c)Copyright 1987, Matthew Dillon, All Rights Reserved");
X	puts("putfiles file/dir file/dir .....");
X	exit(1);
X    }
X
X    chan = DOpen(NULL, PORT_FILECOPY, -80, 126);
X    if (chan < 0) {
X	puts("Unable to connect");
X	exit(1);
X    }
X    ggread(chan, &fn, 1);
X    if (fn != 'Y') {
X	puts("Remote Server Software Error");
X	close(chan);
X    }
X    for (i = 1; i < ac; ++i) {
X	if (strncmp(av[i], "-d", 2) == 0) {/*-ddir or -d dir*/
X	    char *dir = av[i]+2;
X	    if (*dir == 0 && i+1 < ac) {
X		++i;
X		dir = av[i];
X	    }
X	    if (writehdr_nc(chan, 'C', dir, 0) != 'Y') {
X		puts ("unable to go to specified remote directory");
X		break;
X	    }
X	} else {
X	    if (putname(chan, av[i]) < 0)
X	        break;
X	}
X    }
X    printf("\nclosing... ");
X    fflush(stdout);
X    close(chan);
X    puts("done");
X}
X
Xputname(chan, file)
Xchar *file;
X{
X    STAT sstat;
X    char svdir[256];
X    int ret;
X
X    printf("%-20s ", file);
X    if (stat(file, &sstat) < 0) {
X	puts("NOT FOUND");
X	return(1);
X    }
X    if (sstat.st_mode & S_IFDIR) {
X	DIR *dir;
X	struct direct *de;
X
X	getwd(svdir);
X	puts("DIR");
X	chdir(file);
X	if (writehdr(chan, 'X', file, 0) != 'Y') {
X	    puts("Remote unable to make directory");
X	    goto f1;
X	}
X	if (dir = opendir(".")) {
X	    while (de = readdir(dir))  {
X		if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name,"..")==0)
X		    continue;
X		if (putname(chan, de->d_name) < 0) {
X		    ret = -1;
X		    break;
X		}
X	    }
X	}
X	writehdr(chan, 'Y', "?", 0);
Xf1:
X	chdir(svdir);
X    } else {
X	ret = putfile(chan, file);
X    }
X    return(ret);
X}
X
Xputfile(chan, file)
Xchar *file;
X{
X    int fd = open(file, O_RDONLY);
X    long n, r, len;
X    long ttl = 0;
X    char co;
X
X    fflush(stdout);
X    if (fd < 0) {
X	puts("FILE NOT FOUND");
X	return(0);
X    }
X    len = ttl = lseek(fd, 0, 2);
X    lseek(fd, 0, 0);
X    if (writehdr(chan, 'W', file, len) != 'Y') {
X	puts("REMOTE UNABLE TO ACCEPT FILE");
X	close(fd);
X	return(0);
X    }
X    printf("%6ld/%-6ld", ttl - len, ttl);
X    while (len) {
X	fflush(stdout);
X	r = (len > sizeof(Buf)) ? sizeof(Buf) : len;
X	n = read(fd, Buf, r);
X	if (n != r) {
X	    puts("Local File error");
X	    close(fd);
X	    return(-1);
X	}
X	if (gwrite(chan, Buf, n) != n) {
X	    puts("Remote error");
X	    close(fd);
X	    return(-1);
X	}
X	len -= n;
X	printf("\010\010\010\010\010\010\010\010\010\010\010\010\010");
X	printf("%6ld/%-6ld", ttl - len, ttl);
X    }
X    close(fd);
X    if (len) {
X	puts("REMOTE ERROR");
X	return(-1);
X    }
X    printf("  Queued, waiting... ");
X    fflush(stdout);
X    ggread(chan, &co, 1);
X    if (co != 'Y') {
X	puts("Remote Server Software Error");
X	return(-1);
X    }
X    puts("OK");
X    return(0);
X}
X
Xwritehdr(chan, c, name, len)
Xunsigned char c;
Xchar *name;
Xlong len;
X{
X    short i;
X    for (i = strlen(name) - 1; i >= 0; --i) {
X	if (name[i] == '/' || name[i] == ':')
X	    break;
X    }
X    name += i + 1;
X    return(writehdr_nc(chan, c, name, len));
X}
X
Xwritehdr_nc(chan, c, name, len)
Xunsigned char c;
Xchar *name;
Xlong len;
X{
X    gwrite(chan, &c, 1);
X    c = strlen(name) + 1;
X    gwrite(chan, &c, 1);
X    gwrite(chan, name, c);
X    len = htonl68(len);
X    gwrite(chan, &len, 4);
X    if (ggread(chan, &c, 1) == 1)
X	return(c);
X    return(-1);
X}
X
SHAR_EOF
echo "extracting client/dsoc.c"
sed 's/^X//' << \SHAR_EOF > client/dsoc.c
X
X/*
X *	DSOC.C
X *
X *	DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved
X *
X *	DSOC [port#]
X *
X *      Connect to the specified port# .. Used to connect to a remote CLI
X *	(s_shell server on the Amiga, which requires PIPE: to work, port 8196,
X *	is the default)
X *
X *	Uses standard cooked mode instead of RAW mode.
X */
X
X
X#include <sys/types.h>
X#include <sys/ioctl.h>
X#include <stdio.h>
X#include <sys/file.h>
X#include <signal.h>
X#include <errno.h>
X#include "../server/servers.h"
X
Xint fd;
Xchar buf[2048];
X
Xmain(ac,av)
Xchar *av[];
X{
X    int n;
X    extern int handler();
X    int port = (av[1]) ? atoi(av[1]) : PORT_AMIGASHELL;
X
X    puts("DSOC V1.01 11 March 1988 Connecting");
X    fd = DOpen(NULL, port, 0, 0);
X    if (fd < 0) {
X	perror("DOpen");
X	exit(1);
X    }
X    puts("Connected");
X    signal(SIGIO, handler);
X    fcntl(fd, F_SETOWN, getpid());
X    fcntl(fd, F_SETFL, FNDELAY|FASYNC);
X    while ((n = gread(0, buf, sizeof(buf))) > 0) {
X	gwrite(fd, buf, n);
X    }
X    fprintf(stderr, "EOF\n");
X    DEof(fd);
X    for (;;)
X	pause();
X}
X
Xhandler()
X{
X    int n;
X    char buf[1024];
X
X    while ((n = read(fd, buf, sizeof(buf))) > 0) 
X	write(1, buf, n);
X    if (n == 0) {
X	write(1, "REMEOF\n", 7);
X	exit(1);
X    }
X}
X
SHAR_EOF
echo "extracting client/draw.c"
sed 's/^X//' << \SHAR_EOF > client/draw.c
X
X/*
X *	DRAW.C
X *
X *	DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved
X *
X *	DRAW [port#]
X *
X *	Put terminal into RAW mode and connect to the remote dnet port.
X *	used mainly to test DNET.  Can also be used open a TERM window on
X *	the amiga (via the STERM server), which is the default (port 8195)
X */
X
X
X
X#include <sys/types.h>
X#include <sys/ioctl.h>
X#include <stdio.h>
X#include <sys/file.h>
X#include <signal.h>
X#include <errno.h>
X#include "../server/servers.h"
X
Xint fd;
Xchar buf[4096];
X
Xmain(ac,av)
Xchar *av[];
X{
X    int n;
X    extern int handler();
X    int port = (av[1]) ? atoi(av[1]) : PORT_AMIGATERM;
X
X    puts("DRAW V1.01 11 March 1988 Connecting");
X    fd = DOpen(NULL, port, 0, 0);
X    if (fd < 0) {
X	perror("DOpen");
X	exit(1);
X    }
X    puts("Connected");
X    signal(SIGIO, handler);
X    ttyraw();
X    fcntl(fd, F_SETOWN, getpid());
X    fcntl(fd, F_SETFL, FNDELAY|FASYNC);
X    fcntl(0,  F_SETFL, FNDELAY);
X    while ((n = gread(0, buf, sizeof(buf))) > 0) {
X	gwrite(fd, buf, n);
X    }
X    fprintf(stderr, "EOF\n");
X    DEof(fd);
X    for (;;)
X	pause();
X}
X
Xhandler()
X{
X    int n;
X    char buf[1024];
X
X    while ((n = read(fd, buf, sizeof(buf))) > 0) 
X	write(1, buf, n);
X    if (n == 0) {
X	write(1, "REMEOF\n", 7);
X	ttynormal();
X	exit(1);
X    }
X}
X
Xstatic struct sgttyb ttym;
X
Xttyraw()
X{
X    ioctl (0, TIOCGETP, &ttym);
X    ttym.sg_flags |= RAW;
X    ttym.sg_flags &= ~CBREAK;
X    ttym.sg_flags &= ~ECHO;
X    ioctl (0, TIOCSETP, &ttym);
X}
X
Xttynormal()
X{
X    ioctl (0, TIOCGETP, &ttym);
X    ttym.sg_flags &= ~RAW;
X    ttym.sg_flags |= CBREAK;
X    ttym.sg_flags |= ECHO;
X    ioctl (0, TIOCSETP, &ttym);
X}
X
SHAR_EOF
echo "extracting client/dprint.c"
sed 's/^X//' << \SHAR_EOF > client/dprint.c
X
X/*
X *  DPRINT.C
X *
X *	DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved
X *
X *  send files to the other computer's printer.
X *
X *  DPRINT [files]	(stdin if no files specified)
X */
X
X#include <stdio.h>
X#include "../server/servers.h"
X
Xmain(ac,av)
Xchar *av[];
X{
X    register short i;
X    long chan;
X
X    chan = DOpen(NULL, PORT_PRINTER, -80, 126);
X    if (chan < 0) {
X	puts("Unable to connect");
X	exit(1);
X    }
X    for (i = 1; i < ac; ++i) {
X	FILE *fi;
X
X	printf("%-20s ", av[i]);
X	fflush(stdout);
X	if (fi = fopen(av[i], "r")) {
X	    printf("dumping ");
X	    fflush(stdout);
X	    dump_file(fi, chan);
X	    fclose(fi);
X	    puts("");
X	}
X    }
X    if (ac == 1)
X	dump_file(stdin, chan);
X    close(chan);
X}
X
Xdump_file(fi, chan)
XFILE *fi;
X{
X    int n;
X    char buf[256];
X
X    while ((n = fread(buf, 1, sizeof(buf), fi)) > 0) {
X	gwrite(chan, buf, n);
X    }
X}
X
SHAR_EOF
echo "extracting client/Makefile"
sed 's/^X//' << \SHAR_EOF > client/Makefile
X
X#	DNET CLIENTS
X#
X#   DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved.
X#
X
XNETLIB = ../lib/dnetlib.o
XBIN = ../bin
X
X
Xall:	$(NETLIB) $(BIN)/draw $(BIN)/dsoc $(BIN)/putfiles $(BIN)/dprint \
X	$(BIN)/getfiles
X
X$(BIN)/draw:		draw.o
X	cc draw.o $(NETLIB) -o $(BIN)/draw
X
X$(BIN)/dsoc:		dsoc.o
X	cc dsoc.o $(NETLIB) -o $(BIN)/dsoc
X
X$(BIN)/getfiles:    	getfiles.o 
X	cc getfiles.o $(NETLIB) -o $(BIN)/getfiles
X
X$(BIN)/putfiles:    	putfiles.o 
X	cc putfiles.o $(NETLIB) -o $(BIN)/putfiles
X
X$(BIN)/dprint:		dprint.o
X	cc dprint.o $(NETLIB) -o $(BIN)/dprint
X
Xclean:
X	rm -f *.o make.out
X
SHAR_EOF
if `test ! -d dnet`
then
  mkdir dnet
  echo "mkdir dnet"
fi
echo "extracting dnet/channel.h"
sed 's/^X//' << \SHAR_EOF > dnet/channel.h
X
X/*
X *  CHANNEL.H
X *
X *	DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved
X *
X *  Channel structures for SCMD_* channel commands.
X *
X *  NOTE: These structures may be ONLY 0,2,4, or 6 bytes in size.
X */
X
X#define CSWITCH struct _CSWITCH
X#define COPEN	struct _COPEN
X#define CCLOSE	struct _CCLOSE
X#define CACKCMD struct _CACKCMD
X#define CEOFCMD struct _CEOFCMD
X#define CIOCTL  struct _CIOCTL
X
XCSWITCH {		/*  SWITCH current data channel */
X    ubyte   chanh;	/*  channel to switch to	*/
X    ubyte   chanl;
X};
X
XCOPEN { 		/*  OPEN port on channel	*/
X    ubyte   chanh;	/*  try to use this channel     */
X    ubyte   chanl;
X    ubyte   porth;	/*  requested port from client  */
X    ubyte   portl;
X    ubyte   error;	/*  error return 0=ok		*/
X    char    pri;	/*  chan. priority -127 to 126  */
X};
X
XCCLOSE {		/*  CLOSE a channel		*/
X    ubyte   chanh;	/*  channel to close		*/
X    ubyte   chanl;
X};
X
XCACKCMD {		/*  Acknowledge an open	    	*/
X    ubyte   chanh;	/*  channel   			*/
X    ubyte   chanl;
X    ubyte   error;	/*  0=ok 1=fail 33=retry w/ different channel	*/
X    ubyte   filler;
X};
X
XCEOFCMD {		/*  Send [R/W] EOF		*/
X    ubyte   chanh;	/*  channel to send EOF on	*/
X    ubyte   chanl;
X    ubyte   flags;	/*  CHANF_ROK/WOK (bits to clear)  */
X    ubyte   filler;
X};
X
XCIOCTL {
X    ubyte   chanh;	/* channel			*/
X    ubyte   chanl;
X    ubyte   cmd;	/* ioctl command		*/
X    ubyte   valh;	/* ioctl value			*/
X    ubyte   vall;
X    ubyte   valaux;	/* auxillary field		*/
X};
X
X#define CIO_SETROWS	1	/* PTY's only			*/
X#define CIO_SETCOLS	2	/* PTY's only			*/
X#define CIO_STOP	3	/* any channel, flow control	*/
X#define CIO_START	4	/* any channel, flow control	*/
X#define CIO_FLUSH	5	/* any channel, flush pending 	*/  
X				/* writes down the toilet	*/
X
X#define CHAN	struct _CHAN
X
XCHAN {
X    int	    fd;		/*	file descriptor for channel		*/
X    uword   port;	/*	port#  (used in open sequence)		*/
X    ubyte   state;	/*	state of channel			*/
X    ubyte   flags;	/*	channel flags				*/
X    char    pri;	/*	priority of channel			*/
X};
X
SHAR_EOF
echo "extracting dnet/control.c"
sed 's/^X//' << \SHAR_EOF > dnet/control.c
X
X/*
X *  CONTROL.C
X *
X *	DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved
X *
X *  -handle various actions:
X *	RTO	- read timeout
X *	WTO	- write timeout (a packet not acked)
X *	RNET	- state machine for raw receive packet
X *	WNET	- starts timeout sequence if Write occured
X *	WUPDATE - update write windows, send packets
X *	RUPDATE - execute received packets in correct sequence
X *	RECCMD	- execute decoded SCMD commands obtained from received
X *			packets.
X */
X
X#include "dnet.h"
X#include <stdio.h>
X
Xextern void do_cmd();
X
X/*
X *  RTO:    read timeout.   Timeout occured while waiting for the rest of
X *	    a packet.  Reset state and restart read.
X */
X
Xdo_rto()
X{
X    RState = 0;
X    RcvData = 0;
X    if (DDebug)
X	fprintf(stderr, "RTO\n");
X}
X
X/*
X *  WTO:    Write timeout (unresolved output windows exist).  Resend a CHECK
X *	    command for all unresolved (READY) output windows
X */
X
Xvoid
Xdo_wto()
X{
X    register short i;
X    register PKT *pkt;
X    register uword len = 0;
X
X    if (DDebug)
X	fprintf(stderr, "WTO\n");
X    if (Restart) {
X	WCBuf[0] = SYNC;
X	WCBuf[1] = PKCMD_RESTART;
X	WCBuf[2] = (WCBuf[0]<<1)^WCBuf[1];
X	NetWrite(WCBuf, 3, 1);
X	WTimeout(WTIME);
X	return;
X    }
X    for (i = 0; i < WPUsed; ++i) {
X	pkt = WPak[i];
X	if (pkt->state == READY) {  /*  send check  */
X	    WCBuf[len+0] = SYNC;
X	    WCBuf[len+1] = PKCMD_CHECK | ((WPStart + i) << 5);
X	    WCBuf[len+2] = (WCBuf[len+0] << 1) ^ WCBuf[len+1];
X	    len += 3;
X	}
X    }
X    if (len) {
X	NetWrite(WCBuf, len, 1);
X	WTimeout(WTIME);
X    }
X}
X
X/*
X *  RNET:   Receved data ready from network.  The RNet request will
X *	    automagically be reissued on return.
X */
X
Xdo_rnet()
X{
X    register ubyte *ptr = RcvBuf;
X    register long len  = RcvData;
X    long lrd = len;
X    long mark;
X    static uword dlen;
X    static uword dblen;
X    static ubyte dctl;
X
X    if (DDebug)
X	fprintf(stderr, "NETREAD %08lx %ld\n", RcvBuf, RcvData);
X    while (len) {
X	switch(RState) {
X	case RS_SYNC:
X	    --len;
X	    if (*ptr++ == SYNC)
X		RState = RS_CTL;
X	    break;
X	case RS_CTL:
X	    --len;
X	    dctl = *ptr++;
X	    RState = RS_CCHK;
X	    break;
X	case RS_CCHK:
X	    /* warning: (ubyte) cast not implemented properly for some
X	     * compilers
X	     */
X	    if ((((SYNC<<1)^dctl) & 0xFF) == *ptr) {
X		++ptr;
X		--len;
X		if (dctl & PKF_DATA) {
X		    RState = RS_LEN1;
X		    break;
X		}
X		RState = RS_SYNC;
X		if (DDebug)
X		    fprintf(stderr, "DO COMMAND (0DATA) 0x%02lx\n", dctl);
X		do_cmd(dctl, NULL, 0);
X	    } else {
X		if (DDebug)
X		    fprintf(stderr, "RS_CCHK FAILED\n");
X		if (len) {      /*  Resync at earliest point    */
X		    ++len;
X		    --ptr;
X		}
X	    }
X	    RState = RS_SYNC;
X	    break;
X	case RS_LEN1:
X	    dlen = *ptr;
X	    ++ptr;
X	    --len;
X	    RState = RS_LEN2;
X	    break;
X	case RS_LEN2:
X	    dlen = (dlen << 8) + *ptr + 2;
X	    ++ptr;
X	    --len;
X	    if (dlen > MAXPKT+2) {
X		if (DDebug)
X		    fprintf(stderr,"RAW DATA: LENGTH FAILURE (cmd %02x) %ld\n",
X			dctl, dlen
X		    );
X		RState = RS_SYNC;
X		break;
X	    }
X	    RState = RS_DATA;
X	    break;
X	case RS_DATA:
X	    if (DDebug)
X		fprintf(stderr, "RS_DATA->");
X	    if (dlen <= len) {
X		register uword chk = chkbuf(ptr, dlen - 2);
X		if ((chk >> 8) == ptr[dlen-2] && (chk & 0xFF) == ptr[dlen-1]) {
X		    if (DDebug)
X			fprintf(stderr, "DO MPX (%ld DATA) 0x%02lx\n",
X			    dlen-2, dctl
X			);
X		    do_cmd(dctl, ptr, dlen-2);
X		    len -= dlen;
X		    ptr += dlen;
X		} else {
X		    if (DDebug)
X			fprintf(stderr, "RS_DATA: FAILED\n");
X		}
X		RState = RS_SYNC;
X	    } else {
X		if (DDebug)
X		    fprintf(stderr, "RS_DATA: INCOMPLETE READ\n");
X		goto break2;	/*  incomplete read  */
X	    }
X	    break;
X	}
X    }
Xbreak2:
X    switch(RState) {
X    case RS_DATA:
X	RExpect = dlen - len;
X	if (RExpect < 0) {
X	    fprintf(stderr, "panic, RExpect = %ld\n", RExpect);
X	    exit(1);
X	}
X	break;
X    default:
X	RExpect = 0;
X	break;
X    }
X    do_cmd(-1, NULL, 0);
X    mark = len;
X    {
X	if (len && ptr != RcvBuf)
X	    bcopy(ptr, RcvBuf, len);
X	RcvData = len;
X    }
X    if (RState == RS_DATA) {
X	;
X	/*
X	 *RDisable();
X	 *RTimeout(RTIME/1000);
X	 */
X    }
X    /*	REMOVED 21 Sept 1988
X     * do_wupdate();
X     */
X    return(mark);
X}
X
X/*
X *  WNET:   The last data packet has been sent to the network...  Start a
X *	    timeout sequence (1 second).  If this times out we will send
X *	    a CHECK packet for all unresolved transmission windows.
X */
X
Xdo_wnet()
X{
X    if (DidWrite) {
X	DidWrite = 0;
X	if (DDebug)
X	    fprintf(stderr, "WNET-STARTTO\n");
X	WTimeout(WTIME);
X    } else {
X	if (DDebug)
X	    fprintf(stderr, "WNET-NOP\n");
X    }
X}
X
X/*
X *  DO_WUPDATE()
X *
X *  (1) Remove EMPTY windows at head of transmit queue.
X *  (2) Fill up transmit queue with pending requests, if any.
X *
X *  First two bits determine CMD as follows:
X *	    0bbbbbbb		DATA	    0-127 bytes of DATA
X *	    10bbbccc		DATA	    0-7 bytes of DATA, ccc=channel
X *								  command.
X *	    11bbbbbb bbbbbbbb	DATA	    0-1023 bytes of DATA
X */
X
Xdo_wupdate()
X{
X    static short XPri;
X    int maxpktsize;
X    register XIOR *ior;
X    register PKT *pkt;
X    register long len;
X
X    while (WPUsed && WPak[0]->state == EMPTY) {
X	pkt = WPak[0];
X	WPak[0] = WPak[1];
X	WPak[1] = WPak[2];
X	WPak[2] = WPak[3];
X	WPak[3] = pkt;
X	--WPUsed;
X	++WPStart;
X    }
X    if (Restart)
X	return(0);
X
X    while (WPUsed != 4 && (ior = (XIOR *)RemHead(&TxList))) {
X	register long offset = 0;
X
X	{
X	    short npri;
X
X	    if (ior->io_Command == SCMD_DATA)
X		npri = ior->io_Pri << 2;
X	    else
X		npri = XPri;
X	    if (npri >= XPri)  {
X		XPri = npri;
X	    } else {
X		if (XPri - npri > 100)
X		    XPri -= 10;
X		else if (XPri - npri > 50)
X		    XPri -= 5;
X		else
X		    --XPri;
X	    }
X	    maxpktsize = MAXPKT - (XPri - npri);
X	    if (DDebug)
X		fprintf(stderr, "**MAXPKTSIZE = %ld  XPri %ld npri %ld\n",
X		    maxpktsize, XPri, npri
X		);
X	    if (maxpktsize < MINPKT)
X		maxpktsize = MINPKT;
X	}
X
X	pkt = WPak[WPUsed];
X	pkt->state = READY;
X	pkt->sync  = SYNC;
X	pkt->ctl   = PKCMD_WRITE | PKF_DATA | ((WPStart + WPUsed)<<5);
X	pkt->cchk = (pkt->sync << 1) ^ pkt->ctl;
X	for (;;) {
X	    if (offset > (maxpktsize-8))                /*  not enough room */
X		break;
X	    if (ior->io_Command == SCMD_DATA && ior->io_Channel != WChan) {
X		/*  CSWITCH */
X		WChan = ior->io_Channel;
X		pkt->data[offset+0] = 0x80|SCMD_SWITCH|(2<<3);
X		pkt->data[offset+1] = WChan >> 8;
X		pkt->data[offset+2] = WChan;
X		offset += 3;
X		if (DDebug)
X		    fprintf(stderr, "SEND SWITCH %ld\n", WChan);
X	    }
X	    len = ior->io_Length - ior->io_Actual;
X	    if (ior->io_Command == SCMD_DATA) {     /*  DATA    */
X		if (DDebug)
X		    fprintf(stderr, "SEND DATA %ld bytes\n", len);
X		if (offset + len > (maxpktsize-4))
X		    len = (maxpktsize-4) - offset;
X		if (len < 128) {
X		    pkt->data[offset] = len;
X		    ++offset;
X		} else {
X		    pkt->data[offset+0] = (len>>8)|0xC0;
X		    pkt->data[offset+1] = len;
X		    offset += 2;
X		}
X	    } else {				    /*	COMMAND */
X		pkt->data[offset] = 0x80|ior->io_Command|(len<<3);
X		++offset;
X	    }
X	    bcopy((char *)ior->io_Data+ior->io_Actual,pkt->data+offset,len);
X	    offset += len;
X	    ior->io_Actual += len;
X	    if (ior->io_Actual == ior->io_Length) {
X		free(ior->io_Data);
X		free(ior);
X		ior = (XIOR *)RemHead(&TxList);          /* Next packet      */
X		if (ior == NULL)
X		    break;
X	    }
X	}
X	pkt->iolength = offset + OVERHEAD;
X	pkt->lenh = offset >> 8;
X	pkt->lenl = offset;
X	{
X	    register uword chksum = chkbuf(pkt->data, offset);
X	    pkt->data[offset+0] = chksum >> 8;
X	    pkt->data[offset+1] = chksum;
X	}
X	NetWrite(&pkt->sync, pkt->iolength, 1);
X	if (ior) {
X	    ++ior->io_Pri;
X	    Enqueue(&TxList, ior);
X	    --ior->io_Pri;
X	}
X	++WPUsed;
X    }
X}
X
Xvoid
Xdumpcheck(ptr)
Xregister ubyte *ptr;
X{
X    register short i;
X    for (i = 0;i < 8; ++i) {
X	if (ptr[i])
X	    replywindow(i);
X	ptr[i] = 0;
X    }
X}
X
Xvoid
Xdo_cmd(ctl, buf, bytes)
Xshort ctl;	/* actually a ubyte */
Xubyte *buf;
X{
X    ubyte window = ctl >> 5;
X    ubyte rwindow;
X    static ubyte Chk, Chkwin[8];	/* remember window checks */
X
X    if (ctl == -1)  {                           /* end of sequence */
X	dumpcheck(Chkwin);
X	Chk = 0;
X	return;
X    }
X    if ((ctl & PKF_MASK) == PKCMD_CHECK) {      /* CHECK packet    */
X	Chkwin[window] = 1;
X	Chk = 1;
X	return;
X    }
X    if (Chk) {                                  /* NON-CHECK packet*/
X	dumpcheck(Chkwin);
X	Chk = 0;
X    }
X    switch(ctl & PKF_MASK) {
X    case PKCMD_WRITE:
X	rwindow = (window - RPStart) & 7;
X	if (rwindow < 4) {
X	    bcopy(buf, RPak[rwindow]->data, bytes);
X	    RPak[rwindow]->iolength = bytes;
X	    RPak[rwindow]->state = READY;
X	    if (rwindow == 0)
X		do_rupdate();
X	}
X	replywindow(window);
X	break;
X    case PKCMD_ACK:
X	rwindow = (window - WPStart) & 7;
X	if (rwindow < WPUsed)       /*  mark as sent    */
X	    WPak[rwindow]->state = EMPTY;
X	break;
X    case PKCMD_NAK:		    /*	resend		*/
X	rwindow = (window - WPStart) & 7;
X	if (rwindow < WPUsed) {     /*  resend          */
X	    NetWrite(&WPak[rwindow]->sync, WPak[rwindow]->iolength, 0);
X	} else {
X	    fprintf(stderr, "Soft Error: Illegal NAK\n");
X	}
X	break;
X    case PKCMD_ACKRSTART:
X    case PKCMD_RESTART:
X	{
X	    uword chan;
X	    uword chksum;
X	    int len;
X	    int fd;
X
X	    if ((ctl & PKF_MASK) == PKCMD_ACKRSTART)
X		Restart = 0;
X	    do_netreset();
X	    if ((ctl & PKF_MASK) == PKCMD_RESTART) {
X		gethostname(WCBuf+5, sizeof(WCBuf)-5-3);
X		len = strlen(WCBuf+5)+1;
X		WCBuf[0] = SYNC;
X		WCBuf[1] = PKCMD_ACKRSTART | PKF_DATA;
X		WCBuf[2] = (SYNC << 1) ^ WCBuf[1];
X		WCBuf[3] = 0;
X		WCBuf[4] = len;
X		chksum = chkbuf(WCBuf+5, len);
X		WCBuf[5+len] = chksum >> 8;
X		WCBuf[6+len] = chksum;
X		NetWrite(WCBuf, 7+len, 1);
X	    }
X	    if (bytes)
X		setlistenport(buf);
X	    else
X		setlistenport("");
X	    do_wupdate();
X	    WTimeout(WTIME);
X	}
X	break;
X    }
X    do_rupdate();
X}
X
Xdo_rupdate()
X{
X    while (RPak[0]->state == READY) {
X	register PKT *pkt = RPak[0];
X	register ubyte *ptr = pkt->data;
X	register uword len;
X	uword iolen = pkt->iolength;
X	ubyte cmd;
X
X	if (DDebug)
X	    fprintf(stderr, "Interpret Received Commands\n");
X	while (iolen) {
X	    cmd = SCMD_DATA;
X	    len = ptr[0];
X	    ++ptr;
X	    --iolen;
X	    if (len >= 128) {
X		if (len < 0xC0) {
X		    cmd = len & 7;
X		    len = (len >> 3) & 7;
X		} else {
X		    len = ((len << 8) | *ptr) & 0x3FFF;
X		    ++ptr;
X		    --iolen;
X		}
X	    }
X	    iolen -= len;
X	    if (DDebug)
X		fprintf(stderr, " MPXCMD %ld (%ld bytes)\n", cmd, len);
X	    do_reccmd(cmd, ptr, len);
X	    ptr += len;
X	}
X	RPak[0] = RPak[1];
X	RPak[1] = RPak[2];
X	RPak[2] = RPak[3];
X	RPak[3] = pkt;
X	pkt->state = EMPTY;
X	++RPStart;
X    }
X}
X
Xdo_reccmd(cmd, ptr, len)
Xubyte *ptr;
X{
X    switch(cmd) {
X    case SCMD_DATA:
X	if (RChan < MAXCHAN && (Chan[RChan].flags & CHANF_ROK))
X	    gwrite(Chan[RChan].fd, ptr, len);
X	break;
X    case SCMD_SWITCH:
X	RChan = (ptr[0]<<8)|ptr[1];
X	break;
X    case SCMD_OPEN:
X	{
X	    register COPEN *cop = (COPEN *)ptr;
X	    CACKCMD ack;
X	    uword chan = (cop->chanh << 8) | cop->chanl;
X
X	    ack.chanh = cop->chanh;
X	    ack.chanl = cop->chanl;
X	    ack.error = 0;
X
X	    if (chan >= MAXCHAN || Chan[chan].state) {
X		ack.error = 33;     /* magic */
X		WriteStream(SCMD_ACKCMD, &ack, sizeof(CACKCMD), chan);
X		break;
X	    }
X	    {
X		int error;
X		int s;
X		uword port = (cop->porth<<8)|cop->portl;
X
X		if (isinternalport(port)) {
X		    error = iconnect(&s, port);
X		} else {
X		    struct sockaddr sa;
X		    s = socket(PF_UNIX, SOCK_STREAM, 0);
X		    if (DDebug)
X			fprintf(stderr, " REC OPEN, CONNECTING ch%d po%d\n",
X			    chan, port
X			);
X		    sa.sa_family = AF_INET;
X		    sprintf(sa.sa_data,".PORT.%ld", port);
X		    error = connect(s, &sa, sizeof(sa));
X		    if (error < 0) {
X			startserver(port);
X			error = connect(s, &sa, sizeof(sa));
X		    }
X		    if (DDebug)
X			fprintf(stderr, " CONNECTED err=%ld\n", error);
X		}
X		if (error < 0) {
X		    ack.error = 2;
X		} else {
X		    extern void do_open();
X		    fcntl(s, F_SETFL, FNDELAY);
X		    Chan[chan].state = CHAN_OPEN;
X		    Chan[chan].flags = CHANF_ROK|CHANF_WOK;
X		    Chan[chan].fd = s;
X		    Chan[chan].pri= cop->pri;
X		    FD_SET(s, &Fdread);
X		    FD_SET(s, &Fdexcept);
X		    FdChan[s] = chan;
X		    Fdstate[s] = do_open;
X		}
X		WriteStream(SCMD_ACKCMD, &ack, sizeof(CACKCMD), -1);
X	    }
X	}
X	break;
X    case SCMD_CLOSE:	/*  receive close   */
X	{
X	    extern void nop();
X	    register CCLOSE *clo = (CCLOSE *)ptr;
X	    uword chan = (clo->chanh<<8)|clo->chanl;
X	    int fd = Chan[chan].fd;
X
X	    if (DDebug)
X		fprintf(stderr, " SCMD_CLOSE\n");
X	    if (chan >= MAXCHAN || Chan[chan].state == CHAN_FREE)
X		break;
X	    /*
X	    Chan[chan].state = CHAN_CLOSE;
X	    Chan[chan].flags |= CHANF_RCLOSE;
X	    */
X	    Chan[chan].flags &= ~(CHANF_ROK|CHANF_WOK);
X	    FD_CLR(fd, &Fdread);
X	    FD_CLR(fd, &Fdexcept);
X	    Chan[chan].state = CHAN_FREE;
X	    Chan[chan].fd = -1;
X	    Fdstate[fd] = nop;
X	    close(fd);
X	    ClearChan(&TxList, chan, 0);
X
X	    if (Chan[chan].flags & CHANF_LCLOSE) {
X		if (DDebug)
X		    fprintf(stderr," REMOTE CLOSE %ld, LOCAL ALREADY CLOSED\n",
X			fd
X		    );
X	    } else {
X		CCLOSE cc;
X		char dummy;
X		cc.chanh = chan >> 8;
X		cc.chanl = chan;
X		WriteStream(SCMD_CLOSE, &cc, sizeof(CCLOSE), chan);
X		/*
X		shutdown(Chan[chan].fd, 2);
X		write(Chan[chan].fd, &dummy, 0);
X		*/
X		if (DDebug)
X		    fprintf(stderr," REMOTE CLOSE %ld, LOCAL NOT YET CLOSED\n",
X			fd
X		    );
X	    }
X	}
X	break;
X    case SCMD_ACKCMD:	/*  acknowledge of my open	*/
X	{
X	    register CACKCMD *cack = (CACKCMD *)ptr;
X	    uword chan = (cack->chanh<<8)|cack->chanl;
X	    if (chan >= MAXCHAN || Chan[chan].state != CHAN_LOPEN)
X		break;
X	    if (DDebug)
X		fprintf(stderr, "ackerr = %ld\n", cack->error);
X	    if (cack->error == 33) {
X		uword newchan = alloc_channel();
X		COPEN co;
X		if (newchan < MAXCHAN) {
X		    Chan[newchan] = Chan[chan];
X		    Chan[chan].state = CHAN_FREE;
X		    Chan[chan].fd = -1;
X		    co.chanh = newchan >> 8;
X		    co.chanl = newchan;
X		    co.porth = Chan[chan].port >> 8;
X		    co.portl = Chan[chan].port;
X		    co.error = 0;
X		    co.pri   = Chan[chan].pri;
X		    WriteStream(SCMD_OPEN, &co, sizeof(COPEN), newchan);
X		    break;
X		}
X	    }
X	    if (cack->error) {
X		extern void nop();
X		ubyte error = cack->error;
X		int fd = Chan[chan].fd;
X
X		gwrite(fd, &error, 1);
X		Fdstate[fd] = nop;
X		Chan[chan].fd = -1;
X		Chan[chan].state = CHAN_FREE;
X		FD_CLR(fd, &Fdread);
X		FD_CLR(fd, &Fdexcept);
X		close(fd);
X	    } else {
X		ubyte error = 0;
X		extern void do_open();
X		gwrite(Chan[chan].fd, &error, 1);
X		Chan[chan].state = CHAN_OPEN;
X		Chan[chan].flags = CHANF_ROK|CHANF_WOK;
X		Fdstate[Chan[chan].fd] = do_open;
X	    }
X	}
X	break;
X    case SCMD_EOFCMD:	/*  EOF on channel		*/
X	{
X	    register CEOFCMD *eof = (CEOFCMD *)ptr;
X	    uword chan = (eof->chanh<<8)|eof->chanl;
X
X	    if (chan < MAXCHAN && Chan[chan].state == CHAN_OPEN) {
X		Chan[chan].flags &= ~eof->flags;
X		if (eof->flags & CHANF_ROK) {
X		    char dummy;
X		    shutdown(Chan[chan].fd, 1);
X		    write(Chan[chan].fd, &dummy, 0);
X		}
X	    }
X	}
X	break;
X    case SCMD_QUIT:
X	dneterror("QUIT");
X	break;
X    case SCMD_IOCTL:
X	{
X	    register CIOCTL *cio = (CIOCTL *)ptr;
X	    uword chan = (cio->chanh<<8)|cio->chanl;
X
X	    if (chan < MAXCHAN && Chan[chan].state == CHAN_OPEN) {
X		switch(cio->cmd) {
X		case CIO_SETROWS:
X		    isetrows(Chan[chan].fd, (cio->valh<<8)|cio->vall);
X		    break;
X		case CIO_SETCOLS:
X		    isetcols(Chan[chan].fd, (cio->valh<<8)|cio->vall);
X		    break;
X		case CIO_STOP:
X		    break;
X		case CIO_START:
X		    break;
X		case CIO_FLUSH:
X		    ClearChan(&TxList, chan, 0);
X		    break;
X		}
X	    }
X	}
X	break;
X    default:
X	break;
X    }
X}
X
Xreplywindow(window)
X{
X    ubyte rwindow = (window - RPStart) & 7;
X    WCBuf[0] = SYNC;
X
X    if (DDebug) {
X	if (rwindow >= 4 || RPak[rwindow]->state == READY)
X	    fprintf(stderr, " ACK WINDOW %ld\n", window);
X	else
X	    fprintf(stderr, " NAK WINDOW %ld\n", window);
X    }
X    if (rwindow >= 4 || RPak[rwindow]->state == READY)  /* data ready */
X	WCBuf[1] = PKCMD_ACK | (window << 5);           /* ack it     */
X    else
X	WCBuf[1] = PKCMD_NAK | (window << 5);           /* nack it    */
X    WCBuf[2] = (SYNC << 1) ^ WCBuf[1];
X    NetWrite(WCBuf, 3, 0);
X}
X
SHAR_EOF
echo "extracting dnet/TODO3"
sed 's/^X//' << \SHAR_EOF > dnet/TODO3
XFrom tlm@newton.physics.purdue.edu Mon Mar 14 13:05:30 1988
XReceived: by cory (5.57/1.26)
X	id AA00614; Mon, 14 Mar 88 13:00:13 PST
XReceived: by newton.physics.purdue.edu (5.54/2.1)
X	id AA12775; Mon, 14 Mar 88 15:59:30 EST
XDate: Mon, 14 Mar 88 15:59:30 EST
XFrom: tlm@newton.physics.purdue.edu (Timothy Lee Meisenheimer)
XMessage-Id: <8803142059.AA12775@newton.physics.purdue.edu>
XApparently-To: dnet@cory.berkeley.edu
X
XThanks Matt, but no dice. I still couldn't get it to work in
Xlocal mode??? What I did do was get the latest version (time
Xstamp of March 11) and recompiled everything. The host name
Xproblem is now gone (newton.physics.purdure.edu= --> newton.physics.
Xpurdue.ed=) but I found that the environment DNETHOST must get
Xset in the child process when using "dtest" but not in the
Xparent process. Consequently it was still no go until I set
Xit by hand. Now I can do the local net just dandy!! I'm playing
Xaround with how to get the responce .vs. baud right (mucking
Xaround in control.c). Will let you know what I find out. Bye
Xthe way does this latest version still have problems with all
Xthose interupts on the select which was bringing the vaxen to
Xtheir knees? Guess I'll find out tonight :-).
X
X			tim.
X
X
SHAR_EOF
echo "extracting dnet/subs.c"
sed 's/^X//' << \SHAR_EOF > dnet/subs.c
X
X/*
X *  SUBS.C
X *
X *	DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved
X *
X *	Support subroutines
X *
X */
X
X#include "dnet.h"
X
X/*
X *   WRITESTREAM()
X *
X *	Queues new SCMD_?? level commands to be sent
X */
X
XWriteStream(sdcmd, buf, len, chan)
Xubyte *buf;
Xuword chan;
X{
X    register XIOR *ior = (XIOR *)malloc(sizeof(XIOR));
X
X    if (DDebug)
X	fprintf(stderr, "**SEND MPX CMD %ld (%ld bytes on channel %ld)\n",
X	    sdcmd, len, chan
X	);
X
X    ior->io_Data = (ubyte *)malloc(len);
X    ior->io_Length = len;
X    ior->io_Actual = 0;
X    ior->io_Command = sdcmd;
X    ior->io_Error = 0;
X    ior->io_Channel = chan;
X    ior->io_Pri = (chan > MAXCHAN) ? 126 : Chan[chan].pri;
X    bcopy(buf, ior->io_Data, len);
X    Enqueue(&TxList, ior);
X    /*
X     *	REMOVED 21 SEPT 1988
X     * do_wupdate();
X     */
X}
X
X/*
X *  ALLOC_CHANNEL()
X *
X *	Allocate a free channel.  Used in SCMD_OPEN and SCMD_ACKCMD
X */
X
Xalloc_channel()
X{
X    static ulong ran = 13;
X    register uword i;
X
X    ran = ((ran * 13) + 1) ^ (ran >> 9) + time(0);
X    for (i = ran % MAXCHAN; i < MAXCHAN; ++i) {
X	if (Chan[i].state == 0)
X	    return(i);
X    }
X    for (i = ran % MAXCHAN; i < MAXCHAN; --i) {
X	if (Chan[i].state == 0)
X	    return(i);
X    }
X    return(-1);
X}
X
X/*
X *    Remove all nodes with the given channel ID.
X */
X
XClearChan(list, chan, all)
XLIST *list;
Xuword chan;
X{
X    register XIOR *io, *in;
X
X    for (io = (XIOR *)list->lh_Head; io != (XIOR *)&list->lh_Tail; io = in) {
X	in = (XIOR *)io->io_Node.ln_Succ;
X	if (io->io_Channel == chan) {
X	    if (all || io->io_Command == SCMD_DATA) {
X		io->io_Node.ln_Succ->ln_Pred = io->io_Node.ln_Pred;
X		io->io_Node.ln_Pred->ln_Succ = io->io_Node.ln_Succ;
X		free(io->io_Data);
X		free(io);
X	    }
X	}
X    }
X}
X
X/*
X *  Queue a packet into a prioritized list.  FIFO is retained for packets
X *  of the same priority.  This implements one level of channel priorities,
X *  before the packets actually get queued to the network.  Since up to
X *  4 packets might be queued (200 * 4 = 800 bytes of data or 4 seconds @
X *  2400 baud), a second level of prioritization will also reduce the
X *  physical packet size when two channels at relatively large differing
X *  priorities are in use.
X *
X *	These and other list routines compatible with Amiga list routines.
X */
X
XEnqueue(list, ior)
XLIST *list;
XXIOR *ior;
X{
X    register XIOR *io;
X    char pri = ior->io_Pri;
X
X    io = (XIOR *)list->lh_Head;
X    while (io != (XIOR *)&list->lh_Tail) {
X	if (pri > io->io_Pri)
X	    break;
X	io = (XIOR *)io->io_Node.ln_Succ;
X    }
X    ior->io_Node.ln_Succ = (NODE *)io;
X    ior->io_Node.ln_Pred = io->io_Node.ln_Pred;
X    ior->io_Node.ln_Succ->ln_Pred = (NODE *)ior;
X    ior->io_Node.ln_Pred->ln_Succ = (NODE *)ior;
X}
X
XAddTail(list, node)
XLIST *list;
XNODE *node;
X{
X    node->ln_Succ = (NODE *)&list->lh_Tail;
X    node->ln_Pred = list->lh_TailPred;
X    node->ln_Succ->ln_Pred = node;
X    node->ln_Pred->ln_Succ = node;
X}
X
XAddHead(list, node)
XLIST *list;
XNODE *node;
X{
X    node->ln_Succ = list->lh_Head;
X    node->ln_Pred = (NODE *)list;
X    node->ln_Succ->ln_Pred = node;
X    node->ln_Pred->ln_Succ = node;
X}
X
Xubyte *
XRemHead(list)
XLIST *list;
X{
X    NODE *node;
X
X    node = list->lh_Head;
X    if (node->ln_Succ == NULL)
X	return(NULL);
X    node->ln_Succ->ln_Pred = node->ln_Pred;
X    node->ln_Pred->ln_Succ = node->ln_Succ;
X    return((ubyte *)node);
X}
X
XNewList(list)
XLIST *list;
X{
X    list->lh_Head = (NODE *)&list->lh_Tail;
X    list->lh_Tail = NULL;
X    list->lh_TailPred = (NODE *)&list->lh_Head;
X}
X
XGetNext(node)
XNODE *node;
X{
X    register NODE *next = node->ln_Succ;
X    if (*(long *)next)
X	return((long)next);
X    return(NULL);
X}
X
X/*
X *  CHKBUF
X *
X *	Checksum a buffer.  Uses a simple, but supposedly very good
X *	scheme.
X */
X
Xchkbuf(buf, bytes)
Xregister ubyte *buf;
Xregister uword bytes;
X{
X    register uword i;
X    register ubyte c1,c2;
X
X    for (i = c1 = c2 = 0; i < bytes; ++i) {
X	c1 += buf[i];
X	c2 += c1;
X    }
X    c1 = -(c1 + c2);
X    return((c1<<8)|c2);
X}
X
X/*
X *   Write timeout signal handler.
X */
X
Xsigwto()
X{
X    WTimedout = 1;
X    Wto_act = 0;
X}
X
XTimerOpen()
X{
X    static struct sigvec SA = { sigwto, 0, 0 };
X    sigvec(SIGALRM, &SA, NULL);
X}
X
XTimerClose()
X{
X    signal(SIGALRM, SIG_IGN);
X}
X
XWTimeout(us)
X{
X    static struct itimerval itv;
X    struct itimerval ov;
X    long mask;
X
X    itv.it_value.tv_sec = us / 1000000;
X    itv.it_value.tv_usec= (us % 1000000);
X
X    mask = sigblock(sigmask(SIGALRM));
X    setitimer(ITIMER_REAL, &itv, &ov);
X    Wto_act = 1;
X    WTimedout = 0;
X    sigsetmask(mask);
X    if (DDebug)
X	fprintf(stderr, "WTimeout set\n");
X}
X
Xdneterror(str)
Xchar *str;
X{
X    register short i;
X
X    NetClose();
X    TimerClose();
X    exit(1);
X}
X
X/*
X *    setenv(name, str).  name must be of the form "NAME="
X */
X
Xsetenv(name, str)
Xchar *name;
Xchar *str;
X{
X    extern char **environ;
X    static char **elist;
X    static int elen;
X    char *ptr;
X    int i, len;
X
X    len = strlen(name);
X    if (elist == NULL) {
X	for (i = 0; environ[i]; ++i);
X	elist = (char **)malloc((i+3)*sizeof(char *));
X	elen = i + 3;
X	bcopy(environ, elist, i*sizeof(char *));
X	environ = elist;
X    }
X    for (i = 0; elist[i]; ++i) {
X	if (strncmp(elist[i], name, len) == 0)
X	    break;
X    }
X    if (i == elen) {
X	elen += 4;
X	elist = environ = (char **)realloc(elist, elen*sizeof(char *));
X    }
X    ptr = (char *)malloc(len + strlen(str) + 1);
X    sprintf(ptr, "%s%s", name, str);
X    if (elist[i]) {
X	elist[i] = ptr;
X    } else {
X	elist[i] = ptr;
X	elist[i+1] = NULL;
X	elen = i + 1;
X    }
X}
X
Xvoid
Xstartserver(port)
Xuword port;
X{
X    char dir[MAXPATHLEN];
X    struct passwd pw_info;
X    FILE *fi;
X
X    if (!port)
X	return;
X    if (getenv("DNETDIR")) {
X	strcpy(dir, getenv("DNETDIR"));
X	strcat(dir, "dnet.servers");
X	if (fi = fopen(dir, "r")) {
X	    if (scan_for_server(fi, port))
X		return;
X	}
X    }
X    pw_info = *getpwuid(getuid());
X    strcpy(dir, pw_info.pw_dir);
X    strcat(dir, "/.dnet/dnet.servers");
X    if (fi = fopen(dir, "r")) {
X	if (scan_for_server(fi, port))
X	    return;
X    }
X    /*
X     *	LAST TRY
X     */
X    if (fi = fopen(LASTTRYDNETSERVERS, "r")) {
X	if (scan_for_server(fi, port))
X	    return;
X    }
X    fprintf(stderr, "Unable to find one of (1) dnet.servers or (2) server\n");
X    fprintf(stderr, "entry for port %d\n", port);
X    fflush(stderr);
X    return;
X}
X
Xscan_for_server(fi, port)
XFILE *fi;
Xshort port;
X{
X    char buf[256];
X    char path[MAXPATHLEN];
X    char cdir[MAXPATHLEN];
X    long portno;
X    short found = 0;
X    void checktilda();
X
X    while (fgets(buf, 256, fi)) {
X	if (sscanf(buf, "%ld %s %s", &portno, path, cdir) == 3) {
X	    checktilda(path);
X	    checktilda(cdir);
X	    if (portno == port) {
X		if (!fork()) {
X		    int i;
X		    fclose(fi);
X		    setuid(getuid());
X		    signal(SIGHUP, SIG_DFL);
X		    signal(SIGINT, SIG_DFL);
X		    signal(SIGQUIT, SIG_DFL);
X		    signal(SIGTERM, SIG_DFL);
X		    signal(SIGCHLD, SIG_DFL);
X		    signal(SIGTSTP, SIG_IGN);
X		    ioctl(open("/dev/tty", 2), TIOCNOTTY, NULL);
X		    i = open("/dev/null", O_RDWR, 0);
X		    dup2(i, 0);
X		    dup2(i, 1);
X		    for (i = 3; i < 256; ++i)
X			close(i);
X		    sprintf(buf, "server.%ld.%ld", port, getuid());
X		    execl(path, buf, cdir, NULL);
X		    fprintf(stderr, "Unable to exec server: %s\n", path);
X    		    fflush(stderr);
X		    _exit(1);
X		}
X		sleep(4);    /* is a hack */
X		found = 1;
X		break;
X	    }
X	}
X    }
X    fclose(fi);
X    return(found);
X}
X
Xvoid
Xchecktilda(buf)
Xchar *buf;
X{
X    if (buf[0] == '~') {
X	short bindex = 1;
X	short pathlen;
X	struct passwd pw_info, *pw;
X
X	pw_info.pw_dir = getenv("HOME");
X	if (buf[1] && buf[1] != '/') {
X	    char username[128];
X	    while (buf[bindex] && buf[bindex] != '/')
X		++bindex;
X	    bcopy(buf+1, username, bindex-1);
X	    username[bindex-1] = 0;
X	    if (pw = getpwnam(username)) {
X		pw_info = *pw;
X	    } else {
X		fprintf(stderr, "Unable to find password entry for %s\n",
X		    username
X		);
X		fprintf(stderr, "passing /tmp as dir for server");
X    		fflush(stderr);
X		pw_info.pw_dir = "/tmp";
X	    }
X	}
X
X	/*
X	 * ~[username]<rest of path>	 ->   <basedir><rest of path>
X	 */
X
X	pathlen = strlen(pw_info.pw_dir);
X	bcopy(buf + bindex, buf + pathlen, strlen(buf + bindex) + 1);
X	bcopy(pw_info.pw_dir, buf, pathlen);
X    }
X    fflush(stderr);
X}
X
SHAR_EOF
echo "End of archive 1 (of 2)"
# if you want to concatenate archives, remove anything after this line
exit