#include "network.h" #include "diagnostics.h" #include "util.h" //SOCKET sockRaw = INVALID_SOCKET; #define DEF_PACKET_SIZE 32 #define MAX_PACKET 1024 #define ICMP_ECHO 8 #define ICMP_ECHOREPLY 0 #define ICMP_MIN 8 // minimum 8 byte icmp packet (just header) // ICMP header // typedef struct _ihdr { BYTE i_type; BYTE i_code; // type sub code USHORT i_cksum; USHORT i_id; USHORT i_seq; ULONG timestamp; // This is not the std header, but we reserve space for time }IcmpHeader; // The IP header // typedef struct iphdr { unsigned int h_len:4; // length of the header unsigned int version:4; // Version of IP unsigned char tos; // Type of service unsigned short total_len; // total length of the packet unsigned short ident; // unique identifier unsigned short frag_and_flags; // flags unsigned char ttl; unsigned char proto; // protocol (TCP, UDP etc) unsigned short checksum; // IP checksum unsigned int sourceIP; unsigned int destIP; }IpHeader; void CDiagnostics::FillIcmpData( IN OUT CHAR *pIcmp, IN DWORD dwDataSize ) /*++ Routine Description Creates a ICMP packet by filling in the fields of a passed in structure Arguments pIcmp Pointer to an ICMP buffer dwDataSize Size of the buffer Return Value none --*/ { IcmpHeader *pIcmpHdr; PCHAR pIcmpData; pIcmpHdr = (IcmpHeader*)pIcmp; // Fill in the IMCP buffer // pIcmpHdr->i_type = ICMP_ECHO; pIcmpHdr->i_code = 0; pIcmpHdr->i_id = (USHORT)GetCurrentProcessId(); pIcmpHdr->i_cksum = 0; pIcmpHdr->i_seq = 0; // Append the size of the ICMP packet // pIcmpData = pIcmp + sizeof(IcmpHeader); // Place some junk in the buffer. // memset(pIcmpData,'E', dwDataSize - sizeof(IcmpHeader)); } DWORD CDiagnostics::DecodeResponse( IN PCHAR pBuf, IN int nBytes, IN struct sockaddr_in *pFrom, IN int nIndent ) /*++ Routine Description The response is an IP packet. We must decode the IP header to locate the ICMP data Arguments pBuf Pointer to the recived IP packet nBytes Size of the recived buffer pFrom Information about who the packet is from nIndent How much to indent the text by Return Value none --*/ { IpHeader *pIphdr; IcmpHeader *pIcmphdr; USHORT uIphdrLength; pIphdr = (IpHeader *)pBuf; // number of 32-bit words *4 = bytes // uIphdrLength = pIphdr->h_len * 4 ; if ( nBytes < uIphdrLength + ICMP_MIN) { // Invalid length // return ERROR_INVALID_DATA; } // Extract the ICMP header // pIcmphdr = (IcmpHeader*)(pBuf + uIphdrLength); if (pIcmphdr->i_type != ICMP_ECHOREPLY) { // Invalid type // return ERROR_INVALID_DATA; } if (pIcmphdr->i_id != (USHORT)GetCurrentProcessId()) { // Invalid process ID // return ERROR_INVALID_DATA; } WCHAR szw[5000]; wsprintf(szw,ids(IDS_PING_PACKET),nBytes,inet_ntoa(pFrom->sin_addr),pIcmphdr->i_seq,GetTickCount() - pIcmphdr->timestamp); FormatPing(szw); return S_OK; } USHORT CDiagnostics::CheckSum( IN USHORT *pBuffer, IN DWORD dwSize ) /*++ Routine Description Computes the checksum for the packet Arguments pBuffer Buffer containing the packet dwSize Size of the buffer Return Value Checksum value --*/ { DWORD dwCheckSum=0; while(dwSize >1) { dwCheckSum+=*pBuffer++; dwSize -= sizeof(USHORT); } if( dwSize ) { dwCheckSum += *(UCHAR*)pBuffer; } dwCheckSum = (dwCheckSum >> 16) + (dwCheckSum & 0xffff); dwCheckSum += (dwCheckSum >>16); return (USHORT)(~dwCheckSum); } BOOL CDiagnostics::IsInvalidIPAddress( IN LPCWSTR pszHostName ) { CHAR szIPAddress[MAX_PATH]; if( lstrlen(pszHostName) > 255 ) { return TRUE; } for(INT i=0; pszHostName[i]!=L'\0'; i++) { szIPAddress[i] = pszHostName[i]; } szIPAddress[i] = 0; return IsInvalidIPAddress(szIPAddress); } BOOL CDiagnostics::IsInvalidIPAddress( IN LPCSTR pszHostName ) /*++ Routine Description Checks to see if an IP Host is a in valid IP address 0.0.0.0 is not valid 255.255.255.255 is not valid "" is not valid Arguments pszHostName Host Address Return Value TRUE Is invalid IP address FALSE Valid IP address --*/ { BYTE bIP[4]; int iRetVal; LONG lAddr; if( NULL == pszHostName || strcmp(pszHostName,"") == 0 || strcmp(pszHostName,"255.255.255.255") ==0) { // Invalid IP Host // return TRUE; } lAddr = inet_addr(pszHostName); if( INADDR_NONE != lAddr ) { // Formatted like an IP address X.X.X.X // if( lAddr == 0 ) { // Invalid IP address 0.0.0.0 // return TRUE; } } return FALSE; } int CDiagnostics::Ping( IN LPCTSTR pszwHostName, IN int nIndent ) /*++ Routine Description Pings a host Arguments pszwHostName Host to ping nIndent How much to indent when displaying the ping text Return Value TRUE Successfully pinged FALSE Failed to ping --*/ { SOCKET sockRaw; DWORD dwTimeout; struct sockaddr_in dest,from; hostent * pHostent; DWORD dwRetVal; int lDataSize, lFromSize = sizeof(from); CHAR bIcmp[MAX_PACKET], bRecvbuf[MAX_PACKET]; CHAR szAscii[MAX_PATH + 1]; BOOL bPinged = TRUE; WCHAR szw[5000]; sockRaw = INVALID_SOCKET; FormatPing(NULL); // Convert the wide string into a char string, the winsock functions can not handle wchar stuff // if( !wcstombs(szAscii,pszwHostName,MAX_PATH) ) { // Could not convert the string from wide char to char, might be empty, thus invalid IP // wsprintf(szw,ids(IDS_INVALID_IP),pszwHostName); FormatPing(szw); return FALSE; } // Check if the IP address is pingable // if( IsInvalidIPAddress(szAscii) ) { // We refuse to waste time on ping the IP address // wsprintf(szw,ids(IDS_INVALID_IP),pszwHostName); FormatPing(szw); return FALSE; } // Create a winsock socket // sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, WSA_FLAG_OVERLAPPED); if( INVALID_SOCKET == sockRaw ) { // Unable to create socket // wsprintf(szw,ids(IDS_SOCKET_CREATE_FAIL)); FormatPing(szw); return FALSE; } // Set recieve timeout to 1 second // dwTimeout = 1000; dwRetVal = setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&dwTimeout, sizeof(dwTimeout)); if( SOCKET_ERROR == dwRetVal ) { // Unable to set socket options // wsprintf(szw,ids(IDS_SOCKET_CREATE_FAIL)); FormatPing(szw); closesocket(sockRaw); sockRaw = INVALID_SOCKET; return FALSE; } // Set send timeout to one second // dwTimeout = 1000; dwRetVal = setsockopt(sockRaw, SOL_SOCKET, SO_SNDTIMEO, (char*)&dwTimeout, sizeof(dwTimeout)); if( SOCKET_ERROR == dwRetVal ) { // Unable to set socket options // wsprintf(szw,ids(IDS_SOCKET_CREATE_FAIL)); FormatPing(szw); sockRaw = INVALID_SOCKET; closesocket(sockRaw); return FALSE; } // Set the destination info // memset(&dest, 0, sizeof(dest)); dest.sin_family = AF_INET; pHostent = gethostbyname(szAscii); if( !pHostent ) { // Unable to resolve name // wsprintf(szw,ids(IDS_RESOLVE_NAME_FAIL)); FormatPing(szw); closesocket(sockRaw); sockRaw = INVALID_SOCKET; return FALSE; } // Create the ICMP packet // ULONG ulAddr; memcpy(&ulAddr,pHostent->h_addr,pHostent->h_length); dest.sin_addr.s_addr = ulAddr; lDataSize = DEF_PACKET_SIZE; lDataSize += sizeof(IcmpHeader); // Fill the ICMP packet // FillIcmpData(bIcmp,lDataSize); int nFailPingCount = 0; // Send four ICMP packets and wait for a response // for(DWORD dwPacketsSent = 0; dwPacketsSent < 4; dwPacketsSent++) { int nWrote, nRead; if( ShouldTerminate() ) goto end; // Fillin the ICMP header // ((IcmpHeader*)bIcmp)->i_cksum = 0; ((IcmpHeader*)bIcmp)->timestamp = GetTickCount(); ((IcmpHeader*)bIcmp)->i_seq = (USHORT) dwPacketsSent; ((IcmpHeader*)bIcmp)->i_cksum = CheckSum((USHORT*)bIcmp,lDataSize); // Send the ICMP packet // nWrote = sendto(sockRaw, bIcmp, lDataSize, 0, (struct sockaddr*)&dest, sizeof(dest)); if( SOCKET_ERROR == nWrote ) { // Unable to send packet // nFailPingCount++; bPinged = FALSE; wsprintf(szw,ids(IDS_SEND_FAIL),dwPacketsSent); FormatPing(szw); continue; } BOOLEAN bTryAgain = FALSE; do { bTryAgain = FALSE; if( ShouldTerminate() ) goto end; // Recive the packet // nRead = recvfrom(sockRaw, bRecvbuf, MAX_PACKET, 0, (struct sockaddr*)&from, (int *)&lFromSize); if( nRead != SOCKET_ERROR && lFromSize!=0 && memcmp(&dest.sin_addr,&from.sin_addr,sizeof(IN_ADDR))!=0 ) { // This is not who we sent the packet to. // try again. // bTryAgain = TRUE; } } while(bTryAgain); if( ShouldTerminate() ) goto end; if (nRead == SOCKET_ERROR) { // Did not receive response // bPinged = FALSE; nFailPingCount++; wsprintf(szw,ids(IDS_UNREACHABLE)); FormatPing(szw); continue; } if (S_OK != DecodeResponse(bRecvbuf, nRead, &from,nIndent)) { nFailPingCount++ ; bPinged = FALSE; wsprintf(szw,ids(IDS_UNREACHABLE)); FormatPing(szw); continue; } } end: // Close the socket // if( sockRaw != INVALID_SOCKET ) { closesocket(sockRaw); } return nFailPingCount == 0 ? TRUE:FALSE; } BOOL CDiagnostics::Connect( IN LPCTSTR pszwHostName, IN DWORD dwPort ) /*++ Routine Description Establish a TCP connect Arguments pszwHostName Host to ping dwPort Port to connect to Return Value TRUE Successfully connected FALSE Failed to establish connection --*/ { SOCKET s; SOCKADDR_IN sAddr; CHAR szAscii[MAX_PATH + 1]; hostent * pHostent; // Create the socket // s = socket(AF_INET, SOCK_STREAM, PF_UNSPEC); if (INVALID_SOCKET == s) { return FALSE; } // Bind this socket to the server's socket address // memset(&sAddr, 0, sizeof (sAddr)); sAddr.sin_family = AF_INET; sAddr.sin_port = htons((u_short)dwPort); wcstombs(szAscii,(WCHAR *)pszwHostName,MAX_PATH); pHostent = gethostbyname(szAscii); if( !pHostent ) { return FALSE; } // Set the destination info // ULONG ulAddr; memcpy(&ulAddr,pHostent->h_addr,pHostent->h_length); sAddr.sin_addr.s_addr = ulAddr; // Attempt to connect // if (connect(s, (SOCKADDR*)&sAddr, sizeof(SOCKADDR_IN)) == 0) { // Connection succeded // closesocket(s); return TRUE; } else { // Connection failed // closesocket(s); return FALSE; } }