欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Linux C/C++實現(xiàn)DNS客戶端請求域名IP的示例代碼

 更新時間:2024年03月05日 09:24:27   作者:1776323096  
DNS全稱:Domain Name System,域名解析系統(tǒng),是互聯(lián)網(wǎng)的一項服務(wù),本文主要介紹了C/C++如何實現(xiàn)DNS客戶端請求域名IP,感興趣的可以了解下

一、DNS介紹

全稱:Domain Name System,域名解析系統(tǒng)。是互聯(lián)網(wǎng)的一項服務(wù)。它實質(zhì)上是一個 域名 和 IP 相互映射的分布式數(shù)據(jù)庫,有了它,我們就可以通過域名更方便的訪問互聯(lián)網(wǎng)。

**功能:**每個IP地址都可以有一個主機名,主機名由一個或多個字符串組成,字符串之間用小數(shù)點隔開。有了主機名,就不要死記硬背每臺IP設(shè)備的IP地址,只要記住相對直觀有意義的主機名就行了。這就是DNS協(xié)議所要完成的功能。

主機名到IP地址的映射有兩種方式:

靜態(tài)映射,每臺設(shè)備上都配置主機到IP地址的映射,各設(shè)備獨立維護自己的映射表,而且只供本設(shè)備使用;

動態(tài)映射,建立一套域名解析系統(tǒng)(DNS),只在專門的DNS服務(wù)器上配置.主機到IP地址的映射,網(wǎng)絡(luò)上需要使用主機名通信的設(shè)備,首先需要到DNS服務(wù)器查詢主機所對應(yīng)的IP地址。

通過主機名,最終得到該主機名對應(yīng)的IP地址的過程叫做域名解析(或主機名解析)。在解析域名時,可以首先采用靜態(tài)域名解析的方法,如果靜態(tài)域名解析不成功,再采用動態(tài)域名解析的方法??梢詫⒁恍┏S玫挠蛎湃腱o態(tài)域名解析表中,這樣可以大大提高域名解析效率。

DNS 有以下特點:

  • 分布式的
  • 協(xié)議支持 TCP 和 UDP,常用端口是 53
  • 每一級域名的長度限制是 63
  • 域名總長度限制是 253

二、DNS協(xié)議

1、頭部

TransactionID(事務(wù)ID):DNS 報文的 ID 標(biāo)識。對于請求報文和其對應(yīng)的應(yīng)答報文,該字段的值是相同的,通過它可以區(qū)分 DNS 應(yīng)答報文是對哪個請求進行響應(yīng)的。

Flags(標(biāo)志):DNS 報文中的標(biāo)志字段。

第15位:QR(Response),查詢請求/響應(yīng)的標(biāo)志信息。0為請求(query) 1為響應(yīng)(response)。

第14-11位:Opcode, 操作碼。0 表示標(biāo)準(zhǔn)查詢;1 表示反向查詢;2 表示服務(wù)器狀態(tài)請求。

第10位:AA(Authoritative),授權(quán)應(yīng)答,該字段在響應(yīng)報文中有效。值為 1 時,表示名稱服務(wù)器是權(quán)威服務(wù)器;值為 0 時,表示不是權(quán)威服務(wù)器。

第9位:TC(Truncated):表示是否被截斷。值為 1 時,表示響應(yīng)已超過 512 字節(jié)并已被截斷(一個UDP報文為512字節(jié)),只返回前 512 個字節(jié)。

第8位:RD(Recursion Desired):是否請求遞歸(這個比特位被請求設(shè)置,應(yīng)答的時候使用的相同的值返回)。該字段能在一個查詢中設(shè)置,并在響應(yīng)中返回。如果該位為 1,告訴名稱服務(wù)器必須處理這個查詢,這種方式被稱為一個遞歸查詢。如果該位為 0,且被請求的名稱服務(wù)器沒有一個授權(quán)回答,它將返回一個能解答該查詢的其他名稱服務(wù)器列表。這種方式被稱為迭代查詢。

第7位:RA(Recursion Available):可用遞歸。該字段只出現(xiàn)在響應(yīng)報文中。當(dāng)值為 1 時,表示DNS服務(wù)器支持遞歸查詢。

第6-4位:Z:保留字段,在所有的請求和應(yīng)答報文中,它的值必須為 0。

