Linux下C/C++实现DNS查询(DNS QUERY)

这篇具有很好参考价值的文章主要介绍了Linux下C/C++实现DNS查询(DNS QUERY)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

DNS 的全称是 Domain Name System 或者 Domain Name Service,它主要的作用就是将人们所熟悉的网址 (域名) “翻译”成电脑可以理解的 IP 地址,这个过程叫做 DNS 域名解析。域名是由一串用点分隔的名字组成的 Internet 上某一台计算机或计算机组的名称,用于在数据传输时对计算机的定位标识。

DNS 查询到底是怎么完成的?

我们输入域名,浏览器就会在后台,自动向 DNS 服务器发出请求,获取对应的 IP 地址。这就是 DNS 查询。

Linux下C/C++实现DNS查询(DNS QUERY)

命令行工具 dig 可以跟 DNS 服务器互动,我们就用它演示 DNS 查询。简单介绍dig,dig(域信息搜索器)是一个用于询问 DNS 域名服务器的灵活的工具。它执行 DNS 搜索,显示从受请求的域名服务器返回的答复。

Linux下C/C++实现DNS查询(DNS QUERY)
然后说了查询的目标数例如上述命令只查询了一个网站,所以query就为1,但是有三个answer,所以answer=3.

上面这几行字需要注意question section,这个显示的是你查询了什么,这边显示的是你在查询www.baidu.com的A记录。也是仅仅只有一条查询。

最后显示查询结果及查询时间与DNS服务器。

DNS用的是TCP协议还是UDP协议

DNS占用53号端口,同时使用TCP和UDP协议。那么DNS在什么情况下使用这两种协议?

首先了解一下TCP与UDP传送字节的长度限制:

UDP报文的最大长度为512字节,而TCP则允许报文长度超过512字节。当DNS查询超过512字节时,协议的TC标志出现删除标志,这时则使用TCP发送。通常传统的UDP报文一般不会大于512字节。

DNS在区域传输的时候使用TCP协议,其他时候使用UDP协议。

  • 域名解析 - UDP

客户端向DNS服务器查询域名,一般返回的内容都不超过512字节,用UDP传输即可。不用经过TCP三次握手,这样DNS服务器负载更低,响应更快。虽然从理论上说,客户端也可以指定向DNS服务器查询的时候使用TCP,但事实上,很多DNS服务器进行配置的时候,仅支持UDP查询包。

  • 区域传输 -TCP

实现这种功能时则有时需要TCP协议,即进行与主域名服务器进行查询以确认数据是否有效,用TCP则是依赖了其可靠性

理论上来说,在客户端与DNS进行通信的过程中,使用两种传输协议在理论上都是可以实行的,但是事实上在目前的浏览器或者说在目前的清醒进行客户端与DNS的通信时一般默认使用UDP,而且某些客户端与DNS进行通信的时候还指定了使用UDP的通信方式,这就和当前HTTP与HTTPS对比下,在民用,安全问题不严峻的前提下,会偏向于使用速度更快的协议。

域名结构

Linux下C/C++实现DNS查询(DNS QUERY)
DNS 整个结构图是树状结构,最顶层称为 根域, 用 点 " . " 表示,相应服务器称为根服务器,整个域名空间的解析权都归根服务器所有。

因为负载庞大,所以采用 “ 委派” 机制,根域下设置一级域,将顶级域解析权委派给一级域服务器。同理,顶级域 下面设置 二级域, 二级域 下设 三级域。

根域:

位于域名空间最顶层, 一般用 一个 点 " . " 表示

一级域:

一般代表一种类型的组织机构或者国家地区。如 .net (网络供应商), .com (工商企业) , .org (团体组织), .edu (教育机构) , .gov (政府部门) , .cn (中国国家域名)

二级域:

用来标明顶级域内的一个特定组织,国家顶级域下面的二级域名有国家网络部门统一管理,如 .cn 顶级域名下面设置的二级域名 : .com.cn , .net.cn , .edu.cn

子域:

二级域下所创建的各级域统称为子域,各个组织或用户可以自由申请注册自己的域名

