/***********************************************************************/ /* Morgan Martinet (C) 1998 */ /* projet: realiser un client FTP (RFC959) */ /***********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /**********************/ /* Variables globales */ /**********************/ char * commandName; int ftpPort; char hostName[256]; struct sockaddr_in hostAddr; struct sockaddr_in dataAddr; struct sockaddr_in controlAddr; int controlSocket; int dataSocket; char derniereReponse[1024*8]; int connecte; /* 1 = Oui, 0 = Non */ int loginOk; /* 1 = Oui, 0 = Non */ char user[64]; char password[64]; char typeTransfert; /***************************/ /* Prototypes de fonctions */ /***************************/ void Initialisation(void); int Connecter( char *host ); void ToutFermer(void); int LireReponse(void); void EnvoyerCommande( char * cmd ); int QuitterFTP(void); int VerifierConnection(void); int Login( char * aUser, char * aPasswd ); int Cwd( char * hostDirectory ); int Pwd(void); int SupprimerFichier( char * fileName ); int ConnaitreSysteme( void ); int ChoisirType( char * type ); int ChoisirPort( void ); char * ObtenirNomLocal( char * fileName ); int InitDataConnection( void ); int LireFichier( char * cmd, char * fileName, int afficher ); int EcrireFichier( char * cmd, char * fileName ); void ExecuterCommandes(void); void AfficherAide(void); /***********************/ /* Fonction principale */ /***********************/ int main( int argc, char ** argv ) { int resultat = 0; commandName = argv[0]; Initialisation(); /* utiliser le premier argument de la commande comme nom de serveur FTP */ if ( argc > 1 ) resultat = Connecter( argv[1] ); /* si la connection a reussi, on essaie de s'authentifier */ if ( resultat ) Login( "", "" ); /* on passe en mode commandes, jusqu'a ce que l'on tappe "bye" */ ExecuterCommandes(); /* Fin du programme */ ToutFermer(); return 0; } /********************/ /* Autres fonctions */ /********************/ void Initialisation(void) { struct servent *sp; /* interroger la liste des services, afin de trouver le service ftp */ /* on requiert la couche tcp car le protocole FTP ne */ /* fonctionne qu'avec elle. */ sp = getservbyname("ftp", "tcp"); if (sp == NULL) { fprintf(stderr, "%s: service 'ftp/tcp' inconnu.\n", commandName ); exit(1); } /* recuperer le port utilise par le service ftp */ /* le port est deja au format NetworkOrder, donc il n'est pas */ /* necessaire d'utiliser la fonction htons */ ftpPort = sp->s_port; } int Connecter( char *host ) { struct hostent *hp = 0; int len; /* Si on etait deja connecte, on ferme tout */ if ( connecte ) ToutFermer(); /* On essaie d'obtenir des informations sur le serveur FTP struct hostent qui contient: le nom officiel du site (h_name) la liste d'alias (h_aliases) le type d'adresse du site (h_addrtype) la longueur de l'adresse (h_length) la liste d'adresse (h_addr_list) */ hp = gethostbyname(host); if (hp == NULL) { fprintf(stderr, "%s: %s: ", commandName, host); /* Imprimer le message d'erreur genere par gethostbyname */ herror((char *)NULL); return 0; } /* On recopie le type d'adresse dans notre structure adresse (sockaddr_in) pour se connecter au serveur */ hostAddr.sin_family = hp->h_addrtype; /* Puis on recopie le contenu de la premiere adresse, dans notre structure */ if (hp->h_length > sizeof(hostAddr.sin_addr)) { hp->h_length = sizeof(hostAddr.sin_addr); } memcpy(&hostAddr.sin_addr, hp->h_addr_list[0], hp->h_length); /* On recopie le nom officiel du site dans notre variable globale hostName pour connaitre a tout moment le nom du site auquel nous sommes connectes*/ (void) strncpy(hostName, hp->h_name, sizeof(hostName)); hostName[sizeof(hostName)-1] = 0; /* On cree notre socket de controle avec comme parametres: domaine = celui de l'adresse du site que l'on a obtenu precedemment (generalement AF_INET = ARPA Internet Protocols) type = STREAM car c'est le mode obligatoire pour le FTP protocol = 0 car on utilise le protocole par defaut du type STREAM (TCP) */ controlSocket = socket(hostAddr.sin_family, SOCK_STREAM, 0); if (controlSocket < 0) { fprintf( stderr, "%s: socket", commandName ); perror(""); return 0; } /* On definit le port attribue au FTP dans notre structure sockaddr_in, car une connexion entre socket necessite une adresse reseau + un port */ hostAddr.sin_port = ftpPort; /* On essaie de se connecter au serveur FTP avec l'adresse et le port que l'on vient de definir */ if ( connect(controlSocket, (struct sockaddr *)&hostAddr, sizeof (hostAddr)) < 0 ) { fprintf( stderr, "%s: connect ", commandName ); perror(""); ToutFermer(); return 0; } /* On recupere des informations sur notre adresse */ len = sizeof (controlAddr); if (getsockname(controlSocket, (struct sockaddr *)&controlAddr, &len) < 0) { fprintf( stderr, "%s: getsockname", commandName ); perror(""); ToutFermer(); return 0; } /* Nous sommes a present connectes */ connecte = 1; printf("Connecte a %s.\n", hostName); /* Obtenir l'identification du serveur */ if ( LireReponse() != 220 ) { ToutFermer(); return 0; } /* Connection Ok */ return 1; } void ToutFermer(void) { QuitterFTP(); close( controlSocket ); connecte = 0; } int LireReponse(void) { int count = 0; char * tmp = derniereReponse; char * tmp2 = derniereReponse; int continuer = 1; /* Lire des donnees sur la socket de control */ while( continuer ) { tmp += count; count = recv( controlSocket, tmp, sizeof(derniereReponse), 0 ); if ( count > 0 ) { /* Ajouter un caractere nul a la fin de la chaine, pour marquer la fin */ if ( count < sizeof(derniereReponse) ) tmp[count] = 0; else derniereReponse[ sizeof(derniereReponse)-1 ] = 0; while( *tmp2 ) { if ( *tmp2 == '\n' ) { printf( "\n" ); continuer = *(tmp+3) == '-'; } else { putchar( *tmp2 ); } tmp2++; } } else /* echec */ return -1; } /* Renvoyer le code reponse */ return atoi(derniereReponse); } void EnvoyerCommande( char * cmd ) { char buffer[1024]; sprintf( buffer, "%s%c%c", cmd, 13, 10 ); /* cmd + CR + LF */ send( controlSocket, buffer, strlen(buffer), 0 ); } int QuitterFTP(void) { if ( connecte ) { EnvoyerCommande( "QUIT" ); if ( LireReponse() == 221 ) { loginOk = 0; return 1; } else return 0; } else return 0; } int VerifierConnection(void) { if ( ! connecte ) { fprintf( stderr, "Vous n'etes pas connecte a un serveur.\n" ); return 0; } if ( ! loginOk ) { fprintf( stderr, "Vous devez faire un login.\n" ); return 0; } return 1; } int Login( char * aUser, char * aPasswd ) { char cmd[1024]; int reponse; if ( ! connecte ) { fprintf( stderr, "Vous devez d'abord vous connecter a un serveur.\n" ); return 0; } if ( loginOk ) { fprintf( stderr, "Utilisateur '%s' en ligne.\n", user ); return 0; } /* Envoyer le nom de l'utilisateur */ printf( "Utilisateur: " ); if ( *aUser ) { sprintf( user, "%s", aUser ); printf( "%s\n", user ); } else scanf( "%s", user ); sprintf( cmd, "USER %s", user ); EnvoyerCommande( cmd ); reponse = LireReponse(); if ( reponse == 331 ) { /* Puisque le nom est accepte, on envoie le mot de passe */ printf( "Mot de passe: " ); if ( *aPasswd ) { sprintf( password, "%s", aPasswd ); printf( "%s\n", password ); } else scanf( "%s", password ); sprintf( cmd, "PASS %s", password ); EnvoyerCommande( cmd ); reponse = LireReponse(); } if ( reponse != 230 ) return 0; /* Echec */ /* L'utilisateur a ete accepte */ loginOk = 1; return 1; } int Cwd( char * hostDirectory ) { if ( ! VerifierConnection() ) return 0; if ( strlen( hostDirectory ) > 0 ) { char cmd[1024]; int reponse; sprintf( cmd, "CWD %s", hostDirectory ); EnvoyerCommande( cmd ); reponse = LireReponse(); if ( reponse != 250 ) return 0; /* Succes */ return 1; } else return 0; } int Pwd(void) { int reponse; if ( ! VerifierConnection() ) return 0; EnvoyerCommande( "PWD" ); reponse = LireReponse(); if ( reponse != 250 ) return 0; return 1; } int SupprimerFichier( char * fileName ) { if ( ! VerifierConnection() ) return 0; if ( strlen( fileName ) > 0 ) { char cmd[1024]; int reponse; sprintf( cmd, "DELE %s", fileName ); EnvoyerCommande( cmd ); reponse = LireReponse(); if ( reponse != 250 ) return 0; /* Echec */ /* Succes */ return 1; } else { fprintf( stderr, "del: il faut preciser un fichier" ); return 0; } } int ConnaitreSysteme( void ) { int reponse; if ( ! VerifierConnection() ) return 0; EnvoyerCommande( "SYST" ); reponse = LireReponse(); if ( reponse != 250 ) return 0; /* Echec */ /* Succes */ return 1; } int ChoisirType( char * type ) { char cmd[1024]; if ( ! VerifierConnection() ) return 0; sprintf( cmd, "TYPE %s", type ); EnvoyerCommande( cmd ); if ( LireReponse() != 200 ) return 0; /* Echec */ typeTransfert = *type; return 1; /* Succes */ } int ChoisirPort(void) { char buff[1024]; char *a; char *p; int reponse; if ( ! VerifierConnection() ) return 0; /* Preparer la commande PORT a l'aide de l'adresse de notre socket de transfert */ a = (char *)&dataAddr.sin_addr; p = (char *)&dataAddr.sin_port; #define UC(b) (((int)b)&0xff) /* On definit les 4 octets d'adresse + 2 octets de port */ sprintf( buff, "PORT %d,%d,%d,%d,%d,%d", UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); EnvoyerCommande( buff ); reponse = LireReponse(); if (reponse != 200) return 0; /* Echec */ /* Succes */ return 1; } char * ObtenirNomLocal( char * fileName ) { /* On enleve tout repertoire, pour ne garder que le nom de fichier */ char * resultat = fileName + strlen(fileName) - 1; while( (resultat != fileName) && (*resultat != '/') ) resultat--; return resultat; } /* Preparer le canal pour le transfert de donnees */ int InitDataConnection( void ) { int len; /* Creer la socket de transfert */ /* On recopie notre structure adresse recuperee lors de la creation de la socket de controle dans la structure adresse pour la socket de transfert */ dataAddr = controlAddr; dataAddr.sin_port = 0; /* on laisse le systeme choisir un port */ /* On cree une socket de domaine AF_INET (Arpa Internet Protocols), de type STREAM (obligatoire pour FTP), et avec le protocol par defaut (TCP) */ dataSocket = socket(AF_INET, SOCK_STREAM, 0); if (dataSocket < 0) { fprintf( stderr, "%s: socket", commandName ); perror(""); return 0; } /* Attacher la socket a l'adresse que l'on vient de definir, afin qu'elle soit accessible au serveur FTP */ if (bind(dataSocket, (struct sockaddr *)&dataAddr, sizeof (dataAddr)) < 0) { fprintf( stderr, "%s: bind", commandName ); perror(""); close( dataSocket ); dataSocket = -1; return 0; } /* Recuperer l'adresse de notre socket de transfert, pour que l'on puisse envoyer une requete PORT au serveur FTP, afin qu'il sache comment se connecter a nous */ len = sizeof( dataAddr ); if (getsockname(dataSocket, (struct sockaddr *)&dataAddr, &len) < 0) { fprintf( stderr, "%s: getsockname", commandName ); perror(""); close( dataSocket ); dataSocket = -1; return 0; } /* Ecouter sur la socket de transfert et definir le nombre maximum de connections autorisees (ici 1 car on attend qu'un seul client: le serveur FTP) */ if (listen(dataSocket, 1) < 0) { fprintf( stderr, "%s: listen", commandName ); perror(""); close( dataSocket ); dataSocket = -1; return 0; } /* Envoyer la commande PORT */ if ( ! ChoisirPort() ) { close( dataSocket ); dataSocket = -1; return 0; } /* Succes */ return 1; } int LireFichier( char * cmd, char * fileName, int afficher ) { int reponse; int s; int c; FILE * dest, * src; char buff[1024]; struct sockaddr_in from; size_t fromlen = sizeof(from); char *localFileName; if ( ! VerifierConnection() ) return 0; if ( ! afficher ) { /* Creer un fichier local pour stocker le transfert des donnees */ localFileName = ObtenirNomLocal( fileName ); dest = fopen( localFileName, "w" ); if ( ! dest ) { fprintf( stderr, "%s: impossible de creer le fichier '%s'", commandName, localFileName ); return 0; } } /* Creer la socket de transfert */ if ( ! InitDataConnection() ) return 0; /* Preparer la commande de transfert de fichier */ if ( strlen(fileName) > 0 ) sprintf( buff, "%s %s", cmd, fileName ); else sprintf( buff, "%s", cmd ); /* Envoyer la commande et tester sa reussite */ EnvoyerCommande( buff ); reponse = LireReponse(); if ( reponse != 150 ) { /* le serveur a refuse notre commande */ close( dataSocket ); dataSocket = -1; return 0; } /* accepter une nouvelle connection sur le port reserve au transfert. */ s = accept(dataSocket, (struct sockaddr *) &from, &fromlen); if (s < 0) { fprintf( stderr, "%s: accept", commandName ); perror(""); close(dataSocket); dataSocket = -1; return 0; } /* Fermer la socket a l'ecoute sur le port de transfert */ close(dataSocket); /* Recuperer la nouvelle connection, comme connection pour le transfert */ dataSocket = s; /* Creer un stream sur la socket de transfert */ src = fdopen( dataSocket, "r" ); if ( ! src ) { fprintf( stderr, "%s: fdopen", commandName ); perror( "" ); return 0; } /* Transferer les donnees du fichier */ while ( (c = fgetc(src)) != EOF ) { if ( afficher ) putchar( c ); else fputc( c, dest ); } /* Fermer les fichiers ouverts et la socket de transfert */ fclose( src ); close( dataSocket ); if ( ! afficher ) fclose( dest ); /* Verifier si le transfert s'est bien passe */ reponse = LireReponse(); if ( reponse != 226 ) return 0; /* Echec lors de la reception du fichier */ /* Succes */ return 1; } int EcrireFichier( char * cmd, char * fileName ) { int reponse; int s; int c; FILE * dest, * src; char buff[1024]; struct sockaddr_in from; size_t fromlen = sizeof(from); char *localFileName; if ( ! VerifierConnection() ) return 0; if ( ! ( src = fopen( fileName, "r" ) ) ) { fprintf( stderr, "%s: impossible d'ouvrir le fichier '%s'\n", commandName, fileName ); return 0; } localFileName = ObtenirNomLocal( fileName ); /* Creer la socket de transfert */ if ( ! InitDataConnection() ) return 0; /* Preparer la commande de transfert de fichier */ sprintf( buff, "%s %s", cmd, localFileName ); /* Envoyer la commande et tester sa reussite */ EnvoyerCommande( buff ); reponse = LireReponse(); if ( reponse != 150 ) { /* le serveur a refuse notre commande */ close( dataSocket ); dataSocket = -1; return 0; } /* accepter une nouvelle connection sur le port reserve au transfert. */ s = accept(dataSocket, (struct sockaddr *) &from, &fromlen); if (s < 0) { fprintf( stderr, "%s: accept", commandName ); perror(""); close(dataSocket); dataSocket = -1; return 0; } /* Fermer la socket a l'ecoute sur le port de transfert */ close(dataSocket); /* Recuperer la nouvelle connection, comme connection pour le transfert */ dataSocket = s; /* Creer un stream sur la socket de transfert */ dest = fdopen( dataSocket, "w" ); if ( ! dest ) { fprintf( stderr, "%s: fdopen", commandName ); perror( "" ); return 0; } /* Transferer les donnees du fichier */ while ( (c = fgetc(src)) != EOF ) fputc( c, dest ); /* Fermer les fichiers ouverts et la socket de transfert */ fclose( src ); fclose( dest ); close( dataSocket ); /* Verifier si le transfert s'est bien passe */ reponse = LireReponse(); if ( reponse != 226 ) return 0; /* Echec lors de la reception du fichier */ /* Succes */ return 1; } void ExecuterCommandes(void) { char line[1024]; char *cmd; char *arg; char *tmp; for ( ; ; ) { /* Lire une ligne de texte */ printf( "> " ); if ( ! fgets( line, sizeof(line), stdin ) || *line == '\n' ) { printf( "\n" ); continue; } /* Determiner l'argument de la commande */ cmd = line; arg = line; /* Chercher le permier espace */ while ( *arg && *arg > ' ' ) arg++; *arg = 0; arg++; /* Passer le(s) separateur(s) */ while( *arg && *arg <= ' ' ) arg++; tmp = arg; /* Tant que l'on ne rencontre pas de separateur */ while ( *tmp && *tmp >= ' ' ) tmp++; *tmp = 0; /* Tester la commande */ if ( strcmp( cmd, "bye" ) == 0 ) return; else if ( strcmp( cmd, "?" ) == 0 ) AfficherAide(); else if ( strcmp( cmd, "open" ) == 0 ) { if ( *arg ) Connecter( arg ); else fprintf( stderr, "open: vous devez fournir l'adresse d'un serveur ftp.\n" ); } else if ( strcmp( cmd, "user" ) == 0 ) Login( "", "" ); else if ( strcmp( cmd, "bin" ) == 0 ) ChoisirType( "I" ); else if ( strcmp( cmd, "ascii" ) == 0 ) ChoisirType( "A" ); else if ( strcmp( cmd, "pwd" ) == 0 ) Pwd(); else if ( strcmp( cmd, "cd" ) == 0 ) { if (*arg) Cwd( arg ); else fprintf( stderr, "cd: il faut preciser un repertoire" ); } else if ( strcmp( cmd, "ls" ) == 0 ) { ChoisirType( "A" ); LireFichier( "NLST", arg, 1 ); } else if ( strcmp( cmd, "dir" ) == 0 ) { ChoisirType( "A" ); LireFichier( "LIST", arg, 1 ); } else if ( strcmp( cmd, "type" ) == 0 ) { ChoisirType( "A" ); LireFichier( "RETR", arg, 1 ); } else if ( strcmp( cmd, "get" ) == 0 ) { if (*arg) { ChoisirType( "I" ); LireFichier( "RETR", arg, 0 ); } else fprintf( stderr, "get: il faut preciser un fichier" ); } else if ( strcmp( cmd, "put" ) == 0 ) { if (*arg) { ChoisirType( "I" ); EcrireFichier( "STOR", arg ); } else fprintf( stderr, "put: il faut preciser un fichier" ); } else if ( strcmp( cmd, "del" ) == 0 ) SupprimerFichier( arg ); else if ( strcmp( cmd, "syst" ) == 0 ) ConnaitreSysteme(); } } void AfficherAide(void) { printf( "Liste des commandes:\n" ); printf( "--------------------\n" ); printf( "\n" ); printf( "?...............cette aide\n" ); printf( "open ..se connecter au serveur \n" ); printf( "user............entrer un login et un mot de passe\n" ); printf( "bye.............quitter le programme\n" ); printf( "bin.............passer en mode binaire\n" ); printf( "ascii...........passer en mode texte\n" ); printf( "pwd.............afficher le repertoire courant\n" ); printf( "cd ........aller dans le repertoire \n" ); printf( "ls []......afficher le contenu du repertoire optionnel \n" ); printf( "dir [].....afficher le contenu du repertoire optionnel \n" ); printf( "type ..afficher le fichier \n" ); printf( "get ...obtenir le fichier \n" ); printf( "put ...envoyer le fichier \n" ); printf( "del ...supprimer le fichier \n" ); printf( "syst............Connaitre le systeme du serveur ftp\n" ); printf( "\n" ); }