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); }
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
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.