第3-0位:rcode(Reply code):返回碼字段,表示響應(yīng)的差錯狀態(tài)。

  • 當(dāng)值為 0 時,表示 沒有錯誤;
  • 當(dāng)值為 1 時,表示 報文格式錯誤(Format error),服務(wù)器不能理解請求的報文;
  • 當(dāng)值為 2 時,表示 域名服務(wù)器失敗(Server failure),因為服務(wù)器的原因?qū)е聸]辦法處理這個請求;
  • 當(dāng)值為 3 時,表示 名字錯誤(Name Error),只有對授權(quán)域名解析服務(wù)器有意義,指出解析的域名不存在;
  • 當(dāng)值為 4 時,表示 查詢類型不支持(Not Implemented),即域名服務(wù)器不支持查詢類型;
  • 當(dāng)值為 5 時,表示 拒絕(Refused),一般是服務(wù)器由于設(shè)置的策略拒絕給出應(yīng)答,如服務(wù)器不希望對某些請求者給出應(yīng)答。
  • 當(dāng)值為 6-15 時:保留值

Questions(問題計數(shù)):DNS查詢請求的數(shù)目

Answer RRs(回答資源記錄數(shù)):DNS響應(yīng)的數(shù)目

Authority RRs(權(quán)威名稱服務(wù)器計數(shù)):權(quán)威名稱服務(wù)器的數(shù)目

Additional RRs(附加資源記錄數(shù)):額外的記錄數(shù)目(權(quán)威名稱服務(wù)器對應(yīng) IP 地址的數(shù)目)

2、問題部分

問題部分指的是報文格式中查詢問題區(qū)域(Queries)部分。該部分是用來顯示 DNS 查詢請求的問題,通常只有一個問題。該部分包含正在進行的查詢信息,包含查詢名(被查詢主機名字)、查詢類型、查詢類。

問題部分格式如圖所示。

該部分中每個字段含義如下:

Name(查詢的域名):不定長(例子:www.baidu.com 需寫作:3www5baidu3com0,這里的3,5,3分別指的是后面域名的長度,最后的0是‘\0’,是字符串結(jié)尾標(biāo)志),有時也會是 IP 地址,用于反向查詢。

Type(查詢類型):DNS 查詢請求的資源類型。通常查詢類型為 A 類型,表示由域名獲取對應(yīng)的 IP 地址。

Class(查詢類):指定信息的協(xié)議組,通常為互聯(lián)網(wǎng)地址(值為 1)。

//查詢類型(查詢的資源記錄類型)
enum QueryType
{   
    A     = 0x01, //指定計算機 IP 地址。   
    NS    = 0x02, //指定用于命名區(qū)域的 DNS 名稱服務(wù)器。   
    MD    = 0x03, //指定郵件接收站(此類型已經(jīng)過時了,使用MX代替)   
    MF 	  = 0x04, //指定郵件中轉(zhuǎn)站(此類型已經(jīng)過時了,使用MX代替)   
    CNAME = 0x05, //指定用于別名的規(guī)范名稱。   
    SOA   = 0x06, //指定用于 DNS 區(qū)域的“起始授權(quán)機構(gòu)”。   
    MB    = 0x07, //指定郵箱域名。   
    MG 	  = 0x08, //指定郵件組成員。   
    MR 	  = 0x09, //指定郵件重命名域名。   
    NULL  = 0x0A, //指定空的(NULL)資源記錄   
    WKS   = 0x0B, //描述已知服務(wù)。   
    PTR   = 0x0C, //如果查詢是 IP 地址,則指定計算機名;否則指定指向其它信息的指針。   
    HINFO = 0x0D, //指定計算機 CPU 以及操作系統(tǒng)類型。   
    MINFO = 0x0E, //指定郵箱或郵件列表信息。   
    MX    = 0x0F, //指定郵件交換器。   
    TXT   = 0x10, //指定文本信息。   
    UINFO = 0x64, //指定用戶信息。   
    UID   = 0x65, //指定用戶標(biāo)識符。   
    GID	  = 0x66, //指定組名的組標(biāo)識符。   
    ANY   = 0xFF  //指定所有數(shù)據(jù)類型。   
};  

//查詢類(指定信息的協(xié)議組)
enum QueryClass
{   
    IN     = 0x01, //指定 Internet 類別。   
    CSNET  = 0x02, //指定 CSNET 類別。(已過時)   
    CHAOS  = 0x03, //指定 Chaos 類別。   
    HESIOD = 0x04, //指定 MIT Athena Hesiod 類別。   
    ANY	   = 0xFF  //指定任何以前列出的通配符。   
}; 

3、資源記錄部分

