
Bibliothèque de recherche DNS rapide et outil en ligne de commande
ZDNS est un résolveur DNS haute vitesse et un utilitaire en ligne de commande pour effectuer des mesures DNS à grande échelle. ZDNS est écrit en Go et contient son propre code de résolution récursive ainsi qu'un cache optimisé pour effectuer des recherches sur un ensemble diversifié de noms. Nous utilisons https://github.com/zmap/dns pour construire et analyser les paquets DNS bruts. Pour plus d'informations sur l'architecture et les performances de ZDNS, consultez l'article suivant papier présenté à l'Internet Measurement Conference '22 de l'ACM.
[!TIP] Le Wiki ZDNS contient des informations supplémentaires sur ZDNS et parcourt des cas d'utilisation et des exemples.
ZDNS peut être installé en clonant le dépôt et en exécutant make install.
git clone https://github.com/zmap/zdns.git
cd zdns
make install
ZDNS se compose d'une bibliothèque de résolveur récursif library et d'un wrapper en ligne de commande CLI wrapper.
La bibliothèque consiste en une structure ResolverConfig qui contiendra toutes les options de configuration pour toutes les recherches effectuées.
Le ResolverConfig est utilisé pour créer une ou plusieurs structures Resolver qui effectueront toutes les recherches. Un Resolver
ne doit effectuer qu'une seule recherche à la fois (il n'est pas thread-safe) et plusieurs structures Resolver doivent être
utilisées pour le parallélisme. Consultez nos exemples pour savoir comment utiliser la
bibliothèque. Les Modules sont utilisés pour définir le comportement des recherches.
ZDNS fournit plusieurs types de modules :
Modules DNS bruts fournissent la réponse DNS brute du serveur, similaire à dig, mais en JSON. Il existe un module pour (presque) chaque type d'enregistrement DNS.
Modules de recherche fournissent des réponses plus utiles lorsque plusieurs requêtes sont
nécessaires (par exemple, effectuer une recherche A supplémentaire pour les adresses IP si une NS est
reçue dans NSLOOKUP).
Modules divers fournissent d'autres moyens supplémentaires d'interroger les serveurs (par exemple,
bind.version).
Nous détaillons les modules ci-dessous :
Les modules A, AAAA, AFSDB, ANY, ATMA, AVC, AXFR, BINDVERSION, CAA, CDNSKEY, CDS, CERT, CNAME, CSYNC, DHCID, DMARC, DNSKEY, DS, EID, EUI48, EUI64, GID, GPOS, HINFO, HIP, HTTPS, ISDN, KEY, KX, L32, L64, LOC, LP, MB, MD, MF, MG, MR, MX, NAPTR, NID, NINFO, NS, NSAPPTR, NSEC, NSEC3, NSEC3PARAM, NSLOOKUP, NULL, NXT, OPENPGPKEY, PTR, PX, RP, RRSIG, RT, SVCBS, MIMEA, SOA, SPF, SRV, SSHFP, TALINK, TKEY, TLSA, TXT, UID, UINFO, UNSPEC et URI fournissent la réponse DNS brute sous forme JSON, similaire à dig.
Par exemple, la commande :
echo "censys.io" | zdns A
retourne :
{
"name": "censys.io",
"results": {
"A": {
"data": {
"additionals": [
{
"flags": "",
"type": "EDNS0",
"udpsize": 512,
"version": 0
}
],
"answers": [
{
"answer": "104.18.10.85",
"class": "IN",
"name": "censys.io",
"ttl": 300,
"type": "A"
},
{
"answer": "104.18.11.85",
"class": "IN",
"name": "censys.io",
"ttl": 300,
"type": "A"
}
],
"protocol": "udp",
"resolver": "[2603:6013:9d00:3302::1]:53"
},
"duration": 0.285295416,
"status": "NOERROR",
"timestamp": "2024-08-23T13:12:43-04:00"
}
}
}
Les réponses DNS brutes ne fournissent souvent pas les données dont vous avez besoin. Par exemple,
une réponse MX peut ne pas inclure les enregistrements A associés dans la section
supplémentaire, nécessitant une recherche supplémentaire. Pour combler cette lacune et fournir une
interface plus conviviale, nous proposons également plusieurs modules de recherche : alookup,
mxlookup et nslookup.
alookup agit de manière similaire à nslookup et suivra les enregistrements CNAME.
mxlookup effectuera en plus une recherche A pour les adresses IP correspondant à un enregistrement d'échange.
nslookup effectuera en plus une recherche A/AAAA pour les adresses IP correspondant à un enregistrement NS.
Par exemple,
echo "censys.io" | zdns mxlookup --ipv4-lookup
retourne :
{
"name": "censys.io",
"results": {
"MXLOOKUP": {
"data": {
"exchanges": [
{
"class": "IN",
"ipv4_addresses": [
"209.85.202.27"
],
"name": "alt1.aspmx.l.google.com",
"preference": 5,
"ttl": 300,
"type": "MX"
},
{
"class": "IN",
"ipv4_addresses": [
"142.250.31.26"
],
"name": "aspmx.l.google.com",
"preference": 1,
"ttl": 300,
"type": "MX"
}
]
},
"duration": 0.154786958,
"status": "NOERROR",
"timestamp": "2024-08-23T13:10:11-04:00"
}
}
}
ZDNS prend également en charge les requêtes DNS spéciales de « débogage ». Les modules incluent : BINDVERSION.
ZDNS permet de fournir une entrée dans différents formats selon le comportement souhaité.
L'entrée la plus basique est une liste de noms séparés par des sauts de ligne. Par exemple :
Depuis l'entrée standard :
echo "google.com\nyahoo.com" | zdns A
cat list_of_domains.txt | zdns A
Depuis un fichier
zdns A --input-file=list_of_domains.txt
Si vous n'avez pas besoin de résoudre beaucoup de domaines, fournir le domaine comme argument CLI, similaire à dig, est pris en charge pour la facilité d'utilisation.
Par exemple :
zdns A google.com --name-servers=1.1.1.1
Équivalent à dig -t A google.com @1.1.1.1
Normalement, ZDNS choisira un serveur de noms aléatoire pour chaque recherche de domaine parmi --name-servers. Si vous souhaitez plutôt spécifier
un serveur de noms différent pour chaque domaine, vous pouvez le faire en fournissant des paires domainName,nameServerIP séparées par des sauts de ligne.
Cela remplacera tout serveur de noms fourni avec --name-servers.
Par exemple :
echo "google.com,1.1.1.1\nfacebook.com,8.8.8.8" | zdns A
Vous pouvez voir que le resolver est celui spécifié pour chaque domaine dans la sortie (additionals/answers tronqués par souci de concision) :
$ echo "google.com,1.1.1.1\nfacebook.com,8.8.8.8" | zdns A
{"name":"google.com","results":{"A":{"data":{"additionals":...,"answers":[...],"protocol":"udp","resolver":"1.1.1.1:53"},"duration":0.030490042,"status":"NOERROR","timestamp":"2024-09-13T09:51:34-04:00"}}}
{"name":"facebook.com","results":{"A":{"data":{"additionals":[...],"answers":[...],"protocol":"udp","resolver":"8.8.8.8:53"},"duration":0.061365459,"status":"NOERROR","timestamp":"2024-09-13T09:51:34-04:00"}}}
Les fichiers de zone (provenant d'ICANN CZDS ou équivalent) peuvent être utilisés comme source d'entrée pour ZDNS avec le drapeau --zone-file. Cela permet d'analyser les fichiers de zone à partir de l'entrée standard (par défaut) ou d'un fichier avec le drapeau --input-file.
Par défaut, ZDNS extraira uniquement le nom de chaque enregistrement du fichier de zone. Si vous souhaitez également résoudre les noms référencés dans la section réponse des types d'enregistrements tels que les CNAME ou les NS, vous pouvez utiliser le drapeau CLI --zone-file-include-targets.
Par exemple, avec --zone-file-include-targets et cette entrée de fichier de zone DNS :
example.com. 3600 IN NS ns1.example.com
example.com et ns1.example.com seraient tous deux résolus.
ZDNS prend également en charge la transmission de « déclencheurs » par ligne d'entrée qui mappent les lignes d'entrée à des modules spécifiques. Avec ceux-ci, vous pouvez spécifier que certains domaines soient recherchés avec des modules spécifiques.
Le format d'entrée est :
domain_name,name_server,trigger,trigger_2,etc, où nameServer peut être vide pour utiliser les serveurs de noms par défaut et 1+ déclencheurs peuvent être spécifiés.
Un exemple de fichier input.csv :
example.com,,a-trigger
google.com,,a-trigger,cname-trigger
example.com,1.1.1.1,aaaa-trigger
yahoo.com
apnews.com,1.1.1.1
Et le fichier multiple.ini correspondant :
; Specify Global Options here
[Application Options]
iterative=true
prefer-ipv6-iteration="true"
; List out modules and their respective module-specific options here. A module can only be listed once
[A]
trigger = "a-trigger"
[AAAA]
trigger = "aaaa-trigger"
[CNAME]
trigger = "cname-trigger"
Cela va rechercher :
example.com avec le module A en utilisant les serveurs de noms par défautgoogle.com avec les modules A + CNAME en utilisant les serveurs de noms par défautexample.com avec le module AAAA en utilisant le résolveur 1.1.1.1 de Cloudflareyahoo.com avec tous les modules spécifiés et en utilisant les serveurs de noms par défautapnews.com avec tous les modules spécifiés et en utilisant le résolveur 1.1.1.1 de CloudflareExécutez la commande :
zdns MULTIPLE --multi-config-file="./multiple.ini" --input-file="input.csv"
ZDNS peut soit fonctionner contre un résolveur récursif (par exemple, un serveur DNS d'organisation) [comportement par défaut], soit effectuer sa propre récursion en interne. Si vous effectuez un petit nombre de recherches (par exemple, des millions) et utilisez moins de 10 000 goroutines, il est généralement plus rapide d'utiliser l'un des résolveurs récursifs courants comme Cloudflare ou Google. Cloudflare est presque toujours plus rapide que Google. Cela est particulièrement vrai si vous recherchez des noms populaires car ils sont mis en cache et peuvent être répondus en un seul aller-retour. Lorsque vous utilisez des dizaines de milliers de threads simultanés, envisagez d'effectuer l'itération en interne afin d'éviter de surcharger et/ou de limiter le débit de votre résolveur récursif.
Pour effectuer une récursion locale, exécutez zdns avec le drapeau --iterative. Lorsque ce
drapeau est utilisé, ZDNS effectuera un round-robin entre les serveurs racines publiés (par exemple,
198.41.0.4). En mode itératif, vous pouvez contrôler la taille du cache local en
spécifiant --cache-size et le délai d'attente pour les itérations individuelles en définissant
--iteration-timeout. Le drapeau --timeout contrôle le délai d'attente de l'ensemble de la
résolution pour une entrée donnée (c'est-à-dire la somme de toutes les étapes itératives).
Les performances de ZDNS proviennent d'une parallélisation massive utilisant des goroutines légères Go. Cette architecture comporte plusieurs mises en garde :
Chaque goroutine utilise son propre socket réseau dédié. Ainsi, vous devez être
en mesure d'ouvrir autant de sockets (en termes de nombre maximum de descripteurs de fichiers et de
ports éphémères) que vous avez de threads spécifiés (via --threads). Par défaut,
ZDNS utilise 1 000 threads, ce qui est inférieur au nombre maximum par défaut de Linux de 1024
FD ouverts. Cependant, il est supérieur à la valeur par défaut de Mac OS qui est de 256. Vous pouvez consulter
le nombre maximum de FD ouverts (et donc de sockets) autorisé en exécutant ulimit -n. Si
vous souhaitez exécuter avec un nombre de threads supérieur à ce nombre, vous devez
augmenter le nombre de fichiers ouverts au niveau du système d'exploitation. Si vous ne le faites pas,
vous rencontrerez une erreur fatale similaire à FATA[0000] unable to create socketlisten udp <client IP address>:0: socket: too many open files. Si vous
souhaitez exécuter plus de threads que vous n'avez de ports éphémères disponibles, vous devrez
utiliser plusieurs adresses IP client : --local-addr=A,B,C.
Par défaut, ZDNS « réutilise » les sockets UDP en créant un socket UDP non lié pour chaque routine légère au lancement et en l'utilisant pour toutes les requêtes (indépendamment de l'IP de destination). Cela améliore considérablement les performances car ZDNS et le système d'exploitation hôte n'ont pas besoin de configurer et de démonter un socket pour envoyer chaque paquet (car les requêtes/réponses DNS ont tendance à être un paquet chacune). Cependant, cela signifie que ZDNS pré-allouera un socket pour chaque thread au lancement. Cela peut ne pas être optimal si vous ne recherchez qu'un petit nombre de noms. Par exemple, si vous n'avez besoin de rechercher que 100 noms, mais que vous utilisez les 1 000 threads par défaut, vous lierez mais n'utiliserez jamais 900 sockets UDP. Au lieu de vous soucier du recyclage des sockets, nous vous recommandons de spécifier un nombre raisonnable de threads pour votre cas d'utilisation (car cela évite également tout travail pour démarrer ces threads en premier lieu). C'est pourquoi, cependant, vous pouvez obtenir une erreur concernant l'impossibilité d'ouvrir un grand nombre de sockets même si vous ne recherchez qu'un seul nom. S'il est important de créer un nouveau socket pour chaque requête, vous pouvez désactiver cette réutilisation en spécifiant .
Une grande partie des performances que vous verrez dépend de votre workflow, de votre matériel et du nombre de serveurs de noms sur lesquels la charge est répartie. Si vous cherchez à maximiser les performances
pour votre workflow/matériel, nous vous recommandons de commencer à 100 threads et d'augmenter jusqu'à ce que vous commenciez à voir une augmentation des échecs de résolution de noms.
Pour vous aider, vous pouvez utiliser --output-file=output.jsonl et grep -v "NOERROR" output.jsonl | wc -l pour compter le nombre de noms qui ont échoué à résoudre.
Les drapeaux qui peuvent être utiles pour ajuster les performances sont :
--timeout Le temps maximum que ZDNS passera sur un seul nom--iteration-timeout Le temps maximum que ZDNS passera sur une seule étape d'itération (ex : résoudre google.com au niveau .com)--network-timeout Le temps maximum que ZDNS attendra une réponse d'un serveur de noms--retries=N Si une connexion à un serveur de noms spécifique échoue en --iterative, ZDNS réessaiera avec un autre serveur de noms non interrogé à ce niveau.
Les tentatives sont par nom, donc si --retries=1, ZDNS réessaiera un nom contre un nouveau serveur de noms une fois pendant son processus d'itération complet. Si tous les serveurs de noms ont été interrogés,
un serveur de noms aléatoire sera choisi.--name-servers La liste des serveurs de noms à utiliser pour les recherches, principalement utile avec --iterative=falseDNS inclut beaucoup de données superflues qui ne sont pas toujours utiles. Il existe quatre
niveaux de verbosité des résultats : short, normal (par défaut), long et trace :
short : Court est la sortie de résultat la plus concise. Il contient uniquement des informations sur les réponses.normal : Normal fournit tout ce qui est inclus dans short ainsi que des données sur le serveur répondant.long : Long produit tout ce que le serveur a inclus dans le paquet DNS, y compris les drapeaux.trace : Trace produit tout de chaque étape du processus de récursion.Les utilisateurs peuvent également inclure des champs supplémentaires spécifiques en utilisant le drapeau --include-fields
et en spécifiant une liste de champs, par exemple --include-fields=flags,resolver.
Les champs supplémentaires sont : class, protocol, ttl, resolver, flags, dnssec.
Par défaut, ZDNS s'attend à recevoir une liste de noms à rechercher sur un petit nombre de serveurs de noms. Par exemple :
echo "google.com" | zdns A --name-servers=8.8.8.8,8.8.4.4
Cependant, il arrive que vous souhaitiez plutôt rechercher le même nom sur un grand nombre de serveurs. Cela peut être réalisé en utilisant le mode serveur de noms. Par exemple :
echo "8.8.8.8" | zdns A --name-server-mode --override-name="google.com"
Ici, chaque ligne transmise à ZDNS reçoit une requête A pour google.com. ZDNS prend
également en charge le mélange des deux modes en transmettant une liste délimitée par des virgules de
name,nameServer. Par exemple :
echo "google.com,8.8.8.8" | zdns A enverra une requête A pour
google.com à 8.8.8.8 indépendamment des serveurs de noms spécifiés par le
drapeau --name-servers=. Les lignes qui ne spécifient pas explicitement un serveur de noms utiliseront
les serveurs spécifiés par le système d'exploitation ou le drapeau --name-servers comme cela se produirait
normalement.
Il existe une fonctionnalité disponible pour effectuer une requête DNS particulière contre tous les serveurs de noms. Par exemple, vous pouvez souhaiter obtenir les enregistrements A de tous les serveurs de noms d'un domaine donné. Pour ce faire, vous pouvez :
echo "google.com" | zdns A --all-nameservers
ZDNS prend en charge l'utilisation de plusieurs modules de recherche dans une seule invocation. Par exemple, disons que vous souhaitez effectuer une recherche A,
AAAA et MXLOOKUP pour un ensemble de domaines et que vous souhaitez les effectuer avec une résolution itérative. Vous devrez utiliser le
module MULTIPLE et fournir un fichier de configuration avec les modules et les drapeaux spécifiques aux modules que vous souhaitez utiliser.
Veuillez consulter zdns --help et zdns <NOM_MODULE> --help pour les options globales et spécifiques aux modules qui peuvent être utilisées dans le fichier de configuration.
Par exemple :
cat 1000k_domains.txt | zdns MULTIPLE --multi-config-file="./multiple.ini"
Où multiple.ini est un fichier qui ressemble à :
; Specify Global Options here
[Application Options]
iterative=true
; List out modules and their respective module-specific options here. A module can only be listed once
[MXLOOKUP]
ipv4-lookup = true
; You can use default values and just list modules if you don't need to specify any options
[A]
[AAAA]
Un exemple de fichier multiple.ini est fourni dans src/cli/multiple.ini
Par défaut, ZDNS fonctionnera avec 1 000 goroutines légères. Si vous n'êtes pas
prudent, cela submergera de nombreux fournisseurs DNS en amont. Nous suggérons que
les utilisateurs se coordonnent avec les administrateurs réseau locaux avant d'effectuer des analyses.
Vous pouvez contrôler le nombre de connexions simultanées avec les arguments de ligne de commande --threads et
--go-processes. Des serveurs de noms alternatifs peuvent être
spécifiés avec --name-servers. ZDNS alternera entre ces serveurs lors
des requêtes. Nous avons exécuté ZDNS avec succès avec des dizaines de milliers de
goroutines légères.
Si zdns rencontre un type d'enregistrement qu'il ne prend pas en charge, il générera un enregistrement
de sortie avec le champ type correctement défini et une représentation de la
structure de données sous-jacente dans le champ unparsed_rr. Ne vous fiez pas à la
présence ou à la structure de ce champ. Ce champ (et son existence) peut changer
à tout moment à mesure que nous étendons la prise en charge de types d'enregistrements supplémentaires. Si vous
vous retrouvez à utiliser ce champ, veuillez envisager de soumettre une pull request ajoutant
le support de l'analyseur.
Un benchmark est disponible dans benchmark/ qui peut être utilisé pour exécuter ZDNS de manière prévisible et afficher des statistiques sur l'exécution.
Cela peut être utile pour comparer les performances avant et après une modification de ZDNS.
Voir plus de détails dans le README du benchmark.
Si vous souhaitez contribuer à ZDNS, consultez CONTRIBUTING.
ZDNS Copyright 2020 Regents of the University of Michigan
Sous licence Apache License, Version 2.0 (la « Licence ») ; vous ne pouvez pas utiliser ce fichier sauf en conformité avec la Licence. Vous pouvez obtenir une copie de la Licence à l'adresse http://www.apache.org/licenses/LICENSE-2.0
Sauf si la loi applicable l'exige ou si un accord écrit le stipule, le logiciel distribué sous la Licence est distribué « EN L'ÉTAT », SANS GARANTIES NI CONDITIONS DE QUELQUE NATURE QUE CE SOIT, expresses ou implicites. Voir la Licence pour le langage spécifique régissant les autorisations et limitations.
--recycle-sockets=falseGo utilise volontiers tous les cœurs de CPU qui lui sont disponibles, et peut utiliser une
quantité considérable de CPU si vous spécifiez un grand nombre de threads. Le CPU est
principalement utilisé pour l'analyse et l'encodage JSON. Si vous souhaitez limiter le nombre
de cœurs CPU, vous pouvez le faire en incluant le drapeau --go-processes=n ou en définissant
la variable d'environnement GOMAXPROCS.
Il est difficile de recommander une quantité précise de --threads car cela dépend de plusieurs
facteurs. Le graphique ci-dessous montre comment un workflow exemple a un temps d'exécution plus faible mais des taux d'échec de résolution de noms plus élevés à mesure que le nombre de threads augmente.