W3150A+評估板–EVB-PIC24 用戶手冊(三)

昨天給大家講了關於W3150A+評估板–EVB-PIC24 關於程序員指南中,關於內存映射和EVB B/D固件的編譯及管理程序的一些內容。

今天給大家介紹 程序員指南中,關於EVB B/D固件中應用的問題,包含迴路程序, 網絡服務器,和DHCP 客戶端. 通過管理程序選擇應用.

3.2.6.             應用

它是一個用iinChip™的網絡應用,它包含迴路程序, 網絡服務器,和DHCP 客戶端. 通過管理程序選擇應用.

 3.2.6.1.     迴路 TCP 服務器

迴路TCP服務器程序中, EVB B/D 工作在服務器模式, AX1, PC 測試程序工作在客戶端模式. AX1 連接到EVB B/D, 如果連接成功, AX1 通過 TCP信道發送數據流. EVB B/D通過TCP信道不經過處理髮揮數據流.

迴路TCP 服務器程序使用loopback_tcps() , <圖 3.14> 展示了loopback_tcps()的處理流程.

 <圖 3.14 : loopback_tcps() >

<表 3‑23: loopback_tcps()中的參考函數>

如果服務器socket在SOCK_CLOSED 狀態,loopback_tcps()用Sn_MR_TCP, 監聽端口號和 Option Flag等元素調用socket() 來創建TCP服務器socket.

 socket() 函數改變socket狀態為SOCK_INIT而不顧原來的socket狀態.如果服務器socket創建成功, 在以參數用服務器socket調用listen()之後,它處於TCP 服務器模式.listen()使得服務器socket 狀態為監聽狀態並保持堅挺狀態直到任何客戶端的連接. 

此時, 當任一客戶端嘗試連接到服務器socket時,服務器 socket 狀態從 “listen” 改為 “established”. 此時客戶端和服務器之間的連接建立,在SOCK_ESTABLISHED狀態可以進行數據傳送.

數據的傳送在SOCK_ESTABLISHED中使用 recv() 和send()實現.這裡的數據傳送是EVB B/D(服務器) 和AX1(客戶端)之間1對1的傳送. 

 在SOCK_ESTABLISHED狀態,如果客戶端請求關閉連接, 服務器 socket狀態將從 SOCK_ESTABLISHED 改變為SOCK_CLOSE_WAIT. 在SOCK_CLOSE_WAIT 狀態,不能進行數據通信並且必須關閉服務器 socket. 在 SOCK_CLOSE_WAIT狀態, 調用disconnect() 關閉socket. disconnect() 改變socket 狀態為SOCK_CLOSED 而不顧以前的socket狀態. 

3.2.6.2.     迴路 TCP 客戶端

在 迴路TCP 客戶端程序, EVB B/D 工作在客戶端模式 , AX1, PC 測試程序工作在服務器模式. EVB B/D 嘗試連接到以服務器狀態等待的 AX1, 如果連接成功,EVB B/D 通過TCP 信道接收數據流, 然後 EVB B/D將接收到的數據流不經過處理髮送給AX1.

 迴路 TCP 客戶端程序是用 loopback_tcpc() 創建的, <圖3.15>是loopback_tcpc()處理的進程.

如果客戶端socket在SOCK_CLOSED 狀態, loopback_tcpc()用Sn_MR_TCP, Any Port Number和Option Flag等元素調用socket()以創建TCP客戶端socket.

 這裡調用socket, get_system_any_port()使用任意端口號. 這是因為如果用同樣的端口號嘗試連接到同樣的服務器連接有可能失敗. 成功創建socket後, 用服務器socket的元素調用 connect()以連接到AX1服務器.

  connect()使得socket狀態變成SOCK_SYNSENT,並使狀態保持在SOCK_SYNSENT直到收到來自服務器的鏈接授權. 如果連接成功,socket狀態從SOCK_SYNSENT改變為SOCK_ESTABLISHED. 在 SOCK_ESTABLISHED 狀態,操作和loopback_tcps()一樣. 

<圖 3.15: loopback_tcpc()>

<表 3‑24: loopback_tcpc()中的參考函數>

3.2.6.3.     迴路 UDP

迴路UDP 程序用UDP協議的單播數據報通信.它的操作和迴路TCP 服務器/客戶端 程序一樣. UDP 通信包括單播數據報通信和廣播數據報通信, 基本上支持用一個信道實現多目的地的1到多通信. 

 迴路 UDP 程序使用loopback_udp() , <圖 3-16> 是 loopback_udp()處理進程.

