/*=============================================================================|
|  PROJECT SNAP7                                                         1.3.0 |
|==============================================================================|
|  Copyright (C) 2013, 2015 Davide Nardella                                    |
|  All rights reserved.                                                        |
|==============================================================================|
|  SNAP7 is free software: you can redistribute it and/or modify               |
|  it under the terms of the Lesser GNU General Public License as published by |
|  the Free Software Foundation, either version 3 of the License, or           |
|  (at your option) any later version.                                         |
|                                                                              |
|  It means that you can distribute your commercial software linked with       |
|  SNAP7 without the requirement to distribute the source code of your         |
|  application and without the requirement that your application be itself     |
|  distributed under LGPL.                                                     |
|                                                                              |
|  SNAP7 is distributed in the hope that it will be useful,                    |
|  but WITHOUT ANY WARRANTY; without even the implied warranty of              |
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
|  Lesser GNU General Public License for more details.                         |
|                                                                              |
|  You should have received a copy of the GNU General Public License and a     |
|  copy of Lesser GNU General Public License along with Snap7.                 |
|  If not, see  http://www.gnu.org/licenses/                                   |
|=============================================================================*/

#include "snap_msgsock.h"

//---------------------------------------------------------------------------

static SocketsLayer SocketsLayerInitializer;

//---------------------------------------------------------------------------
//  Base class endian aware
//---------------------------------------------------------------------------
TSnapBase::TSnapBase()
{
	int x = 1;
	LittleEndian=*(char *)&x == 1;
}
//---------------------------------------------------------------------------
word TSnapBase::SwapWord(word Value)
{
	if (LittleEndian)
		return  ((Value >> 8) & 0xFF) | ((Value << 8) & 0xFF00);
	else
	    return Value;
}
//---------------------------------------------------------------------------
longword TSnapBase::SwapDWord(longword Value)
{
	if (LittleEndian)
		return (Value >> 24) | ((Value << 8) & 0x00FF0000) | ((Value >> 8) & 0x0000FF00) | (Value << 24);
	else
		return Value;
}
//---------------------------------------------------------------------------
void Msg_CloseSocket(socket_t FSocket)
{
    #ifdef OS_WINDOWS
    closesocket(FSocket);
    #else
    close(FSocket);
    #endif
}
//---------------------------------------------------------------------------
longword Msg_GetSockAddr(socket_t FSocket)
{
    sockaddr_in RemoteSin;
    #ifdef OS_WINDOWS
    int namelen = sizeof(RemoteSin);
    #else
    uint32_t namelen = sizeof(RemoteSin);
    #endif
    namelen=sizeof(sockaddr_in);
    if (getpeername(FSocket,(struct sockaddr*)&RemoteSin, &namelen)==0)
        return RemoteSin.sin_addr.s_addr;
    else
        return 0;
}
//---------------------------------------------------------------------------
TMsgSocket::TMsgSocket()
{
    Pinger = new TPinger();
    // Set Defaults
    strcpy(LocalAddress,"0.0.0.0");
    LocalPort=0;
    strcpy(RemoteAddress,"127.0.0.1");
    RemotePort=0;
    WorkInterval=100;
    RecvTimeout=500;
    SendTimeout=10;
    PingTimeout=750;
    Connected=false;
    FSocket=INVALID_SOCKET;
    LastTcpError=0;
    LocalBind=0;
}
//---------------------------------------------------------------------------
TMsgSocket::~TMsgSocket()
{
    DestroySocket();
    delete Pinger;
}
//---------------------------------------------------------------------------
void TMsgSocket::SetSin(sockaddr_in &sin, char *Address, u_short Port)
{	
    uint32_t in_addr;
    in_addr=inet_addr(Address);
    memset(&sin, 0, sizeof(sin));
    LastTcpError=0;

    if (in_addr!=INADDR_NONE)
    {
		sin.sin_addr.s_addr = in_addr;
		sin.sin_family = AF_INET;
		sin.sin_port = htons(Port);
    }
    else
        LastTcpError=WSAEINVALIDADDRESS;
}
//---------------------------------------------------------------------------
void TMsgSocket::GetSin(sockaddr_in sin, char *Address, u_short &Port)
{
  strcpy(Address,inet_ntoa(sin.sin_addr));
  Port=htons(sin.sin_port);
}
//---------------------------------------------------------------------------
void TMsgSocket::GetLocal()
{
    #ifdef OS_WINDOWS
		int namelen = sizeof(LocalSin);
    #else
        uint32_t namelen = sizeof(LocalSin);
    #endif
    if (getsockname(FSocket, (struct sockaddr*)&LocalSin, &namelen)==0)
        GetSin(LocalSin, LocalAddress, LocalPort);
}
//---------------------------------------------------------------------------
void TMsgSocket::GetRemote()
{
    #ifdef OS_WINDOWS
        int namelen = sizeof(RemoteSin);
    #else
        uint32_t namelen = sizeof(RemoteSin);
    #endif
	if (getpeername(FSocket,(struct sockaddr*)&RemoteSin, &namelen)==0)
		GetSin(RemoteSin, RemoteAddress, RemotePort);
}
//---------------------------------------------------------------------------
int TMsgSocket::GetLastSocketError()
{
#ifdef OS_WINDOWS
    return WSAGetLastError();
#else    
    return errno;
#endif
}
//---------------------------------------------------------------------------
void TMsgSocket::Purge()
{
    // small buffer to empty the socket
    char Trash[512];
    int Read;
    if (LastTcpError!=WSAECONNRESET)
    {
        if (CanRead(0)) {
           do
           {
               Read=recv(FSocket, Trash, 512, MSG_NOSIGNAL );
           } while(Read==512);
        }
    }
}
//---------------------------------------------------------------------------
void TMsgSocket::CreateSocket()
{
	DestroySocket();
	LastTcpError=0;
	FSocket =socket(AF_INET, SOCK_STREAM, IPPROTO_TCP );
	if (FSocket!=INVALID_SOCKET)
		SetSocketOptions();
	else
		LastTcpError =GetLastSocketError();
}
//---------------------------------------------------------------------------
void TMsgSocket::GotSocket()
{
    ClientHandle=RemoteSin.sin_addr.s_addr;
    // could be inherited it if wee need further actions on the socket
}
//---------------------------------------------------------------------------
void TMsgSocket::SetSocket(socket_t s)
{
    FSocket=s;
    if (FSocket!=INVALID_SOCKET)
    {
		SetSocketOptions();
		GetLocal();
		GetRemote();
		GotSocket();
    }
    Connected=FSocket!=INVALID_SOCKET;
}
//---------------------------------------------------------------------------
void TMsgSocket::DestroySocket()
{
    if(FSocket != INVALID_SOCKET)
    {        
        if (shutdown(FSocket, SD_SEND)==0)
			Purge();
	#ifdef OS_WINDOWS
		closesocket(FSocket);
	#else
		close(FSocket);
	#endif
		FSocket=INVALID_SOCKET;
    }
    LastTcpError=0;
}
//---------------------------------------------------------------------------
int TMsgSocket::WaitingData()
{
    int result = 0;
    u_long x = 0;
#ifdef OS_WINDOWS
    if (ioctlsocket(FSocket, FIONREAD, &x) == 0)
        result = x;
#else
    if (ioctl(FSocket, FIONREAD, &x) == 0)
        result = x;
#endif
    if (result>MaxPacketSize)
        result = MaxPacketSize;
    return result;
}
//---------------------------------------------------------------------------
int TMsgSocket::WaitForData(int Size, int Timeout)
{
    longword Elapsed;

    // Check for connection active
    if (CanRead(0) && (WaitingData()==0))
        LastTcpError=WSAECONNRESET;
    else
        LastTcpError=0;

    // Enter main loop
    if (LastTcpError==0)
    {
        Elapsed =SysGetTick();
        while((WaitingData()<Size) && (LastTcpError==0))
        {
            // Checks timeout
            if (DeltaTime(Elapsed)>=(longword)(Timeout))
                LastTcpError =WSAETIMEDOUT;
            else
                SysSleep(1);
        }
    }
    if(LastTcpError==WSAECONNRESET)
            Connected =false;

    return LastTcpError;
}
//---------------------------------------------------------------------------
void TMsgSocket::SetSocketOptions()
{
    int NoDelay = 1;
	int KeepAlive = 1;
    LastTcpError=0;
    SockCheck(setsockopt(FSocket, IPPROTO_TCP, TCP_NODELAY,(char*)&NoDelay, sizeof(NoDelay)));

	if (LastTcpError==0)
        SockCheck(setsockopt(FSocket, SOL_SOCKET, SO_KEEPALIVE,(char*)&KeepAlive, sizeof(KeepAlive)));
}
//---------------------------------------------------------------------------
int TMsgSocket::SockCheck(int SockResult)
{
  if (SockResult == (int)(SOCKET_ERROR))
     LastTcpError = GetLastSocketError();

  return LastTcpError;
}
//---------------------------------------------------------------------------
bool TMsgSocket::CanWrite(int Timeout)
{
    timeval TimeV;
    int64_t x;
    fd_set FDset;

	if(FSocket == INVALID_SOCKET)
		return false;

    TimeV.tv_usec = (Timeout % 1000) * 1000;
    TimeV.tv_sec = Timeout / 1000;

    FD_ZERO(&FDset);
    FD_SET(FSocket, &FDset);

    x = select(FSocket + 1, NULL, &FDset, NULL, &TimeV); //<-Ignore this warning in 64bit Visual Studio
    if (x==(int)SOCKET_ERROR) 
    {
        LastTcpError = GetLastSocketError();
        x=0;
    }
    return (x > 0);
}
//---------------------------------------------------------------------------
bool TMsgSocket::CanRead(int Timeout)
{
    timeval TimeV;
    int64_t x;
    fd_set FDset;

	if(FSocket == INVALID_SOCKET)
		return false;

	TimeV.tv_usec = (Timeout % 1000) * 1000;
    TimeV.tv_sec = Timeout / 1000;

    FD_ZERO(&FDset);
    FD_SET(FSocket, &FDset);

    x = select(FSocket + 1, &FDset, NULL, NULL, &TimeV); //<-Ignore this warning in 64bit Visual Studio
    if (x==(int)SOCKET_ERROR)
    {
       LastTcpError = GetLastSocketError();
       x=0;
    }
    return (x > 0);
}
//---------------------------------------------------------------------------
#ifdef NON_BLOCKING_CONNECT
//
// Non blocking connection (UNIX) Thanks to Rolf Stalder
//
int TMsgSocket::SckConnect()
{
	int n, flags, err;
	socklen_t len;
	fd_set rset, wset;
	struct timeval tval;

 	SetSin(RemoteSin, RemoteAddress, RemotePort);

	if (LastTcpError == 0) {
		CreateSocket();
		if (LastTcpError == 0) {
			flags = fcntl(FSocket, F_GETFL, 0);
			if (flags >= 0) {
				if (fcntl(FSocket, F_SETFL, flags | O_NONBLOCK)  != -1) {
					n = connect(FSocket, (struct sockaddr*)&RemoteSin, sizeof(RemoteSin));
					if (n < 0) {
						if (errno != EINPROGRESS) {
							LastTcpError = GetLastSocketError();
						}
						else {
							// still connecting ... 
							FD_ZERO(&rset);
							FD_SET(FSocket, &rset);
							wset = rset;
							tval.tv_sec = PingTimeout / 1000;
							tval.tv_usec = (PingTimeout % 1000) * 1000;

							n = select(FSocket+1, &rset, &wset, NULL, 
							           (PingTimeout ? &tval : NULL));
							if (n == 0) {
								// timeout
								LastTcpError = WSAEHOSTUNREACH;
							}
							else {
								if (FD_ISSET(FSocket, &rset) || FD_ISSET(FSocket, &wset)) {
									err = 0;
									len = sizeof(err);						
									if (getsockopt(
									      FSocket, SOL_SOCKET, SO_ERROR, &err, &len) == 0) {
										if (err) {
											 LastTcpError = err;
										}
										else {
											if (fcntl(FSocket, F_SETFL, flags) != -1) {
												GetLocal();
												ClientHandle = LocalSin.sin_addr.s_addr;
											}
											else {
												LastTcpError = GetLastSocketError();
											}
										}
									}
									else {
										LastTcpError = GetLastSocketError();
									}
								}
								else {
									LastTcpError = -1;
								}
							}
						} // still connecting
					} 
					else if (n == 0) { 
						// connected immediatly
						GetLocal();
						ClientHandle = LocalSin.sin_addr.s_addr;
					}
				}
				else {
					LastTcpError = GetLastSocketError();
				} // fcntl(F_SETFL)
			}
			else {
				LastTcpError = GetLastSocketError();
			} // fcntl(F_GETFL)
		} //valid socket 
	} // LastTcpError==0
	Connected=LastTcpError==0;
 	return LastTcpError;
}
#else
//
// Regular connection (Windows)
//
int TMsgSocket::SckConnect()
{
    int Result;
    SetSin(RemoteSin, RemoteAddress, RemotePort);
    if (LastTcpError==0)
    {
        if (Ping(RemoteSin))
        {
            CreateSocket();
            if (LastTcpError==0)
            {
                Result=connect(FSocket, (struct sockaddr*)&RemoteSin, sizeof(RemoteSin));
                if (SockCheck(Result)==0)
                {
                    GetLocal();
                    // Client handle is self_address (here the connection is ACTIVE)
                    ClientHandle=LocalSin.sin_addr.s_addr;
                }
            }
        }
        else
            LastTcpError=WSAEHOSTUNREACH;
    }
    Connected=LastTcpError==0;
    return LastTcpError;
}
#endif
//---------------------------------------------------------------------------
void TMsgSocket::SckDisconnect()
{
    DestroySocket();
    Connected=false;
}
//---------------------------------------------------------------------------
void TMsgSocket::ForceClose()
{
    if(FSocket != INVALID_SOCKET)
    {
        try {
            #ifdef OS_WINDOWS
            closesocket(FSocket);
            #else
            close(FSocket);
            #endif
        } catch (...) {
        }
        FSocket=INVALID_SOCKET;
    }
    LastTcpError=0;
}
//---------------------------------------------------------------------------
int TMsgSocket::SckBind()
{
    int Res;
    int Opt=1;
    SetSin(LocalSin, LocalAddress, LocalPort);
    if (LastTcpError==0)
    {
        CreateSocket();
        if (LastTcpError==0)
        {		
            setsockopt(FSocket ,SOL_SOCKET, SO_REUSEADDR, (const char *)&Opt, sizeof(int));
            Res =bind(FSocket, (struct sockaddr*)&LocalSin, sizeof(sockaddr_in));
            SockCheck(Res);
            if (Res==0) 
            {
                LocalBind=LocalSin.sin_addr.s_addr;
            }
        }
    }
    else
        LastTcpError=WSAEINVALIDADDRESS;

    return LastTcpError;
}
//---------------------------------------------------------------------------
int TMsgSocket::SckListen()
{
    LastTcpError=0;
    SockCheck(listen(FSocket ,SOMAXCONN));
    return LastTcpError;
}
//---------------------------------------------------------------------------
bool TMsgSocket::Ping(char *Host)
{
    return Pinger->Ping(Host, PingTimeout);
}
//---------------------------------------------------------------------------
bool TMsgSocket::Ping(sockaddr_in Addr)
{
	if (PingTimeout == 0)
		return true;
	else
        return Pinger->Ping(Addr.sin_addr.s_addr, PingTimeout);
}
//---------------------------------------------------------------------------
socket_t TMsgSocket::SckAccept()
{
    socket_t result;
    LastTcpError=0;
    result = accept(FSocket, NULL, NULL);
    if(result==INVALID_SOCKET)
        LastTcpError =GetLastSocketError();
    return result;
}
//---------------------------------------------------------------------------
int TMsgSocket::SendPacket(void *Data, int Size)
{
    int Result;
    
    LastTcpError=0;
    if (SendTimeout>0)
    {
        if (!CanWrite(SendTimeout))
        {
            LastTcpError = WSAETIMEDOUT;
            return LastTcpError;
        }
    }
    if (send(FSocket, (char*)Data, Size, MSG_NOSIGNAL)==Size)
        return 0;
    else
        Result =SOCKET_ERROR;

    SockCheck(Result);
    return Result;
}
//---------------------------------------------------------------------------
bool TMsgSocket::PacketReady(int Size)
{
	return (WaitingData()>=Size);
}
//---------------------------------------------------------------------------
int TMsgSocket::Receive(void *Data, int BufSize, int &SizeRecvd)
{
    LastTcpError=0;
    if (CanRead(RecvTimeout))
    {
        SizeRecvd=recv(FSocket ,(char*)Data ,BufSize ,MSG_NOSIGNAL );

        if (SizeRecvd>0) // something read (default case)
           LastTcpError=0;
        else
            if (SizeRecvd==0)
                LastTcpError = WSAECONNRESET;  // Connection reset by Peer
            else
                LastTcpError=GetLastSocketError(); // we need to know what happened
    }
    else
        LastTcpError = WSAETIMEDOUT;

    if (LastTcpError==WSAECONNRESET)
        Connected = false;

    return LastTcpError;
}
//---------------------------------------------------------------------------
int TMsgSocket::RecvPacket(void *Data, int Size)
{
    int BytesRead;
    WaitForData(Size, RecvTimeout);
    if (LastTcpError==0)
    {
        BytesRead=recv(FSocket, (char*)Data, Size, MSG_NOSIGNAL);
        if (BytesRead==0)
            LastTcpError = WSAECONNRESET;  // Connection reset by Peer
        else
            if (BytesRead<0)
                LastTcpError = GetLastSocketError();
    }
    else // After the timeout the bytes waiting were less then we expected
        if (LastTcpError==WSAETIMEDOUT)
            Purge();

    if (LastTcpError==WSAECONNRESET)
        Connected =false;

    return LastTcpError;
}
//---------------------------------------------------------------------------
int TMsgSocket::PeekPacket(void *Data, int Size)
{
    int BytesRead;
    WaitForData(Size, RecvTimeout);
    if (LastTcpError==0)
    {
        BytesRead=recv(FSocket, (char*)Data, Size, MSG_PEEK | MSG_NOSIGNAL );
        if (BytesRead==0)
            LastTcpError = WSAECONNRESET;  // Connection reset by Peer
        else
            if (BytesRead<0)
                LastTcpError = GetLastSocketError();
    }
    else // After the timeout the bytes waiting were less then we expected
        if (LastTcpError==WSAETIMEDOUT)
            Purge();

    if (LastTcpError==WSAECONNRESET)
        Connected =false;

    return LastTcpError;
}
//---------------------------------------------------------------------------
bool TMsgSocket::Execute()
{
    return true;
}
//==============================================================================
// PING
//==============================================================================

