現代生活中,網絡可謂是無處不在,購物需要網絡,付款需要網絡,各種生活繳費需要網絡,在各行各業的工作中,更是離不開網絡。說到底,網絡的作用無非是支援計算機間進行資料交換。世界各地有着不計其數的網絡裝置,這些網絡裝置是如何有序正常的進行資料交流的呢?網絡以及各種協定的工作原理又是怎樣的呢?本系列部落格,我們将嘗試自上而下的對網路的工作原理進行介紹,從應用層開始,逐層向下,詳細的幫助你了解網絡的核心工作原理。當然,網絡協定多如牛毛,在網絡分層中每一層的知識也是非常浩渺,希望這些部落格可以起到抛磚引玉的作用,能夠使你對于天天使用的網際網路網絡在宏觀上有認識,在微觀上也有了解。
說到網絡,對于普通使用者來說,使用最多的可能就是浏覽各種網站了,雖然現在移動裝置上的app基本代替了傳統的pc應用和網站,但是這些app裡提供的資料本質上網站中提供的資料并無不同,使用的網絡技術并無不同。
我們知道,不論是通路網站還是app内進行接口請求,這些資料都是存儲在“伺服器”這種特殊的遠端裝置上的,要向伺服器擷取資料,首先我們需要找到伺服器的位置,這很好了解,隻有找到它,我們才能和它産生資料交流。網際網路無論多大,本質上依然是通過電纜、光纖或各種無線裝置這類連接配接媒體連接配接在一起的,如果一台裝置沒有硬體上連接配接入網際網路,那麼說破天我們也無法和它産生資料互動。要找到一台網際網路裝置,實際上是通過其實體mac位址來找到的,這就像現實中的門牌号一樣,每家的門牌号都不同,說到這,我們要再老生常談一下,抛出網絡分層模型給你看:
關于這個網絡分層模型,它在我們後面部落格中的出境還少不了,現在你可以先不用管它,你隻需要先知道實體層是負責裝置實體媒介相關的協定,資料鍊路層通過硬體的mac位址找到具體要網絡裝置,網絡層通過ip協定來封裝真實的mac位址,傳輸層是對網絡層的一種封裝,tcp,udp等傳輸協定在這一層工作,而最上層的應用層就是我們常說的網絡應用協定,如dns,http,https和fpt協定工作在這一層。
關于網絡分層模型,我們先把多說了,我們的宗旨是自上而下的了解網絡,那麼還是回到第一步來。我們在通路網站時,都會現在浏覽器輸入網站的位址,這通常是一個域名,例如我要通路自己的技術部落格網站,我會在浏覽器輸入如下的位址:
https://huishao.cc/
huishao.cc就是一個域名,首先隻通過域名我們是找不到要通路的對方伺服器的,這就好像現實中我要去小王家,可以我隻知道小王的名字“王某某”是無法找到他的家的,我需要有一個住址簿,告訴我小王究竟住在哪了,這樣我才能找到他。當然,此住址可能也不是真正的實體位置,可能是一個社群,比如小王住在“光明社群”,具體光明社群在哪,我們可以再通過檢視地圖擷取。對應到網際網路中,域名就是一個名字,它友善我們對網站進行記憶,ip位址則是要通路的對方在邏輯上的位址,這友善網際網路的網絡管理,最終的硬體位址則是真正的對方位置。ip位址到硬體位址的映射,等我們讨論到了再細聊,本篇部落格我們就說域名到ip位址映射這一過程。
現在你應該已經明确,要通過域名找到某個裝置,第一步是先得到此域名對應的ip位址,那麼此ip位址是怎麼得到的呢?首先,一定有一個地方維護了域名與ip位址的映射關系,如果你有過建站的經曆,那麼你一定進行過域名綁定操作,一個網站建成後,理論上就已經可以使用ip的方式來進行通路,但是為了易記和動态變動ip,通常會對其進行域名綁定。由域名擷取到ip的這一過程,我們稱之為域名解析。
域名解析是一種服務,提供域名解析服務的伺服器即是dns伺服器,下圖可以很形象的表示域名伺服器的工作方式:
可以發現,映射表中記錄了域名與ip間的映射關系,在實際的應用中,上圖中描述的場景看似可行,實際卻并非如此,世界上的域名與ip總數是一個非常龐大的數字,由一台伺服器來維護所有域名ip資訊幾乎不可能,而且對于域名解析服務,請求量是巨大的,會有大量的使用者頻繁的進行域名解析請求,單伺服器明顯是不能滿足需求的。是以,實際生産環境中的dns解析是采用層層遞進,多級緩存,遞歸查詢的方式進行的。再看下圖:
上圖看似複雜,實際上隻是描述了三個關鍵詞:層層遞進,多級緩存,遞歸查詢。
下面我們來解釋域名要解析成正确的ip位址,要經過的幾個重要過程。
1. 本機hosts檔案
本機hosts檔案是優先級最高的域名ip映射表,對于mac作業系統,這個檔案在根目錄的etc檔案夾下,我們可以直接将域名與對應的ip寫在這個檔案中,在進行域名解析時,首先會從這個檔案中找。廣播ip和本機ip對應的域名實際上就定義在這裡,如下:
你也可以在其中新增任意映射,例如将huishao.cc的域名映射到127.0.0.1的本機ip,儲存後,在浏覽器再輸入huishao.cc,你将無法再通路到珲少的部落格網站,如下圖所示:
更多時候,hosts的正确用法是開發應用程式時,測試環境和正式環境可以将域名配置到不同的ip,這樣無需應用程式代碼中做邏輯,隻需要切換hosts檔案即可實作環境的切換。
2. 本機應用緩存
本機應用緩存是多級緩存中的第一級,例如當我們在浏覽器中通路過某個域名後,其解析的結果會被浏覽器緩存下來,當我們再次通路這個域名時,其首先會檢查浏覽器緩存,如果緩存能夠命中此域名,則直接使用,緩存的有效時間會受ttl配置影響(我們後面會介紹)。
3. 本機系統緩存
與本機應用緩存類似,作業系統中也會有一份域名解析的緩存,如果本機應用緩存中沒有命中,會從作業系統緩存中檢查是否之前有過此域名的解析記錄。如果能夠命中則會直接使用。
4. 路由器域名解析緩存
如果本機系統緩存依然沒有命中,而你的裝置又是通過路由器接入的公網,此時你的域名解析服務很大可能是路由器提供的,可以打開網絡設定的dns一欄,觀察dns伺服器的位址,如果是192.168.x.x類型内網位址,則說明是由路由器來完成dns解析了。如下圖所示:
路由器内,實際上也會緩存一張dns解析表,會從其中尋找是否有可以命中的緩存,如果存在并且未過期,則直接使用。有時候,你會發現電腦可以直接使用ip通路網站但是無法使用域名進行通路,很大可能是路由器的dns服務出問題了,最簡單的解決方式就是将配置的dns伺服器ip位址改成公共的。
5. 通路本地域名伺服器
如果以上的緩存都沒有命中,那麼邏輯上我們就需要通過外網的dns服務來進行解析了,首先本地伺服器(ldns)來解析域名,這裡的本地伺服器是指城市或區域的dns伺服器,一般就有營運商部署在當地,距離近,性能好,并且也有緩存機制,幾乎可以覆寫大多數的域名解析請求。
6. 轉發與遞歸
如果你通路的域名比較冷門,本地伺服器依然無法解析,則會進行轉發,将此請求轉發到更進階的營運商dns伺服器或者根dns伺服器,根dns伺服器會根據域名來傳回頂級的域名伺服器位址,本地伺服器可以繼續向頂級域名伺服器請求解析。如此遞歸進行,直到解析成功,再将ip位址依次傳回到我們的裝置,并逐層做緩存,以便我們下次通路時可以快速得到響應。
上面過程中,我們有提到根域名伺服器,其是最進階别的域名伺服器,它負責傳回頂級域名伺服器,目前全球有13個根域名伺服器站。頂級域名伺服器用來針對某個頂級域名進行解析,例如.com頂級域名,.edu頂級域名,.cc頂級域名和.cn頂級域名等。頂級域名伺服器在解析時會将查詢到的主域名伺服器傳回。主域名伺服器負責某個區域的域名解析,同樣,主域名伺服器會配套輔助域名伺服器進行備份與分擔負載。
前面說了這麼多,都是宏觀上的認識。現在,我們要讨論一些更深入的東西了。雖然對于dns是幹什麼的,解析的過程是怎樣的我們有了一些了解。但是dns協定究竟是怎麼操作的呢?ip資料是怎麼得到的?我們可以手動來進行dns解析麼?要了解這些問題,首先需要對dns協定本身做個了解。
dns協定是工作在應用層的一種協定,全稱domain name system。dns協定是基于udp之上實作的,前面說過udp是工作在傳輸層的一種網絡協定,等我們說到它的時候再深入探讨。現在你隻需要知道,基于udp任何人都可以實作一個dns解析服務。dns解析分為兩步,首先需要用戶端向伺服器發送一個dns請求封包,伺服器收到封包,解析完成後再傳回一個dns封包給用戶端,此封包中就包含解析的資料。
dns協定規定其請求封包與響應封包的結構是一緻的,都包含header,question,answer,authority,additional這5個部分。
header部分的長度是一定的,固定為12個位元組。dns協定文檔中有一張圖,很好的描述了header的資料結構:
id:id占了兩個位元組,它是一個辨別符,由用戶端請求的時候填充,dns伺服器解析後,會将此id傳回,用來讓用戶端将響應與請求對應起來。
配置字段:上圖中第2行的都是配置字段,其占了兩個位元組。
qr占1為,設定為0表示目前是dns請求封包,設定為1表示目前為dns響應封包。
opcode占4位,此值由請求封包設定,并且被複制到響應封包傳回。其用來設定查詢的類型,設定為0表示标準查詢,即由域名解析出ip,設定為1表示反向查詢,即由ip反查出域名,設定為2用來查詢伺服器的狀态,3-15為保留字段,以待後續使用。
aa字段占1位,隻在傳回的響應封包中有,0表示傳回資料的伺服器不是權威伺服器,1表示傳回資料的伺服器是權威伺服器。需要注意,傳回的響應封包中可能有多個應答,此字段表明的是第一個應答的伺服器類型。
tc字段占1位,表示此封包是否由于資料的傳輸大小而被截斷,當此字段的為1時,資料不可信。
rd字段占1位,該值需要在請求封包中設定,響應封包會直接複制該值。此值表示是否希望伺服器進行遞歸查詢。
ra字段占1位,其在響應封包中設定,表示服務端是否支援遞歸查詢。
z字段占3位,是保留字段。
rcode字段占4位,是響應封包的響應碼,0表示沒有錯誤;1表示請求格式有誤,服務端無法解析;2表示伺服器出錯;3表示請求的域名不存在;4表示伺服器不支援這類請求;5表示伺服器拒絕此次請求;6-15是保留參數。
qdcount:占16位,表明question部分包含的執行個體個數,是無符号數。
ancount:占16位,表明answer部分包含的回答個數,是無符号數。
nscount:占16位,表明authority部分包含的授權伺服器數量,是無符号整數。
arcount:占16位,表明additional部分中包含的資源記錄數量,是無符号整數。
這個部分用來定義查詢的問題,問題的個數在qdcount指明,通常隻會攜帶一個問題。每個問題的格式定義如下:
qname:此部分位元組數不定,描述要查詢的域名。在解析的時候,這部分以0x00結尾。需要注意,域名通常由符号“.”進行分割,每段的長度不定,qname每段的開頭會先指明此段的長度,以huishao.cc域名為例,其構造出的qname部分如下:
0x07 0x68 0x75 0x69 0x73 0x68 0x61 0x6f 0x02 0x63 0x63 0x00
其中最後一個位元組0x00标記了qname部分的結束,0x07表示第一段的長度為7個位元組,即0x68 0x75 0x69 0x73 0x68 0x61 0x6f是第一段,通過查詢ascii碼對照表可知,這段資料就是huishao,同理,之後的一個位元組為0x02,表示第二段的長度為2個位元組,0x63對應ascii表中的字母c,最終可以解析為huishao.cc。
qtype:占兩個位元組,對應查詢的類型,定義如下:
type:意義
對應的值
a:ipv4主機位址
1
ns:權威域名伺服器
2
md:郵箱位址(棄用,使用mx)
3
mf:轉發郵箱(棄用,使用mx)
4
cname:規範的别名
5
soa:标記權威區域開始
6
mb:郵箱域名
7
mg:郵箱成員
8
mr:郵箱重命名域名
9
null:空的類型
10
wks:服務描述
11
ptr:域名指針
12
hinfo:主機資訊
13
minfo:郵箱或者郵件清單資訊
14
mx:郵件交換
15
txt:字元串
16
aaaa: ipv6域名
28
上面列舉的查詢類型中,有兩個我們需要額外關注,a和cname,a類型即是我們查詢域名ip所要使用的,cname别名技術也很常用,後面會介紹。
qclass:占兩個位元組,表明查詢的類别,定義如下:
class:意義
in:internet查詢
cs:棄用,rfc查詢
ch:the choas class
hs:hesiod
進行dns解析時,隻需要設定成in類即可。
這部分是響應的傳回資料,可能包含多條資源記錄,其格式如下:
name:此記錄所屬的域名,長度不定,需要注意,這一部分存放的可能是真正的域名(格式和qname一緻),也可能是指針,指向真正存放域名的位元組位置,甚至可以是一部分是域名,一部分是指針。這樣做的好處是可以節省響應封包的資料空間,當檢查到某個位元組的高兩位為11時,則此位元組及之後一個位元組就是一個指針。例如對于huishao.cc域名的解析,其響應的完整的dns封包如下(16進制):
b3 a4 81 80 00 01 00 01 00 00 00 00 07 68 75 69
73 68 61 6f 02 63 63 00 00 01 00 01 c0 0c 00 01
00 01 00 00 02 58 00 04 b9 c7 6d 99
其中開頭的12個位元組為header部,随後的16個位元組為question部,後面的即為answer部,answer部分開頭的c0位元組高兩位為11,表明其是一個指針,占兩個位元組,c0,0c兩個位元組将前兩位的1去掉後為十進制數12,表明name的真實值在第12個位元組處開始,即複用了qname的資料。
type:占兩個位元組,與qtype定義一緻。
class:占兩個位元組,與qclass定義一緻。
ttl:占4個位元組,此字段非常重要,标記了緩存的有效時長,機關是秒。順便分析一下上面的資料,此dns解析資料的緩存有效期為0x0258,即600秒,10分鐘。
rdlength:占兩個位元組,表明rdata字段的位元組數。
rdata:真正的解析資料,與type有關,如果是ipv4域名解析,此處為解析的結果。
這兩部分的資料結構與answer部分完全一緻,解析方式也完全一緻。
通過前面的介紹,dns協定的工作原理應該是明了了,如果需要更深入的了解細節,可以閱讀其官方的文檔:
https://datatracker.ietf.org/doc/html/rfc1035
當然,如果你還是感覺雲裡霧裡也沒有關系,我們通過實踐來驗證理論。
wireshark是一個網絡封包分析軟體,能夠截取網絡封包,對于網絡傳輸的資料包進行分析十分友善。我們打開此軟體後,找一個域名進行通路,即可抓取到對應的dns資料包,以huishao.cc為例,如下圖所示:
可以看到,wireshark可以分析出此次網絡互動的時間,發起方ip,目标方ip,協定類型,資料長度和相信資訊。在上面的示例中,第一條記錄是dns請求封包,第二條記錄是dns響應封包。我們先看看dns請求封包的資料:
可以看到,wireshark将每一層網絡協定都分析了出來,我們先隻關注最上層的domian name system部分,這部分的十六進制資料是上圖中選中的部分。可以發現其和我們上面介紹的協定格式是一一對應的。在看響應封包:
資料的格式也是完全對應的,理論誠不欺我啊。
下面,我們可以以huishao.cc域名為例,手動使用udp協定來試一試發送dns請求以及對請求到的資料進行解析。首先先看完整的測試代碼:
上面的代碼有詳細的注釋,你可以嘗試運作下進行域名解析,需要注意,上面填寫的192.168.1.1是本地路由器的域名伺服器位址,你需要将其替換成自己的,當然你也可以使用通用的域名解析伺服器,如114.114.114.144。上面的代碼采用c語言編寫,是以在處理資料的時候會有一些複雜,有些點需要注意。
簡單了解,位域可以讓結構體中的資料以byte為機關已經存儲,例如上面定義的dns_header結構體,我們按照dns協定的結構對其内資料所占的位進行了定義,有一點需要額外注意,在定義結構體時,位域字段的順序與實際填充的順序是相反的,位域的填充是從低位元組開始的,如上代碼所示,對于1個位元組的位域來說,我們定義的時候,先定義的rd字段,最後定義的qr字段,實際在存儲資料時,這一個位元組的最高位會存儲qr,最低位會存儲rd。
在定義結構體時,還有一個細節需要注意,如果結構體中的資料位元組數不是一緻的,則其建立的記憶體大小可能和實際所需要的并不一緻,例如r_data結構體,其中有int和short類型的資料,則其會以4位元組為标準進行對齊,我們需要手動設定其對齊位數,不然後續資料填充時會出現偏差。
網絡位元組序是tcp/ip協定中定義的一種資料格式,其采用的是大端(big-endian)的排序方式,即對于一個字(兩個位元組)的資料,低位元組在前,高位元組在後。這與我們可讀的主機位元組序剛好是相反的,在c語言中,使用htons可以把short類型的資料進行網絡和主機位元組序的轉換,htonl把long類型的資料進行網絡和主機位元組序的轉換。
可以在如下位址下載下傳到完整的上述代碼:
https://gitee.com/jaki/dns_c
溫馨提示,上面代碼中解析的域名隻傳回了一個a類型的解析應答,如果你解析其他域名,可能會有很多cname類型的應答,應答個數也可能不止一個,你可以嘗試下優化下代碼,完整的實作dns的解析邏輯。
本篇部落格到此就結束了,我相信你對從域名擷取到ip的過程有了更多的認識,如果遇到了域名解析的問題,你應該明白如何檢視響應結果來定位問題了,但是,這隻是我們日常使用的網絡中的第一步,目前我們連應用層的核心都還沒有接觸到,不積跬步,無以至千裡,與君共勉。
專注技術,熱愛生活,交流技術,也做朋友。 ——珲少 qq:316045346