<圖 3.16: loopback_udp()>

 

<表 3‑25: loopback_udp()的參考函數>

如果udp socket 在 SOCK_CLOSED 狀態, socket() 用Sn_MR_UDP, Port Number和Option Flag等元素調用以創建UDP socket.

UDP通信和TCP相反, 是不需要連接進程請求的數據報通信. 因此在socket建立後可立刻進行直接數據通信. 在創建 UDP socket後, udp socket 的狀態將從SOCK_CLOSED改為SOCK_UDP.

這裡, 不像TCP 數據通信使用send() 和recv(), 而是使用sendto()和ecvfrom().  

 這是因為TCP是已知目的地的一對一通信方法,但是UDP是不需要連接進程的一對多通信. sendto()發送數據到特定目的地的特定端口, recvfrom() 用於接收來自臨時端口的入數據. 來自recvfrom()的目的信息將被用目的地址和目的端口告知用戶,目的地址和目的端口以元素的形式發送.

在loopback_udp()中, 沒有使用close()的例子, 但是萬一不再需要UDP 通信, 同樣可以調用close() 並關閉udp socket.  

 3.2.6.4.     網絡服務器

網絡服務器程序是一個TCP服務器程序,它使用基於TCP協議的HTTP協議. 在創建網絡服務器程序之前, 需要理解在網絡服務器和網絡客戶端(網絡瀏覽器)之間發送的HTTP協議消息結構.HTTP, 是超文本傳輸協議的縮寫,用於基於因特網的網絡服務器和客戶端瀏覽器之間的傳輸協議.

<表 3‑26: 網絡瀏覽器的 HTTP 請求運行進程 >

網絡服務器程序分析從網絡瀏覽器接收到的 HTTP請求消息的方法和URI(統一資源標識符), 如果相關的URI只是簡單的請求網頁, 就發送網頁. 如果請求是諸如CGI(公共網關接口)的動作,就接收該動作並在網頁中顯示結果.

 <圖 3.17> 是網絡服務器和網絡客戶端之間的HTTP 消息流,<表 3-28> 是HTTP消息結構.

<圖 3.17: HTTP 消息流>

<表 3‑27: HTTP消息格式>

 

想了解更多有關HTTP消息的信息,請參考 RFC2616. HTTP 請求消息隨着網絡瀏覽器的不同而不同. 

 

<表 3-29> 是Windows 2000上的Internet Explores和EVB B/D的HTTP消息通信.

<表 3‑28: EVB B/D和網絡瀏覽器之間的HTTP消息>

 

網絡瀏覽器程序由管理HTTP服務器socket的web_server()和管理HTTP消息的 proc_http() 組成。

<圖 3.18> 是處理進程.

 

<圖 3.18: web_server()>

因為web_server() 是TCP服務器程序,它的創建和 Chapter 3.2.6.1所解釋的loopback_tcps()相似. Difference between web_server()和loopback_tcps() 不同之處在於數據通信代碼. web_server() 調用 proc_http(),proc_http()處理來自在http socket的SOCK_ESTABLISHED上的網絡瀏覽器的HTTP請求消息.  

 在調用proc_http()函數後, web_server()處於等待狀態,知道收到來自網絡瀏覽器的HTTP請求消息的HTTP 回復消息, 然後調用 disconnect() 關閉 http socket.

這種 socket 關閉叫做Active Close, 在這種情況下, EVB B/D 先請求關閉客戶端. 如果你喜歡,也可使用 Passive Close,它是客戶端先提出斷開連接請求. 網絡瀏覽器支持Active Close的原因是因為 EVB B/D 支持其他客戶端的鏈接.

<圖 3.19: proc_http()>

proc_http() 調用 parse_http_request() 以分析接收到的網絡瀏覽器的 HTTP 請求消息.被分析的HTTP請求消息的METHOD是“GET”, “HEAD”或 “POST”, 將調用get_http_uri_name()並且URI Name將從HTTP請求消息中提取出來.如果提取的URI Name是“/” ,就用“index.html”代替URI Name “/” ,“index.html”是EVB B/D 的網絡瀏覽器默認頁面,因為這意味着網絡瀏覽器在請求網絡服務器的默認頁面.

