linux socket 构造dns,Linux socket编程 DNS查询IP地址

本来是一次计算机网络的实验,但是还没有完全写好,DNS的响应请求报文的冗余信息太多了,不只有IP地址。所以这次的实验主要就是解析DNS报文。同时也需要正确的填充请求报文。如果代码有什么bug,欢迎指正啊。代码排版有点乱。。。

本文有以下内容

DNS报文的填充和解析

利用socket API传输信息

一、填充DNS请求报文

随便百度一下,就可以知道DNS报文的格式。所以这里只介绍如何填充DNS报文。

首先是填充报文首部:

/* 填充首部的格式大致相同,下面的填充值是参考他人抓包分析的结果 */

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)的代码

/* 填充域名 */

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报文

下面是代码:

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(这个域名有点复杂)。

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+14],recvMsg[pos+15]);

}

}

else if(retype == 5) {

cnt = pos + 12 ;

}

pos = pos + 12 + offset;

}

}

四、完整的代码和总结

下面是完整的代码:

/*************************************************************************

> 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

#include

#include

#include

#include

#include

#include

#include

#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+14],recvMsg[pos+15]);

}

}

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向服务器发送报文

不知道如何发送请求报文 百度喽

不知道如何处理响应报文 自己查看报文格式

暂时就这么多吧!

感谢下面的博客:

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值