本来是一次计算机网络的实验,但是还没有完全写好,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<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+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向服务器发送报文
  • 不知道如何发送请求报文 百度喽
  • 不知道如何处理响应报文 自己查看报文格式

暂时就这么多吧!

感谢下面的博客:

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系统下的实现,不过没试过,但是也给了启发

Linux socket编程 DNS查询IP地址的更多相关文章

  1. Linux下的C Socket编程 -- 获取对方IP地址

    Linux下的C Socket编程(二) 获取域名对应的IP地址 经过上面的讨论,如果我们想要连接到远程的服务器,我们需要知道对方的IP地址,系统函数gethostbyname便能够实现这个目的.它能 ...

  2. Linux下编程获取本地IP地址的常见方法

    转载于:http://blog.csdn.net/k346k346/article/details/48231933 在进行linux网络编程时,经常用到本机IP地址.本文罗列一下常见方法,以备不时之 ...

  3. 【Linux 网络编程】网络IP地址结构体

    (1)IPv4套接口地址结构通常也称为"网际套接字地址结构",它以"sockaddr_in"命名,        定义在<netinet/in.h> ...

  4. 在windows系统和linux系统中查询IP地址命令的不同

    在linux和windows系统上查询IP地址的命令是不一样的.         在linux中的命令行模式下,输入ifconfig即可查询到IP.而在windows系统下要查询IP地址需要先打开do ...

  5. Linux Socket 编程简介

    在 TCP/IP 协议中,"IP地址 + TCP或UDP端口号" 可以唯一标识网络通讯中的一个进程,"IP地址+端口号" 就称为 socket.本文以一个简单的 ...

  6. Linux socket 编程中存在的五个隐患

    前言:         Socket API 是网络应用程序开发中实际应用的标准 API.尽管该 API 简单,但是   开发新手可能会经历一些常见的问题.本文识别一些最常见的隐患并向您显示如何避免它 ...

  7. c/c++ 网络编程 UDP 改变IP地址

    网络编程 UDP 改变IP地址 在程序里动态改变主机的IP地址 1,改变ipv4的地址 #include <stdio.h> #include <string.h> #incl ...

  8. Mac通过域名查询IP地址

    Mac通过域名查询IP地址 方法一:使用Mac自带的"网络实用工具" 步骤: 搜索"网络使用工具",并打开: 点击LookUp,输入互联网地址,点击Lookup ...

  9. Linux下获取本机IP地址的代码

    Linux下获取本机IP地址的代码,返回值即为互联网标准点分格式的字符串. #define ETH_NAME "eth0" //获得本机IP地址 char* GetLocalAdd ...

随机推荐

  1. SRM 391(1-250pt)

    DIV1 250pt 题意:给两个'a'-'z'的字符串,是否存在一个'a'-'z'的置换,使得能将一个字符串转化成另一个字符串. 解法:题意即是求,s1和s2对应位置出现的字符在原字符串中出现的次数 ...

  2. 终止imp/exp和expdp/impdp进程运行的方法

    一.停止EXP/IMP优化速度 可以直接KILL 进程,但先要KILL 父进程,然后KILL子进程,只KILL子进程,EXP/IMP还会在后台执行的 样例:ps -ef |grep imp 查询到pi ...

  3. Java 判断一段网络资源是否存在

    package cn.ycmedia.common.utils; import java.io.InputStream; import java.net.URL; import java.net.UR ...

  4. What is therelationship between @EJB and ejb-ref/ejb-local-ref?

    http://glassfish.java.net/javaee5/ejb/EJB_FAQ.html What is therelationship between @EJB and ejb-ref/ ...

  5. VMware于CentOS网络设置

    VMware于CentOS网络设置 底: 笔记本电脑有两块网卡: 1. 网卡连接公司内网,仅仅配置了内网ip和子网掩码. 2. 无线网卡.连接4g无线路由器.dhcp自己主动配置. 问题: 在VMwa ...

  6. 工作于内存和文件之间的页缓存, Page Cache, the Affair Between Memory and Files

    原文作者:Gustavo Duarte 原文地址:http://duartes.org/gustavo/blog/post/what-your-computer-does-while-you-wait ...

  7. rsyslog 报 WARNING: rsyslogd is running in compatibility mode.

      [root@localhost log]# uname -a Linux localhost.localdomain 2.6.32 #1 SMP Sun Sep 20 18:58:21 PDT 2 ...

  8. iOS应用内支付(内购)的个人开发过程及坑!

    本文会给大家详细介绍iOS内购,这是本人16年5月底的开发过程,希望对看完此篇文章的人有所帮助. 本文基于XcodeVersion 7.3 (7D175)版本,手机是iPhone 6,9.3系统. 部 ...

  9. xeam Build Definition Extension uninstall 卸载

    之前在VS上装了Build definition 的扩展,后来发现很不好用,想卸载掉,就增 工具下面找add-in manager, 结果找不到,external tools下面也找不到, googl ...

  10. java多态---内存关系

    在该列中,a.lookDoor()会报错,因为azhong没有lookDoor这个方法,同理,a.playGame()也会报错. 注意!!! 最后一句Dog dd=(Dog)a: 这句话非常错误! 在 ...