首页 > 代码库 > Linux socket编程 DNS查询IP地址
Linux socket编程 DNS查询IP地址
本来是一次计算机网络的实验,但是还没有完全写好,DNS的响应请求报文的冗余信息太多了,不只有IP地址。所以这次的实验主要就是解析DNS报文。同时也需要正确的填充请求报文。如果代码有什么bug,欢迎指正啊。代码排版有点乱。。。
本文有以下内容
DNS报文的填充和解析
利用socket API传输信息
一、填充DNS请求报文
随便百度一下,就可以知道DNS报文的格式。所以这里只介绍如何填充DNS报文。
首先是填充报文首部:
1 2 3 4 5 6 7 8 9 | /* 填充首部的格式大致相同,下面的填充值是参考他人抓包分析的结果 */ buf[0] = 0x00; buf[1] = 0; buf[2] = 0x01; buf[3] = 0; buf[4] = 0; buf[5] = 1; buf[6] = buf[7] = 0; buf[8] = buf[9] = buf[10] = buf[11] = 0; |
然后填充报文的问题部分:
- 域名格式:该部分一数字开始以0结束。
- 查询类型:1代表IP地址、2代表名字服务器、5代表规范名称、12代表指针记录
- 查询类:1代表互联网
下面是填充域名为百度(www.baidu.com)的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | /* 填充域名 */ buf[12] = 3; buf[13] = buf[14] = buf[15] = ‘w‘ ; buf[16] = 5; buf[17] = ‘b‘ ; buf[18] = ‘a‘ ; buf[19] = ‘i‘ ; buf[20] = ‘d‘ ; buf[21] = ‘u‘ ; buf[22] = 3; buf[23] = ‘c‘ ; buf[24] = ‘o‘ ; buf[25] = ‘m‘ ; buf[26] = 0; /* 填充查询类型和查询类 */ buf[27] = 0; buf[28] = 1; buf[29] = 0; buf[30] = 1; |
二、利用socket发送DNS报文
下面是代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | int sendDNSPacket(unsigned char *buf, int len, char *recvMsg) { int s; struct sockaddr_in sin ; memset (& sin ,0, sizeof ( sin )); sin .sin_addr.s_addr = inet_addr( "127.0.0.1" ); /* 本体DNS服务器的地址 */ sin .sin_family = AF_INET; sin .sin_port = htons(SERVER_PORT); /* 端口为53 */ s = socket(PF_INET,SOCK_DGRAM,0); /* UDP传报文 */ sendto(s,buf,len,0,( struct sockaddr *)& sin , sizeof ( sin )); return recv(s,recvMsg,MAX_SIZE,0); } |
这部分就是普通的socket的创建、发送和接收过程。
三、解析DNS响应报文
自己错将16进制的数错看为10进制数了,在这里坑了很长时间。注意报文中的指针的偏移量,只有指针的偏移量指的是规范名称该资源记录才是IP地址。在报文中还有很多和IP地址无关的资源记录。下面是代码,不过遇到复杂的DNS报文可能有bug。这里只实验了三个域名:www.ccnu.edu.cn www.baidu.com www.163.com(这个域名有点复杂)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | void resolve(unsigned char *recvMsg, int len, int len_recvMsg) { int pos = len; int cnt = 12; while (pos < len_recvMsg) { unsigned char now_pos = recvMsg[pos+1]; unsigned char retype = recvMsg[pos+3]; unsigned char reclass = recvMsg[pos+5]; unsigned char offset = recvMsg[pos+11]; if (retype == 1) { if (now_pos == cnt && reclass == 1) { printf ( "%u.%u.%u.%u\n" ,recvMsg[pos+12],recvMsg[pos+13],recvMsg[pos+13],recvMsg[pos+14]); } } else if (retype == 5) { cnt = pos + 12 ; } pos = pos + 12 + offset; } } |
四、完整的代码和总结
下面是完整的代码:
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 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | /************************************************************************* > File Name: MyFiles/C和C++程序/socket/getIP.c > Author: mr_zys > Mail: 247629929@qq.com > Created Time: Thu 12 Jun 2014 05:22:06 PM CST > Operating System: Ubuntu 12.04 LTS > Programming Language: Linux c > Compiler: gcc > Description: this is a program with Linux socket APIs to ask DNS server for domain name‘s IP adress! ************************************************************************/ #include<stdio.h> #include<stdlib.h> #include<string.h> #include<errno.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #include<netdb.h> #define MAX_SIZE 1024 #define SERVER_PORT 53 void setHead(unsigned char *buf) { buf[0] = 0x00; buf[1] = 0; buf[2] = 0x01; buf[3] = 0; buf[4] = 0; buf[5] = 1; buf[6] = 0; buf[7] = 0; buf[8] = buf[9] = buf[10] = buf[11] = 0; } void setQuery( char *name, unsigned char *buf, int len) { strcat (buf+12,name); int pos = len + 12; buf[pos] = 0; buf[pos+1] = 1; buf[pos+2] = 0; buf[pos+3] = 1; } int changeDN( char *DN, char *name) { int i = strlen (DN) - 1; int j = i + 1; int k; name[j+1] = 0; for (k = 0; i >= 0; i--,j--) { if (DN[i] == ‘.‘ ) { name[j] = k; k = 0; } else { name[j] = DN[i]; k++; } } name[0] = k; return ( strlen (DN) + 2); } void printName( int len, char *name) { int i; for (i = 0; i < len; i++) printf ( "%x." ,name[i]); printf ( "\n" ); } int sendDNSPacket(unsigned char *buf, int len, char *recvMsg) { int s; struct sockaddr_in sin ; memset (& sin ,0, sizeof ( sin )); sin .sin_addr.s_addr = inet_addr( "127.0.0.1" ); sin .sin_family = AF_INET; sin .sin_port = htons(SERVER_PORT); s = socket(PF_INET,SOCK_DGRAM,0); sendto(s,buf,len,0,( struct sockaddr *)& sin , sizeof ( sin )); return recv(s,recvMsg,MAX_SIZE,0); } void resolve(unsigned char *recvMsg, int len, int len_recvMsg) { int pos = len; int cnt = 12; while (pos < len_recvMsg) { unsigned char now_pos = recvMsg[pos+1]; unsigned char retype = recvMsg[pos+3]; unsigned char reclass = recvMsg[pos+5]; unsigned char offset = recvMsg[pos+11]; if (retype == 1) { if (now_pos == cnt && reclass == 1) { printf ( "%u.%u.%u.%u\n" ,recvMsg[pos+12],recvMsg[pos+13],recvMsg[pos+13],recvMsg[pos+14]); } } else if (retype == 5) { cnt = pos + 12 ; } pos = pos + 12 + offset; } } int main() { unsigned char buf[MAX_SIZE]; /* socket发送的数据 */ char DN[MAX_SIZE]; /* 将要解析的域名(www.xxx.xxx) */ char name[MAX_SIZE]; /* 转换为符合DNS报文格式的域名 */ char recvMsg[MAX_SIZE]; /* 接收的数据 */ int len; /* socket发送数据的长度 */ int s; /* socket handler */ printf ( "输入需要解析的域名:" ); scanf ( "%s" ,DN); len = changeDN(DN,name); //printName(len,name); /* 打印转换后的域名,检测是否转换正确 */ int j; //printf("len is %d\n",len); setHead(buf); setQuery(name,buf,len); len += 16; int len_recvMsg = sendDNSPacket(buf,len,recvMsg); printf ( "接收的报文长度为 %d 字节\n" ,len_recvMsg); printf ( "下面是接收报文的16进制表示:\n" ); int i; for (i = 0; i < len_recvMsg; i++) { printf ( "%x." ,(unsigned char )recvMsg[i]); } printf ( "\n" ); printf ( "%s的IP地址为:\n" ,DN); resolve(recvMsg,len,len_recvMsg); } |
总结:刚开始感觉无从下手
- 不知道如何与DNS服务器交换信息 就用socket向服务器发送报文
- 不知道如何发送请求报文 百度喽
- 不知道如何处理响应报文 自己查看报文格式
暂时就这么多吧!
感谢下面的博客:
http://blog.csdn.net/ericzhong83/article/details/8108103介绍DNS报文
http://wenku.baidu.com/link?url=RNPg5HHx3YkkupGsmr4sePZqplCLefkM4SI458prY4IgQz0qgNRJg8hnBAcPKz7Ry_q8khoTUCLiB3ZmvMpTBrM8LHiZ6tj_TXtlDRyU0QG介绍DNS报文
http://blogfeifei.iteye.com/blog/1213628更详细的介绍
http://blog.csdn.net/kevinshq/article/details/7199573windows系统下的实现,不过没试过,但是也给了启发