Je suis Charlie

Autres trucs

Accueil

Seulement les RFC

Seulement les fiches de lecture

Ève

RFC 6891: Extension Mechanisms for DNS (EDNS(0))

Date de publication du RFC : Avril 2013
Auteur(s) du RFC : J. Damas (Bond Internet Systems), M. Graff, P. Vixie (Internet Systems Consortium)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dnsext
Première rédaction de cet article le 17 avril 2013


L'extension EDNS au traditionnel DNS a plus de treize ans et fête cette longue durée avec une nouvelle version, essentiellement cosmétique. EDNS a servi à faire sauter un certain nombre de barrières qui limitait la croissance du DNS, notamment l'antédiluvienne limite des réponses à seulement 512 octets.

Le protocole DNS, dans sa forme originale, spécifiée dans le RFC 1034, ne permettait pas de négocier des options, d'indiquer au serveur ce que sait faire le client, en plus des capacités minimales qu'impose le protocole. La norme originale (RFC 1035, section 2.3.4) imposait une limite de 512 octets aux messages DNS envoyés sur UDP. Une telle limite est bien trop basse, depuis longtemps, par exemple pour DNSSEC (section 3 de notre RFC), et ne correspondait pas aux capacités des réseaux et des machines modernes. Le DNS avait d'autres limites comme des champs de taille fixe, ne permettant qu'un petit nombre de valeurs possibles, désormais presque toutes définies (par exemple, avant EDNS, les codes de réponse - RCODE - ne faisaient que quatre bits, donc étaient presque tous épuisés).

EDNS0 est un mécanisme d'extension du DNS et une première extension, pour indiquer une taille supérieure aux 512 octets. L'extension se fait en squattant des champs inutilisés du paquet (DNS est un format binaire rigide, il ne permet donc pas facilement d'ajouter de nouvelles possibilités) et en créant un pseudo-type d'enregistrement, le type OPT. Déclaré comme indispensable par certaines autres extensions (notamment DNSSEC), EDNS fait aujourd'hui partie du bagage nécessaire à toute mise en œuvre du DNS.

L'extension pour indiquer la taille permet au client de spécifier la quantité d'octets qu'il est capable de recevoir (section 4.3). Avec le client DNS dig, cela se fait avec l'option bufsize (désormais activée par défaut dans les versions récentes de dig). Notre RFC recommande une valeur par défaut de 4 096 octets (section 6.2.5).

Prenons par exemple le TLD .hk car c'est un des plus gros en nombre de serveurs de noms. Si je demande cette liste :


% dig NS hk.

; <<>> DiG 9.8.1-P1 <<>> NS hk.
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 65212
;; flags: qr rd ra; QUERY: 1, ANSWER: 10, AUTHORITY: 0, ADDITIONAL: 14

;; QUESTION SECTION:
;hk.				IN	NS

;; ANSWER SECTION:
hk.			69658	IN	NS	X.HKIRC.NET.hk.
hk.			69658	IN	NS	T.HKIRC.NET.hk.
hk.			69658	IN	NS	S.HKIRC.NET.hk.
hk.			69658	IN	NS	A.HKIRC.NET.hk.
hk.			69658	IN	NS	B.HKIRC.NET.hk.
hk.			69658	IN	NS	W.HKIRC.NET.hk.
hk.			69658	IN	NS	Z.HKIRC.NET.hk.
hk.			69658	IN	NS	U.HKIRC.NET.hk.
hk.			69658	IN	NS	Y.HKIRC.NET.hk.
hk.			69658	IN	NS	V.HKIRC.NET.hk.

;; ADDITIONAL SECTION:
A.HKIRC.NET.hk.		69658	IN	A	203.119.2.18
B.HKIRC.NET.hk.		69658	IN	A	203.119.87.19
B.HKIRC.NET.hk.		69658	IN	AAAA	2001:dca:1000::cb77:5713
S.HKIRC.NET.hk.		69658	IN	A	128.32.136.3
S.HKIRC.NET.hk.		69658	IN	AAAA	2607:f140:ffff:fffe::3
T.HKIRC.NET.hk.		69658	IN	A	128.32.136.14
T.HKIRC.NET.hk.		69658	IN	AAAA	2607:f140:ffff:fffe::e
U.HKIRC.NET.hk.		69658	IN	A	210.201.138.58
U.HKIRC.NET.hk.		69658	IN	AAAA	2404:0:10a0::58
V.HKIRC.NET.hk.		69658	IN	A	204.61.216.46
V.HKIRC.NET.hk.		69658	IN	AAAA	2001:500:14:6046:ad::1
W.HKIRC.NET.hk.		69658	IN	A	202.12.28.140
W.HKIRC.NET.hk.		69658	IN	AAAA	2001:dc0:1:0:4777::140
X.HKIRC.NET.hk.		69658	IN	A	202.45.188.39

;; Query time: 1 msec
;; SERVER: 130.129.5.6#53(130.129.5.6)
;; WHEN: Tue Mar 12 19:30:33 2013
;; MSG SIZE  rcvd: 486

On voit que la réponse était proche des 512 octets et que, pour qu'elle tienne dans cette limite, le serveur a dû sérieusement réduire la taille de la section additionnelle (additional section). Si le serveur avait dû réduire encore plus, jusqu'à retirer des enregistrements de la section réponse (answer section), il aurait dû mettre le bit TC (troncation) à VRAI, imposant ainsi au client de reessayer en TCP.

Mais EDNS0 permet d'avoir la totalité de la section additionnelle (notez la pseudo-section lié à l'enregistrement OPT) :


