007. 從零開始搭建 DNS 服務器——入門篇
前言
簡單來說,域名解析協(xié)議(DNS,Domain Name System)是一個應用層協(xié)議,可以將域名和 IP 地址相互映射,使人更方便地訪問互聯(lián)網(wǎng),而不用去記住復雜的 IP 地址。
DNS 協(xié)議建立在 UDP 或 TCP 協(xié)議之上,默認使用 53 號端口??蛻舳四J通過 UDP 協(xié)議進行通訊,本文就以 UDP 協(xié)議進行講解,不考慮報文長度超過了 512 字節(jié)使用 TCP 協(xié)議的情況。
了解了 DNS 的基本原理,我們就可以寫一個最簡單的?DNS 服務器了。在入門階段,這個簡單的 DNS 服務器,僅具有最基本的“代理”功能。
即我們的 DNS 服務器在接到客戶端發(fā)來的 DNS 請求后,直接將請求轉(zhuǎn)發(fā)到其他可用的 DNS服務器(如114.114.114.114)上,而不對報文做任何修改。待 DNS 響應后,直接將響應結(jié)果返回給 DNS 客戶端。
實現(xiàn)
DNS 服務器需要 bind 到 53 端口,而且采用 UDP 協(xié)議。先導入 Python 標準庫:socket,用?socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
?走的是 UDP 協(xié)議。
import socket
if __name__ == '__main__':
? ?# 建立一個套接字對象(UDP),負責接受 DNS 報文
? ?sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
? ?# 綁定到 53 端口
? ?sk.bind(('0.0.0.0', 53))
? ?print('dns server is started')
在 DNS 服務器運行的邏輯中,使用?While True 循環(huán),一直接受來自客戶端的請求。接收到的數(shù)據(jù)是一個兩元組,解包得到報文消息和客戶端 IP 地址。
while True:
? ?message, addr = sk.recvfrom(1024)
新建另一個 socket 對象,直接將報文轉(zhuǎn)發(fā)給 114 DNS。
# 將請求轉(zhuǎn)發(fā)到 114 DNS
redirect_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
redirect_socket.sendto(message, ('114.114.114.114', 53))
最后將響應數(shù)據(jù)返回給客戶端,一次 DNS 請求處理完成。
response_data, _ = redirect_socket.recvfrom(1024)
# 將114響應響應給客戶
sk.sendto(response_data, addr)
完整代碼如下:
# coding:utf-8
# @Author:Gscsd
import socket
if __name__ == '__main__':
? ?# 建立一個套接字對象(UDP),負責接受 DNS 報文
? ?sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
? ?# 綁定到 53 端口
? ?sk.bind(('0.0.0.0', 53))
? ?print('dns server is started')
? ?while True:
? ? ? ?message, addr = sk.recvfrom(1024)
? ? ? ?# 將請求轉(zhuǎn)發(fā)到 114 DNS
? ? ? ?redirect_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
? ? ? ?redirect_socket.sendto(message, ('114.114.114.114', 53))
? ? ? ?response_data, _ = redirect_socket.recvfrom(1024)
? ? ? ?# 將114響應響應給客戶
? ? ? ?sk.sendto(response_data, addr)
總結(jié)
這樣,我們就實現(xiàn)了一個極其簡單的 DNS 服務器(或者說是DNS 代理服務器)。審視這十來行代碼,還是有很多不足之處:
單純轉(zhuǎn)發(fā)數(shù)據(jù),徒勞無功,不如直接請求 114 DNS;
無法自定義 IP 與域名映射,即自定義記錄;
沒有緩存機制,每次請求都要向 114 DNS 請求;
沒有處理客戶端請求的錯誤,如請求超時等;
只能處理單個請求,沒有并發(fā)處理能力。
這些不足將會在以后的文章中加以改進,敬請關(guān)注。
延伸閱讀
超詳細 DNS 協(xié)議解析:https://zhuanlan.zhihu.com/p/351059293
socket --- 底層網(wǎng)絡接口 — Python 3.11.1 文檔:https://docs.python.org/zh-cn/3/library/socket.html
DNS協(xié)議:https://www.jianshu.com/p/915de89d070e