' ########################################################################################
' File: cCTSocket.inc
' Contents: FreeBasic Windows Socket class support.
' Version: 1.00
' Compiler: FreeBasic 32 & 64-bit Windows
' Copyright (c) 2017 Rick Kelly
' Released into the public domain for private and public use without restriction
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ########################################################################################

#Pragma Once

#Include Once "win/winsock2.bi"
#Include Once "win/ws2tcpip.bi"

' ########################################################################################
' cCTSocket Class
' ########################################################################################

Type cCTSocket Extends Object

Dim lStartup       as BOOLEAN
Dim iStartupError  as Long
Dim iLastError     as Long
Dim WSAD           as WSADATA

    Private:

    Declare Function WinSockStartup (ByRef iError as Long) as BOOLEAN

    Public:

    Declare Function TCPConnect(ByRef hSocket as SOCKET, _
                                ByRef szIPAddress as ZString, _
                                ByVal wPort as WORD, _
                                ByVal iTimeout as Long) as BOOLEAN    
    Declare Function HostNameFromIP(ByRef szIPAddress as ZString, _
                                    ByRef szHostName as ZString) as BOOLEAN
    Declare Function IPFromHostName(ByRef szHostName as ZString, _
                                    ByRef szIPAddress as ZString) as BOOLEAN
    Declare Function ConnectSocket(ByVal hSocket as SOCKET, _
                                   ByRef SockAddress as SOCKADDR_IN) as BOOLEAN
    Declare Function Disconnect(ByRef hSocket as SOCKET) as BOOLEAN
    Declare Sub SocketClose(ByVal hSocket as SOCKET)
    Declare Function SocketShutdown(ByVal hSocket as SOCKET) as BOOLEAN
    Declare Function GetSocket(ByVal nFamily as Long, _
                               ByVal nType as Long, _
                               ByVal nProtocol as Long, _
                               ByRef hSocket as SOCKET) as BOOLEAN
    Declare Function HostByName(ByRef szHostName as ZString, _
                                ByRef lpHostEntry as HOSTENT Ptr) as BOOLEAN
    Declare Sub IPToString(ByVal nIPAddress as ulong, _
                           ByRef szIPAddress as ZString)
    Declare Function StringToIP(ByRef szIPAddress as ZString) as ulong
    Declare Function HostByAddress(ByRef szHostName as ZString, _
                                   ByRef lpHostEntry as HOSTENT Ptr) as BOOLEAN
    Declare Function OpenListenerSocket(ByRef hSocket as SOCKET, _
                                        ByVal HWnd as DWORD, _
                                        ByVal nMessageID as DWORD, _
                                        ByVal nEvents as Long, _
                                        ByVal lDefaultIP as BOOLEAN, _         
                                        ByVal nType as Long, _
                                        ByVal wPort as WORD, _
                                        ByRef szIPAddress as ZString) as BOOLEAN                               
    Declare Function PreferredAddress(ByRef sLocalHostName as String, _
                                      ByRef SockAddress as SOCKADDR_IN) as BOOLEAN                               
    Declare Function LocalHostName(ByRef sLocalHostName as String) as BOOLEAN                               
    Declare Function UDPBroadcast(ByVal sMessage as String, _
                                  ByRef sResponse as String, _
                                  ByVal wPort as WORD, _
                                  ByVal iTimeOut as Long, _
                                  ByVal nMessageSize as Long) as BOOLEAN                               
    Declare Function BindSocket(ByVal hSocket as SOCKET, _
                                ByRef SockAddress as SOCKADDR_IN) as BOOLEAN                               
    Declare Function ListenSocket(ByVal hSocket as SOCKET) as BOOLEAN
    Declare Function AcceptSocket(ByVal hListeningSocket as SOCKET, _
                                  ByRef hAcceptSocket as SOCKET, _
                                  ByRef AcceptSockAddress as SOCKADDR_IN) as BOOLEAN
    Declare Function BlockingSocket(ByVal hSocket as SOCKET, _
                                    ByVal nMode as Long) as BOOLEAN 
    Declare Function TimeoutSocket(ByVal hSocket as SOCKET, _
                                   ByVal iTimeout as Long) as BOOLEAN
    Declare Function AsyncSelectSocket(ByVal hSocket as SOCKET, _
                                       ByVal HWnd as DWORD, _
                                       ByVal nMessageID as DWORD, _
                                       ByVal nEvents as Long) as BOOLEAN
    Declare Function UDPSendTo(ByVal hSocket as DWORD, _
                               ByRef sMessage as String, _
                               ByVal wPort as WORD, _
                               ByVal nIPAddress as ulong) as BOOLEAN
    Declare Function UDPReceiveFrom(ByVal hSocket as DWORD, _
                                    ByRef sResponse as String, _
                                    ByRef SockAddress as SOCKADDR_IN, _
                                    ByVal nMessageSize as Long) as BOOLEAN
    Declare Function SendAndReceiveSocket (ByVal hSocket as SOCKET, _
                                           ByVal iBufferSize as Long, _
                                           ByRef sMessage as String, _
                                           ByRef sResponse as String, _
                                           ByVal iTimeOut as Long) as BOOLEAN
    Declare Function SendSocket(ByVal hSocket as SOCKET, _
                                ByRef sData as String) as BOOLEAN
    Declare Function ReceiveSocket(ByVal hSocket as Long, _
                                   ByVal iBufferSize as Long, _
                                   ByRef sReceived as String, _
                                   ByVal iTimeOut as Long) as BOOLEAN                               
    Declare Function SocketReceiveReady(ByVal hSocket as SOCKET, _
                                        ByVal iTimeout as Long) as BOOLEAN
    Declare Property GetLastErrorCode() as Long
    Declare Property StartupStatus() as BOOLEAN
    Declare Property StartupError() as Long
    Declare Constructor
    Declare Destructor

