Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

298 lines
7.3 KiB

  1. // ping.cpp
  2. //
  3. #include "stdpch.h"
  4. #pragma hdrstop
  5. #include "host.h"
  6. #include "output.h" // COutput
  7. #include "diag.h"
  8. #include "async.hh"
  9. #include "ping.h"
  10. #include "svcguid.h"
  11. #include "wire.h" // net structs
  12. #include <conio.h>
  13. void fill_icmp_data (char *, int);
  14. USHORT checksum (USHORT *, int);
  15. DWORD decode_resp (char *,int ,struct sockaddr_in *, COutput& out);
  16. BOOL EXPORT CheckPing(TEST_INFO * lpNL)
  17. {
  18. g_Q.push_front (new CPing(lpNL));
  19. return TRUE;
  20. }
  21. void CPing::Go()
  22. {
  23. SOCKET sockRaw = (SOCKET)NULL;
  24. struct sockaddr_in dest,from;
  25. int bread,datasize;
  26. int fromlen = sizeof(from);
  27. int timeout;
  28. int nCount = 0;
  29. char *icmp_data;
  30. char *recvbuf;
  31. unsigned int addr=0;
  32. USHORT seq_no = 0;
  33. DWORD dw;
  34. m_pInfo->dTimeDelta = 0;
  35. m_pInfo->dwErr = 0;
  36. if (m_pInfo->host.m_strDescription.size() > 0)
  37. {
  38. m_pInfo->output.good(TEXT("pinging (%s) %s<br>"),
  39. m_pInfo->host.m_strDescription.c_str(),
  40. m_pInfo->host.GetHost());
  41. }
  42. else
  43. {
  44. m_pInfo->output.good(TEXT("pinging (%s)<br>"), m_pInfo->host.GetHost());
  45. }
  46. //
  47. // WSA_FLAG_OVERLAPPED flag is required for SO_RCVTIMEO, SO_SNDTIMEO
  48. // option. If NULL is used as last param for WSASocket, all I/O on the socket
  49. // is synchronous, the internal user mode wait code never gets a chance to
  50. // execute, and therefore kernel-mode I/O blocks forever. A socket created
  51. // via the socket API has the overlapped I/O attribute set internally. But
  52. // here we need to use WSASocket to specify a RAW socket.
  53. //
  54. // If you want to use timeout with a synchronous non-overlapped socket created
  55. // by WSASocket with last param set to NULL, you can set the timeout using the
  56. // select API, or use WSAEventSelect and set the timeout in the
  57. // WSAWaitForMultipleEvents API.
  58. //
  59. sockRaw = WSASocket (
  60. AF_INET,
  61. SOCK_RAW,
  62. IPPROTO_ICMP,
  63. NULL,
  64. 0,
  65. WSA_FLAG_OVERLAPPED);
  66. if (sockRaw == INVALID_SOCKET)
  67. {
  68. m_pInfo->output.err(TEXT("WSASocket() failed: %d<br>"),m_pInfo->dwErr = WSAGetLastError());
  69. goto Exit;
  70. }
  71. __try
  72. {
  73. timeout = 1000;
  74. bread = setsockopt(
  75. sockRaw,
  76. SOL_SOCKET,
  77. SO_RCVTIMEO,
  78. (char*)&timeout,
  79. sizeof(timeout));
  80. if(bread == SOCKET_ERROR)
  81. {
  82. m_pInfo->output.err(TEXT("failed to set recv timeout: %d<br>"), m_pInfo->dwErr = WSAGetLastError());
  83. __leave;
  84. }
  85. timeout = 1000;
  86. bread = setsockopt(
  87. sockRaw,
  88. SOL_SOCKET,
  89. SO_SNDTIMEO,
  90. (char*)&timeout,
  91. sizeof(timeout));
  92. if(bread == SOCKET_ERROR)
  93. {
  94. m_pInfo->output.err(TEXT("failed to set send timeout: %d<br>"), m_pInfo->dwErr = WSAGetLastError());
  95. __leave;
  96. }
  97. memset(&dest, 0, sizeof(dest));
  98. dest.sin_family = AF_INET;
  99. if ((dest.sin_addr.s_addr = m_pInfo->host) == INADDR_NONE)
  100. {
  101. m_pInfo->output.err(TEXT("failed to gethostbyname (%d)<br>"), m_pInfo->dwErr = WSAHOST_NOT_FOUND);
  102. __leave;
  103. }
  104. datasize = DEF_PACKET_SIZE;
  105. datasize += sizeof(IcmpHeader);
  106. icmp_data = (char*)xmalloc(MAX_PACKET);
  107. recvbuf = (char*)xmalloc(MAX_PACKET);
  108. if (!icmp_data)
  109. {
  110. m_pInfo->output.err(TEXT("HeapAlloc failed %d<br>"),m_pInfo->dwErr = GetLastError());
  111. __leave;
  112. }
  113. memset(icmp_data,0,MAX_PACKET);
  114. fill_icmp_data(icmp_data,datasize);
  115. while(1)
  116. {
  117. int bwrote;
  118. if (nCount++ == 4)
  119. break;
  120. ((IcmpHeader*)icmp_data)->i_cksum = 0;
  121. ((IcmpHeader*)icmp_data)->timestamp = GetTickCount();
  122. ((IcmpHeader*)icmp_data)->i_seq = seq_no++;
  123. ((IcmpHeader*)icmp_data)->i_cksum = checksum((USHORT*)icmp_data,
  124. datasize);
  125. bwrote = sendto(sockRaw,icmp_data,datasize,0,(struct sockaddr*)&dest,
  126. sizeof(dest));
  127. if (bwrote == SOCKET_ERROR)
  128. {
  129. if (WSAGetLastError() == WSAETIMEDOUT)
  130. {
  131. m_pInfo->output.err(TEXT("timed out<br>"));
  132. continue;
  133. }
  134. m_pInfo->output.err(TEXT("sendto failed: %d<br>"),m_pInfo->dwErr = WSAGetLastError());
  135. __leave;
  136. }
  137. if (bwrote < datasize )
  138. {
  139. m_pInfo->output.good(TEXT("Wrote %d bytes<br>"),bwrote);
  140. }
  141. bread = recvfrom(
  142. sockRaw,
  143. recvbuf,
  144. MAX_PACKET,
  145. 0,
  146. (struct sockaddr*)&from,
  147. &fromlen);
  148. if (bread == SOCKET_ERROR)
  149. {
  150. if (WSAGetLastError() == WSAETIMEDOUT)
  151. {
  152. m_pInfo->output.err(TEXT("timed out<br>"));
  153. m_pInfo->dwErr = WSAETIMEDOUT;
  154. continue;
  155. }
  156. m_pInfo->output.err(TEXT("recvfrom failed: %d<br>"),m_pInfo->dwErr = WSAGetLastError());
  157. __leave;
  158. }
  159. if (S_OK != (dw = decode_resp(recvbuf, bread, &from, m_pInfo->output)))
  160. {
  161. m_pInfo->dwErr = dw;
  162. continue;
  163. }
  164. }
  165. }
  166. __finally
  167. {
  168. if (sockRaw != INVALID_SOCKET)
  169. closesocket(sockRaw);
  170. }
  171. Exit:
  172. if (m_pInfo->hEvent)
  173. SetEvent(m_pInfo->hEvent);
  174. }
  175. /*
  176. The response is an IP packet. We must decode the IP header to locate
  177. the ICMP data
  178. */
  179. DWORD decode_resp(char *buf, int bytes,struct sockaddr_in *from, COutput& out)
  180. {
  181. IpHeader *iphdr;
  182. IcmpHeader *icmphdr;
  183. unsigned short iphdrlen;
  184. iphdr = (IpHeader *)buf;
  185. iphdrlen = iphdr->h_len * 4 ; // number of 32-bit words *4 = bytes
  186. if (bytes < iphdrlen + ICMP_MIN)
  187. {
  188. out.err(TEXT("Too few bytes from %s<br>"),inet_ntoa(from->sin_addr));
  189. return ERROR_INVALID_DATA;
  190. }
  191. icmphdr = (IcmpHeader*)(buf + iphdrlen);
  192. if (icmphdr->i_type != ICMP_ECHOREPLY)
  193. {
  194. out.err(TEXT("non-echo type %d recvd<br>"),icmphdr->i_type);
  195. return ERROR_INVALID_DATA;
  196. }
  197. if (icmphdr->i_id != (USHORT)GetCurrentProcessId())
  198. {
  199. out.err(TEXT("someone else's packet!<br>"));
  200. return ERROR_INVALID_DATA;
  201. }
  202. #ifdef UNICODE
  203. const wchar_t * szBytes = TEXT("%d bytes from %S:");
  204. #else
  205. const char * szBytes = TEXT("%d bytes from %s:");
  206. #endif
  207. out.good(szBytes, bytes, inet_ntoa(from->sin_addr));
  208. out.good(TEXT(" icmp_seq = %d. "),icmphdr->i_seq);
  209. out.good(TEXT(" time: %d ms<br>"),GetTickCount()-icmphdr->timestamp);
  210. return S_OK;
  211. }
  212. USHORT checksum(USHORT *buffer, int size) {
  213. unsigned long cksum=0;
  214. while(size >1) {
  215. cksum+=*buffer++;
  216. size -=sizeof(USHORT);
  217. }
  218. if(size ) {
  219. cksum += *(UCHAR*)buffer;
  220. }
  221. cksum = (cksum >> 16) + (cksum & 0xffff);
  222. cksum += (cksum >>16);
  223. return (USHORT)(~cksum);
  224. }
  225. /*
  226. Helper function to fill in various stuff in our ICMP request.
  227. */
  228. void fill_icmp_data(char * icmp_data, int datasize){
  229. IcmpHeader *icmp_hdr;
  230. char *datapart;
  231. icmp_hdr = (IcmpHeader*)icmp_data;
  232. icmp_hdr->i_type = ICMP_ECHO;
  233. icmp_hdr->i_code = 0;
  234. icmp_hdr->i_id = (USHORT)GetCurrentProcessId();
  235. icmp_hdr->i_cksum = 0;
  236. icmp_hdr->i_seq = 0;
  237. datapart = icmp_data + sizeof(IcmpHeader);
  238. //
  239. // Place some junk in the buffer.
  240. //
  241. memset(datapart,'E', datasize - sizeof(IcmpHeader));
  242. }