資源記錄部分是指 DNS 報文格式中的最后三個字段,包括 回答問題區(qū)域字段、權(quán)威名稱服務(wù)器區(qū)域字段、附加信息區(qū)域字段。這三個字段均采用一種稱為資源記錄的格式,格式如圖所示。

回答區(qū)域(Answers): 響應(yīng)的內(nèi)容,可以有0-n條(請求時為空即可)

1、Name:域名(2字節(jié)或不定長)

它的格式和Queries區(qū)域的查詢名字字段是一樣的。

有一點不同就是,當(dāng)報文中域名重復(fù)出現(xiàn)的時候,該字段使用2個字節(jié) 的偏移指針來表示。比如,在資源記錄中,域名通常是查詢問題部分的域名的重復(fù),因此用2字節(jié)的指針來表示,具體格式是最前面的兩個高位是 11,用于識別指針。其余的14位從DNS報文的開始處計數(shù)(從0開始),指出該報文中的相應(yīng)字節(jié)數(shù)。

一個典型的例子,0xC00C(1100000000001100),12(00000000001100)正好是頭部的長度,其正好指向Queries區(qū)域的查詢名字字段)。

2、Type:2字節(jié) 響應(yīng)的資源記錄的類型

與問題部分中的查詢類型值是一樣的。

3、Class:2字節(jié) 地址類型

與問題部分中的查詢類的值是一樣的。

4、TTL:4字節(jié) 表示的是資源記錄的生命周期(以秒為單位)

一般用于當(dāng)?shù)刂方馕龀绦蛉〕鲑Y源記錄后決定保存及使用緩存數(shù)據(jù)的時間,它同時也可以表明該資源記錄的穩(wěn)定程度,極為穩(wěn)定的信息會被分配一個很大的值(比如86400,這是一天的秒數(shù))。

5、Datalength:2字節(jié),資源數(shù)據(jù)的長度

指接下來的data長度,單位為字節(jié)。

6、Address/CNAME:資源數(shù)

4字節(jié)地址/不定長域名

授權(quán)區(qū)域(Authoritativenameservers):

1、Name:域名(2字節(jié)或不定長),同上

2、Type:2字節(jié) 同上,響應(yīng)的資源記錄類型。此處為2(NS),表示要獲取該域名的權(quán)威名稱服務(wù)器名稱

3、Class:2字節(jié) 同上,地址類型,即查詢類(指定信息的協(xié)議組)

4、TTL:4字節(jié) 同上

5、Datalength:2字節(jié),資源數(shù)據(jù)的長度,指接下來的data長度,單位為字節(jié)。

6、nameserver:此處為6字節(jié),表示該域名對應(yīng)的權(quán)威名稱服務(wù)器的名稱。

附加區(qū)域(Additionalrecords):

1、Name:域名(2字節(jié)或不定長),同上,在這里指的是 權(quán)威名稱服務(wù)器 的名稱

2、Type:2字節(jié) 同上,響應(yīng)的資源記錄類型。

3、Class:2字節(jié),同上,地址類型,即查詢類(指定信息的協(xié)議組)

4、TTL:4字節(jié) 同上

5、Datalength:2字節(jié),指接下來的data長度,單位為字節(jié)。

6、Address:此處為4字節(jié)地址,指的是 權(quán)威名稱服務(wù)器 的IP地址

三、代碼實現(xiàn)

實現(xiàn)了同步方式與異步方式。

#include <iostream>
#include <strings.h>
#include <string.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/epoll.h>

using namespace std;

//域名數(shù)組
const char* domainAddr[] = {
    "www.0voice.com",
	"draw.0voice.wang",
	"www.baidu.com",
	"tieba.baidu.com",
	"news.baidu.com",
	"zhidao.baidu.com",
	"music.baidu.com",
	"image.baidu.com",
	"v.baidu.com",
	"map.baidu.com",
	"baijiahao.baidu.com",
	"xueshu.baidu.com",
	"cloud.baidu.com",
	"www.163.com",
	"open.163.com",
	"auto.163.com",
	"gov.163.com",
	"money.163.com",
	"sports.163.com",
	"tech.163.com",
	"edu.163.com",
	"www.taobao.com",
	"q.taobao.com",
	"sf.taobao.com",
	"yun.taobao.com",
	"baoxian.taobao.com",
	"www.tmall.com",
	"suning.tmall.com",
	"www.tencent.com",
	"www.qq.com",
	"www.aliyun.com",
	"www.ctrip.com",
	"hotels.ctrip.com",
	"hotels.ctrip.com",
	"vacations.ctrip.com",
	"flights.ctrip.com",
	"trains.ctrip.com",
	"bus.ctrip.com",
	"car.ctrip.com",
	"piao.ctrip.com",
	"tuan.ctrip.com",
	"you.ctrip.com",
	"g.ctrip.com",
	"lipin.ctrip.com",
	"ct.ctrip.com"
};