通過調用find_http_uri_type()獲得HTTP請求消息的HTTP Request Type之後,如果HTTP Request Type是 “CGI” ,就執行相關的 CGI 命令進程.

 在處理CGI 命令後,或HTTP Request Type不是CGI, 用內置在EVB B/D 的ROM File Image的URI Name搜索文件.

如果找到了文件, 創建 HTTP回復消息並發送. 

 HTTP回復消息由HTTP Response Header 傳送和HTTP Response Body傳送組成. 傳送 HTTP Response Header時, 用HTTP Request Type作為元素調用make_http_response_head() 以創建 HTTP Response Header. 傳送完已創建的HTTP Response Header後,再傳送HTTP Response Bodyd. 例如, 如果 HTTP Response body 是 ROM File Image中的任意文件, 則比iinChip™的 MTU大得多. 因此在傳送之前需要分開為iinChip™的最大大小. 此時,如果定義在EVB B/D中的系統環境變量存在於 HTTP Response Body 中, 就調用 replace_sys_env_value() 並用存儲在EVB B/D中的系統環境值代替系統環境變量.

<表 3‑29: “evbctrl.html” 系統環境變量使用>

<表 3-30> EVB B/D的ROM File Image的部分“evbctrl.html”.

 系統環境變量的長度被定義成將被替代的系統環境值的長度. 例如, 如果EVB的源IP地址是最長16位的字符串. 因此 $SRC_IP_ADDRESS$的長度也是16. EVB B/D 的‘ROM File System’ 可用WIZnet提供的 “ROMFileMaker.exe”創建. 請參考 “ROM File Maker Manual Vx.x.pdf” 以獲得更多信息.

  HTTP 請求消息能通過parse_http_request()分成 Method和Request-URI ,並存儲在<表 3-31>定義的 ‘st_http_request’ Date Type中. 它用get_http_uri_type()得到請求URI Type.

<表 3‑30: “st_http_request” 數據>

<圖 3.20: parse_http_request()>

<圖 3.21: find_http_uri_type()>

Request-URI 保存在st_http_request 的URI [MAX_URI_SIZE]中,它在“?”符號前有 URI Name,在“?”符號後有Query String. 當 Request-URI 從網絡瀏覽器傳送到網絡服務器時, SP(Space)以‘+’的形式發送,其他Reserved Texts 以“%HEXHEX”的形式發送.相應地, Request-URI中的Reserved Texts需要被譯碼成原來的值, 從 ‘+’ 到 SP,從%HEXHEX到相應的ASCII值. Request-URI譯碼的詳細信息請參考RFC1738.用get_http_uri_name()提取Request-URI的URI name. Request-URI的Query String 可以包含一到多個用“&”作為分隔符的 “variable=value” 對. 通過get_http_param_value()函數,可以在Query String提取相應的變量值.

<圖 3.22: get_http_uri_name() & get_http_parse_value()>

Web Server Program EVB B/D網絡瀏覽器程序的CGI處理和一般的基於OS的網絡瀏覽器程序不相同.基於OS的網絡瀏覽器程序創建單獨的進程獨立地處理處理器之間的通信. 然而, EVB B/D網絡服務器是無操作系統的, 因此它不是創建獨立的進程, 而是調用相關函數直接進行CGI處理. EVB B/D 支持更新網絡信息的 “NETCONF.CGI” 以及控制文本LCD和EVB B/D D1/D2 LED 的“LCDNLED.CGI” .<圖 3.23>和 <圖 3.24> 是兩個CGI進程圖. 

 

<圖 3.23: NETCONF.CGI 處理>

<圖 3.24: LCDNLED.CGI 處理>

NETCONF.CGI的<FORM>以“POST” Method提交. 用“POST” Method提交<FORM> 不是提交在 Query String而是提交在HTTP請求消息的. NETCONF.CGI的這些參數值同樣用於使用get_http_param_value()函數來提取相關參數值. 

of LCDNLED.CGI的<FORM> 用“GET” Method提交, <FORM> 以 “GET” Method提交,是提交在Request-URI的Query String. Request-URI的Query String提交的參數同樣可以用get_http_param_value()函數提取參數值. 

<表 3‑31: web_server()相關函數>

3.2.6.5.     DHCP 客戶端

