simple implementation of FTP on "C"

Discussion in 'C' started by tonio, Nov 2, 2008.

  1. tonio

    tonio New Member

    Joined:
    Nov 2, 2008
    Messages:
    2
    Likes Received:
    0
    Trophy Points:
    0
    Hi there,

    I'm trying to make a simple FTP client/server in C under Linux. I wrote almost everything but I have a strange bug somewhere :(
    I'll explain in shortly what is my implementation:
    So I have a simple client, it can list directories, change directories, get/put files etc ... the basic commands.
    First of all it connects to the server, logs into his "home" dir and after that it waits for user's commands.
    That's all for the client, you can see the details in the source that I'll provide here.
    At the other side is the server. He listens always on a specified port if there is some client trying to connect. When a client is present the connection is established and the server is ready for the user's commands.
    I decided to make server in a such way that when there is a client that wants to make a simple command like "cd" or "pwd" this is the server process who is executing it. But if there is a command like a "put" or "get" the server creates a process (fork()) and passes the execution of this command to the child.
    When the user wants to make "get", "put" or "dir" I decided also to make a second TCP connection between the server and the client (server -> client), dedicated only for the data transfer, in this way the first connection is available for other commands.
    I inspired myself for this decision looking at the FTP rfc.
    You can also look at the code and see how the things are.
    Now, to my problem :)
    So when I'm making simple commands that doesn't require a process to be forked everything goes OK. But when I'm making some "dir" or "put" for example it doesn't work every time.
    I saw that there is some problem when the server tries to connect to the client when I'm using this kind of commands. (exec_comm.c file contains that). The client refuses to accept the connection from the server :(. And I can't understand why ... I'm sure that there is a bug in my code, but I'm not so good to find it by my self :(
    So if you have some ideas or remarks feel free to post it here :)
    Thank you very much in advance.

    Best regards,

    Tonio

    The client code
    mftp.h file
    Code:
    #ifndef MFTP_H
    #define MFTP_H
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <netinet/in.h>
    #include <netdb.h>
    #include <string.h>
    
    
    #define N 1024
    #define NB_CMDS	11
    
    #define OPEN 	0
    #define CD 	1
    #define MKD 	2
    #define RMD 	3
    #define QUIT 	4
    #define DIR 	5
    #define GET 	6
    #define PUT 	7
    #define PWD 	8
    #define DEL	9
    #define HELP	10
    
    const char *PROMPT="mftp> ";
    
    void prompt(void);
    
    void myconnect(char *,char *,char *);
    void pwd(void);
    void cd(char *);
    void quit(void);
    void mkd(char *);
    void rmd(char *);
    void dir(void);
    void get(char *);
    void put(char *);
    void del(char *);
    void aide(void);
    
    
    
    #endif
    
    
    mftp.c
    Code:
    #include "mftp.h"
    
    static char *commandes[NB_CMDS] = {
    
    
    	"open", "cd", "mkd", "rmd", "quit", "dir", "get", "put", "pwd", "del", "help"
    };
    
    int sd = 0;
    int connecte = 0;
    
    int main(int argc, char *argv[]){
    
    	char buff[N], *param; //1024
    	int j = 0, nb;
    
    	if( argc != 4){
    
    		printf("Usage: %s <server host> <user name> <password>\n", argv[0]);
    		exit(1);
    	}
    
    	printf("Pour voir le menu avec les commandes reconnues tappez 'help' :\n");
    	do{
    
    		memset(buff, '\0', N);
    		
    		prompt();	
    		gets(buff);
    	
    		param = strchr(buff,' '); // param <- commande
    		if (param) {
        			*param=0;
        			param++;
      		}
    
    		for(j = 0; j < NB_CMDS; j++)
    			if( !strcmp(buff, commandes[j]))
    				break;	
    			
    		switch(j){
    
     			case(OPEN):	if(connecte) printf("Deja connecte\n");
    					else myconnect(argv[1], argv[2], argv[3]);
    					break;
    		
    			case(PWD):	if(!connecte) printf("Non connecte!\n"); 
    					else pwd();
    					break;
    
    			case(CD):	if(!connecte) printf("Non connecte!\n"); 
    					else cd(param);
    					break;
    
     			case(MKD):	if(!connecte) printf("Non connecte!\n"); 
    					else mkd(param);
    					break;
    
     			case(RMD):	if(!connecte) printf("Non connecte!\n"); 
    					else rmd(param);
    					break;
    
     			case(QUIT):	quit();
    					return(0);
    					break;
    
     			case(DIR):	if(!connecte) printf("Non connecte!\n"); 
    					else dir();
    					break;
    
     			case(GET):	if(!connecte) printf("Non connecte!\n"); 
    					else get(param);
    					break;
    
     			case(PUT):	if(!connecte) printf("Non connecte!\n"); 
    					else put(param);
    					break;
    			
    			case(DEL):	if(!connecte) printf("Non connecte!\n"); 
    					else del(param);
    					break;
    
    			case (HELP):	aide();
    					break;
    
    			default:	printf("%s commande inconnue\n", buff);	
    					break;
    		}
    		
    	}
    	while(1);
     	
    return(0);
    }
    
    
    void myconnect(char *host_serv, char *user_name, char *password){
    
    	struct sockaddr_in to;
    	struct hostent *host;	
    	int tolen, len;
    	char buff[N];
    
    	char user[N]; 	
    	char pass[N]; 		
    	char *cwd = "CWD /\n";
    
    	to.sin_family = AF_INET;
    	to.sin_port = htons(2100);
    	host = gethostbyname(host_serv);
    	memcpy(&(to.sin_addr), host->h_addr, sizeof(to.sin_addr));
    	tolen = sizeof(to);
    
    	sd = socket(AF_INET, SOCK_STREAM, 0); // socket pour le controle de la connexion
    	
    	if( 0 > connect(sd, (struct sockaddr *)&to, tolen)){
    
    		perror("connect");
    		exit(1);
    	}
    	
    	memset(buff, '\0', N*sizeof(char));
    	if( recv(sd, buff, sizeof(buff), 0 ) < 0){
    
    		perror("recv");
    		exit(5);
    	}
    	else printf("%s\n",buff);
    
    	//USER
    	strcat(user, "USER ");	
    	strcat(user, user_name);
    	strcat(user, "\n");	
    
    	strcat(pass, "PASS ");	
    	strcat(pass, password);
    	strcat(pass, "\n");
    
    	if( send(sd, user, (strlen(user)+1), 0) < 0 ){
    
    		perror("send");
    		exit(2);
    	}
    	memset(buff, '\0', N*sizeof(char));
    	if( recv(sd, buff, sizeof(buff), 0 ) < 0){
    
    		perror("recv");
    		exit(5);
    	}
    	else printf("%s\n",buff);
    
    
    	//PASS
    	if( send(sd, pass, (strlen(user)+1), 0) < 0 ){
    
    		perror("send");
    		exit(2);
    	}
    	memset(buff, '\0', N*sizeof(char));
    	if( recv(sd, buff, sizeof(buff), 0 ) < 0){
    
    		perror("recv");
    		exit(5);
    	}
    	else printf("%s\n",buff);
    
    	//CWD 
    	if( send(sd, cwd, strlen(cwd)+1, 0) < 0 ){
    
    		perror("send");
    		exit(2);
    	}
    	memset(buff, '\0', N*sizeof(char));
    	if( recv(sd, buff, sizeof(buff), 0 ) < 0){
    
    		perror("recv");
    		exit(5);
    	}
    	else printf("%s\n",buff);
    
    	connecte = 1;
    }
    
    void pwd(){
    
    	char buff[N];
    	char *pwd = "PWD \n";
    
    	if( send(sd, pwd, strlen(pwd)+1, 0) < 0 ){
    
    		perror("send");
    		exit(2);
    	}
    	memset(buff, '\0', N*sizeof(char));
    	if( recv(sd, buff, sizeof(buff), 0 ) < 0){
    
    		perror("recv");
    		exit(5);
    	}
    	else printf("vous etes dans '%s'\n",buff);
    }
    
    void cd(char *rep){
    
    	char cwd[N];
    	char buff[N];
    
    	memset(cwd, '\0', N);
    	strcat(cwd, "CWD ");
    	strcat(cwd, rep);
    	strcat(cwd, "\n");
    
    	if( send(sd, cwd, strlen(cwd)+1, 0) < 0 ){
    
    		perror("send");
    		exit(2);
    	}
    	memset(buff, '\0', N*sizeof(char));
    	if( recv(sd, buff, sizeof(buff), 0 ) < 0){
    
    		perror("recv");
    		exit(5);
    	}
    	else printf("cd %s\n",buff);
    }
    
    
    void quit(){
    
    	char quit[N];
    
    	memset(quit, '\0', N*sizeof(char));
     	strcat(quit,"QUIT\n");
    
    	if(connecte)
    		if( send(sd, quit, strlen(quit)+1, 0) < 0 ){
    
    			perror("send");
    			exit(2);
    		}
    	
    	close(sd);
    	sd=0;
    	printf("Au revoir \n");
    }
    
    void mkd(char *rep){
    
    	char mkd[N];
     	char buff[N];
    
    	memset(mkd, '\0', N*sizeof(char));
    	strcat(mkd,"MKD ");
     	strcat(mkd, rep);
    	strcat(mkd,"\n");
    	
    	if( send(sd, mkd, strlen(mkd)+1, 0) < 0 ){
    
    		perror("send");
    		exit(2);
    
    	}
    	memset(buff, '\0', N*sizeof(char));
    	if( recv(sd, buff, sizeof(buff), 0 ) < 0){
    
    		perror("recv");
    		exit(5);
    	}
    	else printf("%s\n",buff);
    }
    
    void rmd(char *rep){
    
    	char rmd[N];
     	char buff[N];
    
    	memset(rmd, '\0', N*sizeof(char));
    	strcat(rmd,"RMD ");
     	strcat(rmd, rep);
    	strcat(rmd,"\n");
    	
    	if( send(sd, rmd, strlen(rmd)+1, 0) < 0 ){
    
    		perror("send");
    		exit(2);
    	}
    
    	memset(buff, '\0', N*sizeof(char));
    	if( recv(sd, buff, sizeof(buff), 0 ) < 0){
    
    		perror("recv");
    		exit(5);
    	}
    	else printf("%s\n",buff);
    }
    
    void dir(){
    
    	char port[N]; 
    	char list[N]; 
    	char buff[N];
    	int min_port = 1025;
    	int max_port = 65535;
    	struct sockaddr_in serv_addr, cl_addr;
    	int sd_client, sd_server, len, i;
    	struct hostent *host;	
    
    	memset(list, '\0', N*sizeof(char));
    	strcat(list,"LIST\n");
     	
    	//le client devient server pour le serveur FTP
    
    	if( 0 > (sd_server = socket(AF_INET, SOCK_STREAM, 0) )){
    
    		perror("socket");
    		exit(1);
    	}
    
    	serv_addr.sin_family = AF_INET;
    	serv_addr.sin_addr.s_addr  = INADDR_ANY;
    	memset(&(serv_addr.sin_zero), '\0', 8);
    
            len = sizeof(cl_addr);
    
    	for( i = min_port; i <= max_port; i++){
    
    		serv_addr.sin_port = htons(i);
    
            	if(0 <= bind(sd_server, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) ) 
    			break;
    	}
    	
    	memset(buff, '\0', N*sizeof(char));
    	memset(port, '\0', N*sizeof(char));
    	sprintf(buff, "%d", serv_addr.sin_port);
    	printf("DIR %s %d\n", buff, htons(serv_addr.sin_port));
    	strcat(port,"PORT ");
     	strcat(port, buff);
    	strcat(port,"\n");
    	
    	//PORT
    	if( send(sd, port, strlen(port)+1, 0) < 0 ){
    
    		perror("send");
    		exit(2);
    
    	}
    	memset(buff, '\0', N*sizeof(char));
    	if( recv(sd, buff, sizeof(buff), 0 ) < 0){
    
    		perror("recv");
    		exit(5);
    	}
    	else printf("port %s\n",buff);
    
    	//LIST  
    	if( send(sd, list, strlen(list)+1, 0) < 0 ){
    
    		perror("send");
    		exit(2);
    
    	}
    	memset(buff, '\0', N*sizeof(char));
    	if( recv(sd, buff, sizeof(buff), 0 ) < 0){
    
    		perror("recv");
    		exit(5);
    	}
    	else printf("list %s\n",buff);
    
    	if( 0 > listen(sd_server, 5) ){ //da se postavi na 1
    	
    		perror("listen");
    		exit(1);
    	}
    
            if( 0> (sd_client = accept(sd_server, (struct sockaddr *)&cl_addr, &len) )){
    
    		perror("accept");
    		exit(1);
    	}
    
    	memset(buff, '\0', N*sizeof(char));
    	while( recv(sd_client, buff, N*sizeof(char), 0 ) > 0){
    
    		printf("%s\n",buff);
    		
    	}
    
    	close(sd_server);
    	close(sd_client);
    	sd_server=0;
    	sd_client=0;
    }
    
    
    void get(char *file){
    
    	char port[N]; 
    	char retr[N]; 
    	char buff[N];
    	int min_port = 1025;
    	int max_port = 65535;
    	struct sockaddr_in serv_addr, cl_addr;
    	int sd_client, sd_server, len, nb0=1, nb1=1, fd, i;
    	struct hostent *host;	
    
    	memset(retr, '\0', N*sizeof(char));
    	strcat(retr,"RETR ");
    	strcat(retr, file);
     	strcat(retr,"\n");
    
    	
            if( 0 > (sd_server = socket(AF_INET, SOCK_STREAM, 0))){
    
    		perror("socket");
    		exit(1);
    	}
    
    	//le client devient server pour le serveur FTP
    	serv_addr.sin_family = AF_INET;
    	serv_addr.sin_addr.s_addr  = INADDR_ANY;
    	memset(&(serv_addr.sin_zero), '\0', 8);
    
            len = sizeof(cl_addr);
    
    	for( i = min_port; i <= max_port; i++){
    
    		serv_addr.sin_port = htons(i);
    
            	if(0 <= bind(sd_server, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) ) 
    			break;
    	}
    
    	memset(buff, '\0', N*sizeof(char));
    	memset(port, '\0', N*sizeof(char));
    	sprintf(buff, "%d", serv_addr.sin_port);
    	printf("GET %s %d\n", buff, htons(serv_addr.sin_port));
    	strcat(port,"PORT ");
     	strcat(port, buff);
    	strcat(port,"\n");
    	//PORT
    	if( send(sd, port, strlen(port)+1, 0) < 0 ){
    
    		perror("send");
    		exit(2);
    
    	}
    	memset(buff, '\0', N*sizeof(char));
    	if( recv(sd, buff, sizeof(buff), 0 ) < 0){
    
    		perror("recv");
    		exit(5);
    	}
    	else printf("port %s\n",buff);
    
    
    	if(0 > (fd = open(file, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR))) {  //read/write
    
    		perror("open");
    		exit(1);
    	}	
    
    	//RETR 
    	if( send(sd, retr, strlen(retr)+1, 0) < 0 ){
    
    		perror("send");
    		exit(2);
    
    	}
    	memset(buff, '\0', N*sizeof(char));
    	if( recv(sd, buff, sizeof(buff), 0 ) < 0){
    
    		perror("recv");
    		exit(5);
    	}
    	else printf("retr %s\n",buff);
    
    	if( 0 > listen(sd_server, 5) ){ //da se postavi na 1
    
    		perror("listen");
    		exit(1);
    	}
    
        	if( 0 > (sd_client = accept(sd_server, (struct sockaddr *)&cl_addr, &len))){
    
    		perror("accept");
    		exit(1);
    	}
    	
    	memset(buff, '\0', N*sizeof(char));
    	while( nb0 != 0 ){
    
                    nb1 = recv(sd_client, buff, sizeof(buff), 0 ); //sizeof() + 1
                    nb0 = write(fd, buff, nb1);	
    
                    if( nb0 == 0 ){
    
                            printf("Fichier recu\n");
                            nb0 = 0;
                    }
            }
    
    	close(sd_server);
    	close(sd_client);
    	sd_server=0;
    	sd_client=0;
    }
    
    void put(char *file){
    
    	char port[N]; 
    	char stor[N]; 
    	char buff[N];
    	int min_port = 1025;
    	int max_port = 65535;
    	struct sockaddr_in serv_addr, cl_addr;
    	int sd_client, sd_server, len, nb, fd, i;
    	struct hostent *host;	
    
    	memset(stor, '\0', N*sizeof(char));
    	strcat(stor,"STOR ");
    	strcat(stor, file);
     	strcat(stor,"\n");
    
    	if( 0 > (sd_server = socket(AF_INET, SOCK_STREAM, 0))){
    
    		perror("socket");
    		exit(1);
    	}
    
    	// le client devient server pour le serveur FTP
    	serv_addr.sin_family = AF_INET;
    	serv_addr.sin_addr.s_addr  = INADDR_ANY;
    	memset(&(serv_addr.sin_zero), '\0', 8);
    
            len = sizeof(cl_addr);
    
    	for( i = min_port; i <= max_port; i++){
    
    		serv_addr.sin_port = htons(i);
    
            	if(0 <= bind(sd_server, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) ) 
    			break;
    	}
    	
    	memset(buff, '\0', N*sizeof(char));
    	memset(port, '\0', N*sizeof(char));
    	sprintf(buff, "%d", serv_addr.sin_port);
    	printf("PUT %s %d\n", buff, ntohs(serv_addr.sin_port));
    	strcat(port,"PORT ");
     	strcat(port, buff);
    	strcat(port,"\n");
    	
    	//PORT
    	if( send(sd, port, strlen(port)+1, 0) < 0 ){
    
    		perror("send");
    		exit(2);
    
    	}
    	memset(buff, '\0', N*sizeof(char));
    	if( recv(sd, buff, sizeof(buff), 0 ) < 0){
    
    		perror("recv");
    		exit(5);
    	}
    	else printf("port %s\n",buff);
    
    	if( 0 > (fd = open(file, O_RDONLY)) ){
    
    		perror("open");
    		exit(1);
    	}
    
    	printf("sled OPEN\n");
    	printf("On va envoye %s sur %d\n", stor, sd);
    	//STOR
    	if( send(sd, stor, strlen(stor)+1, 0) < 0 ){
    
    		perror("send");
    		exit(2);
    
    	}
    	/*memset(buff, '\0', N*sizeof(char));
    	if( recv(sd, buff, sizeof(buff), 0 ) < 0){
    
    		perror("recv");
    		exit(5);
    	}
    	else printf("stor %s\n",buff);*/
    
    	if( 0 > listen(sd_server, 5) ){
    
    		perror("listen");
    		exit(1);
    	}
    
            if( 0 > (sd_client = accept(sd_server, (struct sockaddr *)&cl_addr, &len) )){
    
    		perror("accept");
    		exit(1);
    	}
    	memset(buff, '\0', N*sizeof(char));
    	while( (nb = read(fd, buff, N))  > 0){
    
    		if( 0 > send(sd_client, buff, nb, 0) ){
    
    			perror("send");
    			exit(1);
    		}
    	}
    	
    	close(sd_server); 
    	close(sd_client); 
    	sd_server=0;
    	sd_client=0;
    	close(fd);	
    }
    
    void del(char *file){
    
    	char dele[N]; 
    	char buff[N];
    
    	memset(dele, '\0', N*sizeof(char));
    	strcat(dele,"DELE ");
    	strcat(dele, file);
     	strcat(dele,"\n");
    
    	if( send(sd, dele, strlen(dele)+1, 0) < 0 ){
    
    		perror("send");
    		exit(2);
    
    	}
    
    	memset(buff, '\0', N*sizeof(char));
    	if( recv(sd, buff, sizeof(buff), 0 ) < 0){
    
    		perror("recv");
    		exit(5);
    	}
    	else printf("%s\n",buff);
    
    }
    
    void prompt(void) {
      
    	printf("%s",PROMPT);
      
    	fflush(stdout);
    }
    
    void aide(void){
    	printf("Les commandes FTP sont les suivantes :\n");
    	printf("------------------------------------------------------------\n");
    	printf(" QUIT 	       Quitte le ftp\n");
    	printf(" OPEN  	       Connecte le client sur le serveur\n");
    	printf(" CD  	       Changement du repertoire courant\n");
    	printf(" PWD   	       Affiche le repertoire courant\n");
    	printf(" GET           Telechargement d`un fichier\n");
    	printf(" PUT           Envoie un fichier sur le serveur\n");    
    	printf(" DEL           Suppression d`un fichier\n");
    	printf(" MKD           Creation d`un repertoire\n");
    	printf(" RMD           Suppression d`un repertoire\n");
    	printf(" DIR           Affiche la liste des fichier dans le repertoire courant\n");
    	printf(" HELP          Liste des commandes reconnues par le client\n");
    	printf("------------------------------------------------------------\n");
    }
    
    Server code
    server.h
    Code:
    /**
     *	Fichier: server.h
     *
     */
    
    #ifndef SERVER_H
    #define SERVER_H
    
    
    #include <ctype.h>
    #include <errno.h>
    #include <signal.h>
    #include <unistd.h>
    #include <sys/wait.h>
    #include <time.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <netinet/in.h>
    #include <netdb.h>
    #include <string.h>
    
    
    #define BUFF_LEN 128
    
    //longueure maximale d'une commande
    #define CMD_LEN  1024	
    
    //Limite maximale du nombre des clients qui peuvent se connecter
    #define MAX_USER_LIMIT	10
    
    #define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
    
    
    /**
     *	Structure: client
     * 	Represente la structure utilisee par le serveur pour stocker de l'information
     * 	necessaire a la communication avec le client
     */
    
    typedef struct {
      	
    	int sock,pid; 		//socket de communication; pid du processus fils servant le client
    
      	struct in_addr addrip;	//adresse IP du client .s_addr
    	
       	unsigned short int dataport;	//port pour la connexion servant du transfert des donnees (fichiers, liste des fichiers dans un rep)
    
      	char curdir[256];	//repertoire dans la quelle se trouve le cliet
    
    } client_t, *client;
    
    
    extern client clients[];   //tableau des clients	
    extern int nb_users;       //utilisatuers actuellement connectes
    extern stop;		  //variable a tester : utilisee pour arreter le serveur
    
    /* prototypes des fonctions */
    
    void init_conn(void); 	//accept; procedure qui accepte/refuse un nouveau client
    		
    void waitchild(int);	//attente d'un processus fils
    	
    void main_loop(void);	//boucle principale
    
    void exec_comm(int i);	//execute une commande en provenance du client: mkd, rmd, list, etc.
    
    #endif
    
    
    server.c

    Code:
    /**
     *	Fichier: server.c
    
     *
     */
    
    #include "server.h"
    
    
    client clients[MAX_USER_LIMIT];
    
    int stop = 0;
    int sd_server=0;		//socket d'ecoute sur le serveur
    const int maxusers= MAX_USER_LIMIT;	// max utilisatuers connectes
    int nb_users=0;       		//utilisatuers actuellement connectes
    int max_user_sock = 0;    //socket de valeur maximale
    int serverport = 2100;		//port d'ecoute du serveur
    char *prompt_symb = "serv>";	//invite de commande pour l'administration du serveur
    
    
    /**
     *	Procedure: prompt
     
     *	Affiche l'invite de commande "serv>"
     */
    
    void prompt(void) {
      
    	printf("%s",prompt_symb);
      
    	fflush(stdout);
    }
    
    /**
     *	Procedure: init_conn
     
     *	procedure qui accepte/refuse un nouveau client
     */
    
    void init_conn(void){
    
    
    	int i, sd_client, len;
    	char buff[BUFF_LEN];
    	struct sockaddr_in cl_addr;
    	struct hostent *host;
    
    	len = sizeof(cl_addr);
    
     	if( 0 > (sd_client = accept(sd_server, (struct sockaddr *)&cl_addr, &len)) ){
    
    		perror("accept");
    		return;
    	} 
    
    	//test si le nombre maximale de clients connectes est atteint
    
    	if(nb_users ==  maxusers){
    
    		//le nombre max d'utilisatuers connectes est atteint !!!
    		printf("\nLe nombre max d'utilisatuers connectes est atteint !!!\n");
    
    		memset(buff, '\0', BUFF_LEN);
    		sprintf(buff, "Le serveur est occupe.\r\n");
    		send(sd_client, buff, strlen(buff), 0);
    		close(sd_client);
    		return;
    	}
    	else{ //on accepte la connexion et on remplit la structure du client
    	
    		clients[nb_users]->addrip.s_addr = cl_addr.sin_addr.s_addr; // on doit copier l'adresse IP du client dans la structure 		
    		clients[nb_users]->sock = sd_client;
    		strcpy(clients[nb_users]->curdir,"/");		
    		clients[nb_users]->pid = 0;
    		clients[nb_users]->dataport = 0;
    		printf("\nUn nouveau client s'est connecte \n");
    		max_user_sock = MAX(max_user_sock, clients[nb_users]->sock);
    		nb_users++;
    		printf("Nombre des clients connectes: %d\n", nb_users);
    		prompt();
    	}
    	
    	memset(buff, '\0', BUFF_LEN);
    	sprintf(buff, "Bienvenu\r\n");
    	send(sd_client, buff, strlen(buff), 0);
    }
    
    /**
     *	Procedure: waitchild
     *	attente d'un processus fils
     */
    
    void waitchild(int n) {
    
    	while (wait3(NULL, WNOHANG, NULL) > 0);
    	
    }
    
    /**
     *	Procedure: main_loop
     *	boucle principale du serveur: bind, listen, select 
     */
    
    
    void main_loop(void) 
    {
      	int i,n,addr;
      	unsigned int sock_size;
      	fd_set readfds, testfds;
      	char cmd[CMD_LEN];
      	struct sockaddr_in sa;
    
      	signal(SIGPIPE,SIG_IGN);
      	signal(SIGCHLD,waitchild);
    
      	/* server init */
    
      	printf("Demarrage du serveur ... \n");
    
      	if ((sd_server=socket(AF_INET,SOCK_STREAM,0)) < 0) {
        
    		perror("socket");
        		exit(1);
      	}
    
      	sock_size=sizeof(struct sockaddr_in);
      	memset(&sa,0,sock_size);
      
      	sa.sin_family=AF_INET;
      	sa.sin_port=htons(serverport);
      	addr=INADDR_ANY;
      	memcpy(&sa.sin_addr.s_addr,&addr,sizeof(int));
      
      	if (bind(sd_server,(struct sockaddr *)&sa,sock_size)<0) {
    
        		perror("bind");
    		exit(1);
      	}
    
      	if (listen(sd_server,5)<0) {
        
    		perror("listen");
        		exit(1);
      	}
    
        	printf("OK\n\n");
        	prompt();
    
    	FD_ZERO(&readfds);
        	FD_SET(1,&readfds);
        	FD_SET(sd_server,&readfds);
    	
    	stop = 0;
      	while (!stop) {	
        
    
    		testfds = readfds;
    
    	    	for (i = 0; i < nb_users; i++)
    
          			if (clients[i]->sock)
    				FD_SET(clients[i]->sock,&testfds);
    
    		//selectoin d'un descripteur actif
           		if( 0 > (n = select(max_user_sock, &testfds, (fd_set *)0, (fd_set *)0, (struct timeval *)0))){
    					
          			if (errno!=EINTR || n != -1) 
                    exit(1);
    		}
    
    
    		//qqch a lire a partir du clavier (commande d'administration)
    		if(FD_ISSET(1,&testfds)) {
    	
    			i=read(1,cmd,sizeof(cmd));
    	
    			cmd[i-1]=0;
    			serv_comm(cmd); 
    			if (stop) break;  //on sort du while et on arrete le serveur
    			prompt();
          		}
    
    		 
    		if (FD_ISSET(sd_server,&testfds)) init_conn();  // nouveau client
    
    		//lire des informations provenant des clients 
       		for (i = 0; i < nb_users; i++) {
          			if (clients[i]->sock) {
    				if (FD_ISSET(clients[i]->sock,&testfds))
    	  			exec_comm(i); //i - numero du client dans le tableau des clients
    				prompt();
          			}
        		}
    
        
      	}
    
    
    close(sd_server);
    printf("Serveur arrete.\n");
    }
    
    /**
     *	Fonction: main
     */
    
    int main(int argc, char *argv[]){
    
    	int i;
    
    	for (i = 0; i < maxusers; i++) {
        
    		clients[i]=(client_t *)malloc(sizeof(client_t));
        		clients[i]->sock=0;
      	}
    
    	main_loop();
    	
    
    	for(i = 0; i < maxusers; i++) {
          	
    		if (clients[i]) {
    			free(clients[i]);
    			clients[i]=(client_t *)NULL;
          		}
    
    	}
    return(0);
    }
    
    serv_comm.c

    Code:
    
    /**
     *	Fichier: serv_comm.h
    
     *
     */
    
    #ifndef SERV_COMM
    #define SERV_COMM
    
    #include "server.h"
    
    void serv_comm(char *);	//execute une commande d'administration
    void help(void);	//affiche un menu d'aide 
    void quit(void);	//arrete le serveur
    
    #endif
    
    
    serv_comm.c

    Code:
    
    /**
     *	Fichier: serv_comm.c
     
     *
     */
    
    #include "serv_comm.h"
    
    
    int stop;
    
    /* Execute les commandes d'administration du serveur */
    
    void serv_comm(char *cmd){
    
    	/* Plus tard, si l'on disposera avec plus de commandes, a utiliser plustot un switch */
    	if(strcmp("quit", cmd) == 0) quit();
    	else
    	if(strcmp("help", cmd) == 0) help();
    	else printf("Commande %s inconnue\n", cmd);
    
    }
    
    /* Affiche les commandes d'administration disponibles sur le serveur */
    
    void help(void) {
    
    
    		
    	char *help_info = "Affiche les commandes disponibles";
    	char *quit_info = "Quitte le serveur";
    
      	printf("\nCommandes:\n");
      	
       	printf("%s - %s\n","help", help_info);
    	printf("%s - %s\n","quit", quit_info);
    
    	printf("\n");
    }
    
    
    /* Commande qui arrete le serveur. Pour rendre compatible avec rfc FTP, if faut aussi envoyer un msg commme quoi le serv s'arrete */
    
    void quit(void)
    {
      	int i;
    
     	for (i = 0; i < nb_users; i++) {
        		
    		if (clients[i]->sock) 
        	
    			close(clients[i]->sock);
      	}
    
      stop=1;
    }
    
    exec_comm.h
    Code:
    
    /**
     *	Fichier: exec_comm.h
    
     *
     */
    
    
    #ifndef EXEC_COMM_H
    #define EXEC_COMM_H
    
    #include "server.h"
    
    #include <stdio.h>
    #include <string.h>
    #include <sys/stat.h>
    #include <unistd.h>
    #include <dirent.h>
    
    
    /** 
     * 	Commandes du protocole
     *
     */
    
    // Le client quitte le serveur
    #define QUIT 0     
    
    //Le client envoie son port de donnees
    #define PORT 1
    
    //Le client veut la liste des fichiers dans le rep. courant
    #define LIST 2
    
    //Le client veut changer le repertoire courant
    #define CWD  3
    
    //Le client veut savoir dans quel rep. il est actuellemet
    #define PWD  4
    
    //Le client veut telecharger un fichier
    #define RETR 5
    
    //Le client envoie son user name
    #define USER 6
    
    //Le client envoie son password
    #define PASS 7
    
    //Le client veut envoyer un fichier sur le serveur
    #define STOR 8
    
    //Le client veut remonter dans le rep. precedent
    #define CDUP 9
    
    //Le client veut effacer un fichier
    #define DELE 10
    
    //Le client veut creer un rep.
    #define MKD  11
    
    //Le client veux supprimer un client
    #define RMD  12
    
    /* Prototypes des fonctions */
    
    void exec_comm(int);	//execute une commande
    
    void my_list(int);	//donne la liste des fichiers d'un repertoire
    void stor(int,char *);	//recupere un fichier a partir du client
    void retr(int,char *);	//envoie un fichier vers le clients
    int mkd(int,char *);	//cree un repertoire
    int rmd(int,char *);	//supprime un repertoire vide
    void dele(int,char *);	//supprime un fichier
    
    
    #endif
    
    
    exec_comm.c

    Code:
    /**
     *	Fichier: exec_comm.c
     *
     */
    
    
    #include "exec_comm.h"
    
    //tableau qui contient les commandes du protocole
    
    const char *commandes[13] = {
    
    	"QUIT", "PORT", "LIST", "CWD", "PWD", "RETR", "USER", "PASS", "STOR", "CDUP", "DELE", "MKD", "RMD"
    };
    
    int nb_users;
    
    const char *ok = "OK\r\n";
     
    
    /**
     *	Procedure: exec_comm
     
     *	execute une commande
     */
    
    void exec_comm(int cl){
    
    	int size,j, port;
    	char buff[1024], *param;
    	
    	fd_set fds;
    
    	memset(buff, '\0', 1024);
    
    	size = recv(clients[cl]->sock, buff, sizeof(buff), 0);
    	
    	printf("recu: %s\n", buff);
    
    	//pour recuperer la commande et son parametre
    	buff[size-2]=0;  // -'\r' et  -'\n'
    
    	param = strchr(buff,' '); // param <- commande
    	if (param) {
        		*param=0;
        		param++;
      	}
      
    	for (j = 0; j < strlen(buff); j++)
        		
    		buff[j]=toupper(buff[j]);
      
    	for (j = 0; j < 13; j++)
    
    		if (!strcmp(buff,commandes[j])) break;
    
    	//on teste successivment quelle commande le serveur a recu et apres il execute la fonction appropriee: list, cwd, pwd, etc.
    
    	switch(j){
    
    		case QUIT:	if (clients[cl]->pid) kill(clients[cl]->pid,SIGTERM);
    				clients[cl]->pid=0;
    				close(clients[cl]->sock);
    				clients[cl]->sock=0; //NIKOGA AMA NIKOGA NE GO KOMENTIRAI !!!!!!!!!
      				clients[cl]->dataport=0;
    				nb_users--;
    				printf("%s\n", commandes[j]);
    				break;	
    
    		case  PORT:	port = atoi(param);
    				clients[cl]->dataport = port;
    				printf("%s %s\n", commandes[j], param);
    				send(clients[cl]->sock, ok, strlen(ok)+1, 0);
    				break;	
    
    		case CWD:	memset(clients[cl]->curdir, '\0', (strlen(clients[cl]->curdir)+1));
    				strcpy(clients[cl]->curdir,param);	//on passe le chemin absolu !!!
    				send(clients[cl]->sock, ok, strlen(ok)+1, 0);
    				printf("%s  %s\n", commandes[j],clients[cl]->curdir);
    				break;	
    
    		case PWD:	send(clients[cl]->sock, clients[cl]->curdir, strlen(clients[cl]->curdir)+1, 0);
    				
    				printf("%s %s\n",commandes[j], clients[cl]->curdir);
    				break;
    
    		case USER:	send(clients[cl]->sock, ok, strlen(ok)+1, 0);
    				printf("%s %s\n", commandes[j], param);
    				break;
    
    		case PASS:	send(clients[cl]->sock, ok, strlen(ok)+1, 0);
    				printf("%s %s\n", commandes[j], param);
    				break;
    		
    		// Vu que la liste des fichiers peut etre une taille considerable, il vaut mieux creer un fils 
    		// qui s'occupera avec le client
    		case LIST:	if( (clients[cl]->pid = fork()) == 0 ){ 
    					send(clients[cl]->sock, ok, strlen(ok)+1, 0);
    					my_list(cl);
    				}
    				//send(clients[cl]->sock, ok, strlen(ok)+1, 0);
    				printf("%s %s\n", commandes[j], clients[cl]->curdir);
    				break;
    
    		//Vu qu'un fichier peut etre d'une grande taille, il vaut mieux creer un fils 
    		// qui s'occupera avec l'envoi vers le client
    
    		case RETR:	if( (clients[cl]->pid = fork()) == 0 ) { 
    					send(clients[cl]->sock, ok, strlen(ok)+1, 0);
    					retr(cl, param);
    				}
    				printf("%s %s\n", commandes[j], param);
    				break;
    
    		//Vu qu'un fichier peut etre d'une grande taille, il vaut mieux creer un fils 
    		// qui s'occupera avec la recuperation a partir du client
    
    		case STOR:	if( (clients[cl]->pid = fork()) == 0 ){
    					//send(clients[cl]->sock, ok, strlen(ok)+1, 0);
    					stor(cl, param);
    				}
    				printf("%s %s\n", commandes[j], param);	
    				break;
    
    		//Vu qu'un fichier peut etre d'une grande taille, il vaut mieux creer un fils 
    		// qui s'occupera avec la suppression
    
    		case DELE:	if( (clients[cl]->pid = fork()) == 0 ) dele(cl,param);
    				send(clients[cl]->sock, ok, strlen(ok)+1, 0);
    				printf("%s %s\n", commandes[j], param);
    				break;
    
    		case MKD:	mkd(cl,param);  
    				send(clients[cl]->sock, ok, strlen(ok)+1, 0);
    				printf("%s %s\n", commandes[j], param);
    				break;
    		
    		case RMD:	rmd(cl,param);
    				send(clients[cl]->sock, ok, strlen(ok)+1, 0);
    				printf("%s %s\n", commandes[j], param);
    				break;
    		
    		//Par defaut, on considere que si un client envoie des commandes incomprehensibles, il doit etre deconnecte
    		default:	if (clients[cl]->pid) kill(clients[cl]->pid,SIGTERM);
    				if (clients[cl]->pid) kill(clients[cl]->pid,SIGTERM);
      				clients[cl]->pid=0;
    				close(clients[cl]->sock);
    				clients[cl]->sock = 0;
    				clients[cl]->dataport = 0;
    				nb_users--;
    				printf("Client %d deconnexion brutale\n",cl);
    				break;
    	}	
    }
    
    
    /**
     *	Procedure: my_list
     
     *	envoie la liste des fichiers situes dans un repertoire au client
     */
    
    
    void my_list(int cl){
    
    	struct dirent *dirp;
    	DIR *dirfd;
    	int sd, tolen, nb;
    	struct sockaddr_in to;
    	struct hostent *host;
    
    	to.sin_family = AF_INET;
    	to.sin_port = clients[cl]->dataport;
     	to.sin_addr.s_addr = clients[cl]->addrip.s_addr; //IP
    	tolen = sizeof(to);
    
    	if( (sd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
    
    		perror("socket");
    		exit(1);
    	}
    
    	//le serveur se connecte vers le client
    	if(0 > connect(sd, (struct sockaddr*)&to, tolen)){
    
    		perror("connect MY_LIST");
    		exit(1);
    	}
    
    	//le serveur ouvre le reprtoire desire par le client
    	if( !(dirfd = opendir(clients[cl]->curdir))){
    
    		perror("opendir");
    		exit(1);
    	}
    
    	//le serveur envoie ce qu'il a lu a partir du repertoire vers le client
    	while( (dirp = readdir(dirfd)) != NULL) {
    
    		send(sd, dirp->d_name, 1024*sizeof(char), 0);
    	}
    
    closedir(dirfd);
    close(sd);
    
    exit(0);
    }
    
    
    /**
     *	Procedure: stor
     
     *
     *	Recupere un fichier a partir du client. En fait le client veux envoyer un fichier sur le serveur.
     *	Pour faire cela, il envoye premieremt un port (port des donnees) sur lequel il se met en attente d'une connexion
     *	de la part du serveur. 
     * 	De la cote du serveur, lorsqu'on a recu le port des donnees, on essaye d'etablire une connexion avec le client.
     *	Une fois la connexion etablie, le serveur est pret de recevoir des donnees provenant du client. 
     *	Le fichier sera recu dans le repertoire courant du client (cote serveur)
     */
    
    void stor(int cl, char *filename){
    
    	int fd, nb0=1, nb1=1;
    	char buff[1024];
    	char fichier[1024]; //contient le chemin absolut
    	int s, tolen, nb;
    	struct sockaddr_in to;
    	struct hostent *host;
    	
    	memset(fichier, '\0', 1024*sizeof(char));
    	strcat(fichier, clients[cl]->curdir);
    	strcat(fichier,"/");
    	strcat(fichier, filename);
    	
    	printf("stor FILE: %s\n", fichier);
    
    	//fd = open(fichier, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR);  //read/write
    
    	to.sin_family = AF_INET;
    	to.sin_port = clients[cl]->dataport;
     	to.sin_addr.s_addr = clients[cl]->addrip.s_addr; //IP
    	tolen = sizeof(to);
    
    	if( (s = socket(AF_INET, SOCK_STREAM, 0)) < 0){
    
    		perror("socket");
    		exit(1);
    	}
    
    	if(0 > connect(s, (struct sockaddr*)&to, tolen)){
    
    		perror("connect STOR");
    		exit(1);
    	}
    
    	fd = open(fichier, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR);  //read/write
    	memset(buff, '\0', 1024*sizeof(char));
    	
    	while( nb0 != 0 ){
    
                    nb1 = recv(s, buff, sizeof(buff), 0 ); //strlen + 1  
                    nb0 = write(fd, buff, nb1);
    
                    if( nb0 == 0 ){
    
                            printf("Fichier recu\n");
                            nb0 = 0;
                    }
            }
    
    	close(s);
    	close(fd);
    	s = 0;
    
    exit(1);
    }
    
    /**
     *	Procedure: retr
     
     *
     *	Recupere un fichier a partir du serveur. En fait le client veux telecharger un fichier a partir du serveur.
     *	Pour faire cela, il envoye premieremt un port (port des donnees) sur lequel il se met en attente d'une connexion
     *	de la part du serveur. 
     * 	De la cote du serveur, lorsqu'on a recu le port des donnees, on essaye d'etablire une connexion avec le client.
     *	Une fois la connexion etablie, le serveur est pret d'envoyer des donnees vers client. 
     *	Le fichier sera recu dans le repertoire ou le client a ete demarre.
     */
    
    void retr(int cl, char *filename){
    
    	int fd, nb0, nb1;
    	char buff[1024];
    	char fichier[1024];
    	int sd, tolen, nb;
    	struct sockaddr_in to;
    	struct hostent *host;
    
    	memset(fichier, '\0', 1024*sizeof(char));
    	strcat(fichier, clients[cl]->curdir);
    	strcat(fichier,"/");
    	strcat(fichier, filename);
    
    	printf("retr FILE: %s\n", fichier);	
    	
    	if( (fd = open(fichier, O_RDONLY)) == -1 ){  //read
    
    		perror("open");
    		exit(1);
    
    	}
    
    	to.sin_family = AF_INET;
    	to.sin_port = clients[cl]->dataport;
     	to.sin_addr.s_addr = clients[cl]->addrip.s_addr; //IP
    	tolen = sizeof(to);
    
    	if( (sd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
    
    		perror("socket");
    		exit(1);
    	}
    
    	if(0 > connect(sd, (struct sockaddr*)&to, tolen)){
    
    		perror("connect");
    		exit(1);
    	}
    
    	while( (nb = read(fd, buff, 1024)) > 0 ){
    
    		//printf("sent %d\n", nb);
                    send(sd, buff, nb, 0);
    		printf("sent %d\n", nb);	
            }
    	
    	close(sd);
    	sd=0;
    
    exit(0);
    }
    
    /**
     *	Procedure: mkd
     
     *
     *	Creation d'un repertoire sur le serveur, dans le repertoire courant du client (cote serveur)
     */
    
    int mkd(int cl, char *dir_name){
    
    	char rep[1024]; 
    
    	memset(rep, '\0', 1024*sizeof(char));
    
    	strcat(rep, clients[cl]->curdir);
    	strcat(rep,"/");
    	strcat(rep, dir_name);
    
    	if( mkdir(rep, 0755) == -1 ){
    
    		perror("mkdir");
    		return(0);
    	}	
    	printf("Repertoire %s cree\n", rep);
    
    return(1);
    }
    
    
    /**
     *	Procedure: rmd
     
     *
     *	Suppression d'un repertoire VIDE sur le serveur, dans le repertoire courant du client (cote serveur)
     */
    
    int rmd(int cl, char *dir_name){
    
    	char rep[1024]; 
    
    	memset(rep, '\0', 1024*sizeof(char));
    
    	strcat(rep, clients[cl]->curdir);
    	strcat(rep,"/");
    	strcat(rep, dir_name);
    
    	if( rmdir(rep) == -1 ){
    
    		perror("rmdir");
    		return(0);
    	}	
    	printf("Repertoire %s supprime\n", rep);
    
    return(1);
    }
    
    
    /**
     *	Procedure: dele
     
     *
     *	Suppression d'un fichier sur le serveur, dans le repertoire courant du client (cote serveur)
     */
    
    void dele(int cl, char *filename){
    
    	char fichier[1024];
    
    	memset(fichier, '\0', 1024*sizeof(char));
    	strcat(fichier, clients[cl]->curdir);
    	strcat(fichier,"/");
    	strcat(fichier, filename);
    
    	if( unlink(fichier) == -1 ){
    
    		perror("unlink");
    		exit(1);
    	}	
    
    	printf("Fichier %s supprime\n", fichier);
    
    exit(1);
    }
    
     
  2. xpi0t0s

    xpi0t0s Mentor

    Joined:
    Aug 6, 2004
    Messages:
    3,009
    Likes Received:
    203
    Trophy Points:
    63
    Occupation:
    Senior Support Engineer
    Location:
    England
    This line caught my eye: memset(clients[cl]->curdir, '\0', (strlen(clients[cl]->curdir)+1));

    This seems to be the only place curdir is initialised. curdir is char[256] which contains garbage until it is initialised, with NO GUARANTEE of a zero byte anywhere in those 256 bytes. So what happens if strlen(curdir) returns, say, 500?
     
  3. tonio

    tonio New Member

    Joined:
    Nov 2, 2008
    Messages:
    2
    Likes Received:
    0
    Trophy Points:
    0
    Hi xpi0t0s,

    I fixed this but it's not the main problem because when I'm using only this command I don't any problems. You're right that the fact that I'm not testing the max length of the curdir could lead to a problem.
    Anyway thank you for the post :)
    Yesterday night I saw that my main problem comes when the server creates a child process ( LIST, RETR, STOR and DELE cases) in the exec_comm.c file. When a new process is created to serve a client request, if there is some error, the system doesn't prints the error message on the screen. But if I remove for a while the fork and I'm just letting the main server to do the requested command ( LIST, RETR, STOR and DELE) I see that there is a problem with the connect function.
    At the same time, if I type a netstat -na on a shell, I see that the client waits for a connection but I can't understand why the server can't connect :confused:
    I'm always working with a localhost, so there aren't any kind of firewalls or something like that.

    Command request in the exec_comm.c (with fork())
    Code:
    case LIST:	if( (clients[cl]->pid = fork()) == 0 ){ 
    					send(clients[cl]->sock, ok, strlen(ok)+1, 0);
    					my_list(cl);
    				}
        send(clients[cl]->sock, ok, strlen(ok)+1, 0);
    		printf("%s %s\n", commandes[j], clients[cl]->curdir);
    		break;
    
    and the place where the error message for the connect() function appears
    this is at the my_connect() function in exec_comm.c file
    Code:
    if(0 < connect(sd, (struct sockaddr*)&to, tolen)){
        
    		
    		perror("connect my_list");
      	exit(1);
      }
    
    I suppose there should be the same connection problem with the rest of my commands when a child is created to serve the client.
    Any ideas ? :)

    Best regards,

    Tonio
     
  4. xpi0t0s

    xpi0t0s Mentor

    Joined:
    Aug 6, 2004
    Messages:
    3,009
    Likes Received:
    203
    Trophy Points:
    63
    Occupation:
    Senior Support Engineer
    Location:
    England
    I've had a look through the code but I have no idea. Try outputting debug messages indicating clearly what's going on, to a file, one for the forked process and one for the parent. Don't forget that after calling fork() BOTH processes continue to execute simultaneously, and as you don't wait for the child process to end it could be that the parent does something to disrupt the child processes (e.g. close(fd_server) might be a problem if it happens half way through the operation). Race conditions are very hard to debug and as you have no synchronisation that's what I suspect you have. Or a memory corruption, hence the commend about curdir.
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice