ping的實現(原始通訊端系列三)

來源:互聯網
上載者:User

標籤:des   style   code   color   get   使用   

使用Raw Socket實現Ping

  僅僅採用ICMP.DLL並不能完全實現ICMP靈活多變的各類報文,只有使用Raw Socket才是ICMP的終極解決之道。

  使用Raw Socket發送ICMP報文前,我們要完全依靠自己的程式碼群組裝報文:

//功能:初始化ICMP的前序, 給data部分填充資料, 計算校正和
void init_ping_packet(ICMPHeader *icmp_hdr, int packet_size, int seq_no)
{
 //設定ICMP前序欄位
 icmp_hdr->type = ICMP_ECHO_REQUEST;
 icmp_hdr->code = 0;
 icmp_hdr->checksum = 0;
 icmp_hdr->id = (unsigned short)GetCurrentProcessId();
 icmp_hdr->seq = seq_no;
 icmp_hdr->timestamp = GetTickCount();

 // 填充data域
 const unsigned long int deadmeat = 0xDEADBEEF;
 char *datapart = (char*)icmp_hdr + sizeof(ICMPHeader);
 int bytes_left = packet_size - sizeof(ICMPHeader);
 while (bytes_left > 0)
 {
  memcpy(datapart, &deadmeat, min(int(sizeof(deadmeat)), bytes_left));
  bytes_left -= sizeof(deadmeat);
  datapart += sizeof(deadmeat);
 }

 // 計算校正和
 icmp_hdr->checksum = ip_checksum((unsigned short*)icmp_hdr, packet_size);
}


  計算校正和(Checksum)的函數為:

//功能:計算ICMP包的校正和
unsigned short ip_checksum(unsigned short *buffer, int size)
{
 unsigned long cksum = 0;

 // 將所有的16數相加
 while (size > 1)
 {
  cksum += *buffer++;
  size -= sizeof(unsigned short);
 }
 if (size) //加上最後一個BYTE
 {
  cksum += *(unsigned char*)buffer;
 }

 //和的前16位和後16位相加
 cksum = (cksum >> 16) + (cksum &0xffff);
 cksum += (cksum >> 16);

 return (unsigned short)(~cksum);
}


  在真正發送Ping報文前,需要先初始化Raw Socket:

// 功能:初始化RAW Socket, 設定ttl, 初始化目標地址
// 傳回值:<0 失敗
int setup_for_ping(char *host, int ttl, SOCKET &sd, sockaddr_in &dest)
{
 // 建立原始通訊端
 sd = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, 0, 0, 0);
 if (sd == INVALID_SOCKET)
 {
  cerr << "Failed to create raw socket: " << WSAGetLastError() << endl;
  return - 1;
 }

 if (setsockopt(sd, IPPROTO_IP, IP_TTL, (const char*) &ttl, sizeof(ttl)) ==SOCKET_ERROR)
 {
  cerr << "TTL setsockopt failed: " << WSAGetLastError() << endl;
  return - 1;
 }

 // 初始化目標主機資訊塊
 memset(&dest, 0, sizeof(dest));

 // 將第1個參數轉換為目標IP地址
 unsigned int addr = inet_addr(host);
 if (addr != INADDR_NONE)
 {
  // 為IP地址
  dest.sin_addr.s_addr = addr;
  dest.sin_family = AF_INET;
 }
 else
 {
  // 非IP地址,進行主機名稱和IP地址的轉換
  hostent *hp = gethostbyname(host);
  if (hp != 0)
  {
   // 尋找主機名稱對應的IP地址
   memcpy(&(dest.sin_addr), hp->h_addr, hp->h_length);
   dest.sin_family = hp->h_addrtype;
  }
  else
  {
   // 不能識別的主機名稱
   cerr << "Failed to resolve " << host << endl;
   return - 1;
  }
 }
 return 0;
}


  下面可以利用Raw Socket發送產生的ICMP報文:

//功能:發送產生的ICMP包
//傳回值:<0 發送失敗
int send_ping(SOCKET sd, const sockaddr_in &dest, ICMPHeader *send_buf, int packet_size)
{
 // 發送send_buf緩衝區中的報文
 cout << "Sending " << packet_size << " bytes to " << inet_ntoa(dest.sin_addr)
 << "..." << flush;
 int bwrote = sendto(sd, (char*)send_buf, packet_size, 0, (sockaddr*) &dest,sizeof(dest));
 if (bwrote == SOCKET_ERROR)
 {
  cerr << "send failed: " << WSAGetLastError() << endl;
  return - 1;
 }
 else if (bwrote < packet_size)
 {
  cout << "sent " << bwrote << " bytes..." << flush;
 }
 return 0;
}


  發送Ping報文後,我們需要接收Ping回複ICMP報文:

//功能:接收Ping回複
//傳回值: <0 接收失敗
int recv_ping(SOCKET sd, sockaddr_in &source, IPHeader *recv_buf, int packet_size)
{
 // 等待Ping回複
 int fromlen = sizeof(source);
 int bread = recvfrom(sd, (char*)recv_buf, packet_size + sizeof(IPHeader), 0,(sockaddr*) &source, &fromlen);
 if (bread == SOCKET_ERROR)
 {
  cerr << "read failed: ";
  if (WSAGetLastError() == WSAEMSGSIZE)
  {
   cerr << "buffer too small" << endl;
  }
  else
  {
   cerr << "error #" << WSAGetLastError() << endl;
  }
  return - 1;
 }
 return 0;
}


  並使用如下函數對接收到的報文進行解析:

// 功能:解析接收到的ICMP報文
// 傳回值: -2忽略, -1失敗, 0 成功
int decode_reply(IPHeader *reply, int bytes, sockaddr_in *from)
{
 // 位移到ICMP前序
 unsigned short header_len = reply->h_len *4;
 ICMPHeader *icmphdr = (ICMPHeader*)((char*)reply + header_len);

 // 報文太短
 if (bytes < header_len + ICMP_MIN)
 {
  cerr << "too few bytes from " << inet_ntoa(from->sin_addr) << endl;
  return - 1;
 }
 // 解析回複報文類型
 else if (icmphdr->type != ICMP_ECHO_REPLY)
 {
  //非正常回複
  if (icmphdr->type != ICMP_TTL_EXPIRE)
  {
   //ttl減為零
   if (icmphdr->type == ICMP_DEST_UNREACH)
   {
    //主機不可達
    cerr << "Destination unreachable" << endl;
   }
   else
   {
    //非法的ICMP包類型
    cerr << "Unknown ICMP packet type " << int(icmphdr->type) <<" received" << endl;
   }
   return - 1;
  }
 }
 else if (icmphdr->id != (unsigned short)GetCurrentProcessId())
 {
  //不是本進程發的包, 可能是同機的其它ping進程發的
  return - 2;
 }

 // 指出往返時間TTL
 int nHops = int(256-reply->ttl);
 if (nHops == 192)
 {
  // TTL came back 64, so ping was probably to a host on the
  // LAN -- call it a single hop.
  nHops = 1;
 }
 else if (nHops == 128)
 {
  // Probably localhost
  nHops = 0;
 }

 // 輸出資訊
 cout << endl << bytes << " bytes from " << inet_ntoa(from->sin_addr) <<", icmp_seq " << icmphdr->seq << ", ";
 if (icmphdr->type == ICMP_TTL_EXPIRE)
 {
  cout << "TTL expired." << endl;
 }
 else
 {
  cout << nHops << " hop" << (nHops == 1 ? "" : "s");
  cout << ", time: " << (GetTickCount() - icmphdr->timestamp) << " ms." <<endl;
 }
 return 0;
}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.