[gull] Les requêtes DNS

Marc SCHAEFER schaefer at alphanet.ch
Sun Sep 12 12:31:02 CEST 2004


On Sun, Sep 12, 2004 at 11:17:49AM +0200, Arnaud Burlet wrote:
> et dans le cas d'un serveur dns qui fait (admettons) deux requêtes simultanées 
> (depuis 2 threads ou process différents) vers le même serveur dns, on a le 
> cas :
>     1.2.3.4:53 <----->> 4.3.2.1:53 (pour une premiere requête)
>     1.2.3.4:53 <----->> 4.3.2.1:53 (pour une seconde requête)
> est-ce possible ?

Même si le serveur et le clients sont multi-thread (et sous UNIX ils
seront plutôt single-threaded et multiplexés avec select(2)), un socket
donné (bind(2) à un port local) est un descripteur qui délivre des
données (TCP) ou des données délimitées par datagramme (UDP) de façon
séquentielle (voir plus bas sur l'atomicité de write(2) et de send(2)
selon POSIX).

Il n'y aura jamais simultanéité pour un seul socket.  Il y aura
peut-être un `dispatcher' au niveau du serveur DNS, s'il est
multithreadé: la réception sera single-threaded, le traitement
parallèle. Si le client utilise un port statique (genre 53) plutôt qu'un
port fantôme > 1023, il doit gérer en interne une table de question et
gérer les réponses en fonction.  C'est le cas de BIND9.

Un client peut d'ailleurs s'attendre à recevoir une réponse ne provenant
pas du bon serveur, ou correspondant à aucune question données, pour
diverses raisons (p.ex. attaque). Notamment il ne devrait pas
enregistrer dans son cache des réponses à des questions non posées.
Certaines versions du serveur DNS pour Microsoft Windows avaient ce
problème (et je crois aussi d'anciennes versions 4 de BIND), qui
s'appelle `cache poisoning'.

Pour TCP, c'est différent. Une requête sera précédée d'une ouverture à 3
phases de connexion, et la réponse suivie d'une fermeture de connexion.

En théorie on peut donc effectuer deux opérations successives dans la
mesure où la connexion est bien fermée correctement entre deux.

> - deux clients qui se connectent à ce serveur avec chacun le même port TCP 
> comme source.

impossible (voir plus bas).

> Tout fonctionne jusqu'à l'appel connect() du deuxième client qui échoue, errno 
> indiquant : "Cannot assign requested address".

Il y a ici un bind(2) implicite qui échoue forcément. Un seul processus
peut bind(2)er sur tuple (port local, adresse locale).

Le seul cas qui peut aboutir à un partage d'un socket: si ce processus
lance des fils (fork(2)), ils peuvent partager le socket, mais il y aura
sérialisation (write(2) ou send(2) sont atomiques: l'atomicité signifie,
sous POSIX, que s'il n'est pas possible d'envoyer p.ex. 1024 bytes maintenant,
on envoie ce qu'on peut à TCP et write(2) retourne moins de bytes que
prévu. Il faut donc boucler. Cela est vrai pour tous les périphériques
de type caractère, les tty, etc).

> Ce n'est pas clair dans ce message d'erreur, mais on peut en déduire qu'une 
> connection est bien identifiée par 
>     (addresse source, adresse destination; port source, port destination)

En fait ici, ce qui coince c'est la partie locale (port source, adresse
source).

> Si je reviens à mon exemple en-dessus, un serveur dns a donc un mécanisme qui 
> empèche ce type de situation, ou qui les détecte ?

Cette situation ne se produit pas.

Pour ajouter à la complexité, au niveau du serveur il y a un mécanisme
qui empêche la réutilisation d'un socket pendant un délai (p.ex. 2
minutes), sauf si une option particulière (SO_REUSEADDR) apparue dans
SYSVR4 si je me souviens bien est spécifiée pour le socket.

La raison de ce mécanisme est pour permettre, si je me souviens bien,
des cas étranges de fermeture de connexion incorrects. Le délai est
quelque chose comme 2 * le temps de vie du segment.

Il y a d'ailleurs un état, pour le serveur, dans lequel la paire de
connexion reste toujours dans le système (voir netstat): si le client ne
ferme pas correctement (pour éviter la perte de données). La plupart des
systèmes violent le standard TCP et ont également un timeout à ce niveau
pour éviter des attaques de type DoS.




More information about the gull mailing list