//DNS頭部
struct DNS_HEADER {
    uint16_t transID; //事務(wù)ID:DNS 報文的 ID 標(biāo)識。
                      //對于請求報文和其對應(yīng)的應(yīng)答報文,該字段的值是相同的,
                      //通過它可以區(qū)分 DNS 應(yīng)答報文是對哪個請求進行響應(yīng)的。
    uint16_t flags; //標(biāo)志:DNS 報文中的標(biāo)志字段
	/*
	第15位:QR(Response),查詢請求/響應(yīng)的標(biāo)志信息。0為請求(query) 1為響應(yīng)(response)。

	第14-11位:Opcode, 操作碼。0 表示標(biāo)準(zhǔn)查詢;1 表示反向查詢;2 表示服務(wù)器狀態(tài)請求。

	第10位:AA(Authoritative),授權(quán)應(yīng)答,該字段在響應(yīng)報文中有效。值為 1 時,表示名稱服務(wù)器是權(quán)威服務(wù)器;值為 0 時,表示不是權(quán)威服務(wù)器。

	第9位:TC(Truncated):表示是否被截斷。值為 1 時,表示響應(yīng)已超過 512 字節(jié)并已被截斷(一個UDP報文為512字節(jié)),只返回前 512 個字節(jié)。

	第8位:RD(Recursion Desired):期望遞歸。該字段能在一個查詢中設(shè)置,并在響應(yīng)中返回。
								   如果該位為 1,告訴名稱服務(wù)器必須處理這個查詢,這種方式被稱為一個遞歸查詢。
								   如果該位為 0,且被請求的名稱服務(wù)器沒有一個授權(quán)回答,
								   				它將返回一個能解答該查詢的其他名稱服務(wù)器列表。這種方式被稱為迭代查詢。
								是否請求遞歸(這個比特位被請求設(shè)置,應(yīng)答的時候使用的相同的值返回)。

	第7位:RA(Recursion Available):可用遞歸。該字段只出現(xiàn)在響應(yīng)報文中。當(dāng)值為 1 時,表示DNS服務(wù)器支持遞歸查詢。

	第6-4位:Z:保留字段,在所有的請求和應(yīng)答報文中,它的值必須為 0。

	第3-0位:rcode(Reply code):返回碼字段,表示響應(yīng)的差錯狀態(tài)。
								   當(dāng)值為 0 時,表示 沒有錯誤;
								   當(dāng)值為 1 時,表示 報文格式錯誤(Format error),服務(wù)器不能理解請求的報文;
								   當(dāng)值為 2 時,表示 域名服務(wù)器失?。⊿erver failure),因為服務(wù)器的原因?qū)е聸]辦法處理這個請求;
								   當(dāng)值為 3 時,表示 名字錯誤(Name Error),只有對授權(quán)域名解析服務(wù)器有意義,指出解析的域名不存在;
								   當(dāng)值為 4 時,表示 查詢類型不支持(Not Implemented),即域名服務(wù)器不支持查詢類型;
								   當(dāng)值為 5 時,表示 拒絕(Refused),一般是服務(wù)器由于設(shè)置的策略拒絕給出應(yīng)答,如服務(wù)器不希望對某些請求者給出應(yīng)答。
								   當(dāng)值為 6-15 時:保留值
	*/
    uint16_t questions; //問題計數(shù):DNS查詢請求的數(shù)目
    uint16_t answerRRs; //回答資源記錄數(shù):DNS響應(yīng)的數(shù)目
    uint16_t authorityRRs; //權(quán)威名稱服務(wù)器計數(shù):權(quán)威名稱服務(wù)器的數(shù)目
    uint16_t additionalRRs; //附加資源記錄數(shù):額外的記錄數(shù)目(權(quán)威名稱服務(wù)器對應(yīng) IP 地址的數(shù)目)
};

//DNS查詢問題區(qū)域
struct DNS_QUERIES {
	string   qName;  //查詢的域名,不定長(例子:www.baidu.com 需寫作:3www5baidu3com0)
	uint16_t qType;  //查詢類型:查詢的資源記錄類型
	uint16_t qClass; //查詢類:指定信息的協(xié)議組
};

