// server.cpp : Defines the entry point for the console application. //
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <netdb.h>
#include "pthread.h"
#include <string.h>
#include <arpa/inet.h>
#include <signal.h>

typedef struct threadinfo{

int socket;
struct sockaddr_in infosocket;
int comando;
char * comando_data;
unsigned int host;

int esesclavo;
} threadinfo;

typedef struct tcontrolinfo{
unsigned int hostesclavo;
unsigned int hostcontrolador;
unsigned int portcontrolador;
unsigned int portesclavo;
unsigned int hostftp;
int socket;
pthread_cond_t * condpas;
int * condpasvariable;
}tcontrolinfo;

threadinfo gwdatoscomunicador;

int socketprincipal; //utilizado solo para SIGINT ke lo cierre void * thread_dataport(tcontrolinfo *); void * threadprincipalftp(threadinfo *); void * mandacontrolthread(tcontrolinfo *); void acaba(int);
int mandacontrol(int,int,int,int,int);
int nthread;

pthread_cond_t condpasive;
//pthread_cond_t comunicador_haydatos;
pthread_mutex_t lock1;
int haydatos;

pthread_t threadkrap;
unsigned int comunicador_host;

unsigned int dsthost;
unsigned int localhost;
unsigned int dsthostftp;
int dstport;
int localport;
int iport;

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

        int ilens;
        int soket=0,sok2=0;
        struct sockaddr_in sin;
        struct sockaddr addr_accept;
        threadinfo * puntinfothread;
        int on;
        //signal(SIGINT,acaba);
        iport=7001;
        nthread=0;
        //inicializamos mutexes para comunicador master
        pthread_mutex_init (&lock1, NULL);
        pthread_cond_init (&condpasive, NULL);
        //pthread_cond_init (&comunicador_nohaydatos, NULL);
        //pthread_mutex_lock(&comunicador_lock);
        //haydatos=0;
        //pthread_cond_signal (&comunicador_nohaydatos);
        //pthread_mutex_unlock (&comunicador_lock);
        //pthread_create (&threadkrap, NULL, comunicador_master, 0);
        if (argc<5 )
        {
                printf("eid0 inverse conection ftp emulator listener, \n usage: %s localip localport targetipcontroller targetport targetipftp\nRemember that the inverse conection hub must be running and the ipdest must be connected\n",argv[0]);
                return 0;
        }

        //printf("Iniciando socket hacia fichero %s",argv[1]);
        dsthost=inet_addr(argv[3]);
        //dsthostftp=inet_addr(argv
        dstport=atoi(argv[4]);
        localport=atoi(argv[2]);
        localhost=inet_addr(argv[1]);
        if (argc==6) dsthostftp=inet_addr(argv[5]);
        else dsthostftp=inet_addr("127.0.0.1");
                memset (&sin,0,sizeof(sin));
                sin.sin_family=PF_INET;
                sin.sin_port=htons(localport);
                //sin.sin_port=htons(1500);
                //char * target_ip=argv[1];
                //char target_ip[60]="192.168.0.1";
                sin.sin_addr.s_addr=INADDR_ANY;
                if ((soket=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))==-1)
                {
                        perror("error de socket");
                        return 0;
                }
                socketprincipal=soket;
                        on=1;
        setsockopt(soket, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
                if (bind(soket,(const struct sockaddr *) &sin,sizeof(sin)))
                {
                        perror("error en bind");
                                return 0;
                }

                if (listen(soket,3))
                {
                        printf("error en listen");
                                return 0;
                }
                printf("bindeado en port %s\n",argv[2]);

ilens=sizeof(struct sockaddr_in);
//contathread=0;
while (1)
{

                if ((sok2=accept(soket,&addr_accept,&ilens))==-1)
                {
                        perror("error en accept");
                        return 0;
                }
                printf("main: llamando a threadprincipal\n");
                puntinfothread=malloc(sizeof(threadinfo));
                puntinfothread->socket=sok2;
                //puntinfothread->threadid=contathread;
                memcpy(&(puntinfothread->infosocket),&addr_accept,sizeof(struct sockaddr));
                pthread_create(&threadkrap, NULL, threadprincipalftp,(void *)puntinfothread);

}
return 0;
}