End Type

Constructor cCTSocket()

    This.lStartup = WinSockStartup (This.iStartupError)

End Constructor
Destructor cCTSocket()

' =====================================================================================
' Release winsock resources
' =====================================================================================

    If This.lStartup = True Then

       WSACleanup()

    End If

End Destructor

' =====================================================================================
' Connect to TCP host
' =====================================================================================
Private Function cCTSocket.TCPConnect (ByRef hSocket as SOCKET, _
                                       ByRef szIPAddress as ZString, _
                                       ByVal wPort as WORD, _
                                       ByVal iTimeout as Long) as BOOLEAN

Dim lReturn       as BOOLEAN
Dim nIPAddress    as IN_ADDR
Dim SockAddress   as SOCKADDR_IN

    lReturn = False

    nIPAddress.s_addr = StringToIP(szIPAddress)

    lReturn = GetSocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,hSocket)

    Select Case lReturn

        Case True

            lReturn = TimeoutSocket(hSocket,iTimeout)

            If lReturn = True Then

               SockAddress.sin_family = AF_INET
               SockAddress.sin_port = htons(wPort)
               SockAddress.sin_addr.s_addr = nIPAddress.s_addr

               lReturn = ConnectSocket(hSocket,SockAddress)

            End If

            If lReturn = False Then

               Disconnect(hSocket)

            End If

    End Select

    Function = lReturn

End Function
' =====================================================================================
' Given an IP string, lookup the host name
' =====================================================================================
Private Function cCTSocket.HostNameFromIP (ByRef szIPAddress as ZString, _
                                           ByRef szHostName as ZString) as BOOLEAN

Dim lReturn           as BOOLEAN
Dim lpHost            as HOSTENT Ptr
Dim nIPAddress        as IN_ADDR

    lReturn = False

    nIPAddress.s_addr = StringToIP (szIPAddress)

    If nIPAddress.s_addr = INADDR_NONE Then

        This.iLastError = WSAHOST_NOT_FOUND

        Function = False

        Exit Function

    End If

    lReturn = HostByAddress (szIPAddress,lpHost)

    If lReturn = True Then
    
       szHostName = *Cast(ZString Ptr, lpHost->h_name)

    End If
 
    Function = lReturn

End Function
' =====================================================================================
' Given an IP string, lookup the host name
' =====================================================================================
Private Function cCTSocket.IPFromHostName (ByRef szHostName as ZString, _
                                           ByRef szIPAddress as ZString) as BOOLEAN