//查詢類型(查詢的資源記錄類型)
enum QueryType
{   
    A       = 0x01, //指定計算機 IP 地址。   
    NS      = 0x02, //指定用于命名區(qū)域的 DNS 名稱服務(wù)器。   
    MD      = 0x03, //指定郵件接收站(此類型已經(jīng)過時了,使用MX代替)   
    MF 	    = 0x04, //指定郵件中轉(zhuǎn)站(此類型已經(jīng)過時了,使用MX代替)   
    CNAME   = 0x05, //指定用于別名的規(guī)范名稱。   
    SOA     = 0x06, //指定用于 DNS 區(qū)域的“起始授權(quán)機構(gòu)”。   
    MB      = 0x07, //指定郵箱域名。   
    MG 	    = 0x08, //指定郵件組成員。   
    MR 	    = 0x09, //指定郵件重命名域名。   
    _NULL_  = 0x0A, //指定空的(NULL)資源記錄   
    WKS     = 0x0B, //描述已知服務(wù)。   
    PTR     = 0x0C, //如果查詢是 IP 地址,則指定計算機名;否則指定指向其它信息的指針。   
    HINFO   = 0x0D, //指定計算機 CPU 以及操作系統(tǒng)類型。   
    MINFO   = 0x0E, //指定郵箱或郵件列表信息。   
    MX      = 0x0F, //指定郵件交換器。   
    TXT     = 0x10, //指定文本信息。   
    UINFO   = 0x64, //指定用戶信息。   
    UID     = 0x65, //指定用戶標(biāo)識符。   
    GID	    = 0x66, //指定組名的組標(biāo)識符。   
    _ANY_   = 0xFF  //指定所有數(shù)據(jù)類型。   
};  

//查詢類(指定信息的協(xié)議組)
enum QueryClass
{   
    IN     = 0x01, //指定 Internet 類別。   
    CSNET  = 0x02, //指定 CSNET 類別。(已過時)   
    CHAOS  = 0x03, //指定 Chaos 類別。   
    HESIOD = 0x04, //指定 MIT Athena Hesiod 類別。   
    ANY	   = 0xFF  //指定任何以前列出的通配符。   
}; 

/*
回答區(qū)域:
Answers:查詢響應(yīng)內(nèi)容,可以有0-n條(請求時為空即可)
	Name:域名(2字節(jié)或不定長):
		它的格式和Queries區(qū)域的查詢名字字段是一樣的。
		有一點不同就是,當(dāng)報文中域名重復(fù)出現(xiàn)的時候,該字段使用2個字節(jié)的偏移指針來表示。
		比如,在資源記錄中,域名通常是查詢問題部分的域名的重復(fù),
		因此用2字節(jié)的指針來表示,具體格式是最前面的兩個高位是 11,用于識別指針。
		其余的14位從DNS報文的開始處計數(shù)(從0開始),指出該報文中的相應(yīng)字節(jié)數(shù)。
		一個典型的例子,0xC00C(1100000000001100),12(00000000001100)正好是頭部的長度,其正好指向Queries區(qū)域的查詢名字字段)。
	Type:2字節(jié) 響應(yīng)的資源記錄的類型,與問題部分中的查詢類型值是一樣的。
	Class:2字節(jié) 地址類型,與問題部分中的查詢類的值是一樣的。
	TTL:4字節(jié) 
		以秒為單位,表示的是資源記錄的生命周期,
		一般用于當(dāng)?shù)刂方馕龀绦蛉〕鲑Y源記錄后決定保存及使用緩存數(shù)據(jù)的時間,
		它同時也可以表明該資源記錄的穩(wěn)定程度,極為穩(wěn)定的信息會被分配一個很大的值(比如86400,這是一天的秒數(shù))。
	Datalength:2字節(jié),資源數(shù)據(jù)的長度,指接下來的data長度,單位為字節(jié)。
	Address/CNAME:資源數(shù)據(jù),4字節(jié)地址/不定長域名

授權(quán)區(qū)域
Authoritativenameservers:
	Name:域名(2字節(jié)或不定長),同上
	Type:2字節(jié) 同上,響應(yīng)的資源記錄類型。此處為2(NS),表示要獲取該域名的權(quán)威名稱服務(wù)器名稱
	Class:2字節(jié) 同上,地址類型,即查詢類(指定信息的協(xié)議組)
	TTL:4字節(jié) 同上
	Datalength:2字節(jié),資源數(shù)據(jù)的長度,指接下來的data長度,單位為字節(jié)。
	nameserver:此處為6字節(jié),表示該域名對應(yīng)的權(quán)威名稱服務(wù)器的名稱。

//附加區(qū)域
Additionalrecords:
	Name:域名(2字節(jié)或不定長),同上,在這里指的是 權(quán)威名稱服務(wù)器 的名稱
	Type:2字節(jié) 同上,響應(yīng)的資源記錄類型。
	Class:2字節(jié),同上,地址類型,即查詢類(指定信息的協(xié)議組)
	TTL:4字節(jié) 同上
	Datalength:2字節(jié),指接下來的data長度,單位為字節(jié)。
	Address:此處為4字節(jié)地址,指的是 權(quán)威名稱服務(wù)器 的IP地址
*/