static int PingKind;

#ifdef OS_WINDOWS
// iphlpapi, is loaded dinamically because if this fails we can still try
// to use raw sockets

static char const *iphlpapi = "\\iphlpapi.dll";
#pragma pack(1)

//typedef byte TTxBuffer[40];
typedef byte TTxBuffer[32];
#pragma pack()

typedef HANDLE (__stdcall *pfn_IcmpCreateFile)();
typedef bool (__stdcall *pfn_IcmpCloseHandle)(HANDLE PingHandle);

typedef int (__stdcall *pfn_IcmpSendEcho2)(
    HANDLE PingHandle,
    void *Event,
    void *AcpRoutine,
    void *AcpContext,
    unsigned long DestinationAddress,
    void *RequestData,
    int RequestSize,
    void *not_used,  //should be *IP_OPTION_INFORMATION but we don't use it
    void *ReplyBuffer,
    int ReplySize,
    int Timeout
);

static pfn_IcmpCreateFile IcmpCreateFile;
static pfn_IcmpCloseHandle IcmpCloseHandle;
static pfn_IcmpSendEcho2 IcmpSendEcho2;
static HINSTANCE IcmpDllHandle = 0;
static bool IcmpAvail = false;

bool IcmpInit()
{
	char iphlppath[MAX_PATH+12];

	int PathLen = GetSystemDirectoryA(iphlppath, MAX_PATH);
	if (PathLen != 0)
	{
		strcat(iphlppath, iphlpapi);
		IcmpDllHandle = LoadLibraryA(iphlppath);
	}
	else
		IcmpDllHandle = 0;

    if (IcmpDllHandle != 0)
    {
        IcmpCreateFile=(pfn_IcmpCreateFile)GetProcAddress(IcmpDllHandle,"IcmpCreateFile");
        IcmpCloseHandle=(pfn_IcmpCloseHandle)GetProcAddress(IcmpDllHandle,"IcmpCloseHandle");
        IcmpSendEcho2=(pfn_IcmpSendEcho2)GetProcAddress(IcmpDllHandle,"IcmpSendEcho2");
        return (IcmpCreateFile!=NULL) && (IcmpCloseHandle!=NULL) && (IcmpSendEcho2!=NULL);
    }
    else
        return false;
}

