--- creator: zach tags: 計算機網路, 應用層, DNS created: 2021-09-14 --- # 網路是怎樣連接的(四)DNS ## 思考重點 - 如何利用DNS協議查找相應的IP位置? - 服務器是怎麼存儲那麼多相應的域名消息? - DNS是使用TCP還是UDP協議為基礎傳輸? ## 核心知識 ![](https://i.imgur.com/JVOYNbf.png) <center>DNS核心知識點</center> </BR> ## 為甚麼我們需要DNS DNS是一個與HTTP同樣位於應用層的協議,它提供了域名轉IP地址的解析服務[^1] 不過問題來了,既然電腦(計算機)可以同時擁有域名與IP地址,何不使用域名來表示?同理為何我們輸入網址時不直接使用IP作為輸入? ### IP太難記啦 TCP/IP為了識別網路中的主機,會分配唯一的IP地址給主機,再依據IP地址來進行通訊,但由於真的很難記住,於是TCP/IP協議就利用主機名幫每一台電腦命名,也就是域名系統 其實我們可以想像使用域名地址與IP地址的兩種場景,假設今天我們手上沒有任何記事工具,單純靠記憶記住該網站的訪問地址,哪一種地址解釋方法更為直觀、好懂? - www.example.com (域名) - 123.123.123.12 (IP地址) 可能有人不服,這種簡單的數學排列組合那可是輕而易舉,背個100條也是小菜一疊,不過以面向使用者的,或者單就效率來說,**利用最節省資源的方法,使更多人能夠理解與運用**才是更好的方法。既然如此何不讓看得懂文字的使用者使用域名系統,讓腦子裡只有01的電腦使用IP地址呢? 你可以把DNS想像成是一個儲存域名地址與IP地址對應關係的資料庫,電腦會向DNS詢問這個域名對應的IP地址是多少 [^1]:其實DNS也提供IP轉域名的查找服務 ## DNS原理 ![](https://i.imgur.com/3FE0lFG.png) <center>DNS原理框架</center> </BR> 從圖中我們可以看到DNS服務器的工作就是返回請求的IP地址給主機,每台主機中都會存儲預設的DNS服務器地址,當主機想要查詢IP地址時,都會委託它進行查找。不過問題來了,世界上的網站是何其多,一個服務器是沒有辦法一次儲存那麼多條地址訊息的,到底DNS服務器們是怎麼達成這個任務的? ### 域名結構 ![](https://i.imgur.com/nreecW8.png) <center>服務器階級</center> </BR> 真實的網路世界往往存在無數多台服務器,而且每一個域名都是不可重複的,想讓每一台DNS服務器記下所有域名對應的IP地址是不切實際的,因此工程師們想出一個辦法,何不讓DNS服務器們**合力**記住所有對應地址? 因此設計者使用分布式分層架構來設計整個DNS系統,讓下級的DNS服務器的IP註冊到它們的上級,上級服務器再註冊IP到它們的更上級,從而使我們可以從最高級別DNS服務器完成域名查詢任務 當主機端發起請求時,DNS服務器就可以直接回復儲存的地址,或者可以向主機端提供更低階級同時也更仔細的DNS服務器地址,讓主機向指定DNS服務器發起請求,如此環環相扣的連續收發,最終可以取得對應的IP地址 ![](https://i.imgur.com/LRQwpVI.png) <center>網址的域名層級</center> </BR> DNS服務器的分層架構如上圖所示,還記得我們的電腦會儲存離主機最近的DNS服務器地址嗎,首先該DNS服務器會向*root(根域服務器)*[^2]發起請求,而根域服務器,你可以想像成一切的開端,它儲存了多個頂級網域服務器地址,因此透過根域服務器可以率先訪問儲存最高等級域名的DNS服務器,以此類推,當到達第一層級域名服務器時,我們會查詢它儲存的第二級域名,有點像偵探辦案一樣一個線索一個線索找出最終答案 另外我們也發現既然層次最高的是國家或地區頂級域,但是有些網站並沒有填入阿,依然是.com或.edu結尾,這到底是怎麼一回事?其實.com是代表國際域名,而.com.tw則是台灣域名,它們都代表著第一層級域名,差別只在於搜尋的優先度,假若我們使用.com.tw那麼網頁搜尋就會以台灣範圍為優先 ### 尋找指定DNS服務器 ![](https://i.imgur.com/0WDZh5m.png) <center>搜尋指定DNS服務器</center> </BR> 我們從DNS原理框架圖可以看出,主機端會讓它儲存的預設DNS服務器代替他來進行DNS域名查找,而我們也知道所有服務器都擁有根域服務器地址,有了根域地址就可以展開一系列的尋址動作啦 第一步讓我們來看看域名吧,URL的最右側域名區段是.com.tw(最高級別),該服務器可能儲存的資料如下表: 域名|IP地址|Class|Type :--:|:--:|:--:|:--: myblog.com.tw|192.168.0.1|IN|A herblog.com.tw|192.168.0.2|IN|A hisblog.com.tw|192.168.0.3|IN|A www.haha.com.tw|192.168.0.4|IN|A 其中域名列就是該服務器上總共儲存幾條域名消息,我們可以看到域名消息不一定通通都是同樣類型的,它可能有不同等級的域名,或者不同的儲存類型(A代表域名對應的響應數據是IP地址),而Class In代表Internet 有了! myblog.com.tw這條域名最接近我們的請求域名,於是DNS服務器將它代表的IP地址返回給預設DNS服務器,好讓它向下一台DNS服務器發起請求,這一系列動作會直到被訪問的DNS服務器確實擁有完整的請求域名為止,我們用找人問事來比喻DNS的查找行為 </BR> ![](https://i.imgur.com/RwQZXrE.png) <center> 以找人問事來比喻DNS搜尋</center> </br> 由此可知預設的DNS服務器會代替主機從最高級別一步步搜尋完整的域名地址,當被訪問DNS服務器在接收到請求時,會查找保存的資料表,並把它所知道的最接近域名的IP地址回報出去 一旦預設的DNS服務器在某台DNS服務器上找到儲存完整域名的服務器地址時(這時它訪問到的只是儲存該條域名的服務器地址),將會主動將該服務器IP地址告知主機端,讓主機端與這台服務器直接建立溝通,以獲取最終的IP地址 [^2]:根域約儲存13個第一層級IP地址,另外根域服務器地址存在於所有DNS服務器中 ## Resolver *Resolver*又名解析器,是DNS查找原理的實現,因為應用層不負責連線的工作,因此必須仰賴OS調用Socket函式庫來進行操作,**解析器其實就是Socket函式庫中的一個function API**,我們在上面也介紹了,主機端向DNS服務器發起請求,發起請求就代表一定存在一對服務器端與客戶端,**解析器就擔當主機上的DNS客戶端角色** ```c #include <netdb.h> #include <sys/socket.h> ... address = gethostbyname("www.myblog.com..tw"); //解析器 ... ``` ![](https://i.imgur.com/QGMtw6S.png) <center>解析器運作原理</center> </br> 上圖簡單的說明了應用程式調用了 socket庫函式中的gethostbyname()[^3]後會發生的事。首先函式會根據DNS服務器的類型規格生成特定的DNS請求(二進制形式)接下來OS會委託*Protocol Stack(協議棧或協議疊)[^4]來執行DNS請求的發送[^5],該請求會透過網卡確實傳到DNS手裡,這裡剛好沿續上一小節所講的尋找指定DNS服務器,存儲對應IP位置的DNS服務器會將響應結果透過網卡回傳給主機端,經過協議棧的提取與處理,最終將IP地址消息回傳給指定變數,既然有了IP地址應用程式就可以考慮發送請求的事啦。 [^3]:目前gethostbyname()已經被淘汰,改而使用功能更強大的getaddrinfo() [^4]:協議棧是OS內部關於網路協定的軟體實現 [^5]:傳輸層使用UDP協議,省去握手機制,假如發生掉包協議棧會立刻在重新發送消息