
빠른 DNS 조회 라이브러리 및 CLI 도구
ZDNS는 고속 DNS 리졸버이자 대규모 DNS 측정을 수행하기 위한 명령줄 유틸리티입니다. ZDNS는 Go로 작성되었으며, 자체 재귀적 해석 코드와 다양한 이름 집합의 조회를 최적화하는 캐시를 포함하고 있습니다. 원시 DNS 패킷을 구성하고 파싱하기 위해 https://github.com/zmap/dns를 사용합니다. ZDNS의 아키텍처와 성능에 대한 자세한 내용은 ACM Internet Measurement Conference '22에 발표된 다음 논문을 참조하세요.
[!TIP] ZDNS 위키에는 ZDNS에 대한 추가 정보와 사용 사례 및 예제가 포함되어 있습니다.
ZDNS는 저장소를 클론하고 make install을 실행하여 설치할 수 있습니다.
git clone https://github.com/zmap/zdns.git
cd zdns
make install
ZDNS는 재귀적 리졸버 라이브러리와 CLI 래퍼로 구성됩니다.
라이브러리는 모든 조회에 대한 모든 설정 옵션을 포함하는 ResolverConfig 구조체로 구성됩니다. ResolverConfig는 모든 조회를 수행할 1개 이상의 Resolver 구조체를 생성하는 데 사용됩니다. Resolver는 한 번에 하나의 조회만 수행해야 하며(스레드 안전하지 않음), 병렬 처리를 위해 여러 Resolver 구조체를 사용해야 합니다. 라이브러리 사용 방법은 examples를 참조하세요. Modules는 조회의 동작을 정의하는 데 사용됩니다.
ZDNS는 여러 유형의 모듈을 제공합니다:
Raw DNS 모듈: dig와 유사하게 서버로부터의 원시 DNS 응답을 JSON 형태로 제공합니다. 거의 모든 DNS 레코드 유형에 대한 모듈이 있습니다.
조회 모듈: 여러 쿼리가 필요한 경우 더 유용한 응답을 제공합니다(예: NSLOOKUP에서 NS를 받은 경우 IP 주소에 대한 추가 A 조회 완료).
기타 모듈: 서버를 쿼리하는 다른 추가 수단을 제공합니다(예: bind.version).
아래에 모듈을 자세히 설명합니다:
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, URI 모듈은 dig와 유사하게 JSON 형태의 원시 DNS 응답을 제공합니다.
예를 들어, 명령:
echo "censys.io" | zdns A
다음을 반환합니다:
{
"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"
}
}
}
원시 DNS 응답은 종종 사용자가 원하는 데이터를 제공하지 않습니다. 예를 들어, MX 응답에 연결된 A 레코드가 추가 섹션에 포함되지 않아 추가 조회가 필요할 수 있습니다. 이러한 문제를 해결하고 더 친숙한 인터페이스를 제공하기 위해 몇 가지 조회 모듈도 제공합니다: alookup, mxlookup, nslookup.
alookup은 nslookup과 유사하게 작동하며 CNAME 레코드를 따라갑니다.
mxlookup은 교환 레코드에 해당하는 IP 주소에 대해 추가로 A 조회를 수행합니다.
nslookup은 NS 레코드에 해당하는 IP 주소에 대해 추가로 A/AAAA 조회를 수행합니다.
예를 들어,
echo "censys.io" | zdns mxlookup --ipv4-lookup
다음을 반환합니다:
{
"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는 특수 "디버그" DNS 쿼리도 지원합니다. 모듈에는 BINDVERSION이 포함됩니다.
ZDNS는 원하는 동작에 따라 다양한 형식의 입력을 지원합니다.
가장 기본적인 입력은 개행으로 구분된 이름 목록입니다. 예를 들어:
표준 입력에서:
echo "google.com\nyahoo.com" | zdns A
cat list_of_domains.txt | zdns A
파일에서
zdns A --input-file=list_of_domains.txt
도메인을 많이 해석할 필요가 없다면, dig와 마찬가지로 편의를 위해 CLI 인수로 도메인을 제공할 수 있습니다.
예를 들어:
zdns A google.com --name-servers=1.1.1.1
dig -t A google.com @1.1.1.1과 동일
일반적으로 ZDNS는 --name-servers에서 각 도메인 조회에 대해 무작위 네임서버를 선택합니다. 대신 도메인마다 다른 네임서버를 지정하려면 개행으로 구분된 domainName,nameServerIP 쌍을 제공하면 됩니다. 이렇게 하면 --name-servers로 제공된 모든 네임서버가 재정의됩니다.
예를 들어:
echo "google.com,1.1.1.1\nfacebook.com,8.8.8.8" | zdns A
출력에서 각 도메인에 대해 지정된 resolver를 확인할 수 있습니다(간결함을 위해 additionals/answers는 생략):
$ 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"}}}
Zone 파일(ICANN CZDS 등에서 가져온)은 --zone-file 플래그와 함께 ZDNS의 입력 소스로 사용할 수 있습니다. 이렇게 하면 표준 입력(기본값) 또는 --input-file 플래그를 사용한 파일에서 zone 파일을 파싱할 수 있습니다.
기본적으로 ZDNS는 각 zone 파일 레코드에서 이름만 추출합니다. CNAME 또는 NS 레코드와 같은 레코드 유형의 응답 섹션에서 참조된 이름도 해석하려면 --zone-file-include-targets CLI 플래그를 사용할 수 있습니다.
예를 들어, --zone-file-include-targets와 이 DNS zone 파일 항목이 있는 경우:
example.com. 3600 IN NS ns1.example.com
example.com과 ns1.example.com이 모두 해석됩니다.
ZDNS는 입력 라인을 특정 모듈에 매핑하는 "트리거"를 입력 라인별로 전달하는 기능도 지원합니다. 이를 통해 특정 도메인이 특정 모듈로 조회되도록 지정할 수 있습니다.
입력 형식은 다음과 같습니다:
domain_name,name_server,trigger,trigger_2,etc 여기서 nameServer는 기본 네임서버를 사용하려면 비워둘 수 있으며, 1개 이상의 트리거를 지정할 수 있습니다.
예제 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
그리고 해당 multiple.ini 파일:
; 전역 옵션을 여기에 지정
[Application Options]
iterative=true
prefer-ipv6-iteration="true"
; 모듈 및 해당 모듈별 옵션을 여기에 나열합니다. 모듈은 한 번만 나열될 수 있습니다.
[A]
trigger = "a-trigger"
[AAAA]
trigger = "aaaa-trigger"
[CNAME]
trigger = "cname-trigger"
이렇게 하면 다음과 같이 조회됩니다:
example.com을 기본 네임서버를 사용하여 A 모듈로 조회google.com을 기본 네임서버를 사용하여 A + CNAME 모듈로 조회example.com을 Cloudflare의 1.1.1.1 리졸버를 사용하여 AAAA 모듈로 조회yahoo.com을 기본 네임서버를 사용하여 지정된 모든 모듈로 조회apnews.com을 Cloudflare의 1.1.1.1 리졸버를 사용하여 지정된 모든 모듈로 조회명령 실행:
zdns MULTIPLE --multi-config-file="./multiple.ini" --input-file="input.csv"
ZDNS는 재귀적 리졸버(예: 조직 DNS 서버)를 대상으로 작동하거나(기본 동작) 자체적으로 재귀를 수행할 수 있습니다. 소량(수백만 개)의 조회를 수행하고 10,000개 미만의 Go 루틴을 사용하는 경우 일반적으로 Cloudflare 또는 Google과 같은 일반적인 재귀적 리졸버를 사용하는 것이 가장 빠릅니다. Cloudflare는 거의 항상 Google보다 빠릅니다. 특히 인기 있는 이름을 조회하는 경우 캐시되어 단일 왕복으로 응답할 수 있기 때문입니다. 수만 개의 동시 스레드를 사용하는 경우 재귀적 리졸버에 DOS를 가하거나 속도 제한에 걸리지 않도록 내부적으로 반복을 수행하는 것이 좋습니다.
로컬 재귀를 수행하려면 --iterative 플래그와 함께 zdns를 실행하세요. 이 플래그를 사용하면 ZDNS는 게시된 루트 서버(예: 198.41.0.4) 사이를 라운드 로빈 방식으로 순회합니다. 반복 모드에서는 --cache-size를 지정하여 로컬 캐시의 크기를 제어하고, --iteration-timeout을 설정하여 개별 반복의 타임아웃을 제어할 수 있습니다. --timeout 플래그는 주어진 입력에 대한 전체 해석의 타임아웃(즉, 모든 반복 단계의 합)을 제어합니다.
ZDNS 성능은 경량 Go 루틴을 사용한 대규모 병렬 처리에서 비롯됩니다. 이 아키텍처에는 몇 가지 주의 사항이 있습니다:
각 Go 루틴은 자체 전용 네트워크 소켓을 사용합니다. 따라서 --threads로 지정된 스레드 수만큼 많은 소켓(최대 파일 디스크립터 및 임시 포트 측면에서)을 열 수 있어야 합니다. 기본적으로 ZDNS는 1,000개의 스레드를 사용하며, 이는 Linux의 기본 최대 열린 FD 1024개보다 적습니다. 그러나 Mac OS의 기본 256개보다는 많습니다. 허용되는 최대 열린 FD(따라서 소켓) 수는 ulimit -n을 실행하여 확인할 수 있습니다. 이 숫자보다 더 많은 스레드로 실행하려면 OS 수준에서 열린 파일 수를 늘려야 합니다. 그렇지 않으면 FATA[0000] unable to create socketlisten udp <client IP address>:0: socket: too many open files와 유사한 치명적인 오류가 발생합니다. 사용 가능한 임시 포트보다 더 많은 스레드를 실행하려면 여러 클라이언트 IP 주소를 사용해야 합니다: --local-addr=A,B,C.
기본적으로 ZDNS는 시작 시 각 경량 루틴에 대해 바인딩되지 않은 UDP 소켓을 생성하고 모든 쿼리(대상 IP에 관계없이)에 이를 사용하여 UDP 소켓을 "재사용"합니다. 이렇게 하면 ZDNS와 호스트 OS가 각 개별 패킷을 보내기 위해 소켓을 설정 및 해제할 필요가 없기 때문에 성능이 크게 향상됩니다(DNS 쿼리/응답은 일반적으로 각각 하나의 패킷이므로). 그러나 이는 소량의 이름만 조회하는 경우 최적이 아닐 수 있습니다. 예를 들어 100개의 이름만 조회해야 하지만 기본 1,000개의 스레드를 사용하는 경우 900개의 UDP 소켓을 바인딩하지만 사용하지 않게 됩니다. 소켓 재활용을 고려하는 대신, 사용 사례에 맞는 적절한 스레드 수를 지정하는 것이 좋습니다(이렇게 하면 해당 스레드를 시작하는 작업도 생략되기 때문입니다). 이것이 단일 이름만 조회하는 경우에도 많은 소켓을 열 수 없다는 오류가 발생할 수 있는 이유입니다. 각 쿼리에 대해 새 소켓을 생성하는 것이 중요하다면 --recycle-sockets=false를 지정하여 이 재사용을 비활성화할 수 있습니다.
Go는 사용 가능한 모든 CPU 코어를 기꺼이 사용하며, 많은 스레드를 지정하면 엄청난 양의 CPU를 사용할 수 있습니다. CPU는 주로 파싱 및 JSON 인코딩에 사용됩니다. CPU 코어 수를 제한하려면 --go-processes=n 플래그를 포함하거나 GOMAXPROCS 환경 변수를 설정할 수 있습니다.
정확한 --threads 양을 추천하기는 어렵습니다. 이는 여러 요인에 따라 달라지기 때문입니다. 아래 그래프는 스레드 수가 증가함에 따라 샘플 워크플로우의 실행 시간이 줄어들지만 이름 해석 실패율은 높아지는 것을 보여줍니다.
성능은 워크플로우, 하드웨어 및 네임서버에 부하가 분산되는 방식에 따라 크게 달라집니다. 워크플로우/하드웨어의 성능을 최대화하려면 100개의 스레드에서 시작하여 이름 해석 실패율이 증가하기 시작할 때까지 늘리는 것이 좋습니다. 이를 돕기 위해 --output-file=output.jsonl 및 grep -v "NOERROR" output.jsonl | wc -l을 사용하여 해석에 실패한 이름의 수를 계산할 수 있습니다. 성능 튜닝에 유용할 수 있는 플래그는 다음과 같습니다:
--timeout ZDNS가 단일 이름에 소비하는 최대 시간--iteration-timeout ZDNS가 단일 반복 단계에 소비하는 최대 시간(예: .com 계층에서 google.com 해석)--network-timeout ZDNS가 네임서버의 응답을 기다리는 최대 시간--retries=N 특정 네임서버에 대한 연결이 --iterative 모드에서 실패하면 ZDNS는 해당 계층에서 아직 쿼리되지 않은 다른 네임서버로 재시도합니다.
재시도는 이름별로 이루어지므로 --retries=1인 경우 ZDNS는 전체 반복 과정 중에 한 번 새 네임서버로 이름을 재시도합니다. 모든 네임서버가 쿼리된 경우 무작위 네임서버가 선택됩니다.--name-servers 조회에 사용할 네임서버 목록이며, 주로 --iterative=false와 함께 유용합니다.DNS에는 항상 유용하지 않은 많은 외부 데이터가 포함됩니다. 결과 상세 수준은 short, normal(기본값), long, trace의 네 가지가 있습니다:
short: 가장 간결한 결과 출력입니다. 응답에 대한 정보만 포함합니다.normal: short에 포함된 모든 정보와 응답 서버에 대한 데이터를 제공합니다.long: 서버가 DNS 패킷에 포함시킨 모든 정보(플래그 포함)를 출력합니다.trace: 재귀 과정의 모든 단계에서 모든 것을 출력합니다.사용자는 --include-fields 플래그와 필드 목록을 지정하여 특정 추가 필드를 포함할 수도 있습니다(예: --include-fields=flags,resolver). 추가 필드는 class, protocol, ttl, resolver, flags, dnssec입니다.
기본적으로 ZDNS는 소수의 네임서버에서 조회할 이름 목록을 받을 것으로 예상합니다. 예를 들어:
echo "google.com" | zdns A --name-servers=8.8.8.8,8.8.4.4
그러나 동일한 이름을 많은 수의 서버에서 조회하려는 경우가 있습니다. 이는 _네임서버 모드_를 사용하여 수행할 수 있습니다. 예를 들어:
echo "8.8.8.8" | zdns A --name-server-mode --override-name="google.com"
여기서 ZDNS로 파이프된 모든 라인은 google.com에 대한 A 쿼리를 보냅니다. ZDNS는 name,nameServer의 쉼표로 구분된 목록을 파이프하여 두 모드를 혼합하는 것도 지원합니다. 예를 들어:
echo "google.com,8.8.8.8" | zdns A는 google.com에 대한 A 쿼리를 8.8.8.8로 보내며, --name-servers= 플래그로 지정된 네임서버와 관계없이 작동합니다. 네임서버를 명시적으로 지정하지 않은 라인은 일반적인 경우처럼 OS 또는 --name-servers 플래그로 지정된 서버를 사용합니다.
특정 DNS 쿼리를 모든 네임서버에 대해 수행할 수 있는 기능이 있습니다. 예를 들어, 특정 도메인의 모든 네임서버에서 A 레코드를 가져오고 싶을 수 있습니다. 이렇게 하려면 다음과 같이 할 수 있습니다:
echo "google.com" | zdns A --all-nameservers
ZDNS는 단일 호출에서 여러 조회 모듈을 사용하는 것을 지원합니다. 예를 들어, 도메인 집합에 대해 A, AAAA 및 MXLOOKUP을 수행하고 반복적 해석으로 수행하려는 경우가 있습니다. MULTIPLE 모듈을 사용하고 사용하려는 모듈 및 모듈별 플래그가 포함된 구성 파일을 제공해야 합니다.
전역 및 모듈별 옵션에 대해서는 zdns --help 및 zdns <MODULE_NAME> --help를 참조하세요. 구성 파일에서 사용할 수 있습니다.
예를 들어:
cat 1000k_domains.txt | zdns MULTIPLE --multi-config-file="./multiple.ini"
여기서 multiple.ini는 다음과 같은 파일입니다:
; 전역 옵션을 여기에 지정
[Application Options]
iterative=true
; 모듈 및 해당 모듈별 옵션을 여기에 나열합니다. 모듈은 한 번만 나열될 수 있습니다.
[MXLOOKUP]
ipv4-lookup = true
; 옵션을 지정할 필요가 없으면 기본값을 사용하고 모듈만 나열할 수 있습니다.
[A]
[AAAA]
샘플 multiple.ini 파일은 src/cli/multiple.ini에 제공됩니다.
기본적으로 ZDNS는 1,000개의 경량 Go 루틴으로 작동합니다. 주의하지 않으면 많은 업스트림 DNS 공급자에 과부하를 일으킬 수 있습니다. 사용자는 스캔을 수행하기 전에 로컬 네트워크 관리자와 조정할 것을 권장합니다. --threads 및 --go-processes 명령줄 인수를 사용하여 동시 연결 수를 제어할 수 있습니다. 대체 네임서버는 --name-servers로 지정할 수 있습니다. ZDNS는 요청 시 이러한 서버를 순환합니다. 우리는 수만 개의 경량 루틴으로 ZDNS를 성공적으로 실행했습니다.
zdns가 지원하지 않는 레코드 유형을 만나면 type 필드가 올바르게 설정되고 기본 데이터 구조의 표현이 unparsed_rr 필드에 포함된 출력 레코드를 생성합니다. 이 필드의 존재나 구조에 의존하지 마십시오. 추가 레코드 유형에 대한 지원을 확장함에 따라 이 필드(및 그 존재)는 언제든지 변경될 수 있습니다. 이 필드를 사용하는 경우 파서 지원을 추가하는 풀 리퀘스트를 제출해 주시기 바랍니다.
benchmark/에 예측 가능한 방식으로 ZDNS를 실행하고 실행에 대한 일부 통계를 출력하는 벤치마크가 있습니다. 이는 ZDNS 변경 전후의 성능을 비교하는 데 유용할 수 있습니다. 자세한 내용은 benchmark README를 참조하세요.
ZDNS에 기여하는 데 관심이 있다면 CONTRIBUTING을 참조하세요.
ZDNS Copyright 2020 Regents of the University of Michigan
Apache License, Version 2.0 (이하 "라이선스")에 따라 라이선스가 부여됩니다. 이 파일은 라이선스를 준수하는 경우에만 사용할 수 있습니다. 라이선스 사본은 http://www.apache.org/licenses/LICENSE-2.0 에서 얻을 수 있습니다.
해당 법률에 의해 요구되거나 서면으로 동의하지 않는 한, 라이선스에 따라 배포되는 소프트웨어는 "있는 그대로" 제공되며, 명시적이든 묵시적이든 어떠한 종류의 보증이나 조건도 없습니다. 라이선스에 따른 권리 및 제한 사항을 규정하는 특정 언어는 LICENSE를 참조하세요.