DHCP客戶端程序從網絡中的DHCP服務器分配網絡信息. 注意,如果DHCP客戶端程序必須在其他程序之前開始,那是因為它管理網絡信息設置. 首先, 回顧DHCP(動態主機配置協議)的基本信息, 然後得到 DHCP 客戶端程序的進一步使用.  

 DHCP在傳輸層使用 UDP協議,用UDP 廣播與DHCP服務器通信. 使用廣播的原因是因為它沒有IP地址,且不知道DHCP 服務器IP 地址. 當UDP 在iinChip™里廣播時,目的 IP 地址需設置為255.255.255.255’ 以廣播包傳送.

<圖3.25> 是DHCP 服務器和客戶端之間的消息流.

<圖 3.25: DHCP 消息流>

首先, DHCP客戶端廣播DISCOVERY 消息到本地網絡. 如果DHCP服務器存在於網絡中, DHCP服務器接收到 Discovery 消息,並且提供諸如IP,網關IP, 子網掩碼和DNS服務器IP之類的能被DHCP客戶端使用的網絡信息以及如作為OFFER消息到DHCP客戶端的租約時間等信息. DHCP客戶端通過接收OFFER消息能檢測到DHCP服務器,並發送REQUEST 消息到DHCP服務器以便使用DHCP服務器建議的網絡信息. 接收到來自DHCP客戶端的 REQUEST 消息後, DHCP服務器核實是否請求的網絡信息可用.如果可用, DHCP服務器發送ACK 消息, 如果無效, NACK消息被發送到 DHCP客戶端. 接收到來自DHCP服務器的 ACK 消息後, DHCP 客戶端使用提供的網絡信息. 網絡信息只有在DHCP服務器建議的租約時間內才有效.因此, 如果DHCP 客戶端想繼續使用網絡信息,通常要在一半的租約時間之後重新發送 REQUEST消息到DHCP服務器以保持網絡信息. 在該進程中, DHCP 客戶端可以從DHCP服務器獲得一樣的或是新的網絡信息. 如果接收到新的網絡信息,就一定要使用新的網絡信息.

 DHCP服務器和客戶端之間的消息格式如<圖 3.26>所示,有544字節. 請參考‘RFC1541’ 文檔以獲得DHCP消息格式每個區的詳細解釋. 第一字節的op 區 決定請求/回復, ciaddr之後的區用於傳送網絡信息, 312字節的可選區用於傳送消息類型或客戶端標識符之類的信息.

<圖 3.26: DHCP 消息格式>

 <表 3‑32: DHCP消息數據類型>

 

<圖 3.26>所示的DHCP消息通過<表 3-33>所定義的RIP_MSG數據類型管理. 請參考“inet/dhcp.h”

 

 簡單看一些DHCP消息的選項字段, 選項字段有 <圖 3.27>所示的格式, 它包括 Magic Cookie Field, 一個4字節大小的Lease Identification Cookie 和 從Code 0到Code 255的Code Set. 從 Code1 到 Code 254, codes由{Code, Len, Value}對組成, Code 0 和Code 255隻由{Code} 組成. 想知道Options Field的每個Code的更詳細解釋, 請參考RFC1533.

<圖 3.27: DHCP 消息的選項字段格式>

<表 3‑33: DHCP消息 Option Code定義>

 

在312字節的選項字段中,沒有使用的字節用0填補.   

 

<表 3-34>是在“inet/dhcp.h”定義成枚舉數據類型的例子,並給出了在DHCP客戶端程序中最常用的 Option Codes.

 

 沒有在<表 3-34>中定義的其他代碼在DHCP客戶端程序中沒有使用.

 

 The operation of DHCP客戶端程序的運行在EVB B/D的 main()中顯示. 請參考 <圖3.3: EVB B/D的 main()>

 

 首先, 設置DHCP客戶端在初始化時使用的 MAC地址. 網絡中的所有設備的MAC地址是唯一的. MAC 地址是網絡通信最基本的地址,也是DHCP服務器識別DHCP客戶端的必要信息. 要得到DHCP客戶端程序的MAC地址,用EVB B/D的MAC地址設置DHCP客戶端的全局變量SRC_MAC_ADDR. 通過在SRC_MAC_ADDR設置後調用init_dhcp_client(), 可以註冊兩個函數,以便在DHCP服務器分配的IP地址發生衝突和從DHCP服務器更新IP地址時調用.   

 

 當調用init_dhcp_client()時, 如果每一個函數不是特定的, DHCP 客戶端程序的set_DHCP_network() 和 proc_ip_conflict() 分別進行登記.

 

<圖 3.28: init_dhcp_client()>

當網絡信息更新或發生IP衝突時, 註冊evb_soft_reset() 以自動重啟EVB B/D.

第二, 網絡信息獲取可以通過getIP_DHCPS()完成.

<圖 3.29: getIP_DHCPS()>

getIP_DHCPS()用setSIPR()和setSHAR()等初始化iinChip™. 然後作為DHCP客戶端程序狀態初始化‘dhcp_state’ 變量為 ‘STATE_DHCP_DISCOVER’. 初始化之後, 調用 send_DHCP_DISCOVER() 以傳送一個DHCP DISCOVERY消息給 DHCP 服務器.

在傳送 DISCOVERY DHCP 消息後, 初始化定時器變量,定時器變量是網絡信息的租約時間,網絡信息是通過調用reset_DHCP_time()並且用set_timer()間隔1秒使用‘DHCP Timer’從DHCP服務器獲得的.用0初始化DHCP_Timeout後,等待接收來自DHCP服務器的DHCP消息, 只要 ‘DHCP_WAIT_TIME’ 定義了並且和它在‘MAX_DHCP_RETRY’定義一樣.在等待‘DHCP_WAIT_TIME x MAX_DHCP_RETRY’ 時間時,getIP_DHCPS()通過check_DHCP_state()不斷檢查dhcp_state是否改變為STATE_DHCP_LEASED. 

STATE_DHCP_LEASED 狀態表示網絡信息已被DHCP客戶端得到並且意味着getIP_DHCP() 運行成功. 如果在‘DHCP_WAIT_TIME x MAX_DHCP_RETRY’等待時間內沒有從DHCP服務器獲得網絡信息, check_DHCP_state() 將設置DHCP_Timeout為1. 當DHCP_Timeout是1時, 在釋放DHCP定時器之後getIP_DHCPS() 返回失敗.  

如果從DHCP服務器獲取網絡信息失敗, EVB B/D 用默認網絡信息或先前獲取的網絡信息設置網絡配置.

<表 3-35>是DHCP客戶端的狀態, 超時和 重試計數的定義.

第三, 從DHCP服務器得到的網絡信息的管理可通過check_DHCP_state()實現.<圖 3.30>展示了在 check_DHCP_state() 進程DHCP客戶端狀態改變時的DHCP消息流.

 <表 3‑34: DHCP 客戶端狀態 & 超時定義>

在getIP_DHCP()中,‘DHCP_XID’ 是可變的,用於設置如 <圖 3.26: DHCP 消息格式>所示DHCP消息的xid Field, ‘DHCP_XID’必須是唯一的,且一直保持同一個值直到網絡信息的租約時間過期. DHCP_XID 在這裡設成固定值 ‘0x12345678’, 但是推薦使用隨機值.

當初始化iinChip™為DHCP服務器通信模式時,建議設置源IP地址為‘0.0.0.0’. 可以使用任意IP地址設置iinChip™的源IP地址但是使用‘0.0.0.0’更好些, 因為‘0.0.0.0’ 在IPv4中相當於A級地址並且是一個實際上不被使用的空IP地址. 由於這個原因,就不可能與其他網絡發生衝突.  

對於DHCP服務器傳送UDP廣播包, 注意DHCP消息的標誌位MSB必須設成1. 請參考 <圖3.26: DHCP 消息格式>.
<表 3-36> 是設置標識字段的一部分代碼.

 <表 3‑35: DHCP 消息標識字段設置>

第三, 從DHCP服務器得到的網絡信息的管理可通過check_DHCP_state()實現.<圖 3.30>展示了在 check_DHCP_state() 進程DHCP客戶端狀態改變時的DHCP消息流.

<圖 3.30: 通過DHCP 客戶端狀態的DHCP 消息流>

check_DHCP_state() 檢查是否有來自DHCP服務器的DHCP消息, 它接收並分析DHCP消息. 通過被分析的DHCP消息的字節, 如果是可接收的DHCP消息,在按照<圖 3.30>所示的DHCP消息流改變DHCP客戶端狀態後,check_DHCP_state() 改變成下一個狀態.

<圖 3.31: check_DHCP_state()>

