歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> Linux下的地址解析函數應用實例

Linux下的地址解析函數應用實例

日期:2017/2/28 16:21:06   编辑:Linux教程
0 引言
域名系統(DNS)是一種用於TCP/IP應用程序的分布式數據庫, 它提供主機名字和IP地址之間的轉換及有關電子郵件的選路信息.[1] 目前, 它已經在全球范圍內被廣泛應用. 從應用的角度上看, 對DNS的訪問是通過一個地址解析器(resolver)來完成的. 本文通過講解一些常用的地址解析函數, 並利用精簡後的部分qmail代碼, 讓不熟悉DNS相關函數的程序員了解並掌握常用的地址解析函數.

1 概述
DNS 查詢中, 最常用的兩類分別是A類查詢(A query)和指針查詢(PTR query). 前者是已知主機名, 詢問IP; 後者是已知IP, 詢問主機名. 對於這些查詢, 在Unix主機中可以直接調用基本DNS函數: gethostbyname(3)和gethostbyaddr(3)來實現. 但是對於其他類型的查詢(例如MX查詢), 則沒有專門的函數來負責處理. 此時, 程序員不得不依賴地址解析函數來親自處理這些問題. 這需要對DNS報文格式有基本的了解, 這些將在下面幾節進行說明. 關於gethostbyname(3)和gethostbyaddr(3)兩個函數, 讀者可以查閱自己系統上的man手冊.

2 DNS報文格式
在對地址解析函數講解之前, 有必要先了解一下DNS報文格式. 之後的幾節會頻繁地涉及到本節所講的內容. 如果想對DNS相關協議有更深的了解, 可以閱讀參考文獻[1] [2] [3].

DNS定義了一個用於查詢和響應的報文格式, 圖1 顯示了這個報文的總體格式.

[圖1]

每個DNS查詢(或響應)報文都包含有一個12字節長的首部和四個變長的字段組成.

對於本文來說, 首部中主要關心的是問題數和資源記錄數兩個字段. 這兩個字段分別用於說明各自對應的變長字段中的條目數. 問題數說明查詢問題字段中的條目數; 資源記錄數則說明回答字段中的條目數. 對於一個DNS查詢報文, 問題數通常是1. 對於應答報文, 回答數至少是1.

首部以下是四個變長字段, 本文所關心的是查詢問題字段和回答字段.

查詢問題字段可以包含多個查詢問題, 每個問題的格式如圖2 所示.

[圖2]

其中, 查詢名一項存儲著要查找的名字. 它長度可變並以一種特殊的格式存儲. 程序可以通過其中存儲的內容確定其長度. 具體獲得其中存儲內容的方法, 將在下一節中進行詳細講解. 每一個問題有一個查詢類型, 每個響應(下文中將會提到)也同樣有一個類型. 常用的類型有: A類型---表示期望獲得查詢名的IP地址; PTR查詢---表示期望獲得一個IP地址對應的域名; MX查詢---郵件交換查詢(關於MX查詢的具體內容, 下文會提到). 查詢類指定了所使用的協議簇, 通常是1, 表示Internet地址.

回答字段可以包含多個條目. 每個回答字段是以一種叫做資源記錄(Resource Record, RR)的格式存儲的. ( 授權字段和額外信息字段也同樣以資源記錄的格式存儲信息). 資源記錄的格式如圖3 所示.

[圖3]

域名是記錄中資源數據對應的名字. 它的格式和前面介紹的查詢名字段格式相同. 類型和類字段和前面介紹的查詢類型, 查詢類字段的功能一樣. 類字段的取值通常是1, 表示Internet地址. 生存時間字段是客戶程序保留該資源記錄的秒數. 資源數據長度說明資源數據包含的字節數. 資源數據則根據類型字段的值有不同的格式. 對於A類型, 資源數據是IP地址. 對於MX查詢, 資源數據是優先值和域名, 域名的格式與查詢名字段格式相同(MX記錄的具體內容下文會有介紹).

至此, DNS中用到的報文格式已經基本介紹完. 下一節中將會介紹一些常用的地址解析函數. 閱讀下文時, 最好隨時翻閱本節所講的內容以便於理解.

3 地址解析函數

除了經常用到的gethostbyname(3)和gethostbyaddr(3)函數以外, Linux(以及其它UNIX/UNIX-like系統)還提供了一套用於在底層處理DNS相關問題的函數(這裡所說的底層僅是相對 gethostbyname和gethostbyaddr兩個函數而言). 這套函數被稱為地址解析函數(resolver functions). 用戶可以通過鍵入man resolver來了解其中的具體信息. 這裡將對其中常用到的函數做一個解釋. 常用的地址解析函數原型如下:
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
extern struct state _res;

int res_init(void);

int res_query(const char *dname, int class, int type,
unsigned char *answer, int anslen);

int res_search(const char *dname, int class, int type,
unsigned char *answer, int anslen);

int dn_expand(unsigned char *msg, unsigned char *eomorig,
unsigned char *comp_dn, unsigned char *exp_dn,
int length);

_res: 這個結構體用於保存相關的狀態信息. 它的定義在<resolv.h>中.

res_init: 讀取配置文件並修改環境變量:LOCALDOMAIN. 在調用其他地址解析函數前通常要先調用res_init. 如果執行成功, 函數返回0; 否則返回-1.

res_query: 用來發出一個指定類(由參數class指定)和類型(由參數type指定)的DNS詢問. dname是要查詢的主機名. 返回信息被存儲在answser指向的內存區域中. 信息的長度不能大於anslen個字節. 這個函數會創建一個DNS查詢報文並把它發送到指定的DNS服務器.

res_search: 和res_query的行為類似, 與res_query不同的是, 當域名中不包含點時, 會在域名後面加上默認域名; 同時, 支持遞歸查詢(即當一個服務器沒有存儲詢問的信息時, 會繼續向其他服務器詢問). 一般情況下盡量使用res_search. 因為它的成功幾率會比較大.
Copyright © Linux教程網 All Rights Reserved