void IcmpDone()
{
    if (IcmpDllHandle!=0)
       FreeLibrary(IcmpDllHandle);
    IcmpAvail=false;
}
#endif

//---------------------------------------------------------------------------
//  RAW Socket Pinger
//---------------------------------------------------------------------------
TRawSocketPinger::TRawSocketPinger()
{
    FSocket =socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    FId  =word(size_t(this));
    FSeq =0;
}
//---------------------------------------------------------------------------
TRawSocketPinger::~TRawSocketPinger()
{
    if (FSocket!=INVALID_SOCKET)
    {
      #ifdef OS_WINDOWS
          closesocket(FSocket);
      #else
          close(FSocket);
      #endif
      FSocket=INVALID_SOCKET;
    };
}
//---------------------------------------------------------------------------
void TRawSocketPinger::InitPacket()
{
    memset(&IcmpBuffer,0,ICmpBufferSize);
    FSeq++;
    
	SendPacket=PIcmpPacket(pbyte(&IcmpBuffer)+sizeof(TIPHeader));
    SendPacket->Header.ic_type=ICMP_ECHORQ;
    SendPacket->Header.ic_code=0;
    SendPacket->Header.ic_cksum=0;
    SendPacket->Header.ic_id=FId;
    SendPacket->Header.ic_seq=FSeq;
 
    memset(&SendPacket->Data,0,sizeof(SendPacket->Data));
    SendPacket->Header.ic_cksum=PacketChecksum();
}
//---------------------------------------------------------------------------
word TRawSocketPinger::PacketChecksum()
{
    word *P = (word*)(SendPacket);
    longword Sum = 0;
    int c;
    for (c = 0; c < int(sizeof(TIcmpPacket) / 2); c++) {
        Sum+=*P;
        P++;
    }
    Sum=(Sum >> 16) + (Sum & 0xFFFF);
    Sum=Sum+(Sum >> 16);
    return word(~Sum);
}
//---------------------------------------------------------------------------
bool TRawSocketPinger::CanRead(int Timeout)
{
    timeval TimeV;
    int64_t x;
    fd_set FDset;

    TimeV.tv_usec = (Timeout % 1000) * 1000;
    TimeV.tv_sec = Timeout / 1000;

    FD_ZERO(&FDset);
    FD_SET(FSocket, &FDset);

    x = select(FSocket + 1, &FDset, NULL, NULL, &TimeV); //<-Ignore this warning in 64bit Visual Studio
    if (x==(int)(SOCKET_ERROR))
       x=0;
    return (x > 0);
}
//---------------------------------------------------------------------------
bool TRawSocketPinger::Ping(longword ip_addr, int Timeout)
{
    sockaddr_in LSockAddr;
    sockaddr_in RSockAddr;
    PIcmpReply Reply;

    if (FSocket==INVALID_SOCKET)
      return true;

    // Init packet
    InitPacket();
    Reply=PIcmpReply(&IcmpBuffer);
    // Init Remote and Local Addresses struct
    RSockAddr.sin_family=AF_INET;
    RSockAddr.sin_port=0;
    RSockAddr.sin_addr.s_addr=ip_addr;

    LSockAddr.sin_family=AF_INET;
    LSockAddr.sin_port=0;
    LSockAddr.sin_addr.s_addr=inet_addr("0.0.0.0");

    // Bind to local
    if (bind(FSocket, (struct sockaddr*)&LSockAddr, sizeof(sockaddr_in))!=0)
        return false;
    // Connect to remote (not a really TCP connection, only to setup the socket)
    if (connect(FSocket, (struct sockaddr*)&RSockAddr, sizeof(sockaddr_in))!=0)
        return false;
    // Send ICMP packet
    if (send(FSocket, (char*)SendPacket, sizeof(TIcmpPacket), MSG_NOSIGNAL)!=int(sizeof(TIcmpPacket)))
        return false;
    // Wait for a reply
    if (!CanRead(Timeout))
        return false;// time expired
    // Get the answer
    if (recv(FSocket, (char*)&IcmpBuffer, ICmpBufferSize, MSG_NOSIGNAL)<int(sizeof(TIcmpReply)))
        return false;
    // Check the answer
    return (Reply->IPH.ip_src==RSockAddr.sin_addr.s_addr) &&  // the peer is what we are looking for
           (Reply->ICmpReply.Header.ic_type==ICMP_ECHORP);    // type = reply
}
//---------------------------------------------------------------------------
//  Pinger
//---------------------------------------------------------------------------
TPinger::TPinger()
{
}
//---------------------------------------------------------------------------
TPinger::~TPinger()
{
}
//---------------------------------------------------------------------------
bool TPinger::RawPing(longword ip_addr, int Timeout)
{
    PRawSocketPinger RawPinger = new TRawSocketPinger();
    bool Result;
    Result=RawPinger->Ping(ip_addr, Timeout);
    delete RawPinger;
    return Result;
}
//---------------------------------------------------------------------------
#ifdef OS_WINDOWS
bool TPinger::WinPing(longword ip_addr, int Timeout)
{
    HANDLE PingHandle;
    TTxBuffer TxBuffer;
    TIcmpBuffer IcmpBuffer;
    bool Result;

    PingHandle = IcmpCreateFile();
    if (PingHandle != INVALID_HANDLE_VALUE)
    {
        memset(&TxBuffer,'\55',sizeof(TTxBuffer));
        Result=(IcmpSendEcho2(PingHandle, NULL, NULL, NULL, ip_addr,
            &TxBuffer, sizeof(TxBuffer), NULL, &IcmpBuffer, ICmpBufferSize, Timeout))>0;
        IcmpCloseHandle(PingHandle);
        return Result;
    }
    else
        return false;
}
#endif
//---------------------------------------------------------------------------
bool TPinger::Ping(char *Host, int Timeout)
{
    longword Addr;
    Addr=inet_addr(Host);
    return Ping(Addr, Timeout);
}
//---------------------------------------------------------------------------
bool TPinger::Ping(longword ip_addr, int Timeout)
{
#ifdef OS_WINDOWS
    if (PingKind==pkWinHelper)
        return WinPing(ip_addr, Timeout);
    else
#endif
    if (PingKind==pkRawSocket)
        return RawPing(ip_addr, Timeout);
    else
        return true; // we still need to continue
}
//---------------------------------------------------------------------------
// Checks if raw sockets are allowed
//---------------------------------------------------------------------------
bool RawSocketsCheck()
{
    socket_t RawSocket;
    bool Result;
    RawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

    Result=RawSocket != INVALID_SOCKET;
    if (Result)
    #ifdef OS_WINDOWS
        closesocket(RawSocket);
    #else
        close(RawSocket);
    #endif

    return Result;
}
//---------------------------------------------------------------------------
// Sockets init
// - Winsock Startup (Windows)
// - ICMP Helper Load (Windows)
// - Check for raw socket (Unix or Windows if ICMP load failed)
//---------------------------------------------------------------------------
SocketsLayer::SocketsLayer()
{
#ifdef OS_WINDOWS
    timeBeginPeriod(1); // it's not strictly related to socket but here is a nice place
    WSAStartup(0x202,&wsaData);
    if (IcmpInit())
       PingKind=pkWinHelper;
    else
#endif
    if (RawSocketsCheck())
        PingKind=pkRawSocket;
    else
        PingKind=pkCannotPing;
}

SocketsLayer::~SocketsLayer()
{
#ifdef OS_WINDOWS
    IcmpDone();
    WSACleanup();
    timeEndPeriod(1);
#endif
}


