FTPD.D
上传用户:sunrenlu
上传日期:2022-06-13
资源大小:1419k
文件大小:22k
- /*
- * Copyright (c) 1990, 1999 Erick Engelke
- */
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <time.h>
- #include <ctype.h>
- #include <io.h>
- #include <mem.h>
- #include <rtos.h>
- #include <net.h>
- #include <dir.h>
- #include <dos.h>
- #include <share.h>
- #define S_INIT 0
- #define S_WAIT 1
- #define S_CONN 2 /* connected and talking */
- #define S_DATA 4
- #define S_CLOS 5
- #define S_HALT 6 /* halt service */
- char *cmds[] = {
- "ABOR", "QUIT", "PASS", "HELP", "NOOP", "USER",
- "MKD", "XMKD", "RMD", "XRMD", "CWD", "XCWD", "PWD", "XPWD",
- "SITE", "DEL" , "TYPE", "SIZE", "PORT", "LIST", "NLST", "STOR", "RETR"
- };
- enum {
- ABOR, QUIT, PASS, HELP, NOOP, USER,
- MKD, XMKD, RMD, XRMD, CWD, XCWD, PWD, XPWD,
- SITE, DEL, TYPE, SIZE, PORT, LIST, NLST, STOR, RETR,
- ENDLIST };
- #define FTPDPASS "FTPD.PASSWORD"
- #define FTPDBUFSIZ "FTPD.BUFSIZ"
- #define FTPDPORT 21
- #define FTP_OP_DIR 0
- #define FTP_OP_GET 1
- #define FTP_OP_PUT 2
- #define FTP_OP_DON 3
- #define FTP_OP_NOP 4
- #define FTPDIRLEN 65
- #define FTPPATHLEN 81
- typedef struct {
- int status;
- tcp_Socket socket;
- int dstatus;
- int doperation;
- int dcmd;
- tcp_Socket dsocket;
- struct ffblk ffblk;
- int dhandle; /* for file transfer */
- int gotpass;
- int ascii; /* 1 is ascii */
- longword biglen; /* for file transfer completion stats */
- word bufhead;
- word buftail;
- longword hisip; /* used for data connections */
- word hisport;
- char *errmsg;
- char dirpath[ FTPDIRLEN ];
- char filepath[ FTPPATHLEN ];
- } ftpdses;
- static char *ftpdbuffer;
- int ftpdbufferlen = 0;
- static ftpdses *ftpd;
- char ftpdpassword[ 128 ];
- char ftpdwelcome[ 128 ];
- extern void *getvalue( char * p );
- extern int cmdcompare( char *p, char *command );
- typedef struct dostimedate {
- unsigned xxx : 5;
- unsigned min : 6;
- unsigned hour : 5;
- unsigned date : 5;
- unsigned mon : 4;
- unsigned year : 7;
- };
- /*
- * handlerrs - install an error handler which simply returns as though fail
- * was sellected by the user
- */
- static void interrupt (*olderrhandler)();
- #pragma warn -par
- /*
- * handlerrs - it prevents abort,retry,ignore,...
- * - coded Borland specific
- * - start = nonzero installs handler, zero resets it
- */
- static void handlerrs( int start )
- {
- if ( start ) {
- olderrhandler = getvect( 0x24 );
- harderr( (int (*)()) hardretn );
- } else
- setvect( 0x24, olderrhandler );
- }
- /*
- * strins - copy little_string into big_string
- *
- * eg. strins( "0123456", "abcd", 2 ) creates "ab23456"
- */
- static void strins( char *big, char *little, int len )
- {
- int movlen;
- movlen = min( strlen( little ), ((len > 0) ? len : -len));
- if ( len > 0 ) movmem( little, big, movlen );
- else movmem( little, big - (len + movlen), movlen );
- }
- /*
- * dirformblock - create a unix "ls -l" style of output for this dir entry
- */
- static
- void
- dirformblock( struct ffblk* ff, char* formatted)
- {
- struct dostimedate* pdos_time = (struct dostimedate*) &ff->ff_ftime;
- struct tm unix_time;
- time_t timer = time( NULL );
- int current_year = localtime( &timer )->tm_year + 1900; /* UNIX year bias */
- char buf[16];
- int i;
- memset(&unix_time, 0, sizeof(unix_time));
- unix_time.tm_min = pdos_time->min;
- unix_time.tm_hour = pdos_time->hour;
- unix_time.tm_mday = pdos_time->date;
- unix_time.tm_mon = pdos_time->mon - 1; /* DOS months 1-12, UNIX months 0-11 */
- unix_time.tm_year = pdos_time->year + 1980 /* DOS year bias */ - 1900 /* UNIX year bias */;
-
- sprintf( formatted, "%c", ff->ff_attrib & FA_DIREC ? 'd' : '-' );
- sprintf( buf, "r%cx", !(ff->ff_attrib & FA_RDONLY) ? 'w' : '-' );
- for(i = 0; i<3; i++)
- strcat( formatted, buf );
- sprintf( strchr(formatted, 0), " 1 user users %10s ", ltoa( ff->ff_fsize, buf, 10 ) );
- strftime( strchr(formatted, 0), -1,
- pdos_time->year + 1980 == current_year ? "%b %d %H:%M " : "%b %d %Y ", &unix_time
- );
- strcat(formatted, ff->ff_name);
- strcat(formatted, "rn");
- }
- /*
- * functions to handle paths
- */
- static char *pathfile( ftpdses *f, char *fname )
- {
- /* better return bad name than overwrite buffers */
- if ( (strlen( f->dirpath ) + strlen( fname ) - 2) >= FTPPATHLEN )
- return( fname );
- strcpy( f->filepath, ".\");
- if ( *f->dirpath ) {
- strcat( f->filepath, f->dirpath );
- strcat( f->filepath, "\");
- }
- strcat( f->filepath, fname );
- return( f->filepath );
- }
- static int changedir( ftpdses *f, char *path)
- {
- struct stat st;
- char *p;
- /* fix unix styled slashes */
- while ( (p = strchr( path, '/' )) != NULL ) *p = '\';
- /* handle up one dir */
- if ( (path[0] == '.') && (path[1] == '.' )) {
- /* go up a subdir */
- if ( (p = strrchr( f->dirpath, '\'))!= NULL) {
- *p = 0;
- } else
- f->dirpath[0] = 0;
- return( 0 ); /* say it was successful */
- }
- /* root is . */
- if ( (path[0] == '\' ) && ( path[1] == 0 )) {
- *f->dirpath = 0;
- return( 0 );
- }
- /* if they didn't specify then assume we are adding to path */
- if ( *path == '\' )
- path++; /* skip leading for relative directory */
- else {
- strcpy( f->filepath, f->dirpath );
- if ( *f->dirpath )
- strcat( f->filepath, "\");
- strcat( f->filepath, path );
- path = f->filepath;
- }
- if ( stat( path, &st ) != 0 ) /* no such file or directory */
- return( 1 );
- if ((st.st_mode & S_IFDIR ) == 0 )
- return( 1 ); /* not a directory */
- /* use new directory path */
- strncpy( f->dirpath, path, FTPDIRLEN - 1 );
- f->dirpath[ FTPDIRLEN - 1] = 0;
- return( 0 );
- }
- /*
- * data_op_... operations on data socket
- */
- /*
- * data_op_get - does a "GET"
- * - could be optimized for speed by allocing a second
- * buffer and performing the DOS read while waiting for
- * the remote host to ack the data
- */
- static void data_op_get( ftpdses *f, tcp_Socket *t )
- {
- int len;
- unsigned diff;
- if ( sock_tbused( t ) == 0 ){
- // if ((diff = _dos_read( f->dhandle, ftpdbuffer, ftpdbufferlen )) <= 0 ) {
- diff = 0;
- if( _dos_read( f->dhandle, ftpdbuffer, ftpdbufferlen, &diff ) != 0 )
- f->errmsg = "550 Error while trying to read filern";
- else if ( diff == 0 ) /* eof or possibly error condition */
- f->doperation = FTP_OP_DON;
- else
- sock_write( t, ftpdbuffer, diff );
- }
- }
- static void data_op_init( ftpdses *f, word operation )
- {
- f->doperation = operation;
- f->dstatus = S_INIT;
- f->status = S_DATA;
- f->biglen = 0;
- f->bufhead = f->buftail = 0;
- f->errmsg = "250 Donern";
- sock_puts( &f->socket, "150 Openning data connectionrn");
- }
- static void c_op_dir( ftpdses *f, char *path )
- {
- char buffer[ 81 ], p;
- int status;
- f->dhandle = 0;
- /* support pseudo-standard -L */
- if ( !strnicmp( path, "-L", 2 )) {
- path += 2;
- if ( !*path ) path = NULL;
- }
- /* new for directories */
- if ( path != NULL ) strcpy( buffer, pathfile( f, path ));
- else strcpy( buffer, pathfile( f, "*.*" ));
- // if (path != NULL ) movmem( path, buffer, sizeof(buffer) - 1 );
- // else strcpy( buffer, "*.*" );
- buffer[ sizeof( buffer ) - 1 ] = 0;
- status = findfirst( buffer, &f->ffblk, FA_DIREC);
- if ( !strchr( buffer, '*' ) &&
- !strchr( buffer, '?' ) &&
- !status &&
- (ftpd->ffblk.ff_attrib & FA_DIREC ) &&
- *ftpd->ffblk.ff_name != '.') {
- strcat( buffer, "\*.*");
- status = findfirst( buffer, &f->ffblk, FA_DIREC);
- }
- if (!status) data_op_init( f, FTP_OP_DIR );
- else sock_puts(&f->socket, "550 File or directory not foundrn");
- }
- static void (*oldinit)(char *, char *);
- static void newinit( char *directive, char *value )
- {
- if (!strcmp( directive, FTPDPASS )) {
- strncpy( ftpdpassword, value, sizeof(ftpdpassword)-1);
- ftpdpassword[ sizeof(ftpdpassword) - 1] = 0;
- } else if (!strcmp( directive, FTPDBUFSIZ ))
- ftpdbufferlen = atoi( value );
- else
- if (oldinit) (*oldinit)( directive, value );
- }
- /*
- * getport - reads the FTP port negotiation stuff
- * - return nonzero on success
- *
- */
- static int getport( ftpdses *f, char *text )
- {
- byte a[6], *p;
- int i;
- for ( i = 0 ; (i < 6) && text ; ++i ) {
- if (( p = strchr( text , ',' )) != NULL) *p++ = 0;
- a[i] = atoi( text );
- text = p;
- }
- if ( i == 6 ) {
- f->hisip = intel( *(longword*)a );
- f->hisport = intel16( *(word*)&a[4] );
- } else {
- f->hisip = 0L; /* should be gained from his structure */
- f->hisport = 0;
- }
- return( i == 6 );
- }
- /*
- * parseftpline - called with a particular session and some data
- * - note, this baby supports multiple FTP sessions
- * by calling with different ftpdses values
- */
- static void parseftpline( ftpdses *f, tcp_Socket *s, char *line )
- {
- char *p, *q, cmdnum;
- FILE* file;
- /* remove blanks from line - p points to parameters */
- p = strchr( line, ' ');
- while ( *p == ' ') *p++ = 0;
- if ((q = strchr( p, ' '))!=NULL)
- while ( *q == ' ' ) *q++ = 0;
- if (!*line) return; /* nothing there */
- for ( cmdnum = 0 ; cmdnum < ENDLIST ; ++cmdnum )
- if ( !strnicmp( line, cmds[ cmdnum ], strlen( cmds[ cmdnum ] )))
- break;
- if ( cmdnum == ENDLIST ) {
- sock_puts( s, "500 Command not understood '");
- sock_puts( s, line );
- sock_puts( s, "'rn");
- /* check for unprivileged access */
- } else if ( cmdnum > USER && f->gotpass == 0 )
- sock_puts( s, "530 You're not logged in yetrn");
- /* file commands need a port number */
- else if ( cmdnum > PORT && ( f->hisport == 0 || f->hisip ==0L ))
- sock_puts( s, "425 Sorry, need PORT command firstrn");
- /* do the normal thing */
- else switch( f->dcmd = cmdnum ) {
- case ABOR :
- if (f->status == S_DATA ) {
- sock_abort( &f->dsocket );
- sock_puts( s, "426 Aborting at user's requestrn");
- } else
- sock_puts( s, "550 Abort what?rn");
- break;
- case QUIT :
- sock_puts( s, "221 Goodbyern");
- sock_close( s );
- sock_close( &f->dsocket ); /* if necessary */
- f->status = S_CLOS;
- break;
- case USER :
- sock_puts( s, "331 Password requiredrn");
- break;
- case PASS :
- if ( (*ftpdpassword && *line && !strcmp( p, ftpdpassword )) || !*ftpdpassword ) {
- f->gotpass = 1;
- sock_puts( s, "230 User logged inrn");
- } else {
- sock_puts( s, "530 Not quitern");
- f->gotpass = 0;
- }
- break;
- case HELP :
- sock_puts( s, "214-HELP - list of command accepted by WATTCP FTP daemonrn");
- sock_puts( s, " ABOR QUIT USER PASS HELP NOOP DEL TYPE PORTrn");
- sock_puts( s, " LIST NLST SIZE STOR RETR SITErn");
- sock_puts( s, " RMD XRMD MKD XMKD CWD XCWD PWD XPWDrn");
- sock_puts( s, "214 OKrn");
- break;
- case RMD :
- case XRMD :
- if ( rmdir( pathfile(f,p) ) == 0 )
- sock_puts( s, "250 RMD workedrn");
- else
- sock_puts( s, "550 RMD failedrn");
- break;
- case MKD :
- case XMKD :
- if ( mkdir( pathfile(f,p)) == 0 )
- sock_puts( s, "250 MKD workedrn");
- else
- sock_puts( s, "550 MKD failedrn");
- break;
- case PWD :
- case XPWD :
- sock_puts( s, "257 "");
- sock_puts( s, f->dirpath );
- sock_puts( s, "" is current directory.rn");
- break;
- case CWD :
- case XCWD :
- if ( !changedir( f, p )) {
- sock_puts( s, "250 changed directory to ");
- sock_puts( s, f->dirpath );
- sock_puts( s, "rn");
- } else
- sock_puts( s, "550 CWD failedrn");
- break;
- case DEL :
- sock_puts( s, (!unlink(pathfile(f,p))) ?
- "250 File deleted - "" : "550 Not Deleted - "" );
- sock_puts( s, p );
- sock_puts( s, ""rn");
- break;
- case PORT :
- if ( getport( f, p ))
- sock_puts( s, "200 Port command successfulrn");
- else
- sock_puts( s, "501 Bad port commandrn");
- break;
- case NLST :
- case LIST :
- c_op_dir( f, p ); /* start directory */
- break;
- case STOR : /* start a put */
- unlink( pathfile(f, p) );
- if ( (f->dhandle = open( pathfile(f,p), O_RDWR | O_CREAT |
- ((f->ascii) ? O_TEXT : O_BINARY) | O_DENYNONE, S_IWRITE)) == -1) {
- // creat( pathfile(f,p), S_IWRITE )) == -1 ) {
- sock_puts( &f->socket, "550 Could not create file '");
- sock_puts( &f->socket, p );
- sock_puts( &f->socket, "'.rn");
- } else
- data_op_init( f, FTP_OP_PUT );
- break;
- case RETR : /* start a get */
- if ( (f->dhandle = open( p, O_RDONLY ||
- ( /* (f->ascii) ? O_TEXT : */ O_BINARY))) == -1) {
- sock_puts( &f->socket, "550 Could not open file '");
- sock_puts( &f->socket, p );
- sock_puts( &f->socket, "' for readingrn");
- } else
- data_op_init( f, FTP_OP_GET );
- break;
- case SIZE :
- file = fopen( p, "rb" );
- if ( file == 0 || fseek( file, 0, SEEK_END ) != 0 ) {
- sock_puts( &f->socket, "550 Could not open file '");
- sock_puts( &f->socket, p );
- sock_puts( &f->socket, "' for readingrn");
- } else {
- long int fsize = ftell(file);
- sock_puts( &f->socket, "213 " );
- ltoa( fsize, ftpdbuffer, 10 );
- sock_puts( &f->socket, ftpdbuffer );
- sock_puts( &f->socket, "n" );
- fclose( file );
- }
- break;
-
- case TYPE :
- *p = toupper( *p );
- if ( *p == 'B' || *p == 'I' ) f->ascii = 0;
- else if ( *p == 'A' ) f->ascii = 1;
- else if ( *p == 'L' && *q == '8' ) f->ascii = 0;
- else {
- sock_puts( s, "550 Type not understoodrn");
- p = NULL;
- }
- if (p)
- sock_puts( s, (f->ascii)? "200 ASCII modern":"200 Binary modern");
- break;
- case NOOP :
- sock_puts( s, "200 NOOPrn");
- break;
- case SITE :
- if ( !strnicmp( p, "HALT", 4 )) {
- sock_puts( s, "221 Goodbye! Halting servicern");
- sock_close( s );
- sock_close( &f->dsocket ); /* if necessary */
- f->status = S_HALT;
- } else {
- sock_puts( s, "500 command not understood 'SITE ");
- sock_puts( s, p );
- sock_puts( s, "'rn");
- }
- break;
- }
- }
- /*
- * ftpd_alt_tick - processes data when we are doing something and not
- * awaiting user input
- */
- static void ftpd_alt_tick( ftpdses *f)
- {
- tcp_Socket *t;
- int len, wlen;
- t = &f->dsocket;
- switch ( ftpd->dstatus ) {
- case S_INIT :
- tcp_open( t, FTPDPORT-1, f->hisip, f->hisport, NULL );
- f->dstatus = S_WAIT;
- case S_WAIT :
- if ( sock_established( t )) ftpd->dstatus = S_CONN;
- break;
- case S_CONN :
- switch ( ftpd->doperation ) {
- case FTP_OP_DIR : /* directory operation */
- while ( sock_tbleft( t ) > 80 ) {
- if( f->dcmd == NLST ) {
- sock_puts( t, f->ffblk.ff_name );
- sock_puts( t, "n" );
- } else {
- dirformblock( &f->ffblk, ftpdbuffer );
- sock_puts( t, ftpdbuffer );
- }
- if ( findnext( &f->ffblk ))
- f->doperation = FTP_OP_DON;
- break;
- }
- break;
- case FTP_OP_PUT : /* part of put */
- if ((len = sock_fastread( t, ftpdbuffer, ftpdbufferlen)) > 0 ) {
- wlen = write( f->dhandle, ftpdbuffer, len );
- if (wlen < len || wlen == -1 ) {
- f->doperation = FTP_OP_DON;
- f->errmsg = "550 Error incurred while writing filern";
- }
- }
- break;
- case FTP_OP_GET : data_op_get(f,t); break;
- case FTP_OP_DON : /* close socket .... */
- sock_close( t );
- if (f->dhandle) close( f->dhandle );
- f->dhandle = 0;
- sock_puts( &f->socket, f->errmsg );
- f->doperation = FTP_OP_NOP;
- break;
- case FTP_OP_NOP : break;
- }
- if (!tcp_tick( t )) {
- if ( ftpd->doperation < FTP_OP_DON )
- ftpd->doperation = FTP_OP_DON;
- else {
- sock_close( t );
- ftpd->status = S_CONN;
- ftpd->dstatus = S_INIT;
- }
- }
- break;
- }
- }
- /*
- * ftpd_tick - call this whenever you have a free moment
- * - modify this routine to use several ftpdses
- * structures if you wish
- */
- void ftpd_tick( void )
- {
- tcp_Socket *t;
- t = &ftpd->socket;
- switch ( ftpd->status ) {
- case S_INIT : sock_abort( t );
- memset( ftpd, 0, sizeof( ftpdses ));
- ftpd->ascii = 0;
- tcp_listen( t, FTPDPORT, 0L, 0, NULL, 0 );
- ftpd->status = S_WAIT;
- break;
- case S_WAIT : if ( sock_established( t )) {
- sock_puts(t,"220-FTP Serverrn");
- if (*ftpdwelcome) {
- sock_puts(t,ftpdwelcome);
- sock_puts(t,"rn");
- }
- sock_puts(t,"220 Loginrn");
- ftpd->status = S_CONN;
- sock_mode( t, TCP_MODE_ASCII );
- }
- if ( !tcp_tick( t ))
- ftpd->status = S_CONN;
- break;
- case S_DATA : sock_mode( t, TCP_MODE_BINARY );
- ftpd_alt_tick( ftpd );
- sock_mode( t, TCP_MODE_ASCII );
- /* fall through */
- if ( !tcp_tick( t )) {
- sock_abort( t );
- sock_abort( &ftpd->dsocket );
- ftpd->status = S_CLOS;
- }
- case S_CONN : if ( sock_dataready( t )) {
- sock_gets( t, ftpdbuffer, ftpdbufferlen);
- sock_mode( t, TCP_MODE_BINARY );
- parseftpline( ftpd, t, ftpdbuffer );
- sock_mode( t, TCP_MODE_ASCII );
- }
- if (!tcp_tick( t )) {
- sock_close( t );
- ftpd->status = S_CLOS;
- }
- break;
- case S_CLOS : if ( !tcp_tick( t ) ) {
- ftpd->status = S_INIT;
- sock_close( t );
- }
- break;
- case S_HALT : break;
- }
- }
- static void ftpd_init()
- {
- *ftpdpassword = 0;
- *ftpdwelcome = 0;
- oldinit = usr_init;
- usr_init = newinit;
- ftpd = calloc( sizeof( ftpdses ), 1 ); /* before kernel init */
- ftpd->status = S_INIT;
- }
- #pragma startup ftpd_init 100
- void ftpdthread( DWORD x )
- {
- if (!ftpdbufferlen) ftpdbufferlen = 512;
- ftpdbuffer = kcalloc( 1, ftpdbufferlen);
- handlerrs( 1 );
- if ( *ftpdpassword == 0 )
- cprintf("WARNING: ftpd.password is not set in config file, any password will matchrn");
- while (1) {
- ftpd_tick();
- /* improve response only while doing real stuff */
- if ( ftpd->status == S_HALT ) break;
- else if ( ftpd->status == S_DATA ) rt_sleep(0);
- else rt_sleep( 100 );
- }
- /* if SITE was used to halt us */
- while ( tcp_tick( &ftpd->socket )) rt_sleep( 100 );
- kfree( ftpdbuffer );
- handlerrs( 0 );
- }