void * threadprincipalftp(threadinfo * infothread) {
int soket1,soket2,soket3;
char buffer[4096];
int locport;
int ilens;
struct sockaddr_in sin2;
struct sockaddr addr_accept;
pthread_cond_t condpas;
int condpasvariable;
fd_set fdsr;
int nbyt;
char * i1,*i3;
unsigned int i2,i4;
char buffernumero[50];
int numero[6];
int puertopasive;
int psoket; //sokets del server
pthread_t threadheader;
//threadinfo * puntinfothread;
char * buffer2; //buffer ke guardara informacion modificadat tcontrolinfo * controlinfo;
soket1=(infothread)->socket;
free(infothread);
//char pasivo[200];
//mandacontrol(srcport,dsthost,dstport); pthread_cond_init (&condpas, NULL);
printf("threadprincipalftp: iniciado\n");

        if ((soket2=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))==-1)
          {
            perror("error de socket");
            return 0;
          }
 comienzobind:
        memset (&sin2,0,sizeof(struct sockaddr_in));
        sin2.sin_family=PF_INET;
        pthread_mutex_lock(&lock1);
        sin2.sin_port=htons(iport);
        locport=iport++;
        pthread_mutex_unlock(&lock1);
        sin2.sin_addr.s_addr=INADDR_ANY;

                if (bind(soket2,(const struct sockaddr *) &sin2,sizeof(struct sockaddr)))
                {
                        perror("error en bind");
                        goto comienzobind;
                }
                mandacontrol(dsthost,localhost,locport,dstport,dsthostftp);
                if (listen(soket2,3))
                {
                        printf("error en listen");
                                goto comienzobind;
                }
                printf("principalftp: bindeado en port %i\n",locport);

ilens=sizeof(struct sockaddr_in);
//contathread=0;

if ((soket3=accept(soket2,&addr_accept,&ilens))==-1) {

     perror("error en accept");
     goto comienzobind;

}
printf("principalftp: Se ha conectado el esclavo!!\n"); while (1) {

FD_ZERO(&fdsr);
//FD_ZERO(&fdse);
FD_SET(soket1,&fdsr);
//FD_SET(soket1,&fdse);
FD_SET(soket3,&fdsr);
//FD_SET(soket3,&fdse);
if (select(soket3+1, &fdsr, NULL, NULL, NULL) == -1) {

      perror("principal: select");
      goto quit;

}
if (FD_ISSET(soket1,&fdsr)){

      if ((nbyt = recv(soket1,buffer,4095,0)) <= 0)
        {
          shutdown(soket3,1);
        goto quit;
        }
      if ((send(soket3,buffer,nbyt,0)) <= 0)
        goto quit;

}
if (FD_ISSET(soket3,&fdsr)) {

      if ((nbyt = recv(soket3,buffer,4095,0)) <= 0)
        {
          shutdown(soket1,1);
          goto quit;
        }
      buffer[nbyt]=0;
      if ((i1=strstr(buffer,"227 Ent")))
      {
        printf("ENCONTRANDO PASV!! PASAMOS A CAMBIARLO!\n");
        if ((i1=strchr(i1,'('))==0)
        {perror("error de PASV");
        //goto sinpasv;
        }
        i4=(unsigned int)i1;
        for (i2=0;i2<5;i2++)
        {
          i1++;
          i3=strchr(i1,',');
          memcpy(buffernumero,i1,i3-i1);
          buffernumero[i3-i1]=0;
          numero[i2]=atoi(buffernumero);
          i1=i3;
         }
        i3=strchr(i1,')');
        i1++;
        memcpy(buffernumero,i1,i3-i1);
        buffernumero[i3-i1]=0;
        numero[i2]=atoi(buffernumero);
        //printf("numeros:%d,%d,%d,%d,%d,%d\n",numero[0],numero[1],numero[2],numero[3],numero[4],numero[5]);

        //y ahora bindeamos un puerto nuevo
        if ((psoket=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))==-1)
          {
            perror("error de socket");
            return 0;
          }
 comienzobind2:
        memset (&sin2,0,sizeof(struct sockaddr));
        sin2.sin_family=PF_INET;
        pthread_mutex_lock(&lock1);
        sin2.sin_port=htons(iport);
        puertopasive=iport++;
        pthread_mutex_unlock(&lock1);
        sin2.sin_addr.s_addr=INADDR_ANY;
        if (bind(psoket,(const struct sockaddr *) &sin2,sizeof(struct sockaddr)))
          {
            perror("error en bind");
            goto comienzobind2;
          }

//ahora crearemos un thread nuevo ke eskuche el el data port

        condpasvariable=0;
        controlinfo=malloc(sizeof(tcontrolinfo));
        //mandacontrol(dsthost,localhost,locport,dstport);
        controlinfo->socket=psoket;
        controlinfo->hostesclavo=dsthost;
        controlinfo->hostcontrolador=localhost;
        controlinfo->portcontrolador=puertopasive;
        controlinfo->portesclavo=((numero[4]<<8)|numero[5]);
        controlinfo->condpas=&condpas;
        controlinfo->condpasvariable=&condpasvariable;
        printf("portesclavo original=%u\n",controlinfo->portesclavo);
        //pthread_mutex_lock(&lock1);
        //condpas=0;
        //pthread_mutex_unlock(&lock1);
        pthread_create(&threadheader, NULL, thread_dataport,(void *)controlinfo);

        //ahora sustituiremos el comando
        buffer2=malloc(4096);
        memcpy(buffer2,buffer,i4-(unsigned int)buffer);//copiamos hasta (
        sprintf(buffernumero,"(%u,%u,%u,%u,%u,%u",((unsigned char*)&localhost)[0],((unsigned char*)&localhost)[1],((unsigned char*)&localhost)[2],((unsigned char*)&localhost)[3],((unsigned char*)&puertopasive)[1],((unsigned char*)&puertopasive)[0]);
        printf("comando pasive modificado a %s\n",buffernumero);

        memcpy((char *)((unsigned int)buffer2+(unsigned int)i4-(unsigned int)buffer),buffernumero,strlen(buffernumero)+1);//copiamos el 0 para markarlo
        i4=strlen(buffer2);
        //printf("prueba: ->%s\n",buffer2);
        //printf("-->%i,%i\n",1+nbyt-(unsigned int)i3+(unsigned int)buffer,i4);
        memcpy(&(buffer2[i4]),i3,1+nbyt-(unsigned int)i3+(unsigned int)buffer);
        //memcpy(&(buffer2[i4]),i3,1+nbyt-(unsigned int)i3+(unsigned int)buffer);
        i4+=1+nbyt-(unsigned int)i3+(unsigned int)buffer;//nuevo tamaño de pakete de salida
        buffer2[i4]=0;

        //ahora tenemos ke esperar a bindear el puerto
        pthread_mutex_lock(&lock1);
        while (condpasvariable==0)
          pthread_cond_wait(&condpas,&lock1);
        pthread_mutex_unlock(&lock1);
        condpasvariable=0;
        sleep(2);
        if ((send(soket1,buffer2,i4,0)) <= 0)
        goto quit;
        free(buffer2);
      }//if strstr(227 PASSIVE)
      else
      if ((send(soket1,buffer,nbyt,0)) <= 0) goto quit;

}
}

