socket编程实现简单DNS协议实现获取域名ip(UDP)

http://www.isayme.org/socket-udp-dns-ping-ip.html

在文章《无需socket或ping命令,使用gethostbyname获取域名ip地址》中提到用gethostbyname获取域名ip的方法。今天就用个socket的方法吧。

原理比较简单:在浏览器输入网址即可浏览网页,但浏览器是如何知道相应网址对应的服务器的ip呢?稍微了解一点的同学都知道这归功于DNS服务。具体DNS的资料这里就不多说了,网上大堆的信息。我们需要知道的是浏览网页时浏览器会向你网卡设置的DNS服务器IP发送DNS消息以获取相应域名的ip地址。这个DNS服务器的IP地址可以自定义也可以不填留着让系统自动获取。我个人会自己选个DNS,一般可选的包括google提供的8.8.8.8和4.4.4.4,但因为大家都懂的河蟹问题,这两个DNS服务器不适合咱们天朝淫民使用,我用的DNS服务器是OpenDNS 提供的,设置如下图:

我们今天需要解决的是向DNS服务器发送DNS协议包,等待DNS服务器返回域名对应的IP地址。

DNS可以通过TCP也可以通过UDP传输,不过实际使用中99%以上使用UDP。前两天看到提到使用TCP协议避免DNS污染的方法。本文暂时先以UDP协议作为例子。UDP协议无需connect连接DNS服务器,直接发送数据包即可。

目前的关键是如何构建DNS数据包及如何解析返回数据包。

DNS数据包包括DNS协议头+DNS正文段,详细内容可参考文章《DNS协议及应用》。协议头为固定的12字节,结构如下:

1
2
3
4
5
6
7
8
9
      
      
typedef struct _DNS_HDR
{  
  U16 id;
  U16 tag;
  U16 numq;
  U16 numa;
  U16 numa1;
  U16 numa2;
}DNS_HDR;

不过这不是严格的结构体,因为严格的结构体中是使用的位字段,我们这里省事就没有那么严格。我们在初始化的时候置id为1,tag为网络字节序的0x0100,numq为1即可。其他都为0。即请求一次递归查询。至于查询什么内容,则由DNS数据包的正文决定。查询ip的正文包括域名值和请求类型标识字段。其中请求类型定义为结构体:

1
2
3
4
5
      
      
typedef struct _DNS_QER
{
   U16 type;
   U16 classes;
}DNS_QER;

其中type表明请求类型,我们是获取域名ip即请求主机A记录,此值为网络字节序的1,classes表示internet问题类,通常也是网络字节序的1。如此我们的DNS数据包格式就是:DNS_HDR+域名+DNS_QER。

值得一提的是"域名"需要经过简单的编码,如 microsoft.com,在问题名称字段中的域名称被编码为一系列的长度-值格式。 例如,域 microsoft.com 表示为0x09microsoft0x03com0x00,其中十六进制数字表示每个标签的长度、 ASCII 字符表示单个标签,并最终 0 指示名称的结尾。
表达不清,详情可参考《 DNS 协议

发送完请求数据包等待接收响应包。相应包需判断是否是正确的相应,因为可能我们发送的请求吧格式有误,特别是域名的编码那里可能会出错。判断额方法就是协议头的numa不等于0;然后就是解析数据包。由于我们测试的程序很简答,请求的响应包的最后四个字节就是我们需要的ip地址。简单的读取解析即可。

说的很乱,本人不大会措词,并不清楚的可参考文章中提到的两篇文章,也可留言相互讨论。下面贴出我的测试代码:

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
      
      
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#define BUF_SIZE 1024
#define SRV_PORT 53
typedef unsigned short U16;
const char srv_ip[] = "208.67.222.222";
typedef struct _DNS_HDR
{  
  U16 id;
  U16 tag;
  U16 numq;
  U16 numa;
  U16 numa1;
  U16 numa2;
}DNS_HDR;
typedef struct _DNS_QER
{
   U16 type;
   U16 classes;
}DNS_QER;
int main(int argc, char** argv)
{
    int      servfd,clifd,len = 0,i;
    struct   sockaddr_in servaddr, addr;
    int      socklen = sizeof(servaddr);
    char     buf[BUF_SIZE];
    char     *p;
    DNS_HDR  *dnshdr = (DNS_HDR *)buf;
    DNS_QER  *dnsqer = (DNS_QER *)(buf + sizeof(DNS_HDR));
   
    if ((clifd  =  socket(AF_INET, SOCK_DGRAM, 0 ))  <   0 )
    {
         printf( " create socket error!\n " );
         return -1;
    }
   
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    inet_aton(srv_ip, &servaddr.sin_addr);
    servaddr.sin_port = htons(SRV_PORT);
   
    /*if (connect(clifd, (struct sockaddr *)&servaddr, socklen) < 0)
    {
          printf( " can't connect to %s!\n ", argv[ 1 ]);
          return -1;
    }*/
    memset(buf, 0, BUF_SIZE);
    dnshdr->id = (U16)1;
    dnshdr->tag = htons(0x0100);
    dnshdr->numq = htons(1);
    dnshdr->numa = 0;
   
    strcpy(buf + sizeof(DNS_HDR) + 1, argv[1]);
    p = buf + sizeof(DNS_HDR) + 1; i = 0;
    while (p < (buf + sizeof(DNS_HDR) + 1 + strlen(argv[1])))
    {
        if ( *p == '.')
        {
            *(p - i - 1) = i;
            i = 0;
        }
        else
        {
            i++;
        }
        p++;
    }
    *(p - i - 1) = i;
       
    dnsqer = (DNS_QER *)(buf + sizeof(DNS_HDR) + 2 + strlen(argv[1]));
    dnsqer->classes = htons(1);
    dnsqer->type = htons(1);
   
     
    len = sendto(clifd, buf, sizeof(DNS_HDR) + sizeof(DNS_QER) + strlen(argv[1]) + 2, 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
    //len = send(clifd, buf, sizeof(DNS_HDR) + sizeof(DNS_QER) + strlen(argv[1]) + 2, 0);
    i = sizeof(struct sockaddr_in);
    len = recvfrom(clifd, buf, BUF_SIZE, 0, (struct sockaddr *)&servaddr, &i);
    //len = recv(clifd, buf, BUF_SIZE, 0);
    if (len < 0)
    {
          printf("recv error\n");
          return -1;
    }
    if (dnshdr->numa == 0)
    {
          printf("ack error\n");
          return -1;
    }
    p = buf + len -4;
    printf("%s ==> %u.%u.%u.%u\n", argv[1], (unsigned char)*p, (unsigned char)*(p + 1), (unsigned char)*(p + 2), (unsigned char)*(p + 3));
 
    close(clifd);
    return 0;
}

我的测试结果如下:

1
2
3
4
5
6
      
      
[root@isayme ping]# ./a.out isayme.org
isayme.org ==> 173.231.29.114
[root@isayme ping]# ./a.out google.com
google.com ==> 74.125.71.147
[root@isayme ping]# ./a.out twitter.com
twitter.com ==> 37.61.54.158

转载本站文章请注明转载自: 独语者
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值