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字节,结构如下:
|
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的正文包括域名值和请求类型标识字段。其中请求类型定义为结构体:
|
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; | } | |
我的测试结果如下:
|
[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 | |
转载本站文章请注明转载自:
独语者