quit:
//shutdown(osock,2);
printf("principal: cerrando sockets\n"); //shutdown(soket2,1);
//shutdown(soket3,1);
//shutdown(soket1,1);
close(soket3);
close(soket3);
close(soket1);
pthread_exit(0);
}

void * thread_dataport(tcontrolinfo * control) {
int psoket,psoket2,psoket3,ilens;
int nbyt;
fd_set fdsr;
struct sockaddr addr_accept;
char buffer[10000];
int retselect;
unsigned int hostesclavo;
unsigned int hostcontrolador;
unsigned int hostftp;
int portesclavo;
int portcontrolador;
hostftp=control->hostftp;
hostesclavo=control->hostesclavo;
hostcontrolador=control->hostcontrolador; portcontrolador=control->portcontrolador; portesclavo=control->portesclavo;

// printf("threaddataport: mensaje control con datos %u,%u,%u,%u,%u\n",hostesclavo,hostcontrolador,portcontrolador,portesclavo,dsthostftp);

nbyt=0; //quita un warning;
psoket=control->socket;

//decimos al principal ke ya le puede decir al cliente ke se conecte pthread_mutex_lock(&lock1);
*(control->condpasvariable)=1;
pthread_cond_signal(control->condpas); pthread_mutex_unlock(&lock1);

if (listen(psoket,3)==-1)

                {
                        printf("error en listen,data port acabado con error\n");
                        close(psoket);
                         return 0;
                }

ilens=sizeof(struct sockaddr_in);
//contathread=0;

if ((psoket2=accept(psoket,&addr_accept,&ilens))==-1) {

     perror("error en accept");
     return 0;
     //goto quit2;

}
printf("ftpdata: Se ha conectado el cliente\n");

mandacontrol(hostesclavo,hostcontrolador,portcontrolador,portesclavo,dsthostftp);

//ahora el stream ha mandado un mensaje de control sobre el server, esperaremos ke se nos conecte if ((psoket3=accept(psoket,&addr_accept,&ilens))==-1) {

     perror("ftpdata: error en accept de server\n");
     goto quit2;

}
printf("ftpdata: Se ha conectado el server\n");

/* Wait until buffer is not full */
retselect=1;
while (1) {

FD_ZERO(&fdsr);
//FD_ZERO(&fdse);
FD_SET(psoket2,&fdsr);
//FD_SET(psoket2,&fdse);
FD_SET(psoket3,&fdsr);
//FD_SET(psoket3,&fdse);
if ((select(psoket3+1, &fdsr, NULL, NULL, NULL)) == -1) {

      perror("principal: select");
      goto quit2;

}
if (retselect !=0) printf("dataport:Tenemos datos del select\n"); if (FD_ISSET(psoket2,&fdsr)){

      if ((nbyt = recv(psoket2,buffer,9999,0)) <= 0)
        {
          shutdown(psoket3,1);
          goto quit2;
        }
      if ((send(psoket3,buffer,nbyt,0)) <= 0)
        goto quit2;
     if (retselect !=0) printf("dataprt:Tenemos datos del select2\n");

}
if (FD_ISSET(psoket3,&fdsr)){

      if ((nbyt = recv(psoket3,buffer,9999,0)) <= 0)
        {
          shutdown(psoket,1);
        goto quit2;

        }

if ((send(psoket2,buffer,nbyt,0)) <= 0) goto quit2; if (retselect !=0) printf("dataprt:Tenemos datos del select3\n"); }
retselect=0;
}//while(1)
quit2:
free(control);
//shutdown(psoket,1);
//shutdown(psoket2,1);
//shutdown(psoket3,1);
//shutdown(psoket,1);
//close(psoket);
close(psoket2);
close(psoket3);
close(psoket);
printf("acabado conexion dataport\n"); pthread_exit(0);
//return 0;
}

int mandacontrol(hostesclavo,hostcontrol,portcontrol,portesclavo,hostftp) {
tcontrolinfo *controlinfo;
controlinfo=malloc(sizeof(tcontrolinfo)); controlinfo->hostesclavo=hostesclavo; controlinfo->hostcontrolador=hostcontrol; controlinfo->portcontrolador=portcontrol; controlinfo->portesclavo=portesclavo; controlinfo->hostftp=hostftp;
pthread_create(&threadkrap, NULL, mandacontrolthread,(void *)controlinfo); return 0;
}

void * mandacontrolthread(tcontrolinfo * control) {
int soket;
char pakete[100];
struct sockaddr_in sin;
unsigned int hostesclavo;
unsigned int hostcontrolador;
unsigned int hostftp;
int portesclavo;
int portcontrolador;
hostesclavo=control->hostesclavo;
hostcontrolador=control->hostcontrolador; portcontrolador=control->portcontrolador; portesclavo=control->portesclavo;
hostftp=control->hostftp;
free(control);
memset(&sin,0,sizeof(struct sockaddr)); sin.sin_family=AF_INET;
sin.sin_port=htons(7000);
sin.sin_addr.s_addr=inet_addr("127.0.0.1"); if ((soket=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))==-1)

perror("mandacontrol: socket");

if (connect(soket,(struct sockaddr *)&sin,sizeof(struct sockaddr))==-1)

perror("mandacontrol: connect");
sprintf(pakete,"CONTROL%03u.%03u.%03u.%03uREDIRECCIONAPUERTO%03u.%03u.%03u.%03u%05u%05u%03u.%03u.%03u.%03u\n",((unsigned char *)(&hostesclavo))[0],((unsigned char *)(&hostesclavo))[1],((unsigned char *)(&hostesclavo))[2],((unsigned char *)(&hostesclavo))[3],((unsigned char *)(&hostcontrolador))[0],((unsigned char *)(&hostcontrolador))[1],((unsigned char *)(&hostcontrolador))[2],((unsigned char *)(&hostcontrolador))[3],(unsigned int)portcontrolador,(unsigned int)portesclavo,((unsigned char *)(&hostftp))[0],((unsigned char *)(&hostftp))[1],((unsigned char *)(&hostftp))[2],((unsigned char *)(&hostftp))[3]); printf("mandando pakete de control: %s\n",pakete); send(soket,pakete,strlen(pakete),0);
shutdown(soket,2);
close(soket);
pthread_exit(0);
}

void acaba(senyal)
{
printf("Se acabo!!\n");
//close(socketprincipal);
exit(1);
}