Linux 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++構(gòu)造函數(shù)的類型,淺拷貝與深拷貝詳解
這篇文章主要為大家詳細介紹了C++構(gòu)造函數(shù)的類型,淺拷貝與深拷貝,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-03-03C語言SetConsoleTextAttribute函數(shù)使用方法
這篇文章介紹了C語言SetConsoleTextAttribute函數(shù)的使用方法,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-12-12OpenCV獲取圖像中直線上的數(shù)據(jù)具體流程
對圖像進行處理時,經(jīng)常會有這類需求:客戶想要提取出圖像中某條直線或者ROI區(qū)域內(nèi)的感興趣數(shù)據(jù),進行重點關(guān)注,怎么操作呢,下面小編通過實例代碼介紹下OpenCV獲取圖像中直線上的數(shù)據(jù),一起看看吧2021-11-11函數(shù)外初始化與函數(shù)內(nèi)初始化詳細解析
函數(shù)內(nèi)初始化:bool FillStr(char *&szDst, int nSize);第一個參數(shù)中的&一定不能少,這是因為在函數(shù)外部我們只聲明了這個指針,具體這個指針指向內(nèi)存中的哪個地址我們并不知道,所以&是為了說明傳遞的是這個指針的引用,那么在函數(shù)內(nèi)初始化后這個指針的地址也就是外面指針的地址了2013-09-09