|
|||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionFTP servers are great; they allow you to store data you want to save, remotely. However, most of the time, you have to install a lot of things on the remote computer: the FTP server itself, including, most of the time, dozens of files, their dependencies, etc. Here is where the
Using the codeFirst of all, on Win32, we need to initialize Winsock: #ifdef WIN32
WSADATA WSAData;
WORD wVersionRequested = MAKEWORD( 1, 1 );
if( WSAStartup(wVersionRequested, &WSAData) != 0 ) {
return 0;
}
#endif
The class name is CFtpServer FtpServer; Then, we configure it: FtpServer.SetDataPortRange( 100,900 ); FtpServer.AllowAnonymous( true, "C:/anonymous" ); We tell the class which TCP-port can be used for transfers (here, [100-999]), and we tell the class to allow anonymous users. Then, we create a user: CFtpServer::UserNode *FtpUser =
FtpServer.AddUser( "test", "pass", "C:\\");
Here, the user's login is test, his password is pass, and his start directory is: C:/. Besides this, we can check if the user was successfully created, by checking the pointer returned by After that, we set the maximum number of clients who can log in as this user, and we give him some privileges: FtpServer.SetUserMaxClient( FtpUser, CFtpServer::Unlimited );
FtpServer.SetUsetPriv( FtpUser, CFtpServer::READFILE |
CFtpServer::WRITEFILE | CFtpServer::LIST |
CFtpServer::DELETEFILE | CFtpServer::CREATEDIR |
CFtpServer::DELETEDIR );
At last, we ask the class to start listening - here, it will listen on the TCP-port 21 and on all network interfaces - and to accept incoming clients: // If you only want to listen on the TCP Loopback interface, // replace 'INNADDR_ANY' by 'inet_addr("127.0.0.1")'. FtpServer.StartListening( INADDR_ANY, 21 ); FtpServer.StartAccepting(); If your program has nothing else to do, we ask the main thread to wait, forever: while( 1 ) #ifdef WIN32 Sleep( 1000 ); #else usleep( 1000 ); #endif Here we go, the FTP server is running! Now, the public part of the public: /* Constructor */ CFtpServer(void); /* Destructor */ ~CFtpServer(void); /**************************************** * USER ****************************************/ /* The Structure which will be allocated for each User. */ struct UserNode { int iMaxClient; unsigned char ucPriv; #ifdef CFTPSERVER_USE_EXTRACMD unsigned char ucExtraCmd; #endif char *szName; char *szPasswd; bool bIsEnabled; char *szStartDir; int iNbClient; struct UserNode *PrevUser; struct UserNode *NextUser; }; /* Enumerate the different Privilegies a User can get. */ enum { READFILE = 0x1, WRITEFILE = 0x2, DELETEFILE = 0x4, LIST = 0x8, CREATEDIR = 0x10, DELETEDIR = 0x20, }; /* Create a new User. Don't create a user named "anonymous", call AllowAnonymous();. Arguments: -the User Name. -the User Password. -the User Start directory. Returns: -on success: the adress of the New-User's CFtpServer::UserNode structure. -on error: NULL. */ struct CFtpServer::UserNode *AddUser( const char *szName, const char *szPasswd, const char* szStartDir ); /* Set the Privilegies of a User. Arguments: -a pointer to the CFtpServer::UserNode structure of the User. -the user's privilegies of the CFtpServer Enum of privilegies, separated by the | operator. Returns: -on success: true. -on error: false. */ bool SetUserPriv( struct CFtpServer::UserNode *User, unsigned char ucPriv ); /* Delete a User, and by the way all the Clients connected to this User. Arguments: -a pointer to the CFtpServer::UserNode structure of the User. Returns: -on success: true. -on error: false. */ bool DelUser( struct CFtpServer::UserNode * User ); /* Get the Head of the User List. Arguments: Returns: -the Head of the User List. */ struct CFtpServer::UserNode *GetUserListHead( void ) { return this->UserListHead; } /* Get the Last User of the List. Arguments: Returns: -the Last User of the List. */ struct CFtpServer::UserNode *GetUserListLast( void ) { return this->UserListLast; } /* Get the Next User of the List. Arguments: -a pointer to a CFtpServer::UserNode structure. Returns: -the Next User of the list. */ struct CFtpServer::UserNode *GetNextUser( const struct CFtpServer::UserNode *User ) { if( User ) return User->NextUser; return NULL; } /* Get the Previous User of the List. Arguments: -a pointer to a CFtpServer::UserNode structure. Returns: -the Previous User of the List. */ struct CFtpServer::UserNode *GetPreviousUser( const struct CFtpServer::UserNode *User ) { if( User ) return User->PrevUser; return NULL; } enum { Error = -2, None = -1, Unlimited = 0 }; /* Set the maximum number of Clients which can be connected to the User at a time. Arguments: -a pointer to the CFtpServer::UserNode structure of the User. -the number of clients who will be able to be connected to the user at a time. CFtpServer::UserMaxClient.Unlimited: Unlimited. CFtpServer::UserMaxClient.None: None. Returns: -on success: true. -on error: false. */ bool SetUserMaxClient( struct CFtpServer::UserNode *User, int iMaxClient ); /* Get the maximum number of Clients which can be connected to the User at a time. Arguments: -a pointer to the CFtpServer::UserNode structure of the User. Returns: -on success: the number of clients which can be connected to the User at a time. -on error: -2. */ unsigned int GetUserMaxClient( const struct CFtpServer::UserNode *User ) { if( User ) return User->iMaxClient; return 0; } /* Delete all the Users, and by the way all the Server's Clients connected to a User. */ void DelAllUser( void ); /* Get a User's privilegies Arguments: -a pointer to the CFtpServer::UserNode structure of the User. Returns: -on success: the user's privilegies concatenated with the bitwise inclusive binary operator "|". -on error: 0. */ unsigned char GetUserPriv( const struct CFtpServer::UserNode *User ) { if( User ) { return User->ucPriv; } else return 0; } /* Get a pointer to a User's Name. Arguments: -a pointer to the CFtpServer::UserNode structure of the User. Returns: -on success: a pointer to the User's Name. -on error: NULL. */ const char* GetUserLogin( const struct CFtpServer::UserNode *User ) { if( User ) { return User->szName; } else return NULL; } /* Get a pointer to a User's Password. Arguments: -a pointer to the CFtpServer::UserNode structure of the User. Returns: -on success: a pointer to the User's Password. -on error: NULL. */ const char* GetUserPasswd( const struct CFtpServer::UserNode *User ) { if( User ) { return User->szPasswd; } else return NULL; } /* Get a pointer to a User's Start Directory. Arguments: -a pointer to the CFtpServer::UserNode structure of the User. Returns: -on success: a pointer to the User's Start Directory. -on error: NULL. */ const char* GetUserStartDir( const struct CFtpServer::UserNode *User ) { if( User ) { return User->szStartDir; } else return NULL; } #ifdef CFTPSERVER_USE_EXTRACMD /* Enum the Extra Commands a User can got. */ enum eFtpExtraCmd { ExtraCmd_EXEC = 0x1, }; /* Set the supported Extra-Commands of a User. Arguments: -a pointer to the CFtpServer::UserNode structure of the User. -the user's Extra-Commands concatenated with the bitwise inclusive binary operator "|". Returns: -on success: true. -on error: false. */ bool SetUserExtraCmd( struct CFtpServer::UserNode *User, unsigned char dExtraCmd ); /* Get the supported Extra-Commands of a User. Arguments: -a pointer to the CFtpServer::UserNode structure of the User. Returns: -on succes: the user's Extra-Commands concatenated with the bitwise inclusive binary operator "|". -on error: 0. */ unsigned char GetUserExtraCmd( const struct CFtpServer::UserNode *User ) { if( User ) { return User->ucExtraCmd; } else return 0; } #endif /*************************************** * START / STOP ***************************************/ /* Ask the Server to Start Listening on the TCP-Port supplied by SetPort(). Arguments: -the Network Adress CFtpServer will listen on. Example: INADDR_ANY for all local interfaces. inet_addr( "127.0.0.1" ) for the TCP Loopback interface. -the TCP-Port on which CFtpServer will listen. Returns: -on success: true. -on error: false, the supplied Adress or TCP-Port may not be valid. */ bool StartListening( unsigned long ulAddr, unsigned short int usPort ); /* Ask the Server to Stop Listening. Returns: -on success: true. -on error: false. */ bool StopListening( void ); /* Check if the Server is currently Listening. Returns: -true: if the Server is currently listening. -false: if the Server isn't currently listening. */ bool IsListening( void ) { return this->bIsListening; } /* Ask the Server to Start Accepting Clients. Returns: -on success: true. -on error: false. */ bool StartAccepting( void ); /* Check if the Server is currently Accpeting Clients. Returns: -true: if the Server is currently accepting clients. -false: if the Server isn't currently accepting clients. */ bool IsAccepting( void ) { return this->bIsAccepting; } /**************************************** * CONFIG ****************************************/ /* Get the TCP Port on which CFtpServer will listen for incoming clients. Arguments: Returns: -on success: the TCP-Port. -on error: 0. */ unsigned short GetListeningPort( void ) { return this->usPort; } /* Set the TCP Port Range CFtpServer can use to Send and Receive Files or Data. Arguments: -the First Port of the Range. -the Number of Ports, including the First previously given. Returns: -on success: true. -on error: false. */ bool SetDataPortRange( unsigned short int usDataPortStart, unsigned int iNumber ); /* Get the TCP Port Range CFtpServer can use to Send and Receive Files or Data. Arguments: -a Pointer to the First Port. -a Pointer to the Number of Ports, including the First. Returns: -on success: true. -on error: false. */ bool GetDataPortRange( unsigned short int *usDataPortStart, int *iNumber ) { if( usDataPortStart && iNumber ) { *usDataPortStart = this->DataPortRange.usStart; *iNumber = this->DataPortRange.iNumber; return true; } return false; } /* Allow or disallow Anonymous users. Its privilegies will be set to CFtpServer::READFILE | CFtpServer::LIST. Arguments: -true if you want CFtpServer to accept anonymous clients, otherwise false. -the Anonymous User Start Directory. Returns: -on success: true. -on error: false. */ bool AllowAnonymous( bool bDoAllow, const char *szStartPath ); /* Check if Anonymous Users are allowed. Returns: -true: if Anonymous Users are allowed. -false: if Anonymous Users aren't allowed. */ bool IsAnonymousAllowed( void ) { return this->bAllowAnonymous; } #ifdef CFTPSERVER_ANTIBRUTEFORCING /* Set the delay the Server will wait when checking for the Client's pass. */ void SetCheckPassDelay( int iCheckPassDelay ) { this->iCheckPassDelay = iCheckPassDelay; } /* Get the delay the Server will wait when checking for the Client's pass. Returns: -the delay the Server will wait when checking for the Client's pass. */ int GetSetCheckPassDelay( void ) { return this->iCheckPassDelay; } #endif /**************************************** * STATISTICS ****************************************/ /* Get in real-time the number of Clients connected to the Server. Returns: -the current number of Clients connected to the Server. */ int GetNbClient( void ) { return this->iNbClient; } /* Get in real-time the number of existing Users. Returns: -the current number of existing Users. */ int GetNbUser( void ) { return this->iNbUser; } /* Get the number of Clients currently connected using the specified User. Arguments: -a pointer to the CFtpServer::UserNode structure of the User. Returns: -on success: the count of Clients using currently the User. -on error: -1. */ int GetNbClientUsingUser( struct CFtpServer::UserNode *User ) { if( User ) { return User->iNbClient; } else return -1; } Enjoy! Quick test!If you want to check if the server is running, or if a user exists, start the example application and type the following in a command prompt: telnet localhost 21
USER test
PASS pass
STAT /
QUIT
Of course, assuming that you made the application listen on port 21 (default), and that the user's name you are trying to use is 'test' and its password is 'pass'. If it doesn't write "530 Please login with valid USER and PASS" and you see the user's main directory listing, then it works :). For example: > telnet localhost 21
< 220 Browser Ftp Server.
> USER test
< 331 Password required for this user.
> PASS pass
< 230 User Logged In.
> STAT /
< 213-Status follows:
< -rwx------ 1 user group 0 Feb 4 16:26 AUTOEXEC.BAT
< drwx------ 1 user group 0 Feb 5 15:16 BJPrinter
< -rwx------ 1 user group 212 Feb 4 16:20 boot.ini
< -rwx------ 1 user group 4952 Aug 30 2002 Bootfont.bin
< -rwx------ 1 user group 0 Feb 4 16:26 CONFIG.SYS
< drwx------ 1 user group 0 Feb 4 16:31 Documents and
Settings
< -rwx------ 1 user group 1073254400 Feb 5 13:51 hiberfil.sys
< -rwx------ 1 user group 0 Feb 4 16:26 IO.SYS
< -rwx------ 1 user group 0 Feb 4 16:26 MSDOS.SYS
< -rwx------ 1 user group 47564 Aug 3 2004 NTDETECT.COM
< -rwx------ 1 user group 251712 Aug 3 2004 ntldr
< -rwx------ 1 user group 1610612736 Feb 5 13:51 pagefile.sys
< drwx------ 1 user group 0 Feb 5 15:12 Program Files
< drwx------ 1 user group 0 Feb 5 14:09 RECYCLER
< drwx------ 1 user group 0 Feb 4 16:30 System Volume
Information
< drwx------ 1 user group 0 Feb 5 14:04 torrent
< drwx------ 1 user group 0 Feb 4 22:10 UT2004Demo
< drwx------ 1 user group 0 Feb 5 16:19 WINDOWS
< 213 End of status
> QUIT
< 221 Goodbye.
Here, I have added '< ' or '> ' at the beginning of each line in order to make it more understandable: '> ' means 'you send', and '< ' means 'you receive'. Points of interestWriting the History
| ||||||||||||||||||||||||||||