Go4Expert

Go4Expert (http://www.go4expert.com/)
-   C (http://www.go4expert.com/forums/c/)
-   -   simple implementation of FTP on "C" (http://www.go4expert.com/forums/simple-implementation-ftp-c-t14938/)

tonio 3Nov2008 05:18

simple implementation of FTP on "C"
 
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);
}


xpi0t0s 3Nov2008 18:21

Re: simple implementation of FTP on "C"
 
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?

tonio 3Nov2008 19:50

Re: simple implementation of FTP on "C"
 
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 :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

xpi0t0s 4Nov2008 00:19

Re: simple implementation of FTP on "C"
 
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.


All times are GMT +5.5. The time now is 17:54.