www.pudn.com > S3c2410bsp.rar > ftpdLib.c
/* Copyright 1990 - 1998 Wind River Systems, Inc. */
#include "copyright_wrs.h"
#include "vxWorks.h"
#include "ioLib.h"
#include "taskLib.h"
#include "lstLib.h"
#include "stdio.h"
#include "sys/socket.h"
#include "netinet/in.h"
#include "errno.h"
#include "version.h"
#include "string.h"
#include "stdlib.h"
#include "iosLib.h"
#include "inetLib.h"
#include "dirent.h"
#include "sys/stat.h"
#include "sys/times.h"
#include "sockLib.h"
#include "logLib.h"
#include "unistd.h"
#include "pathLib.h"
#include "sysLib.h"
#include "ftpdLib.h"
#include "ctype.h"
#include "time.h"
#include "loginLib.h"
#include "fioLib.h"
#include "dosFsLib.h"
#define FTPD_BINARY_TYPE 0x1
#define FTPD_ASCII_TYPE 0x2
#define FTPD_EBCDIC_TYPE 0x4
#define FTPD_LOCAL_BYTE_TYPE 0x8
#define FTPD_STREAM_MODE 0x10
#define FTPD_BLOCK_MODE 0x20
#define FTPD_COMPRESSED_MODE 0x40
#define FTPD_NO_RECORD_STRU 0x100
#define FTPD_RECORD_STRU 0x200
#define FTPD_PAGE_STRU 0x400
#define FTPD_USER_OK 0x1000
#define FTPD_ANONYMOUS 0x2000
#define FTPD_PASSIVE 0x10000
#define FTPD_REPRESENTATION(slot) ( (slot)->status & 0xff)
#ifdef _unused_
#define FTPD_TRANS_MODE(slot) (((slot)->status >> 8) & 0xff)
#define FTPD_FILE_STRUCTURE(slot) (((slot)->status >> 16) & 0xff)
#define FTPD_STATUS(slot) (((slot)->status >> 24) & 0xff)
#endif
#define FTP_DATA_PORT 20
#define FTP_DAEMON_PORT 21
#define FTPD_SOCK_FREE -1
#define FTPD_WORK_TASK_NAME_LEN 40
#define FTPD_WAIT_MAX 90
#define FTPD_WAIT_INTERVAL 5
#define FTPD_CONN_TIMEOUT 200
#define FTPD_UC(ch) (((int) (ch)) & 0xff)
#define FTPD_MULTI_LINE 0x10000000
#define FTPD_WINDOW_SIZE 10240
#define FTPD_ANONYMOUS_USER_1 "anonymous"
#define FTPD_ANONYMOUS_USER_2 "ftp"
#define FTPD_ANONYMOUS_USER_3 "guest"
/* globals */
int ftpdDebug = FALSE; /* TRUE: debugging messages */
int ftpdTaskPriority = 56;
int ftpdTaskOptions = VX_SUPERVISOR_MODE | VX_UNBREAKABLE;
int ftpdWorkTaskPriority = 152;
int ftpdWorkTaskOptions = VX_SUPERVISOR_MODE | VX_UNBREAKABLE;
int ftpdWorkTaskStackSize = 12000;
int ftpdWindowSize = FTPD_WINDOW_SIZE;
int ftpsMaxClients = 4; /* Default max. for simultaneous connections */
int ftpsCurrentClients;
/* locals */
LOCAL BOOL ftpsActive = FALSE; /* Server started? */
LOCAL BOOL ftpsShutdownFlag; /* Server halt requested? */
typedef struct
{
NODE node;
int status;
int byteCount;
int cmdSock;
STATUS cmdSockError;
int dataSock;
struct sockaddr_in peerAddr;
struct sockaddr_in dataAddr;
caddr_t buf;
int bufSize;
time_t timeUsed;
char curDirName
[MAX_FILENAME_LENGTH+1];
char curUserName /* current user */
[MAX_LOGIN_NAME_LEN+1];
} FTPD_SESSION_DATA;
LOCAL int ftpdTaskId = -1;
LOCAL int ftpdServerSock = FTPD_SOCK_FREE;
LOCAL LIST ftpsSessionList;
LOCAL SEM_ID ftpsMutexSem;
LOCAL SEM_ID ftpsSignalSem;
LOCAL char ftpdWorkTaskName [FTPD_WORK_TASK_NAME_LEN];
LOCAL int ftpdNumTasks;
LOCAL FUNCPTR pLoginVrfyFunc = NULL ;
LOCAL char defaultHomeDir /* default root directory */
[MAX_FILENAME_LENGTH+1] = {0};
LOCAL char guestHomeDir /* anonymous root directory */
[MAX_FILENAME_LENGTH+1] = {0};
LOCAL char writeDirName /* anonymous upload directory */
[MAX_FILENAME_LENGTH+1] = {0};
/* Various messages to be told to the clients */
LOCAL const char *messages [] =
{
"Can't open passive connection",
"Parameter not accepted",
"Data connection error",
"Directory non existent or syntax error",
"Local resource failure: %s",
"VxWorks FTP server (VxWorks %s) ready.",
"Password required",
"User logged in",
"Bye...see you later",
"USER and PASS required",
"No files found or invalid directory or permission problem",
"Transfer complete",
"File \"%s\" not found or permission problem",
"Cannot create file \"%s\" or permission problem",
"Changed directory to \"%s\"",
"Type set to I, binary mode",
"Type set to A, ASCII mode",
"Port set okay",
"Current directory is \"%s\"",
"File structure set to NO RECORD",
"Stream mode okay",
"Allocate and Account not required",
"Entering Passive Mode (%d,%d,%d,%d,%d,%d)",
"NOOP -- did nothing as requested...hah!",
"Command not recognized",
"Error in input file",
"Unimplemented TYPE %d",
"You could at least say goodbye.",
"The following commands are recognized:",
"End of command list.",
"File deleted successfully.",
"Login failed.",
"File exists, ready for destination name.",
"File renamed successfuly.",
"\"%s\" directory created",
"Directory %s removed",
"%s: not a plain file",
"UNIX Type: L8 Version: VxWorks",
"\"%s\" directory could not be created",
"Timeout (%d seconds): closing connection.",
"Guest login ok, send your complete e-mail address as password.",
"Guest login ok, access restrictions apply.",
"Guest login ok, upload directory is %s.",
"Guest access denied.",
};
#define MSG_PASSIVE_ERROR 0
#define MSG_PARAM_BAD 1
#define MSG_DATA_CONN_ERROR 2
#define MSG_DIR_NOT_PRESENT 3
#define MSG_LOCAL_RESOURCE_FAIL 4
#define MSG_SERVER_READY 5
#define MSG_PASSWORD_REQUIRED 6
#define MSG_USER_LOGGED_IN 7
#define MSG_SEE_YOU_LATER 8
#define MSG_USER_PASS_REQ 9
#define MSG_DIR_ERROR 10
#define MSG_TRANS_COMPLETE 11
#define MSG_FILE_ERROR 12
#define MSG_CREATE_ERROR 13
#define MSG_CHANGED_DIR 14
#define MSG_TYPE_BINARY 15
#define MSG_TYPE_ASCII 16
#define MSG_PORT_SET 17
#define MSG_CUR_DIR 18
#define MSG_FILE_STRU 19
#define MSG_STREAM_MODE 20
#define MSG_ALLOC_ACCOUNT 21
#define MSG_PASSIVE_MODE 22
#define MSG_NOOP_OKAY 23
#define MSG_BAD_COMMAND 24
#define MSG_INPUT_FILE_ERROR 25
#define MSG_TYPE_ERROR 26
#define MSG_NO_GOOD_BYE 27
#define MSG_COMMAND_LIST_BEGIN 28
#define MSG_COMMAND_LIST_END 29
#define MSG_DELE_OK 30
#define MSG_USER_LOGIN_FAILED 31
#define MSG_RNFR_OK 32
#define MSG_RNTO_OK 33
#define MSG_MKD_OK 34
#define MSG_RMD_OK 35
#define MSG_FILE_NOTREG 36
#define MSG_SYST_REPLY 37
#define MSG_MKD_ERROR 38
#define MSG_CONN_TIMEOUT 39
#define MSG_GUEST_PASS 40
#define MSG_GUEST_OK 41
#define MSG_GUEST_UPLOAD_OK 42
#define MSG_GUEST_ACCESS 43
LOCAL char *ftpdCommandList =
"HELP USER PASS QUIT LIST NLST\n\
RETR STOR CWD TYPE PORT PWD\n\
STRU MODE ALLO ACCT PASV NOOP\n\
DELE RNFR RNTO MKD RMD MDTM\n\
SIZE SYST XCWD XPWD XMKD XRMD\n";
/* forward declarations */
LOCAL FTPD_SESSION_DATA *ftpdSessionAdd (void);
LOCAL void ftpdSessionDelete (FTPD_SESSION_DATA *);
LOCAL STATUS ftpdWorkTask (FTPD_SESSION_DATA *);
LOCAL STATUS ftpdCmdSend (FTPD_SESSION_DATA *, int, int, const char *,
int, int, int, int, int, int);
LOCAL STATUS ftpdDataConnGet (FTPD_SESSION_DATA *);
LOCAL void ftpdDataStreamSend (FTPD_SESSION_DATA *, char *);
LOCAL STATUS ftpdDataStreamReceive (FTPD_SESSION_DATA *, FILE *outStream);
LOCAL void ftpdSockFree (int *);
LOCAL STATUS ftpdDirListGet (int, char *, BOOL);
LOCAL void ftpdDebugMsg (char *, int, int, int, int);
LOCAL void unImplementedType (FTPD_SESSION_DATA *pSlot);
LOCAL void dataError (FTPD_SESSION_DATA *pSlot);
LOCAL void fileError (FTPD_SESSION_DATA *pSlot);
LOCAL void transferOkay (FTPD_SESSION_DATA *pSlot);
STATUS ftpLoginUserVerify (char *name, char *passwd);
void ftpdLoginInstall(FUNCPTR pLoginFunc);
#define rmdir(dName) remove((dName))
#define mkdir(dName) ftpMkdir((dName))
LOCAL STATUS ftpMkdir
(
char *dirName /* directory name */
)
{
int fd;
if ((fd = open (dirName, O_RDWR | O_CREAT, FSTAT_DIR | DEFAULT_DIR_PERM))
== ERROR)
{
return (ERROR);
}
return (close (fd));
}
LOCAL void ftpdTask (void)
{
int newSock;
FAST FTPD_SESSION_DATA *pSlot;
int on = 1;
int addrLen;
struct sockaddr_in addr;
char a_ip_addr [INET_ADDR_LEN];
ftpdNumTasks = 0;
FOREVER
{
addrLen = sizeof (struct sockaddr);
ftpdDebugMsg ("waiting for a new client connection...\n",0,0,0,0);
newSock = accept (ftpdServerSock, (struct sockaddr *) &addr, &addrLen);
if (newSock < 0)
{
ftpdDebugMsg ("cannot accept a new connection\n",0,0,0,0);
break;
}
semTake (ftpsMutexSem, WAIT_FOREVER);
setsockopt (newSock, SOL_SOCKET, SO_KEEPALIVE, (char *) &on,
sizeof (on));
inet_ntoa_b (addr.sin_addr, a_ip_addr);
ftpdDebugMsg ("accepted a new client connection from %s\n",
(int) a_ip_addr, 0, 0, 0);
pSlot = (FTPD_SESSION_DATA *)lstFirst (&ftpsSessionList);
while (pSlot != NULL)
{
FTPD_SESSION_DATA * pNext ;
time_t now = time(NULL);
pNext = (FTPD_SESSION_DATA *)lstNext (&pSlot->node);
if( (now - pSlot->timeUsed) > FTPD_CONN_TIMEOUT )
{
ftpdDebugMsg("session %x idle byteCount %d\n",
(int) pSlot, pSlot->byteCount, 0, 0);
if ( pSlot->byteCount == 0 )
{
ftpdCmdSend (pSlot, pSlot->cmdSock, 421,
messages [MSG_CONN_TIMEOUT],
now - pSlot->timeUsed, 0, 0, 0, 0, 0);
ftpdDebugMsg("deleting session %x\n", (int)pSlot, 0, 0, 0);
/* XXX ftpdSessionDelete( pSlot ); */
ftpdSockFree (&pSlot->cmdSock);
taskDelay( sysClkRateGet() / 10 );
}
else
pSlot->byteCount = 0 ;
}
pSlot = pNext ;
}
pSlot = ftpdSessionAdd ();
if (pSlot == NULL)
{
ftpdCmdSend (pSlot, newSock, 421,
"Session limit reached, closing control connection",
0, 0, 0, 0, 0, 0);
ftpdDebugMsg ("cannot get a new connection slot\n",0,0,0,0);
semGive (ftpsMutexSem);
continue;
}
pSlot->cmdSock = newSock;
bcopy ( (char *)&addr, (char *)&pSlot->peerAddr,
sizeof (struct sockaddr_in));
bcopy ( (char *)&addr, (char *)&pSlot->dataAddr,
sizeof (struct sockaddr_in));
pSlot->dataAddr.sin_port = htons (FTP_DATA_PORT);
sprintf (ftpdWorkTaskName, "tFtpdServ%d", ftpdNumTasks);
ftpdDebugMsg ("creating a new server task %s...\n",
(int) ftpdWorkTaskName, 0, 0, 0);
if (taskSpawn (ftpdWorkTaskName, ftpdWorkTaskPriority,
ftpdWorkTaskOptions, ftpdWorkTaskStackSize,
ftpdWorkTask, (int) pSlot,
0, 0, 0, 0, 0, 0, 0, 0, 0) == ERROR)
{
ftpdCmdSend (pSlot, newSock, 421,
"Service not available, closing control connection",
0, 0, 0, 0, 0, 0);
ftpdSessionDelete (pSlot);
ftpdDebugMsg ("cannot create a new work task\n",0,0,0,0);
semGive (ftpsMutexSem);
continue;
}
ftpdDebugMsg ("done.\n",0,0,0,0);
semGive (ftpsMutexSem);
}
ftpsActive = FALSE;
return;
}
/*******************************************************************************
* RETURNS:
* OK if server started, or ERROR otherwise.
*
* ERRNO: N/A
*/
STATUS ftpdInit
(
int port,
int stackSize
)
{
int on = 1;
struct sockaddr_in ctrlAddr;
if (ftpsActive)
return (OK);
ftpsShutdownFlag = FALSE;
ftpsCurrentClients = 0;
ftpdServerSock = socket (AF_INET, SOCK_STREAM, 0);
if (ftpdServerSock < 0)
return (ERROR);
lstInit (&ftpsSessionList);
ftpsMutexSem = semMCreate (SEM_Q_FIFO | SEM_DELETE_SAFE);
if (ftpsMutexSem == NULL)
{
close (ftpdServerSock);
return (ERROR);
}
ftpsSignalSem = semBCreate (SEM_Q_FIFO, SEM_EMPTY);
if (ftpsSignalSem == NULL)
{
close (ftpdServerSock);
semDelete (ftpsMutexSem);
return (ERROR);
}
if ( port == 0 )
port = FTP_DAEMON_PORT ;
ctrlAddr.sin_family = AF_INET;
ctrlAddr.sin_addr.s_addr = INADDR_ANY;
ctrlAddr.sin_port = htons ( port );
if (setsockopt (ftpdServerSock, SOL_SOCKET, SO_REUSEADDR,
(char *) &on, sizeof (on)) < 0)
{
close (ftpdServerSock);
semDelete (ftpsMutexSem);
semDelete (ftpsSignalSem);
return (ERROR);
}
if (bind (ftpdServerSock, (struct sockaddr *) &ctrlAddr,
sizeof (ctrlAddr)) < 0)
{
close (ftpdServerSock);
semDelete (ftpsMutexSem);
semDelete (ftpsSignalSem);
return (ERROR);
}
if (listen (ftpdServerSock, 5) < 0)
{
close (ftpdServerSock);
semDelete (ftpsMutexSem);
semDelete (ftpsSignalSem);
return (ERROR);
}
ftpdTaskId = taskSpawn ("tFtpdTask", ftpdTaskPriority - 1, ftpdTaskOptions,
stackSize == 0 ? ftpdWorkTaskStackSize : stackSize,
(FUNCPTR) ftpdTask, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
if (ftpdTaskId == ERROR)
{
ftpdDebugMsg ("ERROR: ftpdTask cannot be created\n",0,0,0,0);
close (ftpdServerSock);
semDelete (ftpsMutexSem);
semDelete (ftpsSignalSem);
return (ERROR);
}
ftpsActive = TRUE;
ftpdLoginInstall(ftpLoginUserVerify);
ftpdDebugMsg ("ftpdTask created\n",0,0,0,0);
return (OK);
}
/*******************************************************************************
*/
STATUS ftpdDelete (void)
{
BOOL serverActive = FALSE;
FTPD_SESSION_DATA * pData;
if (! ftpsActive)
return (OK);
semTake (ftpsMutexSem, WAIT_FOREVER);
taskDelete (ftpdTaskId);
ftpdTaskId = -1;
if (ftpsCurrentClients != 0)
serverActive = TRUE;
ftpsShutdownFlag = TRUE;
pData = (FTPD_SESSION_DATA *)lstFirst (&ftpsSessionList);
while (pData != NULL)
{
ftpdSockFree (&pData->cmdSock);
pData = (FTPD_SESSION_DATA *)lstNext (&pData->node);
}
semGive (ftpsMutexSem);
if (serverActive)
{
semTake (ftpsSignalSem, WAIT_FOREVER);
}
ftpdSockFree (&ftpdServerSock);
lstFree (&ftpsSessionList);
semDelete (ftpsMutexSem);
semDelete (ftpsSignalSem);
ftpsActive = FALSE;
return (OK);
}
/*******************************************************************************
*/
LOCAL FTPD_SESSION_DATA *ftpdSessionAdd (void)
{
FAST FTPD_SESSION_DATA *pSlot;
if (ftpsCurrentClients >= ftpsMaxClients)
return (NULL);
pSlot = (FTPD_SESSION_DATA *) calloc (sizeof (FTPD_SESSION_DATA), 1);
if (pSlot == NULL)
{
return (NULL);
}
if (ftpdWindowSize < 0 || ftpdWindowSize > 65536)
ftpdWindowSize = FTPD_WINDOW_SIZE;
pSlot->bufSize = ftpdWindowSize ;
ftpdDebugMsg ("allocating new session buffer %d bytes\n",
pSlot->bufSize,0,0,0);
pSlot->buf = malloc( pSlot->bufSize );
if( pSlot->buf == NULL )
{
free(pSlot);
return (NULL);
}
pSlot->dataSock = FTPD_SOCK_FREE;
pSlot->cmdSock = FTPD_SOCK_FREE;
pSlot->cmdSockError = OK;
pSlot->status = FTPD_STREAM_MODE | FTPD_BINARY_TYPE | FTPD_NO_RECORD_STRU;
pSlot->byteCount = 0;
strcpy( pSlot->curDirName,"/tffs0/" );
semTake (ftpsMutexSem, WAIT_FOREVER);
lstAdd (&ftpsSessionList, &pSlot->node);
ftpdNumTasks++;
ftpsCurrentClients++;
semGive (ftpsMutexSem);
return (pSlot);
}
/*******************************************************************************
*/
LOCAL void ftpdSessionDelete
(
FAST FTPD_SESSION_DATA *pSlot
)
{
if (pSlot == NULL) /* null slot? don't do anything */
return;
semTake (ftpsMutexSem, WAIT_FOREVER);
--ftpdNumTasks;
--ftpsCurrentClients;
lstDelete (&ftpsSessionList, &pSlot->node);
ftpdSockFree (&pSlot->cmdSock); /* release data and command sockets */
ftpdSockFree (&pSlot->dataSock);
free (pSlot->buf);
free (pSlot);
if (ftpsShutdownFlag)
{
if (ftpsCurrentClients == 0)
semGive (ftpsSignalSem);
}
semGive (ftpsMutexSem);
return;
}
/*******************************************************************************
*
* ftpPathAccessVerify - verify client access to a path
*
*/
LOCAL STATUS ftpPathAccessVerify
(
FTPD_SESSION_DATA *pSlot,
char * path,
int mode
)
{
char * where ;
int len;
/* allways allow access if not anonymous user */
if( (pSlot->status & FTPD_ANONYMOUS) == 0 )
return (OK);
if( mode == O_RDONLY )
{
where = guestHomeDir ;
}
else
{
where = writeDirName ;
}
len = strlen(where);
if (*where==EOS||*path==EOS) goto deny;
/* perform case-insensitive comparison a la strncmp() */
{
FAST char *s1, *s2;
FAST int i;
for( s1 = where, s2 = path, i = 0;
(*s1!=EOS && *s2!=EOS) && istatus & FTPD_ANONYMOUS )
{
int len = strlen( guestHomeDir );
if( strncmp( guestHomeDir, path, len) != 0)
return NULL ;
strcpy( path, path+len );
}
return(path);
}
/*******************************************************************************
*
*
*
*/
LOCAL void ftpPathNormalize
(
FTPD_SESSION_DATA *pSlot,
char * path,
char * buffer,
char ** pResult
)
{
if ( (strcmp(path,".") == 0) || (path[0] == EOS) )
{
*pResult = &pSlot->curDirName [0];
return ;
}
(void) pathCat (pSlot->curDirName, path, buffer);
pathCondense (buffer); /* condense ".." shit */
*pResult = buffer;
}
/*******************************************************************************
*/
LOCAL STATUS ftpdWorkTask
(
FTPD_SESSION_DATA *pSlot /* pointer to the active slot to be handled */
)
{
FAST int sock;
FAST char *pBuf, *pBufEnd;
struct sockaddr_in passiveAddr;
char *dirName;
FAST int numRead;
int addrLen = sizeof (passiveAddr);
int portNum [6];
u_long value;
char renFile [MAX_FILENAME_LENGTH];
char newPath [MAX_FILENAME_LENGTH];
char *pFileName;
FILE *outStream;
char *upperCommand;
pBuf = &pSlot->buf [0];
pBufEnd = pSlot->bufSize-1 + pBuf ;
sock = pSlot->cmdSock;
if (ftpsShutdownFlag)
{
ftpdCmdSend (pSlot, sock, 421,
"Service not available, closing control connection",
0, 0, 0, 0, 0, 0);
ftpdSessionDelete (pSlot);
return (OK);
}
if (ftpdCmdSend (pSlot, sock, 220, messages [MSG_SERVER_READY],
(int)vxWorksVersion, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
FOREVER
{
taskDelay (0); /* time share among same priority tasks */
if (pSlot->cmdSockError == ERROR)
goto connectionLost;
pSlot->byteCount = 0 ;
(void) time( &pSlot->timeUsed );
if (ftpsShutdownFlag)
break;
bzero( pBuf, 8 );
FOREVER
{
taskDelay (0);
if ((numRead = read (sock, pBuf, 1)) <= 0)
{
if (ftpsShutdownFlag)
{
*pBuf = EOS;
break;
}
if (numRead == 0)
ftpdCmdSend (pSlot, sock, 221, messages [MSG_NO_GOOD_BYE],
0, 0, 0, 0, 0, 0);
ftpdSessionDelete (pSlot);
return ERROR;
}
if ( *pBuf == '\r' )
continue;
if (( *pBuf == '\n' ) || (pBuf == pBufEnd))
{
*pBuf = EOS;
break;
}
pBuf++;
} /* FOREVER - read line */
pBuf = &pSlot->buf [0];
for (upperCommand = pBuf; (*upperCommand != ' ') &&
(*upperCommand != EOS); upperCommand++)
*upperCommand = toupper (*upperCommand);
ftpdDebugMsg ("read command %s\n", (int)pBuf,0,0,0);
if (ftpsShutdownFlag)
{
ftpdCmdSend (pSlot, sock, 421,
"Service not available, closing control connection",
0, 0, 0, 0, 0, 0);
break;
}
if (strncmp (pBuf, "USER", 4) == 0)
{
if ( *(pBuf + 4) == '\0' )
pSlot->curUserName[0] = '\0';
else
strncpy(pSlot->curUserName, pBuf+5, MAX_LOGIN_NAME_LEN);
pSlot->status &= ~(FTPD_USER_OK | FTPD_ANONYMOUS);
if( (strcmp(pSlot->curUserName, FTPD_ANONYMOUS_USER_1) ==0) ||
(strcmp(pSlot->curUserName, FTPD_ANONYMOUS_USER_2) ==0) ||
(strcmp(pSlot->curUserName, FTPD_ANONYMOUS_USER_3) ==0) )
{
pSlot->status |= FTPD_ANONYMOUS ; /* tentative */
ftpdCmdSend (pSlot, sock, 331, messages [MSG_GUEST_PASS],
0, 0, 0, 0, 0, 0) ;
continue;
}
if (ftpdCmdSend (pSlot, sock, 331, messages [MSG_PASSWORD_REQUIRED],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
continue;
}
else if (strncmp (pBuf, "PASS", 4) == 0)
{
if( pSlot->status & FTPD_ANONYMOUS )
{
if( (guestHomeDir[0] == EOS) || (pBuf[5]==EOS) )
{
ftpdCmdSend (pSlot, sock, 530,
messages [MSG_USER_LOGIN_FAILED], 0, 0, 0, 0, 0, 0);
continue;
}
pSlot->status |= FTPD_USER_OK;
strcpy (pSlot->curDirName, guestHomeDir);
if( guestHomeDir [ strlen(guestHomeDir) -1 ] != '/');
strcat(pSlot->curDirName, "/");
strcpy(newPath, writeDirName);
if( writeDirName[0] == EOS )
ftpdCmdSend (pSlot, sock, 230,
messages [MSG_GUEST_OK], 0, 0, 0, 0, 0, 0);
else
ftpdCmdSend (pSlot, sock, 230,
messages [MSG_GUEST_UPLOAD_OK],
(int) ftpdPathForPrint(pSlot, newPath),
0, 0, 0, 0, 0);
continue;
}
if ( pLoginVrfyFunc != (FUNCPTR)NULL )
{
ftpdDebugMsg ("Verifying login for user %s\n",
(int)pSlot->curUserName,0,0,0);
if ( (*pLoginVrfyFunc)(pSlot->curUserName, pBuf+5) != OK )
{
if (ftpdCmdSend (pSlot, sock,
530, messages [MSG_USER_LOGIN_FAILED],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
pSlot->status &= ~FTPD_USER_OK;
continue;
}
}
else if( guestHomeDir[0] != EOS )
{
if (ftpdCmdSend (pSlot, sock,
530, messages [MSG_USER_LOGIN_FAILED],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
pSlot->status &= ~FTPD_USER_OK;
continue;
}
pSlot->status |= FTPD_USER_OK;
if (ftpdCmdSend (pSlot, sock, 230, messages [MSG_USER_LOGGED_IN],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
continue;
}
else if (strncmp (pBuf, "QUIT", 4) == 0)
{
/* sayonara */
ftpdCmdSend (pSlot, sock, 221, messages [MSG_SEE_YOU_LATER],
0, 0, 0, 0, 0, 0);
ftpdSessionDelete (pSlot);
return OK;
}
else if (strncmp (pBuf, "SYST", 4) == 0)
{
ftpdCmdSend (pSlot, sock, 215, messages [MSG_SYST_REPLY],
0, 0, 0, 0, 0, 0);
continue;
}
else if (strncmp (pBuf, "HELP", 4) == 0)
{
if (ftpdCmdSend (pSlot, sock, FTPD_MULTI_LINE | 214,
messages [MSG_COMMAND_LIST_BEGIN], 0, 0, 0, 0, 0, 0)
== ERROR)
goto connectionLost;
if (write (pSlot->cmdSock, ftpdCommandList,
strlen (ftpdCommandList)) <= 0)
goto connectionLost;
if (ftpdCmdSend (pSlot, sock, 214, messages [MSG_COMMAND_LIST_END],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
continue; /* All is well go wait for the next command */
}
else if ((pSlot->status & FTPD_USER_OK) == 0) /* validated yet? */
{
if (ftpdCmdSend (pSlot, sock, 530, messages [MSG_USER_PASS_REQ],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
/* do not proceed further until he's legit */
continue;
}
if (strncmp (pBuf, "LIST", 4) == 0 ||
strncmp (pBuf, "NLST", 4) == 0)
{
STATUS retVal;
ftpPathNormalize ( pSlot, &pBuf[5], newPath, &dirName );
ftpdDebugMsg ("listing %s\n", (int)dirName,0,0,0);
if( ftpPathAccessVerify(pSlot, dirName, O_RDONLY) == ERROR )
{
if (ftpdCmdSend (pSlot, sock,
550, messages [MSG_GUEST_ACCESS],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
continue;
}
if (ftpdDataConnGet (pSlot) == ERROR)
{
if (ftpdCmdSend (pSlot, sock,
426, messages [MSG_DATA_CONN_ERROR],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
continue;
}
retVal = ftpdDirListGet (pSlot->dataSock, dirName,
(strncmp (pBuf, "LIST", 4) == 0));
if (retVal == ERROR)
{
if (ftpdCmdSend (pSlot, sock, 550, messages [MSG_DIR_ERROR],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
}
else
{
if (ftpdCmdSend (pSlot, sock,
226, messages [MSG_TRANS_COMPLETE],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
}
ftpdSockFree (&pSlot->dataSock);
}
else if ( (strncmp (pBuf, "RETR", 4) == 0) ||
(strncmp (pBuf, "SIZE", 4) == 0) ||
(strncmp (pBuf, "MDTM", 4) == 0) )
{
struct stat fileStat ;
ftpPathNormalize ( pSlot, &pBuf[5], newPath, &pFileName );
ftpdDebugMsg ("accessing %s\n", (int)pFileName,0,0,0);
if( ftpPathAccessVerify(pSlot, pFileName, O_RDONLY) == ERROR )
{
if (ftpdCmdSend (pSlot, sock,
550, messages [MSG_GUEST_ACCESS],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
continue;
}
if(stat(pFileName, &fileStat) == ERROR )
{
if (ftpdCmdSend (pSlot, sock, 550, messages [MSG_FILE_ERROR],
(int)(&pBuf[5]), 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
continue;
}
if( ! S_ISREG (fileStat.st_mode) )
{
if (ftpdCmdSend (pSlot, sock, 550, messages [MSG_FILE_NOTREG],
(int)(&pBuf[5]), 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
continue;
}
/* decide what to do with the file according to the actual cmd */
if (strncmp (pBuf, "RETR", 4) == 0)
{
/* ship it away */
ftpdDataStreamSend (pSlot, pFileName);
}
else if (strncmp (pBuf, "SIZE", 4) == 0)
{
if (ftpdCmdSend (pSlot, sock, 213, "%lu",
fileStat.st_size, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
}
else if (strncmp (pBuf, "MDTM", 4) == 0)
{
struct tm fileDate;
localtime_r (&fileStat.st_mtime, &fileDate);
if (ftpdCmdSend (pSlot, sock, 213, "%04d%02d%02d%02d%02d%02d",
fileDate.tm_year+1900,
fileDate.tm_mon,
fileDate.tm_mday,
fileDate.tm_hour,
fileDate.tm_min,
fileDate.tm_sec
) == ERROR)
goto connectionLost;
}
}
else if (strncmp (pBuf, "STOR", 4) == 0)
{
STATUS retVal ;
/* store a file */
ftpPathNormalize ( pSlot, &pBuf[5], newPath, &pFileName );
ftpdDebugMsg ("STOR %s\n", (int)pFileName,0,0,0);
if( ftpPathAccessVerify(pSlot, pFileName, O_WRONLY) == ERROR )
{
if (ftpdCmdSend (pSlot, sock,
550, messages [MSG_GUEST_ACCESS],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
continue;
}
if ((outStream = fopen (pFileName, "w")) == NULL)
{
if (ftpdCmdSend (pSlot, sock, 553, messages[MSG_CREATE_ERROR],
(int)(&pBuf[5]), 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
continue;
}
/* receive the file */
retVal = ftpdDataStreamReceive (pSlot, outStream);
(void) fclose (outStream);
if( retVal == ERROR )
{
unlink( pFileName );
}
}
else if (strncmp (pBuf, "RNFR", 4) == 0)
{
struct stat st; /* for stat() system call */
ftpPathNormalize ( pSlot, &pBuf[5], newPath, &pFileName );
ftpdDebugMsg ("RNFR %s\n", (int)pFileName,0,0,0);
if( ftpPathAccessVerify(pSlot, pFileName, O_RDWR) == ERROR )
{
if (ftpdCmdSend (pSlot, sock,
550, messages [MSG_GUEST_ACCESS],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
continue;
}
if( stat( pFileName, &st ) != OK )
{
if (ftpdCmdSend (pSlot, sock, 550, messages [MSG_FILE_ERROR],
(int) pFileName, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost ;
renFile[0] = EOS ;
continue;
}
strncpy( renFile, pFileName, MAX_FILENAME_LENGTH );
if (ftpdCmdSend (pSlot, sock, 350, messages [MSG_RNFR_OK],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost ;
}
else if (strncmp (pBuf, "RNTO", 4) == 0)
{
ftpPathNormalize ( pSlot, &pBuf[5], newPath, &pFileName );
ftpdDebugMsg ("RNTO %s\n", (int)pFileName,0,0,0);
if( ftpPathAccessVerify(pSlot, pFileName, O_WRONLY) == ERROR )
{
if (ftpdCmdSend (pSlot, sock,
550, messages [MSG_GUEST_ACCESS],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
continue;
}
if( rename( renFile, pFileName ) != OK )
{
if (ftpdCmdSend (pSlot, sock, 550, messages [MSG_DIR_ERROR],
(int) &pBuf[5], 0, 0, 0, 0, 0) == ERROR)
goto connectionLost ;
renFile[0] = EOS ;
continue ;
}
if (ftpdCmdSend (pSlot, sock, 250, messages [MSG_RNTO_OK],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost ;
renFile[0] = EOS ;
}
else if (strncmp (pBuf, "DELE", 4) == 0)
{
ftpPathNormalize ( pSlot, &pBuf[5], newPath, &pFileName );
ftpdDebugMsg ("DELE %s\n", (int)pFileName,0,0,0);
if( ftpPathAccessVerify(pSlot, pFileName, O_WRONLY) == ERROR )
{
if (ftpdCmdSend (pSlot, sock,
550, messages [MSG_GUEST_ACCESS],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
continue;
}
if( remove( pFileName ) != OK )
{
if (ftpdCmdSend (pSlot, sock, 550, messages [MSG_FILE_ERROR],
(int) &pBuf[5], 0, 0, 0, 0, 0) == ERROR)
goto connectionLost ;
continue;
}
else
{
if (ftpdCmdSend (pSlot, sock, 200, messages [MSG_DELE_OK],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost ;
}
}
else if ((strncmp (pBuf, "MKD", 3) == 0) ||
(strncmp (pBuf, "XMKD", 4) == 0) )
{
if(strncmp (pBuf, "MKD", 3) == 0)
ftpPathNormalize ( pSlot, &pBuf[4], newPath, &pFileName );
else
ftpPathNormalize ( pSlot, &pBuf[5], newPath, &pFileName );
ftpdDebugMsg ("MKD %s\n", (int)pFileName,0,0,0);
if( ftpPathAccessVerify(pSlot, pFileName, O_WRONLY) == ERROR )
{
if (ftpdCmdSend (pSlot, sock,
550, messages [MSG_GUEST_ACCESS],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
continue;
}
if( mkdir( pFileName ) != OK )
{
if (ftpdCmdSend (pSlot, sock, 550, messages [MSG_MKD_ERROR],
(int) &pBuf[4], 0, 0, 0, 0, 0) == ERROR)
goto connectionLost ;
continue;
}
else
{
if (ftpdCmdSend (pSlot, sock, 200, messages [MSG_MKD_OK],
(int) &pBuf[4], 0, 0, 0, 0, 0) == ERROR)
goto connectionLost ;
}
}
else if ((strncmp (pBuf, "RMD", 3) == 0) ||
(strncmp (pBuf, "XRMD", 4) == 0) )
{
char *pDir ;
if(strncmp (pBuf, "RMD", 3) == 0)
pDir = pBuf+4 ;
else
pDir = pBuf+5 ;
ftpPathNormalize ( pSlot, pDir, newPath, &pFileName );
ftpdDebugMsg ("RMD %s\n", (int)pFileName,0,0,0);
if( ftpPathAccessVerify(pSlot, pFileName, O_WRONLY) == ERROR )
{
if (ftpdCmdSend (pSlot, sock,
550, messages [MSG_GUEST_ACCESS],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
continue;
}
if( rmdir( pFileName ) != OK )
{
if (ftpdCmdSend (pSlot, sock, 550, messages [MSG_FILE_ERROR],
(int) pDir, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost ;
continue;
}
else
{
if (ftpdCmdSend (pSlot, sock, 200, messages [MSG_RMD_OK],
(int) pDir, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost ;
}
}
else if ((strncmp (pBuf, "CWD", 3) == 0)||
(strncmp (pBuf, "CDUP", 4) == 0)||
(strncmp (pBuf, "XCWD", 4) == 0)||
(strncmp (pBuf, "XCUP", 4) == 0) )
{
struct stat st;
if(strncmp (pBuf, "CWD", 3) == 0)
ftpPathNormalize ( pSlot, &pBuf[4], newPath, &dirName );
else if( strncmp (pBuf, "XCWD", 4) == 0)
ftpPathNormalize ( pSlot, &pBuf[5], newPath, &dirName );
else
ftpPathNormalize ( pSlot, "..", newPath, &dirName );
if( ftpPathAccessVerify(pSlot, newPath, O_RDONLY) == ERROR )
{
if (ftpdCmdSend (pSlot, sock,
550, messages [MSG_GUEST_ACCESS],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
continue;
}
if( (stat( newPath, &st ) != OK ) || !S_ISDIR (st.st_mode))
{
if (ftpdCmdSend (pSlot, sock, 501,
messages[MSG_DIR_NOT_PRESENT],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost ;
continue;
}
(void) strncpy (pSlot->curDirName, newPath, MAX_FILENAME_LENGTH);
if (ftpdCmdSend (pSlot, sock, 250, messages [MSG_CHANGED_DIR],
(int)ftpdPathForPrint(pSlot,newPath),
0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
}
else if (strncmp (pBuf, "TYPE", 4) == 0)
{
if (pBuf [5] == 'I' || pBuf [5] == 'i' ||
pBuf [5] == 'L' || pBuf [5] == 'l')
{
pSlot->status |= FTPD_BINARY_TYPE;
pSlot->status &= ~FTPD_ASCII_TYPE;
if (ftpdCmdSend (pSlot, sock, 200, messages [MSG_TYPE_BINARY],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
}
else if (pBuf [5] == 'A' || pBuf [5] == 'a')
{
pSlot->status |= FTPD_ASCII_TYPE;
pSlot->status &= ~FTPD_BINARY_TYPE;
if (ftpdCmdSend (pSlot, sock, 200, messages [MSG_TYPE_ASCII],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
}
else
{
if (ftpdCmdSend (pSlot, sock, 504, messages [MSG_PARAM_BAD],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
}
}
else if (strncmp (pBuf, "PORT", 4) == 0)
{
(void) sscanf (&pBuf [5], "%d,%d,%d,%d,%d,%d",
&portNum [0], &portNum [1], &portNum [2],
&portNum [3], &portNum [4], &portNum [5]);
pSlot->dataAddr.sin_port = portNum [4] * 256 + portNum [5];
pSlot->dataAddr.sin_port = htons (pSlot->dataAddr.sin_port);
value = (portNum [0] << 24) | (portNum [1] << 16) |
(portNum [2] << 8) | portNum [3];
pSlot->dataAddr.sin_addr.s_addr = htonl (value);
if (ftpdCmdSend (pSlot, sock, 200, messages [MSG_PORT_SET],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
}
else if ((strncmp (pBuf, "PWD", 3) == 0) ||
(strncmp (pBuf, "XPWD", 4) == 0))
{
(void) strcpy (pBuf, pSlot->curDirName);
if (ftpdCmdSend (pSlot, sock, 257, messages [MSG_CUR_DIR],
(int)ftpdPathForPrint(pSlot,pBuf),
0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
}
else if (strncmp (pBuf, "STRU", 4) == 0)
{
if (pBuf [5] == 'F' || pBuf [5] == 'f')
{
if (ftpdCmdSend (pSlot, sock, 200, messages [MSG_FILE_STRU],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
}
else
{
if (ftpdCmdSend (pSlot, sock, 504, messages [MSG_PARAM_BAD],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
}
}
else if (strncmp (pBuf, "MODE", 4) == 0)
{
/* specify transfer mode */
/* we only support stream mode -- no block or compressed mode */
if (pBuf [5] == 'S' || pBuf [5] == 's')
{
if (ftpdCmdSend (pSlot, sock, 200, messages [MSG_STREAM_MODE],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
}
else
{
if (ftpdCmdSend (pSlot, sock, 504, messages [MSG_PARAM_BAD],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
}
}
else if (strncmp (pBuf, "ALLO", 4) == 0 ||
strncmp (pBuf, "ACCT", 4) == 0)
{
/* allocate and account commands are not need */
if (ftpdCmdSend (pSlot, sock, 202, messages [MSG_ALLOC_ACCOUNT],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
}
else if (strncmp (pBuf, "PASV", 4) == 0)
{
int outval1;
int outval2;
int outval3;
int outval4;
int outval5;
int outval6;
ftpdSockFree (&pSlot->dataSock);
if ((pSlot->dataSock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
{
if (ftpdCmdSend (pSlot, sock, 425, messages [MSG_PASSIVE_ERROR],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
continue ;
}
addrLen = sizeof (pSlot->dataAddr);
if (getsockname (pSlot->cmdSock,
(struct sockaddr *) &pSlot->dataAddr,
&addrLen) < 0)
{
if (ftpdCmdSend (pSlot, sock,
425, messages [MSG_PASSIVE_ERROR],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
}
pSlot->dataAddr.sin_port = htons (0);
addrLen = sizeof (struct sockaddr_in);
if (bind (pSlot->dataSock, (struct sockaddr *)&pSlot->dataAddr,
sizeof (struct sockaddr_in)) < 0 ||
getsockname (pSlot->dataSock,
(struct sockaddr *) &pSlot->dataAddr,
&addrLen) < 0 ||
listen (pSlot->dataSock, 1) < 0)
{
ftpdSockFree (&pSlot->dataSock);
if (ftpdCmdSend (pSlot, sock,
425, messages [MSG_PASSIVE_ERROR],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
continue;
}
pSlot->status |= FTPD_PASSIVE;
value = pSlot->dataAddr.sin_addr.s_addr;
outval1 = ( (u_char *)&value)[0];
outval2 = ( (u_char *)&value)[1];
outval3 = ( (u_char *)&value)[2];
outval4 = ( (u_char *)&value)[3];
outval5 = ( (u_char *)&pSlot->dataAddr.sin_port)[0];
outval6 = ( (u_char *)&pSlot->dataAddr.sin_port)[1];
if (ftpdCmdSend (pSlot, pSlot->cmdSock,
227, messages [MSG_PASSIVE_MODE],
outval1, outval2, outval3, outval4,
outval5, outval6) == ERROR)
goto connectionLost;
}
else if (strncmp (pBuf, "NOOP", 4) == 0)
{
/* don't do anything */
if (ftpdCmdSend (pSlot, sock, 200, messages [MSG_NOOP_OKAY],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
}
else
{
if (ftpdCmdSend (pSlot, sock, 500, messages [MSG_BAD_COMMAND],
0, 0, 0, 0, 0, 0) == ERROR)
goto connectionLost;
}
}
ftpdSessionDelete (pSlot);
return (OK);
connectionLost:
ftpdDebugMsg ("FTP Control connection error, closed.\n", 0,0,0,0);
ftpdSessionDelete (pSlot);
return (ERROR);
}
/*******************************************************************************
*/
LOCAL STATUS ftpdDataConnGet
(
FTPD_SESSION_DATA *pSlot
)
{
FAST int newSock;
int addrLen;
struct sockaddr_in addr;
int on = 1;
int retry = 0;
if (pSlot->cmdSock == FTPD_SOCK_FREE)
return (ERROR);
pSlot->byteCount = 0;
(void) time( &pSlot->timeUsed );
if (pSlot->dataSock != FTPD_SOCK_FREE)
{
if (pSlot->status & FTPD_PASSIVE)
{
/* we're being passive. wait for our client to connect to us. */
addrLen = sizeof (struct sockaddr);
ftpdDebugMsg ("doing accept for passive fd=%d\n",
(int)pSlot->dataSock,0,0,0);
if ((newSock = accept (pSlot->dataSock, (struct sockaddr *) &addr,
&addrLen)) < 0)
{
ftpdCmdSend (pSlot, pSlot->cmdSock,
425, "Can't open data connection",
0, 0, 0, 0, 0, 0);
ftpdSockFree (&pSlot->dataSock);
/* we can't be passive no more */
pSlot->status &= ~FTPD_PASSIVE;
return (ERROR);
}
if (setsockopt (newSock, SOL_SOCKET, SO_KEEPALIVE, (char *) &on,
sizeof (on)) != 0)
{
ftpdSockFree (&pSlot->dataSock);
return (ERROR);
}
if (ftpdWindowSize < 0 || ftpdWindowSize > 65536)
ftpdWindowSize = FTPD_WINDOW_SIZE;
/* set the window size */
if (setsockopt(newSock, SOL_SOCKET, SO_SNDBUF,
(char *)&ftpdWindowSize, sizeof (ftpdWindowSize)))
printf("Couldn't set the Send Window to 10k\n");
if (setsockopt(newSock, SOL_SOCKET, SO_RCVBUF,
(char *)&ftpdWindowSize, sizeof (ftpdWindowSize)))
printf("Couldn't set the Send Window to 10k\n");
/* replace the dataSock with our new connection */
(void) close (pSlot->dataSock);
pSlot->dataSock = newSock;
/* N.B.: we stay passive */
if (ftpdCmdSend (pSlot, pSlot->cmdSock,
150, "Opening %s mode data connection",
pSlot->status & FTPD_ASCII_TYPE ? (int) "ASCII"
: (int) "BINARY", 0, 0, 0, 0, 0) == ERROR)
{
(void) close (pSlot->dataSock);
return (ERROR);
}
return (OK);
}
else
{
/* reuse the old connection -- it's still useful */
if (ftpdCmdSend (pSlot, pSlot->cmdSock,
125, "Using existing data connection",
0, 0, 0, 0, 0, 0) == ERROR)
{
ftpdSockFree (&pSlot->dataSock);
return (ERROR);
}
return (OK);
}
}
else
{
/* Determine address for local end of connection. */
addrLen = sizeof (struct sockaddr);
if (getsockname (pSlot->cmdSock, (struct sockaddr *) &addr, &addrLen)
< 0)
{
return (ERROR);
}
/* Replace control port with default data port. */
addr.sin_port = htons (FTP_DATA_PORT);
/* open a new data socket */
if ((pSlot->dataSock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
{
return (ERROR);
}
if (setsockopt (pSlot->dataSock, SOL_SOCKET,
SO_REUSEADDR, (char *) &on, sizeof (on)) < 0 ||
bind (pSlot->dataSock, (struct sockaddr *) &addr,
sizeof (addr)) < 0)
{
ftpdSockFree (&pSlot->dataSock);
return (ERROR);
}
/* Set socket address to PORT command values or default. */
bcopy ( (char *)&pSlot->dataAddr, (char *)&addr,
sizeof (struct sockaddr_in));
/* try until we get a connection to the client's port */
while (connect (pSlot->dataSock,
(struct sockaddr *) &addr, sizeof (addr)) < 0)
{
if ((errno & 0xffff) == EADDRINUSE && retry < FTPD_WAIT_MAX)
{
taskDelay (FTPD_WAIT_INTERVAL * sysClkRateGet ());
retry += FTPD_WAIT_INTERVAL;
continue;
}
/* timeout -- we give up */
ftpdCmdSend (pSlot, pSlot->cmdSock,
425, "Can't build data connection",
0, 0, 0, 0, 0, 0);
ftpdSockFree (&pSlot->dataSock);
return (ERROR);
}
if (setsockopt (pSlot->dataSock, SOL_SOCKET, SO_KEEPALIVE,
(char *) &on, sizeof (on)) != 0)
{
ftpdSockFree (&pSlot->dataSock);
return (ERROR);
}
/* Check for window size validity */
if (ftpdWindowSize < 0 || ftpdWindowSize > 65536)
ftpdWindowSize = FTPD_WINDOW_SIZE;
/* set the window size */
if (setsockopt(pSlot->dataSock, SOL_SOCKET, SO_SNDBUF,
(char *)&ftpdWindowSize, sizeof (ftpdWindowSize)))
printf("Couldn't set the Send Window to 10k\n");
if (setsockopt(pSlot->dataSock, SOL_SOCKET, SO_RCVBUF,
(char *)&ftpdWindowSize, sizeof (ftpdWindowSize)))
printf("Couldn't set the Send Window to 10k\n");
if (ftpdCmdSend (pSlot, pSlot->cmdSock,
150, "Opening %s mode data connection",
pSlot->status & FTPD_ASCII_TYPE ? (int) "ASCII" :
(int) "BINARY", 0, 0, 0, 0, 0) == ERROR)
{
ftpdSockFree (&pSlot->dataSock);
return (ERROR);
}
}
return (OK);
}
/*******************************************************************************
* SEE ALSO:
* ftpdDataStreamReceive which is symmetric to this function.
*/
LOCAL void ftpdDataStreamSend
(
FTPD_SESSION_DATA *pSlot, /* pointer to our session slot */
char *fileName /* pointer to the input file stream */
)
{
FAST char *pBuf;
FAST int netFd;
FAST int fileFd;
FAST int cnt;
int retval = 0;
/* get a fresh connection or reuse the old one */
if (ftpdDataConnGet (pSlot) == ERROR)
{
dataError (pSlot);
return;
}
if ((fileFd = open (fileName, O_RDONLY, 0)) == ERROR)
{
fileError (pSlot);
return;
}
pBuf = &pSlot->buf [0];
if (pSlot->status & FTPD_ASCII_TYPE) /* ASCII representation */
{
FAST FILE * inStream = NULL ; /* buffered input file stream */
FAST FILE * outStream = NULL ; /* buffered output socket stream */
FAST int ch; /* character holder */
if ((inStream = fdopen (fileFd, "r")) == NULL)
{
fileError (pSlot);
return;
}
if ((outStream = fdopen (pSlot->dataSock, "w")) == NULL)
{
goto data_err ;
}
while ((ch = getc (inStream)) != EOF)
{
pSlot->byteCount++;
/* if '\n' is encountered, we prepend a '\r' */
if (ch == '\n')
{
if (ferror (outStream))
goto data_err ;
if (putc ('\r', outStream) == EOF)
goto data_err ;
taskDelay(0);
}
if (putc (ch, outStream) == EOF)
goto data_err ;
/* Abort the file transfer if a shutdown is in progress. */
if (ch == '\n' && ftpsShutdownFlag)
goto data_err ;
}
/* flush it out */
(void) fflush (outStream);
if (ferror (inStream))
{
/* error in reading the file */
goto file_err ;
}
if (ferror (outStream))
{
/* error in sending the file */
goto data_err ;
}
fclose (outStream);
fclose (inStream);
/* everything is okay */
transferOkay (pSlot);
return ;
data_err:
dataError (pSlot);
goto _err;
file_err:
fileError (pSlot);
_err:
if( outStream != NULL )
fclose (outStream);
if( inStream != NULL )
fclose (inStream);
return;
}
else if (pSlot->status & FTPD_BINARY_TYPE) /* BINARY representation */
{
netFd = pSlot->dataSock;
/* unbuffered block I/O between file and network */
while ((cnt = read (fileFd, pBuf, pSlot->bufSize)) > 0 &&
(retval = write (netFd, pBuf, cnt)) == cnt)
{
pSlot->byteCount += cnt;
if (ftpsShutdownFlag)
{
/* Abort the file transfer if a shutdown is in progress. */
cnt = 1;
break;
}
taskDelay(0);
}
/* cnt should be zero if the transfer ended normally */
if (cnt != 0)
{
if (cnt < 0)
{
fileError (pSlot);
close( fileFd );
return;
}
ftpdDebugMsg ("data xfer failed: read %d bytes, wrote %d bytes\n",
cnt, retval,0,0);
dataError (pSlot);
close( fileFd );
return;
}
transferOkay (pSlot);
close( fileFd );
}
else
unImplementedType (pSlot); /* invalide representation type */
}
/*******************************************************************************
* SEE ALSO:
* ftpdDataStreamSend which is symmetric to this function.
*/
LOCAL STATUS ftpdDataStreamReceive
(
FTPD_SESSION_DATA *pSlot,
FILE *outStream
)
{
FAST char *pBuf;
FAST int ch;
FAST FILE *inStream;
FAST int fileFd;
FAST int netFd;
FAST BOOL dontPutc;
FAST int cnt;
/* get a fresh data connection or reuse the old one */
if (ftpdDataConnGet (pSlot) == ERROR)
goto data_err;
pBuf = &pSlot->buf [0];
if (pSlot->status & FTPD_ASCII_TYPE)
{
/* ASCII representation */
if ((inStream = fdopen (pSlot->dataSock, "r")) == NULL)
goto data_err;
while ((ch = getc (inStream)) != EOF)
{
dontPutc = FALSE;
pSlot->byteCount++;
while (ch == '\r')
{
if (ferror (outStream))
{
fclose (inStream);
goto data_err ;
}
if ((ch = getc (inStream)) != '\n')
{
(void) putc ('\r', outStream);
if (ch == '\0' || ch == EOF)
{
dontPutc = TRUE;
break;
}
}
taskDelay(0);
}
if (dontPutc == FALSE)
(void) putc (ch, outStream);
if (ch == '\n' && ftpsShutdownFlag)
{
fclose (inStream);
goto data_err ;
}
}
(void) fflush (outStream);
if (ferror (inStream))
{
fclose (inStream);
goto data_err ;
}
if (ferror (outStream))
{
fclose (inStream);
goto file_err ;
}
fclose (inStream);
transferOkay (pSlot);
return OK ;
}
else if (pSlot->status & FTPD_BINARY_TYPE)
{
fileFd = fileno (outStream);
netFd = pSlot->dataSock;
while ((cnt = fioRead (netFd, pBuf, pSlot->bufSize)) > 0)
{
if (write (fileFd, pBuf, cnt) != cnt)
goto file_err ;
pSlot->byteCount += cnt;
if (ftpsShutdownFlag)
{
cnt = -1;
break;
}
taskDelay(0);
}
if (cnt < 0)
goto data_err ;
transferOkay (pSlot);
return OK ;
}
else
{
unImplementedType (pSlot);
return ERROR;
}
file_err:
fileError (pSlot);
return ERROR;
data_err:
dataError (pSlot);
return ERROR;
}
/*******************************************************************************
*
*/
LOCAL void ftpdSockFree
(
int *pSock
)
{
if (*pSock != FTPD_SOCK_FREE)
{
(void) close (*pSock);
*pSock = FTPD_SOCK_FREE;
}
}
/*******************************************************************************
*
* NOTE: we're using EOS as integral part of the pattern and name.
*/
LOCAL BOOL ftpDirListPattern ( char * pat, char * name )
{
FAST char * pPat, *pNext ;
const char anyOne = '?' ;
const char anyMany = '*' ;
pPat = pat ;
for(pNext = name ; * pNext != EOS ; pNext ++ )
{
if( *pPat == anyOne )
{
pPat ++ ;
}
else if( *pPat == anyMany )
{
if ( pNext[0] == '.' ) /* '*' dont match . .. and .xxx */
return FALSE ;
if ( toupper(pPat[1]) == toupper(pNext[1]) )
pPat ++ ;
else if (toupper(pPat[1]) == toupper(pNext[0]) )
pPat += 2 ;
else
continue ;
}
else if( toupper(*pPat) != toupper(*pNext) )
{
return FALSE ;
}
else
{
pPat ++ ;
}
}
/* loop is done, let's see if there is anything left */
if( (*pPat == EOS) || (pPat[0] == anyMany && pPat[1] == EOS))
return TRUE ;
else
return FALSE ;
}
#ifdef __unitest__
int ftpPatternTest(void)
{
int i, err = 0 ;
const struct
{
char * pat;
char * name;
BOOL result;
} testData[] = {
{ "*", "abcd.e_f-j", 1},
{ "*", "1", 1},
{ "*", "", 1},
{ "*abcde.f", "abcde.f", 1},
{ "*abcde.f", "0abcde.f", 1},
{ "*ab.f", "0abcde.f", 0},
{ "*de.f", "0abcde.f", 1},
{ "*ab?.c", "zabcde.f", 0},
{ "*ab??.c", "zabcde.f", 0},
{ "*ab???.c", "zabcde.f", 0},
{ "*ab???.?", "zabcde.f", 1},
{ "*ab???.?", "aBcde.c", 1},
{ "*", "a", 1},
{ "*", "a.1", 1},
{ "*", "a.b.c.$%(", 1},
{ "*.*", "a.b", 1},
{ "*.*", "1.2.3.4", 1},
{ "*.*", ".a.b", 1},
{ "?*", ".x", 1},
{ "?*", "x.y.t.r.#@%", 1},
{ "?*", "x", 1},
{ "?*", ".", 1},
{ "?a*", "aa", 1},
{ "?a*", "xa", 1},
{ "?a*", ".a", 1},
{ "?a*", ".ab", 1},
{ "?a*", "xaq1.&%#@___", 1},
{ "?a*", "_a-b.cde", 1},
{ "?A?B?C", "AaBbCc", 1},
{ "?A?B?C", "aabbcc", 1},
{ "?A?B?C", "AABBCC", 1},
{ "?A?B?C", ".a.b.c", 1},
{ "?A?B?C", "xa-b_c", 1},
{ "?*.dat", "p.dat", 1},
{ "?*.dat", "README.Dat", 1},
{ "?*.dat", "Copy of some.dat", 1},
{ "README?*", "readMe.", 1},
{ "README?*", "Readme1", 1},
{ "README?*", "readmeFile", 1},
{ "README?*", "ReAdMe, please", 1},
{ "??? ???", "fll .,&", 1},
{ "??? ???", "asd ___", 1},
{ "??? ???", "123 321", 1},
{ "*.*", "a", 0},
{ "*.*", "1-2-3-4", 0},
{ "?*.*", ".a", 0},
{ "?a*", "a", 0},
{ "?a*", "ax", 0},
{ "?A?B?C", "aBbCc", 0},
{ "?A?B?C", "aabcc", 0},
{ "?A?B?C", "AABBC", 0},
{ "?A?B?C", "a.b.c", 0},
{ "?A?B?C", "aab.c", 0},
{ "?*.dat", "p.d", 0},
{ "?*.dat", ".dat", 0},
{ "?*.dat", "README_Dat", 0},
{ "?*.dat", "Copy of some dat", 0},
{ "README?*", "readMe", 0},
{ "README?*", "README", 0},
{ "README?*", "1README_DAT", 0},
{ "README?*", "_ReAdMe, please", 0},
{ "??? ???", "fll_.,&", 0},
{ "??? ???", "asd.___", 0},
{ "??? ???", "23 321", 0},
{ "??? ???", "123 21", 0},
{ "", "a", 0}
};
for(i=0; i st_mtime, &fileDate);
if( fileStat->st_attrib & DOS_ATTR_RDONLY )
fileStat->st_mode &= ~(S_IWUSR|S_IWGRP|S_IWOTH);
if( S_ISDIR (fileStat->st_mode) )
fType = 'd' ;
else if( S_ISREG (fileStat->st_mode) )
fType = '-' ;
else
fType = '?' ;
/* print file mode */
fdprintf(fd, "%c%c%c%c%c%c%c%c%c%c",
fType,
(fileStat->st_mode & S_IRUSR)? 'r':'-',
(fileStat->st_mode & S_IWUSR)? 'w':'-',
(fileStat->st_mode & S_IXUSR)? 'x':'-',
'-', '-', '-', /* XXX- figure out what to do with last flags */
(fileStat->st_attrib & DOS_ATTR_ARCHIVE)? 'A':'-',
(fileStat->st_attrib & DOS_ATTR_SYSTEM)? 'S':'-',
(fileStat->st_attrib & DOS_ATTR_HIDDEN)? 'H':'-'
);
/* fake links, user and group fields */
fdprintf(fd, " %2d %-7s %-7s ", fileStat->st_nlink, "user", "group" );
/* print file size - XXX: add 64 bit file size support */
fdprintf(fd, " %9lu ", fileStat->st_size );
/* print date */
if( fileDate.tm_year == nowDate.tm_year )
{
fdprintf(fd, "%s %2d %02d:%02d ",
monthNames [fileDate.tm_mon + 1],/* month */
fileDate.tm_mday, /* day of month */
fileDate.tm_hour, /* hour */
fileDate.tm_min /* minute */
);
}
else
{
fdprintf(fd, "%s %2d %04d ",
monthNames [fileDate.tm_mon + 1],/* month */
fileDate.tm_mday, /* day of month */
fileDate.tm_year+1900 /* year */
);
}
} /* if doLong */
else
{ /* short listing */
if( prefix != NULL)
fdprintf(fd, prefix);
}
/* last, print file name */
if( fdprintf(fd, "%s\n", fileName ) == ERROR )
return ERROR;
return OK ;
}
/*******************************************************************************
* INTERNAL
* Borrowed from ls() in usrLib.c.
*
* RETURNS: OK or ERROR.
*
* SEE ALSO: ls(), stat()
*/
LOCAL STATUS ftpdDirListGet
(
int sd,
char *dirName,
BOOL doLong
)
{
FAST STATUS status;
FAST DIR *pDir;
FAST struct dirent *pDirEnt;
struct stat fileStat;
BOOL firstFile;
char *pPattern;
char fileName [MAX_FILENAME_LENGTH];
if (dirName == NULL)
dirName = ".";
pDir = NULL ; pPattern = NULL ;
/* Open dir */
pDir = opendir (dirName) ;
if( pDir == NULL && (pPattern = rindex(dirName, '/')) != NULL )
{
*pPattern++ = EOS ;
pDir = opendir (dirName) ;
}
if( pDir == NULL )
goto _nodir ;
/* List files */
ftpdDebugMsg ("listing dir %s pattern %s\n", (int)dirName,(int) pPattern,
0,0);
status = OK;
firstFile = TRUE;
do
{
errno = OK;
pDirEnt = readdir (pDir);
if (pDirEnt != NULL)
{
if (pPattern != NULL &&
ftpDirListPattern( pPattern, pDirEnt->d_name) == FALSE )
continue ;
if (doLong) /* if doing long listing */
{
strncpy( fileName, dirName, MAX_FILENAME_LENGTH );
if( fileName [ strlen(fileName) -1 ] != '/');
strcat(fileName, "/");
strncat( fileName, pDirEnt->d_name,
MAX_FILENAME_LENGTH - strlen(dirName) );
if (stat (fileName, &fileStat) != OK)
{
bzero( (caddr_t) &fileStat, sizeof(fileStat));
}
}
if( ftpdDirListEnt(sd, pDirEnt->d_name,
&fileStat, NULL, doLong ) == ERROR )
return ( ERROR | closedir (pDir) );
}
else
{
if (errno != OK)
{
if (fdprintf (sd, "error reading entry: %x\n", errno) == ERROR)
return ( ERROR | closedir (pDir));
status = ERROR;
}
}
} while (pDirEnt != NULL); /* until end of dir */
/* fdprintf (sd, "\n"); XXX*/
/* Close dir */
status |= closedir (pDir);
return (status);
_nodir:
fdprintf (sd, "Can't open \"%s\".\n", dirName);
return (ERROR);
}
/*******************************************************************************
*
* unImplementedType - send FTP invalid representation type error reply
*/
LOCAL void unImplementedType
(
FTPD_SESSION_DATA *pSlot
)
{
ftpdCmdSend (pSlot, pSlot->cmdSock, 550, messages [MSG_TYPE_ERROR],
FTPD_REPRESENTATION (pSlot), 0, 0, 0, 0, 0);
ftpdSockFree (&pSlot->dataSock);
}
/*******************************************************************************
*
* dataError - send FTP data connection error reply
*
* Send the final error message about connection error and delete the session.
*/
LOCAL void dataError
(
FTPD_SESSION_DATA *pSlot
)
{
ftpdCmdSend (pSlot, pSlot->cmdSock, 426, messages [MSG_DATA_CONN_ERROR],
0, 0, 0, 0, 0, 0);
ftpdSockFree (&pSlot->dataSock);
}
/*******************************************************************************
*
* fileError - send FTP file I/O error reply
*
* Send the final error message about file error and delete the session.
*/
LOCAL void fileError
(
FTPD_SESSION_DATA *pSlot
)
{
ftpdCmdSend (pSlot, pSlot->cmdSock, 551, messages [MSG_INPUT_FILE_ERROR],
0, 0, 0, 0, 0, 0);
ftpdSockFree (&pSlot->dataSock);
}
/*******************************************************************************
*
* transferOkay - send FTP file transfer completion reply
*/
LOCAL void transferOkay
(
FTPD_SESSION_DATA *pSlot
)
{
ftpdSockFree (&pSlot->dataSock);
ftpdCmdSend (pSlot, pSlot->cmdSock, 226, messages [MSG_TRANS_COMPLETE],
0, 0, 0, 0, 0, 0);
}
/*******************************************************************************
*
* ftpdCmdSend - send a FTP command reply
*
* In response to a request, we send a reply containing completion
* status, error status, and other information over a command connection.
*/
LOCAL STATUS ftpdCmdSend
(
FTPD_SESSION_DATA *pSlot, /* pointer to the session slot */
int controlSock, /* control socket for reply */
int code, /* FTP status code */
const char *format, /* printf style format string */
int arg1,
int arg2,
int arg3,
int arg4,
int arg5,
int arg6
)
{
int buflen;
char buf [BUFSIZE]; /* local buffer */
FAST char *pBuf = &buf [0]; /* pointer to buffer */
BOOL lineContinue =
(code & FTPD_MULTI_LINE) == FTPD_MULTI_LINE;
/*
* If this routine is called before a session is established, the
* pointer to session-specific data is NULL. Otherwise, exit with
* an error if an earlier attempt to send a control message failed.
*/
if ( (pSlot != NULL) && (pSlot->cmdSockError == ERROR))
return (ERROR);
code &= ~FTPD_MULTI_LINE; /* strip multi-line bit from reply code */
/* embed the code first */
(void) sprintf (pBuf, "%d%c", code, lineContinue ? '-' : ' ');
pBuf += strlen (pBuf);
(void) sprintf (pBuf, format, arg1, arg2, arg3, arg4, arg5, arg6);
pBuf += strlen (pBuf);
/* telnet style command terminator */
(void) sprintf (pBuf, "\r\n");
/* send it over to our client */
buflen = strlen (buf);
if ( write (controlSock, buf, buflen) != buflen )
{
if (pSlot != NULL)
pSlot->cmdSockError = ERROR;
ftpdDebugMsg ("sent %s Failed on write\n", (int)buf,0,0,0);
return (ERROR); /* Write Error */
}
ftpdDebugMsg ("sent %s\n", (int)buf,0,0,0);
return (OK); /* Command Sent Successfully */
}
/*******************************************************************************
*
* ftpdDebugMsg - print out FTP command request and replies for debugging.
*/
LOCAL void ftpdDebugMsg
(
char *format,
int arg1,
int arg2,
int arg3,
int arg4
)
{
if (ftpdDebug == TRUE)
logMsg (format, arg1, arg2, arg3, arg4, 0, 0);
}
STATUS ftpLoginUserVerify(char *name, char *passwd )
{
if ( (strcmp(name, "hualong") == 0) && (strcmp(passwd, "hualong") == 0) )
{
return (OK);
}
else
{
return (ERROR);
}
}
/*******************************************************************************
* RETURNS: N/A
*
* SEE ALSO: loginLib
*/
void ftpdLoginInstall
(
FUNCPTR pLoginFunc
)
{
pLoginVrfyFunc = pLoginFunc;
}
/*******************************************************************************
* RETURNS: OK, or ERROR
*/
STATUS ftpdAnonymousAllow
(
const char * rootDir, /* path for guest root directory */
const char * uploadDir /* path for guest uploads */
)
{
if( rootDir == NULL )
return ERROR;
strncpy( guestHomeDir, rootDir, MAX_FILENAME_LENGTH );
if( uploadDir != NULL )
{
strcpy( writeDirName, guestHomeDir );
strcat( writeDirName, "/" );
strncat( writeDirName, uploadDir, MAX_FILENAME_LENGTH );
pathCondense( writeDirName );
}
else
{
writeDirName[0] = EOS ; /* clear */
}
return OK ;
}
/*******************************************************************************
*
* RETURNS: OK
*/
STATUS ftpdHomeDirSet( const char * homeDir )
{
if( homeDir == NULL )
return ERROR;
strncpy( defaultHomeDir, homeDir, MAX_FILENAME_LENGTH );
return( OK);
}