DNS Packet Structure

DNS 分为查询请求和查询响应,请求和响应的报文结构基本相同。DNS 报文格式如图所示。

Linux下C/C++实现DNS查询(DNS QUERY)
上图中显示了 DNS 的报文格式。其中,事务 ID、标志、问题计数、回答资源记录数、权威名称服务器计数、附加资源记录数这 6 个字段是DNS的报文首部,共 12 个字节。整个 DNS 格式主要分为 3 部分内容,即基础结构部分(报文首部)、问题部分、资源记录部分。
Linux下C/C++实现DNS查询(DNS QUERY)

DNS Headers
DNS数据包具有如下所示的标头。请注意,请求和回复遵循相同的内容标头格式。

Linux下C/C++实现DNS查询(DNS QUERY)

标志字段中每个字段的含义如下:

QR(Response):查询请求/响应的标志信息。查询请求时,值为 0;响应时,值为 1。

Opcode:操作码。其中,0 表示标准查询;1 表示反向查询;2 表示服务器状态请求。

AA(Authoritative):授权应答,该字段在响应报文中有效。值为 1 时,表示名称服务器是权威服务器;值为 0 时,表示不是权威服务器。

TC(Truncated):表示是否被截断。值为 1 时,表示响应已超过 512 字节并已被截断,只返回前 512 个字节。

RD(Recursion Desired):期望递归。该字段能在一个查询中设置,并在响应中返回。该标志告诉名称服务器必须处理这个查询,这种方式被称为一个递归查询。如果该位为 0,且被请求的名称服务器没有一个授权回答,它将返回一个能解答该查询的其他名称服务器列表。这种方式被称为迭代查询。

RA(Recursion Available):可用递归。该字段只出现在响应报文中。当值为 1 时,表示服务器支持递归查询。
Z:保留字段,在所有的请求和应答报文中,它的值必须为 0。

rcode(Reply code):返回码字段,表示响应的差错状态。当值为 0 时,表示没有错误;当值为 1 时,表示报文格式错误(Format error),服务器不能理解请求的报文;当值为 2 时,表示域名服务器失败(Server failure),因为服务器的原因导致没办法处理这个请求;当值为 3 时,表示名字错误(Name Error),只有对授权域名解析服务器有意义,指出解析的域名不存在;当值为 4 时,表示查询类型不支持(Not Implemented),即域名服务器不支持查询类型;当值为 5 时,表示拒绝(Refused),一般是服务器由于设置的策略拒绝给出应答,如服务器不希望对某些请求者给出应答
Linux下C/C++实现DNS查询(DNS QUERY)图中的数据包为 DNS 请求包,Domain Name System(query) 部分方框标注中的信息为 DNS 报文中的基础结构部分。

Linux下C/C++实现DNS查询(DNS QUERY)

// query type
enum {
	QTYPE_A = 1,		
	QTYPE_NS = 2,		
	QTYPE_MD = 3,		
	QTYPE_MF = 4,		
	QTYPE_CNAME = 5,	
	QTYPE_SOA = 6,		
	QTYPE_MB = 7,		
	QTYPE_MG = 8,		
	QTYPE_MR = 9,		
	QTYPE_NULL = 10,	
	QTYPE_WKS = 11,		
	QTYPE_PTR = 12,		
	QTYPE_HINFO = 13,	
	QTYPE_MINFO = 14,	
	QTYPE_MX = 15,		
	QTYPE_TXT = 16,		
	QTYPE_AAAA = 28,	
	QTYPE_ANY = 255
};

// options
enum {
	DNS_OPT_TIMEOUT,
	DNS_OPT_RETRY,
	DNS_OPT_IPV4,
	DNS_OPT_IPV6,
	DNS_OPT_PORT,
	DNS_OPT_BUF,
	DNS_OPT_BUFSIZE
};