Dim lReturn           as BOOLEAN
Dim lpHost            as HOSTENT Ptr
Dim HOST_ADDR_LIST    as IN_ADDR

    szIPAddress = ""
    lReturn = HostByName (szHostName,lpHost)

    If lReturn = True Then

       HOST_ADDR_LIST = *Cast(IN_ADDR Ptr, lpHost->h_addr_list[0])
       
       If HOST_ADDR_LIST.s_addr = INADDR_NONE Then

          This.iLastError = WSAHOST_NOT_FOUND

          lReturn = False
          
       Else 

          IPToString (HOST_ADDR_LIST.s_addr, szIPAddress)
          
       End If

    End If
 
    Function = lReturn

End Function
' =====================================================================================
' Open a Listener Socket
' =====================================================================================
Private Function cCTSocket.OpenListenerSocket (ByRef hSocket as SOCKET, _
                                               ByVal HWnd as DWORD, _
                                               ByVal nMessageID as DWORD, _
                                               ByVal nEvents as Long, _
                                               ByVal lDefaultIP as BOOLEAN, _           'If TRUE, preferred IP returned in szIPAddress
                                               ByVal nType as Long, _
                                               ByVal wPort as WORD, _
                                               ByRef szIPAddress as ZString) as BOOLEAN

Dim SockAddress           as SOCKADDR_IN
Dim sLocalHostName        as String
Dim nIPProto              as Long
Dim lReturn               as BOOLEAN

    lReturn = True
    nIPProto = IIf(nType = SOCK_STREAM,IPPROTO_TCP,IPPROTO_UDP)

' Get preferred IP address if requested

    If lDefaultIP = True Then

       lReturn = PreferredAddress(sLocalHostName,SockAddress)

       If lReturn = True Then

          IPToString(SockAddress.sin_addr.s_addr,szIPAddress)

       End If

    Else

       SockAddress.sin_addr.s_addr = StringToIP(szIPAddress)

    End If

    If lReturn = False Then

       Function = False
       Exit Function

    End If

' Open Socket

    If GetSocket(AF_INET,nType,nIPProto,hSocket) = False Then

       Function = False
       Exit Function

    End If

    SockAddress.sin_family = AF_INET
    SockAddress.sin_port = htons(wPort)

' Open, Bind, Listen Socket

    If GetSocket(AF_INET,nType,nIPProto,hSocket) = True Then

       If BindSocket(hSocket,SockAddress) = True Then

          lReturn = AsyncSelectSocket(hSocket,HWnd,nMessageID,nEvents)

          If lReturn = True Then

             If nIPProto = IPPROTO_TCP Then

                lReturn = ListenSocket(hSocket)

             End If

          End If

       End If

    End If

    If lReturn = False Then

       Disconnect(hSocket)

    End If

    Function = lReturn

End Function
' =====================================================================================
' Get Preferred IPv4 address for TCP/UDP
' =====================================================================================
Private Function cCTSocket.PreferredAddress (ByRef sLocalHostName as String, _
                                             ByRef SockAddress as SOCKADDR_IN) as BOOLEAN

Dim lReturn           as BOOLEAN
Dim iError            as Long
Dim szLocalHostName   as ZString * MAX_COMPUTERNAME_LENGTH + 1
Dim pAddrInfo         as ADDRINFOA Ptr
Dim pFreeAddrInfo     as ADDRINFOA Ptr
Dim hints             as ADDRINFOA

    lReturn = LocalHostName(sLocalHostName)

    If lReturn = True Then

       szLocalHostName = sLocalHostName
       hints.ai_family = AF_INET

       iError = getaddrinfo(StrPtr(szLocalHostName),ByVal Null,@hints,@pAddrInfo)
       This.iLastError = WSAGetLastError

       If iError = 0 Then

          This.iLastError = WSAEAFNOSUPPORT
          lReturn = False

          pFreeAddrInfo = pAddrInfo

          Do While pAddrInfo <> Null

             Select Case pAddrInfo->ai_protocol

                Case 0,IPPROTO_UDP,IPPROTO_TCP
                
                   SockAddress = *Cast(SOCKADDR_IN Ptr, pAddrInfo->ai_addr)

                   This.iLastError = Null
                   lReturn = True
                   pAddrInfo = Null

                Case Else

                   pAddrInfo = pAddrInfo->ai_next

             End Select

          Loop

          freeaddrinfo(ByVal pFreeAddrInfo)

       Else

          lReturn = False

       End If

    End If

    Function = lReturn

