實作一個 DNS 客戶端,輸入域名(如 www.baidu.com),透過 UDP Port 53 向 DNS 伺服器(如 114.114.114.114)發送查詢,解析回應封包取得 IP 列表、CName 別名與查詢耗時。
標準 DNS 查詢/回應用 UDP(低開銷),Zone Transfer 用 TCP。本實作採 UDP Port 53。
Header (12 bytes): ID(2) | Flags(2) | QDCOUNT(2) | ANCOUNT(2) | NSCOUNT(2) | ARCOUNT(2) Question Section (QDCOUNT entries): Name (variable) | Type(2) | Class(2) Answer Section (ANCOUNT entries): Name | Type | Class | TTL(4) | RDLENGTH(2) | RDATA(variable)
重點欄位:
QR=0 查詢、1 回應。Type=1 (A) IPv4 位址、5 (CNAME) 規范名稱。Class=1 (IN) Internet。長度(1B) + 標籤內容,以 0 結束。例:www.baidu.com → 03 77 77 77 05 62 61 69 64 75 03 63 6f 6d 00。11,後 14 位為相對封包起始位移。支援巢狀指標。CDNSLookupclass CDNSLookup {
public:
BOOL DNSLookup(ULONG dnsIP, char* domain,
vector* ipList = nullptr,
vector* cnameList = nullptr,
ULONG timeout = 1000, ULONG* timeSpent = nullptr);
private:
BOOL SendDNSRequest(sockaddr_in&, char* domain);
BOOL RecvDNSResponse(sockaddr_in&, ULONG timeout,
vector*, vector*, ULONG*);
BOOL EncodeDotStr(char* dot, char* encoded, USHORT maxLen);
BOOL DecodeDotStr(char* encoded, USHORT* encLen,
char* dot, USHORT maxDot, char* pktStart = nullptr);
// ... 初始化/清理、Socket 管理
};
SendDNSRequest:構建 Header(ID=ProcessID, Flags=0x0100, QDCOUNT=1)、編碼域名、附加 Type=A/Class=IN、sendto 發送。RecvDNSResponse:WSAWaitForMultipleEvents 等待回應、recvfrom 接收、驗證 ID/Flags、解析 Question 區、迴圈解析 Answer 區(Type=A 存 IP、Type=CNAME 遞迴解碼 Name)、計算耗時。DecodeDotStr 遞迴處理壓縮指標,需傳入封包起始位址以計算絕對位移。int main() {
CDNSLookup dns;
vector ips, cnames;
ULONG ms = 0;
BOOL ok = dns.DNSLookup(inet_addr("114.114.114.114"),
"www.baidu.com", &ips, &cnames, 1000, &ms);
if (!ok) { printf("timeout\n"); return -1; }
for (int i=0;i
輸出範例
IP1 = 180.101.49.11
IP2 = 180.101.49.12
CName1 = www.a.shifen.com
time spent = 23 ms