today keys : DNS, Header, Flags, QR, RD, AA, RA, RCODE, Additional, EDNS, 패킷, 헤더, 섹션
1. DNS 메시지의 전체 구조
DNS는 질의와 응답이 같은 형식을 씁니다.
모든 메시지는 고정 크기의 헤더 하나와, 가변 길이의 섹션 4개로 이루어집니다.

- 질의 메시지는 보통 Question만 채우고 Answer·Authority는 비어 있습니다.
- Additional에는 EDNS를 쓰는 경우 OPT 레코드가 하나 들어갑니다.
- 응답 메시지는 Question을 그대로 복사해 돌려주고, 나머지 세 섹션에 결과를 담습니다.
- 질의인지 응답인지, 결과가 무엇인지는 모두 헤더에 표시됩니다. 그래서 패킷을 읽을 때는 항상 헤더부터 봅니다.
DNS 전송 방식
- 기본은 UDP 53입니다.
- 응답이 UDP 크기 한도를 넘으면 서버는 TC=1로 잘린 응답을 보내고, 클라이언트는 TCP 53으로 다시 질의합니다.
- 존 전송(AXFR/IXFR)처럼 큰 데이터는 처음부터 TCP를 씁니다. TCP에서는 메시지 앞에 2바이트 길이 필드가 붙는다는 점만 다르고, 메시지 구조는 같습니다.
2. DNS 헤더 구조
2.1 헤더의 구성
헤더는 2바이트짜리 필드 6개, 총 12바이트입니다.

