/* rftp.c */ #define RFTPMASTER /* * Includes */ #include #include #include "vrftp.h" #include "dfault.h" #include "evtdef.h" #include "hstdef.h" #include "prodef.h" #include "rcmnd.h" #include "cliutl.h" #include "tsxutl.h" #include "kbdutl.h" #include "rtfile.h" #include "usrblk.h" #include "access.h" #include "inicli.h" #include "suspnd.h" /* * Internal routines */ extern VOID printtxt(); /* (txt) print text */ extern int dosess(); /* rftp spinner */ extern int setftp(); /* setup to receive incoming FTP request */ extern VOID rftpresp(); /* (s) send response to net */ extern VOID rftpd(); /* (code) rftp function decoder */ extern int ftpgo(); /* open FTP connection back to requestor */ extern VOID ftpd(); /* (code) ftp transfer routines */ extern int getword(); /* (string,word) extract a word */ extern char *stptok(); /* (p,t,i,d) stop on token */ extern char *stpblk(); /* (ch) skip to chars */ extern int Scheckpass(); /* (us,ps) password checker */ extern int Scompass(); /* (ps,en) password decoder */ extern VOID strlwr(); /* (st) convert string to lower case */ /* * Defines */ #define FASCII 0 #define FIMAGE 1 #define FTPCLEAR 0 #define FTPDONE 1 #define FTPABORT 2 #define BUFFERS 2048 #define READSIZE 512 #define RSPSTRING 200 #define GENBUFR 128 /* * Global Variables For RFTP Control Channel */ int pswdreqd = 1; /* password required */ static int aflag = 0, /* Attach to Service Flag */ rflag = 0, /* RFTP restart flag */ rftpcskt = -1, /* socket for incoming ftp */ portnum[8], /* originator information */ mflag = 0, /* monitor processing */ waitpos = 0, /* marker for gathering strings from net */ rfstate = 0, /* state of the control channel */ retstate = 0, /* to emulate a subroutine call */ rcnt = 0; /* number of characters from control */ /* * Global Variables For FTP Data Channel */ static int ftpdskt = -1, /* socket for ftp data connection */ ftpstate = 0, /* state of the ftp data transfer */ ftpfinished = FTPCLEAR, /* ftp finished state */ ftpfilemode = FASCII, /* how to open the file */ finished = TRUE, /* file transfer flag */ lstype = 0, /* what type of ftp list to do */ ftpfh = 0, /* file handle for ftp data */ fcnt = 0, /* transfer counter */ xp = 0, /* general array index */ towrite = 0, /* file -> net counter */ len = 0, /* net -> file counter */ filcnt = 0, /* number of files listed */ filblks = 0; /* number of data blocks in filcnt files */ /* * Global Buffers / Pointers */ struct userblock user; /* usernames / passwords / mailboxes */ char xs[BUFFERS]; /* buffer space for file transfer */ static char command[GENBUFR], /* command string */ rsp[RSPSTRING], /* response string */ pathname[GENBUFR], /* space to keep path name */ filspec[GENBUFR]; /* current file filespec */ scratch[GENBUFR], /* scratch string */ rnfrfile[GENBUFR], /* rename this file */ myuser[20]; /* user name on my machine */ static char *nextfile; /* pointer to the next filename */ static char * /* password file */ pass = "sy:paswrd.fil"; static char * /* message file */ mesg = "sy:rftp.msg"; static char * /* help file */ help = "sy:rftp.hlp"; /* * Debugging options * * bit 0 (0x01) debug internal events * bit 1 (0x02) debug dosess * bit 2 (0x04) debug rftpd() * bit 3 (0x08) debug ftpd() */ static char *usetxt[] = { "", #ifdef DEBUGOPTION " RFTP [?] [-d level] [-hmp filespec] [-aer]", " ? List the Help Text and Exit RFTP", " d level Debug Level", " 1 - enable event printing", " 2 - enable dosess() printing", " 4 - enable rftpd() printing", " 8 - enable ftpd() printing", #else " RFTP [?] [-hmp filespec] [-aer]", " ? List the Help Text and Exit RFTP", #endif " a Attach to Service", " e Enable Monitoring of all RFTP Transactions", " h Specify the Help Filespec", " m Specify the Message Filespec", " p Specify the Password Filespec", " r Restart RFTP after Disconnect", "", 0 }; /* * printout text */ VOID printtxt(txt) char **txt; { char **dp; for (dp = txt; *dp; dp++) { kb_puts(*dp); kb_nline(); } } main(argc,argv) int argc; char *argv[]; { int ev,i,j,k; register char c; register struct socket *skt; /* * Initialize for TSX+ clients */ inicli(); /* * Initialize kb handler * Set the break character to ^C */ kb_init(0x03); /* * identify self */ printtxt(vrstxt); /* * parse arguments */ for(i=1; i LOWWATER) { if(fgetss(rsp+4,RSPSTRING-5,fp) != NULL) { rftpresp(rsp); } else { fini = TRUE; } } else { suspnd(0); } } while(fini != TRUE); } /* * This is the error print routine for TSXUTL.C * It will be called only if there is an internal error */ VOID lsterr() { if(*errstr) { kb_puts(errstr); kb_nline(); errstr[0] = '\0'; } } /* * This routine is a dumby routine for TSXUTL.C * It should never be called, but returns a 1 [ABORT] */ int rtresp() { kb_puts("rtresp: This dumby routine should never be called"); return(1); } /* * Strip Telnet options */ VOID strip(s) register char *s; { register char *t; register int fndIAC; fndIAC = 0; t = s; do { if(fndIAC) { fndIAC = 0; } else if((*s & 0xFF) == 0xFF) { fndIAC = 1; } else { *t++ = *s; } } while(*s++); } /* * Remote FTP Server */ VOID rftpd(code) int code; { register struct socket *skt; register char *p; FILE *fp; char c,word[80]; int i,rftpcmnd; if(rftpcskt < 0) return; switch(rfstate) { case 0: if(code!=CONOPEN) break; /* * get default ftp information */ skgtftp(rftpcskt,portnum); portnum[4] = portnum[6]; portnum[5] = portnum[7]; ftpfilemode = FASCII; skt = (struct socket *) mapskt(rftpcskt); p = skt->tcpout.i.ipsource; sprintf(rsp, /*Net*/ "220-%s", vrstxt[1] /*Net*/ ); rftpresp(rsp); sprintf(rsp, /*Net*/ " Remote FTP Server %s [%d.%d.%d.%d]", skt->hostname, *(p+0)&0xFF, *(p+1)&0xFF, *(p+2)&0xFF, *(p+3)&0xFF /*Net*/ ); rftpresp(rsp); sprintf(rsp, /*Net*/ " Initiated from host: %d.%d.%d.%d", portnum[0], portnum[1], portnum[2], portnum[3] /*Net*/ ); rftpresp(rsp); if((fp = rtopen(mesg,"r",0,0)) != NULL) { transfer(fp); rtclose(fp); } rftpresp( /*Net*/ "220 Server Ready" /*Net*/ ); /* * check pass */ if(pswdreqd) { retstate = 1; } else { retstate = 2; } rfstate = 50; waitpos = 0; break; case 1: /* * check for passwords */ rfstate = 50; retstate = 1; waitpos = 0; if(getword(command,word) == FALSE) break; #ifdef DEBUGOPTION if(debug&0x04) { kbprintf("rftpd(%d): %s %s\r\n", code,word,command); } #endif strlwr(command); rftpcmnd = rcmnd(word); switch(rftpcmnd) { case F_USER: /* * keep user name */ strncpy(myuser,command,19); sprintf(rsp, /*Net*/ "331 Password required for user %s", myuser /*Net*/ ); rftpresp(rsp); /* * wait for password */ break; case F_PASS: if(Scheckpass(myuser,command)) { /* * User Specific Message File */ if(user.msgfil.path[0] != '\0' && (fp = rtopen(&user.msgfil.path,"r",0,0)) != NULL) { sprintf(rsp, /*Net*/ "230-Message to User %s", myuser /*Net*/ ); rftpresp(rsp); transfer(fp); rtclose(fp); } sprintf(rsp, /*Net*/ "230 User %s logged in [password verified]", myuser /*Net*/ ); /* * Log Session */ sprintf(scratch, /*Log*/ "RFTP: %s %s", myuser,command /*Log*/ ); logsession(rftpcskt,scratch); retstate = 2; } else { sprintf(rsp, /*Net*/ "531 User %s login failed [password not verified]", myuser /*Net*/ ); } rftpresp(rsp); break; case F_QUIT: rftpresp( /*Net*/ "221 Goodbye" /*Net*/ ); rcnt = -1; break; default: rftpresp( /*Net*/ "530 USER and PASS required to activate me" /*Net*/ ); break; } break; case 2: /* * interpret commands that are * received from the other side * set to a safe state to handle recursion * wait for another command line from client */ rfstate = 50; retstate = 2; waitpos = 0; if(getword(command,word) == FALSE) break; #ifdef DEBUGOPTION if(debug&0x04) { kbprintf("rftpd(%d): %s %s\r\n", code,word,command); } #endif strlwr(command); c = toupper(command[0]); rftpcmnd = rcmnd(word); switch(rftpcmnd) { case F_LIST: case F_NLST: if(rftpcmnd==F_LIST) { lstype = 1; } else { lstype = 0; } if((command[0]=='\0') || (command[0]=='.')) strcpy(command,"*.*"); /* * find first name */ if(!faccess(command,LIST_PRIV)) { sprintf(rsp, /*Net*/ "521 %s", errstr /*Net*/ ); } else if((p = setpath(command)) == NULL) { sprintf(rsp, /*Net*/ "522 %s", errstr /*Net*/ ); } else if((nextfile = filnam(p,NULL,1)) == NULL ) { sprintf(rsp, /*Net*/ "520 %s", errstr /*Net*/ ); } else { strncpy(filspec,p,15); /* * open the connection */ if(ftpgo() >= 0) { /* * ready for data */ filcnt = 0; filblks = 0; ftpstate = 40; } else { lastname(); } } if(errstr[0] != '\0') { errstr[0] = '\0'; rftpresp(rsp); } break; case F_CDUP: case F_XCUP: strcpy(command,".."); case F_CWD: case F_XCWD: getpath(pathname); if(!daccess(command,CWD_PRIV)) { sprintf(rsp, /*Net*/ "521 %s", errstr /*Net*/ ); } else if(cd(command)) { sprintf(rsp, /*Net*/ "522 %s", errstr /*Net*/ ); cd(pathname); } else { /* * success */ sprintf(rsp, /*Net*/ "250 Directory is %s", getpath(pathname) /*Net*/ ); } errstr[0] = '\0'; rftpresp(rsp); break; case F_MKD: case F_XMKD: if(!faccess(command,MKD_PRIV)) { sprintf(rsp, /*Net*/ "521 %s", errstr /*Net*/ ); } else if((p = setpath(command)) == NULL) { sprintf(rsp, /*Net*/ "522 %s", errstr /*Net*/ ); } else if(mkdir(p)) { sprintf(rsp, /*Net*/ "523 %s", errstr /*Net*/ ); } else { /* * created directory */ sprintf(rsp, /* Net*/ "257 Directory %s is created", expand(pathname,command) /*Net*/ ); } errstr[0] = '\0'; rftpresp(rsp); respath(); break; case F_RMD: case F_XRMD: if(!faccess(command,RMD_PRIV)) { sprintf(rsp, /*Net*/ "521 %s", errstr /*Net*/ ); } else if((p = setpath(command)) == NULL) { sprintf(rsp, /*Net*/ "522 %s", errstr /*Net*/ ); } else if(delfil(1,p,0,0)) { sprintf(rsp, /*Net*/ "523 %s", errstr /*Net*/ ); } else { /* * deleted directory */ sprintf(rsp, /*Net*/ "224 Directory %s is deleted", expand(pathname,p) /*Net*/ ); } errstr[0] = '\0'; rftpresp(rsp); respath(); break; case F_DELE: if(!faccess(command,DELE_PRIV)) { sprintf(rsp, /*Net*/ "521 %s", errstr /*Net*/ ); } else if((p = setpath(command)) == NULL) { sprintf(rsp, /*Net*/ "522 %s", errstr /*Net*/ ); } else if(delfil(0,p,0,0)) { sprintf(rsp, /*Net*/ "523 %s", errstr /*Net*/ ); } else { /* * deleted file */ sprintf(rsp, /* Net*/ "224 File %s is deleted", expand(pathname,p) /*Net*/ ); } errstr[0] = '\0'; rftpresp(rsp); respath(); break; case F_STOR: ftpstate = 0; /* * First, donot allow a write file * command to delete an existing * file if the user does not have * access to the file and DELE_PRIV. */ if(!(i=faccess(command,STOR_PRIV))) { sprintf(rsp, /*Net*/ "521 %s", errstr /*Net*/ ); } else if((ftpfh=rtopen(command,"rn",0,0))!=NULL) { rtclose(ftpfh); if((ac_priv&DELE_PRIV)!=DELE_PRIV) { i = 0; sprintf(rsp, /* Net*/ "522 File %s exists, no delete privilege", command /*Net*/ ); } } if(i && (ftpfh=rtopen(command,"wn",0,0))==NULL) { i = 0; sprintf(rsp, /*Net*/ "523 Cannot open file %s for write", command /*Net*/ ); } if(!i) { errstr[0] ='\0'; rftpresp(rsp); } else { strncpy(filspec,command,GENBUFR-1); /* * open connection */ if(ftpgo() >= 0) { /* * ready for data */ ftpstate = 30; } else { rtclose(ftpfh); } } break; case F_RETR: ftpstate = 0; if(!faccess(command,RETR_PRIV)) { sprintf(rsp, /*Net*/ "521 %s", errstr /*Net*/ ); errstr[0] ='\0'; rftpresp(rsp); } else if((ftpfh=rtopen(command,"rn",0,0))==NULL) { sprintf(rsp, /*Net*/ "520 File %s not found", command /*Net*/ ); rftpresp(rsp); } else { strncpy(filspec,command,GENBUFR-1); /* * open connection */ if(ftpgo() >= 0) { /* * ready for data */ ftpstate = 20; } else { rtclose(ftpfh); } } break; case F_TYPE: if(c == 'I') { ftpfilemode = FIMAGE; rftpresp( /*Net*/ "200 Type set to I, binary transfer mode" /*Net*/ ); } else if(c == 'A') { ftpfilemode = FASCII; rftpresp( /*Net*/ "200 Type set to A, ASCII transfer mode" /*Net*/ ); } else { sprintf(rsp, /*Net*/ "504 Type parameter '%c' not accepted, not implemented", c /*Net*/ ); rftpresp(rsp); } break; case F_PORT: /* * get the requested port number * from the command given */ sscanf(command,"%d,%d,%d,%d,%d,%d", &portnum[0],&portnum[1], &portnum[2],&portnum[3], &portnum[4],&portnum[5]); sprintf(rsp, /*Net*/ "200 Port command successful [%d.%d.%d.%d %d,%d -> %u]", portnum[0], portnum[1], portnum[2], portnum[3], portnum[4], portnum[5], (portnum[4]<<8) + portnum[5] /*Net*/ ); rftpresp(rsp); break; case F_QUIT: rftpresp( /*Net*/ "221 Goodbye" /*Net*/ ); rfstate = 60; break; case F_PWD: case F_XPWD: /* * get directory */ getpath(pathname); sprintf(rsp, /*Net*/ "257 %s is the current directory", pathname /*Net*/ ); rftpresp(rsp); break; case F_USER: /* * keep user name */ strncpy(myuser,command,19); /* * check pass */ if(pswdreqd) { sprintf(rsp, /*Net*/ "331 Password required for user %s", myuser /*Net*/ ); cd("dk:"); retstate = 1; } else { /* * confirm log in without password */ sprintf(rsp, /*Net*/ "230 User %s logged in [without password]", myuser /*Net*/ ); } rftpresp(rsp); break; case F_STRU: /* * only one STRU allowed */ if(c == 'F') { rftpresp( /*Net*/ "200 Stru F, file structure" /*Net*/ ); } else { sprintf(rsp, /*Net*/ "504 STRU parameter '%c' not accepted, not implemented", c /*Net*/ ); rftpresp(rsp); } break; case F_MODE: /* * only one MODE allowed */ if(c == 'S') { rftpresp( /*Net*/ "200 Mode S, stream mode" /*Net*/ ); } else { sprintf(rsp, /*Net*/ "504 MODE parameter '%c' not accepted, not implemented", c /*Net*/ ); rftpresp(rsp); } break; case F_PCLR: if(!faccess(command,PCLR_PRIV)) { sprintf(rsp, /*Net*/ "521 %s", errstr /*Net*/ ); } else if((p = setpath(command)) == NULL) { sprintf(rsp, /*Net*/ "522 %s", errstr /*Net*/ ); } else { /* * unprotect file */ setclrp(p,0,0); sprintf(rsp, /* Net*/ "259 File %s protection flag is clear", expand(pathname,p) /*Net*/ ); } errstr[0] = '\0'; rftpresp(rsp); respath(); break; case F_PSET: if(!faccess(command,PSET_PRIV)) { sprintf(rsp, /*Net*/ "521 %s", errstr /*Net*/ ); } else if((p = setpath(command)) == NULL) { sprintf(rsp, /*Net*/ "522 %s", errstr /*Net*/ ); } else { /* * protect file */ setclrp(p,1,0); sprintf(rsp, /* Net*/ "259 File %s protection flag is set", expand(pathname,p) /*Net*/ ); } errstr[0] = '\0'; rftpresp(rsp); respath(); break; case F_RNFR: rnfrfile[0] = '\0'; if(!faccess(command,RNFR_PRIV)) { sprintf(rsp, /*Net*/ "521 %s", errstr /*Net*/ ); } else if((ftpfh=rtopen(command,"rn",0,0))==NULL) { sprintf(rsp, /*Net*/ "520 File %s not found", command /*Net*/ ); } else { /* * Save file name */ strncpy(rnfrfile,command,GENBUFR-1); rtclose(ftpfh); sprintf(rsp, /*Net*/ "350 File %s exists, need new name", command /*Net*/ ); } errstr[0] = '\0'; rftpresp(rsp); break; case F_RNTO: /* * Require a RNFR file */ if(rnfrfile[0] == '\0') { i = 0; sprintf(rsp, /*Net*/ "550 Require RNFR command" /*Net*/ ); } else if((p = setpath(rnfrfile)) == NULL) { i = 0; sprintf(rsp, /*Net*/ "522 %s", errstr /*Net*/ ); } else /* * Second, donot allow a rename file * command to delete an existing * file if the user does not have * access to the file and DELE_PRIV. */ if(!(i=faccess(rmtfile(command),RNTO_PRIV))) { sprintf(rsp, /*Net*/ "521 %s", errstr /*Net*/ ); } else if( (ftpfh=fopen(rtfile(rmtfile(command),getdef()),"rn")) != NULL ) { fclose(ftpfh); if((ac_priv&DELE_PRIV) != DELE_PRIV) { i = 0; sprintf(rsp, /* Net*/ "526 RNTO file %s exists, no delete privilege", rmtfile(command) /*Net*/ ); } } if(i) { if(rename(p,command,0,0)) { /* * failed */ sprintf(rsp, /*Net*/ "550 %s", errstr /*Net*/ ); } else { /* * rename file */ sprintf(rsp, /* Net*/ "250-Renamed %s", expand(pathname,p) /*Net*/ ); rftpresp(rsp); sprintf(rsp, /* Net*/ "250 To %s", expand(pathname,rmtfile(command)) /*Net*/ ); } } errstr[0] = '\0'; rnfrfile[0] = '\0'; rftpresp(rsp); respath(); break; case F_ABOR: ftpfinished = FTPABORT; if(ftpdskt == -1) { rftpresp( /*Net*/ "252 ABOR Command Done" /*Net*/ ); } else { rftpresp( /*Net*/ "426 Transfer aborting. Data connection closing" /*Net*/ ); } break; case F_ACCT: case F_ALLO: rftpresp( /*Net*/ "202 Allocate and Account not required for this server" /*Net*/ ); break; case F_HELP: /*Net*/ rftpresp("214-Begin Help"); rftpresp(" "); rftpresp(" TSX-Plus FTP server, commands:"); rftpresp(" ABOR ACCT ALLO APPE* CWD CDUP DELE HELP"); rftpresp(" LIST MKD MODE NLST NOOP PASS PASV* PCLR"); rftpresp(" PORT PSET PWD QUIT REIN* REST* RETR RMD"); rftpresp(" RNFR RNTO SITE* SMNT* STAT STOR STOU* STRU"); rftpresp(" SYST TYPE USER XCWD XCUP XMKD XPWD XRMD"); rftpresp(" [*] - unsupported commands"); rftpresp(" "); for(i=0; filtxt[i]!=0; i++) { sprintf(rsp," %s", filtxt[i]); rftpresp(rsp); } if((fp = rtopen(help,"r",0,0)) != NULL) { transfer(fp); rtclose(fp); } rftpresp("214 End Help"); /*Net*/ break; case F_STAT: skt = (struct socket *) mapskt(rftpcskt); p = skt->tcpout.i.ipsource; /*Net*/ sprintf(rsp,"211-Begin Stat"); rftpresp(rsp); sprintf(rsp," %s", vrstxt[1]); rftpresp(rsp); sprintf(rsp," Remote FTP Server %s [%d.%d.%d.%d]", skt->hostname, *(p+0)&0xFF, *(p+1)&0xFF, *(p+2)&0xFF, *(p+3)&0xFF); rftpresp(rsp); sprintf(rsp," Initiated from host: %d.%d.%d.%d", portnum[0], portnum[1], portnum[2], portnum[3]); rftpresp(rsp); sprintf(rsp," transfer type = %s, structure = file, mode = stream", ftpfilemode ? "binary" : "ascii"); rftpresp(rsp); rftpresp( "211 End Stat"); /*Net*/ break; case F_SYST: rftpresp( /*Net*/ "215 PDP-11/LSI-11 system type" /*Net*/ ); break; case F_NOOP: rftpresp( /*Net*/ "200 Okay" /*Net*/ ); break; default: if(rftpcmnd != F_NULL) { /* * unimplemented commands */ sprintf(rsp, /*Net*/ "502 Command '%s' not implemented", r_clist[rftpcmnd] /*Net*/ ); } else { /* * command not understood */ sprintf(rsp, /*Net*/ "500 Command '%s %s' not understood", word,command /*Net*/ ); } rftpresp(rsp); break; } break; case 50: /* * subroutine to wait * for a particular character */ while(0<(rcnt=skread(rftpcskt,&command[waitpos],1))) { if(command[waitpos] == '\n') { skdeque(rftpcskt); rfstate = retstate; /* * find end of string */ while ( (command[waitpos] < 33) && (waitpos >= 0) ) waitpos--; /* * put in terminator */ command[++waitpos] = '\0'; /* * strip Telnet options */ strip(command); /* * want upper case */ for(i=0; i<4; i++) command[i] = toupper(command[i]); break; } else { if(waitpos < GENBUFR-1) waitpos += rcnt; } } break; case 60: /* * wait for message to get through * or connection is broken */ if(0 >= skenque(rftpcskt,1) || sktest(rftpcskt)) { rcnt = -1; } break; default: break; } if(rcnt < 0) { #ifdef DEBUGOPTION if(debug&0x04) { kbprintf("rftpd(%d): rfstate = %d, rcnt = %d\r\n", code,rfstate,rcnt); } #endif ftpfinished = FTPCLEAR; clrftp(ftpfinished); if(rftpcskt >= 0) { sksendwait(rftpcskt,1,(long) SENDWAIT); skclose(rftpcskt); rftpcskt = skrelease(rftpcskt,1); } if(rflag && !aflag) { /* * Start listening to the FTP port */ while((rftpcskt = setftp()) < 0 && !fndbrk) { /* * Could not get a socket, wait 5 seconds */ suspnd(300); } } else { fndbrk = 1; } } } /* * ftpgo * * open the FTP data connection to the remote host */ int ftpgo() { char st[24]; /* * Clear ftpfinished */ ftpfinished = FTPCLEAR; /* * Request a socket */ ftpdskt = socket(1); if(ftpdskt >= 0 ) { sprintf(rsp, /*Net*/ "150 Opening %s data connection for %s", (ftpfilemode==FASCII ? "an ASCII" : "a BINARY"),filspec /*Net*/ ); rftpresp(rsp); } else { rftpresp( /*Net*/ "599 RFTP Socket allocation error, retry operation" /*Net*/ ); return(-1); } /* * wait for control data to be sent */ if(sksendwait(rftpcskt,1,(long) SENDWAIT)) { rfstate = 0; rcnt = -1; return(-1); } /* * Force the from port */ skfromport(ftpdskt,HFTP-1); /* * Connecting to */ sprintf(st,"%d.%d.%d.%d #%u", portnum[0], portnum[1], portnum[2], portnum[3], (portnum[4]<<8) + portnum[5]); connect(ftpdskt,st); /* * reset to default port */ portnum[4] = portnum[6]; portnum[5] = portnum[7]; return(ftpdskt); } /* * FTP receive and send file functions */ VOID ftpd(code) int code; { int i,siz; char *p; char ps[96]; if(ftpdskt < 0) return; #ifdef DEBUGOPTION if(debug&0x08) { kbprintf("ftpd(%d): ftpstate = %d\r\n", code,ftpstate); } #endif switch(ftpstate){ default: break; case 40: /* * list file names in current dir */ if(code==CONFAIL || code==CONCLOSE) { /* * something went wrong */ fcnt = -1; break; } if(code!=CONOPEN) { /* * waiting for connection to open */ break; } ftpstate = 41; /* * send the "nextfile" string * and then see if there is * another file name to send */ case 41: /* * Check for aborted transfers */ if(ftpfinished) { ftpstate = 22; break; } if(skroom(ftpdskt) < LOWWATER) break; xp = 0; while(nextfile!=NULL && xp LOWWATER) suspnd(-1); } break; case 30: if(code==CONFAIL || code==CONCLOSE) /* * something went wrong */ fcnt = -1; if(code!=CONOPEN) /* * waiting for connection to open */ break; ftpstate = 31; len = 0; xp = 0; case 31: /* * file has already been opened, * take everything from the connection * and place into the open file: ftpfh */ /* * wait until xs is full * before writing to disk */ if(len <= 0) { if(xp) { if(ftpfh != NULL) { if(!fwrite(xs,1,xp,ftpfh)) { /* * disk full err */ rftpresp( /*Net*/ "552 Disk write error, probably disk full" /*Net*/ ); fcnt = -1; break; } } xp = 0; } /* * expected or desired len to go */ len = BUFFERS; } /* * how much to read */ if(len < BUFFERS) { i = len; } else { i = BUFFERS; } fcnt = skread(ftpdskt,&xs[xp],i); /* * adjust counts */ if(fcnt > 0) { /* * read successful * adjust counts and dequeue data */ len -= fcnt; xp += fcnt; skdeque(ftpdskt); /* * disable next suspension */ suspnd(-1); } /* * connection closed */ if(fcnt < 0) { /* * write last block */ if(ftpfh != NULL) { if(xp && !fwrite(xs,1,xp,ftpfh)) { /* * disk full check */ rftpresp( /*Net*/ "552 Disk write error, probably disk full" /*Net*/ ); break; } if(ftpfinished == FTPCLEAR) ftpfinished = FTPDONE; } } break; case 20: if(code==CONFAIL || code==CONCLOSE) /* * something went wrong */ fcnt = -1; if(code!=CONOPEN) /* * waiting for connection to open */ break; /* * Initialize parameters */ finished = FALSE; towrite = 0; xp = 0; ftpstate = 21; case 21: /* * Check for aborted transfers */ if(ftpfinished) { ftpstate = 22; break; } /* * transfer file(s) to the * other host via ftp request * file is already open=ftpfh */ if(towrite <= xp && finished == FALSE) { /* * need to read again */ fcnt = fread(xs,1,READSIZE,ftpfh); if(ftpfilemode == FASCII) { xs[fcnt] = '\0'; fcnt = strlen(xs); } if(fcnt == 0) finished = TRUE; towrite = fcnt; xp = 0; } fcnt = towrite - xp; if(fcnt > 0) { fcnt = skwrite(ftpdskt,&xs[xp],fcnt); } if(fcnt > 0) { /* * write successful * adjust counts and queue data */ xp += fcnt; skenque(ftpdskt,0); /* * disable next suspension if more room */ if(skroom(ftpdskt)) { suspnd(-1); } } /* * done if: the file * is all read from disk * and all sent or other side * has ruined connection */ if((towrite <= xp && finished == TRUE) || sktest(ftpdskt)) { ftpfinished = FTPDONE; ftpstate = 22; } else { break; } case 22: /* send done * wait for other side to accept * everything and then start close */ if(0 >= skenque(ftpdskt,0) || sktest(ftpdskt)) { fcnt = -1; } break; case 0: break; } /* * after proccessing data from connection, */ if(fcnt < 0) { #ifdef DEBUGOPTION if(debug&0x08) { kbprintf("ftpd(%d): ftpstate = %d, fcnt = %d\r\n", code,ftpstate,fcnt); } #endif clrftp(ftpfinished); ftpfinished = FTPCLEAR; } } /* * File transfer finishup */ static VOID clrftp(fstate) int fstate; { if(ftpfh != NULL) { rtclose(ftpfh); ftpfh = NULL; } /* * File list finishup */ lastname(); respath(); /* * File rename finishup */ rnfrfile[0] = '\0'; /* * Reset FTP transfer state */ ftpstate = 0; fcnt = 0; /* * Close data connection */ if(ftpdskt >= 0) { sksendwait(ftpdskt,0,(long) SENDWAIT); skclose(ftpdskt); ftpdskt = skrelease(ftpdskt,1); switch(fstate) { default: break; case FTPDONE: rftpresp( /*Net*/ "226 Transfer complete" /*Net*/ ); break; case FTPABORT: rftpresp( /*Net*/ "226 Abort successful" /*Net*/ ); break; } } } /* * getword: remove a word from a string. Things within quotes are * assumed to be one word. * return TRUE on success, FALSE on end of string */ static int getword(string,word) char *string; register char *word; { register char *p; register int i; char *q; i = 0; /* * skip leading blanks */ p = stpblk(string); if(!(*p)) { /* * no words in string */ word[0] = '\0'; return(FALSE); } if(*p == '\"') { /* * word delimited by quotes */ while(p[++i] && p[i] != '\"') word[i-1] = p[i]; word[i-1] = '\0'; if(!p[i]) { /* * Missing \". Assumed at end of string. */ } else { i++; } q = p+i; } else { /* * get word, max len 79 */ q = stptok(p, word, 79, " \t\r\n"); } /* * remove trailing blanks */ p = stpblk(q); /* * remove extracted stuff */ strcpy(string,p); return(TRUE); } static char *stptok( p, toword, ilen, delim) register char *p; char *toword; int ilen; char *delim; { register char *adv; register int i; int j,end; adv = toword; end = 0; j = strlen(delim); do { for(i=0; i= (toword+ilen-1)) end++; *adv++=*p++; } } while(!end); *adv='\0'; return(p); } char *stpblk(ch) register char *ch; { while(*ch == ' ' || *ch == '\t') ch++; return(ch); } /* * Scheckpass ( us, ps ) * * Check the password file for the user/password * combination. An inaccessable password file * results in an invalid return. * * If the user/password are validated then * the default directory is accessed and the * users access rights are loaded. An inaccessable * default directory results in an invalid return. * * Returns valid(1)/invalid(0) */ int Scheckpass(us,ps) char *us,*ps; { FILE *fp; if(NULL==(fp = rtopen(pass,"rn",0,0))) return(0); while (NULL != fread(&user,sizeof(struct userblock),1,fp)) { /* * does password check ? */ if(!strncmp(us,&user.username,USERPASSLEN) && (PASSNOTREQUIRED & user.userflag[0] || Scompass(ps,&user.password))) { rtclose(fp); if(!(RFTP_PRIV & user.userflag[1])) { rftpresp( /*Net*/ "532-User does not have FTP privilege" /*Net*/ ); return(0); } /* * Expand the user specifications */ lduserpaths(); /* * Set Users default directory */ getpath(pathname); if(cd(&user.defdir.path)) { /* * failed */ sprintf(rsp, /*Net*/ "530-%s", errstr /*Net*/ ); rftpresp(rsp); cd(pathname); errstr[0] = '\0'; return(0); } /* * success */ sprintf(rsp, /*Net*/ "230-Logged into Default Directory - %s", getpath(pathname) /*Net*/ ); rftpresp(rsp); return(1); } } rtclose(fp); return(0); } /* * Scompass ( ps, en ) * * Compute and check the encrypted password */ int Scompass(ps,en) char *ps,*en; { int i,ck; char *p,c; ck = 0; p = ps; /* * checksum the string */ while (*p) ck += *p++; c = ck; /* * XOR with checksum */ i = USERPASSLEN; while (i--) { if((((*ps ^ c)|32)&127)!=*en) return(0); /* * increment checksum to hide length */ if(*ps) { ps++; } else { c++; } en++; } return(1); } static VOID strlwr(st) register char *st; { while(*st) { *st = tolower(*st); *st++; } }