check_DHCP_state() 通過<圖 3.31>所示的流程和DHCP客戶端狀態相應地進行處理. 如果看一下check_DHCP_state()的DHCP_STATE_LEASED狀態,從DHCP服務器獲得的租約時間是有限的, 假如一半的租約時間過去了, check_DHCP_state()發送 DHCP_REQEUST消息到DHCP 服務器並且在備份源IP後改變它為DHCP_STATE_REREQUEST. 由於它連續不斷地發送DHCP_REQUEST 給服務器, 網絡信息得到保持.

<圖 3.32: parse_DHCPMSG() & check_DHCP_Timeout()>

parseDHCPMSG() 從DHCP服務器接收DHCP消息, 對DHCP消息的類型進行分類並保存網絡信息. 當運行check_DHCP_state()時, 以防在DHCP_WAIT_TIME期間沒有收到DHCP消息或收到的DHCP消息不是所期望的,就調用check_DHCP_Timeout()以重新發送DHCP消息到DHCP服務器.如果DHCP消息的重新發送次數達到 MAX_DHCP_RETRY, 在parseDHCPMSG()初始化所有變量以開始連接DHCP服務器和DHCP客戶端後,parseDHCPMSG() 發送 DHCP_DISCOVER 消息到DHCP服務器.

<表3‑36: DHCP 客戶端參考函數>

 3.2.6.6.     DNS 客戶端

在介紹客戶端設置之前簡單看一下DNS(域名系統).

DNS 是一個可以實現因特網域名和因特網IP地址之間相互轉換的系統. DNS由包含IP地址和域名之間的映射表的名稱服務器以及通過轉換查詢為名稱服務器來接收查詢結果的DNS解析器組成.  

DNS 解析器查詢IP地址或域名以轉換成本地名稱服務器. 本地名稱服務器接收查詢,搜尋它的數據庫,並回應解析器. 如果解析器不能找到它所尋找的信息, 本地名稱服務器在更高層發送接收到的查詢給名稱服務器,並可把接收到的答案發送給解析器.    

<圖3.33: 域名系統結構 & DNS 消息流>

 在 <圖 3.33>可見, DNS 查詢 和 DNS應答消息在DNS解析器和名稱服務器是可變換的,由<圖 3.34>所示的5部分組組成.

<圖 3.34: DNS消息格式>

Header Section是固定的2字節長,其他4部分是變長的. Answer、Authority和Additional Section被叫做Resource Records(RRs). Header, Questio,和RRs中的每一個都不同的格式.  

DNS消息的Header Section有消息類型, DNS 查詢類型和變長的計數信息.

在<圖 3.35: Header Section 格式>中, 當DNS消息是從解析器到名稱服務器的請求時,QR Field 是 0;當它是從名稱服務器到解析器時,QR Field 是 1. 當DNS消息以IP地址查詢域名時,Opcode Field 是 0 ;當它查詢名稱服務器狀態時, QR Field 是 1.

QDCOUNT, ANCOUNT, NSCOUNT和 ARCOUNT Field計算變長信息, 代表由Question, Answer, Authority和 Additional Section組成的 Block Count. Question Section 由<圖 3.36: Question Section格式> 所示格式組成. Answer, Authority和Additional Sections由<圖 3.37>所示組成.

 比如, 如果QDCOUNT 是 1, ANCOUNT 是10, NSCOUNT 是10, 並且 ARCOUNT 是10 ,那麼 Question Section是由 <圖 3.36: Question Section 格式>的第一塊所示組成, Answer, Authority和Additional Section 由<圖 3.37>的10塊所示的部分組成.

<圖 3.37>的NAME, <圖 3.36>的QNAME Filed以及 RDDATA Field 也是變長的. QNAME 和 NAME 是變長字段,它們由<圖 3.36> Format所示的部分組成並且處理每一個字段. RDDATA是變長字段,它用RDLENGTH Field的數據長度進行處理. 

 要獲得更多的信息,請參考 RFC1034 和RFC1035

DNS 消息通過在<表 3-38>定義的Data Type運行. 請參考 “inet/dns.h”

<表 3‑37: DNS 消息數據類型>

DNS 解析器基於gethostbyaddr() 和gethostbyname()運行. gethostbyaddr() 發送因特網IP 地址到Internet Domain Name,並且gethostbyname() 發送 因特網Domain Name到因特網 IP地址. gethostbyaddr() 和gethostbyname() 測試 DNS 名稱服務器 IP 地址的設置,並搜索連接DNS名稱服務器所需的iinChip™ 空閑頻道.如果iinChip™存在空閑頻道, gethostbyaddr() 和gethostbyname() 用‘BYNAME’或‘BYIP’作為元素調用 dns_query(). 

