---
# System prepended metadata

title: 網路是怎樣連接的(四)DNS
tags: [計算機網路, ' 應用層', ' DNS']

---

---
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協議，省去握手機制，假如發生掉包協議棧會立刻在重新發送消息