/* Embedded Kermit protocol module */ /* MRU: Fri Mar 15 19:13:22 2002 */ /* Author: Frank da Cruz. Copyright (C) 1995, 2002, Trustees of Columbia University in the City of New York. All rights reserved. */ /* No stdio or other runtime library calls, no system calls, no system includes, no static data, and no global variables in this module. */ #include "vxWorks.h" #include "stdlib.h" #include "string.h" #include "stdio.h" #include "kermit.h" #ifdef DEBUG /* ... except if debugging ... */ extern int debug, errorrate; extern FILE * dp; #endif /* DEBUG */ #define zgetc() ((--(k->zincnt))>=0)?((int)(*(k->zinptr)++)&0xff):readfile(k) int /* The kermit() function */ kermit(short f, /* Function code */ struct k_data *k, /* The control struct */ short r_slot, /* Received packet slot number */ int len, /* Length of packet in slot */ char *msg, /* Message for error packet */ struct k_response *r) { /* Response struct */ int i, j, rc; /* Workers */ int datalen; /* Length of packet data field */ int bctu; /* Block check type for this packet */ UCHAR *p; /* Pointer to packet data field */ UCHAR *q; /* Pointer to data to be checked */ UCHAR c, t; /* Workers */ UCHAR pbc[4]; /* Copy of packet block check */ short seq, prev; /* Copies of sequence numbers */ short chklen; /* Length of packet block check */ unsigned int crc; /* 16-bit CRC */ int ok; #ifdef DEBUG if (debug) { fprintf(dp,"-------------\n"); fprintf(dp,"f=%d state=%d\n",f,k->state); } #endif /* DEBUG */ if (f == K_INIT) { /* Initialize packet buffers etc */ r->filename[0] = '\0'; /* No filename yet. */ r->filedate[0] = '\0'; /* No filedate yet. */ r->filesize = 0L; /* No filesize yet. */ for (i = 0; i < P_WSLOTS; i++) { /* Packet info for each window slot */ freerslot(k,i); freesslot(k,i); } #ifdef F_TSW for (i = 0; i < 64; i++) { /* Packet finder array */ k->r_pw[i] = -1; /* initialized to "no packets yet" */ k->s_pw[i] = -1; /* initialized to "no packets yet" */ } #endif /* F_TSW */ /* Initialize the k_data structure */ for (i = 0; i < 6; i++) k->s_remain[i] = '\0'; k->state = R_WAIT; /* Beginning protocol state */ k->what = W_RECV; /* Default action */ k->s_first = 1; /* Beginning of file */ k->r_soh = k->s_soh = SOH; /* Packet start */ k->r_eom = k->s_eom = CR; /* Packet end */ k->s_seq = k->r_seq = 0; /* Packet sequence number */ k->s_type = k->r_type = 0; /* Packet type */ k->r_timo = P_R_TIMO; /* Timeout interval for me to use */ k->s_timo = P_S_TIMO; /* Timeout for other Kermit to use */ k->r_maxlen = P_PKTLEN; /* Maximum packet length */ k->s_maxlen = P_PKTLEN; /* Maximum packet length */ k->window = P_WSLOTS; /* Maximum window slots */ k->wslots = 1; /* Current window slots */ /* Parity must be filled in by the caller */ k->retry = P_RETRY; /* Retransmission limit */ k->s_ctlq = k->r_ctlq = '#'; /* Control prefix */ k->ebq = 'Y'; /* 8th-bit prefix negotiation */ k->ebqflg = 0; /* 8th-bit prefixing flag */ k->rptq = '~'; /* Send repeat prefix */ k->rptflg = 0; /* Repeat counts negotiated */ k->s_rpt = 0; /* Current repeat count */ k->capas = 0 /* Capabilities */ #ifdef F_LP | CAP_LP /* Long packets */ #endif /* F_LP */ #ifdef F_SW | CAP_SW /* Sliding windows */ #endif /* F_SW */ #ifdef F_AT | CAP_AT /* Attribute packets */ #endif /* F_AT */ ; k->opktbuf[0] = '\0'; /* No packets sent yet. */ k->opktlen = 0; /* This is the only way to initialize these tables -- no static data. */ k->crcta[ 0] = 0L; /* CRC generation table A */ k->crcta[ 1] = 010201L; k->crcta[ 2] = 020402L; k->crcta[ 3] = 030603L; k->crcta[ 4] = 041004L, k->crcta[ 5] = 051205L; k->crcta[ 6] = 061406L; k->crcta[ 7] = 071607L; k->crcta[ 8] = 0102010L; k->crcta[ 9] = 0112211L; k->crcta[10] = 0122412L; k->crcta[11] = 0132613L; k->crcta[12] = 0143014L, k->crcta[13] = 0153215L; k->crcta[14] = 0163416L; k->crcta[15] = 0173617L; k->crctb[ 0] = 0L; /* CRC table B */ k->crctb[ 1] = 010611L; k->crctb[ 2] = 021422L; k->crctb[ 3] = 031233L; k->crctb[ 4] = 043044L, k->crctb[ 5] = 053655L; k->crctb[ 6] = 062466L; k->crctb[ 7] = 072277L; k->crctb[ 8] = 0106110L; k->crctb[ 9] = 0116701L; k->crctb[10] = 0127532L; k->crctb[11] = 0137323L; k->crctb[12] = 0145154L, k->crctb[13] = 0155745L; k->crctb[14] = 0164576L; k->crctb[15] = 0174367L; return(X_OK); } else if (f == K_SEND) { if (rpar(k,'S') != X_OK) /* Send S packet with my parameters */ return(X_ERROR); /* I/O error, quit. */ k->state = S_INIT; /* All OK, switch states */ k->what = W_SEND; /* Act like a sender */ return(X_OK); } else if (f == K_STATUS) { /* Status report requested. */ r->status = k->state; /* Say what state I'm in. */ return(X_STATUS); /* File name, date, size, if any. */ } else if (f == K_QUIT) { /* You told me to quit */ return(X_DONE); /* so I quit. */ } else if (f == K_ERROR) { /* Send an error packet... */ epkt(msg,k); return(X_DONE); /* and quit. */ } else if (f != K_RUN) { /* Anything else is an error. */ return(X_ERROR); } if (k->state == R_NONE) /* (probably unnecessary) */ return(X_OK); /* If we're in the protocol, check to make sure we got a new packet */ #ifdef DEBUG if (debug) fprintf(dp,"r_slot=%d length=%d\n",r_slot,len); #endif /* DEBUG */ if (r_slot < 0) /* We should have a slot here */ return(K_ERROR); else k->ipktinfo[r_slot].len = len; /* Copy packet length to ipktinfo. */ if (len < 4) { /* Packet obviously no good? */ if (k->what == W_RECV) /* If receiving */ return(nak(k,k->r_seq,r_slot)); /* Send NAK for the packet we want */ else /* If sending */ return(resend(k)); /* retransmit last packet. */ } /* Parse the packet */ p = &(k->ipktbuf[0][r_slot]); /* Point to it */ q = p; /* Pointer to data to be checked */ k->ipktinfo[r_slot].len = xunchar(*p++); /* Length field */ seq = k->ipktinfo[r_slot].seq = xunchar(*p++); /* Sequence number */ t = k->ipktinfo[r_slot].typ = *p++; /* Type */ if ((k->what == W_RECV) && /* Echo (it happens), ignore */ (t == 'N' || t == 'Y')) { freerslot(k,r_slot); return(X_OK); } k->ipktinfo[r_slot].dat = p; /* Data field, maybe */ if (k->ipktinfo[r_slot].len == 0) { /* Length 0 means long packet */ c = p[2]; /* Get header checksum */ p[2] = '\0'; if (xunchar(c) != chk1(p-3)) { /* Check it */ freerslot(k,r_slot); /* Bad */ #ifdef DEBUG if (debug) fprintf(dp,"HDR CHKSUM BAD\n"); #endif /* DEBUG */ if (k->what == W_RECV) return(nak(k,k->r_seq,r_slot)); /* Send NAK */ else return(resend(k)); } #ifdef DEBUG if (debug) fprintf(dp,"HDR CHKSUM OK\n"); /* Good */ #endif /* DEBUG */ p[2] = c; /* Put checksum back */ datalen = xunchar(p[0])*95 + xunchar(p[1]) - k->bct; /* Data length */ p += 3; /* Fix data pointer */ k->ipktinfo[r_slot].dat = p; /* Permanent record of data pointer */ } else { /* Regular packet */ datalen = k->ipktinfo[r_slot].len - k->bct - 2; /* Data length */ } if (t == 'S' || k->state == S_INIT) { /* S-packet was retransmitted? */ chklen = 1; /* Block check is always type 1 */ datalen = k->ipktinfo[r_slot].len - 3; /* Data length */ } else chklen = k->bct; #ifdef DEBUG if (debug) fprintf(dp,"bct=%d datalen=%d chklen=%d\n",k->bct,datalen,chklen); #endif /* DEBUG */ for (i = 0; i < chklen; i++) /* Copy the block check */ pbc[i] = p[datalen+i]; pbc[i] = '\0'; /* Null-terminate block check string */ p[datalen] = '\0'; /* and the packet DATA field. */ switch (chklen) { /* Check the block check */ case 1: /* Type 1, 6-bit checksum */ ok = (xunchar(*pbc) == chk1(q)); #ifdef DEBUG if (ok && xerror()) ok = 0; #endif /* DEBUG */ if (!ok) { freerslot(k,r_slot); if (k->what == W_RECV) nak(k,k->r_seq,r_slot); else resend(k); return(X_OK); } break; case 2: /* Type 2, 12-bit checksum */ i = xunchar(*pbc) << 6 | xunchar(pbc[1]); ok = (i == chk2(q)); #ifdef DEBUG if (ok && xerror()) ok = 0; #endif /* DEBUG */ if (!ok) { /* No match */ if (t == 'E') { /* Allow E packets to have type 1 */ int j; j = datalen; p[j++] = pbc[0]; p[j] = '\0'; if (xunchar(pbc[1]) == chk1(q)) break; else p[--j] = '\0'; } freerslot(k,r_slot); if (k->what == W_RECV) nak(k,k->r_seq,r_slot); else resend(k); return(X_OK); } break; case 3: /* Type 3, 16-bit CRC */ crc = (xunchar(pbc[0]) << 12) | (xunchar(pbc[1]) << 6) | (xunchar(pbc[2])); ok = (crc == chk3(q,k)); #ifdef DEBUG if (ok && xerror()) { ok = 0; if (debug) fprintf(dp,"CRC ERROR INJECTED\n"); } #endif /* DEBUG */ if (!ok) { #ifdef DEBUG if (debug) fprintf(dp,"CRC ERROR %ld != %ld\n",crc, chk3(q,k)); #endif /* DEBUG */ if (t == 'E') { /* Allow E packets to have type 1 */ int j; j = datalen; p[j++] = pbc[0]; p[j++] = pbc[1]; p[j] = '\0'; if (xunchar(pbc[2]) == chk1(q)) break; else { j -=2; p[j] = '\0'; } } freerslot(k,r_slot); if (k->what == W_RECV) nak(k,k->r_seq,r_slot); else resend(k); return(X_OK); } } #ifdef DEBUG if (debug) { fprintf(dp,"PACKET OK\n"); fprintf(dp,"Typ=%c\n",t); fprintf(dp,"Dat=%s\n",p); } #endif /* DEBUG */ if (t == 'E') return(X_ERROR); prev = k->r_seq - 1; /* Get sequence of previous packet */ if (prev < 0) prev = 63; #ifdef DEBUG if (debug) fprintf(dp,"Seq=%d Prev=%d\n",seq,prev); #endif /* DEBUG */ if (seq == k->r_seq) { /* Is this the packet we want? */ k->ipktinfo[r_slot].rtr = 0; /* Yes */ } else { freerslot(k,r_slot); /* No, discard it. */ if (seq == prev) { /* If it's the previous packet again */ #ifdef DEBUG if (debug) { fprintf(dp,"PREVIOUS PACKET AGAIN: RETRIES=%d, LIMIT=%d\n", k->ipktinfo[r_slot].rtr, k->retry ); } #endif /* DEBUG */ if (k->ipktinfo[r_slot].rtr++ > k->retry) { /* Count retries */ epkt("Too many retries", k); /* Too may */ return(X_ERROR); /* Give up */ } else { /* Otherwise */ return(resend(k)); /* Send old outbound packet buffer */ } } else if (k->what == W_RECV) { /* Otherwise NAK the one we want */ return(nak(k,k->r_seq,r_slot)); } else { /* or whatever... */ return(resend(k)); } } if (k->what == W_SEND) { /* Sending, check for ACK */ if (t != 'Y') return(resend(k)); /* It isn't */ if (k->state == S_DATA) { /* ACK to Data packet?*/ if (*p == 'X' || *p == 'Z') { /* Contains cancellation request? */ /* NEED TO CLOSE FILE HERE TOO */ if ((rc = spkt('Z',k->s_seq,0,(UCHAR *)0,k)) != X_OK) return(rc); k->state = S_EOF; /* Wait for ACK to EOF */ #if 0 if (*p == 'Z') while ((k->filelist)++) ; /* Go to end of file list */ #endif k->state = S_EOF; /* Wait for ACK to EOF */ } } freerslot(k,r_slot); /* It is, free the ACK. */ } /* Now we have an incoming packet with the expected sequence number. */ #ifdef DEBUG if (debug) fprintf(dp,"GOT good %c-Packet\n",t); #endif /* DEBUG */ switch (k->state) { /* Kermit protocol state switcher */ case S_INIT: /* Got other Kermit's parameters */ case S_EOF: /* Got ACK to EOF packet */ nxtpkt(k); /* Get next packet number etc */ if (k->state == S_INIT) { /* Got ACK to S packet? */ spar(k,p,datalen); /* Set negotiated parameters */ #ifdef DEBUG if (debug) { fprintf(dp,"parity = '%c'\n", k->parity ? k->parity : 'n'); fprintf(dp,"ebqflg = %d\n", k->ebqflg); fprintf(dp,"ebq = '%c'\n", k->ebq); } #endif /* DEBUG */ } k->sendfile = *(k->filelist)++; /* Get next filename */ #ifdef DEBUG if (debug) fprintf(dp,"sendfile = [%s]\n",k->sendfile); #endif /* DEBUG */ if (!(k->sendfile)) k->sendfile = (UCHAR *)""; if (*(k->sendfile)) { /* If there is one */ if ((rc = (k->openf)(k->sendfile,1,k)) != X_OK) /* Try to open */ return(rc); encstr(k->sendfile,k); /* Encode the name for transmission */ if ((rc = spkt('F',k->s_seq,-1,k->xdata,k)) != X_OK) return(rc); /* Send F packet */ k->state = S_FILE; /* Wait for ACK */ } else { /* No more files - we're done */ if ((rc = spkt('B',k->s_seq,0,(UCHAR *)0,k)) != X_OK) return(rc); /* Send EOT packet */ k->state = S_EOT; /* Wait for ACK */ } k->r_seq = k->s_seq; /* Sequence number of packet we want */ return(X_OK); /* Return to control program */ case S_FILE: /* Got ACK to F packet */ nxtpkt(k); /* Get next packet number etc */ if (k->capas & CAP_AT) { /* A-packets negotiated? */ if ((rc = sattr(k)) != X_OK) /* Yes, send Attribute packet */ return(rc); k->state = S_ATTR; /* And wait for its ACK */ } else if (sdata(k) == 0) { /* No A packets - send first data */ /* File is empty so send EOF packet */ if ((rc = spkt('Z',k->s_seq,0,(UCHAR *)0,k)) != X_OK) return(rc); k->state = S_EOF; /* Wait for ACK to EOF */ } else { /* Sent some data */ k->state = S_DATA; /* Wait for ACK to first data */ } k->r_seq = k->s_seq; /* Sequence number to wait for */ return(X_OK); case S_ATTR: /* Got ACK to A packet */ case S_DATA: /* Got ACK to D packet */ nxtpkt(k); /* Get next packet number */ if (k->state == S_ATTR) { /* CHECK ATTRIBUTE RESPONSE */ /* IF REJECTED do the right thing... */ k->state = S_DATA; } rc = sdata(k); /* Send first or next data packet */ #ifdef DEBUG if (debug) fprintf(dp,"sdata(%d) = %d\n",k->s_seq,rc); #endif /* DEBUG */ if (rc == 0) { /* If there was no data to send */ if ((rc = spkt('Z',k->s_seq,0,(UCHAR *)0,k)) != X_OK) return(rc); /* Send EOF */ k->state = S_EOF; /* And wait for ACK */ } /* Otherwise stay in data state */ k->r_seq = k->s_seq; /* Sequence number to wait for */ return(X_OK); case S_EOT: /* Get ACK to EOT packet */ return(X_DONE); /* (or X_ERROR) */ case R_WAIT: /* Waiting for the S packet */ if (t == 'S') { /* Got it */ spar(k,p,datalen); /* Set parameters from it */ rc = rpar(k,'Y'); /* ACK with my parameters */ #ifdef DEBUG if (debug) fprintf(dp,"rpar returns %d\n",rc); #endif /* DEBUG */ if (rc != X_OK) return(X_ERROR); /* I/O error, quit. */ k->state = R_FILE; /* All OK, switch states */ } else { /* Wrong kind of packet, send NAK */ epkt("Unexpected packet type",k); rc = X_ERROR; } freerslot(k,r_slot); /* Free packet slot */ return(rc); case R_FILE: /* Want an F or B packet */ if (t == 'F') { /* File name */ if ((rc = decode(k, r, 0, p)) == X_OK) /* Decode and save */ k->state = R_ATTR; /* Switch to next state */ #ifdef DEBUG if (debug) fprintf(dp,"FILENAME=[%s], code=%d\n",r->filename,rc); #endif /* DEBUG */ if (rc == X_OK) /* All OK so far */ rc = ack(k, k->r_seq, r->filename); /* so ACK the F packet */ else epkt("Filename error",k); /* Error decoding filename */ } else if (t == 'B') { /* Break, end of transaction */ freerslot(k,r_slot); rc = (ack(k, k->r_seq, (UCHAR *)0) == X_OK) ? X_DONE : X_ERROR; } else { epkt("Unexpected packet type",k); rc = X_ERROR; } freerslot(k,r_slot); return(rc); case R_ATTR: /* Want A or D packet */ if (t == 'A') { /* Attribute packet */ #ifdef F_AT gattr(p, r); /* Read the attributes */ #endif /* F_AT */ freerslot(k,r_slot); ack(k, k->r_seq, (UCHAR *) "Y"); /* Always accept the file */ return(X_OK); } else if (t == 'D') { /* First data packet */ k->obufpos = 0; /* Initialize output buffer */ if ((rc = (*(k->openf))(r->filename, 2, k)) == X_OK) { k->state = R_DATA; /* Switch to Data state */ rc = decode(k, r, 1, p); /* Write out first data packet */ freerslot(k,r_slot); } else { epkt("File refused or can't be opened", k); freerslot(k,r_slot); return(rc); } if (rc == X_OK) rc = ack(k, k->r_seq, (UCHAR *)0); else epkt("Error writing data", k); return(rc); } else { epkt("Unexpected packet type",k); return(X_ERROR); } break; case R_DATA: /* Want a D or Z packet */ if (t == 'D') { /* Data */ rc = decode(k, r, 1, p); /* Decode it */ freerslot(k,r_slot); } else if (t == 'Z') { /* End of file */ if (k->obufpos > 0) { /* Flush output buffer */ rc = writefile(k,k->obuf,k->obufpos); k->obufpos = 0; } if (((rc = (*(k->closef))(*p,2)) == X_OK) && (rc == X_OK)) k->state = R_FILE; freerslot(k,r_slot); } else { epkt("Unexpected packet type",k); return(X_ERROR); } if (rc == X_OK) rc = ack(k, k->r_seq, (UCHAR *)0); else epkt(t == 'Z' ? "Can't close file" : "Error writing data",k); return(rc); case R_ERROR: /* Canceled from above */ default: epkt(msg,k); return(X_ERROR); } } /* Utility routines */ UCHAR * getrslot(struct k_data *k, short *n) { /* Find a free packet buffer */ register int i; /* Note: We don't clear the retry count here. It is cleared only after the NEXT packet arrives, which indicates that the other Kermit got our ACK for THIS packet. */ for (i = 0; i < P_WSLOTS; i++) { /* Search */ if (k->ipktinfo[i].len < 1) { *n = i; /* Slot number */ k->ipktinfo[i].len = -1; /* Mark it as allocated but not used */ k->ipktinfo[i].seq = -1; k->ipktinfo[i].typ = SP; /* k->ipktinfo[i].rtr = 0; */ /* (see comment above) */ k->ipktinfo[i].dat = (UCHAR *)0; return(&(k->ipktbuf[0][i])); } } *n = -1; return((UCHAR *)0); } void /* Initialize a window slot */ freerslot(struct k_data *k, short n) { k->ipktinfo[n].len = 0; /* Packet length */ #ifdef COMMENT k->ipktinfo[n].seq = 0; /* Sequence number */ k->ipktinfo[n].typ = (char)0; /* Type */ k->ipktinfo[n].rtr = 0; /* Retry count */ k->ipktinfo[n].flg = 0; /* Flags */ #endif /* COMMENT */ } UCHAR * getsslot(struct k_data *k, short *n) { /* Find a free packet buffer */ #ifdef COMMENT register int i; for (i = 0; i < P_WSLOTS; i++) { /* Search */ if (k->opktinfo[i].len < 1) { *n = i; /* Slot number */ k->opktinfo[i].len = -1; /* Mark it as allocated but not used */ k->opktinfo[i].seq = -1; k->opktinfo[i].typ = SP; k->opktinfo[i].rtr = 0; k->opktinfo[i].dat = (UCHAR *)0; return(&(k->opktbuf[0][i])); } } *n = -1; return((UCHAR *)0); #else *n = 0; return(k->opktbuf); #endif /* COMMENT */ } void /* Initialize a window slot */ freesslot(struct k_data * k, short n) { k->opktinfo[n].len = 0; /* Packet length */ k->opktinfo[n].seq = 0; /* Sequence number */ k->opktinfo[n].typ = (char)0; /* Type */ k->opktinfo[n].rtr = 0; /* Retry count */ k->opktinfo[n].flg = 0; /* Flags */ } /* C H K 1 -- Compute a type-1 Kermit 6-bit checksum. */ int chk1(UCHAR *pkt) { register unsigned int chk; chk = chk2(pkt); chk = (((chk & 0300) >> 6) + chk) & 077; return((int) chk); } /* C H K 2 -- Numeric sum of all the bytes in the packet, 12 bits. */ unsigned int chk2(UCHAR *pkt) { register long chk; register unsigned int m; for (chk = 0; *pkt != '\0'; pkt++) chk += *pkt; return((unsigned int) (chk & 07777)); } /* C H K 3 -- Compute a type-3 Kermit block check. */ /* Calculate the 16-bit CRC-CCITT of a null-terminated string using a lookup table. Assumes the argument string contains no embedded nulls. */ unsigned int chk3(UCHAR *pkt, struct k_data * k) { register long c, crc; for (crc = 0; *pkt != '\0'; pkt++) { c = crc ^ (long)(*pkt); crc = (crc >> 8) ^ (k->crcta[(c & 0xF0) >> 4] ^ k->crctb[c & 0x0F]); } return((unsigned int) (crc & 0xFFFF)); } /* S P K T -- Send a packet. */ /* Call with packet type, sequence number, data length, data, Kermit struct. Returns: X_OK on success X_ERROR on i/o error */ int spkt(char typ, short seq, int len, UCHAR * data, struct k_data * k) { unsigned int crc; /* For building CRC */ int i, j, lenpos, m, n, x; /* Workers */ UCHAR * s, * buf; if (len < 0) { /* Calculate data length ourselves? */ len = 0; s = data; while (*s++) len++; } buf = k->opktbuf; /* Where to put packet (FOR NOW) */ i = 0; /* Packet buffer position */ buf[i++] = k->s_soh; /* SOH */ lenpos = i++; /* Remember this place */ buf[i++] = tochar(seq); /* Sequence number */ buf[i++] = typ; /* Packet type */ j = len + k->bct; if ((len + k->bct + 2) > 94) { /* If long packet */ buf[lenpos] = tochar(0); /* Put blank in LEN field */ buf[i++] = tochar(j / 95); /* Make extended header: Big part */ buf[i++] = tochar(j % 95); /* and small part of length. */ buf[i] = NUL; /* Terminate for header checksum */ buf[i++] = tochar(chk1(&buf[lenpos])); /* Insert header checksum */ } else { /* Short packet */ buf[lenpos] = tochar(j+2); /* Single-byte length in LEN field */ } if (data) /* Copy data, if any */ for ( ; len--; i++) buf[i] = *data++; buf[i] = '\0'; switch (k->bct) { /* Add block check */ case 1: /* 1 = 6-bit chksum */ buf[i++] = tochar(chk1(&buf[lenpos])); break; case 2: /* 2 = 12-bit chksum */ j = chk2(&buf[lenpos]); buf[i++] = (unsigned)tochar((j >> 6) & 077); buf[i++] = (unsigned)tochar(j & 077); break; case 3: /* 3 = 16-bit CRC */ crc = chk3(&buf[lenpos],k); buf[i++] = (unsigned)tochar(((crc & 0170000)) >> 12); buf[i++] = (unsigned)tochar((crc >> 6) & 077); buf[i++] = (unsigned)tochar(crc & 077); break; } buf[i++] = k->s_eom; /* Packet terminator */ buf[i] = '\0'; /* String terminator */ k->s_seq = seq; /* Remember sequence number */ k->opktlen = i; /* Remember length for retransmit */ #ifdef DEBUG /* CORRUPT THE PACKET SENT BUT NOT THE ONE WE SAVE */ if (xerror()) { UCHAR p[P_PKTLEN+8]; strncpy((char *)p,(const char *)buf,P_PKTLEN); p[i-2] = 'X'; #ifdef DEBUG if (debug) fprintf(dp,"XPKT[%s]\n",&p[1]); #endif /* DEBUG */ return((*(k->txd))(p,k->opktlen,k->parity)); /* Send it. */ } #endif /* DEBUG */ #ifdef DEBUG if (debug) fprintf(dp,"SPKT[%s]\n",&buf[1]); #endif /* DEBUG */ return((*(k->txd))(buf,k->opktlen,k->parity)); /* Send it. */ } /* N A K -- Send a NAK (negative acknowledgement) */ int nak(struct k_data * k, short seq, short slot) { int rc; rc = spkt('N', seq, 0, (UCHAR *)0, k); if (k->ipktinfo[slot].rtr++ > k->retry) rc = X_ERROR; return(rc); } /* A C K -- Send an ACK (positive acknowledgement) */ int ack(struct k_data * k, short seq, UCHAR * text) { int len, rc; len = 0; if (text) { /* Get length of data */ UCHAR *p; p = text; for ( ; *p++; len++) ; } rc = spkt('Y', seq, len, text, k); /* Send the packet */ if (rc == X_OK) /* If OK */ k->r_seq = (k->r_seq + 1) % 64; /* bump the packet number */ return(rc); } /* S P A R -- Set parameters requested by other Kermit */ void spar(struct k_data * k, UCHAR *s, int datalen) { int x, y; UCHAR c; s--; /* Line up with field numbers. */ if (datalen >= 1) /* Max packet length to send */ k->s_maxlen = xunchar(s[1]); if (datalen >= 2) /* Timeout on inbound packets */ k->r_timo = xunchar(s[2]); /* No padding */ if (datalen >= 5) /* Outbound Packet Terminator */ k->s_eom = xunchar(s[5]); if (datalen >= 6) /* Incoming control prefix */ k->r_ctlq = s[6]; if (datalen >= 7) { /* 8th bit prefix */ k->ebq = s[7]; if ((s[7] > 32 && s[7] < 63) || (s[7] > 95 && s[7] < 127)) { if (!k->parity) /* They want it */ k->parity = 1; /* Set parity to something nonzero */ k->ebqflg = 1; } else if (s[7] == 'Y' && k->parity) { k->ebqflg = 1; k->ebq = '&'; } else if (s[7] == 'N') { /* WHAT? */ } } if (datalen >= 8) { /* Block check */ k->bct = s[8] - '0'; if ((k->bct < 1) || (k->bct > 3)) k->bct = 1; } if (datalen >= 9) { /* Repeat counts */ if ((s[9] > 32 && s[9] < 63) || (s[9] > 95 && s[9] < 127)) { k->rptq = s[9]; k->rptflg = 1; } } if (datalen >= 10) { /* Capability bits */ x = xunchar(s[10]); #ifdef F_LP /* Long packets */ if (!(x & CAP_LP)) #endif /* F_LP */ k->capas &= ~CAP_LP; #ifdef F_SW /* Sliding Windows */ if (!(x & CAP_SW)) #endif /* F_SW */ k->capas &= ~CAP_SW; #ifdef F_AT /* Attributes */ if (!(x & CAP_AT)) #endif /* F_AT */ k->capas &= ~CAP_AT; #ifdef F_RS /* Recovery */ if (!(x & CAP_RS)) #endif /* F_RS */ k->capas &= ~CAP_RS; #ifdef F_LS /* Locking shifts */ if (!(x & CAP_LS)) #endif /* F_LS */ k->capas &= ~CAP_LS; /* In case other Kermit sends addt'l capas fields ... */ for (y = 10; (xunchar(s[y]) & 1) && (datalen >= y); y++) ; } #ifdef F_LP /* Long Packets */ if (k->capas & CAP_LP) { if (datalen > y+1) { x = xunchar(s[y+2]) * 95 + xunchar(s[y+3]); k->s_maxlen = (x > P_PKTLEN) ? P_PKTLEN : x; if (k->s_maxlen < 10) k->s_maxlen = 60; } } #endif /* F_LP */ #ifdef F_SW if (k->capas & CAP_SW) { if (datalen > y) { x = xunchar(s[y+1]); k->window = (x > P_WSLOTS) ? P_WSLOTS : x; if (k->window < 1) /* Watch out for bad negotiation */ k->window = 1; if (k->window > 1) if (k->window > k->retry) /* Retry limit must be greater */ k->retry = k->window + 1; /* than window size. */ } } #endif /* F_SW */ } /* R P A R -- Send my parameters to other Kermit */ int rpar(struct k_data * k, char type) { UCHAR *d; short b; int rc, len; d = k->ack_s; /* Where to put it */ d[ 0] = tochar(94); /* Maximum short-packet length */ d[ 1] = tochar(k->s_timo); /* When I want to be timed out */ d[ 2] = tochar(0); /* How much padding I need */ d[ 3] = ctl(0); /* Padding character I want */ d[ 4] = tochar(k->r_eom); /* End-of message character I want */ d[ 5] = k->s_ctlq; /* Control prefix I send */ if ((k->ebq == 'Y') && (k->parity)) /* 8th-bit prefix */ d[ 6] = k->ebq = '&'; /* I need to request it */ else /* else just agree with other Kermit */ d[ 6] = k->ebq; d[ 7] = k->bct + '0'; /* Block check type */ d[ 8] = k->rptq; /* Repeat prefix */ d[ 9] = tochar(k->capas); /* Capability bits */ d[10] = tochar(k->window); /* Window size */ #ifdef F_LP d[11] = tochar(k->r_maxlen / 95); /* Long packet size, big part */ d[12] = tochar(k->r_maxlen % 95); /* Long packet size, little part */ d[13] = '\0'; /* Terminate the init string */ len = 13; #else d[11] = '\0'; len = 11; #endif /* F_LP */ b = k->bct; k->bct = 1; /* Always use block check type 1 */ switch (type) { case 'Y': /* This is an ACK for packet 0 */ rc = ack(k,0,d); break; case 'S': /* It's an S packet */ rc = spkt('S', 0, len, d, k); break; default: rc = -1; } k->bct = b; return(rc); /* Pass along return code. */ } /* D E C O D E -- Decode data field of Kermit packet - binary mode only */ /* Call with: k = kermit data structure r = kermit response structure f = function code 0 = decode filename 1 = decode file data inbuf = pointer to packet data to be decoded Returns: X_OK on success X_ERROR if output function fails */ int decode(struct k_data * k, struct k_response * r, short f, UCHAR *inbuf) { register unsigned int a, a7; /* Current character */ unsigned int b8; /* 8th bit */ int rpt; /* Repeat count */ UCHAR *p; rpt = 0; /* Initialize repeat count. */ if (f == 0) /* Output function... */ p = r->filename; while ((a = *inbuf++ & 0xFF) != '\0') { /* Character loop */ if (k->rptflg && a == k->rptq) { /* Got a repeat prefix? */ rpt = xunchar(*inbuf++ & 0xFF); /* Yes, get the repeat count, */ a = *inbuf++ & 0xFF; /* and get the prefixed character. */ } b8 = 0; /* 8th-bit value */ if (k->parity && (a == k->ebq)) { /* Have 8th-bit prefix? */ b8 = 0200; /* Yes, flag the 8th bit */ a = *inbuf++ & 0x7F; /* and get the prefixed character. */ } if (a == k->r_ctlq) { /* If control prefix, */ a = *inbuf++ & 0xFF; /* get its operand */ a7 = a & 0x7F; /* and its low 7 bits. */ if ((a7 >= 0100 && a7 <= 0137) || a7 == '?') /* Controllify */ a = ctl(a); /* if in control range. */ } a |= b8; /* OR in the 8th bit */ if (rpt == 0) rpt = 1; /* If no repeats, then one */ for (; rpt > 0; rpt--) { /* Output the char 'rpt' times */ if (f == 0) { *p++ = (UCHAR) a; /* to memory */ } else { /* or to file */ int rc; /* buffered for efficiency */ k->obuf[k->obufpos++] = (UCHAR) a; /* Deposit the byte */ if (k->obufpos == k->obuflen) { /* Buffer full? */ rc = writefile(k,k->obuf,k->obuflen); /* Dump it. */ k->obufpos = 0; } } } } if (f == 0) /* If writing to memory */ *p = '\0'; /* terminate the string */ return(X_OK); } ULONG /* Convert decimal string to number */ stringnum(UCHAR *s) { long n; n = 0L; while (*s == SP) s++; while(*s >= '0' && *s <= '9') n = n * 10 + (*s++ - '0'); return(n); } UCHAR * numstring(ULONG n, UCHAR * buf, int buflen) { /* Convert number to string */ int k, x; buf[buflen - 1] = '\0'; for (k = buflen - 2; k > 0; k--) { x = n % 10L; buf[k] = x + '0'; n /= 10L; if (!n) break; } if (n) { #ifdef DEBUG if (debug) fprintf(dp,"numstring n=%d buf=[%s]\n",n,&buf[k]); #endif /* DEBUG */ return((UCHAR *)0); } if (k > 0) { UCHAR * p, * s; s = &buf[k]; p = buf; while ((*p++ = *s++)) ; *(p-1) = '\0'; } #ifdef DEBUG if (debug) fprintf(dp,"numstring final buf=[%s]\n",buf); #endif /* DEBUG */ return((UCHAR *)buf); } #ifdef F_AT /* G A T T R -- Read incoming attributes */ #define SIZEBUFL 32 /* For number conversions */ void gattr(UCHAR * s, struct k_response * r) { long fsize, fsizek; /* File size */ UCHAR c; /* Workers */ int aln, i; UCHAR sizebuf[SIZEBUFL]; r->filedate[0] = '\0'; /* Initialize results */ r->filesize = -1L; while (c = *s++) { /* Get attribute tag */ aln = xunchar(*s++); /* Length of attribute string */ switch (c) { case '!': /* File length in K */ for (i = 0; (i < aln) && (i < SIZEBUFL); i++) /* Copy it */ sizebuf[i] = *s++; sizebuf[i] = '\0'; /* Terminate with null */ if (i < aln) s += (aln - i); /* If field was too long for buffer */ fsizek = stringnum(sizebuf); /* Convert to number */ break; case '#': /* File creation date */ for (i = 0; (i < aln) && (i < DATE_MAX); i++) r->filedate[i] = *s++; /* Copy it into a static string */ if (i < aln) s += (aln - i); r->filedate[i] = '\0'; break; case '1': /* File length in bytes */ for (i = 0; (i < aln) && (i < SIZEBUFL); i++) /* Copy it */ sizebuf[i] = *s++; sizebuf[i] = '\0'; /* Terminate with null */ if (i < aln) s += (aln - i); fsize = stringnum(sizebuf); /* Convert to number */ break; } } if (fsize > -1L) { /* Remember the file size */ r->filesize = fsize; } else if (fsizek > -1L) { r->filesize = fsizek * 1024L; } #ifdef DEBUG if (debug) { fprintf(dp,"gattr r->filesize=%ld\n",r->filesize); fprintf(dp,"gattr r->filedate=[%s]\n",r->filedate); } #endif /* DEBUG */ } #endif /* F_AT */ #define ATTRLEN 48 int sattr(struct k_data *k) { /* Build and send A packet */ int i, x, aln; short filetype; long filelength; UCHAR datebuf[24], * p; filetype = k->binary; filelength = fileinfo(k->sendfile,datebuf,24,&filetype,k->xfermode); k->binary = filetype; #ifdef DEBUG if (debug) { fprintf(dp,"sattr filename: %s\n",k->sendfile); fprintf(dp,"sattr filelength: %ld\n",filelength); fprintf(dp,"sattr binary: %d\n",k->binary); } #endif /* DEBUG */ i = 0; k->xdata[i++] = '"'; if (k->binary) { /* Binary */ k->xdata[i++] = tochar(2); /* Two characters */ k->xdata[i++] = 'B'; /* B for Binary */ k->xdata[i++] = '8'; /* 8-bit bytes (note assumption...) */ } else { /* Text */ k->xdata[i++] = tochar(3); /* Three characters */ k->xdata[i++] = 'A'; /* A = (extended) ASCII with CRLFs */ k->xdata[i++] = 'M'; /* M for carriage return */ k->xdata[i++] = 'J'; /* J for linefeed */ k->xdata[i++] = '*'; /* Encoding */ k->xdata[i++] = tochar(1); /* Length of value is 1 */ k->xdata[i++] = 'A'; /* A for ASCII */ } if (filelength > -1L) { /* File length in bytes */ UCHAR lenbuf[16]; p = numstring(filelength,lenbuf,16); #ifdef DEBUG if (debug) fprintf(dp,"sattr numstring: [%s]\n",p); #endif /* DEBUG */ if (p) { for (x = 0; p[x]; x++) ; /* Get length of length string */ if (i + x < ATTRLEN - 3) { /* Don't overflow buffer */ k->xdata[i++] = '1'; /* Length-in-Bytes attribute */ k->xdata[i++] = tochar(x); while (*p) k->xdata[i++] = *p++; } } } if (datebuf[0]) { /* File modtime */ p = datebuf; for (x = 0; p[x]; x++) ; /* Length of modtime */ if (i + x < ATTRLEN - 3) { /* If it will fit */ k->xdata[i++] = '#'; /* Add modtime attribute */ k->xdata[i++] = tochar(x); /* Its length */ while (*p) /* And itself */ k->xdata[i++] = *p++; } } k->xdata[i++] = '@'; /* End of Attributes */ k->xdata[i++] = ' '; k->xdata[i] = '\0'; /* Terminate attribute string */ #ifdef DEBUG if (debug) fprintf(dp,"sattr k->xdata: [%s]\n",k->xdata); #endif /* DEBUG */ return(spkt('A',k->s_seq,-1,k->xdata,k)); } int getpkt(struct k_data *k) { /* Fill a packet from file */ int i, next, rpt, maxlen; static int c; /* PUT THIS IN STRUCT */ #ifdef DEBUG if (debug) { fprintf(dp,"getpkt k->s_first: %d\n",k->s_first); fprintf(dp,"getpkt k->s_remain=[%s]\n",k->s_remain); } #endif /* DEBUG */ maxlen = k->s_maxlen - 4; if (k->s_first == 1) { /* If first time thru... */ k->s_first = 0; /* don't do this next time, */ k->s_remain[0] = '\0'; /* discard any old leftovers. */ if (k->istring) { /* Get first byte. */ c = *(k->istring)++; /* Of memory string... */ if (!c) c = -1; } else { /* or file... */ c = zgetc(); } if (c < 0) { /* Watch out for empty file. */ #ifdef DEBUG if (debug) fprintf(dp,"getpkt first c=%d\n",c); #endif /* DEBUG */ k->s_first = -1; return(k->size = 0); } #ifdef DEBUG if (debug) fprintf(dp,"getpkt first c='%c'\n",(char)c); #endif /* DEBUG */ } else if (k->s_first == -1 && !k->s_remain[0]) { /* EOF from last time? */ return(k->size = 0); } for (k->size = 0; (k->xdata[k->size] = k->s_remain[k->size]) != '\0'; (k->size)++) ; k->s_remain[0] = '\0'; if (k->s_first == -1) return(k->size); rpt = 0; /* Initialize repeat counter. */ while (k->s_first > -1) { /* Until end of file or string... */ if (k->istring) { next = *(k->istring)++; if (!next) next = -1; } else { next = zgetc(); } #ifdef COMMENT #ifdef DEBUG if (debug) { if (next < 0) fprintf(dp,"getpkt next c=%d\n",c); else if (debug) fprintf(dp,"getpkt next c='%c'\n",c); } #endif /* DEBUG */ #endif /* COMMENT */ if (next < 0) k->s_first = -1; /* If none, we're at EOF. */ k->osize = k->size; /* Remember current size. */ encode(c,next,k); /* Encode the character. */ /* k->xdata[k->size] = '\0'; */ c = next; /* Old next char is now current. */ if (k->size == maxlen) /* Just at end, done. */ return(k->size); if (k->size > maxlen) { /* Past end, must save some. */ for (i = 0; (k->s_remain[i] = k->xdata[(k->osize)+i]) != '\0'; i++) ; k->size = k->osize; k->xdata[k->size] = '\0'; return(k->size); /* Return size. */ } } return(k->size); /* EOF, return size. */ } int sdata(struct k_data *k) { /* Send a data packet */ int len, rc; #ifdef COMMENT if (cx || cz) { /* Interruption not implmented yet */ if (debug) fprintf(db,"sdata interrupted cx = %d cz = %d\n",cx,cz); failure = 1; return(0); } #endif /* COMMENT */ len = getpkt(k); /* Fill data field from input file */ #ifdef DEBUG if (debug) fprintf(dp,"sdata getpkt = %d\n",len); #endif /* DEBUG */ if (len < 1) return(0); rc = spkt('D',k->s_seq,len,k->xdata,k); /* Send the packet */ #ifdef DEBUG if (debug) fprintf(dp,"sdata spkt = %d\n",rc); #endif /* DEBUG */ return((rc == X_ERROR) ? rc : len); } /* E P K T -- Send a (fatal) Error packet with the given message */ void epkt(char * msg, struct k_data * k) { k->bct = 1; (void) spkt('E', 0, -1, (UCHAR *) msg, k); } int encstr(UCHAR * s, struct k_data * k) { /* Fill a packet from string s. */ k->s_first = 1; /* Start lookahead. */ k->istring = s; /* Set input string pointer */ getpkt(k); /* Fill a packet */ k->istring = (UCHAR *)0; /* Reset input string pointer */ k->s_first = 1; /* "Rewind" */ return(k->size); /* Return data field length */ } /* Decode packet data into a string */ void decstr(UCHAR * s, struct k_data * k, struct k_response * r) { k->ostring = s; /* Set output string pointer */ (void) decode(k, r, 0, s); *(k->ostring) = '\0'; /* Terminate with null */ k->ostring = (UCHAR *)0; /* Reset output string pointer */ } void encode(int a, int next, struct k_data * k) { /* Encode character into packet */ int a7, b8, maxlen; maxlen = k->s_maxlen - 4; if (k->rptflg) { /* Doing run-length encoding? */ if (a == next) { /* Yes, got a run? */ if (++(k->s_rpt) < 94) { /* Yes, count. */ return; } else if (k->s_rpt == 94) { /* If at maximum */ k->xdata[(k->size)++] = k->rptq; /* Emit prefix, */ k->xdata[(k->size)++] = tochar(k->s_rpt); /* and count, */ k->s_rpt = 0; /* and reset counter. */ } } else if (k->s_rpt == 1) { /* Run broken, only two? */ k->s_rpt = 0; /* Yes, do the character twice */ encode(a,-1,k); /* by calling self recursively. */ if (k->size <= maxlen) /* Watch boundary. */ k->osize = k->size; k->s_rpt = 0; /* Call self second time. */ encode(a,-1,k); return; } else if (k->s_rpt > 1) { /* Run broken, more than two? */ k->xdata[(k->size)++] = k->rptq; /* Yes, emit prefix and count */ k->xdata[(k->size)++] = tochar(++(k->s_rpt)); k->s_rpt = 0; /* and reset counter. */ } } a7 = a & 127; /* Get low 7 bits of character */ b8 = a & 128; /* And "parity" bit */ if (k->ebqflg && b8) { /* If doing 8th bit prefixing */ k->xdata[(k->size)++] = k->ebq; /* and 8th bit on, insert prefix */ a = a7; /* and clear the 8th bit. */ } if (a7 < 32 || a7 == 127) { /* If in control range */ k->xdata[(k->size)++] = k->s_ctlq; /* insert control prefix */ a = ctl(a); /* and make character printable. */ } else if (a7 == k->s_ctlq) /* If data is control prefix, */ k->xdata[(k->size)++] = k->s_ctlq; /* prefix it. */ else if (k->ebqflg && a7 == k->ebq) /* If doing 8th-bit prefixing, */ k->xdata[(k->size)++] = k->s_ctlq; /* ditto for 8th-bit prefix. */ else if (k->rptflg && a7 == k->rptq) /* If doing run-length encoding, */ k->xdata[(k->size)++] = k->s_ctlq; /* ditto for repeat prefix. */ k->xdata[(k->size)++] = a; /* Finally, emit the character. */ k->xdata[(k->size)] = '\0'; /* Terminate string with null. */ #ifdef COMMENT #ifdef DEBUG if (debug) fprintf(dp,"encode = [%s]\n",k->xdata); #endif /* DEBUG */ #endif /* COMMENT */ } int nxtpkt(struct k_data * k) { /* Get next packet to send */ #ifdef COMMENT short s_slot; k->s_seq = (k->s_seq + 1) & 63; /* Next sequence number */ k->xdata = getsslot(k,&s_slot); return(s_slot); #else k->s_seq = (k->s_seq + 1) & 63; /* Next sequence number */ k->xdata = k->xdatabuf; return(0); #endif /* COMMENT */ } #ifdef DEBUG int xerror() { unsigned int x; if (!errorrate) return(0); x = rand() % 100; if (debug) fprintf(dp,"RANDOM=%d ERROR=%d\n",x,(x < errorrate)); return(x < errorrate); } #endif /* DEBUG */ int resend(struct k_data * k) { UCHAR * buf; if (!k->opktlen) /* Nothing to resend */ return(X_OK); buf = k->opktbuf; #ifdef DEBUG if (debug) fprintf(dp,">PKT[%s]\n",&buf[1]); #endif /* DEBUG */ return((*(k->txd))(buf,k->opktlen,k->parity)); }