gethostbyaddr()和gethostbyname()的例子, 請參考 Chapter 3.2.5.3 Ping Request Program.

  和DNS名稱服務器的實際連接是通過 dns_query()進行的, gethostbyaddr() 和 gethostbyname() 只是報告dns_query()的結果.

<表 3‑38: dns_query()查詢類型定義>

<圖 3.38: gethostbyaddr() & gethostbyname()>

dns_query() 初始化用於DNS之間相互工作的緩衝器,並基於Query Type ‘BYNAME’和‘BYIP’創建Question Section的QNAME.如果Query Type 是 ‘BYNAME’,也就是說, 當用IP地址查詢域名時,域名可以在不發送的情況下被使用.

當Query Type 時 ‘BYIP’, 也就是說, 當用IP地址查詢域名時, 改變 IP 地址為 IP 地址串, 並且在把 “in-addr.arpa” 加到改變的 IP 地址串後使用QNAME. 在創建QNAME之後, 創建UDP Socket用於 DNS 相互工作 ,並通過調用dns_make_query()創建DNS 請求消息. 如果DNS請求消息創建成功, DNS請求消息通過UDP Socket發送給DNS名稱服務器. 發送DNS請求消息後,接收DNS回應消息或者等待直到等待時間失效.    

如果在等待時間期間從DNS名稱服務器接收到DNS回復消息,用dns_parse_response()分析所接收到的DNS回復消息.dns_query()根據Query Type返回 IP地址或 域名.  

<圖 3.39>是 dns_query()的進程圖

 

<圖 3.39: dns_query()>

 

 

<圖 3.40: dns_makequery()>

dns_makequery()創建 DNS請求消息以被發送給DNS名稱服務器. 因為DNS請求消息可以只用Header和 Question Section 進行查詢,不需要創建RRs Sections.如果在 dns_makequery()里查詢header section的創建, 首先, 在DNS消息相互工作里設置ID Field值為任意值. 在這裡, ID設置成 0x1122, 在以後的互相工作中,該值每次遞增1. QR, Opcode, AA, TC和RD Field通過MAKE_FLAG0()分別設置成 QR_QUERY, OP_QUERY/OP_IQUERY, 0, 0和1, 且 RA, Z和RCODE Field 通過MAKE_FLAG1()分別設置成0, 0和 0.

<表 3‑39: Header Section用到的常數和MACRO >

 因為計數字段, QDCOUNT, ANCOUNT, NSCOUNT和 ARCOUNT只有1個question,每一個分別設置成 1, 0, 0和0.

我們看一下Question Section. QNAME Field是設置IP地址串的字段. 域名和IP地址串由1字節的Label length 和最大63字節的Label組成. QNAME的結尾總是設置成0以找出QNAME的變長. <圖 3.41> 是發送在QNAME字段中的域名“www.wiznet.co.kr”的實際例子.

 

 當把域名當成QNAME 時,Question Section的QTYPE Field設置成‘TYPE_PTR’. 當是IP地址時,Question Section的QTYPE Field設置成 ‘TYPE_A’ ,因為QCLASS Field包含在因特網中,設置QCLASS Field為 ‘CLASS_IN’.

表 3-41是用在QTYPE & QCLASS Fields中的常數的定義.

 <表 3‑40 : QTYPE & QCLASS 字段的參數定義>

<圖 3.42: dns_parse_response()>

 <圖 3.42>的dns_parse_response() 分析通過DNS名稱服務器接收到的回復消息. dns_parse_response() 檢查回復消息是否和發送給DNS名稱服務器的請求消息ID一致,並且通過檢查Header Section的QR Field檢查接收到的消息是否是一個回復消息. 如果接收到的消息是來自DNS名稱服務器的回復,通過檢查Header Section的RCODE Field值可以決定改變的成功.

<表 3-42> 是RCODE Field用到的常數的定義.  

 <表 3‑41 : Header Section’s RCODE字段的常數定義>

