c语言实现域名解析

来源:c语言实现域名解析

对原文进行了一点小修改,原文可能是在C++环境编译的,比如“.cpp”后缀的文件编译的,“.cpp”文件默认以C++方式编译,所以原文如果复制保存为“.c”后缀的,编译是通不过的,很多错误。以下是修改过后的代码,DNS请修改为自己所在区域的DNS地址,经测试,直接使用路由IP也可。域名输入例如:“www.baidu.net”,不含引号。测试“www.csdn.net”得到的IP在浏览器打开不是csdn,不知道为什么,其他域名未测试。

c语言实现域名解析:

#include   <stdio.h>
#include   <Winsock2.h>
#include   <windows.h>

typedef enum {false = 0, true = !false}bool;
typedef struct _DNSHEAD{        //dns 头部
    USHORT ID;
    USHORT tag; // dns 标志(参数)
    USHORT numQ;        // 问题数
    USHORT numA;        // 答案数
    USHORT numA1;       // 权威答案数
    USHORT numA2;       // 附加答案数
}DnsHead;
typedef struct _DNSQUERY    //dns 查询结构
{
    //    char   name[64];
    //      //查询的域名,这是一个大小在0到63之间的字符串;
    USHORT type;
    //查询类型,大约有20个不同的类型
    USHORT classes;
    //查询类,通常是A类既查询IP地址。
    
}DnsQuery;

#pragma comment(lib,"ws2_32.lib")

// 初始化操作
bool initWSA();

//显示错误
void displayErrWSA(char *str);

//创建套接字
SOCKET CreateSocket(int type);

//UDP sendto
int MySendto(SOCKET sockTo, const char FAR * buf,int len,char *addr,USHORT port);

//TCP 连接
bool MyConnect(SOCKET s, char *addr,USHORT );

// UDP recvfrom
int MyRecvFrom(SOCKET s, char FAR * buf,int len,char *addr,USHORT port);

//设置DNS 头部
bool SetDNSHead(char *name,char *buf);

int main(int arg,char *are[])
{
    int Result=0;
    char buf[1024]={0};
    char addr[16] = "192.168.1.1";// dns 服务器地址
    char *name = 0; //要查询的域名
    SOCKET sockTo;
    int len;
    DnsHead *DnsH;
    char *getIP;
    //int i;
    
    if ( !initWSA() )//初始化
    {
        displayErrWSA("initWSA err!");
        return 1;
    }
    
    //创建套接字
    if ( (sockTo = CreateSocket(SOCK_DGRAM)) == 0)
    {
        displayErrWSA("CreatSocket err!");
        return 1;
    }
    while (1)
    {
        if (arg < 2)
        {
            char temp[1024]={0};
            printf("\n请输入要查询的域名:");
            scanf("%s",temp);
            if (temp[0] == 'q' ||temp[0] == 'Q')
            {
                break;
            }
            name =  temp;
        }
        else
        {
            arg = 1;
            name =  are[1];
        }
        
        //设置dns 头部
        SetDNSHead(name,buf);
        
        //发送出去的请求数据长度
        len = sizeof(DnsHead)+sizeof(DnsQuery)+strlen(name)+2;
        
        //      for (int  i =0;i<50;i+=2)
        //      {
        //          printf("x",(UCHAR)buf[i]);
        //          printf("x ",(UCHAR)buf[i+1]);
        //      }
        //发送DNS 请求
        if ( ( Result =MySendto(sockTo,buf,len,addr,53) ) <= 0)
        {
            displayErrWSA("sendto err!");
            continue;
        }
        
        //接收应答
        if ( (Result =MyRecvFrom(sockTo,buf,1024,addr,53) ) <=  0)
        {
            displayErrWSA("recvfrom err!");
            continue;
        }
        
        //简单的取得返回的 IP 地址( 收到的最后4字节 )
        DnsH = (DnsHead *)buf;
        if (DnsH->numA == 0)
        {
            printf("无法解析 %s 的IP 地址。\n",name);
            continue;
        }
        getIP =(char *)buf +Result - 4;
        printf("%s 的IP地址为: ",name);
        for (Result= 0 ;Result<4 ;Result++)
        {
            printf("%u.",(UCHAR )getIP[Result]);
        }
        printf("\n");
    }
    return 0;
}

// 初始化操作
bool initWSA()
{
    WORD   wVersionRequested;
    WSADATA   wsaData;
    int Result;
    wVersionRequested = MAKEWORD( 2, 2 );
    Result = WSAStartup( wVersionRequested, &wsaData );
    if(Result   !=   0 )
    {
        return false;
    }
    
    if( LOBYTE( wsaData.wVersion) != 2 ||
        HIBYTE(wsaData.wVersion)!= 2 )
    {
        WSACleanup();
        return false;
    }
    return true;
}

SOCKET CreateSocket(int type)
{
    SOCKET  sock=socket(AF_INET,type,0);
    if (sock == INVALID_SOCKET )
    {
        return 0;
    }
    return sock;
}

int MySendto(SOCKET sockTo, const char FAR * buf,int len,char *addr,USHORT port)
{
    //设置发送数据到的 套接字及地址结构
    SOCKADDR_IN   addrTo;
    addrTo.sin_addr.S_un.S_addr=inet_addr(addr);
    addrTo.sin_family=AF_INET;
    addrTo.sin_port=htons(port);
    
    return sendto(  sockTo, buf, len, 0,
        (struct sockaddr *)&addrTo, sizeof(struct sockaddr)  );
}

