22-Sep-82 23:52:22-EDT,1062;000000000001 Mail-from: ARPANET site UDEL-RELAY rcvd at 22-Sep-82 2352-EDT Date: 22 Sep 1982 17:26:57-EDT From: alison.cornell at UDel-Relay To: arpa.catchings at Cmu-20c Subject: kermit source Via: Cornell; 22 Sep 82 23:40-EDT kermit is the executable kermit program *.c is c program text *.h are the include files for the .c's mainsrc is an archive containing main.c, parms.h, globals.h, and kerm.h (all those files necessary for compiling main.c) klib is an archive containing the object files of all .c's other than main.c BUGS is a list of known bugs at the time of distribution. there is a separate terminal emulator progam which kermit execs; this must be present and called termemul. source is in termemul.c the makefile will recompile it if it gets changed. there is a driver, called tedriver.c, which can be used to debug the terminal emulator if it needs to get changed (probable, since it depends on the communications/host environment). make with no arguments will recompile as necessary and create a new kermit. 22-Sep-82 23:53:13-EDT,3127;000000000000 Mail-from: ARPANET site UDEL-RELAY rcvd at 22-Sep-82 2352-EDT Date: 22 Sep 1982 17:29:09-EDT From: alison.cornell at UDel-Relay To: arpa.catchings at Cmu-20c Subject: Kermit source Via: Cornell; 22 Sep 82 23:40-EDT 9/20/82 alison brown cornell decvax!cornell!alison alison.cornell@udel The command processor has severe brain damage, i.e. only the first n characters of each kermit command are looked at, where n is what is required to disambiguate the command. This means that any following characters are ignored and thus some very strange commands will work. After file transmission is complete it takes quite a while for Kermit to return to command mode. This is innoucuous in that it doesn't do any damage except to the patience of the user, but it is really due to a bug in the handling of a break packet after end-of-file so it can be fixed. SOURCE = main.c commands.c fsa.c globals.c io.c kermit.c tools.c termemul.c FILES = kermitsrc globals tools io fsa commands HEADERS1 = commands.h globals.h parms.h ptypes.h set_parms.h states.h status.h HEADERS2 = retcode.h kermitx: termemul kermit @echo kermit made kermit : main.o klib cc -o kermit main.o klib main.o : mainsrc cc -c main.c 2>main.err mainsrc : main.c parms.h globals.h kerm.h ar ruv mainsrc main.c parms.h globals.h kerm.h netcp mainsrc p:mainsrc klib : $(FILES) netcp klib p:klib ranlib klib kermitsrc : kermit.c status.h retcode.h commands.h parms.h set_parms.h cc -c kermit.c 2>kermit.err ar ruv klib kermit.o rm kermit.o touch kermitsrc globals : globals.c states.h parms.h cc -c globals.c 2>globals.err ar ruv klib globals.o rm globals.o touch globals tools : tools.c globals.h parms.h status.h cc -c tools.c 2>tools.err ar ruv klib tools.o rm tools.o touch tools io : io.c parms.h globals.h cc -c io.c 2>io.err ar ruv klib io.o rm io.o touch io fsa : fsa.c $(HEADERS1) cc -c fsa.c 2>fsa.err ar ruv klib fsa.o rm fsa.o touch fsa commands : commands.c $(HEADERS1) cc -c commands.c 2>commands.err ar ruv klib commands.o rm commands.o touch commands globals.h : globals.c @echo "Remember: changes to globals.c must be reflected in" @echo "globals.h !!!" touch globals.h termemul: termemul.c cc -o termemul termemul.c listings: $(SOURCE) $(HEADERS1) $(HEADERS2) print $? touch listings 01..........1..........2.........3..........4.........5 02..........1..........2.........3..........4.........5 03..........1..........2.........3..........4 04..........1..........2.........3 05..........1..........2 06..........1 07..........1..........2 08..........1..........2.........3 09..........1..........2.........3..........4 10..........1..........2.........3..........4.........5 11..........1..........2...... ...3..........4.........5 12..........1 13..........1..........2 14..........1..........2.........3 15..........1..........2.........3..........4 16..........1..........2.........3..........4.........5.........6.........7 17..........1 23-Sep-82 00:21:18-EDT,99558;000000000000 Mail-from: ARPANET site UDEL-RELAY rcvd at 23-Sep-82 0009-EDT Date: 22 Sep 1982 17:31:36-EDT From: alison.cornell at UDel-Relay To: arpa.catchings at Cmu-20c, ermemul.c.cornell at UDel-Relay Subject: Kermit source Via: Cornell; 22 Sep 82 23:41-EDT /********************************************************/ /* */ /* Kermit file transfer program */ /* */ /* system: Berkeley 4.1 (VAX UNIX) */ /* Cornell University */ /* */ /* date: May 27, 1982 */ /* */ /* programmer: Dean Jagels */ /* modified by Alison Brown September 1982 */ /* */ /********************************************************/ /********************************************************/ /* */ /* global data definitions for kermit */ /* */ /* any changes here must be reflected in globals.h */ /********************************************************/ #include "states.h" #include "parms.h" #include "kerm.h" #include struct packetform { char length; /* packet length */ char seqno; /* packet number */ char ptype; /* packet type */ char data[MAX_DATALEN]; char chksum[MAXCHKLEN]; /* checksum characters */ int trycount; /* num of attempts at sending this packet */ }; /* Link Information */ int localprot = FALSE, /* is this process local or remote ? */ link_active = FALSE, /* is the link active? (not in write-thru mode) */ last_packet = -1, /* seq num of the last packet built */ state = WRITE_THRU; /* automaton state */ debug = FALSE; /* running debug mode ? */ int lnkfd; /* file descriptor for comm line */ /* Link Parameters (set by set command) */ int lnkstat = CLOSED, /* =OPEN or =CLOSED */ lnkduplex = FULL; /* comm line is FUll or HALF duplex */ char lnkspeed = B9600; char lnkname[NAMELEN] = "/dev/develcon"; int outsize = MAX_SIZE, /* max length of packets */ insize = MAX_SIZE, outdatamax = MAX_DATALEN, /* max length of data in packets */ indatamax = MAX_DATALEN, chksumout = CHKSUMLEN, /* num of chars for checksum */ chksumin = CHKSUMLEN, padout = NUMPAD, /* num of pad chars in each direction */ padin = NUMPAD, maxtry = MAX_TRY, /* max number of times to retry a packet */ stry_cnt = 0, /* number of major send errors */ rtry_cnt = 0, /* number of major receive errors */ nak_cnt = 0; /* number of naks received */ unsigned waittime= WAITTIME; /* timeout interval */ char padchout= PADCH, /* pad characters */ padchin = PADCH, eopout = EOPOUT, /* end of packet chars */ eopin = EOPIN, quoteout= QUOTE, /* quoting chars */ quotein = QUOTE; synchar = SYNCHAR; /* marks begin of packet */ *link_stat; /* textual message of link status */ /* packet storage */ int msg_waiting = FALSE; /* is there a message in pktbuffer? */ struct packetform outpkt, /* outgoing packet */ inpkt, /* incoming packet */ outparms_pkt, /* parameters I request: send_init or ack */ inparms_pkt, /* parameters requested by other end */ pktbuffer, /* a copy of the latest data packet rcvd */ pktdelivery; /* packet delivery out of automaton */ /* A bit of explaination of the roles of inpkt and pktbuffer. inpkt is the space into which all new incoming packets are placed. These incoming packets may include ack, nak, and timeout notices. pktbuffer, on the other hand, holds only the latest information packet (data, filename, eof, or error). This is where you go when you have to re-acknowledge the latest data received. */ /* error messages */ char stry_msg[] = "too many line errors sending, or remote kermit not up"; char rtry_msg[] = "too many line errors receiving, or remote kermit not up"; char wth_msg[] = "write_thru: expecting send, send_init, rcv_init"; char rinit_msg[] = "rcv_init: received illegal packet from link"; char ml_msg[] = "main_loop: expecting send, receive, or break"; char ack_msg[] = "await_ack: received unexpected send_init"; char break_msg[] = "break_ack: received unexpected send_init"; char input_msg[] = "input: received unexpected send_init"; char abort_msg[] = "link aborted; must reset"; char send_msg[] = "error in sending on link"; char rcv_msg[] = "error in receiving from link"; char parm_msg[] = "error in incoming link parameters"; char rrcv_msg[] = "rcv_data: remote cannot rcv non-packet data"; char sdata_msg[] = "send_data: type must be one of F,D,Z,E"; char rsnd_msg[] = "send_data: remote cannot send non-packet data"; char uint_msg[] = " by user request"; /* flag to communicate to data link layer that user interrupted */ int user_interrupt = FALSE; int wait_prompt = FALSE; /* TRUE = wait for prompt between packets */ /********************************************************/ /* */ /* Kermit file transfer program */ /* */ /* system: Berkeley 4.1 (VAX UNIX) */ /* Cornell University */ /* */ /* date: May 27, 1982 */ /* */ /* programmer: Dean Jagels */ /* modified by Alison Brown September 1982 */ /* */ /********************************************************/ /* */ /* Kermit data link layer */ /* */ /********************************************************/ #include "ptypes.h" #include "set_parms.h" #include "parms.h" #include "comcodes.h" #include "states.h" #include "status.h" #include "retcode.h" #include "globals.h" #include "tools.h" #include "io.h" #include /*****/ /* /* write-thru: automaton state write_thru /* command: should be one of /* 'I' send information (unadorned mode) /* 'S' initialize sendind /* 'R' initialize receiving /* /*****/ char write_thru(command) char command; { char type; char message[MAX_DATALEN]; int num; int i; #ifdef DEBUG if (debug) fprintf(stderr, "write_thru: command %c\n", command); #endif switch (command) { case INIT_SEND: state = S_INIT; return(SUCCESS); case INIT_RCV: state = R_INIT; return(SUCCESS); case BREAKC: case RESET: /* ignore the command; its obviously redundant */ return (SUCCESS); default: link_stat = wth_msg; #ifdef DEBUG if (debug) fprintf(stderr, "%s\n", wth_msg); #endif return(ILLEGAL_REQUEST); } /* end switch */ } /* write_thru */ /*****/ /* /* state s_init /* assumes outparms_pkt contains the appropriate send_init /*****/ char s_init() { #ifdef DEBUG if (debug) fprintf(stderr, "s_init:\n"); #endif state = S_ACK; if (!xmit(&outparms_pkt)) { stry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = stry_msg; state = ABORT; return(ABORTING); } else return (SUCCESS); } /* s_init */ /*****/ /* /* state s_ack /* /*****/ char s_ack() { char *cptr; int i; int okay; #ifdef DEBUG if (debug) fprintf(stderr, "s_ack: entry\n"); #endif if (!rcv(&inparms_pkt)) { rtry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = rtry_msg; state = ABORT; return(ABORTING); } if (inparms_pkt.ptype _eq_ ACK) { state = MAIN_LOOP; pktcopy(&inparms_pkt, &pktbuffer); cptr = inparms_pkt.data; /* the following ugliness allows trailing parameter fields to be missing */ for (i=0; (*(cptr+i) != EOS) && (i<6) ; i++) { #ifdef DEBUG if (debug) fprintf(stderr, "i=%d, *(cptr+i)=%c, okay=%d\n", i, *(cptr+i), okay); #endif switch (i) { case 0: okay = set_parms(OUTPKTSIZE, *cptr, ctoi(*cptr)); break; case 1: waittime = MAX(waittime, ctoi( *(cptr+1)) ); break; case 2: okay = set_parms(OUTPAD, *(cptr+2), ctoi(*(cptr+2)) ) || okay; break; case 3: padchout = cshift( *(cptr+3) ); break; case 4: eopout = ctoi( *(cptr+4) ); break; case 5: quoteout = *(cptr+5); break; case 6: chksumout = ctoi( *(cptr+6) ); break; default: /* this had better never happen */ abort(); } /* end switch */ } /* for loop */ #ifdef DEBUG if (debug) fprintf(stderr, "s_ack: rcvd data %s\n", inparms_pkt.data); #endif if (!okay) { link_stat = parm_msg; state = ABORT; return(ABORTING); } } else state = S_INIT; return (SUCCESS); } /* s_ack */ /*****/ /* /* state r_init /* Assumes appropriate ack packet in outparms_pkt /*****/ char r_init() { char *cptr; int i; int okay; if (!rcv(&inparms_pkt)) { rtry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = rtry_msg; state = ABORT; return(ABORTING); } if (inparms_pkt.ptype _eq_ SEND_INIT) { if (!xmit(&outparms_pkt)) { stry_cnt++; if (user_interrupt) link_stat = uint_msg; link_stat = stry_msg; state = ABORT; return(ABORTING); } pktcopy( &inparms_pkt, &pktbuffer); state = MAIN_LOOP; cptr = inparms_pkt.data; /* the following ugliness allows trailing parameter fields to be missing */ for (i=0; (*(cptr+i) != EOS) && (i<6) ; i++) { #ifdef DEBUG if (debug) fprintf(stderr, "i=%d, *(cptr+i)=%c, okay=%d\n", i, *(cptr+i), okay); #endif switch (i) { case 0: okay = set_parms(OUTPKTSIZE, *cptr, ctoi(*cptr)); break; case 1: waittime = MAX(waittime, ctoi( *(cptr+1)) ); break; case 2: okay = set_parms(OUTPAD, *(cptr+2), ctoi(*(cptr+2)) ) || okay; break; case 3: padchout = cshift( *(cptr+3) ); break; case 4: eopout = ctoi( *(cptr+4) ); break; case 5: quoteout = *(cptr+5); break; case 6: chksumout = ctoi( *(cptr+6) ); break; default: /* this had better never happen */ abort(); } /* end switch */ } #ifdef DEBUG if (debug) fprintf(stderr, "r_init: data rcvd %s\n", inparms_pkt.data); #endif if (!okay) { link_stat = parm_msg; state = ABORT; return(ABORTING); } return(SUCCESS); } else { state = R_INIT; return (SUCCESS); } } /* r_init */ /*****/ /* /* state main_loop /* /*****/ char main_loop(command) char command; { #ifdef DEBUG if (debug) fprintf(stderr, "main_loop: command %c\n", command); #endif switch(command) { case SEND: if (!xmit(&outpkt)) { stry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = stry_msg; state = ABORT; return(ABORTING); } state = AWAIT_ACK; break; case BREAKC: case RESET: if (!xmit(&outpkt)) { stry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = stry_msg; #ifdef DEBUG if (debug) fprintf(stderr, "%s\n", stry_msg); #endif state = PRE_WRITE_THRU; return(FAILURE); } state = BREAK_ACK; break; case RCV: if (msg_waiting) pktcopy( &pktbuffer, &pktdelivery); else state = INPUT; msg_waiting = FALSE; break; default: link_stat = ml_msg; #ifdef DEBUG if (debug) fprintf(stderr, "%s\n", ml_msg); #endif return(ILLEGAL_REQUEST); } /* end switch */ return (SUCCESS); } /* main_loop */ /*****/ /* /* state await_ack /* /*****/ char await_ack() { #ifdef DEBUG if (debug) fprintf(stderr, "await_ack:"); #endif if (!rcv(&inpkt)) { rtry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = rtry_msg; state = ABORT; return(ABORTING); } #ifdef DEBUG if (debug) fprintf(stderr, "received type %c\n", inpkt.ptype); #endif switch (inpkt.ptype) { case ACK: state = MAIN_LOOP; break; case NAK: nak_cnt++; case TIMEOUT: if (!xmit(&outpkt)) { stry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = stry_msg; state = ABORT; return(ABORTING); } break; case BREAK: sendack(inpkt.seqno); state = PRE_WRITE_THRU; return (FAILURE); case FILE_HEAD: case DATA: case EOFILE: case ERROR: if (!msg_waiting) { sendack(inpkt.seqno); pktcopy(&inpkt, &pktbuffer); } else /* there's already a message in the buffer */ sendnak(pktbuffer.seqno); msg_waiting = TRUE; break; default: link_stat = ack_msg; state = ABORT; return(ABORTING); } /* end switch */ return (SUCCESS); } /* await_ack */ /*****/ /* /* state break_ack /* /*****/ char break_ack() { #ifdef DEBUG if (debug) fprintf(stderr, "break_ack:"); #endif if (!rcv(&inpkt)) { rtry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = rtry_msg; state = ABORT; return(ABORTING); } #ifdef DEBUG if (debug) fprintf(stderr, "rcvd type %c\n", inpkt.ptype); #endif switch (inpkt.ptype) { case ACK: state = PRE_WRITE_THRU; return(SUCCESS); case NAK: nak_cnt++; case TIMEOUT: break; case FILE_HEAD: case DATA: case EOFILE: case ERROR: if (!msg_waiting) { sendack(inpkt.seqno); pktcopy(&inpkt, &pktbuffer); } else /* there's already a message in the buffer */ sendnak(pktbuffer.seqno); msg_waiting = TRUE; break; case BREAK: sendack(inpkt.seqno); state = PRE_WRITE_THRU; return(SUCCESS); case SEND_INIT: link_stat = break_msg; state = ABORT; return(ABORTING); } /* end switch */ if (!xmit(&outpkt)) { stry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = stry_msg; state = ABORT; return(ABORTING); } return (SUCCESS); } /* break_ack */ /*****/ /* /* state input /* /*****/ char input() { #ifdef DEBUG if (debug) fprintf(stderr, "input:"); #endif if (!rcv(&inpkt)) { rtry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = rtry_msg; state = ABORT; return(ABORTING); } #ifdef DEBUG if (debug) fprintf(stderr, "input: rcvd type %c\n", inpkt.ptype); #endif switch (inpkt.ptype) { case NAK: nak_cnt++; case ACK: state = INPUT; break; case TIMEOUT: /* we'll try re-sending an ack for the latest rcvd */ if (pktbuffer.ptype _eq_ SEND_INIT) { if (!xmit( &outparms_pkt) ) { stry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = stry_msg; state = ABORT; return(ABORTING); } } else sendack(pktbuffer.seqno); break; case FILE_HEAD: case DATA: case EOFILE: case ERROR: sendack(inpkt.seqno); state = MAIN_LOOP; pktcopy( &inpkt, &pktbuffer); pktcopy( &inpkt, &pktdelivery); break; case BREAK: sendack(inpkt.seqno); state = PRE_WRITE_THRU; return(FAILURE); default: link_stat = input_msg; state = ABORT; return(ABORTING); } /* end switch */ return(SUCCESS); } /* input */ /*****/ /* /* automaton: step through the fsa guiding the protocol. /* command: one of /* 'D' send information packet (data, filename, eof, error) /* 'G' receive (get) an information packet /* 'B' break the link and go to write_thru mode /* 'S' initialize sending /* 'R' initialize receiving /* 'A' reset after abort /* /* NOTE: All pertinent packets (send initiate, ack for initialization, /* break, etc.) must be built and ready for output before entering /* the automaton. If the automaton were required to construct any /* packets, then the send or receive try counts could not be kept for /* those packets. (Creating a packet implies initializing trycount) /* /* Returns a link status: /* 'I' Illegal command /* 'A' Aborting. Due to persistent link errors, the link has /* been shut down. /* 'F' Failure. The command was not completed, and the link /* has gone into write_thru mode. Generally occurs /* whenever a break is received. Also, when executing /* a connect command, Failure is returned if no ack /* is received from the other end of the link. /* 'S' Success. The command was successfully completed. /* /* In addition, on return the global msg_waiting is set to TRUE if /* there is a message waiting for input. /* /*****/ char automaton(command) char command; { char status; #ifdef DEBUG if (debug) fprintf(stderr, "automaton: command %c\n", command); #endif fflush(stderr); inpkt.trycount = 0; inpkt.data[0] = EOS; do switch (state) { case PRE_WRITE_THRU: inlnkflush(); state = WRITE_THRU; break; case WRITE_THRU: status = write_thru(command); break; case S_INIT: status = s_init(); break; case S_ACK: status = s_ack(); break; case R_INIT: status = r_init(); break; case MAIN_LOOP: status = main_loop(command); break; case AWAIT_ACK: status = await_ack(); break; case BREAK_ACK: status = break_ack(); break; case INPUT: status = input(); break; case ABORT: { char tmp = EOS; char *dummy = &tmp; fillpacket(BREAK, dummy, 0, &outpkt, quoteout); main_loop (RESET); state = ABORT2; } break; case ABORT2: status = SUCCESS; state = WRITE_THRU; default: ; } /* end switch */ while (( state != MAIN_LOOP) && (state != WRITE_THRU) && (state != ABORT2 )); #ifdef DEBUG if (debug) fprintf(stderr, "automaton: returning state %d\n", state); #endif if (state _eq_ MAIN_LOOP) link_active = TRUE; else link_active = FALSE; if (state _eq_ ABORT2) { if (user_interrupt) link_stat = uint_msg; fprintf(stdout, "Aborting: %s\n", link_stat); } return (status); } /* automaton */ /********************************************************/ /* */ /* Kermit file transfer program */ /* */ /* system: Berkeley 4.1 (VAX UNIX) */ /* Cornell University */ /* */ /* date: May 27, 1982 */ /* */ /* programmer: Dean Jagels */ /* modified by Alison Brown September 1982 */ /* */ /********************************************************/ /* Kermit data link layer */ /* */ /********************************************************/ #include #include #include "comcodes.h" #include "ptypes.h" #include "states.h" #include "status.h" #include "retcode.h" #include "parms.h" #include "set_parms.h" #include "globals.h" #include "tools.h" #include #include #include char automaton(); /*****/ /* /* set_parms: set the link parameters /* pcode is a code for the parameter to be set. /* /* pvalue is a string containing the value to be given the parameter. /* If pvalue holds a number, the number must be integer. /* /* set_parms returns false if pcode is not recognized or if /* pvalue is unacceptable. /* /* set_parms cannot be called while the link is active. /* /*****/ int set_parms(pcode, pvaluec, pvaluei) int pcode; char pvaluec; int pvaluei; { int status = TRUE; #ifdef DEBUG if (debug) fprintf(stderr, "setparms: parm %d, valuec %c, valuei %d\n", pcode, pvaluec, pvaluei); #endif if (link_active) { #ifdef DEBUG if (debug) fprintf(stderr, "set_parms: link active\n"); #endif return (FALSE); } switch (pcode) { case OUTPKTSIZE: /* size of outgoing packets */ if (status = bracket(pvaluei, MAXCHKLEN+4, MAX_SIZE)) outsize = pvaluei; outdatamax = outsize-chksumout-3; break; case INPKTSIZE: /* size of incoming packets */ if (status = bracket(pvaluei, MAXCHKLEN+4, MAX_SIZE)) insize = pvaluei; indatamax = insize-chksumin-3; break; case OUTCHKSUM: /* outgoing checksum length */ if (status = bracket(pvaluei, 0, MAXCHKLEN)) chksumout = pvaluei; outdatamax = outsize-chksumout-3; break; case INCHKSUM: /* incoming checksum length */ if (status = bracket(pvaluei, 0, MAXCHKLEN)) chksumin = pvaluei; indatamax = insize-chksumin-3; break; case OUTPAD: /* number of outgoing pad chars */ if (pvaluei < 0) status = FALSE; else padout = pvaluei; break; case INPAD: /* number of incoming pad chars */ if (pvaluei < 0) status = FALSE; else padin = pvaluei; break; case OUTPADCHAR: /* outgoing pad char */ padchout = pvaluec; break; case INPADCHAR: /* incoming pad char, of course */ padchin = pvaluec; break; case SETTIMEOUT: /* timeout interval */ if (pvaluei < 0) status = FALSE; else waittime = pvaluei; break; case OUTEOP: /* outgoing end of packet */ eopout = pvaluec; break; case INEOP: /* and incoming . . . */ eopin = pvaluec; break; case OUTQUOTE: /* quoting character out */ if CONTROL(pvaluec) status = FALSE; else quoteout = pvaluec; break; case INQUOTE: /* . . . and in */ if CONTROL(pvaluec) status = FALSE; else quotein = pvaluec; break; default: status = FALSE; break; } /* end switch */ return (status); } /* set_parms */ /*****/ /* /* read_parms: return values of link parameters /* the parameters are returned in two integer arrays. /* invector gives the values of the parameters for packets coming /* in off the line. /* outvector gives the values of the parameters for outgoing packets. /* /* For those parameters with character values, the integer equivalents /* of the characters' ASCII codes are given. /* /* vector[0] = max packet length in characters /* vector[1] = number of characters in checksum /* vector[2] = number of pad characters /* vector[3] = pad character used /* vector[4] = timeout length (same for both invector and outvector) /* vector[5] = end of packet character /* vector[6] = quote character /* /*****/ read_parms(invector, outvector) int invector[7], outvector[7]; { invector[0] = insize ; outvector[0] = outsize; invector[1] = chksumin ; outvector[1] = chksumout; invector[2] = padin ; outvector[2] = padout; invector[3] = (int) padchin ; outvector[3] = (int) padchout; invector[4] = waittime ; outvector[4] = waittime; invector[5] = (int) eopin ; outvector[5] = (int) eopout; invector[6] = (int) quotein ; outvector[6] = (int) quoteout; } /*****/ /* /* send_data: send a string over the line /* type: type of message being sent /* 'F' or 'f' filename /* 'D' or 'd' text /* 'Z' or 'z' eof /* 'E' or 'e' error /* /* message: string to send /* /* count: number of characters to send from message /* /* status: returned indication of link status /* 'U' link up and connected /* 'W' write_thru mode /* 'A' link aborted /* 'M' message awaiting receipt /* /* ret_code: returned indication of the command's results /* This code is the same as the value returned by automaton /* /*****/ send_data(type, message, count, status, ret_code) char type; char message[]; int count; char *status, *ret_code; { int i; /* #ifdef DEBUG if (debug) { fprintf(stderr, "send_data: type = %c\n", type); fprintf(stderr, " message: "); for (i=0; i 0) *ret_code = SUCCESS; else *ret_code = FAILURE; } *status = shortstat(); return; } type = mytoupper( type ); switch (type) { case DATA: break; case FILE_HEAD: if (localprot) fprintf(stdout, "sending file %s\n", message); break; case EOFILE: if (localprot) if (debug) fprintf(stdout, "EOF on local file\n"); break; case ERROR: if (localprot) fprintf(stdout, "sending error message: %s\n", message); break; default: *ret_code = ILLEGAL_REQUEST; *status = shortstat(); link_stat = sdata_msg; #ifdef DEBUG if (debug) fprintf (stderr, "%s\n", sdata_msg); #endif return; } /* end switch */ i = 0; do { i += fillpacket(type, message+i, count-i, &outpkt, quoteout); *ret_code = automaton(SEND); } while ((count > i) && (*ret_code _eq_ SUCCESS)); *status = shortstat(); } /*****/ /* /* rcv_data: read information off the link /* type: type of packet returned, coded as in send_data /* /* string: data extracted from packet /* /* len: number of characters returned in string /* /* status: returned link status as in send_data /* /* ret_code: returned indication of results, as in send_data /* /*****/ rcv_data(type, string, len, status, ret_code) char *type; char string[]; int *len; char *status; char *ret_code; { int i; if (msg_waiting) { *len = emptypacket(type, string, &pktbuffer, quotein); msg_waiting = FALSE; } else { if (state _eq_ WRITE_THRU) { if (!localprot) { *ret_code = ILLEGAL_REQUEST; link_stat = rrcv_msg; } else { *len = read (lnkfd, string, MAX_DATALEN); *ret_code = SUCCESS; *type = DATA; } *status = shortstat(); return; } else { *ret_code = automaton(RCV); *len = emptypacket(type, string, &pktdelivery, quotein); } } #ifdef DEBUG if (debug) fprintf(stderr, "rcv_data: returned type %c\n", *type); #endif if (localprot) switch (*type) { case DATA: break; case FILE_HEAD: string [*len] = '\0'; if (debug) fprintf(stderr, "receiving file named %s on remote host\n", string); break; case EOFILE: if (debug) fprintf(stderr, "received end of file\n"); break; case ERROR: fprintf(stdout, "error message from remote: "); for(i=0; i<*len; i++) putc(string[i], stdout); putc('\n', stdout); break; default: break; } /* end switch */ *status = shortstat(); } /* rcv_data */ /*****/ /* /* connect : break the link and go to write_thru mode /* status: link status /* ret_code: command results /* /*****/ connect(status, ret_code) char *status, *ret_code; { char tmp[2]; char *dummy; #ifdef DEBUG if (debug) fprintf(stderr, "connect:\n"); #endif dummy = tmp; *dummy = EOS; fillpacket(BREAK, dummy, 0, &outpkt, quoteout); *ret_code = automaton(BREAKC); *status = shortstat(); } /*****/ /* /* read_status: return exhaustive link status. /* status: one of /* 'U' up and operational /* 'W' write_thru mode /* 'A' aborted /* msg_up: true if there is a message awaiting receipt /* naks: a count of how many naks have been received /* errormsg: a message describing the most recent of abnormalities /* /*****/ read_status(status, msg_up, naks, errormsg) char *status; int *msg_up; int *naks; char errormsg[ERRLEN]; { switch(state) { case WRITE_THRU: *status = 'W'; break; case ABORT2: *status = 'A'; break; default : *status = 'U'; break; } /* end switch */ *msg_up = msg_waiting; *naks = nak_cnt; strcpy(errormsg, link_stat); } /* read_status */ /*****/ /* /* init_sending: link initialization /* status: returned link status /* ret_code: results from this command /* /*****/ init_sending(status, ret_code) char *status, *ret_code; { char msg[8]; struct sgttyb ioparms; #ifdef DEBUG if (debug) fprintf(stderr, "init_sending\n"); #endif init_world(); ioctl( lnkfd, TIOCGETP, &ioparms); ioparms.sg_ispeed = ioparms.sg_ospeed = lnkspeed; ioparms.sg_flags = EVENP | ODDP; ioparms.sg_flags |= RAW; ioparms.sg_flags &= ~ECHO; ioctl (lnkfd, TIOCSETP, &ioparms); ioctl (lnkfd, TIOCEXCL, (struct sgttyb *) NULL); msg[0] = itoc(insize); msg[1] = itoc(waittime); msg[2] = itoc(padin); msg[3] = cshift(padchin); msg[4] = itoc(eopin); msg[5] = quotein; msg[6] = itoc(chksumin); msg[7] = EOS; fillpacket(SEND_INIT, msg, 7, &outpkt, NOQUOTE); pktcopy(&outpkt, &outparms_pkt); *ret_code = automaton(INIT_SEND); *status = shortstat(); } /* init_sending */ /*****/ /* /* init_receiving: link initialization /* status: returned link status /* ret_code: results from this command /* /*****/ init_receiving(status, ret_code) char *status, *ret_code; { char msg[8]; struct sgttyb ioparms; #ifdef DEBUG if (debug) fprintf(stderr, "init_receiving\n"); #endif init_world(); ioctl( lnkfd, TIOCGETP, &ioparms); ioparms.sg_ispeed = ioparms.sg_ospeed = lnkspeed; ioparms.sg_flags = EVENP | ODDP; /* no parity */ ioparms.sg_flags |= RAW; ioparms.sg_flags &= ~ECHO; ioctl (lnkfd, TIOCSETP, &ioparms); ioctl (lnkfd, TIOCEXCL, (struct sgttyb *) NULL); ioctl (lnkfd, TIOCHPCL, (struct sgttyb *) NULL); /* hang up on exit*/ msg[0] = itoc(insize); msg[1] = itoc(waittime); msg[2] = itoc(padin); msg[3] = cshift(padchin); msg[4] = itoc(eopin); msg[5] = quotein; msg[6] = itoc(chksumin); msg[7] = EOS; fillpacket(ACK, msg, 7, &outpkt, NOQUOTE); pktcopy(&outpkt, &outparms_pkt); *ret_code = automaton(INIT_RCV); *status = shortstat(); } /* init_receiving */ /******/ /* /* dlink: open the communications line /* /******/ int dlink (stream) char *stream; { if ( (lnkfd = open (stream, 2)) < 0) { perror ("Unable to open link"); return (FALSE); } else { /* drop carrier on comm line only when kermit main process ends */ ioctl (lnkfd, TIOCHPCL, (struct sgttyb *)NULL); ioctl (lnkfd, TIOCEXCL, (struct sgttyb *)NULL); /* exclusive open */ if (debug) fprintf (stderr, "%s opened\n", stream); return (TRUE); } } /*********/ /* /* dunlink : close the communications line /* /*********/ int dunlink() { if (close (lnkfd) < 0) { fprintf (stderr, "lnkfd at close: %d\n", lnkfd); perror ("Unable to close link"); return (FALSE); } else return (TRUE); } /*****/ /* /* reset: reset after abortion /* Basically, it forces the link into write_thru /* /*****/ reset(status, retcode) char *status, *retcode; { char tmp; char *dummy; tmp = EOS; dummy = &tmp; fillpacket(BREAK, dummy, 0, &outpkt, quoteout); *retcode = automaton(RESET); *status = shortstat(); if(*status _eq_ ABORTED) { *retcode = automaton(RESET); *status = shortstat(); } } /*****/ /* /* shutdown: shut down the protocol /* /*****/ shutdown() { char status, ret_code; if ((state != ABORT2) && (state != WRITE_THRU)) connect(&status, &ret_code); state = ABORT2; } /********************************************************/ /* */ /* Kermit file transfer program */ /* */ /* system: Berkeley 4.1 (VAX UNIX) */ /* Cornell University */ /* */ /* date: May 27, 1982 */ /* */ /* programmer: Dean Jagels */ /* modified by Alison Brown September 1982 */ /* */ /********************************************************/ /* */ /* Kermit data link layer */ /* */ /********************************************************/ #include "ptypes.h" #include "set_parms.h" #include "parms.h" #include "comcodes.h" #include "states.h" #include "status.h" #include "retcode.h" #include "globals.h" #include "tools.h" #include "io.h" #include /*****/ /* /* write-thru: automaton state write_thru /* command: should be one of /* 'I' send information (unadorned mode) /* 'S' initialize sendind /* 'R' initialize receiving /* /*****/ char write_thru(command) char command; { char type; char message[MAX_DATALEN]; int num; int i; #ifdef DEBUG if (debug) fprintf(stderr, "write_thru: command %c\n", command); #endif switch (command) { case INIT_SEND: state = S_INIT; return(SUCCESS); case INIT_RCV: state = R_INIT; return(SUCCESS); case BREAKC: case RESET: /* ignore the command; its obviously redundant */ return (SUCCESS); default: link_stat = wth_msg; #ifdef DEBUG if (debug) fprintf(stderr, "%s\n", wth_msg); #endif return(ILLEGAL_REQUEST); } /* end switch */ } /* write_thru */ /*****/ /* /* state s_init /* assumes outparms_pkt contains the appropriate send_init /*****/ char s_init() { #ifdef DEBUG if (debug) fprintf(stderr, "s_init:\n"); #endif state = S_ACK; if (!xmit(&outparms_pkt)) { stry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = stry_msg; state = ABORT; return(ABORTING); } else return (SUCCESS); } /* s_init */ /*****/ /* /* state s_ack /* /*****/ char s_ack() { char *cptr; int i; int okay; #ifdef DEBUG if (debug) fprintf(stderr, "s_ack: entry\n"); #endif if (!rcv(&inparms_pkt)) { rtry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = rtry_msg; state = ABORT; return(ABORTING); } if (inparms_pkt.ptype _eq_ ACK) { state = MAIN_LOOP; pktcopy(&inparms_pkt, &pktbuffer); cptr = inparms_pkt.data; /* the following ugliness allows trailing parameter fields to be missing */ for (i=0; (*(cptr+i) != EOS) && (i<6) ; i++) { #ifdef DEBUG if (debug) fprintf(stderr, "i=%d, *(cptr+i)=%c, okay=%d\n", i, *(cptr+i), okay); #endif switch (i) { case 0: okay = set_parms(OUTPKTSIZE, *cptr, ctoi(*cptr)); break; case 1: waittime = MAX(waittime, ctoi( *(cptr+1)) ); break; case 2: okay = set_parms(OUTPAD, *(cptr+2), ctoi(*(cptr+2)) ) || okay; break; case 3: padchout = cshift( *(cptr+3) ); break; case 4: eopout = ctoi( *(cptr+4) ); break; case 5: quoteout = *(cptr+5); break; case 6: chksumout = ctoi( *(cptr+6) ); break; default: /* this had better never happen */ abort(); } /* end switch */ } /* for loop */ #ifdef DEBUG if (debug) fprintf(stderr, "s_ack: rcvd data %s\n", inparms_pkt.data); #endif if (!okay) { link_stat = parm_msg; state = ABORT; return(ABORTING); } } else state = S_INIT; return (SUCCESS); } /* s_ack */ /*****/ /* /* state r_init /* Assumes appropriate ack packet in outparms_pkt /*****/ char r_init() { char *cptr; int i; int okay; if (!rcv(&inparms_pkt)) { rtry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = rtry_msg; state = ABORT; return(ABORTING); } if (inparms_pkt.ptype _eq_ SEND_INIT) { if (!xmit(&outparms_pkt)) { stry_cnt++; if (user_interrupt) link_stat = uint_msg; link_stat = stry_msg; state = ABORT; return(ABORTING); } pktcopy( &inparms_pkt, &pktbuffer); state = MAIN_LOOP; cptr = inparms_pkt.data; /* the following ugliness allows trailing parameter fields to be missing */ for (i=0; (*(cptr+i) != EOS) && (i<6) ; i++) { #ifdef DEBUG if (debug) fprintf(stderr, "i=%d, *(cptr+i)=%c, okay=%d\n", i, *(cptr+i), okay); #endif switch (i) { case 0: okay = set_parms(OUTPKTSIZE, *cptr, ctoi(*cptr)); break; case 1: waittime = MAX(waittime, ctoi( *(cptr+1)) ); break; case 2: okay = set_parms(OUTPAD, *(cptr+2), ctoi(*(cptr+2)) ) || okay; break; case 3: padchout = cshift( *(cptr+3) ); break; case 4: eopout = ctoi( *(cptr+4) ); break; case 5: quoteout = *(cptr+5); break; case 6: chksumout = ctoi( *(cptr+6) ); break; default: /* this had better never happen */ abort(); } /* end switch */ } #ifdef DEBUG if (debug) fprintf(stderr, "r_init: data rcvd %s\n", inparms_pkt.data); #endif if (!okay) { link_stat = parm_msg; state = ABORT; return(ABORTING); } return(SUCCESS); } else { state = R_INIT; return (SUCCESS); } } /* r_init */ /*****/ /* /* state main_loop /* /*****/ char main_loop(command) char command; { #ifdef DEBUG if (debug) fprintf(stderr, "main_loop: command %c\n", command); #endif switch(command) { case SEND: if (!xmit(&outpkt)) { stry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = stry_msg; state = ABORT; return(ABORTING); } state = AWAIT_ACK; break; case BREAKC: case RESET: if (!xmit(&outpkt)) { stry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = stry_msg; #ifdef DEBUG if (debug) fprintf(stderr, "%s\n", stry_msg); #endif state = PRE_WRITE_THRU; return(FAILURE); } state = BREAK_ACK; break; case RCV: if (msg_waiting) pktcopy( &pktbuffer, &pktdelivery); else state = INPUT; msg_waiting = FALSE; break; default: link_stat = ml_msg; #ifdef DEBUG if (debug) fprintf(stderr, "%s\n", ml_msg); #endif return(ILLEGAL_REQUEST); } /* end switch */ return (SUCCESS); } /* main_loop */ /*****/ /* /* state await_ack /* /*****/ char await_ack() { #ifdef DEBUG if (debug) fprintf(stderr, "await_ack:"); #endif if (!rcv(&inpkt)) { rtry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = rtry_msg; state = ABORT; return(ABORTING); } #ifdef DEBUG if (debug) fprintf(stderr, "received type %c\n", inpkt.ptype); #endif switch (inpkt.ptype) { case ACK: state = MAIN_LOOP; break; case NAK: nak_cnt++; case TIMEOUT: if (!xmit(&outpkt)) { stry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = stry_msg; state = ABORT; return(ABORTING); } break; case BREAK: sendack(inpkt.seqno); state = PRE_WRITE_THRU; return (FAILURE); case FILE_HEAD: case DATA: case EOFILE: case ERROR: if (!msg_waiting) { sendack(inpkt.seqno); pktcopy(&inpkt, &pktbuffer); } else /* there's already a message in the buffer */ sendnak(pktbuffer.seqno); msg_waiting = TRUE; break; default: link_stat = ack_msg; state = ABORT; return(ABORTING); } /* end switch */ return (SUCCESS); } /* await_ack */ /*****/ /* /* state break_ack /* /*****/ char break_ack() { #ifdef DEBUG if (debug) fprintf(stderr, "break_ack:"); #endif if (!rcv(&inpkt)) { rtry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = rtry_msg; state = ABORT; return(ABORTING); } #ifdef DEBUG if (debug) fprintf(stderr, "rcvd type %c\n", inpkt.ptype); #endif switch (inpkt.ptype) { case ACK: state = PRE_WRITE_THRU; return(SUCCESS); case NAK: nak_cnt++; case TIMEOUT: break; case FILE_HEAD: case DATA: case EOFILE: case ERROR: if (!msg_waiting) { sendack(inpkt.seqno); pktcopy(&inpkt, &pktbuffer); } else /* there's already a message in the buffer */ sendnak(pktbuffer.seqno); msg_waiting = TRUE; break; case BREAK: sendack(inpkt.seqno); state = PRE_WRITE_THRU; return(SUCCESS); case SEND_INIT: link_stat = break_msg; state = ABORT; return(ABORTING); } /* end switch */ if (!xmit(&outpkt)) { stry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = stry_msg; state = ABORT; return(ABORTING); } return (SUCCESS); } /* break_ack */ /*****/ /* /* state input /* /*****/ char input() { #ifdef DEBUG if (debug) fprintf(stderr, "input:"); #endif if (!rcv(&inpkt)) { rtry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = rtry_msg; state = ABORT; return(ABORTING); } #ifdef DEBUG if (debug) fprintf(stderr, "input: rcvd type %c\n", inpkt.ptype); #endif switch (inpkt.ptype) { case NAK: nak_cnt++; case ACK: state = INPUT; break; case TIMEOUT: /* we'll try re-sending an ack for the latest rcvd */ if (pktbuffer.ptype _eq_ SEND_INIT) { if (!xmit( &outparms_pkt) ) { stry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = stry_msg; state = ABORT; return(ABORTING); } } else sendack(pktbuffer.seqno); break; case FILE_HEAD: case DATA: case EOFILE: case ERROR: sendack(inpkt.seqno); state = MAIN_LOOP; pktcopy( &inpkt, &pktbuffer); pktcopy( &inpkt, &pktdelivery); break; case BREAK: sendack(inpkt.seqno); state = PRE_WRITE_THRU; return(FAILURE); default: link_stat = input_msg; state = ABORT; return(ABORTING); } /* end switch */ return(SUCCESS); } /* input */ /*****/ /* /* automaton: step through the fsa guiding the protocol. /* command: one of /* 'D' send information packet (data, filename, eof, error) /* 'G' receive (get) an information packet /* 'B' break the link and go to write_thru mode /* 'S' initialize sending /* 'R' initialize receiving /* 'A' reset after abort /* /* NOTE: All pertinent packets (send initiate, ack for initialization, /* break, etc.) must be built and ready for output before entering /* the automaton. If the automaton were required to construct any /* packets, then the send or receive try counts could not be kept for /* those packets. (Creating a packet implies initializing trycount) /* /* Returns a link status: /* 'I' Illegal command /* 'A' Aborting. Due to persistent link errors, the link has /* been shut down. /* 'F' Failure. The command was not completed, and the link /* has gone into write_thru mode. Generally occurs /* whenever a break is received. Also, when executing /* a connect command, Failure is returned if no ack /* is received from the other end of the link. /* 'S' Success. The command was successfully completed. /* /* In addition, on return the global msg_waiting is set to TRUE if /* there is a message waiting for input. /* /*****/ char automaton(command) char command; { char status; #ifdef DEBUG if (debug) fprintf(stderr, "automaton: command %c\n", command); #endif fflush(stderr); inpkt.trycount = 0; inpkt.data[0] = EOS; do switch (state) { case PRE_WRITE_THRU: inlnkflush(); state = WRITE_THRU; break; case WRITE_THRU: status = write_thru(command); break; case S_INIT: status = s_init(); break; case S_ACK: status = s_ack(); break; case R_INIT: status = r_init(); break; case MAIN_LOOP: status = main_loop(command); break; case AWAIT_ACK: status = await_ack(); break; case BREAK_ACK: status = break_ack(); break; case INPUT: status = input(); break; case ABORT: { char tmp = EOS; char *dummy = &tmp; fillpacket(BREAK, dummy, 0, &outpkt, quoteout); main_loop (RESET); state = ABORT2; } break; case ABORT2: status = SUCCESS; state = WRITE_THRU; default: ; } /* end switch */ while (( state != MAIN_LOOP) && (state != WRITE_THRU) && (state != ABORT2 )); #ifdef DEBUG if (debug) fprintf(stderr, "automaton: returning state %d\n", state); #endif if (state _eq_ MAIN_LOOP) link_active = TRUE; else link_active = FALSE; if (state _eq_ ABORT2) { if (user_interrupt) link_stat = uint_msg; fprintf(stdout, "Aborting: %s\n", link_stat); } return (status); } /* automaton */ /********************************************************/ /* */ /* Kermit file transfer program */ /* */ /* system: Berkeley 4.1 (VAX UNIX) */ /* Cornell University */ /* */ /* date: May 27, 1982 */ /* */ /* programmer: Dean Jagels */ /* modified by Alison Brown September 1982 */ /* */ /********************************************************/ #include "ptypes.h" #include "parms.h" #include "globals.h" #include "tools.h" #include #include #include static int timed_out; /*****/ /* /* timetrap: deal with an alarm clock interrupt /* /*****/ timetrap(sig) int sig; { timed_out = TRUE; #ifdef DEBUG if (debug) fprintf(stderr, "rcv_string: timed out\n"); #endif } /*****/ /* /* rcv_string: read string of characters off the link /* str: space to hold the string /* len: number of bytes to read (max) /* /* returns -1 if error /* 0 if timeout /* number of characters actually read otherwise /* /*****/ int rcv_string(str, len) char *str; int len; { unsigned timeleft; int nread; #ifdef DEBUG if (debug) fprintf(stderr, "rcv_string:\n"); #endif if ( (int) signal(SIGALRM, timetrap) _eq_ -1) { #ifdef DEBUG if (debug) fprintf(stderr, "rcv_string: error in signal\n"); #endif abort(); } timed_out = FALSE; timeleft = alarm(waittime); nread = read(lnkfd, str, len); timeleft = alarm(0); #ifdef DEBUG if (debug) fprintf(stderr, "rcv_string: nread=%d, timed_out=%d\n", nread, timed_out); #endif if (nread < 0) { if (timed_out) nread = 0; #ifdef DEBUG if (debug) fprintf(stderr, "rcv_string: error in read\n"); #endif } #ifdef DEBUG str[nread] = EOS; if (debug) fprintf(stderr, "from link: %s\n", str); #endif return(nread); } /* rcv_string */ /*****/ /* /* rcv_char: provide characters off the line on at a time. /* returns -1 if read error /* 0 if timeout /* 1 if all ok /* /*****/ int rcv_char(c) char *c; { static char buffer[BUFSIZE]; static int count = -1, /* count = # of chars left in buffer */ index; /* index = index in buffer of next character to deliver */ if (count <= 0) { /* try to get something off the line */ count = rcv_string(buffer, BUFSIZE); if (count < 0) { count = 0; return(-1); } if (count _eq_ 0) return(0); index = 0; } *c = buffer[index++]; #ifdef DEBUG if (debug) fprintf(stderr, "%o ", *c); #endif count--; return(1); } /* rcv_char */ /*****/ /* /* rcv: receive a packet /* returns FALSE when anything goes wrong. In particular, returns FALSE /* when trycount exceeds maxtry. /* /* This procedure will deal with /* old data packets (sends acks for them) /* packets with checksum errors (sends naks for them) /* acks and naks for packets other than the latest packet sent /* (ignores) /* /* Therefore, if the packet returned is an ack or nak, then it applies /* to the most recently sent information packet. /* /* A returned packet with type 'T' indicates timeout. /* /* I hate this routine. I think it's the ever-present chance of timeout /* that complicates things (thus requiring tests every time you do any i.o.) /* /*****/ int rcv(pkt) struct packetform *pkt; { int pktlen, datalen; int ret_code; char c, temp[MAX_SIZE]; int i; int match, done, packetread; #ifdef DEBUG if (debug) fprintf(stderr, "rcv: have packet %d\n", ctoi(pktbuffer.seqno)); if (debug) if (localprot) fprintf(stderr, "have received packet %d\n", ctoi(pktbuffer.seqno)); #endif done = FALSE; do { if (pkt->trycount++ > maxtry) { #ifdef DEBUG if (debug) fprintf(stderr, "rcv: maxtry exceeded\n"); #endif return(FALSE); } ret_code = rcv_char(&c); if (ret_code < 0) { return(FALSE); } if (ret_code _eq_ 0) { pkt->ptype = TIMEOUT; return(TRUE); } packetread = FALSE; do { /* look for the synch character */ while (c != synchar) { ret_code = rcv_char(&c); if (ret_code < 0) { return(FALSE); } if (ret_code _eq_ 0) { pkt->ptype = TIMEOUT; return(TRUE); } } /* now try to pull out a packet */ ret_code = rcv_char(&c); if (ret_code < 0) { return(FALSE); } if (ret_code _eq_ 0) { pkt->ptype = TIMEOUT; return(TRUE); } if ((c _eq_ padchin) || (c _eq_ synchar)) continue; pkt->length = c; pktlen = ctoi(c); datalen = pktlen - 2 - chksumin; /* read enough characters to satisfy the packet */ for ( i=0; (i < pktlen) && (c != padchin) && (c != synchar); i++) { ret_code = rcv_char(&c); *(temp+i) = c; if (ret_code < 0) { return(FALSE); } if (ret_code _eq_ 0) { pkt->ptype = TIMEOUT; return(TRUE); } } packetread = (i >= pktlen); /* iseqno = temp[0]; pkt->ptype = temp[1]; for (i=0; idata[i] = temp[i+2]; pkt->data[i] = EOS; /* check the sum */ compchksum(pkt, chksumin); match = TRUE; for (i=0; ichksum[i]); if (!match) { sendnak(pktbuffer.seqno); #ifdef DEBUG if (debug) fprintf(stderr, "rcv: checksum error\n"); #endif } /* is it an old data packet ? */ else if ( (pkt->seqno _eq_ pktbuffer.seqno) && (pkt->ptype != ACK) && (pkt->ptype != NAK) ) { if (pkt->ptype _eq_ SEND_INIT) xmit(&outparms_pkt); else sendack(pktbuffer.seqno); #ifdef DEBUG if (debug) fprintf(stderr, "rcv: old data packet\n"); #endif } /* an ack directed at a packet we don't care about? */ else if ((pkt->ptype _eq_ ACK) && ( ctoi(pkt->seqno) != last_packet)) /* ignore it */ ; /* all naks presently go to the caller, so we're done */ else done = TRUE; } while (!done); if (!user_interrupt) return(TRUE); else return (FALSE); } /* rcv */ /*****/ /* /* inlnkflush: empty the input line /* /*****/ inlnkflush() { char buf[BUFSIZE]; while ( rcv_string(buf, BUFSIZE) > 0 ) /* keep looping */ ; } /*****/ /* /* xmit_char: transmit a character /* /*****/ xmit_char(c) char c; { int nwrit; #ifdef DEBUG if (debug) fprintf(stderr, "%o ", c); #endif nwrit = write(lnkfd, &c, 1); } /* xmit_char */ /*****/ /* /* xmit: send a packet down the line /* returns false if anything goes wrong. /* In particular, false is returned if the trycount of the packet /* exceeds maxtry. /* /*****/ int xmit(pkt) struct packetform *pkt; { char outstring[MAX_SIZE]; char *cptr; int outlen; int i; #ifdef DEBUG if (debug) fprintf(stderr, "xmit:packet %d\n", ctoi(pkt->seqno)); if (debug) if (localprot) fprintf(stderr, "packet number %d\n", ctoi(pkt->seqno)); #endif if (pkt->trycount++ > maxtry) { #ifdef DEBUG if (debug) fprintf(stderr, "xmit: maxtry exceeded\n"); #endif return(FALSE); } /* send pad characters */ #ifdef DEBUG if (debug) fprintf(stderr, "sending:"); #endif for (i=0; ilength; outstring[outlen++] = pkt->seqno; outstring[outlen++] = pkt->ptype; for(cptr=pkt->data; *cptr != EOS; cptr++) outstring[outlen++] = *cptr; for(i=0; ichksum[i]; write(lnkfd, outstring, outlen); #ifdef DEBUG if (debug) for (i=0; i char prompt[NAMELEN]; /* the main procedure, diminutive though it is */ main() { strcpy(prompt, "Kermit-Unix>"); comproc(); } /* main */ /********************************************************/ /* */ /* Kermit file transfer program */ /* */ /* system: Berkeley 4.1 (VAX UNIX) */ /* Cornell University */ /* */ /* date: May 27, 1982 */ /* */ /* programmer: Dean Jagels */ /* modified by Alison Brown September 1982 */ /* */ /********************************************************/ #include "status.h" #include "retcode.h" #include "comcodes.h" #include "parms.h" #include "set_parms.h" #include "kerm.h" #include "globals.h" #include "ptypes.h" #include #include #include #include #include unsigned delay = 4; extern char prompt[]; char k_head[] = "receive: file header missing"; char k_eof[] = "receive: end of file missing"; char k_shortw[] = "receive: incomplete write to output file"; char k_set[] = "invalid set command - type help set for how to use set"; char k_badbaud[]= "usage: s[et] b[aud] { 150/300/1200/2400/4800/9600 }"; char k_duplex[] = "usage: s[et] du[plex] { full/half }"; /*****/ /* /* trap for user interrupts /* /*****/ int_trap(sig) int sig; { #ifdef DEBUG if (debug) fprintf(stderr, "user interrupt caught\n"); #endif fprintf (stdout, "\n"); user_interrupt = TRUE; signal (SIGINT, int_trap); } /*****/ /* /* erreport : report an error and log it to stderr /* /*****/ erreport (error) char *error; { char status, retcode; int i; char *cptr; fprintf(stderr, "%s\n", error); if (localprot) fprintf(stdout, "%s\n", error); else { for(i=0, cptr=error; *cptr!=EOS; i++, cptr++); send_data(ERROR, error, i, &status, &retcode); } } /*****/ /* /* errchk: error watch for kermit commands /* /*****/ int errchk(routine, status, retcode) char *routine; char *status, *retcode; { char ptype, pstring[MAX_DATALEN]; int len; int i; if (*status _eq_ MSG_UP) { rcv_data(&ptype, pstring, &len, status, retcode); if ((ptype _eq_ ERROR) && localprot) { fprintf(stdout, "error received: "); for(i=0; i seconds */ #ifdef DEBUG if (debug) fprintf(stderr, "kermit send entered\n"); #endif sleep(delay); /* let the UNIX shell expand the wildcards in the file specs. Only on UNIX . . . */ list = llist; sprintf(list, "echo %s", filelst); filestream = popen(list, "r"); fgets(list, BUFSIZE, filestream); fstatus = pclose(filestream); /* Thus far massaging the input line. Lets get the link rolling */ if (lnkstat == CLOSED) { fprintf (stdout, "Must connect to remote machine before sending\n"); return; } init_sending(&lstatus, &retcode); if (!errchk("send", &lstatus, &retcode)) return; list = getname(filenm, list); while (*filenm != EOS) { /* we have a file name in filenm. Try to open it */ fd = open(filenm, 0); if (fd < 0) perror(filenm); else { for(i=0, cptr=filenm; *cptr!=EOS; i++, cptr++); send_data(FILE_HEAD, filenm, i, &lstatus, &retcode); if (!errchk("send", &lstatus, &retcode)) return; n_read = read(fd, buffer, BUFSIZE); while (n_read > 0) { send_data(DATA, buffer, n_read, &lstatus, &retcode); if (!errchk("send", &lstatus, &retcode)) return; n_read = read(fd, buffer, BUFSIZE); } if (n_read < 0) perror("kermit send"); send_data(EOFILE, "", 0, &lstatus, &retcode); if (!errchk("send", &lstatus, &retcode)) return; if(close(fd) < 0) perror ("kermit send"); } /* end else */ fprintf (stdout, "file transfer complete\n"); list = getname(filenm, list); } /* end while */ connect (&lstatus, &retcode); if (!errchk("send", &lstatus, &retcode)) { return; } } /* send */ /*****/ /* /* more_data : used by kermit receive command to receive data from the /* link and perform some policing. /* In particular, it forces the delivery of packets in the order /* ___ ___ /* | | * /* | file header | /* | [ data ]* | /* | end of file | /* |__ __| /* /* break /* /* Note that the break is fabricated by more_data. /* The data link doesn't return such things. /* /*****/ more_data (ptype, pstring, len, status, retcode) char *ptype; char pstring[]; int *len; char *status, *retcode; { int i; static int indata = FALSE, shutdown = FALSE, msg_held = FALSE; /* /* semantics of indata: /* if last packet returned was file header indata = TRUE /* if last packet returned was data, indata = TRUE /* if last packet returned was end of file, indata = FALSE /* if last packet returned was break, indata = FALSE */ static char holdtype, holdstring[MAX_DATALEN], holdstat, holdret; static int holdlen; #ifdef DEBUG if (debug) fprintf(stderr, "more_data: shutdown=%d indata=%d msg_held=%d", shutdown, indata, msg_held); #endif if (msg_held) { msg_held = FALSE; *ptype = holdtype; strcpy(pstring, holdstring); *len = holdlen; *status = holdstat; *retcode = holdret; return; } if (!shutdown) { rcv_data (ptype, pstring, len, status, retcode); if (*retcode != SUCCESS) { if (*status _eq_ WTHRU) { if (localprot) fprintf(stdout, "file transfer complete\n"); } else errchk("receive", status, retcode); if (indata) { shutdown = TRUE; *ptype = EOFILE; indata = FALSE; } else { *ptype = BREAK; shutdown = FALSE; msg_held = FALSE; } return; } switch(*ptype) { case DATA: if (!indata) { indata = TRUE; erreport (k_head); msg_held = TRUE; holdtype = *ptype; strcpy(holdstring, pstring); holdlen = *len; holdstat = *status; holdret = *retcode; *ptype = FILE_HEAD; strcpy (pstring, "kermXXXXXX"); *len = 10; mktemp(pstring); fprintf(stdout, "received data stored in %s\n", pstring); } /* if indata */ break; case ERROR: if (localprot) { fprintf(stdout, "error received: "); for(i=0; i<*len; i++) putc(pstring[i], stdout); putc('\n', stdout); } more_data(ptype, pstring, len, status, retcode); break; case FILE_HEAD: if (indata) { indata = FALSE; erreport(k_eof); msg_held = TRUE; holdtype = *ptype; strcpy(holdstring, pstring); holdlen = *len; holdstat = *status; holdret = *retcode; *ptype = EOFILE; *len = 0; } else indata = TRUE; break; case EOFILE: if (!indata) fprintf (stdout, "Warning: extra EOF received\n"); else indata = FALSE; break; } /* end switch */ } /* end if shutdown */ else { *ptype = BREAK; *len = 0; /* set the static booleans in preparation for the next receive command */ indata = FALSE; shutdown = FALSE; msg_held = FALSE; } } /* end more_data */ /*****/ /* /* receive command: receive a series of files /* syntax: receive [file] /* /* file contains a (possibly null) string which is the filename under /* which the first file received is stored. /* /*****/ receive (file) char *file; { #define PROTECTION 0644 char ptype, pstring[MAX_DATALEN]; int len; char status, retcode; char *filetmp; int fd, nwrit; /* stall for seconds */ #ifdef DEBUG if (debug) fprintf(stderr, "kermit receive entered\n"); #endif sleep(delay); file = skipspace(file); if (lnkstat == CLOSED) { fprintf (stdout, "Must connect to remote machine before receiving\n"); return; } init_receiving(&status, &retcode); if (!errchk("receive", &status, &retcode)) return; more_data(&ptype, pstring, &len, &status, &retcode); if (*file _eq_ EOS) { filetmp = pstring; pstring[len] = EOS; } else filetmp = file; while (ptype != BREAK) { /* open the file */ fd = creat(filetmp, PROTECTION); if (fd < 0) { perror("kermit receive"); do more_data(&ptype, pstring, &len, &status, &retcode); while (ptype != EOFILE); } else { fprintf (stdout, "receiving - remote filename %s, local filename %s\n", pstring, filetmp); more_data(&ptype, pstring, &len, &status, &retcode); while (ptype != EOFILE) { /* write the packet to the file */ nwrit = write(fd, pstring, len); if (nwrit < 0) { perror("kermit receive"); do more_data(&ptype, pstring, &len, &status, &retcode); while (ptype != EOFILE); } else { if (nwrit != len) fprintf(stderr, "%s\n", k_shortw); more_data(&ptype, pstring, &len, &status, &retcode); } } /* while */ /* close the file */ if (close(fd) < 0) perror ("kermit receive"); if (user_interrupt) if (unlink (filetmp) < 0) fprintf (stdout, "file %s invalid - please remove\n", filetmp); } /* else */ /* read in break packet or header for next file at end of file transfer */ more_data(&ptype, pstring, &len, &status, &retcode); if (ptype == BREAK) continue; filetmp = pstring; pstring[len] = EOS; } /* while */ } /* receive */ /*****/ /* /* kermit connect command - exec the terminal emulator /* /*****/ k_connect() { int n; int termpid; /* process id of child (terminal emulator) */ int texstat; /* exit code returned by child execing term emul */ int retcode; /* = termpid on normal return from terminal emulator */ char fdstr[10]; /* comm line file descriptor (ascii string) */ char speedstr[10]; /* comm line speed (ascii string) */ char duplexstr[10]; /* remote host half or full duplex (echo or not) */ if (debug) fprintf (stderr, "k_connect entered\n"); if ((termpid = fork()) == 0) { sprintf (fdstr, "%d", lnkfd); sprintf (speedstr, "%d", lnkspeed); if (lnkduplex == FULL) strcpy (duplexstr, "full"); else if (lnkduplex == HALF) strcpy (duplexstr, "half"); execl (TEFILE, TEFILE, fdstr, duplexstr, speedstr, 0); fprintf (stdout, "Terminal emulator not found or not executable\n"); exit (4); } /* let child handle all interrupts */ signal (SIGQUIT, SIG_IGN); signal (SIGINT, SIG_IGN); if ((retcode = wait (&texstat)) != termpid) { fprintf (stderr, "Error: return code %d, status %o from terminal emulator\n", retcode, texstat); exit(); } if (debug) fprintf (stderr, "return to k_connect from terminal emulator\n"); signal (SIGINT, int_trap); signal (SIGQUIT, SIG_DFL); } /* k_connect */ /*****/ /* /* kermit show command: print the link's parameters /* /*****/ char sformat[] = "%29s %-10s %-10s\n"; char cformat[] = "%29s %-10c %-10c\n"; char iformat[] = "%29s %-10d %-10d\n"; char oformat[] = "%29s %-10o %-10o\n"; char bformat[] = "link operating at %d baud %s duplex\n"; char duplexing[5]; show() { int invector[7], outvector[7]; read_parms(invector, outvector); fprintf (stdout, "\n"); fprintf (stdout, "Parameters for link %s \n", lnkname); fprintf(stdout, "\n"); fprintf(stdout,sformat, " ", "incoming", "outgoing"); fprintf(stdout,iformat, "max packet length", invector[0], outvector[0]); fprintf(stdout,iformat, "number of checksum characters", invector[1], outvector[1]); fprintf(stdout,iformat, "number of pad characters", invector[2], outvector[2]); fprintf(stdout,oformat, "pad character", invector[3], outvector[3]); fprintf(stdout,iformat, "timeout length (sec)", invector[4], outvector[4]); fprintf(stdout,oformat, "end of packet", invector[5], outvector[5]); fprintf(stdout,cformat, "quote character", invector[6], outvector[6]); fprintf(stdout, "\n"); fprintf(stdout, "pre-file transfer delay: %d\n", delay); if (lnkduplex == FULL) strcpy (duplexing, "full"); else if (lnkduplex == HALF) strcpy (duplexing, "half"); if (lnkspeed == B300) fprintf(stdout,bformat, 300, duplexing); else if (lnkspeed == B1200) fprintf (stdout, bformat, 1200, duplexing); else if (lnkspeed == B2400) fprintf (stdout, bformat, 2400, duplexing); else if (lnkspeed == B4800) fprintf (stdout, bformat, 4800, duplexing); else if (lnkspeed == B9600) fprintf (stdout, bformat, 9600, duplexing); else fprintf (stdout, "link baud rate invalid\n"); fprintf (stdout, "link status: "); if (lnkstat == OPEN) fprintf (stdout, "open\n"); else fprintf (stdout, "closed\n"); if (debug == TRUE) fprintf (stdout, "debugging on\n"); else fprintf (stdout, "debugging off\n"); fprintf (stdout, "\n"); } /* show */ /*****/ /* /* kermit quit command : quit the whole shabang /* /*****/ kquit() { shutdown(); /* shut down the protocol */ if (lnkstat == OPEN) dunlink() ; /* close link */ exit(0); } /*****/ /* /* kermit status command: show detailed link status /* /*****/ status() { char stat, errormsg[ERRLEN]; int msg_up, naks; read_status(&stat, &msg_up, &naks, errormsg); if (msg_up) fprintf(stdout, "a message is awaiting receipt\n"); fprintf(stdout, "Description of most recent error:\n"); fprintf(stdout, "%s\n", errormsg); } /*****/ /* /* linkparms: set link parameters /* called by set to decipher its parm list /* /*****/ outlinkparms(opstring) char *opstring; { int result; char word[NAMELEN], arg[NAMELEN]; char cparm; opstring = skipspace(opstring); while (*opstring != EOS) { opstring = getname(word, opstring); opstring = getname(arg, opstring); if (isupper(word[0])) word[0] = tolower(word[0]); if (*arg _eq_ EOS) fprintf(stdout, "%s\n", k_set); else if (word[0]!= EOS) { /* if a character arguement was specified as an ASCII code, translate it */ if (*arg _eq_ '\\') { int temp; int tempo; temp = atoi(arg+1); tempo = temp%10; temp /= 10; tempo += 8 * (temp%10); temp /= 10; tempo += 64 * temp; cparm = (char) tempo; } else cparm = *arg; switch (word[0]) { case 'p': if (isupper(word[1])) word[1] = tolower(word[1]); switch (word[1]) { case 'k': result = set_parms(OUTPKTSIZE, cparm, atoi(arg)); break; case 'd': result = set_parms(OUTPAD, cparm, atoi(arg)); break; case 'c': result = set_parms(OUTPADCHAR, cparm, word[1]); break; default: fprintf(stdout, "%s\n", k_set); } /* end switch word[1] */ case 'e': result = set_parms(OUTEOP, cparm, word[1]); break; case 'q': result = set_parms(OUTQUOTE, cparm, word[1]); break; case 'c': result = set_parms(OUTCHKSUM, cparm, atoi(arg)); break; default: fprintf(stdout, "%s\n", k_set); } /* switch */ } if (!result) { fprintf(stdout, "%s\n", k_set); break; } opstring = skipspace(opstring); } /* while */ } /* outlinkparms */ inlinkparms(opstring) char *opstring; { int result; char word[NAMELEN], arg[NAMELEN]; char cparm; opstring = skipspace(opstring); while (*opstring != EOS) { opstring = getname(word, opstring); opstring = getname(arg, opstring); if (isupper(word[0])) *word = tolower(*word); if (*arg _eq_ EOS) fprintf(stdout, "%s\n", k_set); else if (*word != EOS) { /* if a character arguement was specified as an ASCII code, translate it */ cparm = ( *arg _eq_ '\\' ? (char) atoi(arg+1) : *arg); switch (word[0]) { case 'p': if (isupper (word[1])) word[1] = tolower(word[1]); switch (word[1]) { case 'k': result = set_parms(INPKTSIZE, cparm, atoi(arg)); break; case 'd': result = set_parms(INPAD, cparm, atoi(arg)); break; case 'c': result = set_parms(INPADCHAR, cparm, word[1]); break; default: fprintf(stdout, "%s\n", k_set); } /* end switch word[1] */ break; case 'e': result = set_parms(INEOP, cparm, *word); break; case 'q': result = set_parms(INQUOTE, cparm, *word); break; case 'c': result = set_parms(INCHKSUM, cparm, atoi(arg)); break; default: fprintf(stdout, "%s\n", k_set); } /* switch */ } if (!result) { fprintf(stdout, "%s\n", k_set); break; } opstring = skipspace(opstring); } /* while */ } /* inlinkparms */ /*****/ /* /* kermit set command: set protocol parmaeters /* opstring: option string from the user /* /*****/ set(opstring) char *opstring; { char word[NAMELEN]; int value; int speed; opstring = getname(word, opstring); if (isupper (word[0])) *word = tolower (*word); switch(word[0]) { case 'b':/* baud */ /* the constants assigned to lnkspeed are defined in sgtty.h */ opstring = getname (word, opstring); speed = atoi(word); switch (speed) { case 150: lnkspeed = B150; break; case 300: lnkspeed = B300; break; case 1200: lnkspeed = B1200; break; case 2400: lnkspeed = B2400; break; case 4800: lnkspeed = B4800; break; case 9600: lnkspeed = B9600; break; default: fprintf (stdout, "%s\n", k_badbaud); break; } break; case 's': /* send */ outlinkparms(opstring); break; case 'r': /* received */ inlinkparms(opstring); break; case 't': /* timeout */ opstring = getname(word, opstring); if (*word != EOS) { value = atoi(word); if (!set_parms(SETTIMEOUT, *word, value)) fprintf(stdout, "%s\n", k_set); } else fprintf(stdout,"usage: s[et] t[imeout] \n"); break; case 'd': if (isupper(word[1])) *(word+1) = tolower(word[1]); switch (word[1]) { case 'e': if (isupper(word[2])) *(word+2) = tolower(word[2]); switch (word[2]) { case 'l': opstring = getname(word, opstring); if (word[0] != EOS) delay = atoi(word); else fprintf (stdout, "usage: s[et] de[lay] \n"); break; case 'b': opstring = getname (word, opstring); if (word[0] != EOS) { if (strncmp (word, "on", 2) == 0) debug=TRUE; else if (strncmp (word, "off", 3) == 0) debug = FALSE; else fprintf (stdout, "usage: s[et] deb[ug] {on/off}\n"); } break; } break; case 'u': opstring = getname (word, opstring); if (isupper (word[0])) word[0] = tolower (word[0]); switch (word[0]) { case 'f': lnkduplex = FULL; break; case 'h': lnkduplex = HALF; break; default: fprintf (stdout, "usage: s[et] du[plex] {full/half}\n"); break; } break; default: fprintf (stdout, "usage: s[et] {de[lay] / du[plex]} {parameters}\n"); break; } break; default: fprintf(stdout, "%s\n", k_set); break; } /* switch */ } /* set */ /*****/ /* /* kermit command processor /* /*****/ comproc() { char inline[BUFSIZE], command[NAMELEN]; char *lineptr; char linkname[NAMELEN]; /* loop invariant: the state of the fsa is write_thru */ signal (SIGINT, int_trap); while (TRUE) { /* set up for a user interrupt */ user_interrupt = FALSE; /* prompt and get a command from the user */ fputs(prompt, stdout); if (gets(inline) _eq_ NULL) continue; lineptr = getname(command, inline); if ((command[0] != EOS) && (command[0] != '\n')&& (command[0] != '\015')) if (isupper(command[0])) command[0] = tolower (command[0]); switch( command[0] ) { case 'c': /* connect */ localprot = TRUE; /* this process initiated connection */ /* if connected before, reset communications line (close seems to be only way to do this. */ if (*lineptr == EOS) { if (lnkstat == OPEN) k_connect(); /* just reconnect */ else { /* no link name supplied - open existing/default link */ if (dlink (lnkname)) { lnkstat = OPEN; k_connect(); } else break; /* can't open link */ } } else { /* set up new link using name supplied by user */ lineptr = getname (linkname, lineptr); if (debug) fprintf (stderr, "linkname: %s, lnkname:%s\n", linkname, lnkname); if (access (linkname, 0) != 0) { fprintf (stderr, "%s does not exist\n", linkname); break; } if (dlink (linkname)) lnkstat = OPEN; else break; strncpy (lnkname, linkname, NAMELEN); k_connect(); } break; case 'e': /* exit */ case 'q': /* quit */ kquit(); return; case 'r': /* receive */ receive(lineptr); break; case 's': /* send, set, show, status */ if (isupper(command[1])) command[1] = tolower(command[1]); switch (command[1]) { case 'e': /* send or set */ if (isupper(command[2])) command[2] = tolower(command[2]); switch ( command[2]) { case 'n': /* send */ send(lineptr); break; case 't': /* set */ set(lineptr); break; default: fprintf(stdout, "%s\n", k_set); break; } /* switch */ break; case 'h': /* show */ show(); break; case 't': /* status */ status(); break; default: fprintf(stdout, "%s\n", k_set); break; } /* switch command[1] */ break; default: fprintf(stdout, "%s is not a valid Kermit command\n", command); break; } /* switch command[0] */ if (user_interrupt) { /* the user's interrupt may have trashed the link. Try to put it in order */ char status, retcode; reset(&status, &retcode); errchk("comproc", &status, &retcode); } } /* end while */ } /* comproc */ /********************************************************/ /* */ /* Kermit file transfer program */ /* */ /* system: Berkeley 4.1 (VAX UNIX) */ /* Cornell University */ /* */ /* date: May 27, 1982 */ /* */ /* programmer: Dean Jagels */ /* modified by Alison Brown September 1982 */ /* */ /********************************************************/ /********************************************************/ /* */ /* utility routines for kdlc */ /********************************************************/ #include "parms.h" #include "ptypes.h" #include "states.h" #include "globals.h" #include "status.h" #include "retcode.h" #include #include /*****/ /* /* cshift: flip the 100 (octal) bit of a character. Used in /* shifting a control character to a printable character, /* and vice-versa /* /*****/ char cshift(c) char c; { return( c ^ 0100 ); } /*****/ /* /* emptypacket: reverse the work of fillpacket /* return the packet's type, and the message in the data field of /* the packet. /* /* The returned value of emptypacket is the number of characters /* returned in message /* /*****/ int emptypacket(type, message, packet, quote) char *type; char *message; struct packetform *packet; char quote; { int index; int msgcount; index = 0; msgcount=0; while (packet->data[index] != EOS) if (packet->data[index] _eq_ quote) { if (quote _eq_ NOQUOTE) message[msgcount++] = packet->data[index++]; else { index++; message[msgcount++] = (packet->data[index] _eq_ quote ? quote : cshift(packet->data[index]) ); index++; } } else message[msgcount++] = packet->data[index++]; *type = packet->ptype; return( msgcount ); } /* emptypacket */ /*****/ /* /* itoc: map argument x into the printable ASCII characters. /* Requires 0 <= x <= 94 /* /*****/ char itoc(x) int x; { if (x<0) return(' '); else if (x>94) return('~'); else return (x+040); /* add 40 octal */ } /*****/ /* /* mytoupper: convert lower case to upper; leave rest unchanged /* /*****/ char mytoupper(c) char c; { if (islower( (int)c )) return( (char)( (int)c - 040) ); else return(c); } /*****/ /* /* fillpacket: fill in all of the particulars of a packet /* when given the type of packet and a (possibly too long) /* string of data. /* /* type: one of /* D Data /* Y ACK /* N NAK /* S Send initiate /* B Break transmission /* F File header /* Z end of file /* E Error /* /* message: a string of characters to be sent. /* /* lastchar: max number of characters to read out of message /* /* packet: the packet we're filling /* /* Fillpacket returns the number of characters actually placed in packet /* /*****/ int fillpacket(type, message, lastchar, pkt, quote) char type; char *message; int lastchar; struct packetform *pkt; char quote; { int chcount = 0; /* num of charactes in data field of packet */ int msgcount = 0; while ( (msgcount < lastchar) && (chcount < outdatamax-1) ) if (CONTROL(message[msgcount]) || (message[msgcount] _eq_ quote)) { if (quote _eq_ NOQUOTE) pkt->data[chcount++] = message[msgcount++]; else { if (chcount+2 >= outdatamax) break; pkt->data[chcount++] = quote; pkt->data[chcount++] = (message[msgcount] _eq_ quote ? quote : cshift(message[msgcount]) ); msgcount++; } } else pkt->data[chcount++] = message[msgcount++]; pkt->data[chcount] = EOS; pkt->length = itoc(chcount)+2+chksumout; pkt->seqno = itoc(NEXT_PACKET); pkt->ptype = type; compchksum(pkt,chksumout); pkt->trycount = 0; return(msgcount); } /* fillpacket */ /*****/ /* /* bracket: check that item falls within [min,max]. /* if it does, return true; /* else return false /* /*****/ int bracket(item, min, max) int item, min, max; { if (item < min) return(FALSE); else if (item > max) return(FALSE); else return(TRUE); } /*****/ /* /* ctoi: reverse of itoc; turn a printable character into an integer. /* /*****/ int ctoi(c) char c; { if (c<' ') return (0); else if (c>'~') return (94); else return (c-040); /* subtract 40 octal */ } /*****/ /* /* compchksum: compute the checksum for the outgoing packet /* /*****/ compchksum(packet, chklength) struct packetform *packet; int chklength; { int index; unsigned int sum; sum = (int) packet->length; sum += (int) packet->seqno; sum += (int) packet->ptype; for (index=0; packet->data[index] != EOS; index++) sum += (int) packet->data[index]; for (index=0; indexchksum[index] = itoc( (sum + ((sum & 0300) >> 6)) & 077 ); sum >>= 8; } } /* compchksum */ /*****/ /* /* init_world : initialize everything which must be initialized /* before leaving write_thru mode /*****/ init_world() { last_packet = -1; nak_cnt = 0; msg_waiting = FALSE; outparms_pkt.trycount = 0; inparms_pkt.trycount = 0; outparms_pkt.length = 0; outparms_pkt.seqno = EOS; inparms_pkt.length = 0; inparms_pkt.seqno = EOS; pktbuffer.length = 0; pktbuffer.seqno = EOS; pktdelivery.length = 0; pktdelivery.seqno = EOS; } /*****/ /* /* pktcopy: copy the packet from source to destination /* /*****/ pktcopy(source, dest) struct packetform *source, *dest; { int i; dest->length = source->length; dest->seqno = source->seqno; dest->ptype = source->ptype; strcpy(dest->data, source->data); for (i=0; ichksum[i] = source->chksum[i]; dest->trycount = 0; } /* pktcopy */ /*****/ /* /* sendack: build and send an ack for the given packet number /* /*****/ int sendack(num) char num; { struct packetform ackpack; #ifdef DEBUG if (debug) fprintf(stderr, "sendack:\n"); #endif ackpack.length = itoc(2+chksumout); ackpack.seqno = num; ackpack.ptype = ACK; ackpack.data[0]= EOS; compchksum( &ackpack, chksumout); ackpack.trycount = 0; return(xmit(&ackpack)); } /* sendack */ /*****/ /* /* sendnak: build and send a nak for the given number /* /*****/ int sendnak(num) char num; { struct packetform nakpack; #ifdef DEBUG if (debug) fprintf(stderr, "sendnak:\n"); #endif nakpack.length = itoc(2+chksumout); nakpack.seqno = num; nakpack.ptype = NAK; nakpack.data[0]= EOS; compchksum( &nakpack, chksumout); nakpack.trycount = 0; return(xmit(&nakpack)); } /* sendnak */ /*****/ /* /* shortstat: return a single character code of link status /* 'U' up and connected /* 'W' write_thru mode /* 'A' link aborted /* 'M' message awaiting receipt /* /*****/ char shortstat() { if (msg_waiting) return(MSG_UP); else switch (state) { case WRITE_THRU: return(WTHRU); case ABORT2: return(ABORTED); default: return(NORMAL); } /* end switch */ } /* Terminal emulator package for Kermit */ /* * Exit from the emulator is via ~. * * If the emulator package is called with file descriptor 0 (stdin) in * RAW mode, and the communications line in RAW mode, everything is * transparent except for ~. (and ~~). * */ # define TBUFSIZ 1024 /* size of buffers for term & comn line input */ # include # include # include # include struct sgttyb newmodes; /* for setting new parm on comm line */ struct sgttyb olinemodes; /* for saving old modes on comm line */ struct sgttyb ostdinmodes; /* for saving old modes on fd 0 (stdin/terminal) */ int lnkfd; /* file descriptor for comm line */ int cpid; /* process id of child copying link > stdout */ main (argc, argv) int argc; char *argv[]; { char ch; /* last char read from stdin */ void intr(); void quit(); if (argc > 1) lnkfd = atoi(argv[1]); /* file descriptor as ascii string */ else { fprintf (stderr, "Terminal emulator: no fd for comm line\n"); _exit(3); } /* SET UP COMM LINE TO BE AS TRANSPARENT AS POSSIBLE */ ioctl (lnkfd, TIOCGETP, &olinemodes); newmodes = olinemodes; newmodes.sg_flags = (EVENP | ODDP); /* no parity */ newmodes.sg_flags |= RAW; newmodes.sg_flags &= ~ECHO; newmodes.sg_ispeed = B9600; newmodes.sg_ospeed = B9600; ioctl (lnkfd, TIOCSETP, &newmodes); /* SET FD 0 (STDIN) - USUALLY THE CONTROL TERMINAL */ ioctl (0, TIOCGETP, &ostdinmodes); newmodes = ostdinmodes; newmodes.sg_flags |= RAW; if (argc > 2) { if (strcmp (argv[2], "half") == 0) newmodes.sg_flags |= ECHO; else if (strcmp (argv[2], "full") == 0) newmodes.sg_flags &= ~ECHO; /* no echo */ else { fprintf (stderr, "Terminal emulator: duplexing %s not recognized\n", argv[2]); ioctl (lnkfd, TIOCSETP, &olinemodes); /* restore link settings */ _exit (3); } } else { fprintf (stdout, "Terminal emulator: no remote host type specified\n"); ioctl (lnkfd, TIOCSETP, &olinemodes); _exit (3); } ioctl (0, TIOCSETP, &newmodes); /* FORK PROCESS TO COPY FROM COMM LINE ONTO STDOUT */ if ((cpid = fork()) == 0) { int n; char commbuf[TBUFSIZ]; /* comm line input buffer */ for(;;) { n = read (lnkfd, commbuf, TBUFSIZ); write (1, commbuf, n); } } /* THIS (PARENT) PROCESS WILL COPY FROM STDIN TO COMMLINE */ /* catch quit signals so that the appropriate cleanup actions can be */ /* performed. */ signal( SIGQUIT, quit ); signal( SIGINT, quit ); signal( SIGKILL, quit ); /* Read characters from the terminal and copy or interpret them as */ /* appropriate. */ fprintf (stdout, "Connected [~. to disconnect]\n"); ch = (015 & 0177); write (lnkfd, &ch, 1); for ( read(0, &ch, 1); 1; read(0, &ch, 1) ) { switch(ch & 0177) { case '~': /* local command to terminal emulator */ write (1, &ch, 1); /* echo tilda locally */ read (0, &ch, 1); switch (ch & 0177) { case '.': write (1, &ch, 1); cleanup(); fprintf (stdout, "\nDisconnected\n"); _exit(0); break; case '#': write (1, &ch, 1); ioctl (lnkfd, TIOCSBRK, (struct sgttyb *)NULL); sleep (1); ioctl (lnkfd, TIOCCBRK, (struct sgttyb *)NULL); break; default: write (lnkfd, &ch, 1); break; } break; default: write (lnkfd, &ch, 1); break; } } } cleanup() { kill (cpid, SIGTERM); ioctl (0, TIOCSETP, &ostdinmodes); ioctl (lnkfd, TIOCSETP, &olinemodes); } void quit() { cleanup(); exit (2); } #define TEFILE "./termemul" /* terminal emulator file name */ #define PLINE "/dev/develcon" #include #include #include #include main() { char ch; int n; int pline; /* file descriptor for comm line */ char plinestr[10]; /* ascii string with fd for comm line */ char dupstr [10]; int termpid; /* process id of child (terminal emulator) */ int retcode; /* = termpid on normal return from terminal emulator */ unsigned *texstat; /* hi byte = exit code from child (te) */ if ((pline = open (PLINE,2)) == -1) { fprintf (stderr, "Unable to open %s\n", PLINE); exit(); } ioctl (pline, TIOCEXCL, (struct sgttyb *) NULL); do { if ((termpid = fork()) == 0) { sleep (1); sprintf (plinestr, "%d", pline); strcpy (dupstr, "full"); execl (TEFILE, TEFILE, plinestr, dupstr, 0); fprintf (stdout, "Terminal emulator not found or not executable\n"); exit (4); } fprintf (stderr, "tedriver: waiting for terminal emulator to finish\n"); /* let child handle all interrupts */ signal (SIGQUIT, SIG_IGN); signal (SIGINT, SIG_IGN); if ((retcode = wait (texstat)) != termpid) { fprintf (stderr, "Error: return code %d, status %o from terminal emulator\n", retcode, texstat); exit(); } fprintf (stderr, "tedriver: status: %d\n",texstat); fprintf (stderr, "tedriver: End of terminal emulator\nMore?"); read (0, &ch, 1); } while (ch != 'n'); close (pline); } 23-Sep-82 02:26:45-EDT,85931;000000000000 Mail-from: ARPANET site UDEL-RELAY rcvd at 23-Sep-82 0217-EDT Date: 22 Sep 1982 17:34:29-EDT From: alison.cornell at UDel-Relay To: arpa.catchings at Cmu-20c Subject: Kermit source Via: Cornell; 23 Sep 82 1:57-EDT /********************************************************/ /* */ /* Kermit file transfer program */ /* */ /* system: Berkeley 4.1 (VAX UNIX) */ /* Cornell University */ /* */ /* date: May 27, 1982 */ /* */ /* programmer: Dean Jagels */ /* modified by Alison Brown September 1982 */ /* */ /********************************************************/ /********************************************************/ /* */ /* utility routines for kdlc */ /********************************************************/ #include "parms.h" #include "ptypes.h" #include "states.h" #include "globals.h" #include "status.h" #include "retcode.h" #include #include /*****/ /* /* cshift: flip the 100 (octal) bit of a character. Used in /* shifting a control character to a printable character, /* and vice-versa /* /*****/ char cshift(c) char c; { return( c ^ 0100 ); } /*****/ /* /* emptypacket: reverse the work of fillpacket /* return the packet's type, and the message in the data field of /* the packet. /* /* The returned value of emptypacket is the number of characters /* returned in message /* /*****/ int emptypacket(type, message, packet, quote) char *type; char *message; struct packetform *packet; char quote; { int index; int msgcount; index = 0; msgcount=0; while (packet->data[index] != EOS) if (packet->data[index] _eq_ quote) { if (quote _eq_ NOQUOTE) message[msgcount++] = packet->data[index++]; else { index++; message[msgcount++] = (packet->data[index] _eq_ quote ? quote : cshift(packet->data[index]) ); index++; } } else message[msgcount++] = packet->data[index++]; *type = packet->ptype; return( msgcount ); } /* emptypacket */ /*****/ /* /* itoc: map argument x into the printable ASCII characters. /* Requires 0 <= x <= 94 /* /*****/ char itoc(x) int x; { if (x<0) return(' '); else if (x>94) return('~'); else return (x+040); /* add 40 octal */ } /*****/ /* /* mytoupper: convert lower case to upper; leave rest unchanged /* /*****/ char mytoupper(c) char c; { if (islower( (int)c )) return( (char)( (int)c - 040) ); else return(c); } /*****/ /* /* fillpacket: fill in all of the particulars of a packet /* when given the type of packet and a (possibly too long) /* string of data. /* /* type: one of /* D Data /* Y ACK /* N NAK /* S Send initiate /* B Break transmission /* F File header /* Z end of file /* E Error /* /* message: a string of characters to be sent. /* /* lastchar: max number of characters to read out of message /* /* packet: the packet we're filling /* /* Fillpacket returns the number of characters actually placed in packet /* /*****/ int fillpacket(type, message, lastchar, pkt, quote) char type; char *message; int lastchar; struct packetform *pkt; char quote; { int chcount = 0; /* num of charactes in data field of packet */ int msgcount = 0; while ( (msgcount < lastchar) && (chcount < outdatamax-1) ) if (CONTROL(message[msgcount]) || (message[msgcount] _eq_ quote)) { if (quote _eq_ NOQUOTE) pkt->data[chcount++] = message[msgcount++]; else { if (chcount+2 >= outdatamax) break; pkt->data[chcount++] = quote; pkt->data[chcount++] = (message[msgcount] _eq_ quote ? quote : cshift(message[msgcount]) ); msgcount++; } } else pkt->data[chcount++] = message[msgcount++]; pkt->data[chcount] = EOS; pkt->length = itoc(chcount)+2+chksumout; pkt->seqno = itoc(NEXT_PACKET); pkt->ptype = type; compchksum(pkt,chksumout); pkt->trycount = 0; return(msgcount); } /* fillpacket */ /*****/ /* /* bracket: check that item falls within [min,max]. /* if it does, return true; /* else return false /* /*****/ int bracket(item, min, max) int item, min, max; { if (item < min) return(FALSE); else if (item > max) return(FALSE); else return(TRUE); } /*****/ /* /* ctoi: reverse of itoc; turn a printable character into an integer. /* /*****/ int ctoi(c) char c; { if (c<' ') return (0); else if (c>'~') return (94); else return (c-040); /* subtract 40 octal */ } /*****/ /* /* compchksum: compute the checksum for the outgoing packet /* /*****/ compchksum(packet, chklength) struct packetform *packet; int chklength; { int index; unsigned int sum; sum = (int) packet->length; sum += (int) packet->seqno; sum += (int) packet->ptype; for (index=0; packet->data[index] != EOS; index++) sum += (int) packet->data[index]; for (index=0; indexchksum[index] = itoc( (sum + ((sum & 0300) >> 6)) & 077 ); sum >>= 8; } } /* compchksum */ /*****/ /* /* init_world : initialize everything which must be initialized /* before leaving write_thru mode /*****/ init_world() { last_packet = -1; nak_cnt = 0; msg_waiting = FALSE; outparms_pkt.trycount = 0; inparms_pkt.trycount = 0; outparms_pkt.length = 0; outparms_pkt.seqno = EOS; inparms_pkt.length = 0; inparms_pkt.seqno = EOS; pktbuffer.length = 0; pktbuffer.seqno = EOS; pktdelivery.length = 0; pktdelivery.seqno = EOS; } /*****/ /* /* pktcopy: copy the packet from source to destination /* /*****/ pktcopy(source, dest) struct packetform *source, *dest; { int i; dest->length = source->length; dest->seqno = source->seqno; dest->ptype = source->ptype; strcpy(dest->data, source->data); for (i=0; ichksum[i] = source->chksum[i]; dest->trycount = 0; } /* pktcopy */ /*****/ /* /* sendack: build and send an ack for the given packet number /* /*****/ int sendack(num) char num; { struct packetform ackpack; #ifdef DEBUG if (debug) fprintf(stderr, "sendack:\n"); #endif ackpack.length = itoc(2+chksumout); ackpack.seqno = num; ackpack.ptype = ACK; ackpack.data[0]= EOS; compchksum( &ackpack, chksumout); ackpack.trycount = 0; return(xmit(&ackpack)); } /* sendack */ /*****/ /* /* sendnak: build and send a nak for the given number /* /*****/ int sendnak(num) char num; { struct packetform nakpack; #ifdef DEBUG if (debug) fprintf(stderr, "sendnak:\n"); #endif nakpack.length = itoc(2+chksumout); nakpack.seqno = num; nakpack.ptype = NAK; nakpack.data[0]= EOS; compchksum( &nakpack, chksumout); nakpack.trycount = 0; return(xmit(&nakpack)); } /* sendnak */ /*****/ /* /* shortstat: return a single character code of link status /* 'U' up and connected /* 'W' write_thru mode /* 'A' link aborted /* 'M' message awaiting receipt /* /*****/ char shortstat() { if (msg_waiting) return(MSG_UP); else switch (state) { case WRITE_THRU: return(WTHRU); case ABORT2: return(ABORTED); default: return(NORMAL); } /* end switch */ } /********************************************************/ /* */ /* Kermit file transfer program */ /* */ /* system: Berkeley 4.1 (VAX UNIX) */ /* Cornell University */ /* */ /* date: May 27, 1982 */ /* */ /* programmer: Dean Jagels */ /* modified by Alison Brown September 1982 */ /* */ /********************************************************/ /* Kermit data link layer */ /* */ /********************************************************/ #include #include #include "comcodes.h" #include "ptypes.h" #include "states.h" #include "status.h" #include "retcode.h" #include "parms.h" #include "set_parms.h" #include "globals.h" #include "tools.h" #include #include #include char automaton(); /*****/ /* /* set_parms: set the link parameters /* pcode is a code for the parameter to be set. /* /* pvalue is a string containing the value to be given the parameter. /* If pvalue holds a number, the number must be integer. /* /* set_parms returns false if pcode is not recognized or if /* pvalue is unacceptable. /* /* set_parms cannot be called while the link is active. /* /*****/ int set_parms(pcode, pvaluec, pvaluei) int pcode; char pvaluec; int pvaluei; { int status = TRUE; #ifdef DEBUG if (debug) fprintf(stderr, "setparms: parm %d, valuec %c, valuei %d\n", pcode, pvaluec, pvaluei); #endif if (link_active) { #ifdef DEBUG if (debug) fprintf(stderr, "set_parms: link active\n"); #endif return (FALSE); } switch (pcode) { case OUTPKTSIZE: /* size of outgoing packets */ if (status = bracket(pvaluei, MAXCHKLEN+4, MAX_SIZE)) outsize = pvaluei; outdatamax = outsize-chksumout-3; break; case INPKTSIZE: /* size of incoming packets */ if (status = bracket(pvaluei, MAXCHKLEN+4, MAX_SIZE)) insize = pvaluei; indatamax = insize-chksumin-3; break; case OUTCHKSUM: /* outgoing checksum length */ if (status = bracket(pvaluei, 0, MAXCHKLEN)) chksumout = pvaluei; outdatamax = outsize-chksumout-3; break; case INCHKSUM: /* incoming checksum length */ if (status = bracket(pvaluei, 0, MAXCHKLEN)) chksumin = pvaluei; indatamax = insize-chksumin-3; break; case OUTPAD: /* number of outgoing pad chars */ if (pvaluei < 0) status = FALSE; else padout = pvaluei; break; case INPAD: /* number of incoming pad chars */ if (pvaluei < 0) status = FALSE; else padin = pvaluei; break; case OUTPADCHAR: /* outgoing pad char */ padchout = pvaluec; break; case INPADCHAR: /* incoming pad char, of course */ padchin = pvaluec; break; case SETTIMEOUT: /* timeout interval */ if (pvaluei < 0) status = FALSE; else waittime = pvaluei; break; case OUTEOP: /* outgoing end of packet */ eopout = pvaluec; break; case INEOP: /* and incoming . . . */ eopin = pvaluec; break; case OUTQUOTE: /* quoting character out */ if CONTROL(pvaluec) status = FALSE; else quoteout = pvaluec; break; case INQUOTE: /* . . . and in */ if CONTROL(pvaluec) status = FALSE; else quotein = pvaluec; break; default: status = FALSE; break; } /* end switch */ return (status); } /* set_parms */ /*****/ /* /* read_parms: return values of link parameters /* the parameters are returned in two integer arrays. /* invector gives the values of the parameters for packets coming /* in off the line. /* outvector gives the values of the parameters for outgoing packets. /* /* For those parameters with character values, the integer equivalents /* of the characters' ASCII codes are given. /* /* vector[0] = max packet length in characters /* vector[1] = number of characters in checksum /* vector[2] = number of pad characters /* vector[3] = pad character used /* vector[4] = timeout length (same for both invector and outvector) /* vector[5] = end of packet character /* vector[6] = quote character /* /*****/ read_parms(invector, outvector) int invector[7], outvector[7]; { invector[0] = insize ; outvector[0] = outsize; invector[1] = chksumin ; outvector[1] = chksumout; invector[2] = padin ; outvector[2] = padout; invector[3] = (int) padchin ; outvector[3] = (int) padchout; invector[4] = waittime ; outvector[4] = waittime; invector[5] = (int) eopin ; outvector[5] = (int) eopout; invector[6] = (int) quotein ; outvector[6] = (int) quoteout; } /*****/ /* /* send_data: send a string over the line /* type: type of message being sent /* 'F' or 'f' filename /* 'D' or 'd' text /* 'Z' or 'z' eof /* 'E' or 'e' error /* /* message: string to send /* /* count: number of characters to send from message /* /* status: returned indication of link status /* 'U' link up and connected /* 'W' write_thru mode /* 'A' link aborted /* 'M' message awaiting receipt /* /* ret_code: returned indication of the command's results /* This code is the same as the value returned by automaton /* /*****/ send_data(type, message, count, status, ret_code) char type; char message[]; int count; char *status, *ret_code; { int i; /* #ifdef DEBUG if (debug) { fprintf(stderr, "send_data: type = %c\n", type); fprintf(stderr, " message: "); for (i=0; i 0) *ret_code = SUCCESS; else *ret_code = FAILURE; } *status = shortstat(); return; } type = mytoupper( type ); switch (type) { case DATA: break; case FILE_HEAD: if (localprot) fprintf(stdout, "sending file %s\n", message); break; case EOFILE: if (localprot) if (debug) fprintf(stdout, "EOF on local file\n"); break; case ERROR: if (localprot) fprintf(stdout, "sending error message: %s\n", message); break; default: *ret_code = ILLEGAL_REQUEST; *status = shortstat(); link_stat = sdata_msg; #ifdef DEBUG if (debug) fprintf (stderr, "%s\n", sdata_msg); #endif return; } /* end switch */ i = 0; do { i += fillpacket(type, message+i, count-i, &outpkt, quoteout); *ret_code = automaton(SEND); } while ((count > i) && (*ret_code _eq_ SUCCESS)); *status = shortstat(); } /*****/ /* /* rcv_data: read information off the link /* type: type of packet returned, coded as in send_data /* /* string: data extracted from packet /* /* len: number of characters returned in string /* /* status: returned link status as in send_data /* /* ret_code: returned indication of results, as in send_data /* /*****/ rcv_data(type, string, len, status, ret_code) char *type; char string[]; int *len; char *status; char *ret_code; { int i; if (msg_waiting) { *len = emptypacket(type, string, &pktbuffer, quotein); msg_waiting = FALSE; } else { if (state _eq_ WRITE_THRU) { if (!localprot) { *ret_code = ILLEGAL_REQUEST; link_stat = rrcv_msg; } else { *len = read (lnkfd, string, MAX_DATALEN); *ret_code = SUCCESS; *type = DATA; } *status = shortstat(); return; } else { *ret_code = automaton(RCV); *len = emptypacket(type, string, &pktdelivery, quotein); } } #ifdef DEBUG if (debug) fprintf(stderr, "rcv_data: returned type %c\n", *type); #endif if (localprot) switch (*type) { case DATA: break; case FILE_HEAD: string [*len] = '\0'; if (debug) fprintf(stderr, "receiving file named %s on remote host\n", string); break; case EOFILE: if (debug) fprintf(stderr, "received end of file\n"); break; case ERROR: fprintf(stdout, "error message from remote: "); for(i=0; i<*len; i++) putc(string[i], stdout); putc('\n', stdout); break; default: break; } /* end switch */ *status = shortstat(); } /* rcv_data */ /*****/ /* /* connect : break the link and go to write_thru mode /* status: link status /* ret_code: command results /* /*****/ connect(status, ret_code) char *status, *ret_code; { char tmp[2]; char *dummy; #ifdef DEBUG if (debug) fprintf(stderr, "connect:\n"); #endif dummy = tmp; *dummy = EOS; fillpacket(BREAK, dummy, 0, &outpkt, quoteout); *ret_code = automaton(BREAKC); *status = shortstat(); } /*****/ /* /* read_status: return exhaustive link status. /* status: one of /* 'U' up and operational /* 'W' write_thru mode /* 'A' aborted /* msg_up: true if there is a message awaiting receipt /* naks: a count of how many naks have been received /* errormsg: a message describing the most recent of abnormalities /* /*****/ read_status(status, msg_up, naks, errormsg) char *status; int *msg_up; int *naks; char errormsg[ERRLEN]; { switch(state) { case WRITE_THRU: *status = 'W'; break; case ABORT2: *status = 'A'; break; default : *status = 'U'; break; } /* end switch */ *msg_up = msg_waiting; *naks = nak_cnt; strcpy(errormsg, link_stat); } /* read_status */ /*****/ /* /* init_sending: link initialization /* status: returned link status /* ret_code: results from this command /* /*****/ init_sending(status, ret_code) char *status, *ret_code; { char msg[8]; struct sgttyb ioparms; #ifdef DEBUG if (debug) fprintf(stderr, "init_sending\n"); #endif init_world(); ioctl( lnkfd, TIOCGETP, &ioparms); ioparms.sg_ispeed = ioparms.sg_ospeed = lnkspeed; ioparms.sg_flags = EVENP | ODDP; ioparms.sg_flags |= RAW; ioparms.sg_flags &= ~ECHO; ioctl (lnkfd, TIOCSETP, &ioparms); ioctl (lnkfd, TIOCEXCL, (struct sgttyb *) NULL); msg[0] = itoc(insize); msg[1] = itoc(waittime); msg[2] = itoc(padin); msg[3] = cshift(padchin); msg[4] = itoc(eopin); msg[5] = quotein; msg[6] = itoc(chksumin); msg[7] = EOS; fillpacket(SEND_INIT, msg, 7, &outpkt, NOQUOTE); pktcopy(&outpkt, &outparms_pkt); *ret_code = automaton(INIT_SEND); *status = shortstat(); } /* init_sending */ /*****/ /* /* init_receiving: link initialization /* status: returned link status /* ret_code: results from this command /* /*****/ init_receiving(status, ret_code) char *status, *ret_code; { char msg[8]; struct sgttyb ioparms; #ifdef DEBUG if (debug) fprintf(stderr, "init_receiving\n"); #endif init_world(); ioctl( lnkfd, TIOCGETP, &ioparms); ioparms.sg_ispeed = ioparms.sg_ospeed = lnkspeed; ioparms.sg_flags = EVENP | ODDP; /* no parity */ ioparms.sg_flags |= RAW; ioparms.sg_flags &= ~ECHO; ioctl (lnkfd, TIOCSETP, &ioparms); ioctl (lnkfd, TIOCEXCL, (struct sgttyb *) NULL); ioctl (lnkfd, TIOCHPCL, (struct sgttyb *) NULL); /* hang up on exit*/ msg[0] = itoc(insize); msg[1] = itoc(waittime); msg[2] = itoc(padin); msg[3] = cshift(padchin); msg[4] = itoc(eopin); msg[5] = quotein; msg[6] = itoc(chksumin); msg[7] = EOS; fillpacket(ACK, msg, 7, &outpkt, NOQUOTE); pktcopy(&outpkt, &outparms_pkt); *ret_code = automaton(INIT_RCV); *status = shortstat(); } /* init_receiving */ /******/ /* /* dlink: open the communications line /* /******/ int dlink (stream) char *stream; { if ( (lnkfd = open (stream, 2)) < 0) { perror ("Unable to open link"); return (FALSE); } else { /* drop carrier on comm line only when kermit main process ends */ ioctl (lnkfd, TIOCHPCL, (struct sgttyb *)NULL); ioctl (lnkfd, TIOCEXCL, (struct sgttyb *)NULL); /* exclusive open */ if (debug) fprintf (stderr, "%s opened\n", stream); return (TRUE); } } /*********/ /* /* dunlink : close the communications line /* /*********/ int dunlink() { if (close (lnkfd) < 0) { fprintf (stderr, "lnkfd at close: %d\n", lnkfd); perror ("Unable to close link"); return (FALSE); } else return (TRUE); } /*****/ /* /* reset: reset after abortion /* Basically, it forces the link into write_thru /* /*****/ reset(status, retcode) char *status, *retcode; { char tmp; char *dummy; tmp = EOS; dummy = &tmp; fillpacket(BREAK, dummy, 0, &outpkt, quoteout); *retcode = automaton(RESET); *status = shortstat(); if(*status _eq_ ABORTED) { *retcode = automaton(RESET); *status = shortstat(); } } /*****/ /* /* shutdown: shut down the protocol /* /*****/ shutdown() { char status, ret_code; if ((state != ABORT2) && (state != WRITE_THRU)) connect(&status, &ret_code); state = ABORT2; } /********************************************************/ /* */ /* Kermit file transfer program */ /* */ /* system: Berkeley 4.1 (VAX UNIX) */ /* Cornell University */ /* */ /* date: May 27, 1982 */ /* */ /* programmer: Dean Jagels */ /* modified by Alison Brown September 1982 */ /* */ /********************************************************/ #include "ptypes.h" #include "parms.h" #include "globals.h" #include "tools.h" #include #include #include static int timed_out; /*****/ /* /* timetrap: deal with an alarm clock interrupt /* /*****/ timetrap(sig) int sig; { timed_out = TRUE; #ifdef DEBUG if (debug) fprintf(stderr, "rcv_string: timed out\n"); #endif } /*****/ /* /* rcv_string: read string of characters off the link /* str: space to hold the string /* len: number of bytes to read (max) /* /* returns -1 if error /* 0 if timeout /* number of characters actually read otherwise /* /*****/ int rcv_string(str, len) char *str; int len; { unsigned timeleft; int nread; #ifdef DEBUG if (debug) fprintf(stderr, "rcv_string:\n"); #endif if ( (int) signal(SIGALRM, timetrap) _eq_ -1) { #ifdef DEBUG if (debug) fprintf(stderr, "rcv_string: error in signal\n"); #endif abort(); } timed_out = FALSE; timeleft = alarm(waittime); nread = read(lnkfd, str, len); timeleft = alarm(0); #ifdef DEBUG if (debug) fprintf(stderr, "rcv_string: nread=%d, timed_out=%d\n", nread, timed_out); #endif if (nread < 0) { if (timed_out) nread = 0; #ifdef DEBUG if (debug) fprintf(stderr, "rcv_string: error in read\n"); #endif } #ifdef DEBUG str[nread] = EOS; if (debug) fprintf(stderr, "from link: %s\n", str); #endif return(nread); } /* rcv_string */ /*****/ /* /* rcv_char: provide characters off the line on at a time. /* returns -1 if read error /* 0 if timeout /* 1 if all ok /* /*****/ int rcv_char(c) char *c; { static char buffer[BUFSIZE]; static int count = -1, /* count = # of chars left in buffer */ index; /* index = index in buffer of next character to deliver */ if (count <= 0) { /* try to get something off the line */ count = rcv_string(buffer, BUFSIZE); if (count < 0) { count = 0; return(-1); } if (count _eq_ 0) return(0); index = 0; } *c = buffer[index++]; #ifdef DEBUG if (debug) fprintf(stderr, "%o ", *c); #endif count--; return(1); } /* rcv_char */ /*****/ /* /* rcv: receive a packet /* returns FALSE when anything goes wrong. In particular, returns FALSE /* when trycount exceeds maxtry. /* /* This procedure will deal with /* old data packets (sends acks for them) /* packets with checksum errors (sends naks for them) /* acks and naks for packets other than the latest packet sent /* (ignores) /* /* Therefore, if the packet returned is an ack or nak, then it applies /* to the most recently sent information packet. /* /* A returned packet with type 'T' indicates timeout. /* /* I hate this routine. I think it's the ever-present chance of timeout /* that complicates things (thus requiring tests every time you do any i.o.) /* /*****/ int rcv(pkt) struct packetform *pkt; { int pktlen, datalen; int ret_code; char c, temp[MAX_SIZE]; int i; int match, done, packetread; #ifdef DEBUG if (debug) fprintf(stderr, "rcv: have packet %d\n", ctoi(pktbuffer.seqno)); if (debug) if (localprot) fprintf(stderr, "have received packet %d\n", ctoi(pktbuffer.seqno)); #endif done = FALSE; do { if (pkt->trycount++ > maxtry) { #ifdef DEBUG if (debug) fprintf(stderr, "rcv: maxtry exceeded\n"); #endif return(FALSE); } ret_code = rcv_char(&c); if (ret_code < 0) { return(FALSE); } if (ret_code _eq_ 0) { pkt->ptype = TIMEOUT; return(TRUE); } packetread = FALSE; do { /* look for the synch character */ while (c != synchar) { ret_code = rcv_char(&c); if (ret_code < 0) { return(FALSE); } if (ret_code _eq_ 0) { pkt->ptype = TIMEOUT; return(TRUE); } } /* now try to pull out a packet */ ret_code = rcv_char(&c); if (ret_code < 0) { return(FALSE); } if (ret_code _eq_ 0) { pkt->ptype = TIMEOUT; return(TRUE); } if ((c _eq_ padchin) || (c _eq_ synchar)) continue; pkt->length = c; pktlen = ctoi(c); datalen = pktlen - 2 - chksumin; /* read enough characters to satisfy the packet */ for ( i=0; (i < pktlen) && (c != padchin) && (c != synchar); i++) { ret_code = rcv_char(&c); *(temp+i) = c; if (ret_code < 0) { return(FALSE); } if (ret_code _eq_ 0) { pkt->ptype = TIMEOUT; return(TRUE); } } packetread = (i >= pktlen); /* iseqno = temp[0]; pkt->ptype = temp[1]; for (i=0; idata[i] = temp[i+2]; pkt->data[i] = EOS; /* check the sum */ compchksum(pkt, chksumin); match = TRUE; for (i=0; ichksum[i]); if (!match) { sendnak(pktbuffer.seqno); #ifdef DEBUG if (debug) fprintf(stderr, "rcv: checksum error\n"); #endif } /* is it an old data packet ? */ else if ( (pkt->seqno _eq_ pktbuffer.seqno) && (pkt->ptype != ACK) && (pkt->ptype != NAK) ) { if (pkt->ptype _eq_ SEND_INIT) xmit(&outparms_pkt); else sendack(pktbuffer.seqno); #ifdef DEBUG if (debug) fprintf(stderr, "rcv: old data packet\n"); #endif } /* an ack directed at a packet we don't care about? */ else if ((pkt->ptype _eq_ ACK) && ( ctoi(pkt->seqno) != last_packet)) /* ignore it */ ; /* all naks presently go to the caller, so we're done */ else done = TRUE; } while (!done); if (!user_interrupt) return(TRUE); else return (FALSE); } /* rcv */ /*****/ /* /* inlnkflush: empty the input line /* /*****/ inlnkflush() { char buf[BUFSIZE]; while ( rcv_string(buf, BUFSIZE) > 0 ) /* keep looping */ ; } /*****/ /* /* xmit_char: transmit a character /* /*****/ xmit_char(c) char c; { int nwrit; #ifdef DEBUG if (debug) fprintf(stderr, "%o ", c); #endif nwrit = write(lnkfd, &c, 1); } /* xmit_char */ /*****/ /* /* xmit: send a packet down the line /* returns false if anything goes wrong. /* In particular, false is returned if the trycount of the packet /* exceeds maxtry. /* /*****/ int xmit(pkt) struct packetform *pkt; { char outstring[MAX_SIZE]; char *cptr; int outlen; int i; #ifdef DEBUG if (debug) fprintf(stderr, "xmit:packet %d\n", ctoi(pkt->seqno)); if (debug) if (localprot) fprintf(stderr, "packet number %d\n", ctoi(pkt->seqno)); #endif if (pkt->trycount++ > maxtry) { #ifdef DEBUG if (debug) fprintf(stderr, "xmit: maxtry exceeded\n"); #endif return(FALSE); } /* send pad characters */ #ifdef DEBUG if (debug) fprintf(stderr, "sending:"); #endif for (i=0; ilength; outstring[outlen++] = pkt->seqno; outstring[outlen++] = pkt->ptype; for(cptr=pkt->data; *cptr != EOS; cptr++) outstring[outlen++] = *cptr; for(i=0; ichksum[i]; write(lnkfd, outstring, outlen); #ifdef DEBUG if (debug) for (i=0; i /*****/ /* /* write-thru: automaton state write_thru /* command: should be one of /* 'I' send information (unadorned mode) /* 'S' initialize sendind /* 'R' initialize receiving /* /*****/ char write_thru(command) char command; { char type; char message[MAX_DATALEN]; int num; int i; #ifdef DEBUG if (debug) fprintf(stderr, "write_thru: command %c\n", command); #endif switch (command) { case INIT_SEND: state = S_INIT; return(SUCCESS); case INIT_RCV: state = R_INIT; return(SUCCESS); case BREAKC: case RESET: /* ignore the command; its obviously redundant */ return (SUCCESS); default: link_stat = wth_msg; #ifdef DEBUG if (debug) fprintf(stderr, "%s\n", wth_msg); #endif return(ILLEGAL_REQUEST); } /* end switch */ } /* write_thru */ /*****/ /* /* state s_init /* assumes outparms_pkt contains the appropriate send_init /*****/ char s_init() { #ifdef DEBUG if (debug) fprintf(stderr, "s_init:\n"); #endif state = S_ACK; if (!xmit(&outparms_pkt)) { stry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = stry_msg; state = ABORT; return(ABORTING); } else return (SUCCESS); } /* s_init */ /*****/ /* /* state s_ack /* /*****/ char s_ack() { char *cptr; int i; int okay; #ifdef DEBUG if (debug) fprintf(stderr, "s_ack: entry\n"); #endif if (!rcv(&inparms_pkt)) { rtry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = rtry_msg; state = ABORT; return(ABORTING); } if (inparms_pkt.ptype _eq_ ACK) { state = MAIN_LOOP; pktcopy(&inparms_pkt, &pktbuffer); cptr = inparms_pkt.data; /* the following ugliness allows trailing parameter fields to be missing */ for (i=0; (*(cptr+i) != EOS) && (i<6) ; i++) { #ifdef DEBUG if (debug) fprintf(stderr, "i=%d, *(cptr+i)=%c, okay=%d\n", i, *(cptr+i), okay); #endif switch (i) { case 0: okay = set_parms(OUTPKTSIZE, *cptr, ctoi(*cptr)); break; case 1: waittime = MAX(waittime, ctoi( *(cptr+1)) ); break; case 2: okay = set_parms(OUTPAD, *(cptr+2), ctoi(*(cptr+2)) ) || okay; break; case 3: padchout = cshift( *(cptr+3) ); break; case 4: eopout = ctoi( *(cptr+4) ); break; case 5: quoteout = *(cptr+5); break; case 6: chksumout = ctoi( *(cptr+6) ); break; default: /* this had better never happen */ abort(); } /* end switch */ } /* for loop */ #ifdef DEBUG if (debug) fprintf(stderr, "s_ack: rcvd data %s\n", inparms_pkt.data); #endif if (!okay) { link_stat = parm_msg; state = ABORT; return(ABORTING); } } else state = S_INIT; return (SUCCESS); } /* s_ack */ /*****/ /* /* state r_init /* Assumes appropriate ack packet in outparms_pkt /*****/ char r_init() { char *cptr; int i; int okay; if (!rcv(&inparms_pkt)) { rtry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = rtry_msg; state = ABORT; return(ABORTING); } if (inparms_pkt.ptype _eq_ SEND_INIT) { if (!xmit(&outparms_pkt)) { stry_cnt++; if (user_interrupt) link_stat = uint_msg; link_stat = stry_msg; state = ABORT; return(ABORTING); } pktcopy( &inparms_pkt, &pktbuffer); state = MAIN_LOOP; cptr = inparms_pkt.data; /* the following ugliness allows trailing parameter fields to be missing */ for (i=0; (*(cptr+i) != EOS) && (i<6) ; i++) { #ifdef DEBUG if (debug) fprintf(stderr, "i=%d, *(cptr+i)=%c, okay=%d\n", i, *(cptr+i), okay); #endif switch (i) { case 0: okay = set_parms(OUTPKTSIZE, *cptr, ctoi(*cptr)); break; case 1: waittime = MAX(waittime, ctoi( *(cptr+1)) ); break; case 2: okay = set_parms(OUTPAD, *(cptr+2), ctoi(*(cptr+2)) ) || okay; break; case 3: padchout = cshift( *(cptr+3) ); break; case 4: eopout = ctoi( *(cptr+4) ); break; case 5: quoteout = *(cptr+5); break; case 6: chksumout = ctoi( *(cptr+6) ); break; default: /* this had better never happen */ abort(); } /* end switch */ } #ifdef DEBUG if (debug) fprintf(stderr, "r_init: data rcvd %s\n", inparms_pkt.data); #endif if (!okay) { link_stat = parm_msg; state = ABORT; return(ABORTING); } return(SUCCESS); } else { state = R_INIT; return (SUCCESS); } } /* r_init */ /*****/ /* /* state main_loop /* /*****/ char main_loop(command) char command; { #ifdef DEBUG if (debug) fprintf(stderr, "main_loop: command %c\n", command); #endif switch(command) { case SEND: if (!xmit(&outpkt)) { stry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = stry_msg; state = ABORT; return(ABORTING); } state = AWAIT_ACK; break; case BREAKC: case RESET: if (!xmit(&outpkt)) { stry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = stry_msg; #ifdef DEBUG if (debug) fprintf(stderr, "%s\n", stry_msg); #endif state = PRE_WRITE_THRU; return(FAILURE); } state = BREAK_ACK; break; case RCV: if (msg_waiting) pktcopy( &pktbuffer, &pktdelivery); else state = INPUT; msg_waiting = FALSE; break; default: link_stat = ml_msg; #ifdef DEBUG if (debug) fprintf(stderr, "%s\n", ml_msg); #endif return(ILLEGAL_REQUEST); } /* end switch */ return (SUCCESS); } /* main_loop */ /*****/ /* /* state await_ack /* /*****/ char await_ack() { #ifdef DEBUG if (debug) fprintf(stderr, "await_ack:"); #endif if (!rcv(&inpkt)) { rtry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = rtry_msg; state = ABORT; return(ABORTING); } #ifdef DEBUG if (debug) fprintf(stderr, "received type %c\n", inpkt.ptype); #endif switch (inpkt.ptype) { case ACK: state = MAIN_LOOP; break; case NAK: nak_cnt++; case TIMEOUT: if (!xmit(&outpkt)) { stry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = stry_msg; state = ABORT; return(ABORTING); } break; case BREAK: sendack(inpkt.seqno); state = PRE_WRITE_THRU; return (FAILURE); case FILE_HEAD: case DATA: case EOFILE: case ERROR: if (!msg_waiting) { sendack(inpkt.seqno); pktcopy(&inpkt, &pktbuffer); } else /* there's already a message in the buffer */ sendnak(pktbuffer.seqno); msg_waiting = TRUE; break; default: link_stat = ack_msg; state = ABORT; return(ABORTING); } /* end switch */ return (SUCCESS); } /* await_ack */ /*****/ /* /* state break_ack /* /*****/ char break_ack() { #ifdef DEBUG if (debug) fprintf(stderr, "break_ack:"); #endif if (!rcv(&inpkt)) { rtry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = rtry_msg; state = ABORT; return(ABORTING); } #ifdef DEBUG if (debug) fprintf(stderr, "rcvd type %c\n", inpkt.ptype); #endif switch (inpkt.ptype) { case ACK: state = PRE_WRITE_THRU; return(SUCCESS); case NAK: nak_cnt++; case TIMEOUT: break; case FILE_HEAD: case DATA: case EOFILE: case ERROR: if (!msg_waiting) { sendack(inpkt.seqno); pktcopy(&inpkt, &pktbuffer); } else /* there's already a message in the buffer */ sendnak(pktbuffer.seqno); msg_waiting = TRUE; break; case BREAK: sendack(inpkt.seqno); state = PRE_WRITE_THRU; return(SUCCESS); case SEND_INIT: link_stat = break_msg; state = ABORT; return(ABORTING); } /* end switch */ if (!xmit(&outpkt)) { stry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = stry_msg; state = ABORT; return(ABORTING); } return (SUCCESS); } /* break_ack */ /*****/ /* /* state input /* /*****/ char input() { #ifdef DEBUG if (debug) fprintf(stderr, "input:"); #endif if (!rcv(&inpkt)) { rtry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = rtry_msg; state = ABORT; return(ABORTING); } #ifdef DEBUG if (debug) fprintf(stderr, "input: rcvd type %c\n", inpkt.ptype); #endif switch (inpkt.ptype) { case NAK: nak_cnt++; case ACK: state = INPUT; break; case TIMEOUT: /* we'll try re-sending an ack for the latest rcvd */ if (pktbuffer.ptype _eq_ SEND_INIT) { if (!xmit( &outparms_pkt) ) { stry_cnt++; if (user_interrupt) link_stat = uint_msg; else link_stat = stry_msg; state = ABORT; return(ABORTING); } } else sendack(pktbuffer.seqno); break; case FILE_HEAD: case DATA: case EOFILE: case ERROR: sendack(inpkt.seqno); state = MAIN_LOOP; pktcopy( &inpkt, &pktbuffer); pktcopy( &inpkt, &pktdelivery); break; case BREAK: sendack(inpkt.seqno); state = PRE_WRITE_THRU; return(FAILURE); default: link_stat = input_msg; state = ABORT; return(ABORTING); } /* end switch */ return(SUCCESS); } /* input */ /*****/ /* /* automaton: step through the fsa guiding the protocol. /* command: one of /* 'D' send information packet (data, filename, eof, error) /* 'G' receive (get) an information packet /* 'B' break the link and go to write_thru mode /* 'S' initialize sending /* 'R' initialize receiving /* 'A' reset after abort /* /* NOTE: All pertinent packets (send initiate, ack for initialization, /* break, etc.) must be built and ready for output before entering /* the automaton. If the automaton were required to construct any /* packets, then the send or receive try counts could not be kept for /* those packets. (Creating a packet implies initializing trycount) /* /* Returns a link status: /* 'I' Illegal command /* 'A' Aborting. Due to persistent link errors, the link has /* been shut down. /* 'F' Failure. The command was not completed, and the link /* has gone into write_thru mode. Generally occurs /* whenever a break is received. Also, when executing /* a connect command, Failure is returned if no ack /* is received from the other end of the link. /* 'S' Success. The command was successfully completed. /* /* In addition, on return the global msg_waiting is set to TRUE if /* there is a message waiting for input. /* /*****/ char automaton(command) char command; { char status; #ifdef DEBUG if (debug) fprintf(stderr, "automaton: command %c\n", command); #endif fflush(stderr); inpkt.trycount = 0; inpkt.data[0] = EOS; do switch (state) { case PRE_WRITE_THRU: inlnkflush(); state = WRITE_THRU; break; case WRITE_THRU: status = write_thru(command); break; case S_INIT: status = s_init(); break; case S_ACK: status = s_ack(); break; case R_INIT: status = r_init(); break; case MAIN_LOOP: status = main_loop(command); break; case AWAIT_ACK: status = await_ack(); break; case BREAK_ACK: status = break_ack(); break; case INPUT: status = input(); break; case ABORT: { char tmp = EOS; char *dummy = &tmp; fillpacket(BREAK, dummy, 0, &outpkt, quoteout); main_loop (RESET); state = ABORT2; } break; case ABORT2: status = SUCCESS; state = WRITE_THRU; default: ; } /* end switch */ while (( state != MAIN_LOOP) && (state != WRITE_THRU) && (state != ABORT2 )); #ifdef DEBUG if (debug) fprintf(stderr, "automaton: returning state %d\n", state); #endif if (state _eq_ MAIN_LOOP) link_active = TRUE; else link_active = FALSE; if (state _eq_ ABORT2) { if (user_interrupt) link_stat = uint_msg; fprintf(stdout, "Aborting: %s\n", link_stat); } return (status); } /* automaton */ /********************************************************/ /* */ /* Kermit file transfer program */ /* */ /* system: Berkeley 4.1 (VAX UNIX) */ /* Cornell University */ /* */ /* date: May 27, 1982 */ /* */ /* programmer: Dean Jagels */ /* modified by Alison Brown September 1982 */ /* */ /********************************************************/ #include "status.h" #include "retcode.h" #include "comcodes.h" #include "parms.h" #include "set_parms.h" #include "kerm.h" #include "globals.h" #include "ptypes.h" #include #include #include #include #include unsigned delay = 4; extern char prompt[]; char k_head[] = "receive: file header missing"; char k_eof[] = "receive: end of file missing"; char k_shortw[] = "receive: incomplete write to output file"; char k_set[] = "invalid set command - type help set for how to use set"; char k_badbaud[]= "usage: s[et] b[aud] { 150/300/1200/2400/4800/9600 }"; char k_duplex[] = "usage: s[et] du[plex] { full/half }"; /*****/ /* /* trap for user interrupts /* /*****/ int_trap(sig) int sig; { #ifdef DEBUG if (debug) fprintf(stderr, "user interrupt caught\n"); #endif fprintf (stdout, "\n"); user_interrupt = TRUE; signal (SIGINT, int_trap); } /*****/ /* /* erreport : report an error and log it to stderr /* /*****/ erreport (error) char *error; { char status, retcode; int i; char *cptr; fprintf(stderr, "%s\n", error); if (localprot) fprintf(stdout, "%s\n", error); else { for(i=0, cptr=error; *cptr!=EOS; i++, cptr++); send_data(ERROR, error, i, &status, &retcode); } } /*****/ /* /* errchk: error watch for kermit commands /* /*****/ int errchk(routine, status, retcode) char *routine; char *status, *retcode; { char ptype, pstring[MAX_DATALEN]; int len; int i; if (*status _eq_ MSG_UP) { rcv_data(&ptype, pstring, &len, status, retcode); if ((ptype _eq_ ERROR) && localprot) { fprintf(stdout, "error received: "); for(i=0; i seconds */ #ifdef DEBUG if (debug) fprintf(stderr, "kermit send entered\n"); #endif sleep(delay); /* let the UNIX shell expand the wildcards in the file specs. Only on UNIX . . . */ list = llist; sprintf(list, "echo %s", filelst); filestream = popen(list, "r"); fgets(list, BUFSIZE, filestream); fstatus = pclose(filestream); /* Thus far massaging the input line. Lets get the link rolling */ if (lnkstat == CLOSED) { fprintf (stdout, "Must connect to remote machine before sending\n"); return; } init_sending(&lstatus, &retcode); if (!errchk("send", &lstatus, &retcode)) return; list = getname(filenm, list); while (*filenm != EOS) { /* we have a file name in filenm. Try to open it */ fd = open(filenm, 0); if (fd < 0) perror(filenm); else { for(i=0, cptr=filenm; *cptr!=EOS; i++, cptr++); send_data(FILE_HEAD, filenm, i, &lstatus, &retcode); if (!errchk("send", &lstatus, &retcode)) return; n_read = read(fd, buffer, BUFSIZE); while (n_read > 0) { send_data(DATA, buffer, n_read, &lstatus, &retcode); if (!errchk("send", &lstatus, &retcode)) return; n_read = read(fd, buffer, BUFSIZE); } if (n_read < 0) perror("kermit send"); send_data(EOFILE, "", 0, &lstatus, &retcode); if (!errchk("send", &lstatus, &retcode)) return; if(close(fd) < 0) perror ("kermit send"); } /* end else */ fprintf (stdout, "file transfer complete\n"); list = getname(filenm, list); } /* end while */ connect (&lstatus, &retcode); if (!errchk("send", &lstatus, &retcode)) { return; } } /* send */ /*****/ /* /* more_data : used by kermit receive command to receive data from the /* link and perform some policing. /* In particular, it forces the delivery of packets in the order /* ___ ___ /* | | * /* | file header | /* | [ data ]* | /* | end of file | /* |__ __| /* /* break /* /* Note that the break is fabricated by more_data. /* The data link doesn't return such things. /* /*****/ more_data (ptype, pstring, len, status, retcode) char *ptype; char pstring[]; int *len; char *status, *retcode; { int i; static int indata = FALSE, shutdown = FALSE, msg_held = FALSE; /* /* semantics of indata: /* if last packet returned was file header indata = TRUE /* if last packet returned was data, indata = TRUE /* if last packet returned was end of file, indata = FALSE /* if last packet returned was break, indata = FALSE */ static char holdtype, holdstring[MAX_DATALEN], holdstat, holdret; static int holdlen; #ifdef DEBUG if (debug) fprintf(stderr, "more_data: shutdown=%d indata=%d msg_held=%d", shutdown, indata, msg_held); #endif if (msg_held) { msg_held = FALSE; *ptype = holdtype; strcpy(pstring, holdstring); *len = holdlen; *status = holdstat; *retcode = holdret; return; } if (!shutdown) { rcv_data (ptype, pstring, len, status, retcode); if (*retcode != SUCCESS) { if (*status _eq_ WTHRU) { if (localprot) fprintf(stdout, "file transfer complete\n"); } else errchk("receive", status, retcode); if (indata) { shutdown = TRUE; *ptype = EOFILE; indata = FALSE; } else { *ptype = BREAK; shutdown = FALSE; msg_held = FALSE; } return; } switch(*ptype) { case DATA: if (!indata) { indata = TRUE; erreport (k_head); msg_held = TRUE; holdtype = *ptype; strcpy(holdstring, pstring); holdlen = *len; holdstat = *status; holdret = *retcode; *ptype = FILE_HEAD; strcpy (pstring, "kermXXXXXX"); *len = 10; mktemp(pstring); fprintf(stdout, "received data stored in %s\n", pstring); } /* if indata */ break; case ERROR: if (localprot) { fprintf(stdout, "error received: "); for(i=0; i<*len; i++) putc(pstring[i], stdout); putc('\n', stdout); } more_data(ptype, pstring, len, status, retcode); break; case FILE_HEAD: if (indata) { indata = FALSE; erreport(k_eof); msg_held = TRUE; holdtype = *ptype; strcpy(holdstring, pstring); holdlen = *len; holdstat = *status; holdret = *retcode; *ptype = EOFILE; *len = 0; } else indata = TRUE; break; case EOFILE: if (!indata) fprintf (stdout, "Warning: extra EOF received\n"); else indata = FALSE; break; } /* end switch */ } /* end if shutdown */ else { *ptype = BREAK; *len = 0; /* set the static booleans in preparation for the next receive command */ indata = FALSE; shutdown = FALSE; msg_held = FALSE; } } /* end more_data */ /*****/ /* /* receive command: receive a series of files /* syntax: receive [file] /* /* file contains a (possibly null) string which is the filename under /* which the first file received is stored. /* /*****/ receive (file) char *file; { #define PROTECTION 0644 char ptype, pstring[MAX_DATALEN]; int len; char status, retcode; char *filetmp; int fd, nwrit; /* stall for seconds */ #ifdef DEBUG if (debug) fprintf(stderr, "kermit receive entered\n"); #endif sleep(delay); file = skipspace(file); if (lnkstat == CLOSED) { fprintf (stdout, "Must connect to remote machine before receiving\n"); return; } init_receiving(&status, &retcode); if (!errchk("receive", &status, &retcode)) return; more_data(&ptype, pstring, &len, &status, &retcode); if (*file _eq_ EOS) { filetmp = pstring; pstring[len] = EOS; } else filetmp = file; while (ptype != BREAK) { /* open the file */ fd = creat(filetmp, PROTECTION); if (fd < 0) { perror("kermit receive"); do more_data(&ptype, pstring, &len, &status, &retcode); while (ptype != EOFILE); } else { fprintf (stdout, "receiving - remote filename %s, local filename %s\n", pstring, filetmp); more_data(&ptype, pstring, &len, &status, &retcode); while (ptype != EOFILE) { /* write the packet to the file */ nwrit = write(fd, pstring, len); if (nwrit < 0) { perror("kermit receive"); do more_data(&ptype, pstring, &len, &status, &retcode); while (ptype != EOFILE); } else { if (nwrit != len) fprintf(stderr, "%s\n", k_shortw); more_data(&ptype, pstring, &len, &status, &retcode); } } /* while */ /* close the file */ if (close(fd) < 0) perror ("kermit receive"); if (user_interrupt) if (unlink (filetmp) < 0) fprintf (stdout, "file %s invalid - please remove\n", filetmp); } /* else */ /* read in break packet or header for next file at end of file transfer */ more_data(&ptype, pstring, &len, &status, &retcode); if (ptype == BREAK) continue; filetmp = pstring; pstring[len] = EOS; } /* while */ } /* receive */ /*****/ /* /* kermit connect command - exec the terminal emulator /* /*****/ k_connect() { int n; int termpid; /* process id of child (terminal emulator) */ int texstat; /* exit code returned by child execing term emul */ int retcode; /* = termpid on normal return from terminal emulator */ char fdstr[10]; /* comm line file descriptor (ascii string) */ char speedstr[10]; /* comm line speed (ascii string) */ char duplexstr[10]; /* remote host half or full duplex (echo or not) */ if (debug) fprintf (stderr, "k_connect entered\n"); if ((termpid = fork()) == 0) { sprintf (fdstr, "%d", lnkfd); sprintf (speedstr, "%d", lnkspeed); if (lnkduplex == FULL) strcpy (duplexstr, "full"); else if (lnkduplex == HALF) strcpy (duplexstr, "half"); execl (TEFILE, TEFILE, fdstr, duplexstr, speedstr, 0); fprintf (stdout, "Terminal emulator not found or not executable\n"); exit (4); } /* let child handle all interrupts */ signal (SIGQUIT, SIG_IGN); signal (SIGINT, SIG_IGN); if ((retcode = wait (&texstat)) != termpid) { fprintf (stderr, "Error: return code %d, status %o from terminal emulator\n", retcode, texstat); exit(); } if (debug) fprintf (stderr, "return to k_connect from terminal emulator\n"); signal (SIGINT, int_trap); signal (SIGQUIT, SIG_DFL); } /* k_connect */ /*****/ /* /* kermit show command: print the link's parameters /* /*****/ char sformat[] = "%29s %-10s %-10s\n"; char cformat[] = "%29s %-10c %-10c\n"; char iformat[] = "%29s %-10d %-10d\n"; char oformat[] = "%29s %-10o %-10o\n"; char bformat[] = "link operating at %d baud %s duplex\n"; char duplexing[5]; show() { int invector[7], outvector[7]; read_parms(invector, outvector); fprintf (stdout, "\n"); fprintf (stdout, "Parameters for link %s \n", lnkname); fprintf(stdout, "\n"); fprintf(stdout,sformat, " ", "incoming", "outgoing"); fprintf(stdout,iformat, "max packet length", invector[0], outvector[0]); fprintf(stdout,iformat, "number of checksum characters", invector[1], outvector[1]); fprintf(stdout,iformat, "number of pad characters", invector[2], outvector[2]); fprintf(stdout,oformat, "pad character", invector[3], outvector[3]); fprintf(stdout,iformat, "timeout length (sec)", invector[4], outvector[4]); fprintf(stdout,oformat, "end of packet", invector[5], outvector[5]); fprintf(stdout,cformat, "quote character", invector[6], outvector[6]); fprintf(stdout, "\n"); fprintf(stdout, "pre-file transfer delay: %d\n", delay); if (lnkduplex == FULL) strcpy (duplexing, "full"); else if (lnkduplex == HALF) strcpy (duplexing, "half"); if (lnkspeed == B300) fprintf(stdout,bformat, 300, duplexing); else if (lnkspeed == B1200) fprintf (stdout, bformat, 1200, duplexing); else if (lnkspeed == B2400) fprintf (stdout, bformat, 2400, duplexing); else if (lnkspeed == B4800) fprintf (stdout, bformat, 4800, duplexing); else if (lnkspeed == B9600) fprintf (stdout, bformat, 9600, duplexing); else fprintf (stdout, "link baud rate invalid\n"); fprintf (stdout, "link status: "); if (lnkstat == OPEN) fprintf (stdout, "open\n"); else fprintf (stdout, "closed\n"); if (debug == TRUE) fprintf (stdout, "debugging on\n"); else fprintf (stdout, "debugging off\n"); fprintf (stdout, "\n"); } /* show */ /*****/ /* /* kermit quit command : quit the whole shabang /* /*****/ kquit() { shutdown(); /* shut down the protocol */ if (lnkstat == OPEN) dunlink() ; /* close link */ exit(0); } /*****/ /* /* kermit status command: show detailed link status /* /*****/ status() { char stat, errormsg[ERRLEN]; int msg_up, naks; read_status(&stat, &msg_up, &naks, errormsg); if (msg_up) fprintf(stdout, "a message is awaiting receipt\n"); fprintf(stdout, "Description of most recent error:\n"); fprintf(stdout, "%s\n", errormsg); } /*****/ /* /* linkparms: set link parameters /* called by set to decipher its parm list /* /*****/ outlinkparms(opstring) char *opstring; { int result; char word[NAMELEN], arg[NAMELEN]; char cparm; opstring = skipspace(opstring); while (*opstring != EOS) { opstring = getname(word, opstring); opstring = getname(arg, opstring); if (isupper(word[0])) word[0] = tolower(word[0]); if (*arg _eq_ EOS) fprintf(stdout, "%s\n", k_set); else if (word[0]!= EOS) { /* if a character arguement was specified as an ASCII code, translate it */ if (*arg _eq_ '\\') { int temp; int tempo; temp = atoi(arg+1); tempo = temp%10; temp /= 10; tempo += 8 * (temp%10); temp /= 10; tempo += 64 * temp; cparm = (char) tempo; } else cparm = *arg; switch (word[0]) { case 'p': if (isupper(word[1])) word[1] = tolower(word[1]); switch (word[1]) { case 'k': result = set_parms(OUTPKTSIZE, cparm, atoi(arg)); break; case 'd': result = set_parms(OUTPAD, cparm, atoi(arg)); break; case 'c': result = set_parms(OUTPADCHAR, cparm, word[1]); break; default: fprintf(stdout, "%s\n", k_set); } /* end switch word[1] */ case 'e': result = set_parms(OUTEOP, cparm, word[1]); break; case 'q': result = set_parms(OUTQUOTE, cparm, word[1]); break; case 'c': result = set_parms(OUTCHKSUM, cparm, atoi(arg)); break; default: fprintf(stdout, "%s\n", k_set); } /* switch */ } if (!result) { fprintf(stdout, "%s\n", k_set); break; } opstring = skipspace(opstring); } /* while */ } /* outlinkparms */ inlinkparms(opstring) char *opstring; { int result; char word[NAMELEN], arg[NAMELEN]; char cparm; opstring = skipspace(opstring); while (*opstring != EOS) { opstring = getname(word, opstring); opstring = getname(arg, opstring); if (isupper(word[0])) *word = tolower(*word); if (*arg _eq_ EOS) fprintf(stdout, "%s\n", k_set); else if (*word != EOS) { /* if a character arguement was specified as an ASCII code, translate it */ cparm = ( *arg _eq_ '\\' ? (char) atoi(arg+1) : *arg); switch (word[0]) { case 'p': if (isupper (word[1])) word[1] = tolower(word[1]); switch (word[1]) { case 'k': result = set_parms(INPKTSIZE, cparm, atoi(arg)); break; case 'd': result = set_parms(INPAD, cparm, atoi(arg)); break; case 'c': result = set_parms(INPADCHAR, cparm, word[1]); break; default: fprintf(stdout, "%s\n", k_set); } /* end switch word[1] */ break; case 'e': result = set_parms(INEOP, cparm, *word); break; case 'q': result = set_parms(INQUOTE, cparm, *word); break; case 'c': result = set_parms(INCHKSUM, cparm, atoi(arg)); break; default: fprintf(stdout, "%s\n", k_set); } /* switch */ } if (!result) { fprintf(stdout, "%s\n", k_set); break; } opstring = skipspace(opstring); } /* while */ } /* inlinkparms */ /*****/ /* /* kermit set command: set protocol parmaeters /* opstring: option string from the user /* /*****/ set(opstring) char *opstring; { char word[NAMELEN]; int value; int speed; opstring = getname(word, opstring); if (isupper (word[0])) *word = tolower (*word); switch(word[0]) { case 'b':/* baud */ /* the constants assigned to lnkspeed are defined in sgtty.h */ opstring = getname (word, opstring); speed = atoi(word); switch (speed) { case 150: lnkspeed = B150; break; case 300: lnkspeed = B300; break; case 1200: lnkspeed = B1200; break; case 2400: lnkspeed = B2400; break; case 4800: lnkspeed = B4800; break; case 9600: lnkspeed = B9600; break; default: fprintf (stdout, "%s\n", k_badbaud); break; } break; case 's': /* send */ outlinkparms(opstring); break; case 'r': /* received */ inlinkparms(opstring); break; case 't': /* timeout */ opstring = getname(word, opstring); if (*word != EOS) { value = atoi(word); if (!set_parms(SETTIMEOUT, *word, value)) fprintf(stdout, "%s\n", k_set); } else fprintf(stdout,"usage: s[et] t[imeout] \n"); break; case 'd': if (isupper(word[1])) *(word+1) = tolower(word[1]); switch (word[1]) { case 'e': if (isupper(word[2])) *(word+2) = tolower(word[2]); switch (word[2]) { case 'l': opstring = getname(word, opstring); if (word[0] != EOS) delay = atoi(word); else fprintf (stdout, "usage: s[et] de[lay] \n"); break; case 'b': opstring = getname (word, opstring); if (word[0] != EOS) { if (strncmp (word, "on", 2) == 0) debug=TRUE; else if (strncmp (word, "off", 3) == 0) debug = FALSE; else fprintf (stdout, "usage: s[et] deb[ug] {on/off}\n"); } break; } break; case 'u': opstring = getname (word, opstring); if (isupper (word[0])) word[0] = tolower (word[0]); switch (word[0]) { case 'f': lnkduplex = FULL; break; case 'h': lnkduplex = HALF; break; default: fprintf (stdout, "usage: s[et] du[plex] {full/half}\n"); break; } break; default: fprintf (stdout, "usage: s[et] {de[lay] / du[plex]} {parameters}\n"); break; } break; default: fprintf(stdout, "%s\n", k_set); break; } /* switch */ } /* set */ /*****/ /* /* kermit command processor /* /*****/ comproc() { char inline[BUFSIZE], command[NAMELEN]; char *lineptr; char linkname[NAMELEN]; /* loop invariant: the state of the fsa is write_thru */ signal (SIGINT, int_trap); while (TRUE) { /* set up for a user interrupt */ user_interrupt = FALSE; /* prompt and get a command from the user */ fputs(prompt, stdout); if (gets(inline) _eq_ NULL) continue; lineptr = getname(command, inline); if ((command[0] != EOS) && (command[0] != '\n')&& (command[0] != '\015')) if (isupper(command[0])) command[0] = tolower (command[0]); switch( command[0] ) { case 'c': /* connect */ localprot = TRUE; /* this process initiated connection */ /* if connected before, reset communications line (close seems to be only way to do this. */ if (*lineptr == EOS) { if (lnkstat == OPEN) k_connect(); /* just reconnect */ else { /* no link name supplied - open existing/default link */ if (dlink (lnkname)) { lnkstat = OPEN; k_connect(); } else break; /* can't open link */ } } else { /* set up new link using name supplied by user */ lineptr = getname (linkname, lineptr); if (debug) fprintf (stderr, "linkname: %s, lnkname:%s\n", linkname, lnkname); if (access (linkname, 0) != 0) { fprintf (stderr, "%s does not exist\n", linkname); break; } if (dlink (linkname)) lnkstat = OPEN; else break; strncpy (lnkname, linkname, NAMELEN); k_connect(); } break; case 'e': /* exit */ case 'q': /* quit */ kquit(); return; case 'r': /* receive */ receive(lineptr); break; case 's': /* send, set, show, status */ if (isupper(command[1])) command[1] = tolower(command[1]); switch (command[1]) { case 'e': /* send or set */ if (isupper(command[2])) command[2] = tolower(command[2]); switch ( command[2]) { case 'n': /* send */ send(lineptr); break; case 't': /* set */ set(lineptr); break; default: fprintf(stdout, "%s\n", k_set); break; } /* switch */ break; case 'h': /* show */ show(); break; case 't': /* status */ status(); break; default: fprintf(stdout, "%s\n", k_set); break; } /* switch command[1] */ break; default: fprintf(stdout, "%s is not a valid Kermit command\n", command); break; } /* switch command[0] */ if (user_interrupt) { /* the user's interrupt may have trashed the link. Try to put it in order */ char status, retcode; reset(&status, &retcode); errchk("comproc", &status, &retcode); } } /* end while */ } /* comproc */ /********************************************************/ /* */ /* Kermit file transfer program */ /* */ /* system: Berkeley 4.1 (VAX UNIX) */ /* Cornell University */ /* */ /* date: May 27, 1982 */ /* */ /* programmer: Dean Jagels */ /* modified by Alison Brown September 1982 */ /* */ /********************************************************/ /********************************************************/ /* */ /* main procedure for the kermit file transfer layer */ /* */ /********************************************************/ #include "kerm.h" #include "parms.h" #include "globals.h" #include char prompt[NAMELEN]; /* the main procedure, diminutive though it is */ main() { strcpy(prompt, "Kermit-Unix>"); comproc(); } /* main */ /* Terminal emulator package for Kermit */ /* * Exit from the emulator is via ~. * * If the emulator package is called with file descriptor 0 (stdin) in * RAW mode, and the communications line in RAW mode, everything is * transparent except for ~. (and ~~). * */ # define TBUFSIZ 1024 /* size of buffers for term & comn line input */ # include # include # include # include struct sgttyb newmodes; /* for setting new parm on comm line */ struct sgttyb olinemodes; /* for saving old modes on comm line */ struct sgttyb ostdinmodes; /* for saving old modes on fd 0 (stdin/terminal) */ int lnkfd; /* file descriptor for comm line */ int cpid; /* process id of child copying link > stdout */ main (argc, argv) int argc; char *argv[]; { char ch; /* last char read from stdin */ void intr(); void quit(); if (argc > 1) lnkfd = atoi(argv[1]); /* file descriptor as ascii string */ else { fprintf (stderr, "Terminal emulator: no fd for comm line\n"); _exit(3); } /* SET UP COMM LINE TO BE AS TRANSPARENT AS POSSIBLE */ ioctl (lnkfd, TIOCGETP, &olinemodes); newmodes = olinemodes; newmodes.sg_flags = (EVENP | ODDP); /* no parity */ newmodes.sg_flags |= RAW; newmodes.sg_flags &= ~ECHO; newmodes.sg_ispeed = B9600; newmodes.sg_ospeed = B9600; ioctl (lnkfd, TIOCSETP, &newmodes); /* SET FD 0 (STDIN) - USUALLY THE CONTROL TERMINAL */ ioctl (0, TIOCGETP, &ostdinmodes); newmodes = ostdinmodes; newmodes.sg_flags |= RAW; if (argc > 2) { if (strcmp (argv[2], "half") == 0) newmodes.sg_flags |= ECHO; else if (strcmp (argv[2], "full") == 0) newmodes.sg_flags &= ~ECHO; /* no echo */ else { fprintf (stderr, "Terminal emulator: duplexing %s not recognized\n", argv[2]); ioctl (lnkfd, TIOCSETP, &olinemodes); /* restore link settings */ _exit (3); } } else { fprintf (stdout, "Terminal emulator: no remote host type specified\n"); ioctl (lnkfd, TIOCSETP, &olinemodes); _exit (3); } ioctl (0, TIOCSETP, &newmodes); /* FORK PROCESS TO COPY FROM COMM LINE ONTO STDOUT */ if ((cpid = fork()) == 0) { int n; char commbuf[TBUFSIZ]; /* comm line input buffer */ for(;;) { n = read (lnkfd, commbuf, TBUFSIZ); write (1, commbuf, n); } } /* THIS (PARENT) PROCESS WILL COPY FROM STDIN TO COMMLINE */ /* catch quit signals so that the appropriate cleanup actions can be */ /* performed. */ signal( SIGQUIT, quit ); signal( SIGINT, quit ); signal( SIGKILL, quit ); /* Read characters from the terminal and copy or interpret them as */ /* appropriate. */ fprintf (stdout, "Connected [~. to disconnect]\n"); ch = (015 & 0177); write (lnkfd, &ch, 1); for ( read(0, &ch, 1); 1; read(0, &ch, 1) ) { switch(ch & 0177) { case '~': /* local command to terminal emulator */ write (1, &ch, 1); /* echo tilda locally */ read (0, &ch, 1); switch (ch & 0177) { case '.': write (1, &ch, 1); cleanup(); fprintf (stdout, "\nDisconnected\n"); _exit(0); break; case '#': write (1, &ch, 1); ioctl (lnkfd, TIOCSBRK, (struct sgttyb *)NULL); sleep (1); ioctl (lnkfd, TIOCCBRK, (struct sgttyb *)NULL); break; default: write (lnkfd, &ch, 1); break; } break; default: write (lnkfd, &ch, 1); break; } } } cleanup() { kill (cpid, SIGTERM); ioctl (0, TIOCSETP, &ostdinmodes); ioctl (lnkfd, TIOCSETP, &olinemodes); } void quit() { cleanup(); exit (2); } #define TEFILE "./termemul" /* terminal emulator file name */ #define PLINE "/dev/develcon" #include #include #include #include main() { char ch; int n; int pline; /* file descriptor for comm line */ char plinestr[10]; /* ascii string with fd for comm line */ char dupstr [10]; int termpid; /* process id of child (terminal emulator) */ int retcode; /* = termpid on normal return from terminal emulator */ unsigned *texstat; /* hi byte = exit code from child (te) */ if ((pline = open (PLINE,2)) == -1) { fprintf (stderr, "Unable to open %s\n", PLINE); exit(); } ioctl (pline, TIOCEXCL, (struct sgttyb *) NULL); do { if ((termpid = fork()) == 0) { sleep (1); sprintf (plinestr, "%d", pline); strcpy (dupstr, "full"); execl (TEFILE, TEFILE, plinestr, dupstr, 0); fprintf (stdout, "Terminal emulator not found or not executable\n"); exit (4); } fprintf (stderr, "tedriver: waiting for terminal emulator to finish\n"); /* let child handle all interrupts */ signal (SIGQUIT, SIG_IGN); signal (SIGINT, SIG_IGN); if ((retcode = wait (texstat)) != termpid) { fprintf (stderr, "Error: return code %d, status %o from terminal emulator\n", retcode, texstat); exit(); } fprintf (stderr, "tedriver: status: %d\n",texstat); fprintf (stderr, "tedriver: End of terminal emulator\nMore?"); read (0, &ch, 1); } while (ch != 'n'); close (pline); } /********************************************************/ /* */ /* Kermit file transfer program */ /* */ /* system: Berkeley 4.1 (VAX UNIX) */ /* Cornell University */ /* */ /* date: May 27, 1982 */ /* */ /* programmer: Dean Jagels */ /* modified by Alison Brown September 1982 */ /* */ /********************************************************/ /********************************************************/ /* */ /* global data definitions for kermit */ /* */ /* any changes here must be reflected in globals.h */ /********************************************************/ #include "states.h" #include "parms.h" #include "kerm.h" #include struct packetform { char length; /* packet length */ char seqno; /* packet number */ char ptype; /* packet type */ char data[MAX_DATALEN]; char chksum[MAXCHKLEN]; /* checksum characters */ int trycount; /* num of attempts at sending this packet */ }; /* Link Information */ int localprot = FALSE, /* is this process local or remote ? */ link_active = FALSE, /* is the link active? (not in write-thru mode) */ last_packet = -1, /* seq num of the last packet built */ state = WRITE_THRU; /* automaton state */ debug = FALSE; /* running debug mode ? */ int lnkfd; /* file descriptor for comm line */ /* Link Parameters (set by set command) */ int lnkstat = CLOSED, /* =OPEN or =CLOSED */ lnkduplex = FULL; /* comm line is FUll or HALF duplex */ char lnkspeed = B9600; char lnkname[NAMELEN] = "/dev/develcon"; int outsize = MAX_SIZE, /* max length of packets */ insize = MAX_SIZE, outdatamax = MAX_DATALEN, /* max length of data in packets */ indatamax = MAX_DATALEN, chksumout = CHKSUMLEN, /* num of chars for checksum */ chksumin = CHKSUMLEN, padout = NUMPAD, /* num of pad chars in each direction */ padin = NUMPAD, maxtry = MAX_TRY, /* max number of times to retry a packet */ stry_cnt = 0, /* number of major send errors */ rtry_cnt = 0, /* number of major receive errors */ nak_cnt = 0; /* number of naks received */ unsigned waittime= WAITTIME; /* timeout interval */ char padchout= PADCH, /* pad characters */ padchin = PADCH, eopout = EOPOUT, /* end of packet chars */ eopin = EOPIN, quoteout= QUOTE, /* quoting chars */ quotein = QUOTE; synchar = SYNCHAR; /* marks begin of packet */ *link_stat; /* textual message of link status */ /* packet storage */ int msg_waiting = FALSE; /* is there a message in pktbuffer? */ struct packetform outpkt, /* outgoing packet */ inpkt, /* incoming packet */ outparms_pkt, /* parameters I request: send_init or ack */ inparms_pkt, /* parameters requested by other end */ pktbuffer, /* a copy of the latest data packet rcvd */ pktdelivery; /* packet delivery out of automaton */ /* A bit of explaination of the roles of inpkt and pktbuffer. inpkt is the space into which all new incoming packets are placed. These incoming packets may include ack, nak, and timeout notices. pktbuffer, on the other hand, holds only the latest information packet (data, filename, eof, or error). This is where you go when you have to re-acknowledge the latest data received. */ /* error messages */ char stry_msg[] = "too many line errors sending, or remote kermit not up"; char rtry_msg[] = "too many line errors receiving, or remote kermit not up"; char wth_msg[] = "write_thru: expecting send, send_init, rcv_init"; char rinit_msg[] = "rcv_init: received illegal packet from link"; char ml_msg[] = "main_loop: expecting send, receive, or break"; char ack_msg[] = "await_ack: received unexpected send_init"; char break_msg[] = "break_ack: received unexpected send_init"; char input_msg[] = "input: received unexpected send_init"; char abort_msg[] = "link aborted; must reset"; char send_msg[] = "error in sending on link"; char rcv_msg[] = "error in receiving from link"; char parm_msg[] = "error in incoming link parameters"; char rrcv_msg[] = "rcv_data: remote cannot rcv non-packet data"; char sdata_msg[] = "send_data: type must be one of F,D,Z,E"; char rsnd_msg[] = "send_data: remote cannot send non-packet data"; char uint_msg[] = " by user request"; /* flag to communicate to data link layer that user interrupted */ int user_interrupt = FALSE; int wait_prompt = FALSE; /* TRUE = wait for prompt between packets */ 23-Sep-82 02:27:48-EDT,6734;000000000000 Mail-from: ARPANET site UDEL-RELAY rcvd at 23-Sep-82 0226-EDT Date: 22 Sep 1982 17:37:41-EDT From: alison.cornell at UDel-Relay To: arpa.catchings at Cmu-20c Subject: Kermit source Via: Cornell; 23 Sep 82 2:13-EDT /* command codes used for the data link commands and for commanding the automaton */ #define SEND 'D' #define RCV 'G' #define BREAKC 'B' #define INIT_SEND 'S' #define INIT_RCV 'R' #define RESET 'A' int set_parms(pcode, pvaluec, pvaluei) read_parms(invector, outvector) send_data(type, message, status, ret_code) rcv_data(type, string, status, ret_code) connect(status, ret_code) read_status(status, msg_up, naks, errormsg) init_sending(status, ret_code) init_receiving(status, ret_code) int link(stream) reset(status, retcode) quit() struct packetform { char length; /* packet length */ char seqno; /* packet number */ char ptype; /* packet type */ char data[MAX_DATALEN]; char chksum[MAXCHKLEN]; /* checksum characters */ int trycount; /* num of attempts at sending this packet */ }; /* LINK INFORMATION */ extern int localprot, /* is this process local or remote ? */ link_active, /* is the link active? (not in write-thru mode) */ last_packet, /* seq num of the last packet built */ state, /* automaton state */ debug; /* running debug mode ? */ extern int lnkfd; /* file descriptor for comm line */ /* Link parameters (set by set command) */ extern int lnkstat, /* =OPEN or =CLOSED */ lnkduplex; /* host is FUll or HALF duplex (set cmd sets)*/ extern char lnkspeed; /* baud rate on comm line (see sgtty.h) */ extern char lnkname[]; /* =/dev/develcon or something similar */ extern int outsize, /* max length of packets */ insize, outdatamax, /* max length of data in packets */ indatamax, chksumout, /* num of chars for checksum */ chksumin, padout, /* num of pad chars in each direction */ padin, maxtry, /* max number of times to retry a packet */ stry_cnt, /* number of major send errors */ rtry_cnt, /* number of major receive errors */ nak_cnt; /* number of naks received */ extern unsigned waittime; /* timeout interval */ extern char padchout, /* pad characters */ padchin , eopout , /* end of packet chars */ eopin , quoteout, /* quoting chars */ quotein , synchar ; /* marks begin of packet */ /* MISC */ extern char *link_stat; /* textual message of link status */ /* PACKET STORAGE */ extern int msg_waiting; /* is there a message in pktbuffer? */ extern struct packetform outpkt, /* outgoing packet */ inpkt, /* incoming packet */ outparms_pkt, /* parameters I request: send_init or ack */ inparms_pkt, /* parameters requested by other end */ pktbuffer, /* a copy of the latest data packet rcvd */ pktdelivery; /* packet delivery out of automaton */ /* A bit of explaination of the roles of inpkt and pktbuffer. inpkt is the space into which all new incoming packets are placed. These incoming packets may include ack, nak, and timeout notices. pktbuffer, on the other hand, holds only the latest information packet (data, filename, eof, or error). This is where you go when you have to re-acknowledge the latest data received. */ /* ERROR MESSAGES */ extern char stry_msg[]; extern char rtry_msg[]; extern char wth_msg[]; extern char rinit_msg[]; extern char ml_msg[]; extern char ack_msg[]; extern char break_msg[]; extern char input_msg[]; extern char abort_msg[]; extern char send_msg[]; extern char rcv_msg[]; extern char parm_msg[]; extern char rrcv_msg[]; extern char sdata_msg[]; extern char rsnd_msg[]; extern char uint_msg[]; /* flag to notify data link layer that user interrupted */ extern int user_interrupt; extern int wait_prompt; /*TRUE = wait for prompt betw packets */ #define ESCAPE '\035' /* ^] */ #define NAMELEN 50 /* general parameters and all-around usefull stuff */ #define DEBUG /* get the compiler to generate debug code */ #define NEXT_PACKET (last_packet = ++last_packet & 077) /* increment last_packet modulo 64 */ #define MAX(A,B) ((A) > (B) ? (A) : (B)) #define CONTROL(A) ((A <= '\037') || (A == '\177')) #define _eq_ == #define TRUE 1 #define FALSE 0 #define EOS '\0' #define BUFSIZE 512 #define SMLBUFSIZ 16 /* don't always want a big buffer */ #define MAX_DATALEN 90 #define MAX_SIZE 96 #define NUMPAD 10 #define PADCH '\0' #define WAITTIME 12 #define EOPOUT '\012' #define EOPIN '\012' #define MAXCHKLEN 2 #define CHKSUMLEN 1 #define QUOTE '#' #define NOQUOTE '\002' #define SYNCHAR '\001' #define TILDA '\176' #define CR '\015' #define MAX_TRY 5 #define ERRLEN 70 #define TEFILE "./termemul" /* terminal emulator file */ #define FULL 0 /* full duplex (on comm line) */ #define HALF 1 /* half duplex (on comm line) */ #define OPEN 0 /* link has been opened */ #define CLOSED 1 /* link closed or never opened */ /* packet types */ #define DATA 'D' #define ACK 'Y' #define NAK 'N' #define SEND_INIT 'S' #define BREAK 'B' #define FILE_HEAD 'F' #define EOFILE 'Z' #define ERROR 'E' #define TIMEOUT 'T' /* codes for the results of a command */ #define ILLEGAL_REQUEST 'I' #define SUCCESS 'S' #define ABORTING 'A' #define FAILURE 'F' /* parameter codes for specifying what is to be set by set_parms */ #define OUTPKTSIZE 1 #define INPKTSIZE 2 #define OUTCHKSUM 3 #define INCHKSUM 4 #define OUTPAD 5 #define INPAD 6 #define OUTPADCHAR 7 #define INPADCHAR 8 #define SETTIMEOUT 9 #define OUTEOP 10 #define INEOP 11 #define OUTQUOTE 12 #define INQUOTE 13 /* states of the finite automaton */ #define PRE_WRITE_THRU 0 #define WRITE_THRU 1 #define S_INIT 2 #define S_ACK 3 #define MAIN_LOOP 4 #define AWAIT_ACK 5 #define BREAK_ACK 6 #define R_INIT 7 #define INPUT 8 #define ABORT 9 #define ABORT2 10 /* codes describing the status of the link */ #define NORMAL 'U' #define WTHRU 'W' #define ABORTED 'A' #define MSG_UP 'M' int emptypacket(); int fillpacket(); int bracket(); char cshift(); char itoc(); int ctoi(); int sendack(); int sendnak(); char shortstat(); char mytoupper();