//構(gòu)建DNS頭部
DNS_HEADER create_dns_header() {
    DNS_HEADER header;
    bzero(&header, sizeof(header));

    srandom(time(NULL));

    header.transID = random();
    header.flags = htons(0x0100);
    header.questions = htons(0x0001);

    return move(header);
}

//構(gòu)建DNS查詢問題區(qū)域
DNS_QUERIES create_dns_queries(const string& qName, QueryType qType, QueryClass qClass) {
	DNS_QUERIES queries;

	char* tmpName = strdup(qName.c_str());
	char* token = strtok(tmpName, ".");
	while (token != NULL) {
		size_t len = strlen(token);

		queries.qName.append((char*)(&len), 1);
		queries.qName.append(token, len);

		token = strtok(NULL, ".");
	}
	//queries.qName.append("\0");

	free(tmpName);

	queries.qType  = htons(qType);
	queries.qClass = htons(qClass);

	return std::move(queries);
}

//構(gòu)建一個DNS請求包
string create_dns_request(const DNS_HEADER& header, const DNS_QUERIES& queries) {
	string request = "";
	request.append((char*)&header, sizeof(header));

	request.append(queries.qName.data(), queries.qName.length() + 1);
	request.append((char*)&queries.qType, sizeof(queries.qType));
	request.append((char*)&queries.qClass, sizeof(queries.qClass));

	return std::move(request);
}

//解析域名(3www5baidu3com0 ---> www.baidu.com)
string parse_name(const string& name) {
	if (name.empty()) {
		return "";
	}

	string domain = "";
	int pos = 0;

	while (pos < name.length() - 1) {
		int len = name.at(pos);

		pos += 1;
		domain.append(name.data() + pos, len);
		pos += len;

		if (pos >= name.length()) {
			break;
		}

		domain.append(".");
	}

	return std::move(domain);
}

//解析收到的DNS響應(yīng)
void parse_dns_response(const char* data, uint16_t len) {
	if (len == 0) {
		return;
	}

	const char* ptr = data;
	ptr += 4;

	//問題數(shù)
	int questions = ntohs(*((uint16_t*)ptr));
	ptr += 2;

	//回答資源記錄數(shù):DNS響應(yīng)的數(shù)目
	int answerRRs = ntohs(*((uint16_t*)ptr));
	printf("\nanswerRRs:%d\n", answerRRs);
	ptr += 2;

	//跳過查詢問題區(qū)域
	ptr += 4;
	for (int i = 0; i < questions; i++) {
		while(1) {
			int flag = *ptr;

			ptr += (1 + flag);

			if (flag == 0) {
				break;
			}
		}
		ptr += 4;
	}

	//解析回答區(qū)域
	for (int i = 0; i < answerRRs; i++) {
		//解析域名
		string domain = "", ip = "";
		uint8_t flag = *ptr;
		
		//該字段使用是的2個字節(jié)的偏移指針來表示
		if ((flag & 0xC0) == 0xC0) {
			int offsetBytes = ntohs(*(uint16_t*)ptr) & (~0xC000);
			const char* pName = data + offsetBytes;
			domain = parse_name(pName);
			ptr += 2;
		}else {
			domain = parse_name(ptr);
			ptr += (strlen(ptr) + 1);
		}

		//響應(yīng)的資源記錄的類型
		uint16_t type = ntohs(*(uint16_t*)ptr);
		ptr += 4;

		//資源記錄的生命周期
		uint32_t ttl = ntohs(*(uint32_t*)ptr);
		ptr += 4;

		//資源數(shù)據(jù)的長度
		uint16_t dataLength = ntohs(*(uint16_t*)ptr);
		ptr += 2;

		//資源數(shù)據(jù)
		//不定長域名
		if (type == QueryType::CNAME) {
			ip = parse_name(ptr);
		}
		//4字節(jié)地址
		else if (type == QueryType::A) {
			if (dataLength == 4) {
				in_addr addr = {.s_addr = *(uint32_t*)ptr};
				ip = inet_ntoa(addr);
				ptr += 4;
			}
		}

		cout << domain << " : " << ip << endl;
	}
}