如果RCODE是RC_NO_ERROR,那麼諸如Question, Answer, Authority和Additional Section的變長字段將被分析. 因為必要的信息設置在 Answer Section里,在這種情況下,分析Answer Section, 不對其他字段進行分析和處理.如果需要 Authority 和Addition Section的消息,可以自己輕鬆地得到.  

 通過調用dns_parse_question(),Question Section被處理多達Header Section的QDCOUNT次.通過調用dns_parse_question(), Answer Section被處理多達Header Section的ANCOUNT次.

 

Answer Section的TYPE有一個值來自 <表 3-41 : QTYPE & QCLASS Filed的常數定義>,該值是 TYPE_A 或 TYPE_PTR. 如果域名改變成了IP地址, 可以從TYPE_A獲得改變的IP地址;如果IP地址改變成了域名,可以從TYPE_PTR 得到域名. 改變域名或IP地址也通過parse_name()處理和提取.

如果RCODE是RC_NO_ERROR,那麼諸如Question, Answer, Authority和Additional Section的變長字段將被分析. 因為必要的信息設置在 Answer Section里,在這種情況下,分析Answer Section, 不對其他字段進行分析和處理.如果需要 Authority 和Addition Section的消息,可以自己輕鬆地得到.  

 通過調用dns_parse_question(),Question Section被處理多達Header Section的QDCOUNT次.通過調用dns_parse_question(), Answer Section被處理多達Header Section的ANCOUNT次.

 

dns_parse_question() 分析和處理 Question Section. DNS請求消息的Question Section中實際上沒有信息,但是必須進行處理以獲得Answer Section的開始位置. 因為 Question Section的QNAME Field是變長的, parse_name() 處理QNAME Field 以處理變長的進程,且跳過QTYPE和QCLASS Field.   

dns_answer() 分析並處理Answer Section. Answer Section 是實際上發生轉變的部分,並給Answer Section的TYPE Field提供合適的處理.  

Answer Section的TYPE有一個值來自 <表 3-41 : QTYPE & QCLASS Filed的常數定義>,該值是 TYPE_A 或 TYPE_PTR. 如果域名改變成了IP地址, 可以從TYPE_A獲得改變的IP地址;如果IP地址改變成了域名,可以從TYPE_PTR 得到域名. 改變域名或IP地址也通過parse_name()處理和提取.

<圖 3.44: parse_name()>

parse_name() 處理Question Section的QNAME Field或RRs Section 的NAME和 RDDATA Field. QNAME, NAME和RDDATA Field大多數情況下由如 <圖 3.41: Question Section的QNAME Field轉變例子 >所示組成. 然而, 它們可被壓縮以減少 DNS消息大小. 壓縮情況用2字節表示. 第一個字節里,如果上面2比特是’11,’ 說明 Label被壓縮. 偏移位由第一字節除去上面2比特的部分和第2字節組成.

該偏移是DNS消息的Offset,意味着Label的實際值的位置是從DNS消息的開始的偏移值處. 當壓縮圖想重新使用已經使用於DNS消息的域名時, 相關的域名設置已經在DNS消息中的偏移為Indirect,以至於能減小DNS消息的大小. <圖 3.45>是DNS消息的壓縮圖和它的應用的一個例子.

 

<圖 3.45: DNS消息壓縮圖>

<圖3.45>的壓縮圖例子展示了 “F.ISI.ARPA”, “FOO.F.ISI.ARPA”, “ARPA”和ROOT情況下的DNS消息. “F.ISI.ARPA” 以<圖3.41: Question Section的QNAME Field轉換例子 >的格式不經過壓縮以DNS消息的20位偏移被處理.

 在 “FOO.F.ISI.ARPA”中,因為除了“FOO”的剩下的部分和先前被處理的Name一樣, “FOO”以 <圖 3.41: Question Section的QNAME Field轉換例子 > 格式不經過壓縮被處理,剩餘的名稱以Offset 26被處理. ROOT是最高級的域名,Label Length Field是0時被處理.    

 在Name的分析之前,parse_name()檢查Label Length Byte的高2比特是否是11,如果是 ‘11’,相關的Label在Label所在的DNS消息偏移位處分析Label. 如果不是 ‘11’, Label以<圖3.41: Question Section的QNAME Field轉換例子 >的形式被分析以及處理.

<表 3‑42 : DNS客戶端的參考函數>

 

感謝關注!

相關內容可以參考WIZnet中文博客:

W3150A+評估板–EVB-PIC24 用戶手冊(一)

W3150A+評估板–EVB-PIC24 用戶手冊(二)