./fix03/README100644 0 0 4653 5616056061 11120 0ustar rootrootfilename : README purpose : describe fix03 kit date : July 28, 1994 -------------------------------- This kit is entitled fix03 and contains all of the fixes that I have implemented to Cohulip since its release. -------------------------------- ERRATA: The README file for cohulip speaks of using telunix for remote login services and states that terminal control should be set with: stty -icanon -echo min1 This setting hould be: stty -icanon -echo min 1 -------------------------------- NEW: slip.c has been fixed to overcome "hung" slip sessions. sys5_io.c has been fixed to allow running net from a terminal. The ftp files have been fixed to add a prompt, a hash function, a bell function and an in-session help function. A file searching algorithm is fixed to allow finger and smpt to operate correctly. It also fixes a problem that can occur when running net as /usr/bin/net with a full path. The keyboard would not respond, but now it does with the new main.c. ------------------------------- INSTRUCTIONS: 1. verify the length and then copy the following files into your cohulip src subdirectory: file length sum ----------------------------------------- ftp.c 8318 22583 17 ftp.h 1717 6016 4 ftpcli.c 18649 62557 37 ftpserv.c 19110 43566 39 slip.c 11626 48730 23 sys5_io.c 11692 50274 23 ndir.h 1372 44524 3 sys5unix.c 7117 13978 14 main.c 30346 10971 60 ----------------------------------------- 2. Then cd to the cohulip src subdirectory and run the command: make 3. The new binary for "net" will be created in the src subdirectory. Test it by running: ./net where is the name of your usual startup file. Try out the ftp client. It now has a prompt. A bell option to beep you when a file transfer is completed is toggled off and on by the ftp> command "bell". A hash option that will print a '#' for each kilobyte transfered is toggled with the ftp> hash command. A list of ftp options is available at the ftp> command line by entering either "?" or "help". (command listed here should be entered with quote marks). If you have the facilites for doing so, run net from a terminal logged in on a serial port, and try running net from within an xterm. 4. If you are satisfied with the new binary, copy ./net to /usr/bin. If you encounter difficulties, you can email me at rw@rwsys.wimsey.bc.ca -------------------------------- ./fix03/ftp.c100644 0 0 20176 5615313746 11220 0ustar rootroot/* Stuff common to both the FTP server and client */ #include #include "config.h" #include "global.h" #include "mbuf.h" #include "netuser.h" #include "timer.h" #include "tcp.h" #include "ftp.h" #include "telnet.h" #include "iface.h" #include "finger.h" #include "session.h" #include #include #ifndef COH42 #include #endif #ifndef TRUE /* k28 */ #define TRUE 1 #define FALSE 0 #endif void free(); /* FTP Data channel Receive upcall handler */ void ftpdr(tcb,cnt) struct tcb *tcb; int16 cnt; { register struct ftp *ftp; struct mbuf *bp; int wr; wr = 0; #ifdef COHPROF profile(__LINE__,__FILE__); #endif ftp = (struct ftp *)tcb->user; if(ftp->state != RECEIVING_STATE){ close_tcp(tcb); return; } /* This will likely also generate an ACK with window rotation */ recv_tcp(tcb,&bp,cnt); /* RLW - this new strategy for ascii conversion involves * getting a malloc'd buffer, and copying byte by * byte, skipping any '\r' chars. Then writing out * the buffer in a single shot. The previous method * pulled up a byte, checked it, then wrote the byte * and repeated. The profiling revealed that an ftp * was spending 83% of its time in this algorithm. * */ if(ftp->type == ASCII_TYPE) { char *wbuf, /* base of malloc'd buffer */ *inbuf, *dptr, /* destination - current pos in wbuf */ *sptr; /* source in bp->data */ int wr, i, newcnt; /* byte counters */ if(cnt != 0 ) { if( (wbuf=malloc( cnt )) == NULL ) { bp = free_mbuf(bp); return; } if( (inbuf=malloc( cnt )) == NULL ) { free( wbuf ); bp = free_mbuf(bp); return; } } else { /* no data to do */ bp = free_mbuf(bp); return; } if(pullup(&bp,inbuf,cnt) != cnt) { bp = free_mbuf(bp); free(inbuf); free(wbuf); return; } /* scan the source, and copy each char that * is not '\r' to the destination buffer.. * keeping track of number of chars so we * don't page fault and so we can make * write of the correct length. RLW */ for( i = 0, sptr = inbuf, dptr = wbuf, newcnt = 0; i < cnt; i++, sptr++, dptr++, newcnt++ ) { /* skip any number of '\r' * - there _may_ be times when this * is not the desired behavior. */ while( *sptr == '\r' && i < cnt ) {sptr++; i++;} /* move non-car. ret chars */ *dptr = *sptr; } /* write it out to disk */ wr = fwrite(wbuf,1,newcnt,ftp->fp); if( ftp->hashbell & FTPHASH ) { int val, i; ftp->databytes += wr; val = (ftp->databytes >> 10 ) - ftp->lasthash; ftp->lasthash = (ftp->databytes >> 10 ); for( i = 0; i < val; i++) printf( "#" ); fflush(stdout); } /* free resources */ bp = free_mbuf(bp); free(wbuf); free(inbuf); /* done */ return; } /* ftp->type = ASCII */ #ifdef DEAD /* the old ascii conversion algorithm */ while(pullup(&bp,&c,1) == 1){ if(c != '\r') putc(c,ftp->fp); } return; } #endif /* DEAD */ while(bp != NULLBUF){ if(bp->cnt != 0) wr = fwrite(bp->data,1,(unsigned)bp->cnt,ftp->fp); if( ftp->hashbell & FTPHASH ) { int val, i; ftp->databytes += wr; val = (ftp->databytes >> 10 ) - ftp->lasthash; ftp->lasthash = (ftp->databytes >> 10 ); for( i = 0; i < val; i++) printf( "#" ); fflush(stdout); } bp = free_mbuf(bp); } if(ftp->fp != stdout && ferror(ftp->fp)){ /* write error (dsk full?) */ fclose(ftp->fp); ftp->fp = NULLFILE; close_self(tcb,RESET); } } /* FTP Data channel Transmit upcall handler */ void ftpdt(tcb,cnt) struct tcb *tcb; int16 cnt; { struct ftp *ftp; struct mbuf *bp; register char *cp; register int c; #ifdef SYS5 struct stat ss_buf; #endif static int savedch = FALSE; /* added k28 - this mod costs 80 bytes when applied to the Unix version */ #ifdef COHPROF profile(__LINE__,__FILE__); #endif ftp = (struct ftp *)tcb->user; if(ftp->state != SENDING_STATE){ close_tcp(tcb); return; } if((bp = alloc_mbuf(cnt)) == NULLBUF){ /* Hard to know what to do here - You bet! And this could be a real * memory leak if memory is in yellow zone - K5JB */ return; } c = 0; /* double duty as character and flag */ if(ftp->type == IMAGE_TYPE){ bp->cnt = fread(bp->data,1,cnt,ftp->fp); if( ftp->hashbell & FTPHASH ) { int val, i; ftp->databytes += bp->cnt; val = (ftp->databytes >> 10 ) - ftp->lasthash; ftp->lasthash = (ftp->databytes >> 10 ); for( i = 0; i < val; i++) printf( "#" ); fflush(stdout); } if(bp->cnt != cnt) c = EOF; /* flag to get outa here */ } else { /* is ASCII type */ cp = bp->data; /* lifted this out routine I wrote for finger so we wouldn't * have a chronic queue of (window - 1). If necessary to handle * ill formed MS-DOS text files, just remove the #ifs -- K5JB k28 */ if(savedch){ /* unlikely case */ *cp++ = '\r'; *cp++ = '\n'; bp->cnt += 2; cnt -= 2; savedch = FALSE; } while(cnt > 0 && (c = getc(ftp->fp)) != EOF){ if( ftp->hashbell & FTPHASH ) { int val, i; ftp->databytes++; val = (ftp->databytes >> 10 ) - ftp->lasthash; ftp->lasthash = (ftp->databytes >> 10 ); for( i = 0; i < val; i++) printf( "#" ); fflush(stdout); } switch((char)c) { case '\032': /* NO ^Z's! - they clear screens */ break; case '\r': /* Unix and the others use \n as */ /* EOL character */ break; case '\n': /* convert LF to CR/LF */ if(cnt == 1){ /* unlikely case, no room for 2 chars */ savedch = TRUE; cnt = 0; /* make it a short one - drop out */ break; } *cp++ = '\r'; bp->cnt++; cnt--; /* and fall through */ default: *cp++ = (char)c; bp->cnt++; cnt--; break; } } /* while !feof() && cnt != 0 */ } /* else not image type */ if(bp->cnt != 0) send_tcp(tcb,bp); else free_p(bp); if(c == EOF){ /* EOF seen in either ASCII or IMAGE types */ #ifdef UNIX #ifdef SYS5 #ifndef hp9000s500 /* If ftp->fp points to an open pipe (from dir()) it must be closed with */ /* pclose(). System V fstat() can tell us if this was a pipe or not. */ if (fstat(fileno(ftp->fp), &ss_buf) < 0) perror("ftpdt: fstat"); #ifdef COH386 if ((ss_buf.st_mode & S_IFPIP) == S_IFPIP) #else if ((ss_buf.st_mode & IFIFO) == IFIFO) #endif pclose(ftp->fp); /* close pipe from dir */ else fclose(ftp->fp); #else /* hp9000s500 */ /* HP-UX 5.21 on the 500 doesn't understand IFIFO, since we probably don't *. / * care anyway, treat it like BSD is treated... */ if (pclose(ftp->fp) < 0) fclose(ftp->fp); #endif /* hp9000s500 */ #else /* SYS5 */ /* Berkeley Unix can't tell if this was a pipe or not. Try a pclose() */ /* first. If this fails, it must have been an open file. */ if (pclose(ftp->fp) < 0) fclose(ftp->fp); #endif /* SYS5 */ #else /* UNIX */ /* Anything other than Unix */ fclose(ftp->fp); #endif /* UNIX */ ftp->fp = NULLFILE; close_tcp(tcb); } /* if EOF */ } /* Allocate an FTP control block */ struct ftp * ftp_create(bufsize) unsigned bufsize; { void ftp_delete(); register struct ftp *ftp; #ifdef COHPROF profile(__LINE__,__FILE__); #endif if((ftp = (struct ftp *)calloc(1,sizeof (struct ftp))) == NULLFTP) return NULLFTP; if(bufsize != 0 && (ftp->buf = malloc(bufsize)) == NULLCHAR){ #ifdef DEBUG printf("called by ftp_create\n");fflush(stdout); #endif ftp_delete(ftp); #ifdef DEBUG printf("called by ftp_create\n");fflush(stdout); #endif return NULLFTP; } ftp->state = COMMAND_STATE; ftp->type = ASCII_TYPE; /* Default transfer type */ return ftp; } /* Free resources, delete control block */ void ftp_delete(ftp) register struct ftp *ftp; { int i; void freesession(); #ifdef COHPROF profile(__LINE__,__FILE__); #endif if(ftp->fp != NULLFILE && ftp->fp != stdout) fclose(ftp->fp); if(ftp->data != NULLTCB) del_tcp(ftp->data); if(ftp->username != NULLCHAR) free(ftp->username); for (i = 0; i < MAXPATH; i++) if(ftp->path[i] != NULLCHAR) free(ftp->path[i]); if(ftp->buf != NULLCHAR) free(ftp->buf); if(ftp->cd != NULLCHAR) free(ftp->cd); if(ftp->session != NULLSESSION) freesession(ftp->session); free((char *)ftp); } ./fix03/ftp.h100644 0 0 3265 5615313754 11204 0ustar rootroot#define CTLZ 26 /* EOF for CP/M systems */ extern char *userfile; /* List of user names and permissions */ #define MAXPATH 8 /* Maximum number of path/permission pairs */ /* Per-session control block */ struct ftp { struct ftp *prev; /* Linked list pointers */ struct ftp *next; struct tcb *control; /* TCP control connection */ int state; #define COMMAND_STATE 0 /* Awaiting user command */ #define SENDING_STATE 1 /* Sending data to user */ #define RECEIVING_STATE 2 /* Storing data from user */ #define STARTUP_STATE 3 /* Starting up autologin */ #define USER_STATE 4 /* Waiting for the user name */ #define PASS_STATE 5 /* Waiting for the password */ int type; /* Transfer type */ #define IMAGE_TYPE 0 #define ASCII_TYPE 1 FILE *fp; /* File descriptor being transferred */ struct socket port; /* Remote port for data connection */ struct tcb *data; /* Data connection */ /* The following are used only by the server */ char *username; /* Arg to USER command */ char *path[MAXPATH]; /* Allowable path prefix */ char perms[MAXPATH]; /* Permission flag bits */ #define FTP_READ 1 /* Read files */ #define FTP_CREATE 2 /* Create new files */ #define FTP_WRITE 4 /* Overwrite or delete existing files */ char *buf; /* Input command buffer */ int cnt; /* Length of input buffer */ int hashbell; /* flags for hash mark and bell */ #define FTPHASH 1 #define FTPBELL 2 int databytes; /* number databytes in transfer */ int lasthash; /* number of bytes last time of hash print */ int starttime; /* time xfer tcb is created */ char *cd; /* Current directory name */ /* And this is used only by the client */ struct session *session; }; #define NULLFTP (struct ftp *)0 ./fix03/ftpcli.c100644 0 0 44331 5615313766 11711 0ustar rootroot/* added "dele" for file deletion. It was already available in frpserv.c, performed general cleanup, added loop in login process, then added help to escape from a problem login. 9/2/91 - K5JB */ /* FTP client (interactive user) code */ #define LINELEN 128 /* Length of command buffer */ #include #include #include "config.h" #include "global.h" #include "mbuf.h" #include "netuser.h" #include "icmp.h" #include "timer.h" #include "tcp.h" #include "ftp.h" #include "session.h" #include "cmdparse.h" #include "telnet.h" #include "iface.h" #include "finger.h" extern struct session *current; extern char nospace[]; extern char badhost[]; char notsess[] = "Not an FTP session!\n"; char cantwrite[] = "Can't write %s\n"; char cantread[] = "Can't read %s\n"; char helpesc[] = "(Escape this with \"close\" from command mode.)\n"; char ftpprompt[] = "ftp> "; int donothing(),doftpcd(),dolist(),doget(),dols(),doput(),dotype(),doabort(), domkdir(),dormdir(),dodele(), dohash(), dobell(), doftphelp(); struct cmds ftpabort[] = { { "", donothing, 0, NULLCHAR, NULLCHAR }, { "abort", doabort, 0, NULLCHAR, NULLCHAR }, { NULLCHAR, NULLFP, 0, "Only valid command is \"abort\"", NULLCHAR } }; struct cmds ftpcmds[] = { { "", donothing, 0, NULLCHAR, NULLCHAR }, { "?", doftphelp, 0, NULLCHAR, NULLCHAR }, { "bell", dobell, 0, NULLCHAR, NULLCHAR }, { "cd", doftpcd, 2, "cd ", NULLCHAR }, { "dir", dolist, 0, NULLCHAR, NULLCHAR }, { "get", doget, 2, "get remotefile ", NULLCHAR }, { "hash", dohash, 0, NULLCHAR, NULLCHAR }, { "help", doftphelp, 0, NULLCHAR, NULLCHAR }, { "ls", dols, 0, NULLCHAR, NULLCHAR }, { "list", dolist, 0, NULLCHAR, NULLCHAR }, { "mkdir", domkdir, 2, "mkdir ", NULLCHAR }, { "nlst", dols, 0, NULLCHAR, NULLCHAR }, { "rmdir", dormdir, 2, "rmdir ", NULLCHAR }, { "put", doput, 2, "put localfile ", NULLCHAR }, { "type", dotype, 0, NULLCHAR, NULLCHAR }, { "dele", dodele, 2, "delete remotefile", NULLCHAR }, { NULLCHAR, NULLFP, 0, NULLCHAR, NULLCHAR } }; int doftphelp() { printf( "ftp commands: \n" ); printf( "\t?\t\tbell\t\tcd\t\tdele\n" ); printf( "\tdir\t\tget\t\thash\t\thelp\n"); printf( "\tls\t\tlist\t\tmkdir\t\tnlst\n"); printf( "\trmdir\t\tput\t\ttype\n\n"); printf( "\"abort\" will abort an in-progress file transfer.\n" ); printf( "remote servers usually accept \"quit\" to end connection.\n"); printf( "your escape char will allow return to net> prompt.\n\n" ); return(0); } int dohash() { if(current->cb.ftp->hashbell & FTPHASH) { current->cb.ftp->hashbell &= ~FTPHASH; printf( "HASH MARK (#) on 1k boundaries is OFF\n"); } else { current->cb.ftp->hashbell |= FTPHASH; printf( "HASH MARK (#) on 1k boundaries is ON\n"); } return(0); } int dobell() { if(current->cb.ftp->hashbell & FTPBELL) { current->cb.ftp->hashbell &= ~FTPBELL; printf( "BEEP at end of transfer is OFF\n"); } else { current->cb.ftp->hashbell |= FTPBELL; printf( "BEEP at end of tranfer is ON\n"); } return(0); } /* Send a message on the control channel */ /*VARARGS*/ static int sndftpmsg(ftp,fmt,arg) struct ftp *ftp; char *fmt; char *arg; { struct mbuf *bp; int16 len; #ifdef COHPROF profile(__LINE__,__FILE__); #endif len = strlen(fmt) + strlen(arg) + 10; /* fudge factor */ if((bp = alloc_mbuf(len)) == NULLBUF){ printf(nospace); return 1; } sprintf(bp->data,fmt,arg); bp->cnt = strlen(bp->data); send_tcp(ftp->control,bp); return 0; } /* Parse user FTP commands */ static int ftpparse(line,len) char *line; int16 len; { struct mbuf *bp; int cmdparse(); #ifdef COHPROF profile(__LINE__,__FILE__); #endif line[len] = '\0'; /* oops, was left out in couple of places. Seems all routines need this, or won't hurt - K5JB */ switch(current->cb.ftp->state){ case RECEIVING_STATE: case SENDING_STATE: /* The only command allowed in data transfer state is ABORT */ if(cmdparse(ftpabort,line) == -1){ printf("Transfer in progress; only ABORT is acceptable\n"); } fflush(stdout); break; case COMMAND_STATE: /* Save it now because cmdparse modifies the original */ bp = qdata(line,len); if(cmdparse(ftpcmds,line) == -1){ /* Send it direct */ if(bp != NULLBUF) send_tcp(current->cb.ftp->control,bp); else printf(nospace); } else { free_p(bp); } printf( ftpprompt ); fflush(stdout); break; case STARTUP_STATE: /* Starting up autologin */ printf("%sNot connected yet, ignoring: %s",helpesc,line); fflush(stdout); break; case USER_STATE: /* Got the user name */ printf(ftpprompt); return sndftpmsg(current->cb.ftp,"USER %s",line); case PASS_STATE: /* Got the password */ printf(ftpprompt); return sndftpmsg(current->cb.ftp,"PASS %s",line); } return(0); /* satisfy the compiler */ } /* Handle top-level FTP command */ int doftp(argc,argv) int argc; char *argv[]; { int32 resolve(); int atoi(),go(); char *inet_ntoa(); void ftpccr(),ftpccs(),cooked(); struct session *s; struct ftp *ftp,*ftp_create(); struct tcb *tcb; struct socket lsocket,fsocket; #ifdef COHPROF profile(__LINE__,__FILE__); #endif lsocket.address = ip_addr; lsocket.port = lport++; if((fsocket.address = resolve(argv[1])) == 0){ printf(badhost,argv[1]); return 1; } if(argc < 3) fsocket.port = FTP_PORT; else fsocket.port = atoi(argv[2]); /* Allocate a session control block */ if((s = newsession()) == NULLSESSION){ printf("Too many sessions\n"); return 1; } current = s; if((s->name = malloc((unsigned)strlen(argv[1])+1)) != NULLCHAR) strcpy(s->name,argv[1]); s->type = FTP; s->parse = ftpparse; /* Allocate an FTP control block */ if((ftp = ftp_create(LINELEN)) == NULLFTP){ s->type = FREE; printf(nospace); return 1; } ftp->hashbell = 0; ftp->state = STARTUP_STATE; cooked(); /* try this here instead of where it was */ s->cb.ftp = ftp; /* Downward link */ ftp->session = s; /* Upward link */ /* Now open the control connection */ tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE, 0,ftpccr,NULLVFP,ftpccs,0,(int *)ftp); ftp->control = tcb; go(); return 0; } /* Handle null line to avoid trapping on first command in table */ int donothing() { #ifdef COHPROF profile(__LINE__,__FILE__); #endif return(0); } /* Translate 'cd' to 'cwd' for convenience */ int doftpcd(argc,argv) int argc; char *argv[]; { register struct ftp *ftp; argc=argc; #ifdef COHPROF profile(__LINE__,__FILE__); #endif ftp = current->cb.ftp; return sndftpmsg(ftp,"CWD %s\r\n",argv[1]); } /* Translate 'mkdir' to 'xmkd' for convenience */ int domkdir(argc,argv) int argc; char *argv[]; { register struct ftp *ftp; argc=argc; #ifdef COHPROF profile(__LINE__,__FILE__); #endif ftp = current->cb.ftp; return sndftpmsg(ftp,"XMKD %s\r\n",argv[1]); } /* Translate 'rmdir' to 'xrmd' for convenience */ int dormdir(argc,argv) int argc; char *argv[]; { register struct ftp *ftp; argc=argc; #ifdef COHPROF profile(__LINE__,__FILE__); #endif ftp = current->cb.ftp; return sndftpmsg(ftp,"XRMD %s\r\n",argv[1]); } /* delete file, missing for some reason - K5JB */ int dodele(argc,argv) int argc; char *argv[]; { register struct ftp *ftp; argc=argc; #ifdef COHPROF profile(__LINE__,__FILE__); #endif ftp = current->cb.ftp; return sndftpmsg(ftp,"DELE %s\r\n",argv[1]); } /* Handle "type" command from user */ int dotype(argc,argv) int argc; char *argv[]; { register struct ftp *ftp; #ifdef COHPROF profile(__LINE__,__FILE__); #endif ftp = current->cb.ftp; if(argc < 2){ switch(ftp->type){ case IMAGE_TYPE: printf("Image\n%s",ftpprompt); break; case ASCII_TYPE: printf("Ascii\n%s",ftpprompt); break; } return 0; } switch(*argv[1]){ case 'i': case 'b': ftp->type = IMAGE_TYPE; sndftpmsg(ftp,"TYPE I\r\n"); break; case 'a': ftp->type = ASCII_TYPE; sndftpmsg(ftp,"TYPE A\r\n"); break; case 'l': ftp->type = IMAGE_TYPE; sndftpmsg(ftp,"TYPE L %s\r\n",argv[2]); break; default: printf("Invalid type %s\n%s",argv[1], ftpprompt); return 1; } return 0; } /* create data port, and send PORT message */ static void ftpsetup(ftp,recv,send,state) struct ftp *ftp; void (*send)(); void (*recv)(); void (*state)(); { struct socket lsocket; struct mbuf *bp; int time(); #ifdef COHPROF profile(__LINE__,__FILE__); #endif lsocket.address = ip_addr; lsocket.port = lport++; /* Compose and send PORT a,a,a,a,p,p message */ if((bp = alloc_mbuf(35)) == NULLBUF){ /* 5 more than worst case */ printf(nospace); return; } /* I know, this looks gross, but it works! */ sprintf(bp->data,"PORT %u,%u,%u,%u,%u,%u\r\n", hibyte(hiword(lsocket.address)), lobyte(hiword(lsocket.address)), hibyte(loword(lsocket.address)), lobyte(loword(lsocket.address)), hibyte(lsocket.port), lobyte(lsocket.port)); bp->cnt = strlen(bp->data); send_tcp(ftp->control,bp); ftp->databytes = 0; ftp->lasthash = 0; ftp->starttime = time(NULL); /* Post a listen on the data connection */ ftp->data = open_tcp(&lsocket,NULLSOCK,TCP_PASSIVE,0, recv,send,state,0,(int *)ftp); } /* FTP Client Data channel State change upcall handler */ static void ftpcds(tcb,old,new) struct tcb *tcb; char old,new; { struct ftp *ftp; int time(); old=old; #ifdef COHPROF profile(__LINE__,__FILE__); #endif if((ftp = (struct ftp *)tcb->user) == NULLFTP){ /* Unknown connection, kill it */ close_tcp(tcb); return; } switch(new){ case FINWAIT2: case TIME_WAIT: if(ftp->state == SENDING_STATE){ /* We've received an ack of our FIN, so * return to command mode */ ftp->state = COMMAND_STATE; if(current != NULLSESSION && current->cb.ftp == ftp){ printf("Put complete, %lu bytes sent in %d secs\n", tcb->snd.una - tcb->iss - 2, (time(NULL)) - ftp->starttime ); if( ftp->hashbell & FTPBELL ) printf( "\a" ); if( ftp->hashbell & FTPHASH ) printf( "\n" ); printf(ftpprompt); fflush(stdout); } } break; case CLOSE_WAIT: close_tcp(tcb); if(ftp->state == RECEIVING_STATE){ /* End of file received on incoming file */ #ifdef CPM if(ftp->type == ASCII_TYPE) putc(CTLZ,ftp->fp); #endif if(ftp->fp != stdout) fclose(ftp->fp); ftp->fp = NULLFILE; ftp->state = COMMAND_STATE; if(current != NULLSESSION && current->cb.ftp == ftp){ printf("Get complete, %lu bytes received in %d secs\n", tcb->rcv.nxt - tcb->irs - 2, (time(NULL)) - ftp->starttime ); if( ftp->hashbell & FTPBELL ) printf( "\a" ); if( ftp->hashbell & FTPHASH ) printf( "\n" ); printf(ftpprompt); fflush(stdout); } } break; case CLOSED: ftp->data = NULLTCB; del_tcp(tcb); break; } } /* Start receive transfer. Syntax: get [] */ int doget(argc,argv) int argc; char *argv[]; { void ftpdr(); char *remotename,*localname; register struct ftp *ftp; char *mode; #ifdef COHPROF profile(__LINE__,__FILE__); #endif ftp = current->cb.ftp; if(ftp == NULLFTP){ printf(notsess); return 1; } remotename = argv[1]; if(argc < 3) localname = remotename; else localname = argv[2]; if(ftp->fp != NULLFILE && ftp->fp != stdout) fclose(ftp->fp); ftp->fp = NULLFILE; if(ftp->type == IMAGE_TYPE) mode = binmode[WRITE_BINARY]; else mode = "w"; if((ftp->fp = fopen(localname,mode)) == NULLFILE){ printf(cantwrite,localname); return 1; } ftp->state = RECEIVING_STATE; ftpsetup(ftp,ftpdr,NULLVFP,ftpcds); /* Generate the command to start the transfer */ return sndftpmsg(ftp,"RETR %s\r\n",remotename); } /* List remote directory. Syntax: dir [] */ int dolist(argc,argv) int argc; char *argv[]; { void ftpdr(); register struct ftp *ftp; #ifdef COHPROF profile(__LINE__,__FILE__); #endif ftp = current->cb.ftp; if(ftp == NULLFTP){ printf(notsess); return 1; } if(ftp->fp != NULLFILE && ftp->fp != stdout) fclose(ftp->fp); ftp->fp = NULLFILE; if(argc < 3){ ftp->fp = stdout; } else if((ftp->fp = fopen(argv[2],"w")) == NULLFILE){ printf(cantwrite,argv[2]); return 1; } ftp->state = RECEIVING_STATE; ftpsetup(ftp,ftpdr,NULLVFP,ftpcds); /* Generate the command to start the transfer * It's done this way to avoid confusing the 4.2 FTP server * if there's no argument */ if(argc > 1) return sndftpmsg(ftp,"LIST %s\r\n",argv[1]); else return sndftpmsg(ftp,"LIST\r\n",""); } /* Abbreviated (name only) list of remote directory. * Syntax: ls [] */ int dols(argc,argv) int argc; char *argv[]; { void ftpdr(); register struct ftp *ftp; #ifdef COHPROF profile(__LINE__,__FILE__); #endif ftp = current->cb.ftp; if(ftp == NULLFTP){ printf(notsess); return 1; } if(ftp->fp != NULLFILE && ftp->fp != stdout) fclose(ftp->fp); ftp->fp = NULLFILE; if(argc < 3){ ftp->fp = stdout; } else if((ftp->fp = fopen(argv[2],"w")) == NULLFILE){ printf(cantwrite,argv[2]); return 1; } ftp->state = RECEIVING_STATE; ftpsetup(ftp,ftpdr,NULLVFP,ftpcds); /* Generate the command to start the transfer */ if(argc > 1) return sndftpmsg(ftp,"NLST %s\r\n",argv[1]); else return sndftpmsg(ftp,"NLST\r\n",""); } /* Start transmit. Syntax: put [] */ int doput(argc,argv) int argc; char *argv[]; { void ftpdt(); char *remotename,*localname; char *mode; struct ftp *ftp; #ifdef COHPROF profile(__LINE__,__FILE__); #endif if((ftp = current->cb.ftp) == NULLFTP){ printf(notsess); return 1; } localname = argv[1]; if(argc < 3) remotename = localname; else remotename = argv[2]; if(ftp->fp != NULLFILE && ftp->fp != stdout) fclose(ftp->fp); if(ftp->type == IMAGE_TYPE) mode = binmode[READ_BINARY]; else mode = "r"; if((ftp->fp = fopen(localname,mode)) == NULLFILE){ printf(cantread,localname); return 1; } ftp->state = SENDING_STATE; ftpsetup(ftp,NULLVFP,ftpdt,ftpcds); /* Generate the command to start the transfer */ return sndftpmsg(ftp,"STOR %s\r\n",remotename); } /* Abort a GET or PUT operation in progress. Note: this will leave * the partial file on the local or remote system */ int doabort() { register struct ftp *ftp; #ifdef COHPROF profile(__LINE__,__FILE__); #endif ftp = current->cb.ftp; /* Close the local file */ if(ftp->fp != NULLFILE && ftp->fp != stdout) fclose(ftp->fp); ftp->fp = NULLFILE; switch(ftp->state){ case SENDING_STATE: /* Send a premature EOF. * Unfortunately we can't just reset the connection * since the remote side might end up waiting forever * for us to send something. */ close_tcp(ftp->data); printf("Put aborted\n%s",ftpprompt); break; case RECEIVING_STATE: /* Just exterminate the data channel TCB; this will * generate a RST on the next data packet which will * abort the sender */ del_tcp(ftp->data); ftp->data = NULLTCB; printf("Get aborted\n%s",ftpprompt); break; } ftp->state = COMMAND_STATE; fflush(stdout); return(0); } /* FTP Client Control channel Receiver upcall routine */ void ftpccr(tcb,cnt) register struct tcb *tcb; int16 cnt; { struct mbuf *bp; struct ftp *ftp; void doreply(); char c; #ifdef COHPROF profile(__LINE__,__FILE__); #endif if((ftp = (struct ftp *)tcb->user) == NULLFTP){ /* Unknown connection; kill it */ close_tcp(tcb); return; } /* Hold output if we're not the current session */ if(mode != CONV_MODE || current == NULLSESSION || current->cb.ftp != ftp) return; if(recv_tcp(tcb,&bp,cnt) > 0){ while(pullup(&bp,&c,1) == 1){ switch(c){ case '\r': /* Strip cr's */ continue; case '\n': /* Complete line; process it */ ftp->buf[ftp->cnt] = '\0'; doreply(ftp); ftp->cnt = 0; break; default: /* Assemble line */ if(ftp->cnt != LINELEN-1) ftp->buf[ftp->cnt++] = c; break; } } fflush(stdout); } } /* Process replies from the server */ void doreply(ftp) register struct ftp *ftp; { void noecho(),echo(); fwrite(ftp->buf,1,(unsigned)ftp->cnt,stdout); fputc('\n', stdout); if (ftp->cnt < 3) return; ftp->buf[3] = '\0'; switch(ftp->state){ case SENDING_STATE: case RECEIVING_STATE: if (ftp->buf[0] == '5') doabort(); return; break; case STARTUP_STATE: if (!strcmp(ftp->buf, "220")){ ftp->state = USER_STATE; printf("Enter user name: "); fflush(stdout); return; } else { ftp->state = COMMAND_STATE; } break; case USER_STATE: /* revised this part to loop the login - K5JB */ if (!strcmp(ftp->buf, "331")) { ftp->state = PASS_STATE; noecho(); printf("Password: "); fflush(stdout); return; } else ftp->state = COMMAND_STATE; break; case PASS_STATE: echo(); if (!strcmp(ftp->buf, "550")){ ftp->state = USER_STATE; printf("%sTry your user name again: ",helpesc); fflush(stdout); return; }else ftp->state = COMMAND_STATE; break; } printf(ftpprompt); fflush(stdout); } /* FTP Client Control channel State change upcall routine */ void ftpccs(tcb,old,new) register struct tcb *tcb; char old,new; { void ftp_delete(); struct ftp *ftp; char notify = 0; extern char *tcpstates[]; extern char *reasons[]; extern char *unreach[]; extern char *exceed[]; int cmdmode(); old=old; /* Can't add a check for unknown connection here, it would loop * on a close upcall! We're just careful later on. */ ftp = (struct ftp *)tcb->user; if(current != NULLSESSION && current->cb.ftp == ftp) notify = 1; switch(new){ case CLOSE_WAIT: if(notify) printf("%s\n",tcpstates[(int)new]); close_tcp(tcb); break; case CLOSED: /* heh heh */ if(notify){ printf("%s (%s",tcpstates[(int)new],reasons[(int)tcb->reason]); if(tcb->reason == NETWORK){ switch(tcb->type){ case DEST_UNREACH: printf(": %s unreachable\n",unreach[(int)tcb->code]); break; case TIME_EXCEED: printf(": %s time exceeded",exceed[(int)tcb->code]); break; } } printf(")\n"); cmdmode(); } del_tcp(tcb); if(ftp != NULLFTP) ftp_delete(ftp); break; default: if(notify) printf("%s\n",tcpstates[(int)new]); break; } if(notify) fflush(stdout); } ./fix03/ftpserv.c100644 0 0 45246 5615314004 12111 0ustar rootroot/* FTP Server state machine - see RFC 959 */ #define LINELEN 128 /* Length of command buffer */ #include #include #include #include #include "config.h" #include "global.h" #include "mbuf.h" #include "netuser.h" #include "timer.h" #include "tcp.h" #include "ftp.h" #include "telnet.h" #include "unix.h" #include #include /* actually, this works OK with Turbo C */ #include "iface.h" #include "finger.h" #include "session.h" /* Command table */ static char *commands[] = { "user", #define USER_CMD 0 "acct", #define ACCT_CMD 1 "pass", #define PASS_CMD 2 "type", #define TYPE_CMD 3 "list", #define LIST_CMD 4 "cwd", #define CWD_CMD 5 "dele", #define DELE_CMD 6 "name", #define NAME_CMD 7 "quit", #define QUIT_CMD 8 "retr", #define RETR_CMD 9 "stor", #define STOR_CMD 10 "port", #define PORT_CMD 11 "nlst", #define NLST_CMD 12 "pwd", #define PWD_CMD 13 "xpwd", /* For compatibility with 4.2BSD */ #define XPWD_CMD 14 "mkd ", #define MKD_CMD 15 "xmkd", /* For compatibility with 4.2BSD */ #define XMKD_CMD 16 "xrmd", /* For compatibility with 4.2BSD */ #define XRMD_CMD 17 "rmd ", #define RMD_CMD 18 "stru", #define STRU_CMD 19 "mode", #define MODE_CMD 20 NULLCHAR }; /* Response messages */ static char banner[] = "220 %s FTP version %s ready at %s\r\n"; static char badcmd[] = "500 Unknown command\r\n"; static char unsupp[] = "500 Unsupported command or option\r\n"; static char givepass[] = "331 Enter PASS command\r\n"; static char logged[] = "230 Logged in\r\n"; static char typeok[] = "200 Type OK\r\n"; static char only8[] = "501 Only logical bytesize 8 supported\r\n"; static char deleok[] = "250 File deleted\r\n"; static char mkdok[] = "200 MKD ok\r\n"; static char delefail[] = "550 Delete failed\r\n"; static char pwdmsg[] = "257 \"%s\" is current directory\r\n"; static char badtype[] = "501 Unknown type \"%s\"\r\n"; static char badport[] = "501 Bad port syntax\r\n"; static char unimp[] = "502 Command not yet implemented\r\n"; static char bye[] = "221 Goodbye!\r\n"; static char nodir[] = "553 Can't read directory \"%s\"\r\n"; static char cantopen[] = "550 Can't read file \"%s\"\r\n"; static char sending[] = "150 Opening data connection for %s %s\r\n"; static char cantmake[] = "553 Can't create \"%s\"\r\n"; static char portok[] = "200 Port command okay\r\n"; static char rxok[] = "226 File received OK\r\n"; static char txok[] = "226 File sent OK\r\n"; static char noperm[] = "550 Permission denied\r\n"; static char noconn[] = "425 Data connection reset\r\n"; static char notlog[] = "530 Please log in with USER and PASS\r\n"; static char okay[] = "200 Ok\r\n"; static struct tcb *ftp_tcb; /* Start up FTP service */ void ftp1(argc,argv) int argc; char *argv[]; { struct socket lsocket; void ftpscr(),ftpscs(); int atoi(); #ifdef COHPROF profile(__LINE__,__FILE__); #endif lsocket.address = ip_addr; if(argc < 2) lsocket.port = FTP_PORT; else lsocket.port = atoi(argv[1]); ftp_tcb = open_tcp(&lsocket,NULLSOCK,TCP_SERVER,0,ftpscr,NULLVFP,ftpscs,0,(int *)NULLCHAR); } /* Shut down FTP server */ void ftp0() { #ifdef COHPROF profile(__LINE__,__FILE__); #endif if(ftp_tcb != NULLTCB) close_tcp(ftp_tcb); } /* FTP Server Control channel State change upcall handler */ void ftpscs(tcb,old,new) struct tcb *tcb; char old,new; { extern char hostname[],versionf[]; struct ftp *ftp,*ftp_create(); void ftp_delete(); char *inet_ntoa(); long t; char *cp,*cp1; int log(),tprintf(),tcpval(); #ifdef COHPROF profile(__LINE__,__FILE__); #endif old=old; switch(new){ /* Setting QUICKSTART piggybacks the server's banner on the SYN/ACK segment; * leaving it unset waits for the three-way handshake to complete before * sending the banner. Piggybacking unfortunately breaks some old TCPs, * so its use is not (yet) recommended. * There may be another reason to allocate ftp structure on a SYN_RECEIVED * if I don't find out what causes ftp_delete to be called before it has * been allocated - K5JB */ #ifdef QUICKSTART case SYN_RECEIVED: #else case ESTABLISHED: #endif if((ftp = ftp_create(LINELEN)) == NULLFTP){ /* No space, kill connection */ close_tcp(tcb); return; } ftp->control = tcb; /* Downward link */ tcb->user = (char *)ftp; /* Upward link */ /* Set default data port */ ftp->port.address = tcb->conn.remote.address; ftp->port.port = FTPD_PORT; /* Note current directory */ log(tcb,"open FTP"); time(&t); cp = ctime(&t); if((cp1 = index(cp,'\n')) != NULLCHAR) *cp1 = '\0'; tprintf(ftp->control,banner,hostname,versionf,cp); break; case CLOSE_WAIT: close_tcp(tcb); break; case CLOSED: log(tcb,"close FTP"); if((ftp = (struct ftp *)tcb->user) != NULLFTP && /* !tcpval(ftp->control) control session existing? (don't think this had the desired results - K5JB) */ !(tcb->state == SYN_RECEIVED)) /* no ftp struct to delete yet */ ftp_delete(ftp); /* Check if server is being shut down */ if(tcb == ftp_tcb) ftp_tcb = NULLTCB; del_tcp(tcb); break; } } /* FTP Server Control channel Receiver upcall handler */ void ftpscr(tcb,cnt) struct tcb *tcb; int16 cnt; { register struct ftp *ftp; char c; struct mbuf *bp; void ftpcommand(); cnt=cnt; #ifdef COHPROF profile(__LINE__,__FILE__); #endif if((ftp = (struct ftp *)tcb->user) == NULLFTP){ /* Unknown connection, just kill it */ close_tcp(tcb); return; } switch(ftp->state){ case COMMAND_STATE: /* Assemble an input line in the session buffer. Return if incomplete */ recv_tcp(tcb,&bp,0); while(pullup(&bp,&c,1) == 1){ switch(c){ case '\r': /* Strip cr's */ continue; case '\n': /* Complete line; process it */ ftp->buf[ftp->cnt] = '\0'; ftpcommand(ftp); ftp->cnt = 0; break; default: /* Assemble line */ if(ftp->cnt != LINELEN-1) ftp->buf[ftp->cnt++] = c; break; } } /* else no linefeed present yet to terminate command */ break; case SENDING_STATE: case RECEIVING_STATE: /* Leave commands pending on receive queue until * present command is done */ break; } } /* FTP server data channel connection state change upcall handler */ void ftpsds(tcb,old,new) struct tcb *tcb; char old,new; { register struct ftp *ftp; int tprintf(); #ifdef COHPROF profile(__LINE__,__FILE__); #endif if((ftp = (struct ftp *)tcb->user) == NULLFTP){ /* Unknown connection. Kill it */ del_tcp(tcb); } else if((old == FINWAIT1 || old == CLOSING) && ftp->state == SENDING_STATE){ /* We've received an ack of our FIN while sending; we're done */ ftp->state = COMMAND_STATE; tprintf(ftp->control,txok); /* Kick command parser if something is waiting */ if(ftp->control->rcvcnt != 0) ftpscr(ftp->control,ftp->control->rcvcnt); } else if(ftp->state == RECEIVING_STATE && new == CLOSE_WAIT){ /* FIN received on incoming file */ close_tcp(tcb); if(ftp->fp != stdout) fclose(ftp->fp); ftp->fp = NULLFILE; ftp->state = COMMAND_STATE; tprintf(ftp->control,rxok); /* Kick command parser if something is waiting */ if(ftp->control->rcvcnt != 0) ftpscr(ftp->control,ftp->control->rcvcnt); } else if(new == CLOSED){ if(tcb->reason != NORMAL){ /* Data connection was reset, complain about it */ tprintf(ftp->control,noconn); /* And clean up */ if(ftp->fp != NULLFILE && ftp->fp != stdout) fclose(ftp->fp); ftp->fp = NULLFILE; ftp->state = COMMAND_STATE; /* Kick command parser if something is waiting */ if(ftp->control->rcvcnt != 0) ftpscr(ftp->control,ftp->control->rcvcnt); } /* Clear only if another transfer hasn't already started */ if(ftp->data == tcb) ftp->data = NULLTCB; del_tcp(tcb); } } /* Parse and execute ftp commands */ void ftpcommand(ftp) register struct ftp *ftp; { void ftpdr(),ftpdt(),ftpsds(); char *cmd,*arg,*cp,**cmdp,*file; char *pathname(); char *mode; struct socket dport; struct stat statbuf; int i,permcheck(),pport(),rmdir(), log(), unlink(), tprintf(); void free(),ftplogin(); FILE *dir(); #ifdef COHPROF profile(__LINE__,__FILE__); #endif cmd = ftp->buf; if(ftp->cnt == 0){ /* Can't be a legal FTP command */ tprintf(ftp->control,badcmd); return; } cmd = ftp->buf; #ifdef UNIX /* Translate first word to lower case */ for(cp = cmd;*cp != ' ' && *cp != '\0';cp++) *cp = tolower(*cp); #else /* Translate entire buffer to lower case */ for(cp = cmd;*cp != '\0';cp++) *cp = tolower(*cp); #endif /* Find command in table; if not present, return syntax error */ for(cmdp = commands;*cmdp != NULLCHAR;cmdp++) if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0) break; if(*cmdp == NULLCHAR){ tprintf(ftp->control,badcmd); return; } /* Allow only USER, PASS and QUIT before logging in */ if(ftp->cd == NULLCHAR || ftp->path[0] == NULLCHAR){ switch(cmdp-commands){ case USER_CMD: case PASS_CMD: case QUIT_CMD: break; default: tprintf(ftp->control,notlog); return; } } arg = &cmd[strlen(*cmdp)]; while(*arg == ' ') arg++; /* Execute specific command */ switch(cmdp-commands){ case USER_CMD: if((ftp->username = (char *)malloc((unsigned)strlen(arg)+1)) == NULLCHAR){ close_tcp(ftp->control); break; } strcpy(ftp->username,arg); tprintf(ftp->control,givepass); /* erase all user info from possible previous session */ for(i = 0; i < MAXPATH; i++){ if(ftp->path[i] != NULLCHAR){ free(ftp->path[i]); ftp->path[i] = NULLCHAR; } ftp->perms[i] = 0; } if(ftp->cd != NULLCHAR){ free(ftp->cd); ftp->cd = NULLCHAR; } break; case TYPE_CMD: switch(arg[0]){ case 'A': case 'a': /* Ascii */ ftp->type = ASCII_TYPE; tprintf(ftp->control,typeok); break; case 'l': case 'L': while(*arg != ' ' && *arg != '\0') arg++; if(*arg == '\0' || *++arg != '8'){ tprintf(ftp->control,only8); break; } /* Note fall-thru */ case 'B': case 'b': /* Binary */ case 'I': case 'i': /* Image */ ftp->type = IMAGE_TYPE; tprintf(ftp->control,typeok); break; default: /* Invalid */ tprintf(ftp->control,badtype,arg); break; } break; case QUIT_CMD: tprintf(ftp->control,bye); close_tcp(ftp->control); break; case RETR_CMD: /* Disk operation; return ACK now */ tcp_output(ftp->control); file = pathname(ftp->cd,arg); if(ftp->type == IMAGE_TYPE) mode = binmode[READ_BINARY]; else mode = "r"; if(!permcheck(ftp,RETR_CMD,file)){ tprintf(ftp->control,noperm); } else if((ftp->fp = fopen(file,mode)) == NULLFILE){ tprintf(ftp->control,cantopen,file); } else { log(ftp->control,"RETR %s",file); dport.address = ip_addr; dport.port = FTPD_PORT; ftp->state = SENDING_STATE; tprintf(ftp->control,sending,"RETR",arg); ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE, 0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(int *)ftp); } free(file); break; case STOR_CMD: /* Disk operation; return ACK now */ tcp_output(ftp->control); file = pathname(ftp->cd,arg); if(ftp->type == IMAGE_TYPE) mode = binmode[WRITE_BINARY]; else mode = "w"; if(!permcheck(ftp,STOR_CMD,file)){ tprintf(ftp->control,noperm); free(file); break; } else if((ftp->fp = fopen(file,mode)) == NULLFILE){ tprintf(ftp->control,cantmake,file); } else { log(ftp->control,"STOR %s",file); dport.address = ip_addr; dport.port = FTPD_PORT; ftp->state = RECEIVING_STATE; tprintf(ftp->control,sending,"STOR",arg); ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE, 0,ftpdr,NULLVFP,ftpsds,ftp->control->tos,(int *)ftp); } free(file); break; case PORT_CMD: if(pport(&ftp->port,arg) == -1){ tprintf(ftp->control,badport); } else { tprintf(ftp->control,portok); } break; case LIST_CMD: /* Disk operation; return ACK now */ tcp_output(ftp->control); file = pathname(ftp->cd,arg); if(!permcheck(ftp,RETR_CMD,file)){ tprintf(ftp->control,noperm); } else if((ftp->fp = dir(file,1)) == NULLFILE){ tprintf(ftp->control,nodir,file); } else { dport.address = ip_addr; dport.port = FTPD_PORT; ftp->state = SENDING_STATE; tprintf(ftp->control,sending,"LIST",file); ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE, 0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(int *)ftp); } free(file); break; case NLST_CMD: /* Disk operation; return ACK now */ tcp_output(ftp->control); file = pathname(ftp->cd,arg); if(!permcheck(ftp,RETR_CMD,file)){ tprintf(ftp->control,noperm); } else if((ftp->fp = dir(file,0)) == NULLFILE){ tprintf(ftp->control,nodir,file); } else { dport.address = ip_addr; dport.port = FTPD_PORT; ftp->state = SENDING_STATE; tprintf(ftp->control,sending,"NLST",file); ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE, 0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(int *)ftp); } free(file); break; case CWD_CMD: tcp_output(ftp->control); /* Disk operation; return ACK now */ file = pathname(ftp->cd,arg); if(!permcheck(ftp,RETR_CMD,file)){ tprintf(ftp->control,noperm); free(file); } else #if (defined(MSDOS) || defined(ATARI_ST)) if(strcmp(file,"\\") == 0 || (stat(file,&statbuf) == 0 && statbuf.st_mode & S_IFDIR)) #else if(stat(file,&statbuf) == 0 && statbuf.st_mode & S_IFDIR) #endif { /* Succeeded, record in control block */ free(ftp->cd); ftp->cd = file; tprintf(ftp->control,pwdmsg,file); } else { /* Failed, don't change anything */ tprintf(ftp->control,nodir,file); free(file); } break; case XPWD_CMD: case PWD_CMD: tprintf(ftp->control,pwdmsg,ftp->cd); break; case ACCT_CMD: tprintf(ftp->control,unimp); break; case DELE_CMD: file = pathname(ftp->cd,arg); if(!permcheck(ftp,DELE_CMD,file)){ tprintf(ftp->control,noperm); } else if(unlink(file) == 0){ tprintf(ftp->control,deleok); } else { tprintf(ftp->control,delefail); } free(file); break; case PASS_CMD: tcp_output(ftp->control); /* Send the ack now */ ftplogin(ftp,arg); break; case XMKD_CMD: case MKD_CMD: file = pathname(ftp->cd,arg); if(!permcheck(ftp,MKD_CMD,file)){ tprintf(ftp->control,noperm); } else if(mkdir(file,0777) == 0){ /* too many args for MS-DOS but */ tprintf(ftp->control,mkdok); /* doesn't hurt anything - K5JB */ } else { tprintf(ftp->control,cantmake); } free(file); break; case XRMD_CMD: case RMD_CMD: file = pathname(ftp->cd,arg); if(!permcheck(ftp,RMD_CMD,file)){ tprintf(ftp->control,noperm); } else if(rmdir(file) == 0){ tprintf(ftp->control,deleok); } else { tprintf(ftp->control,delefail); } free(file); break; case STRU_CMD: if(tolower(arg[0]) != 'f') tprintf(ftp->control,unsupp); else tprintf(ftp->control,okay); break; case MODE_CMD: if(tolower(arg[0]) != 's') tprintf(ftp->control,unsupp); else tprintf(ftp->control,okay); break; } } int pport(sock,arg) struct socket *sock; char *arg; { int32 n; int atoi(),i; #ifdef COHPROF profile(__LINE__,__FILE__); #endif n = 0; for(i=0;i<4;i++){ n = atoi(arg) + (n << 8); if((arg = index(arg,',')) == NULLCHAR) return -1; arg++; } sock->address = n; n = atoi(arg); if((arg = index(arg,',')) == NULLCHAR) return -1; arg++; n = atoi(arg) + (n << 8); sock->port = n; return 0; } /* Attempt to log in the user whose name is in ftp->username and password * in pass */ void ftplogin(ftp,pass) struct ftp *ftp; char *pass; { char buf[80],*cp,*cp1,*getnenv(); FILE *fp; int anony = 0; char *getenv(); int log(),tprintf(); int i, atoi(); #ifdef COHPROF profile(__LINE__,__FILE__); #endif /* fprintf( stderr, "ftplogin, pass:%s\n", pass ); fflush(stderr); fprintf( stderr, "userfile:%s\n", userfile ); fflush(stderr); */ if((fp = fopen(userfile,"r")) == NULLFILE){ /* Userfile doesn't exist */ tprintf(ftp->control,noperm); return; } while(fgets(buf,sizeof(buf),fp),!feof(fp)){ if(buf[0] == '#') continue; /* Comment */ if((cp = index(buf,' ')) == NULLCHAR) /* Bogus entry */ continue; *cp++ = '\0'; /* Now points to password */ if(strcmp(ftp->username,buf) == 0) break; /* Found user name */ } if(feof(fp)){ /* User name not found in file */ fclose(fp); tprintf(ftp->control,noperm); return; } fclose(fp); /* Look for space after password field in file */ if((cp1 = index(cp,' ')) == NULLCHAR){ /* Invalid file entry */ tprintf(ftp->control,noperm); return; } *cp1++ = '\0'; /* Now points to first path field */ if(strcmp(cp,"*") == 0) anony = 1; /* User ID is password-free */ if(!anony && strcmp(cp,pass) != 0){ /* Password required, but wrong one given */ tprintf(ftp->control,noperm); return; } for(i = 0; i< MAXPATH; i++){ if((cp = index(cp1,' ')) == NULLCHAR){ /* Permission field missing, assume end of line */ break; } *cp++ = '\0'; /* now points to permission field */ ftp->path[i] = (char *)malloc((unsigned)strlen(cp1)+1); strcpy(ftp->path[i],cp1); /* set the permission bits */ ftp->perms[i] = atoi(cp); if ((cp1 = index(cp,' ')) == NULLCHAR){ /* no next path field, so assume end of line */ break; } *cp1++ = '\0'; /* cp1 now points to the next path field */ } /* Set up current directory and LAST specified path prefix */ for (i= MAXPATH - 1; i>=0; i--) if (ftp->perms[i]) break; ftp->cd = (char *)malloc((unsigned)strlen(ftp->path[i])+1); strcpy(ftp->cd,ftp->path[i]); tprintf(ftp->control,logged); if(!anony) log(ftp->control,"%s logged in",ftp->username); else log(ftp->control,"%s logged in, ID %s",ftp->username,pass); } #if (defined(MSDOS) || defined(ATARI_ST)) /* Illegal characters in a DOS filename */ char badchars[] = "\"[]|<>+=;,"; #endif /* Return 1 if the file operation is allowed, 0 otherwise */ int permcheck(ftp,op,file) struct ftp *ftp; int op; char *file; { char *cp; int i,access(); cp=cp; #ifdef COHPROF profile(__LINE__,__FILE__); #endif if(file == NULLCHAR || ftp->path[0] == NULLCHAR) return 0; /* Probably hasn't logged in yet */ #if (defined(MSDOS) || defined(ATARI_ST)) /* Check for characters illegal in MS-DOS file names */ for(cp = badchars;*cp != '\0';cp++){ if(index(file,*cp) != NULLCHAR) return 0; } #endif #if (!AMIGA && !MAC) /* The target file must be under the user's allowed search path */ for(i = 0; i < MAXPATH; i++) if(ftp->path[i] != NULLCHAR && strncmp(file,ftp->path[i],strlen(ftp->path[i])) == 0) break; if(i == MAXPATH) return 0; #endif switch(op){ case RETR_CMD: /* User must have permission to read files */ if(ftp->perms[i] & FTP_READ) return 1; return 0; case DELE_CMD: case RMD_CMD: /* User must have permission to (over)write files */ if(ftp->perms[i] & FTP_WRITE) return 1; return 0; case STOR_CMD: case MKD_CMD: /* User must have permission to (over)write files, or permission * to create them if the file doesn't already exist */ if(ftp->perms[i] & FTP_WRITE) return 1; if(access(file,2) == -1 && (ftp->perms[i] & FTP_CREATE)) return 1; return 0; } return 0; /* "can't happen" -- keep lint happy */ } ./fix03/main.c100644 0 0 73212 5616055600 11343 0ustar rootroot/* filename : main.c * purpose : dispatching and some functions for cohulip. * * notes : derived from ka9q/k5jb/NetBSD 09 and * : also uses new code. There is lots of * : code in this package of unknown origin. * : but is is safe to say that at least this * : file is still under Phil Karn's 1988 * : KA9Q copyright. Other files contain * : copyright notices from other people or * : organizations. * * : it is structured in such a way that new * : code or devices can be added from exiting * : ka9q or k5jb-k29 code. To do so, commands * : need to be entered into the command tables * : and if a device support is added, check out * : the makepiston() function in nedl.c. Such a * : call adds the device to the engine defined * : in motor.c. This version is specifcally built * : for COHERENT 4.2 and supports the ne CON style * : ethernet driver. Randy Wright, Jan 1994. * * - rw@rwsys.wimsey.bc.ca * * */ #define HOSTNAMELEN 64 unsigned restricted_dev=1000; #include #include #include #include #include "options.h" #include "config.h" #include "sokname.h" #include "global.h" #include "mbuf.h" #include "netuser.h" #include "timer.h" #include "icmp.h" #include "iface.h" #include "ip.h" #include "tcp.h" #include "ftp.h" #include "telnet.h" #include "remote.h" #include "motor.h" #ifdef _FINGER #include "finger.h" #endif #include "session.h" #include "cmdparse.h" #ifdef ASY #include "asy.h" #include "slip.h" #endif #include "unix.h" #include "unixopt.h" #ifdef TRACE #include "trace.h" /* Dummy structure for loopback tracing */ struct interface loopback = { NULLIF, "loopback" }; #endif /* various file and path names used by fileinit() */ extern char *homedir,*startup,*userfile,*hosts,*spool,*mailspool, *mailqdir,*mailqueue,*routeqdir,*alias,*helpbox,*public; #ifdef _FINGER extern char *fingersuf,*fingerpath; #endif char defaultnethome[] = "/usr/net"; extern char *netexe; extern struct interface *ifaces; void version(); extern struct mbuf *loopq; extern FILE *trfp; extern char trname[]; int Keyhit = -1; int mode; int exitval; /* used by doexit() */ static char *logname; static FILE *logfp; char badhost[] = "Unknown host %s\n"; char hostname[HOSTNAMELEN]; unsigned nsessions = NSESSIONS; int32 resolve(); char *strncpy(); int16 lport = 1001; char prompt[] = "net> "; char nospace[] = "No space!!\n"; /* Generic malloc fail message */ int background = 0; int backgrd = 0; /* used by shell layer management - K5JB */ extern int reportquit_flag; void report_quit(); int io_active = 0; static char escape = 0x1d; /* default escape character is ^] */ /* Enter command mode */ int cmdmode() { void cooked(); #ifdef COHPROF profile(__LINE__,__FILE__); #endif if(mode != CMD_MODE){ mode = CMD_MODE; cooked(); printf(prompt); fflush(stdout); } return 0; } int /* removed static to make this the only sane exit from program */ doexit() /* left declaration as int to match cmd list */ { void iostop(),exit(); #ifdef COHPROF profile(__LINE__,__FILE__); #endif #ifdef TRACE if (trfp != stdout) fclose(trfp); #endif if (!background) iostop(); /* this function will exit() */ exit(exitval); return(-1); } static int dohostname(argc,argv) int argc; char *argv[]; { #ifdef COHPROF profile(__LINE__,__FILE__); #endif if(argc < 2) printf("%s\n",hostname); else strncpy(hostname,argv[1],HOSTNAMELEN); return 0; } static int dolog(argc,argv) int argc; char *argv[]; { char *make_path(); void free(); #ifdef COHPROF profile(__LINE__,__FILE__); #endif if(argc < 2){ if(logname != NULLCHAR) printf("Log: %s. log off stops.\n",logname); else printf("Log off. log starts.\n"); return 0; } if(logname != NULLCHAR){ free(logname); logname = NULLCHAR; } if(strcmp(argv[1],"off") == 0) return 0; if(argv[1][0] == '/' || argv[1][0] == '\\') logname = make_path("",argv[1],0); else logname = make_path(homedir,argv[1],1); return 0; } static int dohelp() { extern struct cmds cmds[]; register struct cmds *cmdp; int i; #ifdef COHPROF profile(__LINE__,__FILE__); #endif printf("Main commands:"); /* first command is "" and will do \n */ for(i=0,cmdp = cmds;cmdp->name != NULLCHAR;cmdp++,i++){ printf("%-15s",cmdp->name); if(!(i % 5)) printf("\n"); } printf("\n"); return 0; } static int dohelp1(argc,argv) int argc; char *argv[]; { char *make_path(); void free(); char *pp; int i; char helpname[16]; /* should be big enough even for Unix */ char *defaulthelp = "net"; int dohelpfile(); #ifdef COHPROF profile(__LINE__,__FILE__); #endif if(argc < 2) pp = defaulthelp; else pp = argv[1]; for(i=0;i<11;i++){ helpname[i] = pp[i]; if(helpname[i] == '\0') break; } helpname[i] = '\0'; /* to be sure */ strcat(helpname,".hlp"); pp = make_path(homedir,helpname,1); if(pp != NULLCHAR){ dohelpfile(pp,0); /* ignore return value */ free(pp); } return 0; } static /* primitive way to peek at our mail, page at a time */ int domail(argc,argv) int argc; char *argv[]; { char *make_path(); void free(); int atoi(), dohelpfile(); char *pp; char *mfname; int i; char hostmail[16]; /* should be big enough even for Unix */ #ifdef COHPROF profile(__LINE__,__FILE__); #endif if(argc < 2) mfname = hostname; else mfname = argv[1]; for(i=0;i<11;i++){ if(mfname[i] == '.' || mfname[i] == '\0') break; hostmail[i] = mfname[i]; } hostmail[i] = '\0'; strcat(hostmail,".txt"); pp = make_path(mailspool,hostmail,1); if(argc == 3) i = 22 * atoi(argv[2]); else i = 0; if(pp != NULLCHAR){ if(dohelpfile(pp,i) == -1) printf("Or there is no mail\n"); free(pp); } return 0; } int dohelpfile(file,jump) char *file; int jump; { #define HFLINE 120 /* arbitrary, but this will keep cycles down on long lines */ extern int Keyhit, kbread(); FILE *hfile; extern char escape; extern char nospace[]; char *hbuf; int i; void keep_things_going(),check_kbd(),free(); #ifdef COHPROF profile(__LINE__,__FILE__); #endif Keyhit = -1; if((hbuf = (char *)malloc(sizeof(char) * HFLINE)) == NULLCHAR){ perror(nospace); return(0); } if((hfile = fopen(file,"r")) == NULLFILE){ printf("Can't find %s\n",file); fflush(stdout); free(hbuf); return(-1); } if(jump) for(i=0;i