/* File: kermit.c Purpose: Reliable File Transfer Author: Frank da Cruz, et al. Editor: Bill Hullsiek Hullsiek; 21-Jul-1994 Copyright (C) 1985, 1992, Trustees of Columbia University in the City of New York. Permission is granted to any individual or institution to use this software as long as it is not sold for profit. This copyright notice must be retained. This software may not be included in commercial products without the written permission of Columbia University. This code is based on Kermit: A File Transfer Protocol Portions of this code is derived from the kermit implementation under qcp. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SOH 0x01 #define LF 0x0a #define CR 0x0d #define BLANK ' ' #define MAXSP 2048 #define MAXRP 2048 #define MAXBUF MAXRP + 100 #define MAX_STD_PKT 94 #define AVG_FBLKS 20 #define MAX_NC (AVG_FBLKS * FBLK_SIZE) #define MAXTRY 5 #define tochar(ch) (((ch) + ' ') & 0x7f ) #define unchar(ch) ((ch) - ' ') #define ctl(ch) ((ch) ^ 64 ) #define unpar(ch) ((ch) & 127) #define BEGIN(x) change_state(x) #define RESUME if(server) { SERVE } else if(cx == 1) return(-1); else return(0) #define SERVE tinit(); BEGIN(sserv); #define ERR(x) terror(x); zerror(); if (server) { SERVE } else return (-1); /* Kermit states */ enum sttype { stnull, ssini, ssfil, ssdat, sseot, sseof, srini, srfil, srdat, srgen, sserv, ssgen, sipkt } state = stnull; char strbuf [500] = "\0"; int spsiz = MAXSP; int rpsiz = MAXRP; int timint = 5; int rtimo = 7; int rpadn = 0; int spadn = 0; int bctr = 3; int bctu = 1; int ebq = '&'; int ebqflg = 0; int rqf = -1; int rq = 0; int sq = 'Y'; int rpt = 0; int rptq = '~'; int rptflg = 0; int capas = 10; int atcapb = 8; int atcapr = 0; int atcapu = 0; int swcapb = 4; int swcapr = 0; int swcapu = 0; int lpcapb = 2; int lpcapr = 1; int lpcapu = 0; char spadc = 0; char rpadc = 0; char seol = '\r'; char reol = '\r'; char rctlq = '#'; char sctlq = '#'; char ssc = 0; char smark = SOH; char rmark = SOH; int seq = 0; int size; int osize; int maxsiz; int rln; int rsn; int limit = MAXTRY; int sndpkl; char sndpkt [MAXBUF]; char rcvpkt [MAXBUF]; char *rdatap = NULL; char data [MAXRP+1]; char *isp = NULL; char *osp = NULL; FILE *ifp = NULL; FILE *ofp = NULL; char filname [FILENAME_MAX] = ""; char mfilname [FILENAME_MAX] = ""; char ofp_name [FILENAME_MAX] = ""; char ifp_name [FILENAME_MAX] = ""; int nfils = 0; int cc = 0; /* control-c */ int cx = 0; int cz = 0; int xflag = 0; int xpkt = 0; int filcnt = 0; int delay = 10; int mfile = 0; int text = 1; int local = 0; int debug = 0; int server = 0; int srv_flg = 0; int first = 0; int recno = 0; int keep = 0; char start = 0; int nakstate = 0; /* statistics */ unsigned long nspkts = 0; unsigned long nrpkts = 0; unsigned long nnpkts = 0; unsigned long nfchars = 0; unsigned long nschars = 0; unsigned long npchars = 0; unsigned long nrchars = 0; /* Command Parser */ char **xargv = NULL; int xargc = 0; char *cmarg = NULL; char **cmlist = NULL; char cmbuf [CMD_LINE_SIZE]; char action = 0; /* Communication Line */ FILE *tty_ifp; FILE *tty_ofp; unsigned tty_ifp_opt; unsigned tty_ofp_opt; unsigned std_opt; int parity = 0; char ttname [L_tmpnam] = "$mdm"; void usage() { fprintf (stderr, "C-Kermit for QNX-2 21-July-1994 \n\n"); fprintf (stderr, "kermit -xfrsgtRlip123h\n\n"); fprintf (stderr, "kermit -x \n"); fprintf (stderr, " -f \n"); fprintf (stderr, " -r \n"); fprintf (stderr, " -s filename \n"); fprintf (stderr, " -m filename \n"); fprintf (stderr, " -g filename \n"); fprintf (stderr, " -t filename \n"); fprintf (stderr, " -R path1 path2 \n"); fprintf (stderr, " -C command \n"); fprintf (stderr, " -l device \n"); fprintf (stderr, " -i \n"); fprintf (stderr, " -d \n"); fprintf (stderr, " -{1,2,3} \n"); fprintf (stderr, " -p {e,o,m,s,n} \n"); fprintf (stderr, " -h \n"); } void tmsg (char *s) /* put message on the console */ { if (s != NULL) fputs (s, stdout); fflush (stdout); } void tmsgn (int n) /* put number on the console */ { fprintf (stdout, "%d", n); fflush (stdout); } void tmsgl (char *s) /* put message on the console */ { if (s != NULL) fputs (s, stdout); fputc ('\n', stdout); fflush (stdout); } void tchar (char c) /* display a single character on the console */ { fputc (c, stdout); fflush (stdout); } void tstate (enum sttype state) { switch (state) { case stnull: tmsg ("stnull"); break; case ssini: tmsg ("ssini"); break; case ssfil: tmsg ("ssfil"); break; case ssdat: tmsg ("ssdat"); break; case sseot: tmsg ("sseot"); break; case sseof: tmsg ("sseof"); break; case srini: tmsg ("srini"); break; case srfil: tmsg ("srfil"); break; case srdat: tmsg ("srdat"); break; case srgen: tmsg ("srgen"); break; case sserv: tmsg ("sserv"); break; case ssgen: tmsg ("ssgen"); break; case sipkt: tmsg ("sipkt"); break; default: tmsg ("unknown state"); } } void tstats (char *s) /* display statistics */ { float percent; if (nfchars == 0) percent = 0.0; else percent = ((float) nschars / (float) nfchars) * 100; fprintf (stdout, "Char sent: %5U", nschars); fprintf (stdout, "; Pckt sent %5U", nspkts); fprintf (stdout, ", resent: %5U", nrpkts); fprintf (stdout, "; percent = %3.0f ", percent); if (debug) fprintf (stdout, "\n"); else fprintf (stdout, "%s", s); fflush (stdout); } int tt_is_console (FILE *tty_ifp) { int tty_devno; int std_devno; int tty_tid; int std_tid; char mdm_name [L_tmpnam]; char std_name [L_tmpnam]; tty_devno = fdevno (tty_ifp); std_devno = fdevno (stdin); tty_tid = query_device (tty_devno, mdm_name); std_tid = query_device (std_devno, std_name); if (tty_devno != std_devno) /* different device than standard input */ return (1); else if (tty_tid != std_tid) /* different owner */ return (1); else return (0); /* standard input device */ } int ttopen (char *name) /* open communication line */ { int local; local = 0; if (strchr (name, '$') == NULL) { tty_ifp = stdin; tty_ofp = stdout; } else if ((tty_ifp = fopen (name, "r")) == NULL) error ("Invalid input device '%s'. \r\l", name); else if ((tty_ofp = fopen (name, "w")) == NULL) error ("Invalid output device '%s'. \r\l", name); else if (tt_is_console (tty_ifp)) local = 1; else { fclose (tty_ifp); fclose (tty_ofp); tty_ifp = stdin; tty_ofp = stdout; } return (local); } int ttread (int i, int max_nc, int ticks) /* read a terminal line */ { int nc; iotime_set (tty_ifp, ticks); nc = fget (&(rcvpkt[i]), max_nc, tty_ifp); iotime_can (tty_ifp); rcvpkt [i+nc+1] = '\0'; return (nc); } void ttres() /* restore original communications */ { set_option (tty_ifp, tty_ifp_opt); set_option (tty_ofp, tty_ofp_opt); } void ttclos() { fclose (tty_ifp); fclose (tty_ofp); } int ttpkt () /* condition for packet i/o */ { unsigned option; tty_ifp_opt = get_option (tty_ifp); tty_ofp_opt = get_option (tty_ofp); option = tty_ifp_opt; option = option & ~ECHO; option = option & ~EDIT; option = option & ~MAPCR; if (option & HFLOW) { option = option | IFLOW; option = option | OFLOW; } set_option (tty_ifp, option); flush_input (tty_ifp); fflush (tty_ofp); } void ttflui() /* Flush input line */ { flush_input (tty_ifp); } int ttol (char s[], int n) /* output a line */ { int m; m = fput (s, n, tty_ofp); fflush (tty_ofp); return (m); } int ttsspd (int speed) /* verify speed */ { if (speed == 300) return (1); else if (speed = 1200) return (1); else if (speed = 2400) return (1); else if (speed = 4800) return (1); else if (speed = 9600) return (1); else if (speed = 19200) return (1); else return (0); } int zgetc (text) /* Get a character from input file */ { static int bp = -1; /* push-back pointer */ static unsigned char buf [MAX_NC]; static int c = 0; static int i = 0; static int nc = 0; static unsigned char pbbuf [4]; /* push-back buffer */ if (bp >= 0) { /* first return any pushed-back data */ c = pbbuf [bp]; bp--; } else if (cx == 1) { /* Return error message if interrupted */ c = -1; } else { i++; if (i >= nc) { /* read a buffer of data */ i = 0; nc = fget (buf, MAX_NC, ifp); if (nc < 0) { /* treat read error as interruption of file */ cx = 1; tmsgl ("zgetc: read error -- interrupt"); } } if (i >= nc) { c = -1; } else { nschars++; c = buf [i]; if ((text == 1) && (c == '\n')) { c = CR; bp++; pbbuf [bp] = LF; } } } if (c == -1) { bp = -1; nc = 0; } return (c); } int zputc (int c, int text) { int x; c &= 255; if (text && c == CR) { return (0); } else if (text && c == LF) { nrchars++; x = putc ('\n', ofp); return (0); } else { nrchars++; x = putc (c, ofp) & 255; if (c == 255) return (0); return ((x != c) ? -1 : 0); } } int zopeni (char *name) /* open local file for input */ { static struct dir_entry dir; strcpy (ifp_name, name); ifp = fopen (name, "r"); if (ifp == NULL) return (-1); else if (get_dir_entry (ifp, &dir) != 0) { error ("Can't read directory entry for %s \n", name); return (-1); } else { nschars = 0; nfchars = (dir.fnum_blks + 1) * FBLK_SIZE; nfchars = nfchars - (dir.fnum_xtnt * XTNT_HDR); if (dir.fnum_chars_free & 0x400) nfchars = nfchars - (dir.fnum_chars_free ^ 0x400); return (0); } } int zopeno (char *name) /* open local file for output */ { strcpy (ofp_name, name); nrchars = 0; ofp = fopen (name, "w"); if (ofp == NULL) return (-1); else return (0); } int zclosi() { if (fclose (ifp) == EOF) return (-1); else { ifp = NULL; return (0); } } int zcloso (int x) { if (fclose (ofp) == EOF) return (-1); else { ofp = NULL; return (0); } if (x != 0) remove (ofp_name); } void zerror() { if (ifp != NULL) zclosi(); if (ofp != NULL) zcloso(cx | cz); } int zltor (char *local, char *remote) { char *p; char *np; p = local; if ( *p == '[' ) { /* Skip over the node number */ while (*p && *p != ']') p++; } if (*p == '$') /* Can't prepend a device name */ return (-1); if (*p && *(p+1) == ':') /* Skip over the drive number */ p += 2; while (np = strchr (p + 1, '/')) { /* skip over the path */ np++; p = np; } if (*p == 0) return (-1); while (*p != '\0') { if (islower (*p) == 0) *remote = *p; else *remote = toupper(*p); p++; remote++; } *remote = '\0'; } int zrtol (char *remote, char *local) { for ( ; *remote != '\0'; remote++, local++) { *local = (isupper(*remote)) ? tolower (*remote) : * remote; } *local = '\0'; if (strlen (local) > NCPFN) /* truncate names */ local = '\0'; } /* ----------- Presentation Layer ------------ */ char *bldlen (char *str, char *dest) { int len; len = strlen(str); *dest = len + 32; strcpy (dest+1, str); return (dest+len+1); } char *setgen (char type, char *arg1, char *arg2, char *arg3) { char *upstr; char *cp; cp = cmbuf; *cp++ = type; *cp = '\0'; if (*arg1 != '\0') { upstr = bldlen (arg1, cp); if (*arg2 != '\0') { upstr = bldlen (arg2, upstr); if (*arg3 != '\0') { bldlen (arg3, upstr); } } } return (cmbuf); } int gnchar() { int c; if (isp == NULL) c = zgetc (text); else if (*isp == '\0') c = -1; else c = *isp++; return (c); } int pnchar (int c) { nrchars++; if (xflag) { tchar (c); return (1); } else if (osp) { *osp++ = c; return (1); } else return (zputc(c, text)); } void encode (int a, int next) { int a7, b8; if (rptflg) { if (a == next) { if (++rpt < MAX_STD_PKT) { return; } else if (rpt == MAX_STD_PKT) { data [size++] = rptq; data [size++] = tochar (rpt); rpt = 0; } } else if (rpt == 1) { rpt = 0; encode (a, -1); if (size <= maxsiz) osize = size; rpt = 0; encode (a, -1); return; } else if (rpt > 1) { data [size++] = rptq; data [size++] = tochar (++rpt); rpt = 0; } } a7 = a & 127; b8 = a & 128; if (ebqflg && b8) { data [size++] = ebq; a = a7; } if (a7 < 32 || a7 == 127) { data [size++] = sctlq; a = ctl (a); } else if (a7 == sctlq) { data [size++] = sctlq; } else if (ebqflg && a7 == ebq) { data [size++] = sctlq; } else if (rptflg && a7 == rptq) { data [size++] = sctlq; } data [size++] = a; data [size] = '\0'; } int getpkt (int maxlen) { int next; static int c; static int n; static unsigned char remain [10]; size = 0; data [size] = '\0'; if (first == 1) { n = -1; c = gnchar(); if (c < 0) { first = -1; return (size); } first = 0; } else if (first == -1) { return (size); } while (n >= 0) data [size++] = remain [n--]; rpt = 0; while (first > -1) { npchars++; next = gnchar(); if (next < 0) first = -1; osize = size; encode (c, next); c = next; if (size == maxlen) return (size); if (size > maxlen) { for ( ; size > osize; size--) remain [++n] = data [size-1]; size = osize; data [size] = '\0'; return (size); } } return (size); } int decode() { int a, a7, b8; while ((a = *rdatap++) != '\0') { rpt = 1; if (rptflg) { if (a == rptq) { rpt = unchar (*rdatap++); a = *rdatap++; } } b8 = 0; if (ebqflg) { if (a == ebq) { b8 = 128; a = *rdatap++; } } if (a == rctlq) { a = *rdatap++; a7 = a & 127; if (a7 > 62 && a7 < 96) a = ctl (a); } a |= b8; for (; rpt > 0; rpt--) if (pnchar(a) < 0) return (-1); } return (0); } int encstr (char *s) { first = 1; isp = s; getpkt (spsiz); isp = NULL; return (size); } void decstr (char *s) { osp = s; decode(); *osp = '\0'; osp = NULL; } /* ----------- Tranport Layer ----------- */ int nxtpkt (int seq) { return ( (seq + 1) & 63 ); } void tinit() { seq = 0; sndpkl = 0; *filname = '\0'; *sndpkt = '\0'; *rcvpkt = '\0'; ebqflg = 0; sq = 'Y'; rqf = -1; bctu = 1; rptflg = 0; xflag = 0; xpkt = 0; osp = NULL; nspkts = 0; nrpkts = 0; nnpkts = 0; } int chksum (char *p) { unsigned int m; long s; m = (parity) ? 0177 : 0377; for (s = 0; *p != '\0'; *p++) s += *p & m; return ((int) s & 077777); } int chkl (char *packet) { int s, t; s = chksum (packet); t = (((s & 192) >> 6) + s) & 63; return (t); } int chk3 (char *s) { /* File: ccitt.crc Author: Unknown Usage: This file is included by Kermit.c */ static unsigned crctable[256] = { /* CCITT crc */ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 }; long c, crc; crc = 0; while ((c = *s++) != '\0') { if (parity) c &= 0x7f; crc = crctable [ ( c ^ crc ) & 0xff ] ^ ( crc >> 8 ); } return ((int) crc); } int spack (char type, int n, int len, char *data) { int chklen, i, j, k; if (type == 'S' || type == 'I') chklen = 1; else chklen = bctu; if (debug) { tmsg ("spack:"); tmsg (" type = "); tchar (type); tmsg (" n = "); tmsgn (n); tmsg (" len = "); tmsgn (len); tmsg (" chk = "); tmsgn (chklen); tmsg (" data <"); tmsg (data); tmsgl (">"); } i = 0; while (i < spadn) sndpkt [i++] = spadc; sndpkt [i++] = smark; k = i++; sndpkt [i++] = tochar (n); sndpkt [i++] = type; j = len + chklen; if (j <= MAX_STD_PKT-2) sndpkt [k] = tochar (j+2); else { sndpkt [k] = tochar (0); sndpkt [i++] = tochar (j / 95); sndpkt [i++] = tochar (j % 95); sndpkt [i] = '\0'; sndpkt [i++] = tochar (chkl(sndpkt+k)); } sndpkt [i] = '\0'; if (len > 0) { memmove (sndpkt+i, data, len); i = i + len; } sndpkt [i] = '\0'; switch (chklen) { case 1: sndpkt [i++] = tochar (chkl (sndpkt+k)); break; case 2: j = chksum (sndpkt+k); sndpkt [i++] = tochar ((j >> 6) & 077); sndpkt [i++] = tochar ( j & 077); break; case 3: j = chk3 (sndpkt+k); sndpkt [i++] = tochar ((j >> 12) & 017); sndpkt [i++] = tochar ((j >> 6) & 077); sndpkt [i++] = tochar ( j & 077); break; default: error ("spack: block check type (chklen) = %d \n", chklen); } sndpkt [i++] = seol; sndpkt [i] = '\0'; sndpkl = i; i = ttol (sndpkt, sndpkl); return (i); } int nak() { int x; x = spack ('N', seq, 0, ""); return (x); } int resend() /* resend a packet */ { int x; if (sndpkl > 0) { nrpkts++; x = ttol (sndpkt, sndpkl); if (local && !xflag) tstats (" (resend) \r"); } else if (nakstate == 1) { nnpkts++; x = nak(); } else x = 0; return (x); } int serror (char *s) { int nc; nc = spack('E', seq, strlen(s), s); return (nc); } void terror (char *s) { if (server) serror (s); else if (local) { tmsg ("\nError: "); tmsgl (s); } return; } void trap_signal() { if (local) tmsgl ("\nControl-C interrupt of file transfer"); cx = 1; /* handle clean-up */ cc = 1; } int ack() { int x; x = spack ('Y', seq, 0, ""); seq = nxtpkt (seq); return (x); } int ackl (char *s) { int x; x = spack ('Y', seq, strlen(s), s); seq = nxtpkt (seq); return (x); } int rpack () /* read packet and return packet type */ { int chklen = 0; int g = 0; char hcheck = '\0'; int lenx1 = 0; int lenx2 = 0; int i = 0; int j = 0; int max_nc = 1; int nc = 0; char pbc[4]; int rlnpos = 0; enum pkt_states { null_pkt, pkt_start, pkt_hdr, pkt_data, pkt_eol, pkt_end } state = null_pkt; int ticks = 0; char type = 'T'; int x = 0; ticks = rtimo * 20; /* 20 ticks per second */ rsn = -1; rln = -1; rdatap = NULL; while (state != pkt_end) { if (cc) { type = 'Q'; state = pkt_end; } else if (i+max_nc > MAXBUF) { error ("rpack: overflow = %d characters \n", nc); type = 'Q'; state = pkt_end; } else if ((nc = ttread (i, max_nc, ticks)) < max_nc) { type = 'T'; state = pkt_end; } else switch (state) { case null_pkt: if (rcvpkt [i] == rmark) { state = pkt_start; i++; } else g++; break; case pkt_start: if (rcvpkt [i] == rmark) { /* do nothing */ } else { rlnpos = i; rln = unchar(rcvpkt[i++]); state = pkt_hdr; if (rln == 0) max_nc = 5; /* extended hdr */ else if (rln == 1) { terror ("rpack: extended hdr 1 \n"); type = 'Q'; state = pkt_end; } else if (rln == 2) { terror ("rpack: extended hdr 2 \n"); type = 'Q'; state = pkt_end; } else max_nc = 2; /* normal hdr */ } break; case pkt_hdr: rsn = unchar(rcvpkt [i+0]); type = rcvpkt [i+1]; if (type == 'I' || type == 'S') chklen = 1; else chklen = bctu; if (rln > 2) rln = rln - 2; else { lenx1 = rcvpkt [i+2]; lenx2 = rcvpkt [i+3]; hcheck = rcvpkt [i+4]; rcvpkt [i+4] = '\0'; if (unchar (hcheck) != chkl(rcvpkt+rlnpos)) { terror ("rpack: bad header checksum \n"); type = 'Q'; state = pkt_end; } rcvpkt [i+4] = hcheck; rln = (95 * unchar(lenx1)) + unchar(lenx2); } max_nc = rln; rln = rln - chklen; i = i + nc; j = i + rln; rdatap = rcvpkt + i; state = pkt_data; break; case pkt_data: i = i + nc; max_nc = 1; if (rcvpkt[i] == reol) state = pkt_end; else state = pkt_eol; break; case pkt_eol: if (rcvpkt[i] == reol) state = pkt_end; else i++; break; case pkt_end: terror ("rpack: invalid state \n"); break; } } /* perform checksum */ for (x = 0; x < chklen; x++) pbc [x] = rcvpkt [j+x]; rcvpkt [j] = '\0'; switch (chklen) { case 0: break; /* bad packet */ case 1: if (unchar (pbc[0]) != chkl (rcvpkt+rlnpos)) { if (debug) tmsgl ("rpack: checksum 1 problem"); type = 'Q'; } break; case 2: x = unchar (pbc[0]) << 6 | unchar (pbc[1]); if (x != chksum (rcvpkt+rlnpos)) { if (debug) tmsgl ("rpack: checksum 2 problem"); type = 'Q'; } break; case 3: x = unchar(pbc[0])<<12 | unchar(pbc[1])<<6 | unchar(pbc[2]); if (x != chk3 (rcvpkt+rlnpos)) { if (debug) tmsgl ("rpack: checksum 3 problem"); type = 'Q'; } break; default: error ("rpack: block check type (chklen) = %d \n", chklen); } if (debug) { tmsg ("rpack:"); tmsg (" g = "); tmsgn (g); tmsg (" i = "); tmsgn (i); tmsg (" j = "); tmsgn (j); if (chklen != bctu) { tmsg (" chklen = "); tmsgn (chklen); } tmsg (" rln = "); tmsgn (rln); tmsg (" type = "); tchar (type); tmsg (" rsn = "); tmsgn (rsn); tmsg (" data <"); if (rdatap != NULL) tmsg (rdatap); tmsgl (">"); } return (type); } void spar (char *s) /* set parameters */ { int x; s--; x = (rln >= 1) ? unchar(s[1]) : 80; spsiz = (x < 10) ? 80 : x; x = (rln >= 2) ? unchar(s[2]) : 5; timint = (x < 0) ? 5 : x; spadn = 0; spadc = '\0'; if (rln >= 3) { spadn = unchar (s[3]); if (rln >= 4) spadc = ctl(s[4]); else spadc = 0; } seol = (rln >= 5) ? unchar (s[5]) : '\r'; if ((seol < 2) || (seol > 31)) seol = '\r'; x = (rln >= 6) ? s[6] : '#'; rctlq = ((x > 32 && x < 63) || (x > 95 && x < 127)) ? x : '#'; rq = (rln >= 7) ? s [7] : 0; if (rq == 'Y') rqf = 1; else if ((rq > 32 && rq < 63) || (rq > 96 && rq < 127)) rqf = 2; else rqf = 0; x = 1; if (rln >= 8) { x = s[8] - '0'; if ((x < 1) || (x > 3)) x = 1; } bctr = x; if (bctr < 1 || bctr > 3) error ("spar: Received bad block check \n"); if (bctu < 1 || bctu > 3) error ("spar: bctu out of range \n"); switch (rqf) { case 0: ebqflg = 0; break; case 1: if (parity) { ebqflg = 1; ebq = '&'; } break; case 2: if (ebqflg = (ebq == sq || sq == 'Y')) ebq = rq; } if (rln < 9) rptflg = 0; else { rptq = s [9]; rptflg = ((rptq > 32 && rptq < 63) || (rptq > 95 && rptq < 127)); } atcapu = lpcapu = swcapu = 0; if (rln >= 10) { x = unchar (s[10]); atcapu = (x & atcapb) && atcapr; lpcapu = (x & lpcapb) && lpcapr; swcapu = (x & swcapb) && swcapr; for (capas = 10; (unchar (s[capas]) & 1) && (rln >= capas); capas++) ; } if (atcapu) { error ("spar: file attributes not supported \n"); } if (swcapu) { error ("spar: sliding windows not supported \n"); } if (lpcapu) { if (rln > capas+2) { spsiz = (unchar (s[capas+2]) * 95) + unchar (s[capas+3]); if (spsiz > MAXSP) spsiz = MAXSP; } } if (spsiz > MAX_STD_PKT) maxsiz = spsiz - bctr - 6; else maxsiz = spsiz - bctr - 3; } int rinit() { } char *rpar () { int x; data [1] = tochar (MAX_STD_PKT); data [2] = tochar (rtimo); data [3] = tochar (rpadn); data [4] = ctl (rpadc); data [5] = tochar (reol); data [6] = '#'; switch (rqf) { case -1: case 1: if (parity) ebq = sq = '&'; break; case 0: case 2: break; } data [7] = sq; data [8] = bctr + '0'; if (rptflg) data [9] = rptq; else data [9] = '~'; x = atcapr ? atcapb : 0 | lpcapr ? lpcapb : 0 | swcapr ? swcapb : 0; data [10] = tochar (x); data [11] = tochar (0); /* window size */ data [12] = tochar (rpsiz / 95); data [13] = tochar (rpsiz % 95); data [14] = '\0'; return (data+1); } int sinit (char c) { char *s; s = rpar(); if (local == 0 && c == 'S' && server == 0) { tmsgl ("Escape back to local system, give RECEIVE command..."); sleep (delay); } return (spack (c, seq, strlen(s), s)); } char sfile () { int x; char pktnam [FILENAME_MAX]; if (zopeni (filname) < 0) return (-1); zltor (filname, pktnam); x = encstr (pktnam); if (mfile) { strcpy (mfilname, filname); } if (local) { if (mfile) tmsg ("Moving "); else tmsg ("Sending "); tmsg (filname); tmsg (" as "); tmsg (pktnam); if (parity) tmsgl (" with parity. \n"); else tmsgl (" "); } first = 1; seq = nxtpkt (seq); return (spack((xpkt ? 'X' : 'F'), seq, x, data)); } int sdata() { int x; x = getpkt (maxsiz); if (x == 0) { return (0); } if (cx) { return (0); } seq = nxtpkt (seq); x = spack ('D', seq, x, data); return (x); } int seof (char *s) { seq = nxtpkt (seq); if (zclosi () < 0) *s = 'D'; return (spack ('Z', seq, strlen(s), s)); } int seot () { seq = nxtpkt (seq); if (local && *cmarg == '\0') tmsgl ("\nDone"); return (spack ('B', seq, 0, "")); } int scmd (char t, char *s) { int x; if (local) { tmsg ("Remote command: "); tmsgl (s); } x = encstr (s); spack (t, seq, x, data); } int gnfile() { if (cz) { return (0); } else if (nfils-- <= 0) { return (0); } else if (**cmlist == '-') return (0); else { strcpy (filname, *cmlist++); return (1); } } int rcvfil() { char myname [FILENAME_MAX]; decstr (filname); zrtol (filname, myname); if (zopeno (myname) < 0) return (-1); else { if (local && !xflag) { tmsg ("Receiving "); tmsg (filname); tmsg (" as "); tmsgl (myname); } return (0); } } int closof() { int x; if (xflag) return (0); decstr (strbuf); x = strncmp (strbuf, "D", 1) | cx | cz; if (zcloso (x) < 0) return (-1); return (0); } int input() { int type, try; if (start != 0) { type = start; start = 0; return (type); } type = rpack(); for (try = 0; rsn != seq || strchr("TQN", type); try++) { if (cc) return ('T'); if (try > limit) return ('T'); if (type == 'N' && rsn == nxtpkt (seq)) return ('Y'); else if (type == 'T' && state == sserv) { /* do nothing */ } else if (type == 'E') return ('E'); else resend(); type = rpack(); } /* ttflui(); */ return (type); } void change_state (enum sttype new_state) { state = new_state; if (debug) { tmsg ("Change state to "); tstate (new_state); tmsgl ("."); } return; } int yylex() { int ss, type, x; while (cc == 0) { type = input(); if (debug) { tmsg ("yylex:"); tmsg (" type = "); tchar (type); tmsg (" state = "); tstate (state); tmsgl (" "); } if (type == 's') { /* send state */ tinit(); if (sinit('S') < 0) { ERR("sinit -- Could not initialize link"); } else { nakstate = 0; filcnt = 0; BEGIN(ssfil); } } else if (type == 'v') { /* receive state */ tinit(); nakstate = 1; rinit(); BEGIN(srini); } else if (type == 'r') { /* get */ tinit(); ssc = 0; sinit ('I'); BEGIN(sipkt); } else if (type == 'c') { /* host */ tinit(); ssc = 'C'; sinit ('I'); BEGIN(sipkt); } else if (type == 'g') { /* generic */ tinit(); ssc = 'G'; sinit('I'); BEGIN(sipkt); } else if (type == 'x') { /* server */ server = 1; SERVE; } else if (type == 'Y' && (state == ssfil || state == sseof)) { if (state == sseof) { if (local && !xflag) tstats (" (sseof) \r"); if (cx == 1) { /* do not delete file if interrupted */ } else if (*cmarg != '\0') { /* start a command sequence at eot */ } else if (mfile == 0) { /* only delete if mfile is set */ } else if (remove (mfilname) == 0) { tmsg("\n"); tmsg(mfilname); tmsgl(" removed after successful transfer"); } else { tmsg ("\nError: "); tmsg (mfilname); tmsgl (" could not be removed\n"); } } if (filcnt++ == 0) spar (rdatap); cx = 0; bctu = bctr; if (gnfile() > 0) { if (sfile() < 0) { ERR("Could not open file to send"); } else { BEGIN(ssdat); } } else if (seot() < 0) { ERR("seot"); } else { BEGIN(sseot); } } else if (type == 'Y' && state == ssdat) { nspkts++; if (local && !xflag) tstats (" (ssdat) \r"); decstr (strbuf); if (strbuf[0] == 'X') { cx = 1; if (local && !xflag) tmsgl ("File Transfer interrupted by Ctrl-X"); } else if (strbuf[0] == 'Z') { cz = 1; if (local && !xflag) tmsgl ("File Transfer interrupted by Ctrl-Z"); } x = sdata(); if (x < 0) { ERR("sdata"); } else if (x == 0) { if (seof((cx | cz) ? "D" : "") < 0) { ERR("seof"); } else { BEGIN(sseof); } } } else if (type == 'Y' && state == sseot) { if (*cmarg != '\0') start = 'c'; else { RESUME; } } /* Receiver States */ else if (type == 'S' && state == srini) { spar (rdatap); ackl (rpar()); bctu = bctr; nakstate = 1; BEGIN(srfil); } else if (type == 'B' && state == srfil) { ack(); RESUME; } else if (type == 'F' && state == srfil) { if (rcvfil() < 0) { ERR("rcvfil"); } else { ack(); nakstate = 1; BEGIN(srdat); } } else if (type == 'D' && state == srdat) { if (decode() < 0) { ERR("decode"); } else { ack(); } } else if (type == 'T' && state == srdat) { cx = 1; ERR("Timeout in srdat state"); } else if (type == 'Z' && state == srdat) { if (closof() < 0) { ERR("closof"); } else { if (cx) ackl ("X"); else if (cz) ackl ("Z"); else ack(); state = srfil; } } /* Client states */ else if (type == 'Y' && state == sipkt) { spar(rdatap); start = 'E'; } else if (type == 'E' && state == sipkt) { if (ssc) { if (scmd (ssc, cmarg) < 0) { ERR("scmd"); } else { ssc = 0; nakstate = 1; BEGIN(srgen); } } else { if (scmd ('R', cmarg) < 0) { ERR("scmd"); } else { nakstate = 1; BEGIN(srini); } } } else if (type == 'Y' && state == srgen) { xflag = 1; decode(); RESUME; } else if (type == 'S' && state == srgen) { spar (rdatap); ackl (rpar()); bctu = bctr; nakstate = 1; BEGIN(srfil); /* When remote command has been ack'd, check -move flag */ if (mfile == 1) { if (remove (mfilname) == 0) { tmsg ("\n"); tmsg (mfilname); tmsgl (" removed after successful transfer"); } else { tmsg ("\nError: "); tmsg (mfilname); tmsgl (" could not be deleted\n"); } } } /* Server states */ else if (type == 'T' && state == sserv) { if (local) tmsgl ("Kermit: waiting..."); } else if (type == 'X' && (state == srgen || state == srfil)) { xflag = 1; ack(); nakstate = 1; BEGIN(srdat); } else if (type == 'I' && state == sserv) { spar (rdatap); ackl (rpar()); seq = 0; } else if (type == 'R' && state == sserv) { decstr (strbuf); nfils = 1; *cmlist = strbuf; /* point to name for gnfile() */ if (sinit ('S') < 0) { ERR("sinit"); } else { filcnt = 0; nakstate = 0; /* now I'm the sender */ BEGIN(ssfil); } } else if (type == 'S' && state == sserv) { spar (rdatap); ackl (rpar()); bctu = bctr; nakstate = 1; BEGIN(srfil); } else if (type == 'G' && state == sserv) { decstr (strbuf); start = *strbuf; xpkt = 1; nakstate = 0; /* now I'm the sender */ BEGIN(ssgen); } else if (type == 'C' && state == sserv) { decstr (strbuf); ss = system(strbuf); if (ss == 0) BEGIN(ssini); else { terror("Can't do system command"); SERVE; } } else if (type == 'E' && state == sserv) { if (local && rdatap != NULL) tmsgl (rdatap); SERVE; } else if (state == sserv) { ERR("Unknown server command"); SERVE; } /* Generic commands */ else if (type == 'F' && state == ssgen) { ack(); server = 0; return (0); } else if (type == 'T' && state == ssgen) { decstr(strbuf); nfils = 1; *cmlist = strbuf+2; /* setup for gnfile */ if (sinit('S') < 0) { ERR("sinit"); } else { filcnt = 0; BEGIN(ssfil); } } else if (state == ssgen) { serror ("Unknown command"); SERVE; } else if (type == 'E') { if (local) tmsgl (rdatap); RESUME; } else if (type == 'T') { tmsg ("Kermit--timeout in state <"); tstate (state); tmsgl (">\n"); zerror(); if (server) { tinit(); BEGIN(sserv); } else return (-1); } else { if (local) { tmsg ("Kermit--inconsistency: state <"); tstate (state); tmsg ("> packet type <"); if (isprint (type)) tchar (type); else tmsgn ((int) type); tmsgl (">\n"); } zerror(); if (server) { tinit(); BEGIN(sserv); } else return (-1); } } ttres(); return (-1); } void doexit (int x) { ttres(); ttclos(); exit (x); } void fatal (char *msg) { fprintf (stderr, "\r\nFatal: %s\n", msg); exit (-1); } void check_action() { if (action) fatal ("conflicting actions"); } void check_bundling (char *xp, char x) { if (*(xp+1)) { fprintf (stderr, "\r\nFatal: invalid argument bundling after -%c\n", x); exit (-1); } } char *check_next_arg (char x) { *xargv++; xargc--; if ((xargc == 0) || (**xargv == '-')) { fprintf (stderr, "\r\nFatal: missing argument after -%c\n", x); exit (-1); } return (*xargv); } char doarg (char x) { int done; char *xp; char *fspc1; char *fspc2; xp = *xargv+1; while (x) { switch (x) { case 'd': /* debug switch */ debug = 1; break; case 'x': /* server */ check_action(); action = 'x'; break; case 'f': /* generic finish */ check_action(); action = 'g'; cmarg = setgen ('F', "", "", ""); break; case 'r': /* receive */ check_action(); action = 'v'; break; case 'm': /* move */ mfile = 1; case 's': /* send */ check_action(); check_bundling (xp, x); nfils = 0; done = 0; cmlist = xargv+1; while (done == 0) { xargc--; *xargv++; if (xargc < 1) done = 1; else if (*xargv == NULL) done = 1; else if (**xargv == '-') { done = 1; } else { nfils++; } } xargc++; *xargv--; if (nfils < 1) fatal ("missing filename for -s"); action = 's'; break; case 'g': /* get */ check_action(); check_bundling (xp, x); cmarg = check_next_arg (x); action = 'r'; break; case 't': /* remote type */ check_action(); check_bundling (xp, x); fspc1 = check_next_arg (x); cmarg = setgen ('T', fspc1, "", ""); action = 'g'; break; case 'C': /* Host Command */ cmarg = check_next_arg (x); if (action == 0) action = 'c'; break; case 'R': /* rename */ check_bundling (xp, x); fspc1 = check_next_arg (x); fspc2 = check_next_arg (x); cmarg = setgen ('R', fspc1, fspc2, ""); if (action == 0) action = 'g'; break; case 'h': /* help */ usage(); return (-1); case 'l': /* communication device */ check_bundling (xp, x); fspc1 = check_next_arg (x); strcpy (ttname, fspc1); break; case 'i': text = 0; break; case 'p': check_bundling (xp, x); check_next_arg (x); switch (x = **xargv) { case 'e': case 'o': case 'm': case 's': parity = x; break; case 'n': parity = 0; break; default: fatal ("invalid parity"); } break; case '1': case '2': case '3': bctr = x - '0'; break; default: fatal("Invalid argument, type 'kermit -h' for help"); } x = *++xp; } return (0); } char cmdlin() { char x; cmarg = ""; action = 0; cmlist = xargv; while (--xargc > 0) { xargv++; if (**xargv == '-') { x = *(*xargv+1); x = doarg (x); if (x < 0) exit (x); } else { usage(); exit(0); } } return (action); } void main (int argc, char **argv) { xargc = argc; xargv = argv; start = 0; seq = 0; if (argc <= 1) usage(); else { signal (SIGINT, trap_signal); start = cmdlin(); if (start == 0) fatal ("no start state"); else { if ((local = ttopen (ttname)) < 0) fatal ("Can't open line"); ttpkt(); if (start) { doexit (yylex()); } } } }