Up Next

1  Un mini-client/serveur ftp

Le protocole de communication à implanter est le suivant (il ne s’agit pas du vrai protocole utilisé par ftp) :

Côté client
Côté serveur

1.1  Les types de requêtes

Trois types de requêtes sont définies :

1.2  Réponses

Après avoir lu la requête, le serveur renvoie un accusé de réception au client. Cet accusé peut être positif (ANSWER_OK), négatif (ANSWER_ERROR) ou le serveur peut ne pas avoir compris la requête (ANSWER_UNKNOWN). En outre, le client peut ne jamais récupérer d’accusé, à cause d’une coupure de la connexion. Dans le cas d’un accusé négatif, le serveur fournit aussi un code d’erreur permettant d’identifier le problème (par exemple, l’échec peut provenir d’une tentative de récupération d’un fichier inconnu sur le serveur). Pour simplifier, le serveur renvoie la valeur de la variable errno suite à l’action qui a entraîné le rejet de la requête.

1.3  La requête

Dans les deux cas de transfert de fichier, il est nécessaire de connaître la taille du fichier transféré pour savoir si, lors d’un read détectant une fin de fichier (dû à la fermeture de la connexion), tout le contenu a bien été récupéré, ou si la connexion a été rompue trop tôt.

Une requête est donc de la forme :

#define REQUEST_PUT 1
#define REQUEST_GET 2
#define REQUEST_DIR 3
struct request {
    int  kind;
    char path[MAXPATH];
    int  nbbytes;        /* pour PUT seulement */
};

L’entier kind doit être REQUEST_PUT, REQUEST_GET ou REQUEST_DIR. La chaîne path contient le nom du fichier à écrire (put), lire (get) ou lister (dir). L’entier nbbytes contient la taille du fichier, lors d’un PUT seulement.

1.4  La réponse

Une réponse contient donc l’accusé (ack). Si la réponse est positive (ack vaut ANSWER_OK) et que la requête était un GET, nbbytes contient la taille du fichier que le serveur va envoyer. Si la réponse est négative (ANSWER_ERROR), errnum contient le code de l’erreur (valeur de la variable errno).

#define ANSWER_OK      0
#define ANSWER_UNKNOWN 1        /* requête inconnue */
#define ANSWER_ERROR   2        /* erreur lors du traitement */

struct answer {
    int  ack;
    int  nbbytes;  /* pour GET seulement */
    int  errnum;   /* significatif ssi != 0 et ack == ANSWER_ERROR */
};

1.5  Ligne de commande

Le serveur miniftpd est démarré sans argument.

L’exécution d’un client peut prendre l’une des formes suivantes :

miniftp hostname get distfilename localfilename
miniftp hostname put localfilename distfilename
miniftp hostname dir distpathname

1.6  Contrôle d’accès ?

Un serveur ftp doit théoriquement vérifier que les fichiers sont accédés avec les droits d’accès du client, et non pas ceux du serveur (d’où une phase initiale d’authentification avec mot de passe). Dans notre cas, le serveur accède aux fichiers sous l’uid de l’utilisateur qui a lancé ce serveur. Le client peut éventuellement être d’un autre uid, mais aucun contrôle n’est effectué.

1.7  Terminaison des processus fils

Pour gérer une requête, le serveur fork un nouveau processus. Il est intéressant de récupérer le code de retour de ce processus fils, pour pouvoir détecter, par exemple, une terminaison anormale (et cela évite en outre l’apparition de processus zombies). Il existe deux mécanismes de synchronisation avec la terminaison (qui peuvent être combinés) :

Rappel : lorsqu’un processus est bloqué par un appel système (par exemple read ou accept) et qu’un signal lui est envoyé, l’appel système échoue et renvoie -1, avec le code d’erreur EINTR (dans la variable errno).


Up Next