struct dns_query *dns_init(struct dns_query *dns, int query_type);
int dns_set_option(struct dns_query *dns, int opt, ...);
int dns_add_question(struct dns_query *dns, const char *domain, uint16_t qtype);
int dns_send_query(struct dns_query *dns);
struct dns_packet *dns_response_packet(struct dns_query *dns);
int dns_next_rr(struct dns_rr *rr, struct dns_query *dns);
char *tdns_extract_domain(struct dns_query *query, unsigned char *domain);

.....
int main(int argc, char **argv)
{
...

	dns_init(&dns, QUERY_STANDARD);

	// 超时(毫秒)
	dns_set_option(&dns, DNS_OPT_TIMEOUT, 3000);

	// 超时超过时重试的次数
	dns_set_option(&dns, DNS_OPT_RETRY, 2);

	// DNS服务器ipv4
	dns_set_option(&dns, DNS_OPT_IPV4, "8.8.8.8");

	// 或者可以使用ipv6
	// tinydns_set_option(&dns, DNS_OPT_IPV6, "2001:4860:4860::8888");

	// 响应缓冲区,它应该大于或等于512字节
	dns_set_option(&dns, DNS_OPT_BUF, buf);
	dns_set_option(&dns, DNS_OPT_BUFSIZE, sizeof(buf));
...
	if (dns_add_question(&dns, argv[1], QTYPE_A)) 
	{
		printf("failed to add a question\n");
		goto end;
	}

	if (dns_send_query(&dns)) 
	{
		printf("failed to send dns query...\n");
		goto end;
	}

	response = dns_response_packet(&dns);
	if (response == NULL) 
	{
		printf("response is too short...\n");
		goto end;
	}

	printf("-- header --\n");
	printf("id:                         %u\n", response->id);

	printf("query(0) or response(1):    %u\n", response->qr);
	printf("opcode:                     %u\n", response->opcode);
	printf("authoritative answer:       %u\n", response->aa);
	printf("truncated packet:           %u\n", response->tc);
	printf("recursion desired:          %u\n", response->rd);

	printf("recursion available:        %u\n", response->ra);
	printf("zero:                       %u\n", response->z);
	printf("response code (0 is okay):  %u\n", response->rcode);

	printf("questions:                  %u\n", response->qdcount);
	printf("answers:                    %u\n", response->ancount);
	printf("name servers:               %u\n", response->nscount);
	printf("additional records:         %u\n", response->arcount);
	printf("---\n\n");

	printf("RRs (resource records):\n");

	while (dns_next_rr(&rrbuf, &dns)) 
	{
		char *domain = dns_extract_domain(&dns, rrbuf.name);
		printf("\n\n");
		printf("domain:      %s\n", domain);
		printf("type:        %u\n", rrbuf.type);
		printf("class:       %u\n", rrbuf.class);
		printf("ttl:         %u\n", rrbuf.ttl);
		printf("rdlength:    %u\n", rrbuf.rdlength);

...

		free(domain);
	}
...
}

运行结果:

Linux下C/C++实现DNS查询(DNS QUERY)
Linux下C/C++实现DNS查询(DNS QUERY)
tcpdump观察:

Linux下C/C++实现DNS查询(DNS QUERY)同时我们保存了dns_query报文
Linux下C/C++实现DNS查询(DNS QUERY)
Linux下C/C++实现DNS查询(DNS QUERY)If you need the complete source code, please add the WeChat number (c17865354792)

总结

DNS是一个域名系统,是万维网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串。

Welcome to follow WeChat official account【程序猿编码

参考:RFC 1035文章来源地址https://www.toymoban.com/news/detail-450095.html