End Function
' =====================================================================================
' Get Computer Name
' =====================================================================================
Private Function cCTSocket.LocalHostName (ByRef sLocalHostName as String) as BOOLEAN

Dim szLocalHostName   as ZString * MAX_COMPUTERNAME_LENGTH + 1
Dim lReturn           as BOOLEAN
Dim iReturn           as Long
Dim nSize             as Long

    sLocalHostName = ""
    This.iLastError = 0
    nSize = MAX_COMPUTERNAME_LENGTH + 1

    iReturn = GetComputerName(ByVal StrPtr(szLocalHostName),ByVal VarPtr(nSize))

    If iReturn <> 0 Then

       sLocalHostName = szLocalHostName
       lReturn = True

    Else

       This.iLastError = GetLastError()
       lReturn = False

    End If

    Function = lReturn

End Function
' =====================================================================================
' UDP Broadcast
' =====================================================================================
Private Function cCTSocket.UDPBroadcast (ByVal sMessage as String, _
                                         ByRef sResponse as String, _
                                         ByVal wPort as WORD, _
                                         ByVal iTimeOut as Long, _
                                         ByVal nMessageSize as Long) as BOOLEAN

Dim SockAddress       as SOCKADDR_IN
Dim hSocket           as SOCKET
Dim lReturn           as BOOLEAN
Dim iReturn           as Long
Dim iBroadcast        as Long
Dim iBroadcastLen     as Long

    If GetSocket (AF_INET,SOCK_DGRAM,IPPROTO_UDP,hSocket) = False Then

       Function = False
       Exit Function

    End If

    iBroadcast = 1
    iBroadcastLen = Len(iBroadcast)
    
' Turn on the broadcast option    

    iReturn = setsockopt(hsocket,SOL_SOCKET,SO_BROADCAST,ByVal VarPtr(iBroadcast),ByVal VarPtr(iBroadcastLen))
    This.iLastError = WSAGetLastError
    lReturn = IIf(iReturn = SOCKET_ERROR,False,True)

    If lReturn = True Then

       lReturn = UDPSendTo (hSocket,sMessage,wPort,INADDR_BROADCAST)

       If lReturn = True Then

          lReturn = SocketReceiveReady (hSocket,iTimeout)

          If lReturn = True Then

             lReturn = UDPReceiveFrom (hSocket,sResponse,SockAddress,nMessageSize)

          End If

       End If

    End If

    Disconnect(hSocket)

    Function = lReturn

End Function
' =====================================================================================
' Bind Socket
' =====================================================================================
Private Function cCTSocket.BindSocket (ByVal hSocket as SOCKET, _
                                       ByRef SockAddress as SOCKADDR_IN) as BOOLEAN

Dim iReturn           as Long

    iReturn = bind(hSocket,ByVal VarPtr(SockAddress),Len(SockAddress))

    This.iLastError = WSAGetLastError

    Function = IIf(iReturn = SOCKET_ERROR,False,True)

End Function
' =====================================================================================
' Listen Socket
' =====================================================================================
Private Function cCTSocket.ListenSocket (ByVal hSocket as SOCKET) as BOOLEAN

Dim iReturn           as BOOLEAN

    iReturn = listen(hSocket,SOMAXCONN)
    This.iLastError = WSAGetLastError

    Function = IIf(iReturn = SOCKET_ERROR,False,True)

End Function
' =====================================================================================
' Accept connection on a Listening Socket
' =====================================================================================
Private Function cCTSocket.AcceptSocket (ByVal hListeningSocket as SOCKET, _
                                         ByRef hAcceptSocket as SOCKET, _
                                         ByRef AcceptSockAddress as SOCKADDR_IN) as BOOLEAN

Dim iReturn           as Long
Dim SockAddressSize   as Long

    SockAddressSize = SizeOf(AcceptSockAddress)

    hAcceptSocket = accept(hListeningSocket,ByVal VarPtr(AcceptSockAddress),ByVal VarPtr(SockAddressSize))
    This.iLastError = WSAGetLastError

    Function = IIf(iReturn = INVALID_SOCKET,False,True)

End Function
' =====================================================================================
' Set Socket Blocking Mode
' =====================================================================================
Private Function cCTSocket.BlockingSocket (ByVal hSocket as SOCKET, _
                                           ByVal nMode as Long) as BOOLEAN                  '0, set socket to blocking

