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.

579 lines
12 KiB

  1. #include "network.h"
  2. #include "diagnostics.h"
  3. #include "util.h"
  4. //SOCKET sockRaw = INVALID_SOCKET;
  5. #define DEF_PACKET_SIZE 32
  6. #define MAX_PACKET 1024
  7. #define ICMP_ECHO 8
  8. #define ICMP_ECHOREPLY 0
  9. #define ICMP_MIN 8 // minimum 8 byte icmp packet (just header)
  10. // ICMP header
  11. //
  12. typedef struct _ihdr {
  13. BYTE i_type;
  14. BYTE i_code; // type sub code
  15. USHORT i_cksum;
  16. USHORT i_id;
  17. USHORT i_seq;
  18. ULONG timestamp; // This is not the std header, but we reserve space for time
  19. }IcmpHeader;
  20. // The IP header
  21. //
  22. typedef struct iphdr {
  23. unsigned int h_len:4; // length of the header
  24. unsigned int version:4; // Version of IP
  25. unsigned char tos; // Type of service
  26. unsigned short total_len; // total length of the packet
  27. unsigned short ident; // unique identifier
  28. unsigned short frag_and_flags; // flags
  29. unsigned char ttl;
  30. unsigned char proto; // protocol (TCP, UDP etc)
  31. unsigned short checksum; // IP checksum
  32. unsigned int sourceIP;
  33. unsigned int destIP;
  34. }IpHeader;
  35. void
  36. CDiagnostics::FillIcmpData(
  37. IN OUT CHAR *pIcmp,
  38. IN DWORD dwDataSize
  39. )
  40. /*++
  41. Routine Description
  42. Creates a ICMP packet by filling in the fields of a passed in structure
  43. Arguments
  44. pIcmp Pointer to an ICMP buffer
  45. dwDataSize Size of the buffer
  46. Return Value
  47. none
  48. --*/
  49. {
  50. IcmpHeader *pIcmpHdr;
  51. PCHAR pIcmpData;
  52. pIcmpHdr = (IcmpHeader*)pIcmp;
  53. // Fill in the IMCP buffer
  54. //
  55. pIcmpHdr->i_type = ICMP_ECHO;
  56. pIcmpHdr->i_code = 0;
  57. pIcmpHdr->i_id = (USHORT)GetCurrentProcessId();
  58. pIcmpHdr->i_cksum = 0;
  59. pIcmpHdr->i_seq = 0;
  60. // Append the size of the ICMP packet
  61. //
  62. pIcmpData = pIcmp + sizeof(IcmpHeader);
  63. // Place some junk in the buffer.
  64. //
  65. memset(pIcmpData,'E', dwDataSize - sizeof(IcmpHeader));
  66. }
  67. DWORD
  68. CDiagnostics::DecodeResponse(
  69. IN PCHAR pBuf,
  70. IN int nBytes,
  71. IN struct sockaddr_in *pFrom,
  72. IN int nIndent
  73. )
  74. /*++
  75. Routine Description
  76. The response is an IP packet. We must decode the IP header to locate
  77. the ICMP data
  78. Arguments
  79. pBuf Pointer to the recived IP packet
  80. nBytes Size of the recived buffer
  81. pFrom Information about who the packet is from
  82. nIndent How much to indent the text by
  83. Return Value
  84. none
  85. --*/
  86. {
  87. IpHeader *pIphdr;
  88. IcmpHeader *pIcmphdr;
  89. USHORT uIphdrLength;
  90. pIphdr = (IpHeader *)pBuf;
  91. // number of 32-bit words *4 = bytes
  92. //
  93. uIphdrLength = pIphdr->h_len * 4 ;
  94. if ( nBytes < uIphdrLength + ICMP_MIN)
  95. {
  96. // Invalid length
  97. //
  98. return ERROR_INVALID_DATA;
  99. }
  100. // Extract the ICMP header
  101. //
  102. pIcmphdr = (IcmpHeader*)(pBuf + uIphdrLength);
  103. if (pIcmphdr->i_type != ICMP_ECHOREPLY)
  104. {
  105. // Invalid type
  106. //
  107. return ERROR_INVALID_DATA;
  108. }
  109. if (pIcmphdr->i_id != (USHORT)GetCurrentProcessId())
  110. {
  111. // Invalid process ID
  112. //
  113. return ERROR_INVALID_DATA;
  114. }
  115. WCHAR szw[5000];
  116. wsprintf(szw,ids(IDS_PING_PACKET),nBytes,inet_ntoa(pFrom->sin_addr),pIcmphdr->i_seq,GetTickCount() - pIcmphdr->timestamp);
  117. FormatPing(szw);
  118. return S_OK;
  119. }
  120. USHORT
  121. CDiagnostics::CheckSum(
  122. IN USHORT *pBuffer,
  123. IN DWORD dwSize
  124. )
  125. /*++
  126. Routine Description
  127. Computes the checksum for the packet
  128. Arguments
  129. pBuffer Buffer containing the packet
  130. dwSize Size of the buffer
  131. Return Value
  132. Checksum value
  133. --*/
  134. {
  135. DWORD dwCheckSum=0;
  136. while(dwSize >1)
  137. {
  138. dwCheckSum+=*pBuffer++;
  139. dwSize -= sizeof(USHORT);
  140. }
  141. if( dwSize )
  142. {
  143. dwCheckSum += *(UCHAR*)pBuffer;
  144. }
  145. dwCheckSum = (dwCheckSum >> 16) + (dwCheckSum & 0xffff);
  146. dwCheckSum += (dwCheckSum >>16);
  147. return (USHORT)(~dwCheckSum);
  148. }
  149. BOOL
  150. CDiagnostics::IsInvalidIPAddress(
  151. IN LPCWSTR pszHostName
  152. )
  153. {
  154. CHAR szIPAddress[MAX_PATH];
  155. if( lstrlen(pszHostName) > 255 )
  156. {
  157. return TRUE;
  158. }
  159. for(INT i=0; pszHostName[i]!=L'\0'; i++)
  160. {
  161. szIPAddress[i] = pszHostName[i];
  162. }
  163. szIPAddress[i] = 0;
  164. return IsInvalidIPAddress(szIPAddress);
  165. }
  166. BOOL
  167. CDiagnostics::IsInvalidIPAddress(
  168. IN LPCSTR pszHostName
  169. )
  170. /*++
  171. Routine Description
  172. Checks to see if an IP Host is a in valid IP address
  173. 0.0.0.0 is not valid
  174. 255.255.255.255 is not valid
  175. "" is not valid
  176. Arguments
  177. pszHostName Host Address
  178. Return Value
  179. TRUE Is invalid IP address
  180. FALSE Valid IP address
  181. --*/
  182. {
  183. BYTE bIP[4];
  184. int iRetVal;
  185. LONG lAddr;
  186. if( NULL == pszHostName || strcmp(pszHostName,"") == 0 || strcmp(pszHostName,"255.255.255.255") ==0)
  187. {
  188. // Invalid IP Host
  189. //
  190. return TRUE;
  191. }
  192. lAddr = inet_addr(pszHostName);
  193. if( INADDR_NONE != lAddr )
  194. {
  195. // Formatted like an IP address X.X.X.X
  196. //
  197. if( lAddr == 0 )
  198. {
  199. // Invalid IP address 0.0.0.0
  200. //
  201. return TRUE;
  202. }
  203. }
  204. return FALSE;
  205. }
  206. int
  207. CDiagnostics::Ping(
  208. IN LPCTSTR pszwHostName,
  209. IN int nIndent
  210. )
  211. /*++
  212. Routine Description
  213. Pings a host
  214. Arguments
  215. pszwHostName Host to ping
  216. nIndent How much to indent when displaying the ping text
  217. Return Value
  218. TRUE Successfully pinged
  219. FALSE Failed to ping
  220. --*/
  221. {
  222. SOCKET sockRaw;
  223. DWORD dwTimeout;
  224. struct sockaddr_in dest,from;
  225. hostent * pHostent;
  226. DWORD dwRetVal;
  227. int lDataSize, lFromSize = sizeof(from);
  228. CHAR bIcmp[MAX_PACKET], bRecvbuf[MAX_PACKET];
  229. CHAR szAscii[MAX_PATH + 1];
  230. BOOL bPinged = TRUE;
  231. WCHAR szw[5000];
  232. sockRaw = INVALID_SOCKET;
  233. FormatPing(NULL);
  234. // Convert the wide string into a char string, the winsock functions can not handle wchar stuff
  235. //
  236. if( !wcstombs(szAscii,pszwHostName,MAX_PATH) )
  237. {
  238. // Could not convert the string from wide char to char, might be empty, thus invalid IP
  239. //
  240. wsprintf(szw,ids(IDS_INVALID_IP),pszwHostName);
  241. FormatPing(szw);
  242. return FALSE;
  243. }
  244. // Check if the IP address is pingable
  245. //
  246. if( IsInvalidIPAddress(szAscii) )
  247. {
  248. // We refuse to waste time on ping the IP address
  249. //
  250. wsprintf(szw,ids(IDS_INVALID_IP),pszwHostName);
  251. FormatPing(szw);
  252. return FALSE;
  253. }
  254. // Create a winsock socket
  255. //
  256. sockRaw = WSASocket(AF_INET,
  257. SOCK_RAW,
  258. IPPROTO_ICMP,
  259. NULL,
  260. 0,
  261. WSA_FLAG_OVERLAPPED);
  262. if( INVALID_SOCKET == sockRaw )
  263. {
  264. // Unable to create socket
  265. //
  266. wsprintf(szw,ids(IDS_SOCKET_CREATE_FAIL));
  267. FormatPing(szw);
  268. return FALSE;
  269. }
  270. // Set recieve timeout to 1 second
  271. //
  272. dwTimeout = 1000;
  273. dwRetVal = setsockopt(sockRaw,
  274. SOL_SOCKET,
  275. SO_RCVTIMEO,
  276. (char*)&dwTimeout,
  277. sizeof(dwTimeout));
  278. if( SOCKET_ERROR == dwRetVal )
  279. {
  280. // Unable to set socket options
  281. //
  282. wsprintf(szw,ids(IDS_SOCKET_CREATE_FAIL));
  283. FormatPing(szw);
  284. closesocket(sockRaw);
  285. sockRaw = INVALID_SOCKET;
  286. return FALSE;
  287. }
  288. // Set send timeout to one second
  289. //
  290. dwTimeout = 1000;
  291. dwRetVal = setsockopt(sockRaw,
  292. SOL_SOCKET,
  293. SO_SNDTIMEO,
  294. (char*)&dwTimeout,
  295. sizeof(dwTimeout));
  296. if( SOCKET_ERROR == dwRetVal )
  297. {
  298. // Unable to set socket options
  299. //
  300. wsprintf(szw,ids(IDS_SOCKET_CREATE_FAIL));
  301. FormatPing(szw);
  302. sockRaw = INVALID_SOCKET;
  303. closesocket(sockRaw);
  304. return FALSE;
  305. }
  306. // Set the destination info
  307. //
  308. memset(&dest, 0, sizeof(dest));
  309. dest.sin_family = AF_INET;
  310. pHostent = gethostbyname(szAscii);
  311. if( !pHostent )
  312. {
  313. // Unable to resolve name
  314. //
  315. wsprintf(szw,ids(IDS_RESOLVE_NAME_FAIL));
  316. FormatPing(szw);
  317. closesocket(sockRaw);
  318. sockRaw = INVALID_SOCKET;
  319. return FALSE;
  320. }
  321. // Create the ICMP packet
  322. //
  323. ULONG ulAddr;
  324. memcpy(&ulAddr,pHostent->h_addr,pHostent->h_length);
  325. dest.sin_addr.s_addr = ulAddr;
  326. lDataSize = DEF_PACKET_SIZE;
  327. lDataSize += sizeof(IcmpHeader);
  328. // Fill the ICMP packet
  329. //
  330. FillIcmpData(bIcmp,lDataSize);
  331. int nFailPingCount = 0;
  332. // Send four ICMP packets and wait for a response
  333. //
  334. for(DWORD dwPacketsSent = 0; dwPacketsSent < 4; dwPacketsSent++)
  335. {
  336. int nWrote, nRead;
  337. if( ShouldTerminate() ) goto end;
  338. // Fillin the ICMP header
  339. //
  340. ((IcmpHeader*)bIcmp)->i_cksum = 0;
  341. ((IcmpHeader*)bIcmp)->timestamp = GetTickCount();
  342. ((IcmpHeader*)bIcmp)->i_seq = (USHORT) dwPacketsSent;
  343. ((IcmpHeader*)bIcmp)->i_cksum = CheckSum((USHORT*)bIcmp,lDataSize);
  344. // Send the ICMP packet
  345. //
  346. nWrote = sendto(sockRaw,
  347. bIcmp,
  348. lDataSize,
  349. 0,
  350. (struct sockaddr*)&dest,
  351. sizeof(dest));
  352. if( SOCKET_ERROR == nWrote )
  353. {
  354. // Unable to send packet
  355. //
  356. nFailPingCount++;
  357. bPinged = FALSE;
  358. wsprintf(szw,ids(IDS_SEND_FAIL),dwPacketsSent);
  359. FormatPing(szw);
  360. continue;
  361. }
  362. BOOLEAN bTryAgain = FALSE;
  363. do
  364. {
  365. bTryAgain = FALSE;
  366. if( ShouldTerminate() ) goto end;
  367. // Recive the packet
  368. //
  369. nRead = recvfrom(sockRaw,
  370. bRecvbuf,
  371. MAX_PACKET,
  372. 0,
  373. (struct sockaddr*)&from,
  374. (int *)&lFromSize);
  375. if( nRead != SOCKET_ERROR && lFromSize!=0 && memcmp(&dest.sin_addr,&from.sin_addr,sizeof(IN_ADDR))!=0 )
  376. {
  377. // This is not who we sent the packet to.
  378. // try again.
  379. //
  380. bTryAgain = TRUE;
  381. }
  382. }
  383. while(bTryAgain);
  384. if( ShouldTerminate() ) goto end;
  385. if (nRead == SOCKET_ERROR)
  386. {
  387. // Did not receive response
  388. //
  389. bPinged = FALSE;
  390. nFailPingCount++;
  391. wsprintf(szw,ids(IDS_UNREACHABLE));
  392. FormatPing(szw);
  393. continue;
  394. }
  395. if (S_OK != DecodeResponse(bRecvbuf, nRead, &from,nIndent))
  396. {
  397. nFailPingCount++ ;
  398. bPinged = FALSE;
  399. wsprintf(szw,ids(IDS_UNREACHABLE));
  400. FormatPing(szw);
  401. continue;
  402. }
  403. }
  404. end:
  405. // Close the socket
  406. //
  407. if( sockRaw != INVALID_SOCKET )
  408. {
  409. closesocket(sockRaw);
  410. }
  411. return nFailPingCount == 0 ? TRUE:FALSE;
  412. }
  413. BOOL
  414. CDiagnostics::Connect(
  415. IN LPCTSTR pszwHostName,
  416. IN DWORD dwPort
  417. )
  418. /*++
  419. Routine Description
  420. Establish a TCP connect
  421. Arguments
  422. pszwHostName Host to ping
  423. dwPort Port to connect to
  424. Return Value
  425. TRUE Successfully connected
  426. FALSE Failed to establish connection
  427. --*/
  428. {
  429. SOCKET s;
  430. SOCKADDR_IN sAddr;
  431. CHAR szAscii[MAX_PATH + 1];
  432. hostent * pHostent;
  433. // Create the socket
  434. //
  435. s = socket(AF_INET, SOCK_STREAM, PF_UNSPEC);
  436. if (INVALID_SOCKET == s)
  437. {
  438. return FALSE;
  439. }
  440. // Bind this socket to the server's socket address
  441. //
  442. memset(&sAddr, 0, sizeof (sAddr));
  443. sAddr.sin_family = AF_INET;
  444. sAddr.sin_port = htons((u_short)dwPort);
  445. wcstombs(szAscii,(WCHAR *)pszwHostName,MAX_PATH);
  446. pHostent = gethostbyname(szAscii);
  447. if( !pHostent )
  448. {
  449. return FALSE;
  450. }
  451. // Set the destination info
  452. //
  453. ULONG ulAddr;
  454. memcpy(&ulAddr,pHostent->h_addr,pHostent->h_length);
  455. sAddr.sin_addr.s_addr = ulAddr;
  456. // Attempt to connect
  457. //
  458. if (connect(s, (SOCKADDR*)&sAddr, sizeof(SOCKADDR_IN)) == 0)
  459. {
  460. // Connection succeded
  461. //
  462. closesocket(s);
  463. return TRUE;
  464. }
  465. else
  466. {
  467. // Connection failed
  468. //
  469. closesocket(s);
  470. return FALSE;
  471. }
  472. }