到了这里,关于Linux下C/C++实现DNS查询(DNS QUERY)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • SQL Server查询计划(Query Plan)——XML查询计划

    ​​​​​​6.4.3.  XML 查询计划 SQL Server中,除了通过GUI工具和相关命令获取图形及文本查询计划外,我们还可以通过相关命令获取XML格式的查询计划,这里惯称其为XML查询计划。 SQL Server 2005版本引入了XML查询计划的新特性,其充分吸收了图形及文本查询计划的优势所在,通

    2024年02月22日
    浏览(35)
  • Elasticsearch:理解 query_string 和 simple_query_string 查询

    针对很多的开发者来说,如果你不是很熟悉 DSL 查询,那么在有些情况下,query_string 及 simple_query_string 变得非常灵活及方便。在今天的文章中,我来比较一下这两种查询的方法。 我们先使用 _bulk 命令创建如下的一个索引: 此查询使用语法根据运算符(例如 AND 或 NOT)解析和

    2023年04月10日
    浏览(32)
  • ElasticSearch级查询Query DSL上

    目录 ES高级查询Query DSL match_all 返回源数据_source 返回指定条数size 分页查询fromsize 指定字段排序sort 术语级别查询 Term query术语查询 Terms Query多术语查询 exists query ids query range query范围查询 prefix query前缀查询 wildcard query通配符查询 fuzzy query模糊查询        ES中提供了一种强大

    2024年02月20日
    浏览(45)
  • ElasticSearch Index查询(Query DSL)

    先贴一个Query DSL的官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html 我平时喜欢查看官方文档,了解数据查询和存储方面的性能优化点,下面是积累的脚本分享。 查询语句格式 查询类型:match_all,match,term,range,fuzzy,bool 等等 查询条件:查询条件会根

    2024年02月07日
    浏览(37)
  • Elasticsearch复合查询之Boosting Query

    前言 ES 里面有 5 种复合查询,分别是: Boolean Query Boosting Query Constant Score Query Disjunction Max Query Function Score Query Boolean Query在之前已经介绍过了,今天来看一下 Boosting Query 用法,其实也非常简单,总结起来就一句话,对不期待的查询进行相关性降分。 Boost 加权机制底层

    2024年02月12日
    浏览(32)
  • Elasticsearch:使用 query_string 查询的短语及模糊查询

    在我之前的文章系列里,我详细描述了 query_string 的一些功能: Elasticsearch: query_string 查询 Elasticsearch:以更简单的方式编写具有逻辑条件的 Elasticsearch 查询 - query_string Elasticsearch:理解 query_string 和 simple_query_string 查询 在今天的文章中,我们来聊聊 query_string 中的一下特殊查询

    2024年02月09日
    浏览(35)
  • 软件测试|SQLAlchemy query() 方法查询数据

    简介 上一篇文章我们介绍了SQLAlchemy 的安装和基础使用,本文我们来详细介绍一下如何使用SQLAlchemy的query()方法来高效的查询我们的数据。 创建模型 我们可以先创建一个可供我们查询的模型,也可以复用上一篇文章中我们创建的模型,代码如下: 创建实例 创建会话 在使用

    2024年01月16日
    浏览(39)
  • ElasticSearch 高级查询语法Query DSL实战

    ES中提供了一种强大的检索数据方式,这种检索方式称之为Query DSL(Domain Specified Language 领域专用语言) , Query DSL是利用Rest API传递JSON格式的请求体(RequestBody)数据与ES进行交互,这种方式的丰富查询语法让ES检索变得更强大,更简洁。 官方文档:https://www.elastic.co/guide/en/elasti

    2024年02月07日
    浏览(36)
  • Elasticsearch 查询之Function Score Query

    前言 ES 的主查询评分模式分为两种,是信息检索领域的重要算法: TF-IDF 算法 和 BM25 算法。 Elasticsearch 从版本 5.0 开始引入了 BM25 算法作为默认的文档评分(relevance scoring)算法。在此之前,Elasticsearch 使用的是 TF-IDF 算法作为默认的文档评分算法。从版本 5.0 起,BM25 算法取代

    2024年02月12日
    浏览(29)
  • 15.Elasticsearch 7.15 Query DSL 之 Wildcard查询、Regexp查询

    返回包含与通配符模式匹配的文档。 以下搜索返回 user.id 字段包含以 ki 开头并以 y 结尾的文档。这些匹配项可以包括 kiy、kity 或 kimchy (必填, 对象) 你想查询的字段 参数名 描述 boost (Optional, float) 用于降低或提高查询相关性得分的浮点数。默认为1.0。 rewrite (可选,字符串)

    2023年04月08日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包