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);
}
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
Send the comand (LIST) through the control channel, then receive a response from server in the control channel buffer and finally get the data from data channel buffer
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]='\0';
//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;
}