brunch

You can make anything
by writing

C.S.Lewis

by 이권수 May 29. 2024

DNS가 TCP를 사용하는 경우

DNS는 언제나 UDP로 통신할까?


DNS(Domain Name Service)는 우리가 브라우저를 열면 가장 먼저 개입하는 서비스 중 하나이다. 컴퓨터는 영어로 된 도메인만 알고는 목적지 서버와 통신할 수가 없다. 통신하고자 하는 실제 목적지 IP가 반드시 필요하다. 그래서 도메인에 google.com이라고 주소를 입력하면, 가장 먼저 DNS Resolver가 해당 도메인과 연결된 IP를 불러온다. 그러고 나면 실제로 통신이 가능해진다.


DNS는 기본적으로 UDP(User Datagram Protocol)를 사용한다. 이는 UDP가 가볍고 빠르기 때문이다. DNS 쿼리와 응답은 대부분의 경우 매우 짧기 때문에, 속도가 중요한 상황에서는 UDP가 적합하다. 


그러나 DNS가 항상 UDP만 사용하는 것은 아니다. 

특정 상황에서는 TCP(Transmission Control Protocol)를 사용하기도 한다. 


대표적으로, DNS 메시지의 크기가 클 때 TCP를 사용한다. UDP는 일반적으로 512바이트 이하의 메시지를 처리한다. 하지만 현대의 DNS 요청은 더 많은 데이터를 요구할 수 있다. 예를 들어, DNSSEC(Domain Name System Security Extensions)을 사용할 때는 보안 기능을 위한 추가 데이터가 포함되므로 메시지 크기가 커질 수 있다. 이런 경우, UDP로는 데이터를 모두 전송할 수 없으므로 TCP를 사용하여 데이터를 분할하여 전송한다. TCP는 연결 기반 프로토콜로, 데이터의 순서와 완전성을 보장하기 때문에 크고 복잡한 메시지를 안정적으로 전달할 수 있다.


systemd 코드를 살펴보면 다음과 같다. 

https://github.com/systemd/systemd/blob/main/src/resolve/resolved-dns-transaction.c#L1162


DNS_PACKET_TC는 패킷 중 Flag의 일부 비트를 통해 실제 TRUCATED 되었는지 여부를 판단한다. 

https://github.com/systemd/systemd/blob/main/src/resolve/resolved-dns-packet.h#L108


DNS 패킷의 헤더는 총 12바이트인데, 그중 2바이트가 바로 Flag이다. Flag는 총 2바이트, 즉 16비트로 구성되어 있는데, 그 요소는 다음과 같다. 다음의 그림에서 TC Flag가 바로 Truncated 되었는지를 표시해 주는 부분이다. 참고로, 코드에서 보면 총 9비트를 오른쪽으로 Shift 연산을 하는데, 그 이유는 네트워크 프로토콜은 사람이 읽기 쉽도록 Big Endian을 사용하기 때문이다. 즉, 왼쪽 비트가 MSB가 되므로, 9비트만큼 오른쪽으로 쉬프트 하면 TC 비트가 가장 오른쪽에 위치하게 된다.

 


실제로 Route53에 긴 txt 레코드를 넣고 dig 명령어를 수행해 보면 다음과 같이 Truncated 되었다는 에러를 볼 수 있다.


Wireshark를 통해서 Flag를 확인해 보면, 다음과 같이 TC Flag가 1로 변경된 것을 볼 수 있다.


실제로 응답 사이즈를 보면 512보다 큰 것을 볼 수 있다.


이외에도, 다양한 경우에 TCP를 사용하기도 한다.


예컨대, 매우 중요한 도메인에 대한 정보나 다수의 연속된 DNS 쿼리가 필요한 상황에서 TCP를 사용한다. UDP는 빠르고 효율적이지만, 신뢰성 면에서는 TCP보다 떨어진다. UDP는 패킷이 손실되거나 순서가 뒤바뀔 가능성이 있다. 반면, TCP는 패킷의 손실이나 순서 변경 없이 데이터를 전달한다. 따라서 중요한 DNS 쿼리나 응답이 유실되지 않고 정확히 도착해야 하는 상황에서는 TCP가 사용된다.


또한, DNS 존 전송(zone transfer)의 경우에도 TCP를 사용한다. DNS는 여러 서버에 걸쳐 분산된 데이터베이스와 같은 역할을 한다. 이 데이터베이스의 일부인 DNS 존(zone)은 여러 DNS 서버 간에 전송될 수 있다. 이러한 존 전송은 일반적으로 AXFR(전체 존 전송) 또는 IXFR(증분 존 전송)을 통해 이루어진다. 이때, TCP를 사용하여 데이터의 완전성과 일관성을 보장한다. TCP는 연결을 설정하고 데이터 전송 중 오류가 발생하면 재전송을 요청할 수 있기 때문에, 대용량 데이터를 전송할 때 매우 유용하다.



필자는 아직 경험한 적은 없지만, 만약 이와 같은 현상이 발생한다면 TCP 53번 포트를 열어야 한다는 사실을 몰랐을 수도 있겠다는 생각이 든다. 공부하는 김에 알아두면 좋은 포인트라서 글로 남겨보았다.



DNS를 직접 만들어보면서 공부하고 싶다면, Python으로 만들어보면서 공부해 보는 것을 추천한다!

링크: https://kmong.com/gig/576754



작가의 이전글 [Python] Multi-Threading & GIL
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari