From c534ef1682e985928a6a421bf6aa6d84a283bb4e Mon Sep 17 00:00:00 2001 From: xdrm-brackets Date: Tue, 15 Dec 2015 00:02:56 +0100 Subject: [PATCH] =?UTF-8?q?Connection=20des=20SOCKETS=20du=20BUS=20DE=20DO?= =?UTF-8?q?NNEES=20+=20Fermeture=20lors=20du=20code=20de=20succ=C3=A8s=20+?= =?UTF-8?q?=20LIST=20=C3=A0=20faire?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dep/client.c | 6 ++-- dep/server.c | 88 +++++++++++++++++++++++++++++++++------------------ dep/server.h | 21 ++++++++---- proxy_ftp.c | 26 +++++++++------ test | Bin 18600 -> 18719 bytes 5 files changed, 92 insertions(+), 49 deletions(-) diff --git a/dep/client.c b/dep/client.c index 7e05772..f9add28 100644 --- a/dep/client.c +++ b/dep/client.c @@ -85,7 +85,7 @@ void CLIENT_REQUEST(char* serverHost, char* serverPort, char* pRequest, char** p // memset(BUFFER, '\0', sizeof(BUFFER)); // on vide le buffer int nbRecup = sread(&SOCKET, BUFFER); - *pAnswer = malloc( sizeof(BUFFER) ); + *pAnswer = malloc( maxBuffLen ); strcpy(*pAnswer, BUFFER); if( DEBUGMOD ) printf("nbReceived: %d\n", nbRecup); @@ -183,9 +183,9 @@ void CLIENT_SEND(int* pSocket, char* pRequest, char** pAnswer){ /* [6] On lit la réponse =======================================================*/ - int nbRecup = sread(pSocket, BUFFER); + int nbRecup = WAIT_SOCKET_UPDATE(pSocket, BUFFER); - *pAnswer = malloc( sizeof(BUFFER) ); + *pAnswer = malloc( maxBuffLen ); strcpy(*pAnswer, BUFFER); if( DEBUGMOD ) printf("nbReceived: %d\n", nbRecup); diff --git a/dep/server.c b/dep/server.c index 8d845cd..3ed1d8a 100644 --- a/dep/server.c +++ b/dep/server.c @@ -112,7 +112,7 @@ void DROP_SERVER(char* serverHost, char** givenPort, int* listenSocket){ /* [7] On envoie les données par référence =======================================================*/ // port - *givenPort = malloc( sizeof(infoPort) ); + *givenPort = malloc( maxPortLen ); strcpy(*givenPort, infoPort); // socket d'écoute @@ -127,15 +127,8 @@ void DROP_SERVER(char* serverHost, char** givenPort, int* listenSocket){ // CMD: CLIENT (SRV) PROXY (CLT) FTP // DTA: CLIENT (CLT) PROXY (CLT) FTP // -// -// -// [1] Ecoute sur port p de localhost -// [2-n] Si commande, envoi à FTP -// Puis Reception de FTP, envoi à CLIENT - - -void MANAGE_REQUEST(int* SOCKET, char* pRequest){ - if( DEBUGMOD ) printf("====== MANAGE_REQUEST(%d, %s) ======\n\n", *SOCKET, pRequest); +void MANAGE_REQUEST(char* pRequest, int* DUSER_SOCKET, int* DFTP_SOCKET){ + if( DEBUGMOD ) printf("====== MANAGE_REQUEST(%s, %d, %d) ======\n\n", pRequest, *DUSER_SOCKET, *DFTP_SOCKET); char answer[maxBuffLen]; // contiendra la réponse int nbSend; // contiendra le nombre de données envoyées @@ -151,42 +144,46 @@ void MANAGE_REQUEST(int* SOCKET, char* pRequest){ /* [1] On découpe la requête en 2 parties ================================================*/ splitFtpRequest(pRequest, rCommand, rContent); - // strcpy(answer, strcat(rCommand, rContent) ); /* [2] Selection en fonction de @rCommand ================================================*/ - /* (1) Erreur de syntaxe + /* (1) PORT a1,a2,a3,a4,p1,p2 => utilisateur (actif) --------------------------------------------*/ - if( strcmp(rCommand, "ERROR") == 0 ) - strcpy(answer, "666 Tu t'es chié!!\n"); + if( strcmp(rCommand, "PORT") == 0 ){ + int a1, a2, a3, a4 = 0; + int p1, p2 = 0; - /* (2) Connection (username) - --------------------------------------------*/ - else if( strcmp(rCommand, "USER") == 0 ) - strcpy(answer, "331 C'est donc ça ton blase!\n"); + char serverHost[maxHostLen]; + char serverPort[maxPortLen]; + char BUFFER[maxBuffLen]; + + sscanf(pRequest, "PORT %d,%d,%d,%d,%d,%d", &a1, &a2, &a3, &a4, &p1, &p2); + // on récupère l'adresse en + sprintf(serverHost, "%d.%d.%d.%d", a1, a2, a3, a4); + // on récupère le port en + sprintf(serverPort, "%d", p1*256+p2); - /* (n) Commande inconnue - --------------------------------------------*/ - else - strcpy(answer, "??? Connè pô!\n"); + // on se connecte au client + CONNECT_CLIENT(serverHost, serverPort, DUSER_SOCKET); + printf("CONNECTED TO CLIENT %s:%s\n", serverHost, serverPort); + + // on envoie PASV car on veut être en mode passif entre le proxy et le serveur FTP + strcpy(pRequest, "PASV\n"); + } + // WAIT_SOCKET_UPDATE(DFTP_SOCKET, BUFFER); + // xPrint("FTPLIST: %s\n", BUFFER); /* [3] Envoi de la réponse ================================================*/ - formatBuffer(answer); - nbSend = write(*SOCKET, answer, strlen(answer)); - - /* [4] Fermeture de la SOCKET - ================================================*/ - // close(*SOCKET); } void MANAGE_RESPONSE(char** pAnswer, int* DUSER_SOCKET, int* DFTP_SOCKET){ - if( DEBUGMOD ) printf("====== MANAGE_RESPONSE(%d, %s) ======\n\n", *SOCKET, *pAnswer); + if( DEBUGMOD ) printf("====== MANAGE_RESPONSE(%s, %d, %d) ======\n\n", *pAnswer, *DUSER_SOCKET, *DFTP_SOCKET); char response[maxBuffLen]; // contiendra la réponse char ftpCode[4]; // contiendra le code FTP (1ère partie) @@ -239,6 +236,37 @@ void MANAGE_RESPONSE(char** pAnswer, int* DUSER_SOCKET, int* DFTP_SOCKET){ strcpy(response, EXIT_MSG); + /* (7) Mode passif => On lance les SOCKETS du BUS DE DONNEES + --------------------------------------------*/ + else if( strcmp(ftpCode, "227") == 0 ){ // on lance la SOCKET FTP du BUS DE DONNEES + strcpy(response, *pAnswer); + int a1, a2, a3, a4 = 0; + int p1, p2 = 0; + + char serverHost[maxHostLen]; + char serverPort[maxPortLen]; + char BUFFER[maxBuffLen]; + + sscanf(*pAnswer, "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)", &a1, &a2, &a3, &a4, &p1, &p2); + // on récupère l'adresse en + sprintf(serverHost, "%d.%d.%d.%d", a1, a2, a3, a4); + // on récupère le port en + sprintf(serverPort, "%d", p1*256+p2); + + // on se connecte au serveur FTP + CONNECT_CLIENT(serverHost, serverPort, DFTP_SOCKET); + + printf("CONNECTED TO FTP %s:%s\n", serverHost, serverPort); + } + + + /* (8) Fin de transfert de données + --------------------------------------------*/ + else if( strcmp(ftpCode, "226") == 0 ){ // on ferme les SOCKETS du BUS DE DONNEES + strcpy(response, *pAnswer); + close(*DUSER_SOCKET); + close(*DFTP_SOCKET); + } /* (n) Commande inconnue --------------------------------------------*/ @@ -255,7 +283,7 @@ void MANAGE_RESPONSE(char** pAnswer, int* DUSER_SOCKET, int* DFTP_SOCKET){ -int WAIT_CLIENT(int* pSocket, char* pBuffer){ +int WAIT_SOCKET_UPDATE(int* pSocket, char* pBuffer){ memset(pBuffer, 0, maxBuffLen); // on vide le buffer int nbRead = 0; diff --git a/dep/server.h b/dep/server.h index 219d4c5..b729c1f 100644 --- a/dep/server.h +++ b/dep/server.h @@ -10,13 +10,18 @@ void DROP_SERVER(char* serverHost, char** givenPort, int* listenSocket); -/* Gestion de la réponse au client +/* Gestion de la requête du client * -* @SOCKET Pointeur sur la SOCKET concernée -* @pRequest Requête reçue et à traiter +* @pBuffer Requête en question +* @DUSER_SOCKET Pointeur sur la SOCKET du BUS DE DONNEES utilisateur +* @DFTP_SOCKET Pointeur sur la SOCKET du BUS DE DONNEES FTP * +* @history +* [1] si "PORT a1,a2,a3,a4,p1,p2" on démarre la SOCKET CLIENT du BUS DE DONNEES +* [2] +* [3] */ -void MANAGE_REQUEST(int* SOCKET, char* pRequest); +void MANAGE_REQUEST(char* pRequest, int* DUSER_SOCKET, int* DFTP_SOCKET); @@ -27,11 +32,15 @@ void MANAGE_REQUEST(int* SOCKET, char* pRequest); * @DFTP_SOCKET Pointeur sur la SOCKET du BUS DE DONNEES FTP * * @history -* [1] si commande sans transfert nécessaire, on modifie la réponse -* [2] si on a besoin d'un transfert de données +* [1] SI commande sans transfert nécessaire, on modifie la réponse + +* [2] SI on a besoin d'un transfert de données * (1) On initialise les SOCKETS avec la valeur de PORT a,b,c,d,p1,p2 * (2) a.b.c.d (adresse ip) + p1*256+p2 (port) * (3) UTILISATEUR (ACIF) PROXY (PASSIF) SRV_FTP +* +* [3] SI 227, on lance la SOCKET FTP du BUS DE DONNEES (mode passif) +* [4] SI 226, on ferme les SOCKETS du BUS DE DONNEES * */ void MANAGE_RESPONSE(char** pAnswer, int* DUSER_SOCKET, int* DFTP_SOCKET); diff --git a/proxy_ftp.c b/proxy_ftp.c index 5f5509d..483ca69 100644 --- a/proxy_ftp.c +++ b/proxy_ftp.c @@ -21,8 +21,10 @@ static void* testServer(); /* CORPS DU PROG */ /*****************/ int main(int argc, char* argv[]){ - - testServer(); + if( argc > 1 ) + testServer(argv[1]); + else + testServer(""); return EXIT_SUCCESS; } @@ -39,7 +41,7 @@ int main(int argc, char* argv[]){ -static void* testServer(){ +static void* testServer(char* localPort){ int USER_SOCKET; // contiendra le BUS DE COMMANDE utilisateur int FTP_SOCKET; // contiendra le BUS DE COMMANDE FTP int DUSER_SOCKET; // contiendra le BUS DE DONNES utilisateur @@ -58,8 +60,12 @@ static void* testServer(){ /* [0] On démarre le SERVEUR + le CLIENT ==========================================================*/ - serverPort = malloc(4*sizeof(char)); - strcpy(serverPort, "4444"); + serverPort = malloc(maxPortLen); + + if( strlen(localPort) != 0 ) + strcpy(serverPort, localPort); + + DROP_SERVER(remoteHost, &serverPort, &LISTENSOCK); CONNECT_CLIENT(FTP_HOST, FTP_PORT, &FTP_SOCKET); @@ -74,7 +80,7 @@ static void* testServer(){ ============================================================================*/ strcpy(BUFFER, ""); CLIENT_SEND(&FTP_SOCKET, BUFFER, &ftp_response); - MANAGE_RESPONSE(&USER_SOCKET, &ftp_response); + MANAGE_RESPONSE(&ftp_response, &DUSER_SOCKET, &DFTP_SOCKET); swrite(&USER_SOCKET, ftp_response); xPrint("P->F: %s\n\n", BUFFER); @@ -85,7 +91,7 @@ static void* testServer(){ /* [3] On récupère les données reçues (+attente) ============================================================================*/ - if( WAIT_CLIENT(&USER_SOCKET, BUFFER) == -1 ) break; + if( WAIT_SOCKET_UPDATE(&USER_SOCKET, BUFFER) == -1 ) break; xPrint("C->P: %s\n", BUFFER); if( DEBUGMOD ) printf("Recu: %d\n", nbReceived); @@ -94,19 +100,19 @@ static void* testServer(){ /* [4] Traitement de la requête ============================================================================*/ - MANAGE_REQUEST(&BUFFER, &DUSER_SOCKET, &DFTP_SOCKET); + MANAGE_REQUEST(BUFFER, &DUSER_SOCKET, &DFTP_SOCKET); /* [5] Redirection vers le serveur FTP ============================================================================*/ - CLIENT_SEND(&FTP_SOCKET, BUFFER, &ftp_response); xPrint("P->F: %s\n\n", BUFFER); + CLIENT_SEND(&FTP_SOCKET, BUFFER, &ftp_response); xPrint("F->P: %s\n", ftp_response); /* [6] Traitement de la réponse (FTP) ============================================================================*/ - MANAGE_RESPONSE(&USER_SOCKET, &ftp_response, &DUSER_SOCKET, &DFTP_SOCKET); + MANAGE_RESPONSE(&ftp_response, &DUSER_SOCKET, &DFTP_SOCKET); /* [7] Redirection vers le CLIENT diff --git a/test b/test index f95e8599d1ff603a1495cc18ad1e301b63918fdf..e6cfa267b1dfd45a05ecafae436e6f75fd6130b7 100755 GIT binary patch delta 6379 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 delta 5591 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