C - 无法在 FTP 客户端中连接被动模式

C - Cannot connect passive mode in FTP client

我正在编写一个 ftp 客户端。我有控制通道工作,因为我可以执行 CWD、PASV、USER 或 PASSWORD 等命令。

问题是当我想打开数据通道来执行例如 LIST (ls) 时。为此,我尝试使用与控制通道相同的功能调用 passive(pasv) 后打开到指定端口的另一个连接。 我认为连接正常,但我没有收到缓冲区的任何内容,程序一直在等待读取内容。我不确定使用相同的连接功能是否可以,但我认为它应该可以。

整个代码有点大。 comando_list 函数是麻烦之一 socket_connection 是我用来连接的那个。我想一定有麻烦

#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]='[=11=]';

    /* 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, '[=11=]', 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, '[=11=]', 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, '[=11=]', 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] = '[=11=]';
    //memset(serverName, '[=11=]', 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]='[=11=]';
    *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 = '[=11=]';
        }

        //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);

}

我终于解决了这个问题。我对 FTP 的工作原理有错误的看法。我知道对于像 LIST 这样的命令,协议使用数据通道,所以我也使用数据通道来发送命令,我应该使用控制通道来做到这一点。

我执行被动模式FTP的步骤是

  • 使用套接字(控制通道)连接到 FTP 服务器。
  • 当有人调用必须使用数据通道的命令时,通过控制通道发送 pasv 并获取端口和 IP。
  • 使用与第一步(数据通道)相同的功能与我之前获得的端口和 ip 建立新连接
  • 通过控制通道发送命令(LIST),然后在控制通道缓冲区中接收来自服务器的响应,最后从数据通道缓冲区中获取数据

    void command_list(int socketControl, char * bufferControl,int passivePort,char * ip){
    
      int socketData;
    
      /* Buffer to save the data received by data channel */
      char bufferData[MAXDATASIZE];
    
      //Make the data connection (this function is in the question)
      socket_connection(&socketData, bufferData, ip, passivePort);
    
      //Send the command through control socket (recived by parameter)
      send(socketControl,"list\r\n",strlen("list\r\n"), 0);
    
      //Receive confirmation message from control channel
      show_controlServer_answer(socketControl, bufferControl);
    
      //Now show the data that the server send through data channel
      show_dataServer_answer(socketData, bufferData);
    
      close(socketData);
    
    }
    
    void show_dataServer_answer(int socketData, char * bufferData){
    
      while(recv(socketData, bufferData, MAXDATASIZE, 0) > 0){
        //Show answer
        printf("%s",bufferData);
        //Clean buffer
        bzero(bufferData,MAXDATASIZE);
      }
    }
    
    int show_controlServer_answer(int socketControl, char * bufferControl){
    
      int numbytes;
    
      //Clean buffer
      bzero(bufferControl,MAXDATASIZE);
    
      if((numbytes = recv(socketControl, bufferControl,MAXDATASIZE, 0)) < 0){
        printf("show_controlServer_answer: Error en recv()\n");
        printf("%s\n", strerror(errno));
      }
    
      bufferControl[numbytes]='[=10=]';
    
      //Show answer
      printf("%s",bufferControl);
    
      //codigoRespuesta means replyCode 
      int codigoRespuesta = procesar_codigo_respuesta(bufferControl);
    
      //Get the hundred
      codigoRespuesta = codigoRespuesta / 100;
    
      //Clean buffer
      bzero(bufferControl,MAXDATASIZE);
    
      return codigoRespuesta;
    }
    
    int procesar_codigo_respuesta(char * bufferControl){
    
    char *copiaBuffer = malloc(sizeof(char)*MAXDATASIZE);
    strcpy(copiaBuffer, bufferControl);
    
    //get leading three digits
    copiaBuffer = strtok(copiaBuffer, " ");
    
    //convert to int
    int nro = atoi(copiaBuffer);
    
    free(copiaBuffer);
    
    return nro;
    }