1. 程式人生 > >一個簡單的c語言版的DNS解析客戶端,能提供解析耗時

一個簡單的c語言版的DNS解析客戶端,能提供解析耗時

成功的做了一次搬運工,該部分程式碼基本來自於http://blog.csdn.net/jxfgh,不過,原作者沒有新增測試解析耗時的功能,其實我也不知道到時是不是原作者微笑

作為第一次做與網路有關的東西,還是i挺有紀念意義的,所以發博文留念。有新增快取功能的慾望,可惜不知從何下手,故沒有改動,也希望會的童鞋能幫一下忙,搞定快取的問題。

/********實現簡單的DNS解析耗時測試**********
***************************************/
#include <stdio.h>
#include <Winsock2.h>
#include <windows.h>
#include <time.h>


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);//建立套接字函式申明
int MySendto(SOCKET sockTo, const char FAR * buf,int len,char *addr,USHORT port);//UDP sendto函式申明


int MyRecvFrom(SOCKET s, char FAR * buf,int len);//UDP recvfrom函式申明


bool SetDNSHead(char *name,char *buf);//設定DNS 頭部函式申明
int main(int arg,char *are[])//主函式
{
 int Result=0;
 char buf[1024]={0};
 char addr[16] = "42.49.134.2";// dns 伺服器地址
 char *name = 0; //要查詢的域名
 
 if ( !initWSA() )//初始化
 {
  displayErrWSA("initWSA err!");
  return 1;
 }
 SOCKET sockTo ;//建立套接字
 if ( (sockTo = CreateSocket(SOCK_DGRAM)) == 0)
 {
  displayErrWSA("CreatSocket err!");
  return 1;
  }
 while (1)//死迴圈,不停的查詢
 {
  if (arg < 2)
  {
   char temp[1024]={0};//設定要解析域名的字元陣列
   printf("請輸入要查詢的域名:");
   scanf("%s",temp);//輸入待解析域名
   if (temp[0] == 'q' ||temp[0] == 'Q')//判斷是否輸入的是”q“,如果是,退出
   {
    break;
   }
   name =  temp;
  }
  else
  {
   arg = 1;
   name =  are[1];
  }
  clock_t begin=0, end=0;//建立時間變數begin、end,分別用來儲存解析開始可解析結束所對應的時間,單位為ms
  double cost;//設定統計變數,用來儲存整個解析過程的耗時,由end減去begin得出。
  //設定dns 頭部
  SetDNSHead(name,buf);
  //傳送出去的請求資料長度
  int len = sizeof(DnsHead)+sizeof(DnsQuery)+strlen(name)+2;
  
  if ( ( Result =MySendto(sockTo,buf,len,addr,53) ) <= 0)//傳送DNS 請求
  {
   displayErrWSA("sendto err!");
   continue;
  }
  begin = clock();//開始計時
  if ( (Result =MyRecvFrom(sockTo,buf,1024) ) <=  0)//接收應答
  {
   displayErrWSA("recvfrom err!");
   continue;
  }
  end = clock();//返回了結果,停止計時
  cost = (double)(end - begin); //計算解析時間差,單位:ms
  //簡單的取得返回的 IP 地址( 收到的最後4位元組 )
  DnsHead *DnsH = (DnsHead *)buf;
  if (DnsH->numA == 0)
  {
   printf("無法解析 %s 的IP 地址。\n",name);
   continue;
  }
  
  char *getIP =(char *)buf +Result - 4;
  printf("%s 的IP地址為: ",name);
  int Result= 0 ;//輸出DNS解析結果,格式為A.B.C.D
  printf("%u",(UCHAR )getIP[Result]);
  for (Result= 1 ;Result<4 ;Result++)
  {
   printf(".%u",(UCHAR )getIP[Result]);
  }
   printf("  解析耗時:%.0fms",cost);
  printf("\n");
 }
 closesocket(sockTo);
 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;
}


/*****************         UDP sendto*/
int MySendto(SOCKET sockTo, const char FAR * buf,int len,char *addr,USHORT port)
{
 if (addr[0]<'0'||addr[0]>'9') //addr 中不是IP 地址,域名解析
 { 
  struct   sockaddr_in   addr_in;
  HOSTENT *host = NULL;
  host = gethostbyname(addr);//域名解析函式
  if ( host->h_addr_list[0]!=NULL )
  {
   memcpy  ( &addr_in.sin_addr.s_addr,
      host->h_addr_list[0], host->h_length);  
   addr = inet_ntoa(addr_in.sin_addr); 
  }
 }
 //設定傳送資料到的 套接字及地址結構
 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,
     (sockaddr*)&addrTo, sizeof(sockaddr)  );


}
//*********************************************/
/*********************** UDP recvform*/
int MyRecvFrom(SOCKET s, char FAR * buf,int len)
{
 //設定傳送資料到的 套接字及地址結構
 SOCKADDR_IN   addrFrom;
 int addrlen = sizeof(SOCKADDR_IN);
 return recvfrom( s, buf, len, 0, (SOCKADDR *)&addrFrom, &addrlen);
}
/********************** 設定DNS 頭部 */
int  ChName(char *fname,char *tname);//域名轉化
bool SetDNSHead(char *name,char *buf)
{
 memset(buf,0,sizeof(DnsHead));
 //設定頭部
 DnsHead *DnsH = (DnsHead *)buf;
 DnsH->ID = (USHORT)1;
 DnsH->tag = htons(0x0100);
 DnsH->numQ = htons(1);
 DnsH->numA = 0;
 DnsQuery *DnsQ =(DnsQuery *) ( buf+ sizeof(DnsHead) );
 int 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;
 tname[i+2] = 0;
 int k = i+1;
 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;
}