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

#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;
}
}