home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Datafile PD-CD 3
/
PDCD_3.iso
/
internet
/
tcpipsrc
/
FTP
/
c
/
FTPSERV
< prev
next >
Wrap
Text File
|
1995-01-24
|
21KB
|
845 lines
/* FTP Server state machine - see RFC 959 */
#define LINELEN 128 /* Length of command buffer */
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include "global.h"
#include "mbuf.h"
#include "netuser.h"
#include "timer.h"
#include "tcp.h"
#include "ftp.h"
#include "misc.h"
#include "arc.h"
#include "session.h"
static void ftpscs(struct tcb *, char, char);
static void ftpscr(struct tcb *, int16);
static void ftpcommand(struct ftp *);
static int pport(struct socket *, char *);
static void ftplogin(struct ftp *, char *);
int do_banner(char *file, char *pfx, struct tcb *c);
extern char bnrftpanon[];
extern char bnrftppriv[];
/* 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
"rest",
#define REST_CMD 21
NULLCHAR
};
/* Response messages */
static char banner[] = "220 %s FTP server 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 loggeda[] = "230 Logged in as anonymous, restrictions apply\r\n";
static char typeiok[] = "200 Image type OK\r\n";
static char typeaok[] = "200 Ascii 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 char restart[] = "350 Restarting at %lu\r\n";
static struct tcb *ftp_tcb;
/* Start up FTP service */
int ftp1(int argc, char **argv)
{
struct socket lsocket;
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, (void(*)())ftpscr, NULLVFP, (void(*)())ftpscs, 0, (char *) NULL);
return(0);
}
/* Shut down FTP server */
int ftp0(void)
{
if(ftp_tcb != NULLTCB)
close_tcp(ftp_tcb);
return(0);
}
/* FTP Server Control channel State change upcall handler */
static void ftpscs(struct tcb *tcb, char old, char new)
{
extern char hostname[];
struct ftp *ftp;
time_t t;
char *cp,*cp1;
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.
*/
#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 */
set_server_vars("ftp",tcb);
log_event(tcb,"open FTP");
time(&t);
cp = ctime(&t);
if((cp1 = strchr(cp,'\n')) != NULLCHAR)
*cp1 = '\0';
cwprintf(NULL, "%s : Incoming FTP session from %s\r\n",cp,psocket(&tcb->conn.remote));
tprintf(ftp->control,banner,hostname,cp);
break;
case CLOSE_WAIT:
close_tcp(tcb);
break;
case CLOSED:
log_event(tcb,"close FTP");
if((ftp = (struct ftp *)tcb->user) != NULLFTP)
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 */
static void ftpscr(struct tcb *tcb, int16 cnt)
{
register struct ftp *ftp;
char c;
struct mbuf *bp;
cnt = cnt;
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(pullone(&bp,&c) == 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_FILE_STATE:
case SENDING_DATA_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(struct tcb *tcb, char old, char new)
{
register struct ftp *ftp;
if((ftp = (struct ftp *)tcb->user) == NULLFTP)
{
/* Unknown connection. Kill it */
del_tcp(tcb);
}
else if((old == FINWAIT1 || old == CLOSING) && (ftp->state == SENDING_FILE_STATE || ftp->state == SENDING_DATA_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 a