//設(shè)置socket為非阻塞
void setSocketNonBlock(int socketfd, bool nonBlock) {
	if (socketfd < 0) {
		return;
	}

	int flags = fcntl(socketfd, F_GETFL, 0);
	if (flags < 0) {
		return;
	}

	if (nonBlock) {
		flags |= O_NONBLOCK;
	}else {
		flags &= ~O_NONBLOCK;
	}

	fcntl(socketfd, F_SETFL, flags);
}

//向DNS服務(wù)端提交DNS請求(同步方式)
void dns_client_commit_sync() {
	//創(chuàng)建socket
	int fd = socket(AF_INET, SOCK_DGRAM, 0);
	if (fd < 0) {
		perror("socket");
		exit(-1);
	}

	setSocketNonBlock(fd, true);

	sockaddr_in destAddr;
	destAddr.sin_family = AF_INET;
	destAddr.sin_port = htons(53);
	destAddr.sin_addr.s_addr = inet_addr("114.114.114.114"); //DNS服務(wù)器地址

	//連接
	connect(fd, (sockaddr*)&destAddr, sizeof(destAddr));

	for (int i = 0; i < sizeof(domainAddr) / sizeof(domainAddr[0]); i++) {
		//構(gòu)建DNS頭部
    	DNS_HEADER header = create_dns_header();
    
		//構(gòu)建DNS查詢問題區(qū)域
    	DNS_QUERIES queries = create_dns_queries(domainAddr[i], QueryType::A, QueryClass::IN);

		//構(gòu)建一個DNS請求包
		string req = create_dns_request(header, queries);

	    sendto(fd, req.data(), req.length(), 0, (sockaddr*)&destAddr, sizeof(destAddr));

		char buffer[2048] = {0};
		sockaddr_in addr;
		socklen_t addrLen = sizeof(addr);

		while (1) {
			int recvLen = recvfrom(fd, buffer, sizeof(buffer), 0, (sockaddr*)&addr, &addrLen);
			if (recvLen <= 0) {
				continue;
			}

			//解析收到的DNS響應(yīng)
			parse_dns_response(buffer, recvLen);

			break;
		}
	}

	close(fd);
}

//處理DNS響應(yīng)的線程(異步方式)
void *thread_process_async_response(void * arg) {
	int epfd = *(int*)arg;
	epoll_event events[1024] = {0};

	while (1) {
		int nready = epoll_wait(epfd, events, 1024, -1);

		if (nready < 0) {
			if (errno == EINTR || errno == EAGAIN) {
				continue;
			}else {
				break;
			}
		}else if (nready == 0) {
			continue;
		}

		for (int i = 0; i < nready; i++) {
			int fd = events[i].data.fd;
			char recvBuf[1024] = {0};

			if (events[i].events & EPOLLIN) {
				sockaddr_in srcAddr;
				socklen_t addrLen;

				int recvSize = recvfrom(fd, recvBuf, sizeof(recvBuf), 0, (sockaddr*)&srcAddr, &addrLen);

				if (recvSize <= 0) {
					continue;
				}

				parse_dns_response(recvBuf, recvSize);

				epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
				close(fd);
			}
		}
	}

	return NULL;
}

