csocketsftppassive-mode

C - Cannot connect passive mode in FTP client


I am coding a ftp client. I have the control channel working because I can do commands like CWD, PASV, USER or PASSWORD.

The problem is when i want to open the data channel to do for example LIST (ls). For this I try to open another connection to the port specified after calling passive(pasv) with the same function I used for the control channel. I think the connection is working but I am not receiving anything to the buffer and the program stay like waiting for something to read. I am not sure if using the same connection function is okay, but I think it should work.

The entire code is a bit large. The comando_list function is the trouble one and the socket_connection is the one I use to do the connection. Trouble has to be there I think

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/errno.h>
#include <assert.h>


/*Bajar cliente UPnP para manejar las conexiones que vienen desde afuera hacia adentro

/*para obtener el tamaño del archivo usando stat()*/
#include <sys/stat.h>

/*for O_RDONLY*/
#include<fcntl.h>

#define PORT 21 /* El puerto que será abierto */
#define MAXDATASIZE 6000

int * passive;
char * global;
void printHelp(){
    printf("USO DE CLIENTE FTP\n");
    printf("Sintaxis: clienteFTP [servidor[:puerto]] [-h]\n");
    printf("servidor: nombre del servidor FTP al cual se desea conectar\n");
    printf("puerto: puerto al que se desea conectar en ese servidor (por defecto 21)\n");
    printf("-h para pedir ayuda\n");
    printf("\n");
    printf("COMANDOS DISPONIBLES\n");
    printf("QUIT: Finalizar la ejecucion del cliente\n");
    printf("OPEN: Establecer una conexion al servidor especificado como argumento. Este comando tiene como segundo parametro"
    "opcional el puerto al cual establecer la conexion\n");
    printf("USER: Especificar el usuario bajo el cual se desea establecer la conexion\n");
    printf("PASS: Enviar la contraseña en el caso de que el servidor asi lo requiera\n");
    printf("PWD: Solicitarle al servidor que nos indique en que directorio nos encontramos actualmente\n");
    printf("CD: Solicitar al servidor que cambie al directorio especificado como argumento\n");
    printf("LIST: Solicitar al servidor que nos muestre los archivos disponibles en el directorio acutal\n");
    printf("PUT: Iniciar el envio al servidor del archivo indicado como argumento\n");
    printf("GET: Iniciar la recepcion del archivo indicado como argumento\n");
    printf("PASSIVE: Indicar al servidor que queremos hacer uso del modo de transferencia pasivo\n");
    printf("ACTIVE: Indicar al servidor que queremos hacer uso del modo de transferencia activo (por defecto)\n");
}

/* Mostrar por pantalla la respuesta del servidor*/
void show_server_answer(int * sockfd, char * buf){
    /*cantidad de bytes de la respuesta*/
    int numbytes;

        /* Obtener respuesta del server */
        if ((numbytes=recv(*sockfd,buf,MAXDATASIZE,0)) == -1){
            /* llamada a recv() */
            printf("Error en recv() \n");
        }

    buf[numbytes]='\0';

    /* muestra el mensaje del servidor =) */
    printf("Mensaje del Servidor: %s",buf);
}

int receiveData(int * sockfd, char * buf){
        int file_size;
        FILE *received_file;
         int remain_data = 0;
         ssize_t len;
    printf("llegue hasta aca\n");

         char* buffer[1000];
        /* Receiving file size*/ 
        if(recv(*sockfd, buf, MAXDATASIZE, 0)!=0){
                printf("Error\n");
        }
        file_size =atoi(buf);
        //fprintf(stdout, "\nFile size : %d\n", file_size);
    printf("llegue 2\n");
        received_file = fopen("archivo.txt", "w");
        if (received_file == NULL)
        {
                fprintf(stderr, "Failed to open file foo --> %s\n", strerror(errno));

                exit(EXIT_FAILURE);
        }

        remain_data = file_size;

        while (((len = recv(*sockfd, buf, BUFSIZ, 0)) > 0) && (remain_data > 0))
        {
                fwrite(buf, sizeof(char), len, received_file);
                remain_data -= len;
                fprintf(stdout, "Receive %d bytes and we hope :- %d bytes\n", len, remain_data);
        }
        fclose(received_file);

        close(sockfd);

}