Dim iReturn        as Long

    iReturn = ioctlsocket(hSocket,FIONBIO,ByVal VarPtr(nMode))
    This.iLastError = WSAGetLastError

    Function = IIf(iReturn = SOCKET_ERROR,False,True)

End Function
' =====================================================================================
' Request Windows message-based notification of network events for a socket
' =====================================================================================
Private Function cCTSocket.AsyncSelectSocket (ByVal hSocket as SOCKET, _
                                              ByVal HWnd as DWORD, _
                                              ByVal nMessageID as DWORD, _
                                              ByVal nEvents as Long) as BOOLEAN

Dim iReturn        as Long

    iReturn = WSAAsyncSelect (hSocket,HWnd,nMessageID,nEvents)
    This.iLastError = WSAGetLastError

    Function = IIf(iReturn = SOCKET_ERROR,False,True)

End Function
' =====================================================================================
' Send a UDP message
' =====================================================================================
Private Function cCTSocket.UDPSendTo (ByVal hSocket as SOCKET, _
                                      ByRef sMessage as String, _
                                      ByVal wPort as WORD, _
                                      ByVal nIPAddress as ulong) as BOOLEAN

Dim SockAddress       as SOCKADDR_IN
Dim SockAddressSize   as Long
Dim iReturn           as Long

    SockAddress.sin_family = AF_INET
    SockAddress.sin_port = htons(wPort)
    SockAddress.sin_addr.s_addr = htonl(nIPAddress)
    SockAddressSize = Len(SockAddress)

    iReturn = sendto(hSocket,ByVal StrPtr(sMessage),Len(sMessage),0,ByVal VarPtr(SockAddress),SockAddressSize)
    This.iLastError = WSAGetLastError

    Function = IIf(iReturn = SOCKET_ERROR,False,True)

End Function
' =====================================================================================
' Receive a UDP message
' =====================================================================================
Private Function cCTSocket.UDPReceiveFrom (ByVal hSocket as DWORD, _
                                           ByRef sResponse as String, _
                                           ByRef SockAddress as SOCKADDR_IN, _
                                           ByVal nMessageSize as Long) as BOOLEAN

Dim SockAddressSize   as Long
Dim nResponseSize     as Long
Dim lReturn           as BOOLEAN

    sResponse = Space(nMessageSize)
    SockAddressSize = Len(SockAddress)
    nResponseSize = recvfrom(hSocket,ByVal StrPtr(sResponse),nMessageSize,0,ByVal VarPtr(SockAddress),ByVal VarPtr(SockAddressSize))
    This.iLastError = WSAGetLastError

    Select Case nResponseSize

        Case SOCKET_ERROR

            lReturn = False

        Case Is > 0

            sResponse = Left(sResponse,nResponseSize)
            lReturn = True

        Case Else

            sResponse = ""
            lReturn = True

    End Select

    Function = lReturn

End Function
' =====================================================================================
' Send a message and retrieve the response
' =====================================================================================
Private Function cCTSocket.SendAndReceiveSocket (ByVal hSocket as SOCKET, _
                                                 ByVal iBufferSize as Long, _
                                                 ByRef sMessage as String, _
                                                 ByRef sResponse as String, _
                                                 ByVal iTimeOut as Long) as BOOLEAN

Dim lReturn       as BOOLEAN 

    lReturn = SendSocket (hSocket,sMessage)

    If lReturn = True Then

        lReturn = ReceiveSocket (hSocket,iBufferSize,sResponse,iTimeOut)

    End If

    Function = lReturn

End Function
' =====================================================================================
' Send a message to a socket
' =====================================================================================
Private Function cCTSocket.SendSocket (ByVal hSocket as SOCKET, _
                                       ByRef sData as String) as BOOLEAN

Dim nDataSent         as Long
Dim nDataRemaining    as Long
Dim pData             as DWORD
Dim lReturn           as BOOLEAN

    nDataRemaining = Len(sData)
    This.iLastError = 0
    lReturn = True

    If nDataRemaining = 0 Then

       Function = True
       Exit Function

    End If

    pData = StrPtr(sData)

    Do

        nDataSent = send (hSocket,ByVal pData,nDataRemaining,0)
        This.iLastError = WSAGetLastError()

        Select Case nDataSent

            Case SOCKET_ERROR

                lReturn = False
                Exit Do

            Case Else

                pData = pData + nDataSent
                nDataRemaining = nDataRemaining - nDataSent

                If nDataRemaining < 1 Then

                   This.iLastError = 0
                   Exit Do

                End If

        End Select

    Loop

    Function = lReturn

