housel@en.ecn.purdue.edu (Peter S. Housel) (07/03/89)
echo 'x - dial.h' sed 's/^X//' <<'**-dial.h-EOF-**' >dial.h Xtypedef struct { X void *attr; /* pointer to termio attribute struct */ X int baud; /* transmission data rate */ X int speed; /* 212A modem: low=300, high=1200 */ X char *line; /* device name for out-going line */ X char *telno;/* pointer to tel-no digits string */ X int modem; /* specify modem control for direct lines */ X} CALL; X X#define INTRPT -1 /* interrupt occurred */ X#define D_HUNG -2 /* dialer hung (no return from write) */ X#define NO_ANS -3 /* no answer within 10 seconds */ X#define ILL_BD -4 /* illegal baud-rate */ X#define A_PROB -5 /* acu problem (open() failure) */ X#define L_PROB -6 /* line problem (open() failure) */ X#define NO_Ldv -7 /* can't open LDEVS file */ X#define DV_NT_A -8 /* requested device not available */ X#define DV_NT_K -9 /* requested device not known */ X#define NO_BD_A -10 /* no device available at requested baud */ X#define NO_BD_K -11 /* no device known at requested baud */ X X#define LDEVS "/usr/lib/uucp/L-devices" X#define DVC_LEN 64 X#define DEVDIR "/dev/" X#define LOCK "/usr/spool/uucp/LCK.." X X/* minix */ X#define O_RDWR 2 X#ifdef BITS8 X#define ANYP BITS8 X#endif **-dial.h-EOF-** echo 'x - dialvars.c' sed 's/^X//' <<'**-dialvars.c-EOF-**' >dialvars.c Xchar AS, /* True if numbers dialed in ASCII, False for binary digits */ X DI, /* True if modem can dial numbers, False otherwise */ X HC, /* True if modem hangs up when DTR drops, False otherwise */ X TT; /* True if modem uses touchtone by default, False for pulse */ X Xchar *AT, /* Enter command state when online */ X *CS, /* Command start string */ X *CE, /* Command end string - must be present if CS is */ X *DS, /* Dial command string */ X *DE, /* End of dial command string - must be present if DS is */ X *CO, /* Connection made at primary baud rate */ X *CL, /* Connection made at secondary (lower) baud rate */ X *IS, /* Initialization string - reset modem to onhook and ready */ X *HU; /* Hangup command */ X Xint AD, /* Delay after AT string before next command */ X BD, /* Highest communications baud rate */ X BL, /* Another, lower baud rate */ X ID; /* Delay time after initialization */ **-dialvars.c-EOF-** echo 'x - hangup.c' sed 's/^X//' <<'**-hangup.c-EOF-**' >hangup.c X#include "modemcap.h" X#ifdef UNIX_S5 X#include <termio.h> X#endif X#ifdef UNIX_V7 X#include <sgtty.h> X#endif X Xextern int merrno; X Xhangup (fd) Xint fd; X{ X#ifdef UNIX_S5 X struct termio termio; X struct termio hupcl; X#endif X#ifdef UNIX_V7 X struct sgttyb termio; X struct sgttyb hupcl; X#endif X if (HU == (char *) 0 && HC == 0) { X undial (fd); X return (0); X } X if (AT != (char *) 0) { X write (fd, AT, strlen (AT)); X if (AD) X sleep (AD); X } X if (HU) { X if (CS) X write (fd, CS, strlen (CS)); X write (fd, HU, strlen (HU)); X if (CE) X write (fd, CE, strlen (CE)); X X if (IS) { X write (fd, IS, strlen (IS)); X if (ID) X sleep (ID); X } X undial (fd); X return (1); X } X#ifdef UNIX_S5 X ioctl (fd, TCGETA, &termio); X ioctl (fd, TCGETA, &hupcl); X X hupcl.c_cflag &= ~CBAUD; X hupcl.c_cflag |= HUPCL; X X ioctl (fd, TCSETA, &hupcl); X sleep (2); X ioctl (fd, TCSETA, &termio); X#endif X#ifdef UNIX_V7 X#define B0 0 X gtty (fd, &termio); X gtty (fd, &hupcl); X X hupcl.sg_ispeed = B0; X hupcl.sg_ospeed = B0; X stty (fd, &hupcl); X sleep (2); X stty (fd, &termio); X#endif X undial (fd); X return (1); X} **-hangup.c-EOF-** echo 'x - initmodem.c' sed 's/^X//' <<'**-initmodem.c-EOF-**' >initmodem.c X#include "modemcap.h" X Xstatic char f_names[] = "asditthc"; Xstatic char *f_caps[] = { X &AS, &DI, &TT, &HC X}; X Xstatic char i_names[] = "bdblidad"; Xstatic int *i_caps[] = { X &BD, &BL, &ID, &AD X}; X Xstatic char c_names[] = "cscedsdeiscoclathu"; Xstatic char **c_caps[] = { X &CS, &CE, &DS, &DE, &IS, &CO, &CL, &AT, &HU X}; X Xinitmodem (modem, fd) Xchar *modem; /* name of modem */ Xint fd; /* channel to modem */ X{ X static char mcapbuf[1024]; X static char area[1024]; X char *ap = area; X register char *cp; X register int i, j; X register char *s; X char *mgetstr (); X X if (mgetent (mcapbuf, modem) != 1) X return (0); X X for (i = 0, cp = f_names;*cp;i++, cp += 2) X *(f_caps[i]) = mgetflag (cp); X X for (i = 0, cp = i_names;*cp;i++, cp += 2) { X j = mgetnum (cp); X if (j >= 0) X *(i_caps[i]) = j; X else X *(i_caps[i]) = 0; X } X for (i = 0, cp = c_names;*cp;i++, cp += 2) X *(c_caps[i]) = mgetstr (cp, &ap); X X if (IS != (char *) 0) { X write (fd, IS, strlen (IS)); X if (ID) X sleep (ID); X } X return (1); X} **-initmodem.c-EOF-** echo 'x - makefile.unix' sed 's/^X//' <<'**-makefile.unix-EOF-**' >makefile.unix X# Your library directory. XLIBDIR=/usr/lib X# Your local command directory. XLBIN=/usr/local/bin XOSFLAG=-DUNIX_S5 # For System V machines. X# OSFLAG=-DUNIX_V7 # For Version 7 machines. X# For those poor people who need ranlib X# RANLIB=ranlib $(LIBDIR)/libmodemcap.a X# Standard Bourne shell. XSHELL=/bin/sh X XCFLAGS=-O XLDFLAGS=-s X XOFILES=mgetent.o mgetstr.o mgetflag.o mgetnum.o mdial.o merror.o \ X initmodem.o hangup.o dial.o X XCFILES=mgetent.c mgetstr.c mgetflag.c mgetnum.c mdial.c merror.c \ X initmodem.c hangup.c dial.c X XLFILES=libmodemcap.a(mgetent.o)\ X libmodemcap.a(mgetstr.o)\ X libmodemcap.a(mgetflag.o)\ X libmodemcap.a(mgetnum.o)\ X libmodemcap.a(mdial.o)\ X libmodemcap.a(merror.o)\ X libmodemcap.a(initmodem.o)\ X libmodemcap.a(hangup.o)\ X libmodemcap.a(dial.o) X Xall: $(LFILES) X Xinstall: all call X cp modemcap.h /usr/include X cp modemcap /etc/modemcap X cp modemtype /etc/modemtype X cp libmodemcap.a $(LIBDIR) X chmod 644 /usr/include/modemcap.h /etc/modemcap /etc/modemtype $(LIBDIR)/libmodemcap.a X cp call $(LBIN)/call X chmod 711 $(LBIN)/call X Xcall: call.c libmodemcap.a X cc $(LDFLAGS) $(CFLAGS) call.c libmodemcap.a -o call X Xinitmodem.o: initmodem.c /usr/include/modemcap.h X Xmdial.o: mdial.c /usr/include/modemcap.h X Xhangup.o: hangup.c /usr/include/modemcap.h X Xdial.o: dial.c /usr/include/modemcap.h X Xshar: README $(CFILES) makefile modemcap modemtype modemcap.h call.c X shar README makefile modemcap modemtype modemcap.h $(CFILES) call.c > modem.shar **-makefile.unix-EOF-** echo 'x - mdial.c' sed 's/^X//' <<'**-mdial.c-EOF-**' >mdial.c X#include "modemcap.h" X#include <setjmp.h> X#include <signal.h> X#include <dial.h> X Xstatic jmp_buf env; /* long jump buffer if timeout in read */ Xextern int merrno; X Xstatic timeout () X{ X longjmp (env, 1); X} X Xmdial (telno, fd) Xchar *telno; Xint fd; X{ X char buf[64]; /* telephone buffer if AS is false */ X char command[80]; /* dial command buffer */ X int i, j; /* index and length of telephone number */ X char c; /* single character for connection verification */ X X if (! DI) X return (merrno = A_PROB); /* can't dial phone anyhow */ X X if (! AS) /* never used any other kind of modem */ X return (merrno = A_PROB); X else /* normal ascii character phone numbers */ X strcpy (buf, telno); X X sprintf (command, "%s%s%s%s%s", CS, DS, buf, DE, CE); X if (setjmp (env) != 0) { X signal (SIGALRM, SIG_DFL); X return (merrno = D_HUNG); X } X signal (SIGALRM, timeout); X alarm (30); X write (fd, command, strlen (command)); X X if (CO) { /* verify connection */ X if (setjmp (env) != 0) { X signal (SIGALRM, SIG_DFL); X return (merrno = A_PROB); X } X signal (SIGALRM, timeout); X if (TT) X alarm (60); X else X alarm (60 + strlen (telno) / 2); X X for (i = 0;CO[i] != 0;) { X if (read (fd, &c, 1) != 1) X continue; X X if (CO[i] == c) X i++; X else X i = 0; X } X return (0); X } X return (0); X} **-mdial.c-EOF-** echo 'x - merror.c' sed 's/^X//' <<'**-merror.c-EOF-**' >merror.c X#include <stdio.h> X Xint merrno; X Xchar *_merr_list[] = { X "No error", X "Interrupt occurred", X "Dialer Hung", X "No answer", X "Illegal baud rate", X "ACU Problem", X "Line Problem", X "Can't open LDEVS file", X "Requested device not available", X "Requested device not known", X "No device available at requested baud", X "No device known at requested baud" X}; X Xint _msys_nerr = (sizeof (_merr_list) / sizeof (char *)); X Xmerror (s) Xchar *s; X{ X int i = - merrno; X X if (0 <= i && i < _msys_nerr) X fprintf (stderr, "%s: %s\n", s, _merr_list[i]); X else X fprintf (stderr, "%s: Error %d\n", s, merrno); X} **-merror.c-EOF-** echo 'x - mgetent.c' sed 's/^X//' <<'**-mgetent.c-EOF-**' >mgetent.c X#include <stdio.h> X Xchar *__modemcap; Xchar *MODEMCAP = "/usr/etc/modemcap"; X Xstatic isent (ent, name) Xchar *ent; Xchar *name; X{ X char buf[16]; X register int i; X X while (*ent != ':' && *ent != 0) { X for (i = 0;*ent != ':' && *ent != '|' && *ent != 0 && i < 15;i++) X buf[i] = *ent++; X X if (*ent == '|') X ent++; X X buf[i] = 0; X if (strcmp (buf, name) == 0) X return (1); X } X return (0); X} X Xmgetent (bp, name) Xchar *bp; Xchar *name; X{ X char buf[1024]; X register char *cp; X register FILE *modemcap; X register int i; X char *getenv (); X X if ((cp = getenv ("MODEMCAP")) != NULL) { X if (*cp != '/') { X if (isent (cp, name)) { X strcpy (buf, cp); X return (1); X } X } X MODEMCAP = cp; X } X if ((modemcap = fopen (MODEMCAP, "r")) == NULL) X return (-1); X X while (fgets (buf, 512, modemcap) != NULL) { X if (buf[0] == '#') /* skip all comment lines */ X continue; X X i = strlen (buf) - 1; /* find last character in line */ X buf[i] = 0; /* remove trailing newline */ X if (i == 0) /* ignore blank lines */ X continue; X X while (buf[(i = strlen (buf) - 1)] == '\\') { /* is last character a \\, still more */ X cp = &buf[i]; /* find last character */ X cp[0] = 0; /* nullify, end of this part */ X if (fgets (cp, 512, modemcap) == NULL) /* end of file? ... */ X break; /* ... end of entry */ X X cp[strlen (cp) - 1] = 0; /* remove trailing newline */ X if (cp[0] == '#') { /* comment line? ... */ X cp[0] = 0; /* remove that line */ X continue; /* go get another line */ X } X } X if (isent (buf, name)) { X __modemcap = bp; X strcpy (bp, buf); X fclose (modemcap); X return (1); X } X } X fclose (modemcap); X return (0); X} **-mgetent.c-EOF-** echo 'x - mgetflag.c' sed 's/^X//' <<'**-mgetflag.c-EOF-**' >mgetflag.c Xextern char *__modemcap; X Xmgetflag (id) Xregister char *id; X{ X register char *cp = __modemcap; X X if (__modemcap == (char *) 0) /* has mgetent() been called? ... */ X return (-1); /* ... no, can't find number */ X X while (*cp != ':' && *cp != 0) /* find first entry in cap */ X cp++; X X if (*cp == 0) /* empty entry??? */ X return (0); /* ... yes, bad modemcap entry */ X else X cp++; /* point to first character in next */ X X while (*cp != 0) { /* until entry found or end of entry */ X if (cp[0] == id[0] && cp[1] == id[1]) /* found entry!!! */ X return (1); /* return true */ X else { /* not entry, skip this entire entry */ X while (*cp != ':' && *cp != 0) X cp++; /* search for end of current entry */ X X if (*cp != 0) X cp++; /* skip terminating character */ X } X } X return (0); X} **-mgetflag.c-EOF-** echo 'x - mgetnum.c' sed 's/^X//' <<'**-mgetnum.c-EOF-**' >mgetnum.c Xextern char *__modemcap; X Xmgetnum (id) Xregister char *id; X{ X register char *cp = __modemcap; X X if (__modemcap == (char *) 0) /* has mgetent() been called? ... */ X return (-1); /* ... no, can't find number */ X X while (*cp != ':' && *cp != 0) /* find first entry in cap */ X cp++; X X if (*cp == 0) /* empty entry??? */ X return (-1); /* ... yes, bad modemcap entry */ X else X cp++; /* point to first character in next */ X X while (*cp != 0) { /* until entry found or end of entry */ X if (cp[0] == id[0] && cp[1] == id[1]) { /* found entry!!! */ X if (cp[2] != '#') /* is it a numeric value??? */ X return (-1); /* no, something else */ X X return (atoi (&cp[3])); /* return value (just after #) */ X } else { /* not entry, skip this entire entry */ X while (*cp != ':' && *cp != 0) X cp++; /* search for end of current entry */ X X if (*cp != 0) X cp++; /* skip terminating character */ X } X } X return (-1); X} **-mgetnum.c-EOF-** echo 'x - mgetstr.c' sed 's/^X//' <<'**-mgetstr.c-EOF-**' >mgetstr.c Xextern char *__modemcap; X Xchar *mgetstr (id, area) Xregister char *id; Xregister char **area; X{ X register char *cp = __modemcap; X register char *str = *area; /* start of current string */ X X if (__modemcap == (char *) 0) /* has mgetent() been called? ... */ X return ((char *) 0); /* ... no, can't find string */ X X while (*cp != ':' && *cp != 0) /* find first entry in cap */ X cp++; X X if (*cp == 0) /* empty entry??? */ X return ((char *) 0); /* ... yes, bad modemcap entry */ X else X cp++; /* point to first character in next */ X X while (*cp != 0) { /* until entry found or end of entry */ X if (cp[0] == id[0] && cp[1] == id[1]) { /* found entry!!! */ X if (cp[2] != '=') /* is it a string value??? */ X return ((char *) 0); /* no, something else */ X else X break; /* yes, entry was found */ X } else { /* not entry, skip this entire entry */ X while (*cp != ':' && *cp != 0) X cp++; /* search for end of current entry */ X X if (*cp != 0) X cp++; /* skip terminating character */ X } X } X if (*cp == 0) /* end of modem cap entry */ X return ((char *) 0); X X cp += 3; /* point to actual string */ X while (*cp != ':' && *cp != 0) { /* for every character in string ... */ X if (*cp == '\\') { /* translate escaped character */ X cp++; X switch (*cp) { X case 'n': /* newline */ X **area = '\n'; X (*area)++; X cp++; X break; X case 'r': /* carriage return */ X **area = '\r'; X (*area)++; X cp++; X break; X case 'b': /* backspace */ X **area = '\b'; X (*area)++; X cp++; X break; X case 'f': /* form feed */ X **area = '\f'; X (*area)++; X cp++; X break; X case 't': /* tab */ X **area = '\t'; X (*area)++; X cp++; X break; X case 'E': /* Escape character */ X **area = 033; X (*area)++; X cp++; X break; X case '0': X case '1': X case '2': X case '3': X case '4': X case '5': X case '6': X case '7': X **area = ((cp[0] - '0') << 6) + X ((cp[1] - '0') << 3) + X (cp[2] - '0'); X (*area)++; X cp += 3; X break; X default: X **area = *cp++; X (*area)++; X break; X } X } else if (*cp == '^') { /* some control character */ X cp++; X if (*cp >= '@' && *cp <= '_') { X **area = *cp - '@'; X (*area)++; X } X cp++; X } else { /* some normal character */ X **area = *cp++; /* put character in area */ X (*area)++; X } X } X *((*area)++) = 0; /* null terminate area and string */ X return (str); /* return pointer to start of string */ X} **-mgetstr.c-EOF-** echo 'x - modemcap' sed 's/^X//' <<'**-modemcap-EOF-**' >modemcap X# X# @(#)modemcap 1.0 X# X# First attempt at a modem capabilities database. X# X# Capabilities are: X# X# Name Type Meaning X# X# as flag Numbers are in ASCII, not binary X# at string Attention string, forces model into command mode X# from online mode X# ad number Delay after AS X# bd number Highest online baud rate X# bl number Alternate lower baud rate X# cs string Command start string X# ce string Command end string (required if CS is present) X# co string String from modem on remote connection at BD baud rate X# cl string String from modem on remote connection at BL baud rate X# di flag Modem has a dialer X# ds string Start dial command string X# de string End dial command string (required if DS is present) X# is string Initialization string, resets modem to offline, X# ready to dial X# id number Delay after IS X# hc flag Modem hangs up when DTR drops X# hu string Hangup command X# tt flag Modem dials touchtone by default (or DS is set X# that way) X# X# All commands, such as DS (dial command) and HU (hang up) will be X# prefixed by CS and ended with CE. If there is a common prefix X# and suffix, use this feature. Otherwise, each command will have X# to have the entire string built in. X# Xhy|hayes|Hayes Smartmodem 1200:\ X :as:at=+++:ad#6:bd#1200:bl#300:cs=AT:ce=\r:co=CONNECT:\ X :cl=CONNECT:di:ds=DT :de=:is=ATQ0 V1 E1\r:id#2:\ X :hc:hu=H0 V0 E0 Q1:tt: Xpr|promodem|Prometheus Promodem 1200:\ X :as:at=+++:ad#6:bd#1200:bl#300:cs=AT:ce=\r:co=CONNECT:\ X :cl=CONNECT:di:ds=DT:de=:is=ATQ0V1E1\r:id#2:\ X :hu=H0V0E0Q1:tt: Xsi|mk12|Signalman Mark XII:\ X :as:at=+++:ad#6:bd#1200:bl#300:cs=AT:ce=\r:co=CONNECT 1200:\ X :cl=CONNECT:di:ds=DT :de=:is=ATQ0 V1 E1\r:id#2:\ X :hu=H0 V0 E0 Q1:tt: Xds|dc300|Radio Shack Direct-Connect 300 Modem:\ X :bd#300:bl#110: **-modemcap-EOF-** cd .. echo 'x - newgpkt.c' sed 's/^X//' <<'**-newgpkt.c-EOF-**' >newgpkt.c X/* file: newgpkt.c X * author: Peter S. Housel X * X * The |cksum()| routine is taken from UUPC, Copyright 1985, 1986, 1987 by X * Richard H. Lamb, with (possible) changes Copyright 1987 by Stuart Lynne X * X * All other code is Copyright 1989 by Peter S. Housel. X * Redistribution for any purpose is permitted provided this message X * is included intact. No warranty of any sort is provided. X * X * newgpkt version 1.0 1/5/89 X */ X X/* This program was written based on the original UUPC 'g' driver, X * John Gilmore's version of uuslave, and Greg Chesson's protocol X * description article. The last was especially helpful. X * X * This code was written around a severely hacked version of UUPC. X * The call interface is almost identical to the original, but X * the internal implementation is quite different. Also, many X * functions are called that were not available in the original X * UUPC support functions. It should serve as an adequate framework. X * X * The framing strategy requires that a |read()| be interruptable X * by a |SIGALRM|. No "busy wait" or nonblocking read is required. X */ X X#include "dcp.h" X X#define MAXPKT 64 /* incredibly conservative... actually 4096 */ X#define SWINDOW 3 /* initial send window size */ X#define RWINDOW 3 /* window size we want to recieve */ X#define SPKTSIZE 64 /* initial send packet size */ X#define RPKTSIZE 64 /* window size we want to recieve */ X X#define MAXLOST 5 /* max lost packets (closes or such) */ X#define TIMEOUT 5 /* max seconds of before timeout */ X X#define LOSTPKT -1 /* packet lost, got timeout */ X#define BADPKT -2 /* bad checksum, or data read timed out */ X X#define ENV_DLE 0 /* framing char at start of envelope */ X#define ENV_K 1 /* packet length specifier */ X#define ENV_C0 2 /* low-order checksum */ X#define ENV_C1 3 /* high-order checksum */ X#define ENV_C 4 /* control byte */ X#define ENV_X 5 /* xor check byte */ X#define ENV_LEN 6 /* overall envelope length */ X X#define TT_CTRL 0 /* control packet */ X#define TT_DATA 2 /* data packet */ X#define TT_SDATA 3 /* short data packet */ X#define TT_ALTCHAN 1 /* 'alternate channel' - invalid */ X X#define X_CLOSE 1 /* close down protocol */ X#define X_RJ 2 /* reject recieved packet */ X#define X_SRJ 3 /* selectively reject packet - invalid */ X#define X_RR 4 /* reciever ready */ X#define X_INITC 5 /* third init packet */ X#define X_INITB 6 /* second init packet */ X#define X_INITA 7 /* first init packet */ X X#define OP_OPEN 1 /* request to open/init protocol */ X#define OP_CLOSE 2 /* request to close protocol */ X#define OP_WRITE 3 /* request to send packet */ X#define OP_READ 4 /* request to read packet */ X X#define MAGIC (unsigned) 0xAAAA /* checksum magic value */ X X/* from original dcp - determinie if a <= b < c, for mod 8 seq numbers */ X#define between(a,b,c) (((a)<=(b) && (b)<(c)) \ X || ((c)<(a) && (a)<=(b)) \ X || ((b)<(c) && (c)<(a))) X Xunsigned cksum(/* unsigned char *data, int len */); Xextern char *visib(/* unsigned char *data, int len */); X Xstruct { X short sdata; /* 'is this a short data packet' flag */ X unsigned length; /* length of this packet */ X unsigned char *bufloc; /* location of this data pkt's buffer */ X } X inpbufs[8], outbufs[8]; /* input/output queues */ X Xstatic int needack; /* do we need to acknowledge a rcv'd pkt? */ Xstatic int neednack; /* do we need to reject a recieved pkt? */ Xstatic int recv; /* seq. number of last correctly rcv'd pkt */ Xstatic int lastread; /* seq. number of last pkt ret. to caller */ Xstatic int send; /* first packet in output window */ Xstatic int next; /* next pkt to send send <= next < nstuff */ Xstatic int nstuff; /* next loc. to stuff a pkt in output queue */ X /* (last pkt in output window) + 1 */ Xstatic int initpk; /* current init sequence send packet */ Xstatic int skipping; /* skipping out-of-seq packets after RJ */ Xstatic int spktsize; /* send data size (requested by other end) */ Xstatic int swindow; /* send output window size (ditto) */ Xstatic int nlost; /* number of consecutive timeouts */ Xstatic int chanopen = 0; /* 'channel open' flag */ X Xstatic unsigned char buf[ENV_LEN + RPKTSIZE]; /* packet framing buffer */ Xstatic unsigned char *low, *high; /* framing buffer limits */ X Xstatic unsigned char *ibufset, *obufset; /* i/o packet buffer sets */ X X/* |gopenpk()| opens the 'g' packet protocol on the serial line. It X * initializes its state variables, allocates buffers, etc., then calls X * |gmachine()| to do the actual protocol negotiation/initialization. X */ Xgopenpk() X{ X int i; /* index */ X unsigned char *p, *q; /* pointers into buffer set */ X X high = low = buf; /* empty input buffer */ X needack = neednack = 0; /* don't need to accept or reject anything */ X initpk = X_INITA; /* send INITA during initialization */ X recv = lastread = 0; /* initial empty read queue, seq=0 */ X send = next = nstuff = 1; /* first in output queue, seq=1 */ X skipping = nlost = 0; /* nothing lost yet, not skipping */ X X if(gmachine(OP_OPEN) < 0) /* do the open */ X return -1; X X /* allocate send and recieve buffers */ X if(NULL == (p = ibufset = (unsigned char *)malloc(8 * RPKTSIZE))) X return -1; X if(NULL == (q = obufset = (unsigned char *)malloc(8 * spktsize))) X return -1; X X for(i = 0; i < 8; ++i) X { X inpbufs[i].bufloc = p; X p += RPKTSIZE; X outbufs[i].bufloc = q; X q += spktsize; X } X X pktsize = spktsize; /* for dcp compatibility */ X X return 0; X} X X/* |gclosepk()| closes down the packet protocol using the |OP_CLOSE| operation X * of |gmachine()|. X */ Xgclosepk() X{ X return gmachine(OP_CLOSE); X} X X/* |ggetpkt()| reads one packet and returns it. The data is stored in X * the buffer pointed to by |cdata|, and the length is stored in |*lenp|. X * It calls |gmachine()| to get the data, and copies it from the proper input X * buffer to the user's buffer area. "Short data" packets are handled here, X * as opposed to within |gmachine()|. X */ Xint ggetpkt(cdata, lenp) Xunsigned char *cdata; int *lenp; X{ X int nextread; X unsigned char *bufp; X X if(!chanopen) X return -1; X X nextread = (lastread + 1) & 7; X printmsg(M_HIGHPROTO, "waiting for input pkt seq=%d", nextread); X X if(gmachine(OP_READ) < 0) X return -1; X X *lenp = inpbufs[nextread].length; X bufp = inpbufs[nextread].bufloc; X if(inpbufs[nextread].sdata) X { X if(*bufp < 128) /* less than 128 bytes shorter than packet length */ X *lenp -= *bufp++; X else /* more than 128 bytes shorter */ X { X *lenp -= (*bufp++ & 127) * 256; X *lenp -= *bufp++; X } X } X memcpy(cdata, bufp, *lenp); X lastread = nextread; X return 0; X} X X/* |gsendpkt()| queues the packet pointed to by |cdata| of length |len| X * into the packet driver output buffer, and calls |gmachine()| to send X * it. (|gmachine()| will return when the packet has been transmitted but X * not necessarily acknowledged, with window size greater than 1.) If X * |flag| is nonzero, |cdata| is considered a null-terminated string X * which will be null-padded to the packet size and transmitted. X */ Xint gsendpkt(cdata, len, flag) Xunsigned char *cdata; int len, flag; X{ X unsigned char *destp; X unsigned diff; X X if(!chanopen) X return -1; X X destp = outbufs[nstuff].bufloc; X if(flag && len < spktsize) X { X printmsg(M_HIGHPROTO, "Padded message packet |%s|", cdata); X strncpy(destp, cdata, spktsize); X } X else X { X if((diff = spktsize - len) > 127) /* really short packet? */ X { X *destp++ = (diff >> 8) | 128; X *destp++ = diff & 255; X outbufs[nstuff].sdata = 1; X } X else if(diff > 0) /* short packet */ X { X *destp++ = diff; X outbufs[nstuff].sdata = 1; X } X else X outbufs[nstuff].sdata = 0; X memcpy(destp, cdata, len); /* copy into buffer */ X } X printmsg(M_HIGHPROTO, "queued data packet seq=%d len=%d", nstuff, len); X outbufs[nstuff].length = spktsize; X nstuff = (nstuff + 1) & 7; X X return gmachine(OP_WRITE); X} X X/* |gmachine()| is the heart of the 'g' packet driver. Its basic strategy X * is: X * - transmit a packet if necessary X * - return if possible, X * - else read a packet and act on it X * - repeat X * X * |OP_OPEN| requests that the channel be opened, and |OP_CLOSE| requests that X * it be closed. If |why| is |OP_WRITE|, |gmachine()| will return when the X * last packet in the output queue has been transmitted (but not necessarily X * acknowledged). |OP_READ| requests will return as soon as a new packet X * arrives. X */ Xint gmachine(why) Xint why; X{ X int xxx, yyy, len; X unsigned char *bufp; X int shortdat; X int i; X X while(1) X { X if(OP_CLOSE == why) X { X gspack(TT_CTRL, X_CLOSE, 0, (unsigned char *)NULL, 0); X chanopen = 0; X printmsg(M_MEDPROTO, "Sending CLOSE request..."); X } X else if(neednack) X { X gspack(TT_CTRL, X_RJ, recv, (unsigned char *)NULL, 0); X neednack = 0; X printmsg(M_MEDPROTO, "Sending RJ... recv=%d", recv); X } X else if(send != nstuff /* nonzero output queue? */ X && between(send, next, nstuff) /* 'next' in queue? */ X && between(send, next, (send + SWINDOW) & 7)) /* in out win. */ X { X printmsg(M_MEDPROTO, "Sending data packet %d", next); X gspack(outbufs[next].sdata ? TT_SDATA : TT_DATA, X next, recv, outbufs[next].bufloc, outbufs[next].length); X needack = 0; X next = (next + 1) & 7; X if(OP_WRITE == why && next == nstuff) /* go back for more */ X return 0; X } X else if(needack) X { X gspack(TT_CTRL, X_RR, recv, (unsigned char *)NULL, 0); X needack = 0; X printmsg(M_MEDPROTO, "Sending RR... recv=%d", recv); X } X else if(OP_OPEN == why) X { X if(X_INITB == initpk) X i = ilog2(RPKTSIZE) - 5; /* INITB contains packet size, */ X else X i = RWINDOW; /* INITA, INITC contain window size */ X gspack(TT_CTRL, initpk, i, (unsigned char *)NULL, 0); X } X X if(OP_READ == why && recv != lastread) X return 0; X X shortdat = 0; X bufp = buf + ENV_LEN; X switch(grpack(&xxx, &yyy, &len)) X { X case LOSTPKT: X printmsg(M_MEDPROTO, "Lost packet..."); X if(nlost > MAXLOST) X return -1; X next = send; /* retransmit last un-ack'ed pkt, */ X if(OP_READ == why) /* request retransmit */ X neednack = 1; X skipping = 0; X break; X case BADPKT: X printmsg(M_MEDPROTO, "Bad packet..."); X neednack = 1; /* reject! */ X skipping = 1; /* ignore the rest of the 'window' */ X break; X case TT_SDATA: X shortdat = 1; X /* fall through */ X case TT_DATA: X send = (yyy + 1) & 7; /* recieve acknowledgement */ X if(xxx != ((recv + 1) & 7)) X { X printmsg(M_MEDPROTO, X "Recieved data out of sequence (%d != %d)", X xxx, (recv + 1) & 7); X if(!skipping) X { X neednack = 1; /* we must have missed one */ X skipping = 1; X } X else X printmsg(M_MEDPROTO, "(Ignoring)"); X } X else X { X recv = xxx; /* this is most recent correct pkt */ X needack = 1; /* we will ack it */ X skipping = 0; X inpbufs[xxx].length = len; X inpbufs[xxx].sdata = shortdat; X memcpy(inpbufs[xxx].bufloc, bufp, len); X } X break; X case TT_CTRL: X skipping = 0; X switch(xxx) X { X case X_CLOSE: X printmsg(M_MEDPROTO, "* CLOSE *"); X gsendpk(TT_CTRL, X_CLOSE, 0, NULL, 0); X chanopen = 0; X if(OP_CLOSE == why) X return 0; /* expected? */ X else X return -1; /* nope */ X case X_RJ: X printmsg(M_MEDPROTO, "got RJ yyy=%d", yyy); X next = send = (yyy + 1) & 7; X break; X case X_RR: X printmsg(M_MEDPROTO, "got RR yyy=%d", yyy); X send = (yyy + 1) & 7; X break; X case X_INITC: X printmsg(M_MEDPROTO, "* INITC *"); X swindow = yyy; X if(X_INITC == initpk) X { X chanopen = 1; X return 0; X } X break; X case X_INITB: X printmsg(M_MEDPROTO, "* INITB *"); X spktsize = 32 << yyy; X if(X_INITB == initpk) X initpk = X_INITC; X break; X case X_INITA: X printmsg(M_MEDPROTO, "* INITA *"); X swindow = yyy; X initpk = X_INITB; X break; X default: X printmsg(M_MEDPROTO, "bad control packet: xxx=%d", xxx); X } X break; X default: X printmsg(M_MEDPROTO, "bad packet type"); X break; X } X } X} X X/* X * |grpack()| is responsible for reading one 'g'-protocol packet from the X * input communications channel. This includes framing, detecting timeouts, X * and checksum validation. X * The basic strategy is to keep a count of how many bytes have currently X * been read and how many are needed. When enough bytes for a packet header X * ("envelope") have been read, it is validated. If it is valid, and it X * has a nonzero data segment, the data portion is read. When it has X * everything, it does a checksum test and returns, with the control X * information stored in |*xxxp|, |*yyyp|, and the data segment length stored X * in |*lenp|. X */ Xint grpack(xxxp, yyyp, lenp) Xint *xxxp, *yyyp; int *lenp; X{ X int need; /* need how many bytes? */ X int env; /* 'have pkt envelope' flag */ X int gotdle; /* do we have envelope hdr? */ X int tt; /* packet type */ X unsigned sum; /* checksum */ X int remain; /* bytes which we stil need */ X int i; X X env = gotdle = 0; X need = ENV_LEN; /* initially, need a header */ X X alarm(TIMEOUT); /* time out if we don't have a packet */ X timedout = 0; X X while(1) X { X if(low == high) /* prevent framebuffer overruns */ X low = high = buf; X printmsg(M_LOWPROTO, "=> l=%d h=%d g=%d", low - buf, high - buf, X gotdle); X X while((remain = need - (high - low)) > 0) X { X if(timedout || (i = sread2(high, remain)) < 0) X { X alarm(0); X ++nlost; X low = high = buf; /* empty out partial packet, if any */ X return env ? BADPKT : LOSTPKT; X } X high += i; /* got some data - move upper limit up */ X } X if(!gotdle) X { X while(low < high) /* look for header 'DLE' prefix */ X { X if(DLE == *low) X { X gotdle = 1; X break; X } X else X ++low; X } X continue; X } X else if(!env) /* found DLE, but haven't found header yet */ X { X if(low > buf) /* move envelope to buf beginning */ X { X register unsigned char *dst = buf; X while(low < high) X *dst++ = *low++; X low = buf; X high = dst; X } X if(buf[ENV_X] != (buf[ENV_K]^buf[ENV_C0]^buf[ENV_C1]^buf[ENV_C]) X || buf[ENV_K] < 1 || buf[ENV_K] > 9) /* valid? */ X { X ++low; X gotdle = 0; X printmsg(M_LOWPROTO, "grpack: rejecting an envelope"); X continue; X } X env = 1; /* we have an envelope */ X tt = (buf[ENV_C] >> 6) & 3; /* store away control info */ X *xxxp = (buf[ENV_C] >> 3) & 7; X *yyyp = (buf[ENV_C] ) & 7; X if(buf[ENV_K] == 9) /* does it have data? */ X *lenp = 0; X else X *lenp = 16 << buf[ENV_K]; /* size = 32 * 2^(k-1) */ X need += *lenp; /* now need that many more */ X printmsg(M_LOWPROTO, "grpack: tt=%d, xxx=%d, yyy=%d, need=%d", X tt, *xxxp, *yyyp, need); X continue; X } X else X { /* have everything we need */ X if(*lenp) X sum = MAGIC - (cksum(buf + ENV_LEN, *lenp) ^ buf[ENV_C]); X else X sum = MAGIC - buf[ENV_C]; X if(((sum >> 8) & 0xFF) != buf[ENV_C1] X || (sum & 0xFF) != buf[ENV_C0]) X { X alarm(0); X nlost = 0; X printmsg(M_LOWPROTO, "grpack: bad checksum"); X low += ENV_LEN; /* we will search bad data seg for a header */ X return BADPKT; X } X else X { X alarm(0); /* got it all... return */ X nlost = 0; X low += need; X if(*lenp) X printmsg(M_DATA, "|%s|", visib(buf + ENV_LEN, *lenp)); X return tt; X } X } X } X} X X/* X * gspack simply sends out a packet, by assembling an envelope and writing X * it, and the data segment, to the communications channel. X */ Xgspack(tt, xxx, yyy, data, size) Xint tt, xxx, yyy; unsigned char *data; int size; X{ X unsigned char envelope[ENV_LEN]; /* packet envelope */ X unsigned sum; /* checksum */ X unsigned char ctrl; /* header control byte */ X int k; /* size = 32 * 2^(k-1) */ X X ctrl = ((tt & 3) << 6) | ((xxx & 7) << 3) | (yyy & 7); X X if(0 == size) X { X k = 9; /* no data seg */ X sum = MAGIC - ctrl; X } X else X { X int tmp = size; X for(k = -5; tmp != 0; ++k) /* find k */ X tmp >>= 1; X sum = MAGIC - (cksum(data, size) ^ ctrl); X } X envelope[ENV_DLE] = DLE; X envelope[ENV_K] = k; X envelope[ENV_C1] = (sum >> 8) & 0xFF; X envelope[ENV_C0] = sum & 0xFF; X envelope[ENV_C] = ctrl; X envelope[ENV_X] = k ^ envelope[ENV_C0] ^ envelope[ENV_C1] ^ ctrl; X swrite(envelope, ENV_LEN); /* send envelope */ X if(0 != size) X swrite(data, size); /* send data segment */ X printmsg(M_LOWPROTO, "gspack: tt=%d xxx=%d yyy=%d k=%d sum=%d", X tt, xxx, yyy, k, sum); X if(0 != size) X printmsg(M_DATA, "|%s|", visib(data, size)); X} X X/* X * chksum(data, len) came directly from dcp. It checksums the given data X * area using the g protocol algorithm. X */ Xunsigned cksum(data, len) Xint len; unsigned char *data; X{ X unsigned int i, j, tmp, chk1, chk2; X X chk1 = 0xffff; X chk2 = 0; X j = len; X for (i = 0; i < len; i++) X { X if(chk1 & 0x8000) X { X chk1 <<= 1; X chk1++; X } X else X { X chk1 <<= 1; X } X tmp = chk1; X chk1 += (data[i] & 0xff); X chk2 += chk1 ^ j; X if((chk1 & 0xffff) <= (tmp & 0xffff)) X chk1 ^= chk2; X j--; X } X return (chk1 & 0xffff); X} X X/* |ilog2(value)| returns (int)floor(log2(value)). X */ Xint ilog2(value) Xunsigned value; X{ X int i; X X if(value == 0) X return -1; X for(i = 0; value > 1; ++i) X value >>= 1; X X return i; X} **-newgpkt.c-EOF-**