/*
* CFtpServer :: a FTP Server Class Library
* Copyright (C) 2005, Poumailloux Julien aka theBrowser
*
* Mail :: thebrowser@gmail.com
Copyright (c) 2005 POUMAILLOUX Julien
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall
be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-Compiling using GCC:
To succesfully link program which uses CFtpServer, you need to use following command:
g++ -o ftp main.o CFtpServer.o -lnsl -lpthread
( Add '-lsocket' too under SunOS and maybe some other OS. )
Add '-D_FILE_OFFSET_BITS=64' for large file support. ( Under Win, See CFtpServer.h )
*/
#include "stdafx.h"
#include "CFtpServer.h"
#ifndef WIN32
#define SOCKET_ERROR -1
#define INVALID_SOCKET -1
#define MAX_PATH PATH_MAX
#ifndef O_BINARY
#define O_BINARY 0
#endif
#else
#define open _open
#define close _close
#define read _read
#define write _write
#define O_WRONLY _O_WRONLY
#define O_CREAT _O_CREAT
#define O_APPEND _O_APPEND
#define O_BINARY _O_BINARY
#define O_TRUNC _O_TRUNC
#ifndef __USE_FILE_OFFSET64
#define stat _stat
#define lseek _lseek
#else
#define stat _stati64
#define lseek _lseeki64
#define atoll _atoi64
#define _findfirst _findfirsti64
#define _findnext _findnexti64
#endif
typedef int socklen_t;
#define strcasecmp _stricmp
#define strncasecmp _strnicmp
#define vsnprintf _vsnprintf
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
/****************************************
* CONSTRUCTOR && DESTRUCTOR
***************************************/
CFtpServer::CFtpServer(void)
{
int i=0;
this->szMonth[ i++ ] = "Jan"; this->szMonth[ i++ ] = "Feb";
this->szMonth[ i++ ] = "Mar"; this->szMonth[ i++ ] = "Apr";
this->szMonth[ i++ ] = "May"; this->szMonth[ i++ ] = "Jun";
this->szMonth[ i++ ] = "Jul"; this->szMonth[ i++ ] = "Aug";
this->szMonth[ i++ ] = "Sep"; this->szMonth[ i++ ] = "Oct";
this->szMonth[ i++ ] = "Nov"; this->szMonth[ i++ ] = "Dec";
this->usPort = 21;
this->iNbUser = 0;
this->iNbClient = 0;
this->iReplyMaxLen = 4096;
this->DataPortRange.usStart = 100;
this->DataPortRange.iNumber = 900;
this->iCheckPassDelay = 0; // <- Anti BruteForcing Protection
this->bIsAccepting = false;
this->bIsListening = false;
this->AnonymousUser = NULL;
this->bAllowAnonymous = false;
this->UserListHead = this->UserListLast = NULL;
this->ClientListHead = this->ClientListLast = NULL;
srand((unsigned) time(NULL));
#ifdef WIN32
InitializeCriticalSection( &this->m_csUserRes );
InitializeCriticalSection( &this->m_csClientRes );
#else
pthread_mutex_init( &this->m_csUserRes, NULL );
pthread_mutex_init( &this->m_csClientRes, NULL );
pthread_attr_init( &m_pattrServer );
pthread_attr_setstacksize( &m_pattrServer, 5*1024 );
pthread_attr_init( &m_pattrClient );
pthread_attr_setstacksize( &m_pattrClient, 10*1024 );
pthread_attr_init( &m_pattrTransfer );
pthread_attr_setstacksize( &m_pattrTransfer, 5*1024 );
#endif
}
CFtpServer::~CFtpServer(void)
{
if( this->IsListening() )
this->StopListening();
this->DelAllClient();
this->DelAllUser();
#ifdef WIN32
DeleteCriticalSection( &this->m_csUserRes );
DeleteCriticalSection( &this->m_csClientRes );
#else
pthread_mutex_destroy( &this->m_csUserRes );
pthread_mutex_destroy( &this->m_csClientRes );
#endif
}
/****************************************
* CONFIG
***************************************/
bool CFtpServer::AllowAnonymous( bool bDoAllow, const char *szStartPath )
{
if( bDoAllow ) {
if( this->IsAnonymousAllowed() == false ) {
if( (this->AnonymousUser = this->AddUserEx( "anonymous", NULL, szStartPath )) == NULL )
return false;
this->SetUserPriv( this->AnonymousUser, CFtpServer::READFILE | CFtpServer::LIST );
}
this->bAllowAnonymous = true;
return true;
} else {
if( this->IsAnonymousAllowed() == true ) {
if( this->DelUser( this->AnonymousUser ) == false )
return false;
this->AnonymousUser = NULL;
this->bAllowAnonymous = false;
}
return true;
}
return false;
}
bool CFtpServer::SetDataPortRange( unsigned short int usDataPortStart, unsigned int iNumber )
{
if( iNumber >= 1 && usDataPortStart > 0 && (usDataPortStart + iNumber) <= 65535 ) {
this->DataPortRange.usStart = usDataPortStart;
this->DataPortRange.iNumber = iNumber;
return true;
}
return false;
}
/****************************************
* NETWORK
***************************************/
bool CFtpServer::StartListening( unsigned long ulAddr, unsigned short int usPort )
{
if( ulAddr == INADDR_NONE || usPort == 0 )
return false;
if( this->IsListening() ) {
this->CloseSocket( this->ListeningSock );
this->ListeningSock = INVALID_SOCKET;
this->bIsListening = false;
}
int on = 1;
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = ulAddr;
sin.sin_port = htons( usPort );
#ifdef USE_BSDSOCKETS
sin.sin_len = sizeof( sin );
#endif
this->ListeningSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if( this->ListeningSock == INVALID_SOCKET
#ifndef _WIN32 // On win32, SO_REUSEADDR allow 2 sockets to listen on the same TCP-Port.
|| setsockopt( this->ListeningSock, SOL_SOCKET, SO_REUSEADDR,(char *) &on, sizeof( on ) ) == SOCKET_ERROR
#endif
|| bind( this->ListeningSock, (struct sockaddr *) &sin, sizeof( struct sockaddr_in ) ) == SOCKET_ERROR
|| listen( this->ListeningSock, 0) == SOCKET_ERROR )
{
this->CloseSocket( this->ListeningSock );
this->bIsListening = false;
} else this->bIsListening = true;
this->usPort = usPort;
return this->bIsListening;
}
bool CFtpServer::StopListening( void )
{
if( this->IsListening() ) {
this->CloseSocket( this->ListeningSock );
this->ListeningSock = INVALID_SOCKET;
}
this->bIsAccepting = false;
this->bIsListening = false;
return true;
}
bool CFtpServer::StartAccepting( void )
{
if( this->bIsAccepting == false && this->bIsListening == true ) {
#ifdef WIN32
this->hAcceptingThread = (HANDLE) _beginthreadex( NULL, 0,
this->StartAcceptingEx, this, 0, &this->uAcceptingThreadID );
if( this->hAcceptingThread != 0 )
this->bIsAccepting = true;
#else
if( pthread_create( &this->pAcceptingThreadID, &this->m_pattrServer,
this->StartAcceptingEx, this ) == 0 )
{
this->bIsAccepting = true;
}
#endif
}
return this->bIsAccepting;
}
#ifdef WIN32
unsigned CFtpServer::StartAcceptingEx( void *pvParam )
#else
void* CFtpServer::StartAcceptingEx( void *pvParam )
#endif
{
class CFtpServer *CFtpServerEx = ( class CFtpServer* ) pvParam;
SOCKET TempSock; struct sockaddr_in Sin;
socklen_t Sin_len = sizeof(struct sockaddr_in);
while ( 1 ) {
TempSock = accept( CFtpServerEx->ListeningSock, (struct sockaddr *) &Sin, &Sin_len);
if( TempSock != INVALID_SOCKET ) {
struct CFtpServer::ClientNode *NewClient =
CFtpServerEx->AddClient( CFtpServerEx, TempSock