End Function
' =====================================================================================
' Receive a message from a socket
' =====================================================================================
Private Function cCTSocket.ReceiveSocket(ByVal hSocket as Long, _
                                         ByVal iBufferSize as Long, _
                                         ByRef sReceived as String, _
                                         ByVal iTimeOut as Long) as BOOLEAN

Dim lpBuffers         as WSABUF
Dim sBuffer           as String
Dim nFlags            as Long
Dim nBytesReceived    as Long
Dim iReturn           as Long

    sReceived = ""
    nFlags = 0

    Do

        sBuffer = Space(iBufferSize)
        lpBuffers.Len = iBufferSize
        lpBuffers.buf = StrPtr(sBuffer)
        nBytesReceived = 0

        iReturn = WSARecv (hSocket,ByVal VarPtr(lpBuffers),1,ByVal VarPtr(nBytesReceived),ByVal VarPtr(nFlags),ByVal(0),ByVal(0))
        This.iLastError = WSAGetLastError

        If iReturn = SOCKET_ERROR Then

           Function = False
           Exit Function

        End If

        Select Case nBytesReceived

            Case Is > 0

                sReceived = sReceived + Left(sBuffer,nBytesReceived)

                If SocketReceiveReady(hSocket,iTimeOut) = False Then

                   Exit Do

                End If

            Case Else

                Exit Do

        End Select

    Loop

    Function = True

End Function
' =====================================================================================
' Check if any socket message is available to receive
' =====================================================================================
Private Function cCTSocket.SocketReceiveReady (ByVal hSocket as SOCKET, _
                                               ByVal iTimeout as Long) as BOOLEAN

Dim FDSet         as FD_SET
Dim FDTime        as timeval
Dim lReturn       as BOOLEAN
Dim hSocketsReady as Long

    lReturn = False

    FDSet.fd_count = 1
    FDSet.fd_array(0) = hSocket
    FDTime.tv_sec = Int(iTimeOut / 1000)
    FDTime.tv_usec = iTimeout - FDTime.tv_sec * 1000

    hSocketsReady = select_(0,ByVal VarPtr(FDSet),ByVal(0),ByVal(0),ByVal VarPtr(FDTime))

    This.iLastError = WSAGetLastError()

' If return value = number of sockets, socket is ready

    Select Case hSocketsReady

        Case 1

            lReturn = True

        Case SOCKET_ERROR

        Case Else

            This.iLastError = WSAETIMEDOUT

    End Select

    Function = lReturn

End Function
' =====================================================================================
' Set Socket Timeout
' =====================================================================================
Private Function cCTSocket.TimeoutSocket (ByVal hSocket as SOCKET, _
                                          ByVal iTimeout as Long) as BOOLEAN

Dim lReturn     as Long

    lReturn = True

    If iTimeOut > 0 Then

       lReturn = setsockopt(hSocket,SOL_SOCKET,SO_SNDTIMEO,ByVal VarPtr(iTimeOut),Len(iTimeOut))
       iLastError = WSAGetLastError
       lReturn = IIf(lReturn = SOCKET_ERROR,False,True)

       If lReturn = True Then

          lReturn = setsockopt(hSocket,SOL_SOCKET,SO_RCVTIMEO,ByVal VarPtr(iTimeOut),Len(iTimeOut))
          This.iLastError = WSAGetLastError
          lReturn = IIf(lReturn = SOCKET_ERROR,False,True)

       End If

    End If

    Function = lReturn

End Function
' =====================================================================================
' DNS lookup of host IP
' =====================================================================================
Private Function cCTSocket.HostByAddress (ByRef szIPAddress as ZString, _
                                          ByRef lpHostEntry as HOSTENT Ptr) as BOOLEAN

Dim uIN_ADDR    as in_addr

    uIN_ADDR.s_addr = StringToIP (szIPAddress)
                                       
    lpHostEntry = gethostbyaddr (ByVal VarPtr(uIN_ADDR.s_addr),Len(uIN_ADDR.s_Addr),AF_INET)

    This.iLastError = WSAGetLastError()

    Function = IIf(lpHostEntry <> 0,True,False)