int socket_connection(int * sockfd, char * buf, char * server_name, int puerto ){

    /*estructura que recibirá información sobre el servidor*/
    struct hostent *server_host = malloc(sizeof(server_host));

    /* información sobre la dirección del servidor */
    struct sockaddr_in* server = malloc(sizeof(server));

    /*cantidad de bytes de la respuesta*/
    int numbytes;

    /* obtener datos del host a través de su nombre */         
    server_host = gethostbyname(server_name);
    if (server_host == NULL){
        perror("nombre de servidor desconocido\n");
        exit(-1);
    }

    printf("puerto en conexion %i\n",puerto);
    /* Estructura con datos del server */
    server->sin_family = AF_INET;
    bzero(&(server->sin_zero),8);
    server->sin_port = htons((short) puerto);
    server->sin_addr = *((struct in_addr *) server_host->h_addr);

    /* Obtener un descriptor de archivo para el socket */
    if ((*sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
         perror("error al querer abrir el socket\n");
         exit(-1);
    }


    /* Conectar al servidor, que ya debe estar configurado */
    if(connect(*sockfd, (struct sockaddr *) server, sizeof(struct sockaddr)) == -1){
         printf("%s\n", strerror(errno));
         printf("error al querer establecer la conexión con el server\n");
         exit(-1);
    }

    if(*passive==0){
        printf("passive: %i\n",*passive);
        show_server_answer(sockfd, buf);
    }
    else{
        printf("passive: %i\n",*passive);

    }
    *passive=0;

}


char** str_split(char* a_str, const char a_delim, int * cantidad){

    char** result    = 0;
    size_t count     = 0;
    char* tmp        = a_str;
    char* last_comma = 0;
    char delim[2];
    delim[0] = a_delim;
    delim[1] = 0;

    /* Count how many elements will be extracted. */
    while (*tmp)
    {
         if (a_delim == *tmp)
         {
                 count++;
                 last_comma = tmp;
         }
         tmp++;
    }

    /* Add space for trailing token. */
    count += last_comma < (a_str + strlen(a_str) - 1);

    /* Add space for terminating null string so caller
        knows where the list of returned strings ends. */
    count++;

    *cantidad = count -1;

    result = malloc(sizeof(char*) * count);

    if (result)
    {
         size_t idx  = 0;
         char* token = strtok(a_str, delim);

         while (token)
         {
                 assert(idx < count);
                 *(result + idx++) = strdup(token);
                 token = strtok(0, delim);
         }
         assert(idx == count - 1);
         *(result + idx) = 0;
    }

    return result;
}

int hash_function(char * comando){
    int value = -1;
    if (strcmp(comando,"quit") == 0) {
     value = 1;
    }
    if (strcmp(comando,"open") == 0) {
     value = 2;
    }
    if (strcmp(comando,"user") == 0) {
     value = 3;
    }
    if (strcmp(comando,"pass") == 0) {
     value = 4;
    }
    if (strcmp(comando,"pwd") == 0) {
     value = 5;
    }
    if (strcmp(comando,"cd") == 0) {
     value = 6;
    }
    if (strcmp(comando,"list") == 0) {
     value = 7;
    }
    if (strcmp(comando,"put") == 0) {
     value = 8;
    }
    if (strcmp(comando,"get") == 0) {
     value = 9;
    }
    if (strcmp(comando,"passive") == 0) {
     value = 10;
    }
    if (strcmp(comando,"active") == 0) {
     value = 11;
    }
    return value;
}

void comando_pwd(int sockfd, char * buf){
    send(sockfd,"pwd\r\n",strlen("pwd\r\n"),0);
  show_server_answer(&sockfd,buf);
}

void comando_user(int sockfd, char * nombreComando, char** argumentos) {
//  printf("%s %s\n",argumentos[0],argumentos[1]);

    //Armar comando
    /*char comandoCompleto[100];
    memset(comandoCompleto, '\0', sizeof(comandoCompleto));

    strcpy(comandoCompleto, "USER");
        printf("%s\r\n",comandoCompleto);

    strcat(comandoCompleto, " ");
        printf("%s\r\n",comandoCompleto);

    //strcat(comandoCompleto, argumentos[1]);
        printf("%s\r\n",comandoCompleto);

    strcat(comandoCompleto, "\r\n");
        printf("%s\r\n",comandoCompleto);

    printf("%s\r\n",comandoCompleto);*/

    send(sockfd, "USER anonymous\r\n", strlen("USER anonymous\r\n"), 0);
    show_server_answer(&sockfd,nombreComando);
}

void comando_pass(int sockfd, char * nombreComando, char** argumentos){
    //Armar comando
    /*char comandoCompleto[100];
    memset(comandoCompleto, '\0', sizeof(comandoCompleto));

    strcpy(comandoCompleto, "PASS");
    strcat(comandoCompleto, " ");
    //strcat(comandoCompleto, argumentos[1]);
    strcat(comandoCompleto, "\r\n");

    printf("%s\r\n",comandoCompleto);
*/

    send(sockfd, "PASS anonymous\r\n", strlen("PASS anonymous\r\n"), 0);
    show_server_answer(&sockfd,nombreComando);
}

void comando_cd(int sockfd, char * buf,char ** argumentos, int cantArgumentos){
    if(cantArgumentos != 2){
        printf("USO: cd <carpeta/directorio>");
    }
    else{
        //Armar comando
        char comandoCompleto[100];
        memset(comandoCompleto, '\0', sizeof(comandoCompleto));

        strcpy(comandoCompleto, "CD");
        strcat(comandoCompleto, " ");
        strcat(comandoCompleto, argumentos[1]);
        strcat(comandoCompleto, "\r\n");

        printf("%s",comandoCompleto);

        send(sockfd,comandoCompleto,strlen(comandoCompleto),0);
        show_server_answer(&sockfd,buf);


    }

}

void comando_open(int sockfd, char * buf){
    send(sockfd,"OPEN ftp.debian.com\r\n",strlen("OPEN ftp.debian.com\r\n"),0);
    show_server_answer(&sockfd,buf);
}

void comando_list(int passivePort,char * ip){
    /* ficheros descriptores */
    int sockfd;

    /* en donde es almacenará el texto recibido */
    char buffer[MAXDATASIZE];

    //char serverName[100];
    //serverName[0] = '\0';
    //memset(serverName, '\0', sizeof(serverName));

    //strcat(serverName,"ftp.debian.com\n");

    //Using a global variable to have the server for now
    //printf("%s\n", global);
    socket_connection(&sockfd, buf,global,passivePort); 
    send(sockfd,"list\r\n",strlen("list\r\n"),0);

    recv(socket, buf, MAXDATASIZE, 0); //Here the program keeps waiting
    printf("%s",buf);

    //show_server_answer(&sockfd,buf);

    close(sockfd);

}

void RemoveSpaces(char* source)
{
  char* i = source;
  char* j = source;
  while(*j != 0)
  {
    *i = *j++;
    if(*i != ' ')
      i++;
  }
  *i = 0;
}

int getPort(char * buf, char * ip){


    //remove whitespaces from buf

    RemoveSpaces(buf);
    int port=0;
    int i=0;
    char *ptr;
    printf("trim: %s\n",buf);

    ptr = strtok(buf,",");
        while(ptr!=NULL){
            if(i==0){
                strcat(ip,&ptr[23]);
                strcat(ip, ".");
            }
            if (i>0 && i < 3) {
            strcat(ip, ptr);
            strcat(ip, ".");
            }
            else if (i == 3) {
            strcat(ip, ptr);
            }
            else if(i==4){
                port+=atoi(ptr)*256;
            }
            else if(i==5){
                port+=atoi(ptr);
            }
            else{
                printf("error calculando puerto\n");
            }
            i++;
            ptr = strtok(NULL,",");

        }


    return port;




}


int comando_passive(int sockfd, char * buf, int * puerto, char * ip){
    send(sockfd,"pasv\r\n",strlen("pasv\r\n"),0);
    show_server_answer(&sockfd,buf);


    ip[0]='\0';
    *puerto = getPort(buf,ip);
    *passive=1;
    // *puerto = atoi(passivePort);
    printf("%s\n",ip);
     printf("%d\n",*puerto);
}


int runCommand(int * sockfd, char * buf,int * passivePort, char * ip){

    int cantArgumentos;
    char** argumentos;
    //argumentos = str_split(buf, ' ',&cantArgumentos);

    char comandoOriginal[100];
    strcpy(comandoOriginal, buf);

    char* nombreComando = strtok(buf, " ");

    printf("NC %s\n", nombreComando);
    printf("ori %s\n", comandoOriginal);


    switch (hash_function(nombreComando)) {
     case 2:
        comando_open(*sockfd,comandoOriginal);
        break;
     case 3:
        comando_user(*sockfd, comandoOriginal, argumentos);
        break;
     case 4:
         comando_pass(*sockfd, comandoOriginal, argumentos);
         break;
     case 5:
         comando_pwd(*sockfd, comandoOriginal);
         break;
     case 6:
         comando_cd(*sockfd,comandoOriginal, argumentos,cantArgumentos);
         break;
     case 7:
         comando_list(*passivePort,ip);
         break;
     case 10:
        comando_passive(*sockfd,comandoOriginal,passivePort,ip);
        break;
     default:
         printf("El comando no se reconoce como una accion ftp\n");
         break;
     }

}

int loop(int * sockfd, char * buf){
    char * ip = malloc(sizeof(ip));
    int * passivePort = malloc(sizeof(passivePort));
    int looping = 1; //true
    //comando_user(*sockfd,buf);
    //comando_pass(*sockfd,buf);
    while(looping){
        printf("clienteFTP>> ");
        fgets(buf,MAXDATASIZE,stdin);

        //Pasar a minuscula el comando
        int i;
        for(i = 0; buf[i]; i++){
            buf[i] = tolower(buf[i]);
        }

        //Borro el salto de linea del buf en caso de existir
        char * bufCR;
        if((bufCR = strrchr(buf, '\n')) != NULL){
            *bufCR = '\0';
        }

        //veo que comando es
        if(strcmp(buf, "exit") == 0){
            looping = 0;
        }
        else if (strcmp(buf, "help") == 0){
            printHelp();
        }
        else{
            runCommand(sockfd,buf,passivePort,ip);
        }
    }
}

int main(int argc, char *argv[]){

    passive=malloc(sizeof(passive));
    /* ficheros descriptores */
    int sockfd;

    /* en donde es almacenará el texto recibido */
    char buf[MAXDATASIZE];

    if (argc > 3) {
        /* Max Quantity of argumens */
        printf("Uso: %s [servidor[:puerto]][-h]\n",argv[0]);
        exit(-1);
    }

    global=argv[1];

    socket_connection(&sockfd, buf,argv[1],21);
    loop(&sockfd, buf);
    /* cerramos el descriptor de archivo */
    close(sockfd);

}

Solution

  • Finally I solve the problem. I was getting the wrong idea about how FTP works. I knew that for commands like LIST, the protocol uses the data channel, so I was using the data channel also to send the command, and I was supposed to use the control channel to do that.

    The steps I follow to do a passive mode FTP are