bool MyConnect(SOCKET sockTo, char *addr,USHORT port)
{
    
    int   Result;
    
    //设置连接到的 套接字及地址结构
    SOCKADDR_IN   addrTo;
    addrTo.sin_addr.S_un.S_addr=(inet_addr(addr));
    addrTo.sin_family=AF_INET;
    addrTo.sin_port=htons(port);
    
    //连接
    Result = connect(sockTo,(struct sockaddr *)&addrTo,sizeof(SOCKADDR_IN));
    if(SOCKET_ERROR == Result)
    {
        return false;
    }
    return true;
}

int MyRecvFrom(SOCKET s, char FAR * buf,int len,char *addr,USHORT port)
{
    
    //设置发送数据到的 套接字及地址结构
    int addrlen;
    SOCKADDR_IN   addrFrom;
    addrFrom.sin_addr.S_un.S_addr=inet_addr(addr);
    addrFrom.sin_family=AF_INET;
    addrFrom.sin_port=htons(port);
    addrlen = sizeof(SOCKADDR_IN);
    return recvfrom( s, buf, len, 0, (SOCKADDR *)&addrFrom, &addrlen);
}

int  ChName(char *fname,char *tname);//域名转化
bool SetDNSHead(char *name,char *buf)
{
    DnsHead *DnsH;
    DnsQuery *DnsQ;
    int NameLen;
    memset(buf,0,sizeof(DnsHead));
    
    //设置头部
    DnsH = (DnsHead *)buf;
    DnsH->ID = (USHORT)1;
    DnsH->tag = htons(0x0100);
    DnsH->numQ = htons(1);
    DnsH->numA = 0;
    
    DnsQ =(DnsQuery *) ( buf+ sizeof(DnsHead) );
    NameLen = ChName(name,(char *)DnsQ);
    
    //设置查询信息
    DnsQ = (DnsQuery *)( (char *)DnsQ + NameLen );
    DnsQ->classes = htons(1);
    DnsQ->type = htons(1);
    return true;
}

//显示错误信息
void displayErrWSA(char *str)
{
    printf("\n%s,err = %d\n",str,WSAGetLastError());
    getchar();
}

//域名转化
int  ChName(char *fname,char *tname)
{
    
    int j =0;
    int i =strlen(fname)-1;
    int k = i+1;
    tname[i+2] = 0;
    for (; i>=0;i--,k--)
    {
        if (fname[i] == '.')
        {
            tname[k] = j;
            j=0;
        }
        else
        {
            tname[k] = fname[i];
            j++;
        }
    }
    tname[k] = j;
    return strlen(tname)+1;
}


  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
C 语言实现 DNS 域名解析的过程如下: 1. 定义 DNS 查询报文 DNS 查询报文包含以下字段: - 标识符(ID):用于标识该查询和响应,通常是一个随机数。 - 标志(Flags):用于指定该查询的类型和设置。 - 问题数(QDCOUNT):用于指定查询报文中包含的问题数。 - 回答数(ANCOUNT):用于指定响应报文中包含的回答数。 - 授权数(NSCOUNT):用于指定响应报文中包含的授权数。 - 附加数(ARCOUNT):用于指定响应报文中包含的附加数。 - 问题(Question):用于指定查询的域名和类型。 - 回答(Answer):用于返回查询结果。 - 授权(Authority):用于指定授权域名服务器。 - 附加(Additional):用于指定其他信息,例如 DNS 服务器的 IP 地址。 2. 构造 DNS 查询报文 构造 DNS 查询报文的过程如下: - 定义一个 DNS 查询报文结构体,包含上述字段。 - 将域名转换成 DNS 查询格式,即将每个标签的长度和内容以字节方式存储,并在末尾添加一个 0 字节。 - 将查询报文头部填充好,包括标识符、标志、问题数等字段。 - 将查询报文中的问题字段填充好,包括域名和查询类型(通常为 A 记录)。 - 将查询报文序列化成字节数组。 3. 发送 DNS 查询报文 使用套接字 API 发送 DNS 查询报文到 DNS 服务器,例如: ```c #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int sockfd; struct sockaddr_in servaddr; sockfd = socket(AF_INET, SOCK_DGRAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(53); // DNS 服务器端口号 inet_pton(AF_INET, "8.8.8.8", &servaddr.sin_addr); // DNS 服务器 IP 地址 sendto(sockfd, query, query_len, 0, (struct sockaddr *) &servaddr, sizeof(servaddr)); ``` 其中,query 表示序列化后的 DNS 查询报文,query_len 表示报文长度。 4. 接收 DNS 响应报文 使用套接字 API 接收 DNS 响应报文,例如: ```c char response[1024]; int response_len; struct sockaddr_in servaddr; socklen_t servlen = sizeof(servaddr); response_len = recvfrom(sockfd, response, sizeof(response), 0, (struct sockaddr *) &servaddr, &servlen); ``` 其中,response 表示接收到的 DNS 响应报文,response_len 表示报文长度,servaddr 表示 DNS 服务器的地址。 5. 解析 DNS 响应报文 将接收到的 DNS 响应报文解析成可读的格式,例如: ```c struct dns_header *header = (struct dns_header *) response; char *question = response + sizeof(struct dns_header); char *answer = question + strlen(question) + 1; ``` 其中,dns_header 表示 DNS 报文头结构体,question 表示查询问题部分,answer 表示查询回答部分。 6. 提取 DNS 查询结果 从 DNS 响应报文中提取查询结果,例如: ```c struct dns_answer *ans = (struct dns_answer *) answer; char *ip = inet_ntoa(ans->ip); ``` 其中,dns_answer 表示 DNS 回答结构体,ip 表示查询到的 IP 地址。 注意,以上代码仅为示例,实际实现中还需要考虑更多细节和异常情况。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值