simple implementation of FTP on "C"

tonio's Avatar, Join Date: Nov 2008
Newbie Member
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);
}
0
xpi0t0s's Avatar, Join Date: Aug 2004
Mentor
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?
0
tonio's Avatar, Join Date: Nov 2008
Newbie Member
Quote:
Originally Posted by xpi0t0s
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?
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
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
0
xpi0t0s's Avatar, Join Date: Aug 2004
Mentor
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.