dns工作过程及原理 (linux dns及android dan的实现差异)

来源:互联网 发布:中国大豆进口数据 编辑:程序博客网 时间:2024/06/12 20:44

前言

本文分析dns工作过程及原理,给一个简单的dns实现代码流程,并针对linux及android
  • 1
  • 2

下实现dns的不同,分别分析,供学习dns参考。

一、DNS功能

DNS(Domain Name System,域名系统),dns用于进行域名解析,说白了,就是给出一个
  • 1
  • 2

主机名,你给我找出该主机名对应的ip地址。例如:给你www.baidu.com的主机名,你给 
我查出对应的ip地址:163.177.151.109。一些主机名还会有别名,如www.baidu.com就 
有别名www.a.shifen.com,甚至不止一个别名,或一个别名有2个ip地址。在linux机子 
上,运行nslookup(name service lookup)就是进行域名解析。如下面:

~$ nslookup www.baidu.comServer:         127.0.0.1Address:        127.0.0.1#53Non-authoritative answer:www.baidu.com   canonical name = www.a.shifen.com.Name:   www.a.shifen.comAddress: 163.177.151.109Name:   www.a.shifen.comAddress: 163.177.151.110
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

二、DNS的原理

上面介绍了dns的功能,那要实现dns的功能,dns需要做什么工作?主要就3点:1、记录dns
  • 1
  • 2

服务器的ip地址(请求进行域名解析的地方);2、向dns服务器请求进行域名解析(dns 
实质工作),3、缓存已经解析过的主机名与ip地址对应关系(这样已缓存的就不需要再向 
dns服务器请求解析了)。 
一个dns的解析过程如下: 
1、 应用程序请求一个主机名解析,如:应用程序跟dns说,我需要知道www.baidu.com 
的ip地址,你给我查一下; 
2、 Dns先在本地缓存中查询,若查到,返回ip地址给应用,流程结束。若没查到, 进行 
下一步; 
3、 Dns把需要查询的主机名打包进dns数据包,然后把dns数据包发送到dns服务; 
4、 DNS服务器返回查询的主机名的dns数据包; 
5、 Dns解析数据包,把主机名对应的ip地址返回给应用程序; 
下图为网卡抓到的dns数据包的收发,请求www.baidu.com的ip地址: 
这里写图片描述

下图为dns运行流程: 
这里写图片描述 
上面是从本机看到的情况,实际上在向DNS服务器请求时,所请求的DNS服务器也不一 
定知道主机名对应的ip地址,就会向上一级DNS请求,更多资料请自行查找dns协议。

下面代码片段为freescale小系统上的dns处理过程,看起来流程还是比较清晰简单的:

wiced_result_t dns_client_hostname_lookup( const char* hostname, wiced_ip_address_t* address, uint32_t timeout_ms ){    /* 省略部分代码*/     ...    /* Create socket to send packets */    if ( wiced_udp_create_socket( &socket, WICED_ANY_PORT, WICED_STA_INTERFACE ) != WICED_SUCCESS )    {        return WICED_ERROR;    }    while ( ipv4_address_found == WICED_FALSE && remaining_time > 0)    {        /* Send DNS query messages */        for ( a = 0; a < dns_server_address_count; a++ )        {            wiced_ip_address_t host_ipv6_address;            uint16_t           available_space;            /* 创建dns数据包 */            if ( wiced_packet_create_udp( &socket, (uint16_t) ( sizeof(dns_message_header_t) + sizeof(dns_question_t) + hostname_length ), &packet, (uint8_t**) &iter.header, &available_space ) != WICED_SUCCESS )            {                goto exit;            }            /* 省略部分代码*/            ...            /* 发送dns数据包 */            if ( wiced_udp_send( &socket, &dns_server_address_array[a], DNS_PORT, packet ) != WICED_SUCCESS )            {                wiced_packet_delete( packet );                goto exit;            }            /* 省略部分代码*/            ...        }        /* Attempt to receive response packets */        while ( ipv4_address_found == WICED_FALSE && remaining_time > 0 )        {            /* 省略部分代码*/            ...            /* 接收dns数据包 */            if ( wiced_udp_receive( &socket, &packet, remaining_time ) == WICED_SUCCESS )            {                uint16_t     data_length;                uint16_t     available_data_length;                wiced_bool_t answer_found = WICED_FALSE;                /* Extract the data */                result = wiced_packet_get_data( packet, 0, (uint8_t**) &iter.header, &data_length, &available_data_length );                if ( ( result != WICED_SUCCESS ) || ( data_length < sizeof(dns_message_header_t) ) )                {                    goto exit;                }                /* 解析dns数据包,解析出主机名的ip地址 */                /* 省略部分代码*/                ...                wiced_packet_delete( packet );            }            /* 省略部分代码*/            ...        }    }exit:    wiced_udp_delete_socket( &socket );    /* 省略部分代码*/    ...    if ( ipv4_address_found == WICED_TRUE )    {        *address = resolved_ipv4_address;        return WICED_SUCCESS;    }    /* 省略部分代码*/    ...    return WICED_ERROR;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88

三、Linux的dns实现

网上搜索Linux dns时,都是一些linux dns怎么配置的,配置host.conf,
  • 1
  • 2

resolv.conf文件,配置完后,重启一下network就好了,至于为什么配置这些文件,dns 
又是在哪里执行的,都没有说明。 
在Linux上执行nslookup(name service lookup)或ping(ping主机名,如:ping 
www.baidu.com)都会执行dns的动作。执行dns时都是调用gethostbyname函数, 
gethostbyname函数,传入主机名,dns执行成功后,返回主机名对应的ip地址。 
而gethostbyname函数的实现是在c库中完成的,也就是dns的整个执行过程都是由c库实 
现,c库中dns实现过程与上一节介绍的流程原理是相同的,只是还多了一些配置文件用于 
设置参数、dns ip地址什么的,就是前面提到的host.conf,resolv.conf文件,这就是为什 
么Linux的dns只需要配置host.conf,resolv.conf文件,不需要再做其它工作的原因,因为 
c库已经替你完成dns的实际工作。在Linux嵌入式系统中,若发现dns不能正常工作,那就 
看一下host.conf,resolv.conf等文件是否配置正确,还有就是dns运行使用的c库在机子上 
有没有。 
想了解c库怎么实现dns的,请自行下载c库源码分析:http://www.gnu.org/software/libc/

四、Android的dns实现

Android也是搭建在Linux之上,而在Android下就没见到host.conf,resolv.conf这
  • 1
  • 2

些文件,但Android的dns也是能正常工作的。这是因为Android重写了libc库的dns实现 
部分,重写的dns代码在android\bionic\libc\dns目录。重写后的dns,请求dns的接口, 
有gethostbyname(兼容以前的c库接口)及android_gethostbynamefornet(给 
android使用)接口,但根据不同的dns请求发起者,走的流程有点不一样,其中还涉及到 
了netd的处理。Android java层请求dns时,是通过netd进行,netd调用 
android_gethostbynamefornet接口。而Android下编译的ping程序是不通过netd而直 
接调用gethostbyname接口,但最终还是要走到netd,由netd调用 
android_gethostbynamefornet接口。Android下dns的请求流程如下图: 
这里写图片描述

gethostbyname_internal中判断是否为netd调用下来的依据是,
  • 1
  • 2

getenv(“ANDROID_DNS_MODE”)返回是否不为NULL并且值为“local”,而netd启动 
时会设置setenv(“ANDROID_DNS_MODE”, “local”, 1),而ping程序不会。

五、Linux的dns缓存

Linux的dns,在配置resolv.conf文件的时候,第一个dns ip地址配置为nameserver 
  • 1
  • 2

127.0.0.1,也就是从本地dns缓存中查找,查找不到时再根据第二个、第三个dns ip地址 
进行dns请求。

六、第三方dns库

有时候我们不想使用编译工具中libc自带的dns库,也可以移植第三
  • 1
  • 2

方的dns库,如c-ares dns库,该dns库支持多种平台。例如:linux 
平台上,它与linux的配置完全兼容,可以在现有的linux dns配置文 
件上,直接编译c-ares库放到机子上即可使用。

原创粉丝点击