End Function
' =====================================================================================
' Take an IP address and convert to string dot notation
' =====================================================================================
Private Sub cCTSocket.IPToString (ByVal nIPAddress as ulong, _
                                  ByRef szIPAddress as ZString)

Dim lpszIPAddress as ZString Ptr
Dim uIN_ADDR      as in_addr

    uIN_ADDR.s_addr = nIPAddress

    lpszIPAddress = inet_ntoa (uIN_ADDR)

    szIPAddress = *lpszIPAddress

End Sub
' =====================================================================================
' Convert dot notated IP address string
' =====================================================================================
Private Function cCTSocket.StringToIP (ByRef szIPAddress as ZString) as ulong


    Function = inet_addr (szIPAddress)

End Function
' =====================================================================================
' DNS lookup of host name
' =====================================================================================
Private Function cCTSocket.HostByName (ByRef szHostName as ZString, _
                                       ByRef lpHostEntry as HOSTENT Ptr) as BOOLEAN
                                       
    lpHostEntry = gethostbyname (szHostName)

    This.iLastError = WSAGetLastError()

    Function = IIf(lpHostEntry <> 0,True,False)

End Function
' =====================================================================================
' Socket Disconnect
' =====================================================================================
Private Function cCTSocket.Disconnect (ByRef hSocket as SOCKET) as BOOLEAN

Dim lReturn   as BOOLEAN

    lReturn = SocketShutdown(hSocket)
    SocketClose(hSocket)
    
    Function = lReturn

End Function
' =====================================================================================
' Initialize a socket
' =====================================================================================
Private Function cCTSocket.GetSocket (ByVal nFamily as Long, _
                                      ByVal nType as Long, _
                                      ByVal nProtocol as Long, _
                                      ByRef hSocket as SOCKET) as BOOLEAN

    hSocket = WSASocket(nFamily,nType,nProtocol,ByVal(0),ByVal(0),ByVal(0))

    This.iLastError = WSAGetLastError()

    Function = IIf(hSocket <> INVALID_SOCKET,True,False)

End Function
' =====================================================================================
' Shutdown socket
' =====================================================================================
Private Function cCTSocket.SocketShutdown (ByVal hSocket as SOCKET) as BOOLEAN

Dim iReturn   as Long

     iReturn =  shutdown(hSocket,SD_BOTH)
     
     Function = IIf(iReturn = 0,True,False)

End Function
' =====================================================================================
' Close Socket
' =====================================================================================
Private Sub cCTSocket.SocketClose (ByVal hSocket as SOCKET)

       If hSocket <> INVALID_SOCKET Then    

          closesocket(hSocket)
          
       End If

End Sub
' =====================================================================================
' Connect Socket
' =====================================================================================
Private Function cCTSocket.ConnectSocket (ByVal hSocket as SOCKET, _
                                          ByRef SockAddress as SOCKADDR_IN) as BOOLEAN

Dim lReturn   as BOOLEAN

    lReturn = WSAConnect (hSocket, VarPtr(SockAddress), SizeOf(SockAddress), ByVal(0), ByVal(0), ByVal(0), ByVal(0))
    
    This.iLastError = WSAGetLastError()

    Function = IIf(lReturn <> SOCKET_ERROR,True,False)

End Function
' =====================================================================================
' Get last Winsock Error Code
' =====================================================================================
Private Property cCTSocket.GetLastErrorCode() as Long

    GetLastErrorCode = This.iLastError

End Property
' =====================================================================================
' Winsock Startup Status
' =====================================================================================
Private Property cCTSocket.StartupStatus() as BOOLEAN

    StartupStatus = This.lStartup

End Property
' =====================================================================================
' Winsock Startup Error Code
' =====================================================================================
Private Property cCTSocket.StartupError() as Long

    StartupError = This.iStartupError

End Property
' =====================================================================================
' Winsock Startup
' =====================================================================================
Private Function cCTSocket.WinSockStartup (ByRef iError as Long) as BOOLEAN

' Major version 2, minor version 2 minimum required

    iError = WSAStartup (makeword(2,2), @This.WSAD)

    Function = IIf(iError = 0,True,False)

End Function