Thread: c socket send file that contain LIST

  1. #1
    Registered User
    Join Date
    May 2012
    Location
    Italy
    Posts
    53

    c socket send file that contain LIST

    I'm developing a small FTP Server and i have a trouble with the LIST command. Here all the code of my ftp server:

    Code:
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <sys/types.h>
    #include <netdb.h>
    #include <errno.h>
    #include <fcntl.h>
    #include <sys/sendfile.h>
    #include <sys/stat.h>
    #include <signal.h>
    #include <dirent.h>
    #include "prototypes.h"
    
    
    int main(int argc, char *argv[]){
    	
    	check_before_start(argc, argv);
    	
    	int sockd, newsockd, socket_len, rc, rc_list, fd, fpl, i;
    	int NumPorta = atoi(argv[1]);
    	struct sockaddr_in serv_addr, cli_addr; /* strutture contenenti indirizzo del server e del client */
    	off_t offset = 0, offset_list = 0; /* variabile di tipo offset */
    	struct stat stat_buf; /* struttura contenente informazioni sul file scelto */
    	struct hostent *local_ip; /* struttura contenente ip server */
    	static char filename[1024], buffer[256], saved_user[256]; /* dichiaro static cosė viene direttamente inizializzato a 0 l'array */
    	char *user_string = NULL, *username = NULL, *pass_string = NULL, *password = NULL, *other = NULL; /* puntatori per uso vario */
    	size_t fsize; /* grandezza file */
      char **files;
      FILE *fp_list;
      size_t count;
    	
    	if((sockd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
    		perror("Errore creazione socket\n");
    		exit(EXIT_FAILURE);
    	}
    
    
    	bzero((char *) &serv_addr, sizeof(serv_addr)); /* bzero scrive dei null bytes dove specificato per la lunghezza specificata */
    	serv_addr.sin_family = AF_INET; /* la famiglia dei protocolli */
    	serv_addr.sin_port = htons(NumPorta); /* porta htons converte nell'ordine dei byte di rete */
    	serv_addr.sin_addr.s_addr = INADDR_ANY; /* dato che č un server bisogna associargli l'indirizzo della macchina su cui sta girando */
    	
    	if(bind(sockd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0){
    		perror("Errore di bind\n");
    		onexit(0, sockd, 0, 1);
    	}
    
    
    	if(listen(sockd, 5) < 0){
    			perror("Errore nella funzione listen");
        		onexit(0, sockd, 0, 1);
    	}
    	socket_len = sizeof(cli_addr);
    	
    	if((local_ip = gethostbyname("localhost")) == NULL){
    		perror("gethostbyname()");
    		exit(1);
    	}	
    	
    	signal (SIGINT, ( void *)sig_handler); /* se premuto CTRL+C il server termina */
    	
    	while(1){
    		if((newsockd = accept(sockd, (struct sockaddr *) &cli_addr, (socklen_t *) &socket_len)) < 0){
    			perror("Errore nella connessione\n");
    			onexit(newsockd, sockd, 0, 2);
    		}
    
    
        /* inet_ntoa converte un hostname in un ip decimale puntato */
        fprintf(stdout, "Ricevuta richiesta di connessione dall' indirizzo %s\n", inet_ntoa(cli_addr.sin_addr));
    
    
        /************************* MESSAGGIO DI BENVENUTO *************************/
        sprintf(buffer, "220 FTPUtils SERVER [%s]", local_ip->h_name);
        if(send(newsockd, buffer, strlen(buffer), 0) < 0){
          perror("Errore durante l'invio");
    			onexit(newsockd, sockd, 0, 2);
    		}
    		memset(buffer, '0', sizeof(buffer));
        /************************* FINE MESSAGGIO DI BENVENUTO *************************/
    
    
    		/************************* INIZIO PARTE LOGIN *************************/
        /************************* RICEVIAMO NOME UTENTE E MANDIAMO CONFERMA *************************/
    		 if(recv(newsockd, buffer, sizeof(buffer), 0) < 0){
        	perror("Errore nella ricezione del nome utente");
        	onexit(newsockd, sockd, 0, 2);
        }    	
        user_string = strtok(buffer, " ");
        username = strtok(NULL, "\n");
        fprintf(stdout, "%s %s\n", user_string, username);
        sprintf(saved_user, "%s", username);
        memset(buffer, '0', sizeof(buffer));
        strcpy(buffer, "USEROK\n");
        if(send(newsockd, buffer, strlen(buffer), 0) < 0){
          perror("Errore durante l'invio");
    		  onexit(newsockd, sockd, 0, 2);
    		}
    		memset(buffer, '0', sizeof(buffer));
        /************************* FINE NOME UTENTE *************************/
    
    
        /************************* RICEVIAMO PASSWORD E MANDIAMO CONFERMA *************************/
        if(recv(newsockd, buffer, sizeof(buffer), 0) < 0){
        	perror("Errore nella ricezione del nome utente");
        	onexit(newsockd, sockd, 0, 2);
        }
        pass_string = strtok(buffer, " ");
        password = strtok(NULL, "\n");
        fprintf(stdout, "%s %s\n", pass_string, password);
        memset(buffer, '0', sizeof(buffer));
       	strcpy(buffer, "PASSOK\n");
        if(send(newsockd, buffer, strlen(buffer), 0) < 0){
          perror("Errore durante l'invio");
    		  onexit(newsockd, sockd, 0, 2);
    		}
    		memset(buffer, '0', sizeof(buffer));
        /************************* FINE PASSWORD *************************/
        	
        /************************* INVIO CONFERMA LOG IN *************************/
        sprintf(buffer, "230 USER %s logged in\n", saved_user);
        if(send(newsockd, buffer, strlen(buffer), 0) < 0){
    			perror("Errore durante l'invio");
    			onexit(newsockd, sockd, 0, 2);
    		}
    		memset(buffer, '0', sizeof(buffer));
    		/************************* FINE CONFERMA LOG IN *************************/
        /************************* FINE PARTE LOGIN *************************/
    
    
        /************************* RICEZIONE RICHIESTA LIST E INVIO LISTA *************************/
        if(recv(newsockd, buffer, sizeof(buffer), 0) < 0){
        	perror("Errore nella ricezione del nome utente");
        	onexit(newsockd, sockd, 0, 2);
        }
        other = strtok(buffer, "\n");
        if(strcmp(other, "LIST") == 0){
        	printf("Ricevuta richiesta LIST\n");
        } else onexit(newsockd, sockd, 0, 2);
        
        count = file_list("/home/pol/c-dev", &files);
        if((fp_list = fopen("listfiles.txt", "w")) == NULL){
          perror("Impossibile aprire il file per la scrittura LIST");
          exit(EXIT_FAILURE);
        }
        for(i=0; i < count; i++){
          if(strcmp(files[i], "DIR ..") == 0 || strcmp(files[i], "DIR .") == 0){
            continue;
          } else{
            fprintf(fp_list, "%s\n", files[i]);
          }
        }
        return 0;
        if((fpl = open("listfiles.txt", O_RDONLY)) < 0){
          perror("open file with open");
          exit(1);
        }
        if(fstat(fpl, &stat_buf) < 0){
          perror("Errore fstat");
          onexit(newsockd, sockd, 0, 2);
        }
        fsize = stat_buf.st_size;
        if(send(newsockd, &fsize, sizeof(fsize), 0) < 0){
          perror("Errore durante l'invio grande file list");
          onexit(newsockd, sockd, 0, 2);
        }
        rc_list = sendfile(newsockd, fpl, &offset_list, stat_buf.st_size);
        if(rc_list == -1){
          perror("Invio file list non riuscito");
          close(newsockd);
          close(sockd);
          fclose(fp_list);
        }
        if(rc_list != fsize){
          fprintf(stderr, "Trasferimento incompleto: %d di %d bytes\n", rc_list, (int)stat_buf.st_size);
          close(newsockd);
          close(sockd);
          fclose(fp_list);
        }
        fclose(fp_list);
        close(fpl);
        fsize = 0;
        printf("File inviato\n");
    		memset(buffer, '0', sizeof(buffer));
        /************************* FINE RICEZIONE LIST E INVIO LISTA *************************/
    
    
       /* other non useful things */
    and the function that do the file list:

    Code:
    size_t file_list(char *path, char ***ls){
    	DIR *dp;
      struct stat fileStat;
      struct dirent *ep = NULL;
      size_t len, count = 0;
      int file = 0;
      *ls = NULL;
      dp = opendir (path);
      if(dp == NULL){
        fprintf(stderr, "Non esiste la directory: %s\n", path);
        exit(1);
      }
    
    
      ep = readdir(dp);
      while(NULL != ep){
        count++;
        ep = readdir(dp);
      }
      rewinddir(dp);
    
    
      *ls = calloc(count, sizeof(char *));
      count = 0;
      ep = readdir(dp);
      while(NULL != ep){
        if((file = open(ep->d_name, O_RDONLY)) < 0){
          perror("apertura file");
          exit(1);
        }
        if(fstat(file, &fileStat) != 0){
          perror("filestat");
          free(*ls);
          exit(EXIT_FAILURE);
        }
        if(S_ISDIR(fileStat.st_mode)){
          len = strlen(ep->d_name);
          (*ls)[count] = malloc(len+5); /* lunghezza stringa + "DIR \n" */
          strcpy((*ls)[count], "DIR "); /* copio DIR */
          strcat((*ls)[count++], ep->d_name);
          free((*ls)[count]);
          ep = readdir(dp);
        } else{
          (*ls)[count++] = strdup(ep->d_name);
          ep = readdir(dp);
        }
      (void)closedir(dp);
      return count;
      }
    }
    My problem is that the file that will be created (listfiles.txt) contain only 1 name -.-''
    I've tried the function file_list in a separate and smaller program and it works perfectly so it isn't his fault!
    But i don't know why there is the strange behaviour!

  2. #2
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    First, you should learn to use a debugger. Then you can simply step through the code line by line, and follow exactly what is happening, examine variables, etc.

    Second, you need to work on your indentation. It's there, but inconsistent. I personally find 2 spaces a little too close for comfort, making hard to distinguish the different levels of indentation. I suspect this is part of why you didn't spot your problem sooner.

    Third, crank up the warnings on your compiler. When I compiled your file_list function in gcc (with the -Wall flag to enable all warnings), I got the following:
    Code:
    $ gcc -Wall -g -std=c99 -pedantic  files.c   -o files
    files.c: In function ‘file_list’:
    files.c:61: warning: control reaches end of non-void function
    That warning says it's possible to finish the file_function and not actually return anything. So why might that return count; line be skipped? Now take a look at the file_list function you posted. Why are lines 47 and 48 inside the loop (the indentation makes it look like they aren't)?

  3. #3
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    if(dp == NULL)
    ...
    while(NULL != ep)
    Firstly, swapping operands is f-ugly to start with, even when it's applied consistently.
    When you mix and match - ooh look - a colourful (and stinking) keyboard appears.

    > *ls = calloc(count, sizeof(char *));
    Do not rely on calloc giving you a useful NULL pointer.
    Question 7.31
    I say this, because you inexplicably call free later on on what would seem to be a garbage pointer (if this code is broken on your machine).

    > free(*ls);
    And what about all the (*ls)[count] that have been allocated already?

    > if((file = open(ep->d_name, O_RDONLY)) < 0)
    Why are you opening the file?
    More importantly, why are you NOT closing it?
    Are you getting a lot of "too many open files" error messages?

    The error result of fstat() will tell you whether the file exists or not.

    > count = 0;
    Bad idea - you need this to make sure someone doesn't sneak in some extra files between reading the directory to count the files, and then reading it again to store the filenames.

    Your second loop is cumbersome, with having 3 calls to readdir
    Consider
    Code:
    int i = 0;
    while ( (i<count) && (ep = readdir(dp)) != NULL ) {
      if ( fstat(file, &fileStat) == 0 ) {
        if(S_ISDIR(fileStat.st_mode)){
          len = strlen(ep->d_name);
          (*ls)[i] = malloc(len+5); /* lunghezza stringa + "DIR \n" */
          strcpy((*ls)[i], "DIR "); /* copio DIR */
          strcat((*ls)[i], ep->d_name);
        } else {
          (*ls)[i] = strdup(ep->d_name);
        }
        i++;
      } else {
        // do something with fstat errors?
      }
    }
    // the actual number of entries stored, less than or equal to count
    return i;
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  4. #4
    Registered User
    Join Date
    May 2012
    Location
    Italy
    Posts
    53
    Quote Originally Posted by anduril462 View Post
    First, you should learn to use a debugger. Then you can simply step through the code line by line, and follow exactly what is happening, examine variables, etc.

    Second, you need to work on your indentation. It's there, but inconsistent. I personally find 2 spaces a little too close for comfort, making hard to distinguish the different levels of indentation. I suspect this is part of why you didn't spot your problem sooner.

    Third, crank up the warnings on your compiler. When I compiled your file_list function in gcc (with the -Wall flag to enable all warnings), I got the following:
    Code:
    $ gcc -Wall -g -std=c99 -pedantic  files.c   -o files
    files.c: In function ‘file_list’:
    files.c:61: warning: control reaches end of non-void function
    That warning says it's possible to finish the file_function and not actually return anything. So why might that return count; line be skipped? Now take a look at the file_list function you posted. Why are lines 47 and 48 inside the loop (the indentation makes it look like they aren't)?
    you have found the bug XD
    I have put the return in the wrong place!! Thanks a lot

  5. #5
    Registered User
    Join Date
    May 2012
    Location
    Italy
    Posts
    53
    Quote Originally Posted by Salem View Post
    if(dp == NULL)
    ...
    while(NULL != ep)
    Firstly, swapping operands is f-ugly to start with, even when it's applied consistently.
    When you mix and match - ooh look - a colourful (and stinking) keyboard appears.
    You're right xD i've written the code quickly and i haven't checked the syntax!

    Quote Originally Posted by Salem View Post
    > *ls = calloc(count, sizeof(char *));
    Do not rely on calloc giving you a useful NULL pointer.
    Question 7.31
    I say this, because you inexplicably call free later on on what would seem to be a garbage pointer (if this code is broken on your machine).
    Thanks for this tips, i'm going to change the calloc with malloc+memset

    Quote Originally Posted by Salem View Post
    > free(*ls);
    And what about all the (*ls)[count] that have been allocated already?
    But if free *ls i free all the *ls (from 0 to MAX)..maybe XD

    Quote Originally Posted by Salem View Post
    > if((file = open(ep->d_name, O_RDONLY)) < 0)
    Why are you opening the file?
    More importantly, why are you NOT closing it?
    Are you getting a lot of "too many open files" error messages?
    i'm opening for read because fstat need the file!
    O.o you're right i have to close it!!
    No i haven't!

    Quote Originally Posted by Salem View Post
    The error result of fstat() will tell you whether the file exists or not.

    > count = 0;
    Bad idea - you need this to make sure someone doesn't sneak in some extra files between reading the directory to count the files, and then reading it again to store the filenames.
    I know but i need a simplified version, it is for a school project!

    Quote Originally Posted by Salem View Post
    Your second loop is cumbersome, with having 3 calls to readdir
    Consider
    Code:
    int i = 0;
    while ( (i<count) && (ep = readdir(dp)) != NULL ) {
      if ( fstat(file, &fileStat) == 0 ) {
        if(S_ISDIR(fileStat.st_mode)){
          len = strlen(ep->d_name);
          (*ls)[i] = malloc(len+5); /* lunghezza stringa + "DIR \n" */
          strcpy((*ls)[i], "DIR "); /* copio DIR */
          strcat((*ls)[i], ep->d_name);
        } else {
          (*ls)[i] = strdup(ep->d_name);
        }
        i++;
      } else {
        // do something with fstat errors?
      }
    }
    // the actual number of entries stored, less than or equal to count
    return i;
    Thanks i'm going to merge my code

  6. #6
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Quote Originally Posted by polslinux View Post
    But if free *ls i free all the *ls (from 0 to MAX)..maybe XD
    Nope, you need one free for every malloc, and they happen in reverse order. You malloc the outer array *ls, then malloc (*ls)[0] to (*ls)[MAX], so you free (*ls)[0] to (*ls)[MAX], then free(*ls). Read this: Question 6.16

  7. #7
    Registered User
    Join Date
    May 2012
    Location
    Italy
    Posts
    53
    But i'm free the *ls into the while so if i'm using *ls[0] then i free(*ls[0]) etc!

  8. #8
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    > Thanks for this tips, i'm going to change the calloc with malloc+memset
    No, that IS calloc.
    A for loop setting (*ls)[i] = NULL; is what is needed to be safe.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  9. #9
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Why not use a structure to hold the response to various FTP commands? Something like the following
    Code:
    struct response {
        int     status;
        size_t  allocated;
        size_t  length;
        char    data[];
    };
    I added the FTP status code in there, but you can extend the structure as you wish. The main point is that allocated is the number of chars allocated for data (which is a flexible C99 array member), and length is the number of chars in the response, including CR LF, but not the optional terminating ASCII NUL byte.

    This way your FTP command functions all respond with a struct response; all the ftp command functions (LIST, RETR, PASV, and so on) have the same prototype,
    Code:
    struct response *ftpcmd_command(int argc, char *argv[]);
    All you really need is a printf() variant which can append to the above structure:
    Code:
    #define _POSIX_C_SOURCE 200809L
    #include <stdlib.h>
    #include <stdio.h>
    #include <stdarg.h>
    #include <errno.h>
    
    struct response {
        int     status;
        size_t  allocated;
        size_t  length;
        char    data[];
    };
    
    /* printf()-like function to append to the response. Initially, response may be NULL.
     * If the function returns NULL, the old response is discarded. Check errno for cause.
     * The data is always NUL-terminated.
    */
    struct response *append(struct response *response, const char *const format, ...)
    {
        struct response *temp;
        va_list          args;
        size_t           allocated;
        int              size = 0;
    
        /* NULL response? Allocate an initial block. */
        if (!response) {
            allocated = 1024;
    
            response = malloc(allocated + sizeof (struct response));
            if (!response) {
                errno = ENOMEM;
                return NULL;
            }
    
            response->status    = 0;
            response->allocated = allocated;
            response->length    = 0;
        }
    
        while (1) {
    
            va_start(args, format);
            size = vsnprintf(response->length + (char *)response->data,
                             response->allocated - response->length,
                             format, args);
            va_end(args);
    
            /* printf error? */
            if (size < 0) {
                if (response)
                    free(response);
                errno = EINVAL;
                return NULL;
            }
    
            /* Did it fit into the existing response buffer? */
            if (response->length + (size_t)size < response->allocated) {
                response->length += (size_t)size;
                /* vsnprintf() does always add the NUL byte. Still, we can be paranoid. */
                response->data[response->length] = 0;
                return response;
            }
    
            /* How much more should we allocate? Pad to next higher 1024 bytes. */
            allocated = ((response->length + (size_t)size) | 1023) + (size_t)1025;
            if (allocated < response->length || allocated < (size_t)size) {
                /* Oops, size overflow. */
                if (response)
                    free(response);
                errno = EMSGSIZE;
                return NULL;
            }
    
            /* Reallocate. */
            temp = realloc(response, allocated + sizeof (struct response));
            if (!temp) {
                if (response)
                    free(response);
                errno = ENOMEM;
                return NULL;
            }
    
            /* Update response. */
            response = temp;
            response->allocated = allocated;
        }                     
    }
    Last edited by Nominal Animal; 07-03-2012 at 08:25 PM.

  10. #10
    Registered User
    Join Date
    May 2012
    Location
    Italy
    Posts
    53
    What you have write it is too hard for me xD i'm "developing" in C since May 2012
    i have not completely understand "how and when to use struct" so i don't use them (for now)

  11. #11
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by polslinux View Post
    What you have write it is too hard for me xD i'm "developing" in C since May 2012
    i have not completely understand "how and when to use struct" so i don't use them (for now)
    Ah, sorry about that. An FTP client and/or server is a pretty big project, so I thought you might be already familiar with those.

    The technique I showed is probably a bit too complex to start with, because even the structure size changes during run time.. I'd say it's "advanced" material.

    C structs are extremely useful, but I'd recommend a simpler project to learn about them first. You'll get information overload if you try to do everything at once

    In any case I think experimentation is one of the best ways to learn, so good luck with your efforts!

  12. #12
    Registered User
    Join Date
    May 2012
    Location
    Italy
    Posts
    53
    In fact this is a (very small) ftp client and server. Only USER PASS LIST CD CWD RETR have been developed
    And also thanks to all, i will study struct better once i've finished this project!!

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. socket send
    By Drogin in forum Networking/Device Communication
    Replies: 8
    Last Post: 11-19-2009, 07:44 AM
  2. Send and Int Through Socket?
    By BENCHMARKMAN in forum C Programming
    Replies: 2
    Last Post: 03-16-2008, 06:58 AM
  3. send file via socket
    By beon in forum Networking/Device Communication
    Replies: 10
    Last Post: 05-21-2007, 09:12 PM
  4. send file through across socket
    By adk1283 in forum C++ Programming
    Replies: 4
    Last Post: 08-16-2005, 01:51 PM
  5. send zip file via socket
    By WaterNut in forum C Programming
    Replies: 11
    Last Post: 05-24-2005, 11:49 AM

Tags for this Thread