% dig +bufsize=4096 NS hk.

; <<>> DiG 9.8.1-P1 <<>> +bufsize=4096 NS hk.
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 50455
;; flags: qr rd ra; QUERY: 1, ANSWER: 10, AUTHORITY: 0, ADDITIONAL: 20

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;hk.				IN	NS

;; ANSWER SECTION:
hk.			69632	IN	NS	Z.HKIRC.NET.hk.
hk.			69632	IN	NS	S.HKIRC.NET.hk.
hk.			69632	IN	NS	A.HKIRC.NET.hk.
hk.			69632	IN	NS	X.HKIRC.NET.hk.
hk.			69632	IN	NS	T.HKIRC.NET.hk.
hk.			69632	IN	NS	V.HKIRC.NET.hk.
hk.			69632	IN	NS	W.HKIRC.NET.hk.
hk.			69632	IN	NS	Y.HKIRC.NET.hk.
hk.			69632	IN	NS	U.HKIRC.NET.hk.
hk.			69632	IN	NS	B.HKIRC.NET.hk.

;; ADDITIONAL SECTION:
A.HKIRC.NET.hk.		69632	IN	A	203.119.2.18
B.HKIRC.NET.hk.		69632	IN	A	203.119.87.19
B.HKIRC.NET.hk.		69632	IN	AAAA	2001:dca:1000::cb77:5713
S.HKIRC.NET.hk.		69632	IN	A	128.32.136.3
S.HKIRC.NET.hk.		69632	IN	AAAA	2607:f140:ffff:fffe::3
T.HKIRC.NET.hk.		69632	IN	A	128.32.136.14
T.HKIRC.NET.hk.		69632	IN	AAAA	2607:f140:ffff:fffe::e
U.HKIRC.NET.hk.		69632	IN	A	210.201.138.58
U.HKIRC.NET.hk.		69632	IN	AAAA	2404:0:10a0::58
V.HKIRC.NET.hk.		69632	IN	A	204.61.216.46
V.HKIRC.NET.hk.		69632	IN	AAAA	2001:500:14:6046:ad::1
W.HKIRC.NET.hk.		69632	IN	A	202.12.28.140
W.HKIRC.NET.hk.		69632	IN	AAAA	2001:dc0:1:0:4777::140
X.HKIRC.NET.hk.		69632	IN	A	202.45.188.39
X.HKIRC.NET.hk.		69632	IN	AAAA	2405:3001:1:58::1:39
Y.HKIRC.NET.hk.		69632	IN	A	137.189.6.21
Y.HKIRC.NET.hk.		69632	IN	AAAA	2405:3000:3:60::21
Z.HKIRC.NET.hk.		69632	IN	A	194.146.106.70
Z.HKIRC.NET.hk.		69632	IN	AAAA	2001:67c:1010:17::53

;; Query time: 1 msec
;; SERVER: 130.129.5.6#53(130.129.5.6)
;; WHEN: Tue Mar 12 19:30:59 2013
;; MSG SIZE  rcvd: 613

Et voilà, tout le monde est désormais content.

La section 6 décrit le pseudo-enregistrement OPT, dont la présence marque un paquet comme conforme à EDNS. Il est situé dans la section additionnelle du message DNS, a le type 41, le nom de domaine est forcément . (la racine), la classe est détournée de son rôle normal pour indiquer la taille des paquets que l'envoyeur pourra recevoir en retour et le champ TTL est également détourné de son usage normal pour offrir de nouvelles options et de nouveaux codes de retour (rappelez-vous qu'il n'y avait que quatre bits pour ces codes dans le DNS original). Pour l'instant, une seule nouvelle option, le bit DO (DNSSEC OK) qui indique la capacité de l'émetteur à traiter les signatures DNSSEC (il avait été normalisé dans le RFC 3225).

L'ex-champ TTL sert aussi à indiquer le numéro de version d'EDNS, zéro actuellement (d'où le nom EDNS0 qu'on utilise parfois). Programmeurs, attention, certaines API (par exemple celle de DNS Python) nécessitent d'indiquer le numéro de version pour activer EDNS et il faut donc indiquer zéro pour dire qu'on veut de l'EDNS, ce qui peut être déroutant.

Ensuite, les données peuvent contenir plusieurs options. À l'heure actuelle, l'écrasante majoritéé des paquets EDNS n'en contiennent aucune. Mais, si c'est le cas, elles sont codées en TLV, un code indiquant l'option (les valeurs possibles étant dans un registre IANA), une longueur et les données spécifiques à l'option. Un exemple d'une telle option est le NSID du RFC 5001.

On notera qu'EDNS est ce que le DNS appelle une extension hop by hop ce qui veut dire qu'elle s'applique entre deux logiciels adjacents, pas de bout en bout. Si un client DNS demande à un résolveur local, qui demande à son tour à un serveur de .re, les options EDNS mises par le client sont transmises uniquement au résolveur, qui fabriquera ensuite une requête différente (peut-être avec d'autres options), pour le serveur faisant autorité pour .re.

Encore aujourd'hui, il existe des serveurs qui ne gèrent pas EDNS. Par exemple, ceux de microsoft.com répondent FORMERR (Format Error) :


% dig +bufsize=4096 @ns2.msft.net. SOA microsoft.com.

; <<>> DiG 9.8.1-P1 <<>> +bufsize=4096 @ns2.msft.net. SOA microsoft.com.
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: FORMERR, id: 54935
...

La section 6.2.2 de notre RFC précise donc qu'un émetteur intelligent peut alors se rabattre sur du DNS classique et ne pas envoyer l'enregistrement OPT :


% dig @ns2.msft.net. SOA microsoft.com.              

; <<>> DiG 9.8.1-P1 <<>> @ns2.msft.net. SOA microsoft.com.
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 22484
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 2
;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;microsoft.com.			IN	SOA

;; ANSWER SECTION:
microsoft.com.		3600	IN	SOA	ns1.msft.net. msnhst.microsoft.com. 2013031202 300 600 2419200 3600
...

Au moins, les serveurs de microsoft.com répondent, même si c'est par un refus. Plus embêtant, les serveurs qui ne répondent plus du tout lorsque la requête est en EDNS, en général en raison d'une boîte noire mal programmée et mal gérée installée devant le serveur (un pare-feu par exemple : beaucoup d'administrateurs réseaux ne supportent pas que le serveur DNS marche bien et mettent donc une middlebox boguée devant, cf. section 8 de notre RFC, ainsi que le RFC 5625). Un logiciel comme BIND, lorsqu'il ne reçoit pas de réponse, réessaie sans EDNS pour voir si c'est cela la cause du problème. Cette possibilité est décrite en section 6.2.5 qui recommande d'essayer d'abord EDNS avec une taille plus petite (inférieure à la MTU, au cas où le problème soit lié à la fragmentation), puis enfin sans EDNS.

L'EDNS original, du RFC 2671, prévoyait également des nouvelles façons de stocker les composants d'un nom de domaine, en plus des deux méthodes DNS traditionnelles, comprimée ou non (sections 4.2 et 5 de notre RFC et section 4.1.4 du RFC 1035). Cette pratique a été très peu utilisée, en raison des difficultés de déploiement (cf. RFC 3363 et RFC 3364). Notre RFC 6891 abandonne donc cette possibilité et reclasse le RFC 2673 (le seul qui avait utilisé un nouveau type de composants, les « composants binaires ») dans le cimetière des RFC dépassés. C'est le principal changement de ce nouveau RFC (les autres étant plutôt des détails, cf. annexe A.) Notons aussi que ce nouveau RFC est désormais « Norme Internet » et plus simplement « Proposition de norme ».

Pour les amateurs de programmation, du code C d'analyse d'un paquet DNS contenant de l'EDNS est dans mon article « Décoder les paquets DNS capturés avec pcap ».

Quelques exemples de code pour finir. Pour analyser un enregistrement OPT d'EDNS, voir comment c'est fait dans DNSmezzo. Ensuite, pour envoyer des requêtes EDNS, en Go, avec godns :

const (
	EDNSBUFFERSIZE  uint16  = 4096
)
...
m := new(dns.Msg)
...
m.SetEdns0(EDNSBUFFERSIZE, true)

En Python, avec DNSpython (notez qu'on indique la version d'EDNS donc zéro pour activer EDNS n'est pas une erreur) :

message = dns.message.make_query(name, type, use_edns=0, payload=4096)

Et pour finir, en C, si vous assemblez le paquet à la main (il y a évidemment des façons plus simples de faire de l'EDNS), le code pourra ressembler à :

 /* OPT pseudo-RR */
 after_qname[4] = 0;     /* root domain */
 /* OPT = type 41 */
 after_qname[5] = 0;
 after_qname[6] = 41;
 /* Class stores the payload size */
 bufsize_wire = htons(bufsize);
 memmove(after_qname + 7, &bufsize_wire, sizeof(bufsize));
 /* TTL store the RCODE and flags */
 after_qname[9] = 0;
 after_qname[10] = 0;
 if (dnssec) {
     after_qname[11] = 128;      /* DO: request DNSSEC signatures */
 } else {
     after_qname[11] = 0;
 }
 after_qname[12] = 0;
 /* Resource data length (empty, here) */
 after_qname[13] = 0;
 after_qname[14] = 0;

Téléchargez le RFC 6891

Version PDF de cette page (mais vous pouvez aussi imprimer depuis votre navigateur, il y a une feuille de style prévue pour cela)

Source XML de cette page (cette page est distribuée sous les termes de la licence GFDL)