//向DNS服務(wù)端提交DNS請求(異步方式)
void dns_client_commit_async() {
	int epfd = epoll_create(1);
	
	//創(chuàng)建一個線程來處理接收到的數(shù)據(jù)
	pthread_t threadID;
	pthread_create(&threadID, NULL, thread_process_async_response, &epfd);

	for (int i = 0; i < sizeof(domainAddr) / sizeof(domainAddr[0]); i++) {
		int fd = socket(AF_INET, SOCK_DGRAM, 0);
		if (fd < 0) {
			perror("socket");
			exit(-1);
		}

		setSocketNonBlock(fd, true);

		//將此fd加入epoll
		epoll_event event;
		event.events = EPOLL_EVENTS::EPOLLIN;
		event.data.fd = fd;
		epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);

		sockaddr_in destAddr;
		bzero(&destAddr, sizeof(destAddr));
		destAddr.sin_family = AF_INET;
		destAddr.sin_addr.s_addr = inet_addr("114.114.114.114");
		destAddr.sin_port = htons(53);

		connect(fd, (sockaddr*)&destAddr, sizeof(destAddr));

		//構(gòu)建DNS頭部
    	DNS_HEADER header = create_dns_header();
    
		//構(gòu)建DNS查詢問題區(qū)域
    	DNS_QUERIES queries = create_dns_queries(domainAddr[i], QueryType::A, QueryClass::IN);

		//構(gòu)建一個DNS請求包
		string req = create_dns_request(header, queries);

	    sendto(fd, req.data(), req.length(), 0, (sockaddr*)&destAddr, sizeof(destAddr));
	}
}

int main(int argc, char* argv[]) {
#if 1
	dns_client_commit_sync(); //同步實現(xiàn)
#else
	dns_client_commit_async(); //異步實現(xiàn)
	getchar();
#endif

    return 0;
}

以上就是Linux C/C++實現(xiàn)DNS客戶端請求域名IP的示例代碼的詳細內(nèi)容,更多關(guān)于C++ DNS客戶端請求域名IP的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C++實現(xiàn)連連看游戲核心代碼

    C++實現(xiàn)連連看游戲核心代碼

    這篇文章主要為大家詳細介紹了C++實現(xiàn)連連看游戲核心代碼,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-01-01
  • C++模擬鍵盤按鍵的實例

    C++模擬鍵盤按鍵的實例

    今天小編就為大家分享一篇C++模擬鍵盤按鍵的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-07-07
  • C++構(gòu)造函數(shù)的類型,淺拷貝與深拷貝詳解

    C++構(gòu)造函數(shù)的類型,淺拷貝與深拷貝詳解

    這篇文章主要為大家詳細介紹了C++構(gòu)造函數(shù)的類型,淺拷貝與深拷貝,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • C語言SetConsoleTextAttribute函數(shù)使用方法

    C語言SetConsoleTextAttribute函數(shù)使用方法

    這篇文章介紹了C語言SetConsoleTextAttribute函數(shù)的使用方法,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-12-12
  • C++實現(xiàn)對象化的矩陣相乘小程序

    C++實現(xiàn)對象化的矩陣相乘小程序

    這篇文章主要為大家詳細介紹了C++實現(xiàn)對象化的矩陣相乘小程序,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • C++取得本機IP的方法

    C++取得本機IP的方法

    這篇文章主要介紹了C++取得本機IP的方法,代碼簡單功能實用,具有不錯的借鑒參考價值,需要的朋友可以參考下
    2014-10-10
  • C語言中宏定義使用的小細節(jié)

    C語言中宏定義使用的小細節(jié)

    本篇文章是對C語言中宏定義使用的小細節(jié)進行了詳細的分析介紹,需要的朋友參考下
    2013-05-05
  • OpenCV獲取圖像中直線上的數(shù)據(jù)具體流程

    OpenCV獲取圖像中直線上的數(shù)據(jù)具體流程

    對圖像進行處理時,經(jīng)常會有這類需求:客戶想要提取出圖像中某條直線或者ROI區(qū)域內(nèi)的感興趣數(shù)據(jù),進行重點關(guān)注,怎么操作呢,下面小編通過實例代碼介紹下OpenCV獲取圖像中直線上的數(shù)據(jù),一起看看吧
    2021-11-11
  • 函數(shù)外初始化與函數(shù)內(nèi)初始化詳細解析

    函數(shù)外初始化與函數(shù)內(nèi)初始化詳細解析

    函數(shù)內(nèi)初始化:bool FillStr(char *&szDst, int nSize);第一個參數(shù)中的&一定不能少,這是因為在函數(shù)外部我們只聲明了這個指針,具體這個指針指向內(nèi)存中的哪個地址我們并不知道,所以&是為了說明傳遞的是這個指針的引用,那么在函數(shù)內(nèi)初始化后這個指針的地址也就是外面指針的地址了
    2013-09-09
  • 詳細分析Android中實現(xiàn)Zygote的源碼

    詳細分析Android中實現(xiàn)Zygote的源碼

    這篇文章主要介紹了詳細分析Android中實現(xiàn)Zygote的源碼,包括底層的C/C++代碼以及Java代碼部分入口,需要的朋友可以參考下
    2015-07-07

最新評論