通过本地DNS域名服务器寻找域名对应IP地址C/C++

来源:互联网 发布:中锐网络招聘 编辑:程序博客网 时间:2024/05/18 00:22

输入域名即可获取IP地址

/* *模拟DNS协议的工作 */#include<stdlib.h>#include<stdio.h>#include<Winsock2.h>#include<Iphlpapi.h>#include<windows.h>#include<string.h>#pragma comment(lib , "ws2_32.lib")#pragma comment(lib , "IPHLPAPI.LIB")#pragma pack(2) //设置字节对齐属性#define PORT 53 //定义端口号#define TIMEOUT 3000 //超时时间typedef struct //DNS报文首部{    unsigned short id ;    unsigned short flags ;    unsigned short questNum ;    unsigned short answerNum ;    unsigned short authorNum ;    unsigned short additionNum ;} DNSHDR , *pDNSHDR ;typedef struct //DNS报文查询记录{    unsigned short type ;    unsigned short queryclass ;} QUERYHDR , *pQUERYHDR ;typedef struct //DNS报文应答记录{    unsigned short type ;    unsigned short classes ;    unsigned long ttl ;    unsigned short length ;} RESPONSE , *pRESPONSE ;/* *生成一个DNS请求报文 */int genDNSPacket(pDNSHDR pDnsHdr , pQUERYHDR pQueryHdr , char *hostname , char *DNSsendBuff) ;/* *对接收到的DNS应答报文进行解析得到需要的IP地址信息 */void decodeDNSPacket(char *DNSrecvBuff) ;/* *获取本机配置的DNS服务器 */void getDnsServer(char *dnsServer) ;/* *测试主函数 */int main(){    sockaddr_in addr ; //绑定地址    SOCKET ListenSocket ; //发送与接收用的SOCKET    int len = 0 ;    //初始化WinSock    WORD wVersionRequested = MAKEWORD(2,2);    WSADATA wsaData;    if(WSAStartup(wVersionRequested,&wsaData) != 0)    {        printf("初始化WinSock失败!\n") ;        return 0 ;    }    ListenSocket = socket(PF_INET , SOCK_DGRAM , 0) ;    if(ListenSocket == INVALID_SOCKET)    {        printf("Error : socket create failed ! \n") ;        fflush(0) ;        return 0 ;    }    addr.sin_family = PF_INET ;    addr.sin_addr.s_addr = htonl(INADDR_ANY) ; //任何地址    addr.sin_port = htons(PORT) ;    //进行监听端口的绑定    if(bind(ListenSocket , (struct sockaddr*)&addr , sizeof(addr)) != 0)    {        printf("Error : bind failed !\n") ;        fflush(0) ;        closesocket(ListenSocket) ;        return 0 ;    }    char dnsip[20] ;    getDnsServer(dnsip) ;    //进行UDP数据报的发送    int sent ;    hostent *hostdata ;    if(atoi(dnsip)) //是否为IP地址的标准形式    {        u_long ip = inet_addr(dnsip) ;        hostdata = gethostbyaddr((char*)&ip , sizeof(ip) , PF_INET) ;    }    else    {       printf("The DNS IP is not correct ! \n") ;       return 0 ;    }    if(!hostdata)    {        printf("Get the name error ! \n") ;        fflush(0) ;        return 0 ;    }    sockaddr_in dest ; //填写目的地址信息    dest.sin_family = PF_INET ;    //将hostent结构体里面的h_addr_list转化为in_addr类型的地址信息    dest.sin_addr = *(in_addr*)(hostdata->h_addr_list[0]) ;    dest.sin_port = htons(PORT) ;    //获取DNS报文    char hostname[30] , buffer[100] ;    DNSHDR dnsHdr ;    QUERYHDR queryHdr ;    printf("Please input domain name (no more than 30) : ") ;    scanf("%s" , hostname) ;    len = genDNSPacket(&dnsHdr , &queryHdr , hostname , buffer) ;    sent = sendto(ListenSocket , buffer , len , 0 , (sockaddr*)&dest , sizeof(sockaddr_in)) ;    if(sent != len)    {        printf("Error : send error !\n") ;        fflush(0) ;        return 0 ;    }    //进行数据的接收    char buf[400] ;    sockaddr_in dnsServer ;    int addr_len = sizeof(dnsServer) ;    int result ;    while(1) //接收到数据时退出    {        result = recvfrom(ListenSocket , buf , sizeof(buf) - 1 , 0 , (sockaddr*)&dnsServer , &addr_len) ;        if(result > 0)        {            buf[result] = 0 ;            decodeDNSPacket(buf) ;            break ;        }    }    return 0 ;}/* *生成一个DNS请求报文 *生成的DNS请求报文保存在DNSsendBuff中 *hostname为需要查询的域名字符串 *返回值为生成请求报文的长度,单位为字节 */int genDNSPacket(pDNSHDR pDnsHdr , pQUERYHDR pQueryHdr , char *hostname , char *DNSsendBuff){    if( ! strcmp(hostname , "exit"))    {        return -1 ; //输入exit时结束    }    else //正常的DNS查询请求    {        int iSendByte = 0 ;        ZeroMemory(DNSsendBuff , sizeof(DNSsendBuff)) ;        pDnsHdr->id = htons(0x0000) ; //"标识"字段设置为0        pDnsHdr->flags = htons(0x0100) ; //"标志"字段设置为0x0100, 即RD位为1期望递归查询        pDnsHdr->questNum = htons(0x0001) ; //1个查询记录        pDnsHdr->answerNum = htons(0x0000) ; //没有回答记录和其它的记录        pDnsHdr->authorNum = htons(0x0000) ;        pDnsHdr->additionNum = htons(0x0000) ;        //将生成的DNS查询报文首部复制到DNSsendBuff中        memcpy(DNSsendBuff , pDnsHdr , sizeof(DNSHDR)) ;        iSendByte += sizeof(DNSHDR) ; //记录当前的数据量        //对域名字符串进行解析并且进行形式的变换        char *pTrace = hostname ;        char *pHostname = hostname ;        int iStrLen = strlen(hostname) ;        unsigned char iCharNum = 0 ;         //将指针指向的字符串向后移动一个字节        while( *pTrace != '\0')        {            pTrace++ ;        }        while(pTrace != hostname)        {            *(pTrace + 1) = *pTrace ;            pTrace-- ;        }        *(pTrace + 1) = *pTrace ; //将第一个字符移动到第二个字符的位置        pTrace++ ; //第一个字符无实际意义,移动到第二个字符位置        while( *pTrace != '\0')        {            if( *pTrace == '.')            {                *pHostname = iCharNum ;                iCharNum = 0 ;                pHostname = pTrace ;            }            else            {                iCharNum++ ;            }            pTrace++ ;        }       //将最后一个字符'.'之后的字符树写入       *pHostname = iCharNum ;       memcpy(DNSsendBuff + sizeof(DNSHDR) , hostname , iStrLen + 2) ;       iSendByte += (iStrLen + 2) ; //解析完之后会多出两个字符       //在域名字段之后填入“查询类型”和“查询类”       pQueryHdr->type = htons(0x0001) ;       pQueryHdr->queryclass = htons(0x0001) ;       memcpy(DNSsendBuff + sizeof(DNSHDR) + (iStrLen + 2) , pQueryHdr , sizeof(QUERYHDR)) ;       iSendByte += sizeof(QUERYHDR) ; // 累加得到的字节数       return iSendByte ; //返回最终得到的字节数    }}/* *解析收到的DNS报文DNSrecvBuff *应答记录字段中类型为5时即为域名的一个别名,跳过此段应答记录,类型为1时即显示出来 */void decodeDNSPacket(char *DNSrecvBuff){    pDNSHDR pDnsHdr = (pDNSHDR)DNSrecvBuff ; //指针pDnsHdr指向接收到的DNS应答报文首部    //保存所有附加信息    int iQueryNum , iRespNum , iAuthRespNum , iAdditionNum ;    iQueryNum = ntohs(pDnsHdr->questNum) ;    iRespNum = ntohs(pDnsHdr->answerNum) ;    iAuthRespNum = ntohs(pDnsHdr->authorNum) ;    iAdditionNum = ntohs(pDnsHdr->additionNum) ;    //将DNS应答报文的“标志”字段右移15位即取最高位 , 0 为DNS查询报文,1为应答报文    if(pDnsHdr->flags >> 15)    {        //flags低位值为3,标识服务器没有与请求域名相应的记录        if((pDnsHdr->flags & 0x0007) == 3)        {            printf("No corresponding domain name entry . \n") ;            return ;        }        if((pDnsHdr->flags>>10) & 0x0001) //查看标志位AA,看是否时权威应答        {            printf("Authoritative anwser : \n") ;        }        else        {            printf("None-authoritative anwser : \n") ;        }        char *pTraceResponse ;        //指针移向应答报文中的第一个查询记录,因为一般情况下应答报文均会首先附带一个对应的查询记录        pTraceResponse = DNSrecvBuff + sizeof(DNSHDR) ;        //将指针移动到查询记录的域名字段之后        while(*pTraceResponse)         {             pTraceResponse++ ;         }         pTraceResponse++ ;         //跳过查询类型和查询类两个字段,指针指向第一个应答记录         pTraceResponse += sizeof(long) ;         in_addr address ;         pRESPONSE pResponse ;         printf("Addresses : ") ;         for(int i = 0 ; i < iRespNum ; i++)         {             //指针跳过应答记录的“域名”字段,此域名字段一般为一个域名指针,以0xC0开始             pTraceResponse += sizeof(short) ;             pResponse = (pRESPONSE)pTraceResponse ;             if(ntohs(pResponse->type) == 1) //这条应答记录返回的是与之前查询所对应的IP地址             {                 pTraceResponse += sizeof(RESPONSE) ;                 unsigned long ulIP = *(unsigned long *)pTraceResponse ;                 address.s_addr = ulIP ;                 if(i == iRespNum - 1) //最后一条记录显示句号,否则显示分号                 {                     printf("%s . " , inet_ntoa(address)) ;                 }                 else                 {                     printf("%s ; " , inet_ntoa(address)) ;                 }                 //指针移过应答记录的IP地址字段,指向下一条应答记录                 pTraceResponse += sizeof(long) ;             }             else if(ntohs(pResponse->type) == 5) //这条应答记录为所查询主机的一个别名,这里本程序直接跳过这条记录             {                 pTraceResponse += sizeof(RESPONSE) ;                 pTraceResponse += ntohs(pResponse->length) ;             }         }         printf("\n") ;    }    else //标志字段最高位不为1,表示不是一个DNS应答报文,不做任何处理    {        printf("Invalid DNS resolution ! \n\n") ;    }}/* *获得本机设置的DNS服务器信息并保存到dnsServer里面 */void getDnsServer(char *dnsServer){     //获得需要的缓冲区大小    DWORD nLength = 0 ;    if(GetNetworkParams(NULL , &nLength) != ERROR_BUFFER_OVERFLOW)    {        return ;    }    FIXED_INFO *pFixedInfo = (FIXED_INFO*)new BYTE[nLength] ;     //获得本地的网络参数     if(GetNetworkParams(pFixedInfo , &nLength) != ERROR_SUCCESS)     {         delete [] pFixedInfo ;         return ;     }     IP_ADDR_STRING *pCurrentDnsServer = &pFixedInfo->DnsServerList ;     if(pCurrentDnsServer != NULL)     {         //pCurrentDnsServer->IpAddress.String 即为所需要字符串形式的DNS服务器         char *tmp = pCurrentDnsServer->IpAddress.String ;         strcpy(dnsServer , tmp) ;     }}
0 0