| 필드 | 이름 | 크기 | 의미 |
| ID | Identifier | 16비트 | 질의 식별자. 질의자가 무작위로 정하고 응답은 같은 ID로 돌아옴 |
| 플래그 | Flags | 16비트 | 메시지의 종류, 요청 사항, 처리 결과 (2.2에서 상세히) |
| QDCOUNT | Question Count | 16비트 | Question 섹션의 항목 수 (사실상 항상 1) |
| ANCOUNT | Answer Count | 16비트 | Answer 섹션의 레코드 수 |
| NSCOUNT | Name Server Count | 16비트 | Authority 섹션의 레코드 수. 이 섹션에 주로 NS 레코드가 담겨서 붙은 이름 |
| ARCOUNT | Additional Record Count | 16비트 | Additional 섹션의 레코드 수 (OPT 포함) |
- ID (Transaction ID)
- 질의를 보낼 때 무작위로 정하고, 응답은 같은 ID로 돌아옵니다.
- 덤프에서 질의와 응답을 짝지을 때 ID와 출발지 포트를 함께 봅니다.
- COUNT 4개
- 각 섹션에 레코드가 몇 개 있는지를 나타냅니다.
- 섹션 내용을 보기 전에 이 숫자만으로도 응답의 성격을 짐작할 수 있습니다.
2.2 플래그 필드 (16비트)
플래그 필드는 1비트 플래그 8개와 4비트 필드 2개로 이루어집니다.
| 구분 | 필드 |
| 1비트 플래그 | QR, AA, TC, RD, RA, Z, AD, CD |
| 4비트 필드 | Opcode, RCODE |
각 약어의 원래 이름은 2.2.1과 2.2.2의 표에 함께 적었습니다.
약어가 헷갈릴 때는 원래 이름을 떠올리면 의미가 바로 풀립니다.
예를 들어
RD는 Recursion Desired(재귀를 원함),
RA는 Recursion Available(재귀 가능)이라 누가 설정하는 플래그인지가 이름에 드러나 있습니다.
Wireshark는 이 16비트를 Flags: 0x8180처럼 16진수 하나로 보여 줍니다.
비트 위치만 알면 바로 풀어 읽을 수 있습니다.
[첫 번째 바이트]
0x81 = 1 0000 0 0 1
│ │ │ │ └─ RD = 1
│ │ │ └─── TC = 0
│ │ └───── AA = 0
│ └───────── Opcode = 0000 (QUERY)
└──────────── QR = 1 (응답)
[두 번째 바이트]
0x80 = 1 0 0 0 0000
│ │ │ │ └──── RCODE = 0000 (NOERROR)
│ │ │ └────── CD = 0
│ │ └──────── AD = 0
│ └────────── Z = 0
└──────────── RA = 1
즉 0x8180은 "RD=1로 들어온 질의에, 재귀가 가능한 서버가 정상 응답했다"는 뜻입니다. 자주 보는 값은 Part 4에 표로 모아 두었습니다.
2.2.1 1비트 플래그
플래그는 누가 설정하느냐에 따라 세 가지로 나뉩니다. 이 구분을 먼저 알아두면 헷갈릴 일이 크게 줄어듭니다.
| 구분 | 의미 | 플래그 |
| [응답] | 응답자만 설정. 질의 패킷에서는 의미가 없어 0 | AA, TC, RA |
| [질의→복사] | 질의자가 설정하고, 응답자는 그 값을 그대로 복사해 돌려줌 | RD, CD |
| [공통] | 질의와 응답 모두에서 의미가 있지만, 뜻이 서로 다름 | QR, AD |
각 플래그의 값이 0일 때와 1일 때의 의미는 다음과 같습니다.
| Flag | Flag 이름 | 구분 | Bit = 0 | Bit = 1 |
| QR | Query / Response |
공통 | 질의 메시지 | 응답 메시지 |
| AA | Authoritative Answer |
응답 | 비권한 응답. 리졸버가 캐시나 전달받은 결과로 답한 경우 루트·TLD의 referral, lame 서버의 응답 등 |
권한 응답. 그 존을 직접 가진 권한 서버가 답함 |
| TC | TrunCation | 응답 | 응답이 완전함 | 응답이 UDP 크기 한도를 넘어 잘림 → 클라이언트는 TCP로 재질의 |
| RD | Recursion Desired |
질의 →복사 |
재귀 비요청(iterative). "네가 아는 것만 답해라". 리졸버가 루트·TLD·권한 서버에 보내는 질의 |
재귀 요청. "대신 끝까지 찾아 달라". 클라이언트가 리졸버에 보내는 일반 질의 |
| RA | Recursion Available |
응답 | 이 서버가 질의자에게 재귀를 제공하지 않음. 권한 전용 서버(루트·TLD 등)이거나, 재귀 서버라도 질의자가 재귀 허용 범위 밖인 경우 |
이 서버가 질의자에게 재귀를 제공할 수 있음 |
| Z | Zero (예약) | - | 항상 0 (예약 비트) | 사용하지 않음. 수신 측은 보통 무시 |
| AD | Authentic Data |
공통 | 응답: DNSSEC 검증을 거치지 않은 데이터 (검증을 안 하는 서버, 서명 없는 존 등). 질의: AD 결과를 특별히 요청하지 않음 |
응답: 리졸버가 DNSSEC 검증을 완료(secure)한 데이터. 질의: "AD 결과를 이해하니 알려 달라"는 표시 |
| CD | Checking Disabled |
질의 →복사 |
리졸버가 DNSSEC 검증을 수행해야 함. 검증 실패(bogus)면 SERVFAIL |
검증을 끄고 데이터를 그대로 달라. 검증 실패 원인을 디버깅할 때 사용 ( dig +cd) |
2.2.2 4비트 필드 — Opcode(Operation Code)와 RCODE(Response Code)
Opcode (Operation Code) [질의→복사]
- 메시지의 종류입니다. 질의자가 정하고 응답은 같은 값을 돌려줍니다.
| 값 | 코드 | 코드 이름 | 의미 |
| 0 | QUERY | Standard Query | 표준 질의. 일상적인 DNS 조회는 모두 0 |
| 1 | IQUERY | Inverse Query | 역질의. 폐기됨(RFC 3425) |
| 2 | STATUS | Server Status Request | 서버 상태 질의. 사실상 사용하지 않음 |
| 4 | NOTIFY | Zone Change Notification | primary가 secondary에게 존 변경을 알림 |
| 5 | UPDATE | Dynamic Update | 동적 업데이트(DDNS). 레코드 추가·삭제 요청 |
| 6 | DSO | DNS Stateful Operations | TCP 세션 기반 기능(RFC 8490) |
RCODE (Response Code) [응답]
- 응답의 처리 결과입니다. 질의 패킷에서는 항상 0입니다.
| 값 | 코드 | 코드 이름 | 의미 |
| 0 | NOERROR | No Error | 정상 처리. Answer가 0개여도 NOERROR일 수 있음 |
| 1 | FORMERR | Format Error | 질의 형식 오류 |
| 2 | SERVFAIL | Server Failure | 서버가 답을 만드는 데 실패. lame delegation, 권한 서버 무응답, DNSSEC 검증 실패 등 |
| 3 | NXDOMAIN | Non-Existent Domain | 그 이름은 존재하지 않음. 권한 서버가 확정한 답 |
| 4 | NOTIMP | Not Implemented | 지원하지 않는 Opcode |
| 5 | REFUSED | Query Refused | 정책상 처리 거부. ACL, 재귀 비허용, 서비스하지 않는 존 등 |
4비트로는 15까지만 표현할 수 있습니다.
16 이상의 값(예: 16 = BADVERS)은 EDNS OPT 레코드의 확장 RCODE 비트와 합쳐서 표현합니다(Part 2의 1.5 참고).
2.3 헷갈리기 쉬운 포인트
- RD와 RA는 짝이 아닙니다.
- RD는 질의자의 요청이고, RA는 응답자의 능력 표시입니다.
- 루트·TLD 서버는 재귀를 하지 않으므로 응답에 RA=0이 찍힙니다.
- 응답의 RD=1은 "재귀를 했다"는 뜻이 아닙니다.
- 질의의 RD 값을 그대로 복사한 것입니다. 재귀 제공 여부는 RA로 판단합니다.
- 리졸버의 최종 응답은 AA=0이 정상입니다.
- 답이 정확하더라도 리졸버는 그 존의 권한 서버가 아니기 때문입니다.
- AA=1은 권한 서버가 직접 답할 때만 나옵니다.
- RA=0이 나오는 경우는 두 가지입니다.
- 서버가 재귀 기능 자체가 없는 경우와, 재귀 서버이지만 질의자가 허용 범위 밖인 경우입니다.
- 후자는 재귀 서버의 ACL을 점검할 때 유용한 단서가 됩니다.
참고 자료
- RFC 1035 — Domain Names: Implementation and Specification : https://datatracker.ietf.org/doc/html/rfc1035
- RFC 4035 — Protocol Modifications for the DNS Security Extensions (AD/CD 비트) : https://datatracker.ietf.org/doc/html/rfc4035
- RFC 6840 — Clarifications and Implementation Notes for DNSSEC (질의의 AD 비트) : https://datatracker.ietf.org/doc/html/rfc6840
- RFC 6891 — Extension Mechanisms for DNS (EDNS(0), 확장 RCODE) : https://datatracker.ietf.org/doc/html/rfc6891
- RFC 7766 — DNS Transport over TCP : https://datatracker.ietf.org/doc/html/rfc7766
- RFC 8490 — DNS Stateful Operations (Opcode 6) : https://datatracker.ietf.org/doc/html/rfc8490
- IANA — DNS Parameters (Opcode, RCODE 레지스트리) : https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml